759 lines
29 KiB
TypeScript
759 lines
29 KiB
TypeScript
'use client';
|
||
|
||
import { useEffectiveDriverId } from '@/hooks/useEffectiveDriverId';
|
||
import { useNotifications } from '@/components/notifications/NotificationProvider';
|
||
import type { NotificationVariant } from '@/components/notifications/notificationTypes';
|
||
import {
|
||
AlertCircle,
|
||
AlertTriangle,
|
||
Award,
|
||
Bell,
|
||
BellRing,
|
||
Building2,
|
||
ChevronDown,
|
||
ChevronUp,
|
||
LogIn,
|
||
LogOut,
|
||
MessageSquare,
|
||
Shield,
|
||
TrendingUp,
|
||
User,
|
||
Vote,
|
||
Wrench,
|
||
X,
|
||
} from 'lucide-react';
|
||
import { useRouter } from 'next/navigation';
|
||
import { useEffect, useState } from 'react';
|
||
|
||
type DemoNotificationType = 'protest_filed' | 'defense_requested' | 'vote_required' | 'race_performance_summary' | 'race_final_results';
|
||
type DemoUrgency = 'silent' | 'toast' | 'modal';
|
||
|
||
interface NotificationOption {
|
||
type: DemoNotificationType;
|
||
label: string;
|
||
description: string;
|
||
icon: typeof Bell;
|
||
color: string;
|
||
}
|
||
|
||
interface UrgencyOption {
|
||
urgency: DemoUrgency;
|
||
label: string;
|
||
description: string;
|
||
icon: typeof Bell;
|
||
}
|
||
|
||
const notificationOptions: NotificationOption[] = [
|
||
{
|
||
type: 'protest_filed',
|
||
label: 'Protest Against You',
|
||
description: 'A protest was filed against you',
|
||
icon: AlertTriangle,
|
||
color: 'text-red-400',
|
||
},
|
||
{
|
||
type: 'defense_requested',
|
||
label: 'Defense Requested',
|
||
description: 'A steward requests your defense',
|
||
icon: Shield,
|
||
color: 'text-warning-amber',
|
||
},
|
||
{
|
||
type: 'vote_required',
|
||
label: 'Vote Required',
|
||
description: 'You need to vote on a protest',
|
||
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[] = [
|
||
{
|
||
urgency: 'silent',
|
||
label: 'Silent',
|
||
description: 'Only shows in notification center',
|
||
icon: Bell,
|
||
},
|
||
{
|
||
urgency: 'toast',
|
||
label: 'Toast',
|
||
description: 'Shows a temporary popup',
|
||
icon: BellRing,
|
||
},
|
||
{
|
||
urgency: 'modal',
|
||
label: 'Modal',
|
||
description: 'Shows blocking modal (may require response)',
|
||
icon: AlertCircle,
|
||
},
|
||
];
|
||
|
||
type LoginMode = 'none' | 'driver' | 'sponsor' | 'league-owner' | 'league-steward' | 'league-admin' | 'system-owner' | 'super-admin';
|
||
|
||
export default function DevToolbar() {
|
||
const router = useRouter();
|
||
const { addNotification } = useNotifications();
|
||
const [isExpanded, setIsExpanded] = useState(false);
|
||
const [isMinimized, setIsMinimized] = useState(false);
|
||
const [selectedType, setSelectedType] = useState<DemoNotificationType>('protest_filed');
|
||
const [selectedUrgency, setSelectedUrgency] = useState<DemoUrgency>('toast');
|
||
const [sending, setSending] = useState(false);
|
||
const [lastSent, setLastSent] = useState<string | null>(null);
|
||
const [loginMode, setLoginMode] = useState<LoginMode>('none');
|
||
const [loggingIn, setLoggingIn] = useState(false);
|
||
|
||
const currentDriverId = useEffectiveDriverId();
|
||
|
||
// Sync login mode with actual session state on mount
|
||
useEffect(() => {
|
||
if (typeof document !== 'undefined') {
|
||
// Check for actual session cookie first
|
||
const cookies = document.cookie.split(';');
|
||
const sessionCookie = cookies.find(c => c.trim().startsWith('gp_session='));
|
||
|
||
if (sessionCookie) {
|
||
// User has a session cookie, check if it's valid by calling the API
|
||
fetch('/api/auth/session', {
|
||
method: 'GET',
|
||
credentials: 'include'
|
||
})
|
||
.then(res => {
|
||
if (res.ok) {
|
||
return res.json();
|
||
}
|
||
throw new Error('No valid session');
|
||
})
|
||
.then(session => {
|
||
if (session && session.user) {
|
||
// Determine login mode based on user email patterns
|
||
const email = session.user.email?.toLowerCase() || '';
|
||
const displayName = session.user.displayName?.toLowerCase() || '';
|
||
|
||
let mode: LoginMode = 'none';
|
||
if (email.includes('sponsor') || displayName.includes('sponsor')) {
|
||
mode = 'sponsor';
|
||
} else if (email.includes('league-owner') || displayName.includes('owner')) {
|
||
mode = 'league-owner';
|
||
} else if (email.includes('league-steward') || displayName.includes('steward')) {
|
||
mode = 'league-steward';
|
||
} else if (email.includes('league-admin') || displayName.includes('admin')) {
|
||
mode = 'league-admin';
|
||
} else if (email.includes('system-owner') || displayName.includes('system owner')) {
|
||
mode = 'system-owner';
|
||
} else if (email.includes('super-admin') || displayName.includes('super admin')) {
|
||
mode = 'super-admin';
|
||
} else if (email.includes('driver') || displayName.includes('demo')) {
|
||
mode = 'driver';
|
||
}
|
||
|
||
setLoginMode(mode);
|
||
} else {
|
||
setLoginMode('none');
|
||
}
|
||
})
|
||
.catch(() => {
|
||
// Session invalid or expired
|
||
setLoginMode('none');
|
||
});
|
||
} else {
|
||
// No session cookie means not logged in
|
||
setLoginMode('none');
|
||
}
|
||
}
|
||
}, []);
|
||
|
||
const handleDemoLogin = async (role: LoginMode) => {
|
||
if (role === 'none') return;
|
||
|
||
setLoggingIn(true);
|
||
try {
|
||
// Use the demo login API
|
||
const response = await fetch('/api/auth/demo-login', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ role }),
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error('Demo login failed');
|
||
}
|
||
|
||
setLoginMode(role);
|
||
|
||
// Navigate based on role
|
||
if (role === 'sponsor') {
|
||
window.location.href = '/sponsor/dashboard';
|
||
} else {
|
||
// For driver and league roles, go to dashboard
|
||
window.location.href = '/dashboard';
|
||
}
|
||
} catch (error) {
|
||
console.error('Demo login failed:', error);
|
||
alert('Demo login failed. Please check the console for details.');
|
||
} finally {
|
||
setLoggingIn(false);
|
||
}
|
||
};
|
||
|
||
const handleLogout = async () => {
|
||
setLoggingIn(true);
|
||
try {
|
||
// Call logout API
|
||
await fetch('/api/auth/logout', { method: 'POST' });
|
||
|
||
setLoginMode('none');
|
||
// Refresh to update all components
|
||
window.location.href = '/';
|
||
} catch (error) {
|
||
console.error('Logout failed:', error);
|
||
alert('Logout failed. Please check the console for details.');
|
||
} finally {
|
||
setLoggingIn(false);
|
||
}
|
||
};
|
||
|
||
// Only show in development
|
||
if (process.env.NODE_ENV === 'production') {
|
||
return null;
|
||
}
|
||
|
||
const handleSendNotification = async () => {
|
||
setSending(true);
|
||
try {
|
||
const actionUrlByType: Record<DemoNotificationType, string> = {
|
||
protest_filed: '/races',
|
||
defense_requested: '/races',
|
||
vote_required: '/leagues',
|
||
race_performance_summary: '/races',
|
||
race_final_results: '/races',
|
||
};
|
||
|
||
const titleByType: Record<DemoNotificationType, string> = {
|
||
protest_filed: 'Protest Filed Against You',
|
||
defense_requested: 'Defense Requested',
|
||
vote_required: 'Vote Required',
|
||
race_performance_summary: 'Race Performance Summary',
|
||
race_final_results: 'Race Final Results',
|
||
};
|
||
|
||
const messageByType: Record<DemoNotificationType, string> = {
|
||
protest_filed: 'A protest has been filed against you. Please review the incident details.',
|
||
defense_requested: 'A steward requests your defense. Please respond within the deadline.',
|
||
vote_required: 'A protest vote is pending. Please review and vote.',
|
||
race_performance_summary: 'Your race is complete. View your provisional results.',
|
||
race_final_results: 'Stewarding is closed. Your final results are available.',
|
||
};
|
||
|
||
const notificationTypeByDemoType: Record<DemoNotificationType, string> = {
|
||
protest_filed: 'protest_filed',
|
||
defense_requested: 'protest_defense_requested',
|
||
vote_required: 'protest_vote_required',
|
||
race_performance_summary: 'race_performance_summary',
|
||
race_final_results: 'race_final_results',
|
||
};
|
||
|
||
const variant: NotificationVariant = selectedUrgency === 'modal' ? 'modal' : 'toast';
|
||
|
||
addNotification({
|
||
type: notificationTypeByDemoType[selectedType],
|
||
title: titleByType[selectedType],
|
||
message: messageByType[selectedType],
|
||
variant,
|
||
actionUrl: actionUrlByType[selectedType],
|
||
data: {
|
||
driverId: currentDriverId,
|
||
demo: true,
|
||
},
|
||
});
|
||
|
||
setLastSent(`${selectedType}-${selectedUrgency}`);
|
||
setTimeout(() => setLastSent(null), 3000);
|
||
} catch (error) {
|
||
console.error('Failed to send demo notification:', error);
|
||
} finally {
|
||
setSending(false);
|
||
}
|
||
};
|
||
|
||
// const handleSendNotification = async () => {
|
||
// setSending(true);
|
||
// try {
|
||
// const sendNotification = getSendNotificationUseCase();
|
||
|
||
// const raceRepository = getRaceRepository();
|
||
// const leagueRepository = getLeagueRepository();
|
||
|
||
// const [allRaces, allLeagues] = await Promise.all([
|
||
// raceRepository.findAll(),
|
||
// leagueRepository.findAll(),
|
||
// ]);
|
||
|
||
// const completedRaces = allRaces.filter((race) => race.status === 'completed');
|
||
// const scheduledRaces = allRaces.filter((race) => race.status === 'scheduled');
|
||
|
||
// const primaryRace = completedRaces[0] ?? allRaces[0];
|
||
// const secondaryRace = scheduledRaces[0] ?? allRaces[1] ?? primaryRace;
|
||
// const primaryLeague = allLeagues[0];
|
||
|
||
// const notificationDeadline =
|
||
// selectedUrgency === 'modal'
|
||
// ? new Date(Date.now() + 48 * 60 * 60 * 1000)
|
||
// : undefined;
|
||
|
||
// let title: string;
|
||
// let body: string;
|
||
// let notificationType: 'protest_filed' | 'protest_defense_requested' | 'protest_vote_required' | 'race_performance_summary' | 'race_final_results';
|
||
// let actionUrl: string;
|
||
|
||
// switch (selectedType) {
|
||
// case 'protest_filed': {
|
||
// const raceId = primaryRace?.id;
|
||
// title = '🚨 Protest Filed Against You';
|
||
// body =
|
||
// 'A protest has been filed against you for unsafe rejoining during a recent race. Please review the incident details.';
|
||
// notificationType = 'protest_filed';
|
||
// actionUrl = raceId ? `/races/${raceId}/stewarding` : '/races';
|
||
// break;
|
||
// }
|
||
// case 'defense_requested': {
|
||
// const raceId = secondaryRace?.id ?? primaryRace?.id;
|
||
// title = '⚖️ Defense Requested';
|
||
// body =
|
||
// 'A steward has requested your defense regarding a recent incident. Please provide your side of the story within 48 hours.';
|
||
// notificationType = 'protest_defense_requested';
|
||
// actionUrl = raceId ? `/races/${raceId}/stewarding` : '/races';
|
||
// break;
|
||
// }
|
||
// case 'vote_required': {
|
||
// const leagueId = primaryLeague?.id;
|
||
// title = '🗳️ Your Vote Required';
|
||
// body =
|
||
// 'As a league steward, you are required to vote on an open protest. Please review the case and cast your vote.';
|
||
// notificationType = 'protest_vote_required';
|
||
// 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'
|
||
// ? 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({
|
||
// recipientId: currentDriverId,
|
||
// type: notificationType,
|
||
// title,
|
||
// body,
|
||
// actionUrl,
|
||
// urgency: selectedUrgency as NotificationUrgency,
|
||
// requiresResponse: selectedUrgency === 'modal' && !selectedType.startsWith('race_'),
|
||
// actions,
|
||
// data: {
|
||
// ...(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 && selectedType.startsWith('protest_') ? { deadline: notificationDeadline } : {}),
|
||
// },
|
||
// });
|
||
|
||
// setLastSent(`${selectedType}-${selectedUrgency}`);
|
||
// setTimeout(() => setLastSent(null), 3000);
|
||
// } catch (error) {
|
||
// console.error('Failed to send demo notification:', error);
|
||
// } finally {
|
||
// setSending(false);
|
||
// }
|
||
// };
|
||
|
||
if (isMinimized) {
|
||
return (
|
||
<button
|
||
onClick={() => setIsMinimized(false)}
|
||
className="fixed bottom-4 right-4 z-50 p-3 bg-iron-gray border border-charcoal-outline rounded-full shadow-lg hover:bg-charcoal-outline transition-colors"
|
||
title="Open Dev Toolbar"
|
||
>
|
||
<Wrench className="w-5 h-5 text-primary-blue" />
|
||
</button>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div className="fixed bottom-4 right-4 z-50 w-80 bg-deep-graphite border border-charcoal-outline rounded-xl shadow-2xl overflow-hidden">
|
||
{/* Header */}
|
||
<div className="flex items-center justify-between px-4 py-3 bg-iron-gray/50 border-b border-charcoal-outline">
|
||
<div className="flex items-center gap-2">
|
||
<Wrench className="w-4 h-4 text-primary-blue" />
|
||
<span className="text-sm font-semibold text-white">Dev Toolbar</span>
|
||
<span className="px-1.5 py-0.5 text-[10px] font-medium bg-primary-blue/20 text-primary-blue rounded">
|
||
DEMO
|
||
</span>
|
||
</div>
|
||
<div className="flex items-center gap-1">
|
||
<button
|
||
onClick={() => setIsExpanded(!isExpanded)}
|
||
className="p-1.5 hover:bg-charcoal-outline rounded transition-colors"
|
||
>
|
||
{isExpanded ? (
|
||
<ChevronDown className="w-4 h-4 text-gray-400" />
|
||
) : (
|
||
<ChevronUp className="w-4 h-4 text-gray-400" />
|
||
)}
|
||
</button>
|
||
<button
|
||
onClick={() => setIsMinimized(true)}
|
||
className="p-1.5 hover:bg-charcoal-outline rounded transition-colors"
|
||
>
|
||
<X className="w-4 h-4 text-gray-400" />
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Content */}
|
||
{isExpanded && (
|
||
<div className="p-4 space-y-4">
|
||
{/* Notification Type Section */}
|
||
<div>
|
||
<div className="flex items-center gap-2 mb-2">
|
||
<MessageSquare className="w-4 h-4 text-gray-400" />
|
||
<span className="text-xs font-semibold text-gray-400 uppercase tracking-wide">
|
||
Notification Type
|
||
</span>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-2 gap-1">
|
||
{notificationOptions.map((option) => {
|
||
const Icon = option.icon;
|
||
const isSelected = selectedType === option.type;
|
||
|
||
return (
|
||
<button
|
||
key={option.type}
|
||
onClick={() => setSelectedType(option.type)}
|
||
className={`
|
||
flex flex-col items-center gap-1 p-2 rounded-lg border transition-all text-center
|
||
${isSelected
|
||
? 'bg-primary-blue/20 border-primary-blue/50'
|
||
: 'bg-iron-gray/30 border-charcoal-outline hover:bg-iron-gray/50'
|
||
}
|
||
`}
|
||
>
|
||
<Icon className={`w-4 h-4 ${isSelected ? 'text-primary-blue' : option.color}`} />
|
||
<span className={`text-[10px] font-medium ${isSelected ? 'text-primary-blue' : 'text-gray-400'}`}>
|
||
{option.label.split(' ')[0]}
|
||
</span>
|
||
</button>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Urgency Section */}
|
||
<div>
|
||
<div className="flex items-center gap-2 mb-2">
|
||
<BellRing className="w-4 h-4 text-gray-400" />
|
||
<span className="text-xs font-semibold text-gray-400 uppercase tracking-wide">
|
||
Urgency Level
|
||
</span>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-3 gap-1">
|
||
{urgencyOptions.map((option) => {
|
||
const Icon = option.icon;
|
||
const isSelected = selectedUrgency === option.urgency;
|
||
|
||
return (
|
||
<button
|
||
key={option.urgency}
|
||
onClick={() => setSelectedUrgency(option.urgency)}
|
||
className={`
|
||
flex flex-col items-center gap-1 p-2 rounded-lg border transition-all text-center
|
||
${isSelected
|
||
? option.urgency === 'modal'
|
||
? 'bg-red-500/20 border-red-500/50'
|
||
: option.urgency === 'toast'
|
||
? 'bg-warning-amber/20 border-warning-amber/50'
|
||
: 'bg-gray-500/20 border-gray-500/50'
|
||
: 'bg-iron-gray/30 border-charcoal-outline hover:bg-iron-gray/50'
|
||
}
|
||
`}
|
||
>
|
||
<Icon className={`w-4 h-4 ${
|
||
isSelected
|
||
? option.urgency === 'modal'
|
||
? 'text-red-400'
|
||
: option.urgency === 'toast'
|
||
? 'text-warning-amber'
|
||
: 'text-gray-400'
|
||
: 'text-gray-500'
|
||
}`} />
|
||
<span className={`text-[10px] font-medium ${
|
||
isSelected
|
||
? option.urgency === 'modal'
|
||
? 'text-red-400'
|
||
: option.urgency === 'toast'
|
||
? 'text-warning-amber'
|
||
: 'text-gray-400'
|
||
: 'text-gray-500'
|
||
}`}>
|
||
{option.label}
|
||
</span>
|
||
</button>
|
||
);
|
||
})}
|
||
</div>
|
||
<p className="text-[10px] text-gray-600 mt-1">
|
||
{urgencyOptions.find(o => o.urgency === selectedUrgency)?.description}
|
||
</p>
|
||
</div>
|
||
|
||
{/* Send Button */}
|
||
<button
|
||
onClick={handleSendNotification}
|
||
disabled={sending}
|
||
className={`
|
||
w-full flex items-center justify-center gap-2 py-2.5 rounded-lg font-medium text-sm transition-all
|
||
${lastSent
|
||
? 'bg-performance-green/20 border border-performance-green/30 text-performance-green'
|
||
: 'bg-primary-blue hover:bg-primary-blue/80 text-white'
|
||
}
|
||
disabled:opacity-50 disabled:cursor-not-allowed
|
||
`}
|
||
>
|
||
{sending ? (
|
||
<>
|
||
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
||
Sending...
|
||
</>
|
||
) : lastSent ? (
|
||
<>
|
||
✓ Notification Sent!
|
||
</>
|
||
) : (
|
||
<>
|
||
<Bell className="w-4 h-4" />
|
||
Send Demo Notification
|
||
</>
|
||
)}
|
||
</button>
|
||
|
||
{/* Info */}
|
||
<div className="p-3 rounded-lg bg-iron-gray/50 border border-charcoal-outline">
|
||
<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 (may require action)
|
||
</p>
|
||
</div>
|
||
|
||
{/* Login Section */}
|
||
<div className="pt-4 border-t border-charcoal-outline">
|
||
<div className="flex items-center gap-2 mb-3">
|
||
<LogIn className="w-4 h-4 text-gray-400" />
|
||
<span className="text-xs font-semibold text-gray-400 uppercase tracking-wide">
|
||
Demo Login
|
||
</span>
|
||
</div>
|
||
|
||
<div className="space-y-2">
|
||
{/* Driver Login */}
|
||
<button
|
||
onClick={() => handleDemoLogin('driver')}
|
||
disabled={loggingIn || loginMode === 'driver'}
|
||
className={`
|
||
w-full flex items-center gap-2 px-3 py-2 rounded-lg border text-sm font-medium transition-all
|
||
${loginMode === 'driver'
|
||
? 'bg-primary-blue/20 border-primary-blue/50 text-primary-blue'
|
||
: 'bg-iron-gray/30 border-charcoal-outline text-gray-300 hover:bg-iron-gray/50'
|
||
}
|
||
disabled:opacity-50 disabled:cursor-not-allowed
|
||
`}
|
||
>
|
||
<User className="w-4 h-4" />
|
||
{loginMode === 'driver' ? '✓ Driver' : 'Login as Driver'}
|
||
</button>
|
||
|
||
{/* League Owner Login */}
|
||
<button
|
||
onClick={() => handleDemoLogin('league-owner')}
|
||
disabled={loggingIn || loginMode === 'league-owner'}
|
||
className={`
|
||
w-full flex items-center gap-2 px-3 py-2 rounded-lg border text-sm font-medium transition-all
|
||
${loginMode === 'league-owner'
|
||
? 'bg-purple-500/20 border-purple-500/50 text-purple-400'
|
||
: 'bg-iron-gray/30 border-charcoal-outline text-gray-300 hover:bg-iron-gray/50'
|
||
}
|
||
disabled:opacity-50 disabled:cursor-not-allowed
|
||
`}
|
||
>
|
||
<span className="text-xs">👑</span>
|
||
{loginMode === 'league-owner' ? '✓ League Owner' : 'Login as League Owner'}
|
||
</button>
|
||
|
||
{/* League Steward Login */}
|
||
<button
|
||
onClick={() => handleDemoLogin('league-steward')}
|
||
disabled={loggingIn || loginMode === 'league-steward'}
|
||
className={`
|
||
w-full flex items-center gap-2 px-3 py-2 rounded-lg border text-sm font-medium transition-all
|
||
${loginMode === 'league-steward'
|
||
? 'bg-amber-500/20 border-amber-500/50 text-amber-400'
|
||
: 'bg-iron-gray/30 border-charcoal-outline text-gray-300 hover:bg-iron-gray/50'
|
||
}
|
||
disabled:opacity-50 disabled:cursor-not-allowed
|
||
`}
|
||
>
|
||
<Shield className="w-4 h-4" />
|
||
{loginMode === 'league-steward' ? '✓ Steward' : 'Login as Steward'}
|
||
</button>
|
||
|
||
{/* League Admin Login */}
|
||
<button
|
||
onClick={() => handleDemoLogin('league-admin')}
|
||
disabled={loggingIn || loginMode === 'league-admin'}
|
||
className={`
|
||
w-full flex items-center gap-2 px-3 py-2 rounded-lg border text-sm font-medium transition-all
|
||
${loginMode === 'league-admin'
|
||
? 'bg-red-500/20 border-red-500/50 text-red-400'
|
||
: 'bg-iron-gray/30 border-charcoal-outline text-gray-300 hover:bg-iron-gray/50'
|
||
}
|
||
disabled:opacity-50 disabled:cursor-not-allowed
|
||
`}
|
||
>
|
||
<span className="text-xs">⚙️</span>
|
||
{loginMode === 'league-admin' ? '✓ Admin' : 'Login as Admin'}
|
||
</button>
|
||
|
||
{/* Sponsor Login */}
|
||
<button
|
||
onClick={() => handleDemoLogin('sponsor')}
|
||
disabled={loggingIn || loginMode === 'sponsor'}
|
||
className={`
|
||
w-full flex items-center gap-2 px-3 py-2 rounded-lg border text-sm font-medium transition-all
|
||
${loginMode === 'sponsor'
|
||
? 'bg-performance-green/20 border-performance-green/50 text-performance-green'
|
||
: 'bg-iron-gray/30 border-charcoal-outline text-gray-300 hover:bg-iron-gray/50'
|
||
}
|
||
disabled:opacity-50 disabled:cursor-not-allowed
|
||
`}
|
||
>
|
||
<Building2 className="w-4 h-4" />
|
||
{loginMode === 'sponsor' ? '✓ Sponsor' : 'Login as Sponsor'}
|
||
</button>
|
||
|
||
{/* System Owner Login */}
|
||
<button
|
||
onClick={() => handleDemoLogin('system-owner')}
|
||
disabled={loggingIn || loginMode === 'system-owner'}
|
||
className={`
|
||
w-full flex items-center gap-2 px-3 py-2 rounded-lg border text-sm font-medium transition-all
|
||
${loginMode === 'system-owner'
|
||
? 'bg-indigo-500/20 border-indigo-500/50 text-indigo-400'
|
||
: 'bg-iron-gray/30 border-charcoal-outline text-gray-300 hover:bg-iron-gray/50'
|
||
}
|
||
disabled:opacity-50 disabled:cursor-not-allowed
|
||
`}
|
||
>
|
||
<span className="text-xs">👑</span>
|
||
{loginMode === 'system-owner' ? '✓ System Owner' : 'Login as System Owner'}
|
||
</button>
|
||
|
||
{/* Super Admin Login */}
|
||
<button
|
||
onClick={() => handleDemoLogin('super-admin')}
|
||
disabled={loggingIn || loginMode === 'super-admin'}
|
||
className={`
|
||
w-full flex items-center gap-2 px-3 py-2 rounded-lg border text-sm font-medium transition-all
|
||
${loginMode === 'super-admin'
|
||
? 'bg-pink-500/20 border-pink-500/50 text-pink-400'
|
||
: 'bg-iron-gray/30 border-charcoal-outline text-gray-300 hover:bg-iron-gray/50'
|
||
}
|
||
disabled:opacity-50 disabled:cursor-not-allowed
|
||
`}
|
||
>
|
||
<span className="text-xs">⚡</span>
|
||
{loginMode === 'super-admin' ? '✓ Super Admin' : 'Login as Super Admin'}
|
||
</button>
|
||
|
||
{loginMode !== 'none' && (
|
||
<button
|
||
onClick={handleLogout}
|
||
disabled={loggingIn}
|
||
className="w-full flex items-center gap-2 px-3 py-2 rounded-lg border border-red-500/30 bg-red-500/10 text-red-400 text-sm font-medium hover:bg-red-500/20 transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
||
>
|
||
<LogOut className="w-4 h-4" />
|
||
Logout
|
||
</button>
|
||
)}
|
||
</div>
|
||
|
||
<p className="text-[10px] text-gray-600 mt-2">
|
||
Test different user roles for demo purposes. Dashboard works for all roles.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Collapsed state hint */}
|
||
{!isExpanded && (
|
||
<div className="px-4 py-2 text-xs text-gray-500">
|
||
Click ↑ to expand dev tools
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|