feat: comprehensive update with Signal, Copilot, themes, and PDF features
## Signal Messaging Integration - Added SignalService for sending messages to drivers via Signal - SignalMessage model for tracking message history - Driver chat modal for real-time messaging - Send schedule via Signal (ICS + PDF attachments) ## AI Copilot - Natural language interface for VIP Coordinator - Capabilities: create VIPs, schedule events, assign drivers - Help and guidance for users - Floating copilot button in UI ## Theme System - Dark/light/system theme support - Color scheme selection (blue, green, purple, orange, red) - ThemeContext for global state - AppearanceMenu in header ## PDF Schedule Export - VIPSchedulePDF component for schedule generation - PDF settings (header, footer, branding) - Preview PDF in browser - Settings stored in database ## Database Migrations - add_signal_messages: SignalMessage model - add_pdf_settings: Settings model for PDF config - add_reminder_tracking: lastReminderSent for events - make_driver_phone_optional: phone field nullable ## Event Management - Event status service for automated updates - IN_PROGRESS/COMPLETED status tracking - Reminder tracking for notifications ## UI/UX Improvements - Driver schedule modal - Improved My Schedule page - Better error handling and loading states - Responsive design improvements ## Other Changes - AGENT_TEAM.md documentation - Seed data improvements - Ability factory updates - Driver profile page Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
65
frontend/src/components/ThemeToggle.tsx
Normal file
65
frontend/src/components/ThemeToggle.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import { Sun, Moon, Monitor } from 'lucide-react';
|
||||
import { useTheme, ThemeMode } from '@/hooks/useTheme';
|
||||
|
||||
const modes: { value: ThemeMode; label: string; icon: typeof Sun }[] = [
|
||||
{ value: 'light', label: 'Light', icon: Sun },
|
||||
{ value: 'dark', label: 'Dark', icon: Moon },
|
||||
{ value: 'system', label: 'System', icon: Monitor },
|
||||
];
|
||||
|
||||
export function ThemeToggle() {
|
||||
const { mode, resolvedTheme, setMode } = useTheme();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Close dropdown when clicking outside
|
||||
useEffect(() => {
|
||||
function handleClickOutside(event: MouseEvent) {
|
||||
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
}
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||
}, []);
|
||||
|
||||
// Get current icon based on resolved theme
|
||||
const CurrentIcon = resolvedTheme === 'dark' ? Moon : Sun;
|
||||
|
||||
return (
|
||||
<div className="relative" ref={dropdownRef}>
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className="flex items-center justify-center w-9 h-9 rounded-lg bg-muted hover:bg-accent transition-colors focus-ring"
|
||||
aria-label={`Current theme: ${mode}. Click to change.`}
|
||||
aria-expanded={isOpen}
|
||||
aria-haspopup="true"
|
||||
>
|
||||
<CurrentIcon className="h-5 w-5 text-foreground" />
|
||||
</button>
|
||||
|
||||
{isOpen && (
|
||||
<div className="absolute right-0 mt-2 w-36 rounded-lg bg-popover border border-border shadow-elevated z-50 overflow-hidden animate-in fade-in slide-in-from-top-2 duration-150">
|
||||
{modes.map(({ value, label, icon: Icon }) => (
|
||||
<button
|
||||
key={value}
|
||||
onClick={() => {
|
||||
setMode(value);
|
||||
setIsOpen(false);
|
||||
}}
|
||||
className={`flex items-center gap-3 w-full px-3 py-2.5 text-sm transition-colors ${
|
||||
mode === value
|
||||
? 'bg-primary/10 text-primary font-medium'
|
||||
: 'text-popover-foreground hover:bg-accent'
|
||||
}`}
|
||||
>
|
||||
<Icon className="h-4 w-4" />
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user