Files
gridpilot.gg/apps/website/components/onboarding/PersonalInfoStep.tsx
2026-01-18 16:43:32 +01:00

147 lines
5.1 KiB
TypeScript

import { CountrySelect } from '@/components/shared/CountrySelect';
import { Icon } from '@/ui/Icon';
import { Input } from '@/ui/Input';
import { Grid } from '@/ui/primitives/Grid';
import { Stack } from '@/ui/primitives/Stack';
import { Select } from '@/ui/Select';
import { Text } from '@/ui/Text';
import { ChevronRight, Clock } from 'lucide-react';
export interface PersonalInfo {
firstName: string;
lastName: string;
displayName: string;
country: string;
timezone: string;
}
interface FormErrors {
[key: string]: string | undefined;
}
interface PersonalInfoStepProps {
personalInfo: PersonalInfo;
setPersonalInfo: (info: PersonalInfo) => void;
errors: FormErrors;
loading: boolean;
}
const TIMEZONES = [
{ value: 'America/New_York', label: 'Eastern Time (ET)' },
{ value: 'America/Chicago', label: 'Central Time (CT)' },
{ value: 'America/Denver', label: 'Mountain Time (MT)' },
{ value: 'America/Los_Angeles', label: 'Pacific Time (PT)' },
{ value: 'Europe/London', label: 'Greenwich Mean Time (GMT)' },
{ value: 'Europe/Berlin', label: 'Central European Time (CET)' },
{ value: 'Europe/Paris', label: 'Central European Time (CET)' },
{ value: 'Australia/Sydney', label: 'Australian Eastern Time (AET)' },
{ value: 'Asia/Tokyo', label: 'Japan Standard Time (JST)' },
{ value: 'America/Sao_Paulo', label: 'Brasília Time (BRT)' },
];
export function PersonalInfoStep({ personalInfo, setPersonalInfo, errors, loading }: PersonalInfoStepProps) {
return (
<Stack gap={6}>
<Grid cols={2} gap={4}>
<Stack>
<Text as="label" htmlFor="firstName" size="sm" weight="medium" color="text-gray-300" block mb={2}>
First Name *
</Text>
<Input
id="firstName"
type="text"
value={personalInfo.firstName}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setPersonalInfo({ ...personalInfo, firstName: e.target.value })
}
variant={errors.firstName ? 'error' : 'default'}
errorMessage={errors.firstName}
placeholder="John"
disabled={loading}
/>
</Stack>
<Stack>
<Text as="label" htmlFor="lastName" size="sm" weight="medium" color="text-gray-300" block mb={2}>
Last Name *
</Text>
<Input
id="lastName"
type="text"
value={personalInfo.lastName}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setPersonalInfo({ ...personalInfo, lastName: e.target.value })
}
variant={errors.lastName ? 'error' : 'default'}
errorMessage={errors.lastName}
placeholder="Racer"
disabled={loading}
/>
</Stack>
</Grid>
<Stack>
<Text as="label" htmlFor="displayName" size="sm" weight="medium" color="text-gray-300" block mb={2}>
Display Name * <Text color="text-gray-500" weight="normal">(shown publicly)</Text>
</Text>
<Input
id="displayName"
type="text"
value={personalInfo.displayName}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setPersonalInfo({ ...personalInfo, displayName: e.target.value })
}
variant={errors.displayName ? 'error' : 'default'}
errorMessage={errors.displayName}
placeholder="SpeedyRacer42"
disabled={loading}
/>
</Stack>
<Grid cols={2} gap={4}>
<Stack>
<Text as="label" htmlFor="country" size="sm" weight="medium" color="text-gray-300" block mb={2}>
Country *
</Text>
<CountrySelect
value={personalInfo.country}
onChange={(value: string) =>
setPersonalInfo({ ...personalInfo, country: value })
}
error={!!errors.country}
errorMessage={errors.country ?? ''}
disabled={loading}
/>
</Stack>
<Stack>
<Text as="label" htmlFor="timezone" size="sm" weight="medium" color="text-gray-300" block mb={2}>
Timezone
</Text>
<Stack position="relative">
<Stack position="absolute" left={3} top="50%" transform="translateY(-50%)" zIndex={10}>
<Icon icon={Clock} size={4} color="text-gray-500" />
</Stack>
<Select
id="timezone"
value={personalInfo.timezone}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>
setPersonalInfo({ ...personalInfo, timezone: e.target.value })
}
options={[
{ value: '', label: 'Select timezone' },
...TIMEZONES
]}
pl={10}
disabled={loading}
/>
<Stack position="absolute" right={3} top="50%" transform="translateY(-50%)" pointerEvents="none">
<Icon icon={ChevronRight} size={4} color="text-gray-500" transform="rotate(90deg)" />
</Stack>
</Stack>
</Stack>
</Grid>
</Stack>
);
}