397 lines
14 KiB
TypeScript
397 lines
14 KiB
TypeScript
import React, { useState, useEffect } from "react";
|
|
import { Login } from "./components/auth/Login";
|
|
import { ForgotPassword } from "./components/auth/ForgotPassword";
|
|
import { TwoFactorAuth } from "./components/auth/TwoFactorAuth";
|
|
import { Dashboard } from "./components/pages/Dashboard";
|
|
import { Profile } from "./components/pages/Profile";
|
|
import { ResetPassword } from "./components/pages/ResetPassword";
|
|
import { IndividualLearners } from "./components/pages/IndividualLearners";
|
|
import { Organizations } from "./components/pages/Organizations";
|
|
import { NewOrganization } from "./components/pages/NewOrganization";
|
|
import { ContentManager } from "./components/pages/ContentManager";
|
|
import { NewBlog } from "./components/pages/NewBlog";
|
|
import { NewFAQ } from "./components/pages/NewFAQ";
|
|
import { Courses } from "./components/pages/Courses";
|
|
import { CourseBuilder } from "./components/pages/CourseBuilder";
|
|
import { Profilers } from "./components/pages/ProfilersEnhanced";
|
|
import { ProfilerBuilder } from "./components/pages/ProfilerBuilder";
|
|
import { ProfilerMaster } from "./components/pages/ProfilerMaster";
|
|
import { ProfilerPreview } from "./components/pages/ProfilerPreview";
|
|
import { ProfilerApproval } from "./components/pages/ProfilerApproval";
|
|
|
|
import { LandingPages } from "./components/pages/LandingPagesNew";
|
|
import { HomeEditor } from "./components/pages/HomeEditor";
|
|
import { ServicesEditor } from "./components/pages/ServicesEditor";
|
|
import { AboutUsEditor } from "./components/pages/AboutUsEditor";
|
|
import { Webinars } from "./components/pages/Webinars";
|
|
import { Programmes } from "./components/pages/Programmes";
|
|
import { ProgrammeComposer } from "./components/pages/ProgrammeComposer";
|
|
import { ProgrammeAssignment } from "./components/pages/ProgrammeAssignment";
|
|
import { CourseAssignment } from "./components/pages/CourseAssignment";
|
|
import { ClassScheduler } from "./components/pages/ClassScheduler";
|
|
import { OpenProgramme } from "./components/pages/OpenProgramme";
|
|
import { Facilities360 } from "./components/pages/Facilities360";
|
|
import { Facilities360New } from "./components/pages/Facilities360New";
|
|
import { Facilities360Detail } from "./components/pages/Facilities360Detail";
|
|
import { Leads } from "./components/pages/Leads";
|
|
import { Roles } from "./components/pages/Roles";
|
|
import { Analytics } from "./components/pages/Analytics";
|
|
import { Toaster } from "./components/ui/sonner";
|
|
import { SESSION_CONFIG, AutoSaveData, mockNotifications, Notification, mockApprovalTasks, ApprovalTask } from "./data/mockData";
|
|
|
|
type Route =
|
|
| "/login"
|
|
| "/login/forget-password"
|
|
| "/login/2fa"
|
|
| "/dashboard"
|
|
| "/profile"
|
|
| "/profile/reset-password"
|
|
| "/users/individual"
|
|
| "/users/organizations"
|
|
| "/users/organizations/new"
|
|
| "/content"
|
|
| "/content/blogs/new"
|
|
| "/content/faqs/new"
|
|
| "/courses"
|
|
| "/courses/course-builder"
|
|
| "/courses/new"
|
|
| `/courses/${string}/assign`
|
|
| "/profilers"
|
|
| "/profilers/new"
|
|
| "/profilers/preview"
|
|
| "/profiler-approval"
|
|
| "/landing-pages"
|
|
| "/landing-pages/edit/home"
|
|
| "/landing-pages/edit/services"
|
|
| "/landing-pages/edit/about-us"
|
|
| "/webinars"
|
|
| "/programmes"
|
|
| "/programmes/new"
|
|
| `/programmes/${string}/assign`
|
|
| "/class-scheduler"
|
|
| "/open-programme"
|
|
| "/facilities-360"
|
|
| "/facilities-360/new"
|
|
| `/facilities-360/${string}`
|
|
| "/admin/leads"
|
|
| "/admin/roles"
|
|
| "/admin/analytics"
|
|
| "/admin/profiler-master";
|
|
|
|
export default function App() {
|
|
const [currentRoute, setCurrentRoute] = useState<Route>("/login");
|
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
const [user, setUser] = useState({
|
|
name: "Admin User",
|
|
email: "admin@klc.edu",
|
|
role: "Super Admin",
|
|
avatar: "",
|
|
lastLogin: "2024-01-15 14:30",
|
|
});
|
|
|
|
// Auto-save state management
|
|
const [autoSaveData, setAutoSaveData] = useState<{ [key: string]: any }>({});
|
|
|
|
// Notification state management
|
|
const [notifications, setNotifications] = useState<Notification[]>(mockNotifications);
|
|
const [approvalTasks, setApprovalTasks] = useState<ApprovalTask[]>(mockApprovalTasks);
|
|
|
|
// Simulate authentication check
|
|
useEffect(() => {
|
|
const authToken = localStorage.getItem("klc_auth_token");
|
|
if (authToken) {
|
|
setIsAuthenticated(true);
|
|
if (currentRoute === "/login") {
|
|
setCurrentRoute("/dashboard");
|
|
}
|
|
}
|
|
}, []);
|
|
|
|
// Auto-save functionality
|
|
const handleAutoSave = (formData: any) => {
|
|
const routeKey = `${currentRoute}_${user.email}`;
|
|
setAutoSaveData(prev => ({
|
|
...prev,
|
|
[routeKey]: formData
|
|
}));
|
|
|
|
// Persist to localStorage
|
|
const autoSaveData: AutoSaveData = {
|
|
userId: user.email,
|
|
route: currentRoute,
|
|
formData,
|
|
timestamp: new Date().toISOString(),
|
|
isValid: true
|
|
};
|
|
|
|
localStorage.setItem(`autosave_${routeKey}`, JSON.stringify(autoSaveData));
|
|
};
|
|
|
|
// Get auto-saved data for current route
|
|
const getAutoSavedData = () => {
|
|
const routeKey = `${currentRoute}_${user.email}`;
|
|
return autoSaveData[routeKey] || null;
|
|
};
|
|
|
|
// Clear auto-saved data
|
|
const clearAutoSavedData = (route?: string) => {
|
|
const routeKey = route ? `${route}_${user.email}` : `${currentRoute}_${user.email}`;
|
|
setAutoSaveData(prev => {
|
|
const newData = { ...prev };
|
|
delete newData[routeKey];
|
|
return newData;
|
|
});
|
|
localStorage.removeItem(`autosave_${routeKey}`);
|
|
};
|
|
|
|
// Notification handlers
|
|
const handleMarkNotificationAsRead = (notificationId: string) => {
|
|
setNotifications(prev =>
|
|
prev.map(n => n.id === notificationId ? { ...n, read: true } : n)
|
|
);
|
|
};
|
|
|
|
const handleMarkAllNotificationsAsRead = () => {
|
|
setNotifications(prev => prev.map(n => ({ ...n, read: true })));
|
|
};
|
|
|
|
const handleDeleteNotification = (notificationId: string) => {
|
|
setNotifications(prev => prev.filter(n => n.id !== notificationId));
|
|
};
|
|
|
|
// Approval handlers
|
|
const handleApproveTask = (taskId: string, comment: string) => {
|
|
setApprovalTasks(prev =>
|
|
prev.map(task =>
|
|
task.id === taskId ? { ...task, status: 'approved' as const } : task
|
|
)
|
|
);
|
|
// Add success notification
|
|
const task = approvalTasks.find(t => t.id === taskId);
|
|
if (task) {
|
|
const newNotification: Notification = {
|
|
id: `notif_${Date.now()}`,
|
|
type: 'approval',
|
|
title: 'Profiler Approved',
|
|
message: `${task.entityName} has been successfully approved and published`,
|
|
timestamp: new Date().toISOString(),
|
|
read: false,
|
|
priority: 'medium',
|
|
relatedEntity: {
|
|
type: 'profiler',
|
|
id: task.entityId,
|
|
name: task.entityName
|
|
}
|
|
};
|
|
setNotifications(prev => [newNotification, ...prev]);
|
|
}
|
|
};
|
|
|
|
const handleRejectTask = (taskId: string, reason: string) => {
|
|
setApprovalTasks(prev =>
|
|
prev.map(task =>
|
|
task.id === taskId ? { ...task, status: 'rejected' as const } : task
|
|
)
|
|
);
|
|
// Add notification
|
|
const task = approvalTasks.find(t => t.id === taskId);
|
|
if (task) {
|
|
const newNotification: Notification = {
|
|
id: `notif_${Date.now()}`,
|
|
type: 'approval',
|
|
title: 'Profiler Rejected',
|
|
message: `${task.entityName} has been rejected`,
|
|
timestamp: new Date().toISOString(),
|
|
read: false,
|
|
priority: 'high',
|
|
relatedEntity: {
|
|
type: 'profiler',
|
|
id: task.entityId,
|
|
name: task.entityName
|
|
}
|
|
};
|
|
setNotifications(prev => [newNotification, ...prev]);
|
|
}
|
|
};
|
|
|
|
const handleRequestChangesTask = (taskId: string, reason: string) => {
|
|
setApprovalTasks(prev =>
|
|
prev.map(task =>
|
|
task.id === taskId ? { ...task, status: 'changes_requested' as const } : task
|
|
)
|
|
);
|
|
// Add notification
|
|
const task = approvalTasks.find(t => t.id === taskId);
|
|
if (task) {
|
|
const newNotification: Notification = {
|
|
id: `notif_${Date.now()}`,
|
|
type: 'approval',
|
|
title: 'Changes Requested',
|
|
message: `Changes have been requested for ${task.entityName}`,
|
|
timestamp: new Date().toISOString(),
|
|
read: false,
|
|
priority: 'high',
|
|
relatedEntity: {
|
|
type: 'profiler',
|
|
id: task.entityId,
|
|
name: task.entityName
|
|
}
|
|
};
|
|
setNotifications(prev => [newNotification, ...prev]);
|
|
}
|
|
};
|
|
|
|
const navigate = (route: Route) => {
|
|
setCurrentRoute(route);
|
|
};
|
|
|
|
const login = () => {
|
|
localStorage.setItem("klc_auth_token", "mock_token");
|
|
// Set longer session timeout
|
|
const expirationTime = Date.now() + SESSION_CONFIG.LOGOUT_TIMEOUT;
|
|
localStorage.setItem("klc_auth_expiration", expirationTime.toString());
|
|
|
|
setIsAuthenticated(true);
|
|
navigate("/dashboard");
|
|
};
|
|
|
|
const logout = () => {
|
|
localStorage.removeItem("klc_auth_token");
|
|
localStorage.removeItem("klc_auth_expiration");
|
|
|
|
// Clear all auto-saved data for this user
|
|
Object.keys(localStorage).forEach(key => {
|
|
if (key.startsWith(`autosave_`) && key.includes(user.email)) {
|
|
localStorage.removeItem(key);
|
|
}
|
|
});
|
|
|
|
setIsAuthenticated(false);
|
|
setAutoSaveData({});
|
|
navigate("/login");
|
|
};
|
|
|
|
const renderPage = () => {
|
|
if (!isAuthenticated && !currentRoute.startsWith("/login")) {
|
|
return <Login onLogin={login} onNavigate={navigate} />;
|
|
}
|
|
|
|
const commonProps = {
|
|
onNavigate: navigate,
|
|
onLogout: logout,
|
|
user,
|
|
formData: getAutoSavedData(),
|
|
onAutoSave: handleAutoSave,
|
|
onClearAutoSave: clearAutoSavedData,
|
|
notifications,
|
|
onMarkNotificationAsRead: handleMarkNotificationAsRead,
|
|
onMarkAllNotificationsAsRead: handleMarkAllNotificationsAsRead,
|
|
onDeleteNotification: handleDeleteNotification
|
|
};
|
|
|
|
switch (currentRoute) {
|
|
case "/login":
|
|
return <Login onLogin={login} onNavigate={navigate} />;
|
|
case "/login/forget-password":
|
|
return <ForgotPassword onNavigate={navigate} />;
|
|
case "/login/2fa":
|
|
return <TwoFactorAuth onLogin={login} onNavigate={navigate} />;
|
|
case "/dashboard":
|
|
return <Dashboard {...commonProps} />;
|
|
case "/profile":
|
|
return <Profile {...commonProps} />;
|
|
case "/profile/reset-password":
|
|
return <ResetPassword {...commonProps} />;
|
|
case "/users/individual":
|
|
return <IndividualLearners {...commonProps} />;
|
|
case "/users/organizations":
|
|
return <Organizations {...commonProps} />;
|
|
case "/users/organizations/new":
|
|
return <NewOrganization {...commonProps} />;
|
|
case "/content":
|
|
return <ContentManager {...commonProps} />;
|
|
case "/content/blogs/new":
|
|
return <NewBlog {...commonProps} />;
|
|
case "/content/faqs/new":
|
|
return <NewFAQ {...commonProps} />;
|
|
case "/courses":
|
|
return <Courses {...commonProps} />;
|
|
case "/courses/course-builder":
|
|
case "/courses/new":
|
|
return <CourseBuilder {...commonProps} />;
|
|
case "/profilers":
|
|
return <Profilers {...commonProps} />;
|
|
case "/profilers/new":
|
|
return <ProfilerBuilder {...commonProps} />;
|
|
case "/profilers/preview":
|
|
return <ProfilerPreview onBack={() => navigate("/dashboard")} />;
|
|
case "/profiler-approval":
|
|
// Get the approval task from sessionStorage
|
|
const taskData = sessionStorage.getItem('currentApprovalTask');
|
|
const approvalTask = taskData ? JSON.parse(taskData) : approvalTasks[0];
|
|
return (
|
|
<ProfilerApproval
|
|
approvalTask={approvalTask}
|
|
onNavigate={navigate}
|
|
onLogout={logout}
|
|
user={user}
|
|
onApprove={handleApproveTask}
|
|
onReject={handleRejectTask}
|
|
onRequestChanges={handleRequestChangesTask}
|
|
/>
|
|
);
|
|
case "/admin/profiler-master":
|
|
return <ProfilerMaster {...commonProps} />;
|
|
case "/landing-pages":
|
|
return <LandingPages {...commonProps} />;
|
|
case "/landing-pages/edit/home":
|
|
return <HomeEditor {...commonProps} />;
|
|
case "/landing-pages/edit/services":
|
|
return <ServicesEditor {...commonProps} />;
|
|
case "/landing-pages/edit/about-us":
|
|
return <AboutUsEditor {...commonProps} />;
|
|
case "/webinars":
|
|
return <Webinars {...commonProps} />;
|
|
case "/programmes":
|
|
return <Programmes {...commonProps} />;
|
|
case "/programmes/new":
|
|
return <ProgrammeComposer {...commonProps} />;
|
|
case "/class-scheduler":
|
|
return <ClassScheduler {...commonProps} />;
|
|
case "/open-programme":
|
|
return <OpenProgramme {...commonProps} />;
|
|
case "/facilities-360":
|
|
return <Facilities360 {...commonProps} />;
|
|
case "/facilities-360/new":
|
|
return <Facilities360New {...commonProps} />;
|
|
case "/admin/leads":
|
|
return <Leads {...commonProps} />;
|
|
case "/admin/roles":
|
|
return <Roles {...commonProps} />;
|
|
case "/admin/analytics":
|
|
return <Analytics {...commonProps} />;
|
|
default:
|
|
// Handle dynamic routes
|
|
if (currentRoute.startsWith("/programmes/") && currentRoute.endsWith("/assign")) {
|
|
const programmeId = currentRoute.split("/")[2];
|
|
return <ProgrammeAssignment programmeId={programmeId} {...commonProps} />;
|
|
}
|
|
if (currentRoute.startsWith("/courses/") && currentRoute.endsWith("/assign")) {
|
|
const courseId = currentRoute.split("/")[2];
|
|
return <CourseAssignment courseId={courseId} {...commonProps} />;
|
|
}
|
|
if (currentRoute.startsWith("/facilities-360/") && !currentRoute.endsWith("/new")) {
|
|
const tourId = currentRoute.split("/")[2];
|
|
return <Facilities360Detail tourId={tourId} {...commonProps} />;
|
|
}
|
|
return <Dashboard {...commonProps} />;
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="h-screen bg-background overflow-hidden">
|
|
{renderPage()}
|
|
<Toaster position="top-right" />
|
|
</div>
|
|
);
|
|
} |