feat: Implement Staff Detail View

This commit is contained in:
dhinesh-m24
2026-01-29 17:24:54 +05:30
parent 9e19ee7592
commit 48bb1c457c
5 changed files with 731 additions and 341 deletions

View File

@@ -4,6 +4,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import AppRoutes from './routes';
import { store } from './store/store';
import { initializeAuthPersistence } from './services/authService';
import AuthInitializer from './features/auth/AuthInitializer';
// Initialize the QueryClient
const queryClient = new QueryClient();
@@ -13,13 +14,16 @@ initializeAuthPersistence();
/**
* Root Application Component.
* Wraps the app with Redux Provider and React Query Provider.
* Wraps the app with Redux Provider, React Query Provider, and AuthInitializer.
* AuthInitializer ensures auth state is restored from persistence before routes are rendered.
*/
const App: React.FC = () => {
return (
<Provider store={store}>
<QueryClientProvider client={queryClient}>
<AppRoutes />
<AuthInitializer>
<AppRoutes />
</AuthInitializer>
</QueryClientProvider>
</Provider>
);

View File

@@ -0,0 +1,44 @@
import React from 'react';
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { checkAuthStatus } from './authSlice';
import type { RootState, AppDispatch } from '../../store/store';
interface AuthInitializerProps {
children: React.ReactNode;
}
/**
* AuthInitializer Component
* Initializes authentication state from Firebase persistence on app load
* Shows a loading screen until the initial auth check is complete
* This prevents premature redirect to login before the persisted session is restored
*/
const AuthInitializer: React.FC<AuthInitializerProps> = ({ children }) => {
const dispatch = useDispatch<AppDispatch>();
const { isInitialized } = useSelector((state: RootState) => state.auth);
useEffect(() => {
// Perform initial auth check when component mounts
// This restores the persisted session from Firebase
dispatch(checkAuthStatus());
}, [dispatch]);
// Show loading state while initializing auth
if (!isInitialized) {
return (
<div className="flex items-center justify-center h-screen bg-slate-50">
<div className="text-center">
<div className="inline-block">
<div className="w-8 h-8 border-4 border-slate-200 border-t-primary rounded-full animate-spin"></div>
</div>
<p className="mt-4 text-slate-600">Loading application...</p>
</div>
</div>
);
}
return <>{children}</>;
};
export default AuthInitializer;

View File

@@ -17,6 +17,7 @@ interface AuthState {
isAuthenticated: boolean;
status: "idle" | "loading" | "succeeded" | "failed";
error: string | null;
isInitialized: boolean; // Track whether initial auth check has completed
}
const initialState: AuthState = {
@@ -24,6 +25,7 @@ const initialState: AuthState = {
isAuthenticated: false,
status: "idle",
error: null,
isInitialized: false, // Start as false until initial auth check completes
};
/**
@@ -154,6 +156,9 @@ const authSlice = createSlice({
// Check auth status thunk
builder
.addCase(checkAuthStatus.pending, (state) => {
state.status = "loading";
})
.addCase(checkAuthStatus.fulfilled, (state, action) => {
if (action.payload) {
state.user = action.payload;
@@ -163,6 +168,11 @@ const authSlice = createSlice({
state.isAuthenticated = false;
}
state.status = "idle";
state.isInitialized = true; // Mark initialization as complete
})
.addCase(checkAuthStatus.rejected, (state) => {
state.isInitialized = true; // Mark initialization as complete even on error
state.isAuthenticated = false;
});
},
});

View File

@@ -13,6 +13,7 @@ export default function EditStaff() {
const [staff, setStaff] = useState<Staff | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [isSubmitting, setIsSubmitting] = useState(false);
const [isEditing, setIsEditing] = useState(false);
useEffect(() => {
const fetchStaff = async () => {
@@ -41,7 +42,8 @@ export default function EditStaff() {
setIsSubmitting(true);
try {
await workforceService.entities.Staff.update(id, staffData);
navigate("/staff");
setStaff(staffData);
setIsEditing(false);
} catch (error) {
console.error("Failed to update staff", error);
} finally {
@@ -49,6 +51,10 @@ export default function EditStaff() {
}
};
const handleCancel = () => {
setIsEditing(false);
};
if (isLoading) {
return (
<div className="flex flex-col items-center justify-center min-h-[60vh] gap-4">
@@ -60,7 +66,7 @@ export default function EditStaff() {
return (
<DashboardLayout
title={`Edit: ${staff?.employee_name || 'Staff Member'}`}
title={isEditing ? `Edit: ${staff?.employee_name || 'Staff Member'}` : staff?.employee_name || 'Staff Member'}
subtitle={`Management of ${staff?.employee_name}'s professional records`}
backAction={
<Button
@@ -74,11 +80,42 @@ export default function EditStaff() {
}
>
{staff && (
<StaffForm
staff={staff}
onSubmit={handleSubmit}
isSubmitting={isSubmitting}
/>
<div>
{!isEditing && (
<div className="mb-6 flex justify-end">
<Button onClick={() => setIsEditing(true)} variant="secondary">
Edit
</Button>
</div>
)}
{isEditing && (
<div className="mb-6 flex justify-end gap-2">
<Button
onClick={handleCancel}
variant="outline"
disabled={isSubmitting}
>
Cancel
</Button>
<Button
onClick={() => {
// Trigger form submission by dispatching event or calling form submit
const form = document.querySelector('form');
if (form) form.requestSubmit();
}}
disabled={isSubmitting}
>
{isSubmitting ? 'Saving...' : 'Save'}
</Button>
</div>
)}
<StaffForm
staff={staff}
onSubmit={handleSubmit}
isSubmitting={isSubmitting}
disabled={!isEditing}
/>
</div>
)}
</DashboardLayout>
);