wip
This commit is contained in:
@@ -38,7 +38,10 @@ const TIME_ZONES = [
|
||||
{ value: 'Australia/Sydney', label: 'Sydney (AU)', icon: Globe },
|
||||
];
|
||||
|
||||
type RecurrenceStrategy = NonNullable<LeagueConfigFormModel['timings']>['recurrenceStrategy'];
|
||||
type RecurrenceStrategy = Exclude<
|
||||
NonNullable<LeagueConfigFormModel['timings']>['recurrenceStrategy'],
|
||||
undefined
|
||||
>;
|
||||
|
||||
interface LeagueTimingsSectionProps {
|
||||
form: LeagueConfigFormModel;
|
||||
@@ -96,12 +99,16 @@ function RaceDayPreview({
|
||||
const effectiveRaceTime = raceTime || '20:00';
|
||||
|
||||
const getStartTime = (sessionIndex: number) => {
|
||||
const [hours, minutes] = effectiveRaceTime.split(':').map(Number);
|
||||
const [hoursStr, minutesStr] = effectiveRaceTime.split(':');
|
||||
const hours = Number(hoursStr ?? '0');
|
||||
const minutes = Number(minutesStr ?? '0');
|
||||
let totalMinutes = hours * 60 + minutes;
|
||||
|
||||
const active = allSessions.filter(s => s.active);
|
||||
|
||||
const active = allSessions.filter((s) => s.active);
|
||||
for (let i = 0; i < sessionIndex; i++) {
|
||||
totalMinutes += active[i].duration + 10; // 10 min break between sessions
|
||||
const session = active[i];
|
||||
if (!session) continue;
|
||||
totalMinutes += session.duration + 10; // 10 min break between sessions
|
||||
}
|
||||
|
||||
const h = Math.floor(totalMinutes / 60) % 24;
|
||||
@@ -231,10 +238,31 @@ function YearCalendarPreview({
|
||||
}) {
|
||||
// JavaScript getDay(): 0=Sunday, 1=Monday, 2=Tuesday, etc.
|
||||
const dayMap: Record<Weekday, number> = {
|
||||
'Sun': 0, 'Mon': 1, 'Tue': 2, 'Wed': 3, 'Thu': 4, 'Fri': 5, 'Sat': 6
|
||||
Sun: 0,
|
||||
Mon: 1,
|
||||
Tue: 2,
|
||||
Wed: 3,
|
||||
Thu: 4,
|
||||
Fri: 5,
|
||||
Sat: 6,
|
||||
};
|
||||
|
||||
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
||||
const months = [
|
||||
'Jan',
|
||||
'Feb',
|
||||
'Mar',
|
||||
'Apr',
|
||||
'May',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Aug',
|
||||
'Sep',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Dec',
|
||||
] as const;
|
||||
|
||||
const getMonthLabel = (index: number): string => months[index] ?? '—';
|
||||
|
||||
// Parse start and end dates
|
||||
const seasonStart = useMemo(() => {
|
||||
@@ -279,7 +307,8 @@ function YearCalendarPreview({
|
||||
const spacing = totalPossible / rounds;
|
||||
for (let i = 0; i < rounds; i++) {
|
||||
const index = Math.min(Math.floor(i * spacing), totalPossible - 1);
|
||||
dates.push(allPossibleDays[index]);
|
||||
const chosen = allPossibleDays[index]!;
|
||||
dates.push(chosen);
|
||||
}
|
||||
} else {
|
||||
// Not enough days - use all available
|
||||
@@ -380,7 +409,7 @@ function YearCalendarPreview({
|
||||
}
|
||||
|
||||
view.push({
|
||||
month: months[targetMonth],
|
||||
month: months[targetMonth] ?? '—',
|
||||
monthIndex: targetMonth,
|
||||
year: targetYear,
|
||||
days
|
||||
@@ -391,8 +420,8 @@ function YearCalendarPreview({
|
||||
}
|
||||
|
||||
// Get the range of months that contain races
|
||||
const firstRaceDate = raceDates[0];
|
||||
const lastRaceDate = raceDates[raceDates.length - 1];
|
||||
const firstRaceDate = raceDates[0]!;
|
||||
const lastRaceDate = raceDates[raceDates.length - 1]!;
|
||||
|
||||
// Start from first race month, show 12 months total
|
||||
const startMonth = firstRaceDate.getMonth();
|
||||
@@ -414,18 +443,19 @@ function YearCalendarPreview({
|
||||
rd.getDate() === date.getDate()
|
||||
);
|
||||
const isRace = raceIndex >= 0;
|
||||
const raceNumber = isRace ? raceIndex + 1 : undefined;
|
||||
days.push({
|
||||
date,
|
||||
isRace,
|
||||
dayOfMonth: day,
|
||||
isStart: isSeasonStartDate(date),
|
||||
isEnd: isSeasonEndDate(date),
|
||||
raceNumber: isRace ? raceIndex + 1 : undefined,
|
||||
...(raceNumber !== undefined ? { raceNumber } : {}),
|
||||
});
|
||||
}
|
||||
|
||||
view.push({
|
||||
month: months[targetMonth],
|
||||
month: months[targetMonth] ?? '—',
|
||||
monthIndex: targetMonth,
|
||||
year: targetYear,
|
||||
days
|
||||
@@ -438,9 +468,13 @@ function YearCalendarPreview({
|
||||
// Calculate season stats
|
||||
const firstRace = raceDates[0];
|
||||
const lastRace = raceDates[raceDates.length - 1];
|
||||
const seasonDurationWeeks = firstRace && lastRace
|
||||
? Math.ceil((lastRace.getTime() - firstRace.getTime()) / (7 * 24 * 60 * 60 * 1000))
|
||||
: 0;
|
||||
const seasonDurationWeeks =
|
||||
firstRace && lastRace
|
||||
? Math.ceil(
|
||||
(lastRace.getTime() - firstRace.getTime()) /
|
||||
(7 * 24 * 60 * 60 * 1000),
|
||||
)
|
||||
: 0;
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
@@ -525,12 +559,18 @@ function YearCalendarPreview({
|
||||
<div className="text-[9px] text-gray-500">Rounds</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-bold text-white">{seasonDurationWeeks || '—'}</div>
|
||||
<div className="text-lg font-bold text-white">
|
||||
{seasonDurationWeeks || '—'}
|
||||
</div>
|
||||
<div className="text-[9px] text-gray-500">Weeks</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-bold text-primary-blue">
|
||||
{firstRace ? `${months[firstRace.getMonth()]}–${months[lastRace?.getMonth() ?? 0]}` : '—'}
|
||||
{firstRace && lastRace
|
||||
? `${getMonthLabel(firstRace.getMonth())}–${getMonthLabel(
|
||||
lastRace.getMonth(),
|
||||
)}`
|
||||
: '—'}
|
||||
</div>
|
||||
<div className="text-[9px] text-gray-500">Duration</div>
|
||||
</div>
|
||||
@@ -986,7 +1026,9 @@ export function LeagueTimingsSection({
|
||||
onClick={() =>
|
||||
updateTimings({
|
||||
recurrenceStrategy: opt.id as RecurrenceStrategy,
|
||||
intervalWeeks: opt.id === 'everyNWeeks' ? 2 : undefined,
|
||||
...(opt.id === 'everyNWeeks'
|
||||
? { intervalWeeks: 2 }
|
||||
: {}),
|
||||
})
|
||||
}
|
||||
className={`
|
||||
@@ -1054,7 +1096,7 @@ export function LeagueTimingsSection({
|
||||
<Input
|
||||
type="date"
|
||||
value={timings.seasonStartDate ?? ''}
|
||||
onChange={(e) => updateTimings({ seasonStartDate: e.target.value || undefined })}
|
||||
onChange={(e) => updateTimings({ seasonStartDate: e.target.value })}
|
||||
className="bg-iron-gray/30"
|
||||
/>
|
||||
</div>
|
||||
@@ -1066,7 +1108,7 @@ export function LeagueTimingsSection({
|
||||
<Input
|
||||
type="date"
|
||||
value={timings.seasonEndDate ?? ''}
|
||||
onChange={(e) => updateTimings({ seasonEndDate: e.target.value || undefined })}
|
||||
onChange={(e) => updateTimings({ seasonEndDate: e.target.value })}
|
||||
className="bg-iron-gray/30"
|
||||
/>
|
||||
</div>
|
||||
@@ -1086,7 +1128,7 @@ export function LeagueTimingsSection({
|
||||
<Input
|
||||
type="time"
|
||||
value={timings.raceStartTime ?? '20:00'}
|
||||
onChange={(e) => updateTimings({ raceStartTime: e.target.value || undefined })}
|
||||
onChange={(e) => updateTimings({ raceStartTime: e.target.value })}
|
||||
className="bg-iron-gray/30"
|
||||
/>
|
||||
</div>
|
||||
@@ -1214,39 +1256,59 @@ export function LeagueTimingsSection({
|
||||
|
||||
{/* Preview content */}
|
||||
<div className="p-4 min-h-[300px]">
|
||||
{previewTab === 'day' && (
|
||||
<RaceDayPreview
|
||||
template={showSprint ? 'sprintFeature' : 'feature'}
|
||||
practiceMin={timings.practiceMinutes ?? 20}
|
||||
qualifyingMin={timings.qualifyingMinutes ?? 15}
|
||||
sprintMin={showSprint ? (timings.sprintRaceMinutes ?? 20) : undefined}
|
||||
mainRaceMin={timings.mainRaceMinutes ?? 40}
|
||||
raceTime={timings.raceStartTime}
|
||||
/>
|
||||
)}
|
||||
{previewTab === 'day' && (() => {
|
||||
const sprintMinutes = showSprint
|
||||
? timings.sprintRaceMinutes ?? 20
|
||||
: undefined;
|
||||
return (
|
||||
<RaceDayPreview
|
||||
template={showSprint ? 'sprintFeature' : 'feature'}
|
||||
practiceMin={timings.practiceMinutes ?? 20}
|
||||
qualifyingMin={timings.qualifyingMinutes ?? 15}
|
||||
{...(sprintMinutes !== undefined
|
||||
? { sprintMin: sprintMinutes }
|
||||
: {})}
|
||||
mainRaceMin={timings.mainRaceMinutes ?? 40}
|
||||
{...(timings.raceStartTime
|
||||
? { raceTime: timings.raceStartTime }
|
||||
: {})}
|
||||
/>
|
||||
);
|
||||
})()}
|
||||
|
||||
{previewTab === 'year' && (
|
||||
<YearCalendarPreview
|
||||
weekdays={weekdays}
|
||||
frequency={recurrenceStrategy}
|
||||
rounds={timings.roundsPlanned ?? 8}
|
||||
startDate={timings.seasonStartDate}
|
||||
endDate={timings.seasonEndDate}
|
||||
{...(timings.seasonStartDate
|
||||
? { startDate: timings.seasonStartDate }
|
||||
: {})}
|
||||
{...(timings.seasonEndDate
|
||||
? { endDate: timings.seasonEndDate }
|
||||
: {})}
|
||||
/>
|
||||
)}
|
||||
|
||||
{previewTab === 'stats' && (
|
||||
<SeasonStatsPreview
|
||||
rounds={timings.roundsPlanned ?? 8}
|
||||
weekdays={weekdays}
|
||||
frequency={recurrenceStrategy}
|
||||
weekendTemplate={showSprint ? 'sprintFeature' : 'feature'}
|
||||
practiceMin={timings.practiceMinutes ?? 20}
|
||||
qualifyingMin={timings.qualifyingMinutes ?? 15}
|
||||
sprintMin={showSprint ? (timings.sprintRaceMinutes ?? 20) : undefined}
|
||||
mainRaceMin={timings.mainRaceMinutes ?? 40}
|
||||
/>
|
||||
)}
|
||||
{previewTab === 'stats' && (() => {
|
||||
const sprintMinutes = showSprint
|
||||
? timings.sprintRaceMinutes ?? 20
|
||||
: undefined;
|
||||
return (
|
||||
<SeasonStatsPreview
|
||||
rounds={timings.roundsPlanned ?? 8}
|
||||
weekdays={weekdays}
|
||||
frequency={recurrenceStrategy}
|
||||
weekendTemplate={showSprint ? 'sprintFeature' : 'feature'}
|
||||
practiceMin={timings.practiceMinutes ?? 20}
|
||||
qualifyingMin={timings.qualifyingMinutes ?? 15}
|
||||
{...(sprintMinutes !== undefined
|
||||
? { sprintMin: sprintMinutes }
|
||||
: {})}
|
||||
mainRaceMin={timings.mainRaceMinutes ?? 40}
|
||||
/>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user