| | | 1 | | import { useEffect, useRef } from "react"; |
| | | 2 | | import type { SessionUser } from "~/lib/session.server"; |
| | | 3 | | |
| | | 4 | | interface Props { |
| | | 5 | | user: SessionUser; |
| | | 6 | | open: boolean; |
| | | 7 | | onClose: () => void; |
| | | 8 | | onSignOut: () => void; |
| | | 9 | | } |
| | | 10 | | |
| | 0 | 11 | | export function UserModal({ user, open, onClose, onSignOut }: Props) { |
| | 0 | 12 | | const ref = useRef<HTMLDialogElement>(null); |
| | | 13 | | |
| | 0 | 14 | | useEffect(() => { |
| | 0 | 15 | | if (open) ref.current?.showModal(); |
| | 0 | 16 | | else ref.current?.close(); |
| | | 17 | | }, [open]); |
| | | 18 | | |
| | 0 | 19 | | const groups = user.groups ?? []; |
| | 0 | 20 | | const displayName = user.name ?? user.preferred_username ?? user.sub; |
| | 0 | 21 | | const username = user.preferred_username; |
| | 0 | 22 | | const email = user.email; |
| | 0 | 23 | | const initial = (displayName ?? "?")[0]?.toUpperCase() ?? "?"; |
| | | 24 | | |
| | 0 | 25 | | return ( |
| | | 26 | | <dialog |
| | | 27 | | ref={ref} |
| | | 28 | | onClose={onClose} |
| | 0 | 29 | | onClick={(e) => { if (e.target === ref.current) onClose(); }} |
| | | 30 | | style={{ |
| | | 31 | | width: "100%", |
| | | 32 | | maxWidth: 360, |
| | | 33 | | padding: 0, |
| | | 34 | | border: "1px solid var(--c-border)", |
| | | 35 | | borderRadius: 10, |
| | | 36 | | background: "var(--c-bg-2)", |
| | | 37 | | color: "var(--c-fg)", |
| | | 38 | | fontFamily: "Inter, ui-sans-serif, system-ui, sans-serif", |
| | | 39 | | boxShadow: "0 20px 60px rgba(0,0,0,0.25)", |
| | | 40 | | }} |
| | | 41 | | > |
| | 0 | 42 | | <div onClick={(e) => e.stopPropagation()} style={{ display: "flex", flexDirection: "column", gap: 0 }}> |
| | | 43 | | <div style={{ padding: "20px 20px 16px", borderBottom: "1px solid var(--c-border-2)" }}> |
| | | 44 | | <div style={{ display: "flex", alignItems: "center", gap: 14 }}> |
| | | 45 | | <div style={{ |
| | | 46 | | width: 42, height: 42, borderRadius: 999, background: "var(--c-accent)", |
| | | 47 | | display: "flex", alignItems: "center", justifyContent: "center", |
| | | 48 | | fontSize: 16, fontWeight: 700, color: "white", flexShrink: 0, |
| | | 49 | | }}> |
| | | 50 | | {initial} |
| | | 51 | | </div> |
| | | 52 | | <div style={{ minWidth: 0 }}> |
| | | 53 | | <div style={{ fontWeight: 600, fontSize: 14, color: "var(--c-fg)", overflow: "hidden", textOverflow: "elli |
| | | 54 | | {displayName} |
| | | 55 | | </div> |
| | | 56 | | {username && displayName !== username && ( |
| | | 57 | | <div style={{ fontSize: 12, color: "var(--c-fg-3)", marginTop: 2 }}>@{username}</div> |
| | | 58 | | )} |
| | | 59 | | </div> |
| | | 60 | | </div> |
| | | 61 | | </div> |
| | | 62 | | |
| | | 63 | | <div style={{ padding: "12px 20px", display: "flex", flexDirection: "column", gap: 0 }}> |
| | | 64 | | {email && ( |
| | | 65 | | <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "7px 0", bord |
| | | 66 | | <span style={{ color: "var(--c-fg-3)" }}>Email</span> |
| | | 67 | | <span style={{ color: "var(--c-fg)", fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis", whiteS |
| | | 68 | | </div> |
| | | 69 | | )} |
| | | 70 | | {groups.length > 0 && ( |
| | | 71 | | <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", padding: "7px 0", |
| | | 72 | | <span style={{ color: "var(--c-fg-3)", paddingTop: 2 }}>Groups</span> |
| | | 73 | | <div style={{ display: "flex", flexWrap: "wrap", gap: 4, justifyContent: "flex-end", maxWidth: 200 }}> |
| | 0 | 74 | | {groups.map((g) => ( |
| | 0 | 75 | | <span key={g} style={{ |
| | | 76 | | fontSize: 11, padding: "2px 8px", borderRadius: 4, |
| | | 77 | | background: "var(--c-accent-bg)", color: "var(--c-accent)", fontWeight: 500, |
| | | 78 | | }}> |
| | | 79 | | {g} |
| | | 80 | | </span> |
| | | 81 | | ))} |
| | | 82 | | </div> |
| | | 83 | | </div> |
| | | 84 | | )} |
| | | 85 | | </div> |
| | | 86 | | |
| | | 87 | | <div style={{ padding: "12px 20px 16px", borderTop: "1px solid var(--c-border-2)", display: "flex", justifyConte |
| | | 88 | | <button type="button" onClick={onClose} className="btn-secondary">Close</button> |
| | | 89 | | <button type="button" onClick={onSignOut} className="btn-danger">Sign out</button> |
| | | 90 | | </div> |
| | | 91 | | </div> |
| | | 92 | | </dialog> |
| | | 93 | | ); |
| | | 94 | | } |