Spaces:
Sleeping
Sleeping
| import React, { useState, useEffect, useRef } from "react"; | |
| import { Lock, Coffee, Users, Mail, PanelRight, X } from "lucide-react"; | |
| import { toast } from "sonner"; | |
| import { Button } from "@/components/ui/button"; | |
| import { Switch } from "@/components/ui/switch"; | |
| interface ScheduleItem { | |
| time: string; | |
| activity: string; | |
| type: "focused" | "collaborative" | "rest" | "light"; | |
| icon: React.ReactNode; | |
| } | |
| export default function GlobalWorkNotifications() { | |
| const [currentTime, setCurrentTime] = useState<Date>(new Date()); | |
| const [lastNotifiedIndex, setLastNotifiedIndex] = useState<number>(-1); | |
| const [showNotificationModal, setShowNotificationModal] = useState<boolean>(false); | |
| const [notificationsEnabled, setNotificationsEnabled] = useState<boolean>(() => { | |
| if (typeof window !== 'undefined') { | |
| const saved = localStorage.getItem('globalWorkNotificationsEnabled'); | |
| return saved !== null ? saved === 'true' : true; | |
| } | |
| return true; | |
| }); | |
| const [currentNotification, setCurrentNotification] = useState<{ | |
| title: string; | |
| description: string; | |
| type: "focused" | "collaborative" | "rest" | "light"; | |
| } | null>(null); | |
| const notificationAudioRef = useRef<HTMLAudioElement | null>(null); | |
| // Initialize audio element | |
| useEffect(() => { | |
| const audio = new Audio('/notification.mp3'); | |
| audio.preload = 'auto'; | |
| audio.volume = 1.0; // Full volume | |
| notificationAudioRef.current = audio; | |
| return () => { | |
| if (notificationAudioRef.current) { | |
| notificationAudioRef.current.pause(); | |
| notificationAudioRef.current = null; | |
| } | |
| }; | |
| }, []); | |
| // Save notification preferences | |
| useEffect(() => { | |
| localStorage.setItem('globalWorkNotificationsEnabled', notificationsEnabled.toString()); | |
| }, [notificationsEnabled]); | |
| // Update current time every minute | |
| useEffect(() => { | |
| const timer = setInterval(() => { | |
| setCurrentTime(new Date()); | |
| }, 60000); | |
| return () => clearInterval(timer); | |
| }, []); | |
| // Daily schedule data | |
| const scheduleItems: ScheduleItem[] = [ | |
| { time: "9:30 AM – 10:30 AM", activity: "Team Collaboration / Planning", type: "light", icon: <Users className="h-4 w-4" /> }, | |
| { time: "10:30 AM – 11:30 AM", activity: "Deep Work Session 1", type: "focused", icon: <Lock className="h-4 w-4" /> }, | |
| { time: "11:30 AM – 12:00 PM", activity: "Team Discussion / Quick Sync", type: "collaborative", icon: <Users className="h-4 w-4" /> }, | |
| { time: "12:00 PM – 1:00 PM", activity: "Deep Work Session 2", type: "focused", icon: <Lock className="h-4 w-4" /> }, | |
| { time: "1:00 PM – 1:45 PM", activity: "Lunch Break", type: "rest", icon: <Coffee className="h-4 w-4" /> }, | |
| { time: "1:45 PM – 2:30 PM", activity: "Team Collaboration / Emails", type: "light", icon: <Mail className="h-4 w-4" /> }, | |
| { time: "2:30 PM – 3:30 PM", activity: "Deep Work Session 3", type: "focused", icon: <Lock className="h-4 w-4" /> }, | |
| { time: "3:30 PM – 4:00 PM", activity: "Team Discussion / Admin Tasks", type: "collaborative", icon: <PanelRight className="h-4 w-4" /> }, | |
| { time: "4:00 PM – 4:15 PM", activity: "Tea Break", type: "rest", icon: <Coffee className="h-4 w-4" /> }, | |
| { time: "4:15 PM – 4:30 PM", activity: "Flex / Light Catch-Up", type: "light", icon: <Users className="h-4 w-4" /> }, | |
| { time: "4:30 PM – 5:30 PM", activity: "Deep Work Session 4", type: "focused", icon: <Lock className="h-4 w-4" /> }, | |
| { time: "5:30 PM – 6:30 PM", activity: "Team Support / Wrap-Up", type: "light", icon: <Users className="h-4 w-4" /> }, | |
| ]; | |
| const playNotificationSound = () => { | |
| if (notificationAudioRef.current && notificationsEnabled) { | |
| try { | |
| notificationAudioRef.current.currentTime = 0; | |
| notificationAudioRef.current.play().catch(err => { | |
| console.log("Could not play notification sound:", err); | |
| }); | |
| } catch (error) { | |
| console.error("Error playing notification sound:", error); | |
| } | |
| } | |
| }; | |
| const showNotification = (item: ScheduleItem) => { | |
| if (!notificationsEnabled) return; | |
| playNotificationSound(); | |
| const title = `Time for ${item.activity}`; | |
| const description = `${item.time} - ${ | |
| item.type === "focused" ? "Focus on deep work" : | |
| item.type === "collaborative" ? "Connect with your team" : | |
| item.type === "rest" ? "Take a break to recharge" : | |
| "Handle lighter tasks" | |
| }`; | |
| toast.info(title, { description }); | |
| setCurrentNotification({ title, description, type: item.type }); | |
| setShowNotificationModal(true); | |
| document.body.classList.add('overflow-hidden'); | |
| }; | |
| // Check current activity and show notifications only when time changes | |
| useEffect(() => { | |
| const now = currentTime; | |
| const hour = now.getHours(); | |
| const minute = now.getMinutes(); | |
| const currentTimeInMinutes = hour * 60 + minute; | |
| const index = scheduleItems.findIndex((item) => { | |
| const [startTime, endTime] = item.time.split(" – "); | |
| const [startHour, startMinutePart] = startTime.split(":"); | |
| const startMinute = parseInt(startMinutePart); | |
| const startHourNum = parseInt(startHour) + (startTime.includes("PM") && parseInt(startHour) !== 12 ? 12 : 0); | |
| const startTimeInMinutes = startHourNum * 60 + startMinute; | |
| const [endHour, endMinutePart] = endTime.split(":"); | |
| const endMinute = parseInt(endMinutePart); | |
| const endHourNum = parseInt(endHour) + (endTime.includes("PM") && parseInt(endHour) !== 12 ? 12 : 0); | |
| const endTimeInMinutes = endHourNum * 60 + endMinute; | |
| return currentTimeInMinutes >= startTimeInMinutes && currentTimeInMinutes < endTimeInMinutes; | |
| }); | |
| // Only show notification if this is not the initial load and the activity has changed | |
| const isInitialLoad = lastNotifiedIndex === -1; | |
| if (index !== -1) { | |
| if (!isInitialLoad && index !== lastNotifiedIndex && notificationsEnabled) { | |
| console.log('Time-based notification triggered for:', scheduleItems[index].activity); | |
| showNotification(scheduleItems[index]); | |
| } | |
| setLastNotifiedIndex(index); | |
| } else if (isInitialLoad) { | |
| // If no current activity on initial load, set lastNotifiedIndex to avoid future false triggers | |
| setLastNotifiedIndex(-2); | |
| } | |
| }, [currentTime, scheduleItems, notificationsEnabled]); | |
| const getModalStyles = (type: "focused" | "collaborative" | "rest" | "light") => { | |
| switch (type) { | |
| case "focused": return "bg-purple-50 border-purple-200 text-purple-800"; | |
| case "collaborative": return "bg-blue-50 border-blue-200 text-blue-800"; | |
| case "rest": return "bg-red-50 border-red-200 text-red-800"; | |
| case "light": return "bg-green-50 border-green-200 text-green-800"; | |
| default: return ""; | |
| } | |
| }; | |
| const getModalIcon = (type: "focused" | "collaborative" | "rest" | "light") => { | |
| switch (type) { | |
| case "focused": return <Lock className="h-12 w-12 text-purple-600" />; | |
| case "collaborative": return <Users className="h-12 w-12 text-blue-600" />; | |
| case "rest": return <Coffee className="h-12 w-12 text-red-600" />; | |
| case "light": return <Mail className="h-12 w-12 text-green-600" />; | |
| default: return null; | |
| } | |
| }; | |
| const closeModal = () => { | |
| setShowNotificationModal(false); | |
| document.body.classList.remove('overflow-hidden'); | |
| }; | |
| useEffect(() => { | |
| const handleEscapeKey = (event: KeyboardEvent) => { | |
| if (event.key === 'Escape' && showNotificationModal) { | |
| closeModal(); | |
| } | |
| }; | |
| document.addEventListener('keydown', handleEscapeKey); | |
| return () => document.removeEventListener('keydown', handleEscapeKey); | |
| }, [showNotificationModal]); | |
| return ( | |
| <> | |
| {showNotificationModal && currentNotification && ( | |
| <div className="fixed inset-0 z-50 flex items-center justify-center"> | |
| <div className="absolute inset-0 bg-black bg-opacity-50 backdrop-blur-sm" onClick={closeModal} /> | |
| <div className={`relative mx-4 w-full max-w-md rounded-lg border shadow-lg transform transition-all ${getModalStyles(currentNotification.type)}`}> | |
| <button onClick={closeModal} className="absolute right-4 top-4 rounded-full p-1 text-gray-500 hover:bg-gray-100"> | |
| <X className="h-5 w-5" /> | |
| </button> | |
| <div className="p-6"> | |
| <div className="flex items-start gap-4"> | |
| <div className="p-3 rounded-full bg-white shadow-md"> | |
| {getModalIcon(currentNotification.type)} | |
| </div> | |
| <div className="flex-1 pt-2"> | |
| <h2 className="text-xl font-bold">{currentNotification.title}</h2> | |
| <p className="text-base mt-1 opacity-90">{currentNotification.description}</p> | |
| </div> | |
| </div> | |
| <div className="mt-6 p-4 rounded-md bg-white bg-opacity-50"> | |
| {currentNotification.type === "focused" ? ( | |
| <p className="text-sm">🧠 Time to focus! Close distractions, silence notifications, and dedicate this time to deep, meaningful work.</p> | |
| ) : currentNotification.type === "collaborative" ? ( | |
| <p className="text-sm">👥 Connect with your team! This is a good time for meetings, discussions, and collaborative problem-solving.</p> | |
| ) : currentNotification.type === "rest" ? ( | |
| <p className="text-sm">🔋 Take a break! Step away from your desk, stretch, or grab a refreshment to recharge your energy.</p> | |
| ) : ( | |
| <p className="text-sm">📋 Handle lighter tasks! This is a good time for emails, planning, and administrative tasks.</p> | |
| )} | |
| </div> | |
| <div className="mt-6"> | |
| <div className="flex items-center gap-2 mb-4"> | |
| <Switch | |
| id="global-notification-toggle" | |
| checked={notificationsEnabled} | |
| onCheckedChange={setNotificationsEnabled} | |
| /> | |
| <label htmlFor="global-notification-toggle" className="text-sm cursor-pointer"> | |
| {notificationsEnabled ? "Notifications On" : "Notifications Off"} | |
| </label> | |
| </div> | |
| <Button | |
| onClick={closeModal} | |
| className={`font-semibold w-full ${ | |
| currentNotification.type === "focused" ? "bg-purple-600 hover:bg-purple-700" : | |
| currentNotification.type === "collaborative" ? "bg-blue-600 hover:bg-blue-700" : | |
| currentNotification.type === "rest" ? "bg-red-600 hover:bg-red-700" : | |
| "bg-green-600 hover:bg-green-700" | |
| } text-white`} | |
| > | |
| Acknowledge | |
| </Button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| )} | |
| </> | |
| ); | |
| } |