402 lines
20 KiB
TypeScript
402 lines
20 KiB
TypeScript
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||
import { Button } from '@/components/ui/button';
|
||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||
import {
|
||
Settings as SettingsIcon,
|
||
Bell,
|
||
Shield,
|
||
Palette,
|
||
Lock,
|
||
Calendar,
|
||
Sliders
|
||
} from 'lucide-react';
|
||
import { setupPushNotifications } from '@/utils/pushNotifications';
|
||
import { useAuth, isAdmin as checkIsAdmin } from '@/contexts/AuthContext';
|
||
import { ConfigurationManager } from '@/components/admin/ConfigurationManager';
|
||
import { HolidayManager } from '@/components/admin/HolidayManager';
|
||
import { UserRoleManager } from '@/components/admin/UserRoleManager';
|
||
import { NotificationStatusModal } from '@/components/settings/NotificationStatusModal';
|
||
import { useState } from 'react';
|
||
|
||
export function Settings() {
|
||
const { user } = useAuth();
|
||
const isAdmin = checkIsAdmin(user);
|
||
const [showNotificationModal, setShowNotificationModal] = useState(false);
|
||
const [notificationSuccess, setNotificationSuccess] = useState(false);
|
||
const [notificationMessage, setNotificationMessage] = useState<string>();
|
||
const [isEnablingNotifications, setIsEnablingNotifications] = useState(false);
|
||
|
||
const handleEnableNotifications = async () => {
|
||
setIsEnablingNotifications(true);
|
||
setShowNotificationModal(false);
|
||
|
||
try {
|
||
// Check if notifications are supported
|
||
if (!('Notification' in window)) {
|
||
setNotificationSuccess(false);
|
||
setNotificationMessage('Notifications are not supported in this browser. Please use a modern browser like Chrome, Firefox, or Edge.');
|
||
setShowNotificationModal(true);
|
||
setIsEnablingNotifications(false);
|
||
return;
|
||
}
|
||
|
||
// Check current permission status BEFORE attempting to enable
|
||
let permission = Notification.permission;
|
||
|
||
// If permission was previously denied, show user-friendly instructions
|
||
if (permission === 'denied') {
|
||
setNotificationSuccess(false);
|
||
setNotificationMessage(
|
||
'Notification permission was previously denied. To enable notifications:\n\n' +
|
||
'1. Click the lock icon (🔒) or info icon (ℹ️) in your browser\'s address bar\n' +
|
||
'2. Find "Notifications" in the permissions list\n' +
|
||
'3. Change it from "Block" to "Allow"\n' +
|
||
'4. Refresh this page and try again\n\n' +
|
||
'Alternatively, you can enable notifications in your browser\'s site settings.'
|
||
);
|
||
setShowNotificationModal(true);
|
||
setIsEnablingNotifications(false);
|
||
return;
|
||
}
|
||
|
||
// If permission is 'default', request it first
|
||
if (permission === 'default') {
|
||
permission = await Notification.requestPermission();
|
||
|
||
// If user denied the permission request
|
||
if (permission === 'denied') {
|
||
setNotificationSuccess(false);
|
||
setNotificationMessage(
|
||
'Notification permission was denied. To enable notifications:\n\n' +
|
||
'1. Click the lock icon (🔒) or info icon (ℹ️) in your browser\'s address bar\n' +
|
||
'2. Find "Notifications" in the permissions list\n' +
|
||
'3. Change it from "Block" to "Allow"\n' +
|
||
'4. Refresh this page and try again'
|
||
);
|
||
setShowNotificationModal(true);
|
||
setIsEnablingNotifications(false);
|
||
return;
|
||
}
|
||
}
|
||
|
||
// Only proceed if permission is 'granted'
|
||
if (permission !== 'granted') {
|
||
setNotificationSuccess(false);
|
||
setNotificationMessage('Notification permission is required to enable push notifications. Please grant permission and try again.');
|
||
setShowNotificationModal(true);
|
||
setIsEnablingNotifications(false);
|
||
return;
|
||
}
|
||
|
||
// Permission is granted, proceed with setup
|
||
await setupPushNotifications();
|
||
setNotificationSuccess(true);
|
||
setNotificationMessage('Push notifications have been successfully enabled! You will now receive notifications for workflow updates, approvals, and TAT alerts.');
|
||
setShowNotificationModal(true);
|
||
} catch (error: any) {
|
||
console.error('[Settings] Error enabling notifications:', error);
|
||
setNotificationSuccess(false);
|
||
// Provide more specific error messages
|
||
const errorMessage = error?.message || 'Unknown error occurred';
|
||
setNotificationMessage(
|
||
errorMessage.includes('permission')
|
||
? 'Notification permission was denied. Please enable notifications in your browser settings and try again.'
|
||
: errorMessage.includes('Service worker')
|
||
? 'Service worker registration failed. Please refresh the page and try again.'
|
||
: errorMessage.includes('token')
|
||
? 'Authentication required. Please log in again and try enabling notifications.'
|
||
: `Unable to enable push notifications: ${errorMessage}`
|
||
);
|
||
setShowNotificationModal(true);
|
||
} finally {
|
||
setIsEnablingNotifications(false);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className="min-h-screen pb-8">
|
||
<div className="max-w-7xl mx-auto space-y-6">
|
||
{/* Header Card */}
|
||
<Card className="relative overflow-hidden shadow-2xl border-0 rounded-2xl">
|
||
<div className="absolute inset-0 bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900"></div>
|
||
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,_var(--tw-gradient-stops))] from-yellow-400/20 via-transparent to-transparent"></div>
|
||
|
||
<CardContent className="relative z-10 p-6 sm:p-8 lg:p-12">
|
||
<div className="flex items-center gap-4 sm:gap-6">
|
||
<div className="w-14 h-14 sm:w-16 sm:h-16 bg-gradient-to-br from-yellow-400 to-yellow-500 rounded-2xl flex items-center justify-center shadow-xl transform hover:scale-105 transition-transform">
|
||
<SettingsIcon className="w-7 h-7 sm:w-8 sm:h-8 text-slate-900" />
|
||
</div>
|
||
<div>
|
||
<h1 className="text-2xl sm:text-3xl lg:text-4xl font-bold text-white mb-1 sm:mb-2">Settings</h1>
|
||
<p className="text-sm sm:text-base lg:text-lg text-gray-300">Manage your account and preferences</p>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{/* Tabs for Admin, Cards for Non-Admin */}
|
||
{isAdmin ? (
|
||
<Tabs defaultValue="user" className="w-full">
|
||
<TabsList className="grid w-full grid-cols-2 lg:grid-cols-4 mb-8 bg-slate-100 p-1 rounded-xl h-auto gap-1">
|
||
<TabsTrigger
|
||
value="user"
|
||
className="flex items-center justify-center gap-2 py-3 rounded-lg data-[state=active]:bg-white data-[state=active]:shadow-md transition-all"
|
||
>
|
||
<SettingsIcon className="w-4 h-4" />
|
||
<span className="hidden sm:inline">User Settings</span>
|
||
<span className="sm:hidden">User</span>
|
||
</TabsTrigger>
|
||
<TabsTrigger
|
||
value="roles"
|
||
className="flex items-center justify-center gap-2 py-3 rounded-lg data-[state=active]:bg-white data-[state=active]:shadow-md transition-all"
|
||
>
|
||
<Shield className="w-4 h-4" />
|
||
<span className="hidden sm:inline">User Roles</span>
|
||
<span className="sm:hidden">Roles</span>
|
||
</TabsTrigger>
|
||
<TabsTrigger
|
||
value="system"
|
||
className="flex items-center justify-center gap-2 py-3 rounded-lg data-[state=active]:bg-white data-[state=active]:shadow-md transition-all"
|
||
>
|
||
<Sliders className="w-4 h-4" />
|
||
<span className="hidden sm:inline">Configuration</span>
|
||
<span className="sm:hidden">Config</span>
|
||
</TabsTrigger>
|
||
<TabsTrigger
|
||
value="holidays"
|
||
className="flex items-center justify-center gap-2 py-3 rounded-lg data-[state=active]:bg-white data-[state=active]:shadow-md transition-all"
|
||
>
|
||
<Calendar className="w-4 h-4" />
|
||
<span className="hidden sm:inline">Holidays</span>
|
||
<span className="sm:hidden">Holidays</span>
|
||
</TabsTrigger>
|
||
</TabsList>
|
||
|
||
{/* Fixed width container to prevent layout shifts */}
|
||
<div className="w-full min-h-[600px]">
|
||
{/* User Settings Tab */}
|
||
<TabsContent value="user" className="mt-0 space-y-0">
|
||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 sm:gap-6">
|
||
{/* Notification Settings */}
|
||
<Card className="shadow-lg hover:shadow-xl transition-all duration-300 border-0 rounded-md group">
|
||
<CardHeader className="pb-4">
|
||
<div className="flex items-center gap-3">
|
||
<div className="p-3 bg-gradient-to-br from-slate-600 to-slate-700 rounded-md shadow-md group-hover:shadow-lg transition-shadow">
|
||
<Bell className="h-5 w-5 text-white" />
|
||
</div>
|
||
<div>
|
||
<CardTitle className="text-lg font-semibold text-gray-900">Notifications</CardTitle>
|
||
<CardDescription className="text-sm text-gray-600">Manage notification preferences</CardDescription>
|
||
</div>
|
||
</div>
|
||
</CardHeader>
|
||
<CardContent>
|
||
<div className="space-y-4">
|
||
<Button
|
||
onClick={handleEnableNotifications}
|
||
disabled={isEnablingNotifications}
|
||
className="w-full bg-re-green hover:bg-re-green/90 text-white shadow-md hover:shadow-lg transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
||
>
|
||
<Bell className={`w-4 h-4 mr-2 ${isEnablingNotifications ? 'animate-pulse' : ''}`} />
|
||
{isEnablingNotifications ? 'Enabling...' : 'Enable Push Notifications'}
|
||
</Button>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{/* Security Settings */}
|
||
<Card className="shadow-lg hover:shadow-xl transition-all duration-300 border-0 rounded-md group">
|
||
<CardHeader className="pb-4">
|
||
<div className="flex items-center gap-3">
|
||
<div className="p-3 bg-gradient-to-br from-slate-600 to-slate-700 rounded-md shadow-md group-hover:shadow-lg transition-shadow">
|
||
<Lock className="h-5 w-5 text-white" />
|
||
</div>
|
||
<div>
|
||
<CardTitle className="text-lg font-semibold text-gray-900">Security</CardTitle>
|
||
<CardDescription className="text-sm text-gray-600">Password and security settings</CardDescription>
|
||
</div>
|
||
</div>
|
||
</CardHeader>
|
||
<CardContent>
|
||
<div className="space-y-4">
|
||
<div className="p-4 bg-gradient-to-br from-gray-50 to-gray-100 rounded-md border border-gray-200">
|
||
<p className="text-sm text-gray-600 text-center">Security settings will be available soon</p>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{/* Appearance Settings */}
|
||
<Card className="shadow-lg hover:shadow-xl transition-all duration-300 border-0 rounded-md group">
|
||
<CardHeader className="pb-4">
|
||
<div className="flex items-center gap-3">
|
||
<div className="p-3 bg-gradient-to-br from-slate-600 to-slate-700 rounded-md shadow-md group-hover:shadow-lg transition-shadow">
|
||
<Palette className="h-5 w-5 text-white" />
|
||
</div>
|
||
<div>
|
||
<CardTitle className="text-lg font-semibold text-gray-900">Appearance</CardTitle>
|
||
<CardDescription className="text-sm text-gray-600">Theme and display preferences</CardDescription>
|
||
</div>
|
||
</div>
|
||
</CardHeader>
|
||
<CardContent>
|
||
<div className="space-y-4">
|
||
<div className="p-4 bg-gradient-to-br from-gray-50 to-gray-100 rounded-md border border-gray-200">
|
||
<p className="text-sm text-gray-600 text-center">Appearance settings will be available soon</p>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{/* Preferences */}
|
||
<Card className="shadow-lg hover:shadow-xl transition-all duration-300 border-0 rounded-md group">
|
||
<CardHeader className="pb-4">
|
||
<div className="flex items-center gap-3">
|
||
<div className="p-3 bg-gradient-to-br from-slate-600 to-slate-700 rounded-md shadow-md group-hover:shadow-lg transition-shadow">
|
||
<Shield className="h-5 w-5 text-white" />
|
||
</div>
|
||
<div>
|
||
<CardTitle className="text-lg font-semibold text-gray-900">Preferences</CardTitle>
|
||
<CardDescription className="text-sm text-gray-600">Application preferences</CardDescription>
|
||
</div>
|
||
</div>
|
||
</CardHeader>
|
||
<CardContent>
|
||
<div className="space-y-4">
|
||
<div className="p-4 bg-gradient-to-br from-gray-50 to-gray-100 rounded-xl border border-gray-200">
|
||
<p className="text-sm text-gray-600 text-center">User preferences will be available soon</p>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
</TabsContent>
|
||
|
||
{/* User Roles Tab (Admin Only) */}
|
||
<TabsContent value="roles" className="mt-0">
|
||
<UserRoleManager />
|
||
</TabsContent>
|
||
|
||
{/* System Configuration Tab (Admin Only) */}
|
||
<TabsContent value="system" className="mt-0">
|
||
<ConfigurationManager />
|
||
</TabsContent>
|
||
|
||
{/* Holiday Calendar Tab (Admin Only) */}
|
||
<TabsContent value="holidays" className="mt-0">
|
||
<HolidayManager />
|
||
</TabsContent>
|
||
</div>
|
||
</Tabs>
|
||
) : (
|
||
<>
|
||
{/* Non-Admin User Settings Only */}
|
||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 sm:gap-6">
|
||
{/* Notification Settings */}
|
||
<Card className="shadow-lg hover:shadow-xl transition-all duration-300 border-0 rounded-md group">
|
||
<CardHeader className="pb-4">
|
||
<div className="flex items-center gap-3">
|
||
<div className="p-3 bg-gradient-to-br from-slate-600 to-slate-700 rounded-md shadow-md group-hover:shadow-lg transition-shadow">
|
||
<Bell className="h-5 w-5 text-white" />
|
||
</div>
|
||
<div>
|
||
<CardTitle className="text-lg font-semibold text-gray-900">Notifications</CardTitle>
|
||
<CardDescription className="text-sm text-gray-600">Manage notification preferences</CardDescription>
|
||
</div>
|
||
</div>
|
||
</CardHeader>
|
||
<CardContent>
|
||
<div className="space-y-4">
|
||
<Button
|
||
onClick={handleEnableNotifications}
|
||
disabled={isEnablingNotifications}
|
||
className="w-full bg-re-green hover:bg-re-green/90 text-white shadow-md hover:shadow-lg transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
||
>
|
||
<Bell className={`w-4 h-4 mr-2 ${isEnablingNotifications ? 'animate-pulse' : ''}`} />
|
||
{isEnablingNotifications ? 'Enabling...' : 'Enable Push Notifications'}
|
||
</Button>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{/* Security Settings */}
|
||
<Card className="shadow-lg hover:shadow-xl transition-all duration-300 border-0 rounded-md group">
|
||
<CardHeader className="pb-4">
|
||
<div className="flex items-center gap-3">
|
||
<div className="p-3 bg-gradient-to-br from-slate-600 to-slate-700 rounded-md shadow-md group-hover:shadow-lg transition-shadow">
|
||
<Lock className="h-5 w-5 text-white" />
|
||
</div>
|
||
<div>
|
||
<CardTitle className="text-lg font-semibold text-gray-900">Security</CardTitle>
|
||
<CardDescription className="text-sm text-gray-600">Password and security settings</CardDescription>
|
||
</div>
|
||
</div>
|
||
</CardHeader>
|
||
<CardContent>
|
||
<div className="space-y-4">
|
||
<div className="p-4 bg-gradient-to-br from-gray-50 to-gray-100 rounded-md border border-gray-200">
|
||
<p className="text-sm text-gray-600 text-center">Security settings will be available soon</p>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{/* Appearance Settings */}
|
||
<Card className="shadow-lg hover:shadow-xl transition-all duration-300 border-0 rounded-md group">
|
||
<CardHeader className="pb-4">
|
||
<div className="flex items-center gap-3">
|
||
<div className="p-3 bg-gradient-to-br from-slate-600 to-slate-700 rounded-md shadow-md group-hover:shadow-lg transition-shadow">
|
||
<Palette className="h-5 w-5 text-white" />
|
||
</div>
|
||
<div>
|
||
<CardTitle className="text-lg font-semibold text-gray-900">Appearance</CardTitle>
|
||
<CardDescription className="text-sm text-gray-600">Theme and display preferences</CardDescription>
|
||
</div>
|
||
</div>
|
||
</CardHeader>
|
||
<CardContent>
|
||
<div className="space-y-4">
|
||
<div className="p-4 bg-gradient-to-br from-gray-50 to-gray-100 rounded-md border border-gray-200">
|
||
<p className="text-sm text-gray-600 text-center">Appearance settings will be available soon</p>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{/* Preferences */}
|
||
<Card className="shadow-lg hover:shadow-xl transition-all duration-300 border-0 rounded-md group">
|
||
<CardHeader className="pb-4">
|
||
<div className="flex items-center gap-3">
|
||
<div className="p-3 bg-gradient-to-br from-slate-600 to-slate-700 rounded-md shadow-md group-hover:shadow-lg transition-shadow">
|
||
<Shield className="h-5 w-5 text-white" />
|
||
</div>
|
||
<div>
|
||
<CardTitle className="text-lg font-semibold text-gray-900">Preferences</CardTitle>
|
||
<CardDescription className="text-sm text-gray-600">Application preferences</CardDescription>
|
||
</div>
|
||
</div>
|
||
</CardHeader>
|
||
<CardContent>
|
||
<div className="space-y-4">
|
||
<div className="p-4 bg-gradient-to-br from-gray-50 to-gray-100 rounded-md border border-gray-200">
|
||
<p className="text-sm text-gray-600 text-center">User preferences will be available soon</p>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
</>
|
||
)}
|
||
</div>
|
||
|
||
<NotificationStatusModal
|
||
open={showNotificationModal}
|
||
onClose={() => setShowNotificationModal(false)}
|
||
success={notificationSuccess}
|
||
message={notificationMessage}
|
||
/>
|
||
</div>
|
||
);
|
||
}
|
||
|