< Summary

Information
Class: user-modal.tsx
Assembly: app.components
File(s): /home/runner/work/ClutterStock/ClutterStock/frontend/app/components/user-modal.tsx
Tag: 58_25416222083
Line coverage
0%
Covered lines: 0
Uncovered lines: 15
Coverable lines: 15
Total lines: 94
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 13
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/ClutterStock/ClutterStock/frontend/app/components/user-modal.tsx

#LineLine coverage
 1import { useEffect, useRef } from "react";
 2import type { SessionUser } from "~/lib/session.server";
 3
 4interface Props {
 5  user: SessionUser;
 6  open: boolean;
 7  onClose: () => void;
 8  onSignOut: () => void;
 9}
 10
 011export function UserModal({ user, open, onClose, onSignOut }: Props) {
 012  const ref = useRef<HTMLDialogElement>(null);
 13
 014  useEffect(() => {
 015    if (open) ref.current?.showModal();
 016    else ref.current?.close();
 17  }, [open]);
 18
 019  const groups = user.groups ?? [];
 020  const displayName = user.name ?? user.preferred_username ?? user.sub;
 021  const username = user.preferred_username;
 022  const email = user.email;
 023  const initial = (displayName ?? "?")[0]?.toUpperCase() ?? "?";
 24
 025  return (
 26    <dialog
 27      ref={ref}
 28      onClose={onClose}
 029      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    >
 042      <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 }}>
 074                {groups.map((g) => (
 075                  <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}