mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-14 23:22:51 +00:00
feat(web): add Profile page to user menu
Surfaces session info from useAuthMe (name, avatar, username, email, IdP issuer/subject, profile URL) on a new /profile route, structured as Basics + Identity panels matching the Settings page layout. Adds a Profile link to the desktop dropdown and mobile menu. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
da805fae0e
commit
025f41dddd
3 changed files with 94 additions and 4 deletions
|
|
@ -151,6 +151,14 @@ export default function AppShell() {
|
|||
transition
|
||||
className="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-panel py-1 outline-1 -outline-offset-1 outline-line-strong transition data-closed:scale-95 data-closed:transform data-closed:opacity-0 data-enter:duration-100 data-enter:ease-out data-leave:duration-75 data-leave:ease-in"
|
||||
>
|
||||
<MenuItem>
|
||||
<Link
|
||||
to="/profile"
|
||||
className="block w-full px-4 py-2 text-left text-sm text-fg-3 data-focus:bg-overlay data-focus:outline-hidden"
|
||||
>
|
||||
Profile
|
||||
</Link>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
<form method="POST" action="/auth/logout">
|
||||
<button
|
||||
|
|
@ -237,8 +245,15 @@ export default function AppShell() {
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{provider !== "tailscale" && (
|
||||
<div className="mt-3 space-y-1 px-2">
|
||||
<div className="mt-3 space-y-1 px-2">
|
||||
<DisclosureButton
|
||||
as={Link}
|
||||
to="/profile"
|
||||
className="block w-full rounded-md px-3 py-2 text-left text-base font-medium text-fg-muted hover:bg-overlay hover:text-fg"
|
||||
>
|
||||
Profile
|
||||
</DisclosureButton>
|
||||
{provider !== "tailscale" && (
|
||||
<form method="POST" action="/auth/logout">
|
||||
<DisclosureButton
|
||||
as="button"
|
||||
|
|
@ -248,8 +263,8 @@ export default function AppShell() {
|
|||
Sign out
|
||||
</DisclosureButton>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</DisclosurePanel>
|
||||
</Disclosure>
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import * as Insights from "./routes/insights";
|
|||
import * as InsightsEditor from "./routes/insights-editor";
|
||||
import * as InsightsNew from "./routes/insights-new";
|
||||
import * as Settings from "./routes/settings";
|
||||
import * as Profile from "./routes/profile";
|
||||
import AppShellModule from "./layouts/app-shell";
|
||||
|
||||
type RouteModule = {
|
||||
|
|
@ -109,6 +110,7 @@ export const routes: RouteObject[] = [
|
|||
],
|
||||
}),
|
||||
route("settings", Settings),
|
||||
route("profile", Profile),
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
|||
73
apps/fabro-web/app/routes/profile.tsx
Normal file
73
apps/fabro-web/app/routes/profile.tsx
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import type { AuthSessionUser } from "@qltysh/fabro-api-client";
|
||||
import { useAuthMe } from "../lib/queries";
|
||||
import {
|
||||
Mono,
|
||||
Muted,
|
||||
Panel,
|
||||
PanelSkeleton,
|
||||
Row,
|
||||
UrlValue,
|
||||
} from "../components/settings-panel";
|
||||
|
||||
export function meta({}: any) {
|
||||
return [{ title: "Profile — Fabro" }];
|
||||
}
|
||||
|
||||
export default function Profile() {
|
||||
const { data: auth } = useAuthMe();
|
||||
|
||||
if (!auth) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<PanelSkeleton />
|
||||
<PanelSkeleton />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const { user } = auth;
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<BasicsPanel user={user} />
|
||||
<IdentityPanel user={user} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function BasicsPanel({ user }: { user: AuthSessionUser }) {
|
||||
return (
|
||||
<Panel title="Basics">
|
||||
<Row title="Name">
|
||||
<div className="flex items-center gap-3">
|
||||
<img
|
||||
alt=""
|
||||
src={user.avatarUrl}
|
||||
className="size-10 rounded-full outline -outline-offset-1 outline-line-strong"
|
||||
/>
|
||||
<span>{user.name}</span>
|
||||
</div>
|
||||
</Row>
|
||||
<Row title="Username">
|
||||
<Mono>{user.login}</Mono>
|
||||
</Row>
|
||||
<Row title="Email">{user.email}</Row>
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
|
||||
function IdentityPanel({ user }: { user: AuthSessionUser }) {
|
||||
return (
|
||||
<Panel title="Identity">
|
||||
<Row title="IdP issuer">
|
||||
{user.idpIssuer ? <Mono>{user.idpIssuer}</Mono> : <Muted>—</Muted>}
|
||||
</Row>
|
||||
<Row title="IdP subject">
|
||||
{user.idpSubject ? <Mono>{user.idpSubject}</Mono> : <Muted>—</Muted>}
|
||||
</Row>
|
||||
<Row title="Profile URL">
|
||||
<UrlValue url={user.userUrl} />
|
||||
</Row>
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue