feat(auth): Implemented Session Persistence
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
} from 'lucide-react';
|
||||
import Sidebar from './Sidebar';
|
||||
import { getDashboardPath } from '../../services/firestoreService';
|
||||
import { useSessionPersistence } from '../../hooks/useSessionPersistence';
|
||||
|
||||
/**
|
||||
* Main Application Layout for Authenticated Users.
|
||||
@@ -17,6 +18,9 @@ import { getDashboardPath } from '../../services/firestoreService';
|
||||
* Handles role-based navigation rendering and validates user access to dashboard routes.
|
||||
*/
|
||||
const AppLayout: React.FC = () => {
|
||||
// Initialize session persistence and token refresh
|
||||
useSessionPersistence();
|
||||
|
||||
// Typed selectors
|
||||
const { isAuthenticated, user } = useSelector((state: RootState) => state.auth);
|
||||
const dispatch = useDispatch<AppDispatch>();
|
||||
@@ -47,7 +51,10 @@ const AppLayout: React.FC = () => {
|
||||
}
|
||||
|
||||
const handleLogout = () => {
|
||||
dispatch(logoutUser());
|
||||
dispatch(logoutUser()).then(() => {
|
||||
// Navigate to login page after logout is complete
|
||||
navigate('/login', { replace: true });
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
48
apps/web/src/features/layouts/DashboardLayout.tsx
Normal file
48
apps/web/src/features/layouts/DashboardLayout.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
import PageHeader from '../../common/components/PageHeader';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
interface DashboardLayoutProps {
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
actions?: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
maxWidth?: string;
|
||||
backAction?: React.ReactNode;
|
||||
}
|
||||
|
||||
const DashboardLayout: React.FC<DashboardLayoutProps> = ({
|
||||
title,
|
||||
subtitle,
|
||||
actions,
|
||||
children,
|
||||
maxWidth = 'max-w-7xl',
|
||||
backAction
|
||||
}) => {
|
||||
return (
|
||||
<div className="p-4 md:p-8">
|
||||
<div className={`${maxWidth} mx-auto`}>
|
||||
{backAction && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.4, ease: [0.16, 1, 0.3, 1] }}
|
||||
className="mb-4"
|
||||
>
|
||||
{backAction}
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
<PageHeader
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
actions={actions}
|
||||
/>
|
||||
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardLayout;
|
||||
23
apps/web/src/features/layouts/PublicLayout.tsx
Normal file
23
apps/web/src/features/layouts/PublicLayout.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
|
||||
interface PublicLayoutProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Layout for public, unauthenticated pages (e.g. Login, Forgot Password).
|
||||
* Provides a centered container with a gradient background.
|
||||
*/
|
||||
const PublicLayout: React.FC<PublicLayoutProps> = ({ children }) => {
|
||||
return (
|
||||
<div className="min-h-screen w-full bg-slate-50 flex items-center justify-center">
|
||||
{/* Background decoration */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-blue-50 to-indigo-50 -z-10" />
|
||||
<div className="w-full">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PublicLayout;
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
||||
import { LogOut, Menu, X } from 'lucide-react';
|
||||
import { Button } from '../../common/components/ui/button';
|
||||
import { NAV_CONFIG } from "../../common/config/navigation";
|
||||
@@ -22,6 +22,19 @@ const Sidebar: React.FC<SidebarProps> = ({
|
||||
onLogout
|
||||
}) => {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
/**
|
||||
* Handle logout with navigation
|
||||
* Ensures user is redirected to login page after logout
|
||||
*/
|
||||
const handleLogoutClick = async () => {
|
||||
onLogout();
|
||||
// Small delay to allow logout to complete before navigation
|
||||
setTimeout(() => {
|
||||
navigate('/login', { replace: true });
|
||||
}, 100);
|
||||
};
|
||||
|
||||
// Filter navigation based on user role
|
||||
const filteredNav = useMemo(() => {
|
||||
@@ -107,7 +120,7 @@ const Sidebar: React.FC<SidebarProps> = ({
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={onLogout}
|
||||
onClick={handleLogoutClick}
|
||||
title="Logout"
|
||||
className="text-secondary-text hover:text-destructive hover:bg-destructive/10"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user