* Fix mobile league settings: prevent sports scroll and remove completion checkmarks - Add overflow-hidden to sport label text span so flex truncation is properly contained - Add min-w-0 to SettingsSection header inner flex item to prevent title/description overflow - Remove completion check marks from mobile grid nav buttons (league settings has no completion concept) - Remove "X of Y set" counter from mobile nav header - Remove isComplete field from SettingsGridSection type and all data https://claude.ai/code/session_01T7iTb9YZLuWJFtKV753tdB * Fix race window in user creation and restore FlagSvg safety fallback auth.server.ts: switch generateId to application-generated UUIDs so the ID is known before the insert, then move flagConfig assignment from create.after (a separate UPDATE) to create.before so it lands in the initial INSERT with no race window. FlagSvg.tsx: validate the config with parseFlagConfig before rendering; corrupt or missing data falls back to a neutral gray triband flag rather than silently rendering blank SVG shapes. https://claude.ai/code/session_01T7iTb9YZLuWJFtKV753tdB --------- Co-authored-by: Claude <noreply@anthropic.com>
116 lines
3.5 KiB
TypeScript
116 lines
3.5 KiB
TypeScript
import type { ComponentType } from "react";
|
|
import type { LucideProps } from "lucide-react";
|
|
import { ArrowLeft } from "lucide-react";
|
|
import { cn } from "~/lib/utils";
|
|
|
|
type SettingsNavSection = {
|
|
id: string;
|
|
label: string;
|
|
};
|
|
|
|
export type SettingsGridSection = SettingsNavSection & {
|
|
icon: ComponentType<LucideProps>;
|
|
subtitle: string;
|
|
isDanger?: boolean;
|
|
};
|
|
|
|
export function SettingsMobileGridNav({
|
|
sections,
|
|
onSectionChange,
|
|
}: {
|
|
sections: readonly SettingsGridSection[];
|
|
onSectionChange: (sectionId: string) => void;
|
|
}) {
|
|
return (
|
|
<div className="mb-5 lg:hidden">
|
|
<div className="mb-3">
|
|
<p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
|
Manage
|
|
</p>
|
|
</div>
|
|
<div className="grid grid-cols-2 gap-3">
|
|
{sections.map((section) => (
|
|
<button
|
|
key={section.id}
|
|
type="button"
|
|
onClick={() => onSectionChange(section.id)}
|
|
className={cn(
|
|
"flex cursor-pointer flex-col gap-3 rounded-xl border bg-card p-4 text-left transition-colors hover:border-primary/40 active:scale-[0.98]",
|
|
section.isDanger && "border-destructive/40"
|
|
)}
|
|
>
|
|
<div
|
|
className={cn(
|
|
"flex h-10 w-10 items-center justify-center rounded-lg",
|
|
section.isDanger
|
|
? "bg-destructive/15 text-destructive"
|
|
: "bg-primary/20 text-primary"
|
|
)}
|
|
>
|
|
<section.icon className="h-5 w-5" />
|
|
</div>
|
|
<div>
|
|
<p className="text-sm font-semibold leading-tight">{section.label}</p>
|
|
<p className="mt-0.5 text-xs text-muted-foreground">{section.subtitle}</p>
|
|
</div>
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function SettingsMobileSectionPill({
|
|
onShowGrid,
|
|
}: {
|
|
onShowGrid: () => void;
|
|
}) {
|
|
return (
|
|
<div className="mb-5 lg:hidden">
|
|
<button
|
|
type="button"
|
|
onClick={onShowGrid}
|
|
className="flex cursor-pointer items-center gap-1.5 rounded-full border bg-card px-3 py-1.5 text-sm font-medium hover:bg-muted"
|
|
>
|
|
<ArrowLeft className="h-3.5 w-3.5" />
|
|
Back to all settings
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function SettingsDesktopNav({
|
|
sections,
|
|
activeSection,
|
|
onSectionChange,
|
|
}: {
|
|
sections: readonly SettingsGridSection[];
|
|
activeSection: string;
|
|
onSectionChange: (sectionId: string) => void;
|
|
}) {
|
|
return (
|
|
<aside className="hidden lg:block">
|
|
<div className="sticky top-6 rounded-xl border bg-card p-3">
|
|
<p className="px-3 pb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
|
Manage
|
|
</p>
|
|
<nav className="space-y-1">
|
|
{sections.map((section) => (
|
|
<button
|
|
key={section.id}
|
|
type="button"
|
|
onClick={() => onSectionChange(section.id)}
|
|
className={cn(
|
|
"flex w-full cursor-pointer items-center gap-2.5 rounded-md px-3 py-2 text-left text-sm font-medium text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
activeSection === section.id && "bg-muted text-foreground"
|
|
)}
|
|
>
|
|
<section.icon className={cn("h-4 w-4 shrink-0", section.isDanger && "text-destructive")} />
|
|
{section.label}
|
|
</button>
|
|
))}
|
|
</nav>
|
|
</div>
|
|
</aside>
|
|
);
|
|
}
|