Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions client/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/svg+xml" href="./src/assets/logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>client</title>
<title>CiviLink</title>
</head>
<body>
<div id="root"></div>
Expand Down
1 change: 0 additions & 1 deletion client/public/vite.svg

This file was deleted.

85 changes: 65 additions & 20 deletions client/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,83 @@
import { BrowserRouter } from "react-router-dom"
import '@fortawesome/fontawesome-free/css/all.min.css';
import './App.css'
import { AuthProvider } from "./auth/AuthContext.jsx";
import { NotificationsProvider } from "./auth/NotificationsContext.jsx";
import { ApplicationProvider } from "./auth/ApplicationContext.jsx";
import { ChatProvider } from "./auth/ChatContext.jsx";
import { AuthProvider } from "./context/AuthContext.jsx";
import { NotificationsProvider } from "./context/NotificationsContext.jsx";
import { ApplicationProvider } from "./context/ApplicationContext.jsx";
import { ChatProvider } from "./context/ChatContext.jsx";
import { NewsProvider } from './context/NewsContext.jsx';
import CommonRoutes from "./routes/CommonRoutes";
import UserRoutes from "./routes/UserRoutes";
import OfficerRoutes from "./routes/OfficerRoutes";
import AdminRoutes from "./routes/AdminRoutes";

import { ProfileAssetsProvider } from './auth/ProfileAssetsContext.jsx';
import { PaymentProvider } from './auth/PaymentContext.jsx';
import { ProfileAssetsProvider } from './context/ProfileAssetsContext.jsx';
import { PaymentProvider } from './context/PaymentContext.jsx';

import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

const titles = {
"/": "Home | CiviLink",
"/login": "Login | CiviLink",
"/about": "About Us | CiviLink",
"/help": "Help Center | CiviLink",
"/contact": "Contact Us | CiviLink",
"/notifications": "Notifications | CiviLink",
"/user/dashboard": "Dashboard | CiviLink",
"/user/applications": "My Applications | CiviLink",
"/user/messages": "Messages | CiviLink",
"/user/settings": "Settings | CiviLink",
"/user/marriage-form": "Marriage Certificate | CiviLink",
"/user/birth-form": "Birth Certificate | CiviLink",
"/user/tin-form": "TIN Application | CiviLink",
"/officer/dashboard": "Officer Dashboard | CiviLink",
"/officer/applications": "Review Requests | CiviLink",
"/officer/messages": "Communications | CiviLink",
"/officer/settings": "Officer Settings | CiviLink",
"/officer/news": "News Management | CiviLink",
"/admin/dashboard": "Admin Dashboard | CiviLink",
"/admin/manage-officers": "Manage Staff | CiviLink",
"/admin/performance": "Performance | CiviLink",
"/admin/security-report": "Security Audit | CiviLink",
"/admin/settings": "Admin Settings | CiviLink",
};

function TitleManager() {
const location = useLocation();

useEffect(() => {
const currentTitle = titles[location.pathname] || "CiviLink";
document.title = currentTitle;
}, [location]);

return null; // This component doesn't render anything
}


function App() {

return (
<BrowserRouter>
<TitleManager />
<AuthProvider>
<NotificationsProvider>
<ProfileAssetsProvider>
<PaymentProvider>
<ApplicationProvider>
<ChatProvider>
<div className="App">
<CommonRoutes />
<UserRoutes />
<OfficerRoutes />
<AdminRoutes />
</div>
</ChatProvider>
</ApplicationProvider>
</PaymentProvider>
</ProfileAssetsProvider>
<NewsProvider>
<ApplicationProvider>
<ChatProvider>
<div className="App">
<CommonRoutes />
<ProfileAssetsProvider>
<PaymentProvider>
<UserRoutes />
</PaymentProvider>
</ProfileAssetsProvider>
<OfficerRoutes />
<AdminRoutes />
</div>
</ChatProvider>
</ApplicationProvider>
</NewsProvider>
</NotificationsProvider>
</AuthProvider>
</BrowserRouter>
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/AdminSideBar.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import '../styles/components/AdminSideBar.css';
import { useAuth } from '../auth/AuthContext';
import { useAuth } from '../hooks/useAuth';
import { NavLink, useNavigate } from 'react-router-dom';
import { useState } from 'react';
import LogoutModal from './common/LogoutModal';
Expand Down
14 changes: 7 additions & 7 deletions client/src/components/Navigation2.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@

import Logo from '../assets/logo.png';
import { Link, useNavigate } from 'react-router-dom';
import { useAuth } from '../auth/AuthContext.jsx';
import { NotificationBell } from './notifications/NotificationBell.jsx';
import { useAuth } from '../hooks/useAuth';
import { NotificationBell } from './common/NotificationBell.jsx';
import LogoutModal from './common/LogoutModal.jsx';
import { useState } from 'react';
import '../styles/components/Navigation2.css';

function Navigation2(){
function Navigation2() {
const { user, logout, role } = useAuth();
const navigate = useNavigate();
const [showLogoutModal, setShowLogoutModal] = useState(false);
Expand Down Expand Up @@ -62,7 +62,7 @@ function Navigation2(){
<img src={Logo} alt="CiviLink Logo" />
<span>CiviLink</span>
</Link>

<div className="nav-right">
{/* Notification Bell */}
<NotificationBell />
Expand All @@ -75,9 +75,9 @@ function Navigation2(){
<div className="user-info">
<div style={{ fontWeight: 600 }}>{user.fullName}</div>
<div style={{ fontSize: '0.9rem', color: '#666' }}>
{role === 'admin' ? 'Administrator' :
role === 'officer' ? `Officer - ${user.department || 'N/A'}` :
`Citizen`}
{role === 'admin' ? 'Administrator' :
role === 'officer' ? `Officer - ${user.department || 'N/A'}` :
`Citizen`}
</div>
</div>
<div className="user-menu">
Expand Down
27 changes: 9 additions & 18 deletions client/src/components/NewsSlider.jsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,17 @@
import React, { useState, useEffect } from 'react';
import * as newsAPI from '../api/news.api';
import { useNews } from '../hooks/useNews';
import '../styles/components/NewsSlider.css';

const NewsSlider = () => {
const [news, setNews] = useState([]);
const { news, loading, fetchNews, hasInitialData } = useNews();
const [currentIndex, setCurrentIndex] = useState(0);
const [loading, setLoading] = useState(true);

useEffect(() => {
const fetchNews = async () => {
try {
const response = await newsAPI.getLatestNews();
if (response.success) {
setNews(response.data || []);
}
} catch (err) {
console.error('Failed to fetch news:', err);
} finally {
setLoading(false);
}
};
fetchNews();
}, []);
// Only fetch if we don't have data yet (initial load)
if (!hasInitialData) {
fetchNews();
}
}, [hasInitialData, fetchNews]);

useEffect(() => {
if (news.length > 1) {
Expand All @@ -32,7 +22,8 @@ const NewsSlider = () => {
}
}, [news]);

if (loading) return <div className="news-slider-loading">Loading latest news...</div>;
// Only show loading state if it's the VERY FIRST fetch
if (loading && !hasInitialData) return <div className="news-slider-loading">Loading latest news...</div>;
if (news.length === 0) return null;

return (
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/OfficerSideBar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import '../styles/components/OfficerSideBar.css';
import { NavLink, useNavigate } from 'react-router-dom';
import { useAuth } from '../auth/AuthContext.jsx';
import { useAuth } from '../hooks/useAuth';
import { usePermissions } from '../hooks/usePermissions.js';
import LogoutModal from './common/LogoutModal.jsx';
import { useMemo, useState } from 'react';
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/Sidebar1.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import '../styles/components/Sidebar1.css';
import { NavLink, useNavigate } from 'react-router-dom';
import { useAuth } from '../auth/AuthContext.jsx';
import { useAuth } from '../hooks/useAuth';
import LogoutModal from './common/LogoutModal.jsx';
import { useMemo, useState } from 'react';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@

import React, { useState, useRef, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { useNotifications } from '../../auth/NotificationsContext.jsx';
import { useAuth } from '../../auth/AuthContext.jsx';
import './NotificationBell.css';
import { useNotifications } from '../../hooks/useNotifications';
import { useAuth } from '../../hooks/useAuth';
import '../../styles/components/NotificationBell.css';

export const NotificationBell = () => {
const { notifications, unreadCount, markAsRead, isLoading } = useNotifications();
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/common/PaymentModal.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { usePayment } from '../../auth/PaymentContext.jsx';
import { usePayment } from '../../hooks/usePayment';
import '../../styles/components/PaymentModal.css';

const PaymentModal = ({ isOpen, onClose, application }) => {
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/layout/AuthenticatedLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import React from 'react';
import { useAuth } from '../../auth/AuthContext.jsx';
import { useAuth } from '../../hooks/useAuth';
import Navigation2 from '../Navigation2.jsx';
import SideBar1 from '../Sidebar1.jsx';
import AdminSideBar from '../AdminSideBar.jsx';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,9 @@ import React, { createContext, useContext, useReducer, useCallback } from 'react
import { applicationReducer, applicationActions } from '../reducers/applicationReducer.js';
import * as applicationsAPI from '../api/applications.api.js';

const ApplicationContext = createContext(null);
export const ApplicationContext = createContext(null);

export const useApplication = () => {
const context = useContext(ApplicationContext);
if (!context) {
throw new Error('useApplication must be used within an ApplicationProvider');
}
return context;
};
// Hook removed and moved to src/hooks/useApplication.js

export const ApplicationProvider = ({ children }) => {
const [state, dispatch] = useReducer(applicationReducer, {
Expand Down Expand Up @@ -90,11 +84,26 @@ export const ApplicationProvider = ({ children }) => {
}, []);

const value = {
// State
applications: state.applications,
selectedApplication: state.selectedApplication,
isLoading: state.isLoading,
isSubmitting: state.isSubmitting,
error: state.error,

// Derived Logic (Merged from useApplicationState standalone)
hasApplications: state.applications.length > 0,
pendingApplications: state.applications.filter(app => app.status === 'pending'),
approvedApplications: state.applications.filter(app => app.status === 'approved'),
rejectedApplications: state.applications.filter(app => app.status === 'rejected'),
applicationCounts: {
total: state.applications.length,
pending: state.applications.filter(app => app.status === 'pending').length,
approved: state.applications.filter(app => app.status === 'approved').length,
rejected: state.applications.filter(app => app.status === 'rejected').length,
},

// Actions
fetchApplications,
getApplicationDetails,
submitApplication,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,11 @@ import * as authAPI from '../api/auth.api.js';
import * as userAPI from '../api/user.api.js';
import { registerRefreshHandler } from '../utils/api.js';

const AuthContext = createContext(null);

export const useAuth = () => {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
};
export const AuthContext = createContext(null);

// Ignored for now next I'll split it into hooks and controllers
// eslint-disable-next-line react-refresh/only-export-components
// Hook removed and moved to src/hooks/useAuth.js

export const AuthProvider = ({ children }) => {
const [state, dispatch] = useReducer(authReducer, {
Expand Down Expand Up @@ -115,7 +111,7 @@ export const AuthProvider = ({ children }) => {
dispatch({ type: authActions.REFRESH_TOKEN_SUCCESS, payload: userData });
return { success: true, data: userData };
} catch (error) {
dispatch({ type: authActions.REFRESH_TOKEN_FAILURE, payload: error.message });
dispatch({ type: authActions.REFRESH_TOKEN_FAILURE }); // I removed the error message from payload to avoid showing token refresh errors to users in login page
return { success: false, error: error.message };
}
}, []);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@ import React, { createContext, useContext, useReducer, useCallback } from 'react
import { chatReducer, chatActions } from '../reducers/chatReducer.js';
import * as chatAPI from '../api/chat.api.js';

const ChatContext = createContext(null);
export const ChatContext = createContext(null);

export const useChat = () => {
const context = useContext(ChatContext);
if (!context) throw new Error('useChat must be used within a ChatProvider');
return context;
};
// Hook removed and moved to src/hooks/useChat.js

export const ChatProvider = ({ children }) => {
const [state, dispatch] = useReducer(chatReducer, {
Expand Down Expand Up @@ -140,6 +136,11 @@ export const ChatProvider = ({ children }) => {

const value = {
...state,
// Derived Logic (Merged from useChatState standalone)
hasConversations: state.conversations.length > 0,
unreadConversations: state.conversations.filter(c => !c.read),

// Actions
fetchConversations,
fetchCitizenConversations,
fetchConversationDetails,
Expand Down
Loading
Loading