pmtool / src /components /GlobalWorkNotifications.tsx
devarshia5's picture
Upload 487 files
d97b8f9 verified
Raw
History Blame Contribute Delete
11.3 kB
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>
)}
</>
);
}