2026-05-10 17:26:14 -07:00
|
|
|
import { Form } from "react-router";
|
|
|
|
|
import { useEffect, useState } from "react";
|
|
|
|
|
import { Button } from "~/components/ui/button";
|
|
|
|
|
import { Input } from "~/components/ui/input";
|
|
|
|
|
import { Label } from "~/components/ui/label";
|
|
|
|
|
import { TimezoneSelect, TIMEZONE_OPTIONS } from "~/components/league/TimezoneSelect";
|
|
|
|
|
import { AvatarEditor } from "~/components/ui/AvatarEditor";
|
|
|
|
|
import type { User } from "~/models/user";
|
|
|
|
|
|
|
|
|
|
const VALID_TIMEZONES = new Set(
|
|
|
|
|
TIMEZONE_OPTIONS.flatMap((g) => g.zones.map((z) => z.value))
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
type Props = {
|
|
|
|
|
user: User;
|
|
|
|
|
isInActiveDraft: boolean;
|
|
|
|
|
success?: boolean;
|
|
|
|
|
error?: string;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export function ProfileSection({ user, isInActiveDraft, success, error }: Props) {
|
|
|
|
|
const [timezone, setTimezone] = useState(user.timezone ?? "");
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!user.timezone) {
|
|
|
|
|
try {
|
|
|
|
|
const detected = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
|
|
|
if (detected && VALID_TIMEZONES.has(detected)) {
|
|
|
|
|
setTimezone(detected);
|
|
|
|
|
}
|
|
|
|
|
} catch {
|
|
|
|
|
// Intl not available
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}, [user.timezone]);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
<div>
|
|
|
|
|
<h2 className="text-lg font-semibold">Profile</h2>
|
|
|
|
|
<p className="text-sm text-muted-foreground">
|
2026-05-15 10:06:54 -07:00
|
|
|
Your username, avatar, and timezone.
|
2026-05-10 17:26:14 -07:00
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{success && (
|
|
|
|
|
<p className="text-sm text-green-500">Profile updated successfully.</p>
|
|
|
|
|
)}
|
|
|
|
|
{error && <p className="text-sm text-destructive">{error}</p>}
|
|
|
|
|
|
|
|
|
|
<div className="border-b pb-6">
|
|
|
|
|
<AvatarEditor
|
|
|
|
|
id={user.id}
|
|
|
|
|
flagConfig={user.flagConfig}
|
|
|
|
|
uploadedPhotoUrl={user.avatarType === "uploaded" ? user.customAvatarUrl : null}
|
|
|
|
|
uploadUrl="/api/upload-avatar"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<Form method="post" className="space-y-4">
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
<Label htmlFor="username">Username</Label>
|
|
|
|
|
<Input
|
|
|
|
|
id="username"
|
|
|
|
|
name="username"
|
|
|
|
|
defaultValue={user.username ?? ""}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="space-y-1">
|
|
|
|
|
<Label>Email</Label>
|
|
|
|
|
<p className="text-sm text-muted-foreground">{user.email}</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
<Label htmlFor="timezone">Timezone</Label>
|
|
|
|
|
<input type="hidden" name="timezone" value={timezone} />
|
|
|
|
|
<TimezoneSelect
|
|
|
|
|
value={timezone}
|
|
|
|
|
onChange={setTimezone}
|
|
|
|
|
disabled={isInActiveDraft}
|
|
|
|
|
/>
|
|
|
|
|
{isInActiveDraft ? (
|
|
|
|
|
<p className="text-xs text-muted-foreground">
|
|
|
|
|
Timezone cannot be changed while you are in an active draft.
|
|
|
|
|
</p>
|
|
|
|
|
) : (
|
|
|
|
|
<p className="text-xs text-muted-foreground">
|
|
|
|
|
Used for overnight pick protection. We pre-filled your browser's detected timezone.
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
<Button type="submit" name="intent" value="update-profile">
|
|
|
|
|
Save Changes
|
|
|
|
|
</Button>
|
|
|
|
|
</Form>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|