This commit is contained in:
2025-12-13 11:43:09 +01:00
parent 4b6fc668b5
commit bb0497f429
38 changed files with 3838 additions and 55 deletions

View File

@@ -21,9 +21,11 @@ import {
Building2,
LogOut,
LogIn,
TrendingUp,
Award,
} from 'lucide-react';
type DemoNotificationType = 'protest_filed' | 'defense_requested' | 'vote_required';
type DemoNotificationType = 'protest_filed' | 'defense_requested' | 'vote_required' | 'race_performance_summary' | 'race_final_results';
type DemoUrgency = 'silent' | 'toast' | 'modal';
interface NotificationOption {
@@ -63,6 +65,20 @@ const notificationOptions: NotificationOption[] = [
icon: Vote,
color: 'text-primary-blue',
},
{
type: 'race_performance_summary',
label: 'Race Performance Summary',
description: 'Immediate results after main race',
icon: TrendingUp,
color: 'text-primary-blue',
},
{
type: 'race_final_results',
label: 'Race Final Results',
description: 'Final results after stewarding closes',
icon: Award,
color: 'text-warning-amber',
},
];
const urgencyOptions: UrgencyOption[] = [
@@ -81,7 +97,7 @@ const urgencyOptions: UrgencyOption[] = [
{
urgency: 'modal',
label: 'Modal',
description: 'Shows blocking modal (must respond)',
description: 'Shows blocking modal (may require response)',
icon: AlertCircle,
},
];
@@ -193,7 +209,7 @@ export default function DevToolbar() {
let title: string;
let body: string;
let notificationType: 'protest_filed' | 'protest_defense_requested' | 'protest_vote_required';
let notificationType: 'protest_filed' | 'protest_defense_requested' | 'protest_vote_required' | 'race_performance_summary' | 'race_final_results';
let actionUrl: string;
switch (selectedType) {
@@ -224,14 +240,38 @@ export default function DevToolbar() {
actionUrl = leagueId ? `/leagues/${leagueId}/stewarding` : '/leagues';
break;
}
case 'race_performance_summary': {
const raceId = primaryRace?.id;
const leagueId = primaryLeague?.id;
title = '🏁 Race Complete: Performance Summary';
body =
'Your Monza Grand Prix race is finished! You finished P1 with 0 incidents. Provisional rating: +25 points. View full results and standings.';
notificationType = 'race_performance_summary';
actionUrl = raceId ? `/races/${raceId}` : '/races';
break;
}
case 'race_final_results': {
const leagueId = primaryLeague?.id;
title = '🏆 Final Results: Monza Grand Prix';
body =
'Stewarding is now closed. Your final result: P1 (+25 rating). No penalties were applied. View championship standings.';
notificationType = 'race_final_results';
actionUrl = leagueId ? `/leagues/${leagueId}/standings` : '/leagues';
break;
}
}
const actions =
selectedUrgency === 'modal'
? [
{ label: 'View Protest', type: 'primary' as const, href: actionUrl, actionId: 'view' },
{ label: 'Dismiss', type: 'secondary' as const, actionId: 'dismiss' },
]
? selectedType.startsWith('race_')
? [
{ label: selectedType === 'race_performance_summary' ? '🏁 View Race Results' : '🏆 View Standings', type: 'primary' as const, href: actionUrl, actionId: 'view' },
{ label: '🎉 Share Achievement', type: 'secondary' as const, actionId: 'share' },
]
: [
{ label: 'View Protest', type: 'primary' as const, href: actionUrl, actionId: 'view' },
{ label: 'Dismiss', type: 'secondary' as const, actionId: 'dismiss' },
]
: [];
await sendNotification.execute({
@@ -241,13 +281,25 @@ export default function DevToolbar() {
body,
actionUrl,
urgency: selectedUrgency as NotificationUrgency,
requiresResponse: selectedUrgency === 'modal',
requiresResponse: selectedUrgency === 'modal' && !selectedType.startsWith('race_'),
actions,
data: {
protestId: `demo-protest-${Date.now()}`,
...(selectedType.startsWith('protest_') ? {
protestId: `demo-protest-${Date.now()}`,
} : {}),
...(selectedType.startsWith('race_') ? {
raceEventId: `demo-race-event-${Date.now()}`,
sessionId: `demo-session-${Date.now()}`,
position: 1,
positionChange: 0,
incidents: 0,
provisionalRatingChange: 25,
finalRatingChange: 25,
hadPenaltiesApplied: false,
} : {}),
raceId: primaryRace?.id ?? '',
leagueId: primaryLeague?.id ?? '',
...(notificationDeadline ? { deadline: notificationDeadline } : {}),
...(notificationDeadline && selectedType.startsWith('protest_') ? { deadline: notificationDeadline } : {}),
},
});
@@ -315,7 +367,7 @@ export default function DevToolbar() {
</span>
</div>
<div className="grid grid-cols-3 gap-1">
<div className="grid grid-cols-2 gap-1">
{notificationOptions.map((option) => {
const Icon = option.icon;
const isSelected = selectedType === option.type;
@@ -436,7 +488,7 @@ export default function DevToolbar() {
<p className="text-[10px] text-gray-500">
<strong className="text-gray-400">Silent:</strong> Notification center only<br/>
<strong className="text-gray-400">Toast:</strong> Temporary popup (auto-dismisses)<br/>
<strong className="text-gray-400">Modal:</strong> Blocking popup (requires action)
<strong className="text-gray-400">Modal:</strong> Blocking popup (may require action)
</p>
</div>