create-steve-rogers 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/apps/HMSR/backend/.env.example +6 -0
  2. package/apps/HMSR/backend/config/db.js +14 -0
  3. package/apps/HMSR/backend/database/schema.sql +55 -0
  4. package/apps/HMSR/backend/database/seed.js +53 -0
  5. package/apps/HMSR/backend/middleware/auth.js +26 -0
  6. package/apps/HMSR/backend/package-lock.json +1130 -0
  7. package/apps/HMSR/backend/package.json +19 -0
  8. package/apps/HMSR/backend/routes/appointments.js +157 -0
  9. package/apps/HMSR/backend/routes/auth.js +115 -0
  10. package/apps/HMSR/backend/routes/medicalReports.js +126 -0
  11. package/apps/HMSR/backend/routes/patients.js +103 -0
  12. package/apps/HMSR/backend/routes/reports.js +60 -0
  13. package/apps/HMSR/backend/server.js +38 -0
  14. package/apps/HMSR/frontend/package-lock.json +17217 -0
  15. package/apps/HMSR/frontend/package.json +23 -0
  16. package/apps/HMSR/frontend/public/index.html +17 -0
  17. package/apps/HMSR/frontend/src/App.js +91 -0
  18. package/apps/HMSR/frontend/src/components/Layout.js +58 -0
  19. package/apps/HMSR/frontend/src/components/PrivateRoute.js +25 -0
  20. package/apps/HMSR/frontend/src/context/AuthContext.js +54 -0
  21. package/apps/HMSR/frontend/src/index.css +581 -0
  22. package/apps/HMSR/frontend/src/index.js +17 -0
  23. package/apps/HMSR/frontend/src/pages/Appointments.js +250 -0
  24. package/apps/HMSR/frontend/src/pages/Dashboard.js +116 -0
  25. package/apps/HMSR/frontend/src/pages/Login.js +73 -0
  26. package/apps/HMSR/frontend/src/pages/MedicalReports.js +217 -0
  27. package/apps/HMSR/frontend/src/pages/Patients.js +196 -0
  28. package/apps/HMSR/frontend/src/pages/Register.js +98 -0
  29. package/apps/HMSR/frontend/src/pages/Reports.js +170 -0
  30. package/apps/HMSR/frontend/src/services/api.js +15 -0
  31. package/apps/config.js +8 -0
  32. package/exclude.txt +1 -0
  33. package/index.js +55 -0
  34. package/package.json +14 -0
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "hms-frontend",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "dependencies": {
6
+ "axios": "^1.7.7",
7
+ "react": "^18.3.1",
8
+ "react-dom": "^18.3.1",
9
+ "react-router-dom": "^6.26.2",
10
+ "react-scripts": "5.0.1"
11
+ },
12
+ "scripts": {
13
+ "start": "react-scripts start",
14
+ "build": "react-scripts build",
15
+ "test": "react-scripts test",
16
+ "eject": "react-scripts eject"
17
+ },
18
+ "proxy": "http://localhost:5000",
19
+ "browserslist": {
20
+ "production": [">0.2%", "not dead", "not op_mini all"],
21
+ "development": ["last 1 chrome version", "last 1 firefox version", "last 1 safari version"]
22
+ }
23
+ }
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <meta name="theme-color" content="#0d6e6e" />
7
+ <meta name="description" content="King Faisal Hospital Rwanda - Appointment & Medical Report System" />
8
+ <title>KFH Rwanda HMS</title>
9
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
10
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
11
+ <link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700&display=swap" rel="stylesheet" />
12
+ </head>
13
+ <body>
14
+ <noscript>You need to enable JavaScript to run this app.</noscript>
15
+ <div id="root"></div>
16
+ </body>
17
+ </html>
@@ -0,0 +1,91 @@
1
+ import { Routes, Route, Navigate } from 'react-router-dom';
2
+ import { useAuth } from './context/AuthContext';
3
+ import PrivateRoute from './components/PrivateRoute';
4
+ import Layout from './components/Layout';
5
+ import Login from './pages/Login';
6
+ import Register from './pages/Register';
7
+ import Dashboard from './pages/Dashboard';
8
+ import Patients from './pages/Patients';
9
+ import Appointments from './pages/Appointments';
10
+ import MedicalReports from './pages/MedicalReports';
11
+ import Reports from './pages/Reports';
12
+
13
+ function App() {
14
+ const { user, loading } = useAuth();
15
+
16
+ if (loading) {
17
+ return (
18
+ <div className="loading-screen">
19
+ <div className="spinner" />
20
+ <p>Loading...</p>
21
+ </div>
22
+ );
23
+ }
24
+
25
+ return (
26
+ <Routes>
27
+ <Route
28
+ path="/login"
29
+ element={user ? <Navigate to="/dashboard" replace /> : <Login />}
30
+ />
31
+ <Route
32
+ path="/register"
33
+ element={user ? <Navigate to="/dashboard" replace /> : <Register />}
34
+ />
35
+ <Route
36
+ path="/dashboard"
37
+ element={
38
+ <PrivateRoute>
39
+ <Layout>
40
+ <Dashboard />
41
+ </Layout>
42
+ </PrivateRoute>
43
+ }
44
+ />
45
+ <Route
46
+ path="/patients"
47
+ element={
48
+ <PrivateRoute roles={['receptionist']}>
49
+ <Layout>
50
+ <Patients />
51
+ </Layout>
52
+ </PrivateRoute>
53
+ }
54
+ />
55
+ <Route
56
+ path="/appointments"
57
+ element={
58
+ <PrivateRoute roles={['receptionist', 'doctor']}>
59
+ <Layout>
60
+ <Appointments />
61
+ </Layout>
62
+ </PrivateRoute>
63
+ }
64
+ />
65
+ <Route
66
+ path="/medical-reports"
67
+ element={
68
+ <PrivateRoute roles={['doctor']}>
69
+ <Layout>
70
+ <MedicalReports />
71
+ </Layout>
72
+ </PrivateRoute>
73
+ }
74
+ />
75
+ <Route
76
+ path="/reports"
77
+ element={
78
+ <PrivateRoute roles={['receptionist', 'doctor']}>
79
+ <Layout>
80
+ <Reports />
81
+ </Layout>
82
+ </PrivateRoute>
83
+ }
84
+ />
85
+ <Route path="/" element={<Navigate to="/dashboard" replace />} />
86
+ <Route path="*" element={<Navigate to="/dashboard" replace />} />
87
+ </Routes>
88
+ );
89
+ }
90
+
91
+ export default App;
@@ -0,0 +1,58 @@
1
+ import { Link, useLocation, useNavigate } from 'react-router-dom';
2
+ import { useAuth } from '../context/AuthContext';
3
+
4
+ export default function Layout({ children }) {
5
+ const { user, logout } = useAuth();
6
+ const location = useLocation();
7
+ const navigate = useNavigate();
8
+
9
+ const handleLogout = () => {
10
+ logout();
11
+ navigate('/login');
12
+ };
13
+
14
+ const navItems = [
15
+ { path: '/dashboard', label: 'Dashboard', roles: ['receptionist', 'doctor'] },
16
+ { path: '/patients', label: 'Patients', roles: ['receptionist'] },
17
+ { path: '/appointments', label: 'Appointments', roles: ['receptionist', 'doctor'] },
18
+ { path: '/medical-reports', label: 'Medical Reports', roles: ['doctor'] },
19
+ { path: '/reports', label: 'Reports', roles: ['receptionist', 'doctor'] },
20
+ ];
21
+
22
+ const visibleNav = navItems.filter((item) => item.roles.includes(user?.role));
23
+
24
+ return (
25
+ <div className="app-layout">
26
+ <aside className="sidebar">
27
+ <div className="sidebar-brand">
28
+ <span className="brand-icon">+</span>
29
+ <div>
30
+ <h1>KFH Rwanda</h1>
31
+ <p>Hospital Management</p>
32
+ </div>
33
+ </div>
34
+ <nav className="sidebar-nav">
35
+ {visibleNav.map((item) => (
36
+ <Link
37
+ key={item.path}
38
+ to={item.path}
39
+ className={location.pathname === item.path ? 'active' : ''}
40
+ >
41
+ {item.label}
42
+ </Link>
43
+ ))}
44
+ </nav>
45
+ <div className="sidebar-footer">
46
+ <div className="user-info">
47
+ <strong>{user?.full_name}</strong>
48
+ <span className={`role-badge role-${user?.role}`}>{user?.role}</span>
49
+ </div>
50
+ <button type="button" className="btn btn-outline btn-sm" onClick={handleLogout}>
51
+ Logout
52
+ </button>
53
+ </div>
54
+ </aside>
55
+ <main className="main-content">{children}</main>
56
+ </div>
57
+ );
58
+ }
@@ -0,0 +1,25 @@
1
+ import { Navigate } from 'react-router-dom';
2
+ import { useAuth } from '../context/AuthContext';
3
+
4
+ export default function PrivateRoute({ children, roles }) {
5
+ const { user, loading } = useAuth();
6
+
7
+ if (loading) {
8
+ return (
9
+ <div className="loading-screen">
10
+ <div className="spinner" />
11
+ <p>Loading...</p>
12
+ </div>
13
+ );
14
+ }
15
+
16
+ if (!user) {
17
+ return <Navigate to="/login" replace />;
18
+ }
19
+
20
+ if (roles && !roles.includes(user.role)) {
21
+ return <Navigate to="/dashboard" replace />;
22
+ }
23
+
24
+ return children;
25
+ }
@@ -0,0 +1,54 @@
1
+ import React, { createContext, useContext, useState, useEffect } from 'react';
2
+ import api from '../services/api';
3
+
4
+ const AuthContext = createContext(null);
5
+
6
+ export function AuthProvider({ children }) {
7
+ const [user, setUser] = useState(null);
8
+ const [loading, setLoading] = useState(true);
9
+
10
+ useEffect(() => {
11
+ const token = localStorage.getItem('token');
12
+ const savedUser = localStorage.getItem('user');
13
+ if (token && savedUser) {
14
+ setUser(JSON.parse(savedUser));
15
+ api
16
+ .get('/auth/me')
17
+ .then((res) => {
18
+ setUser(res.data);
19
+ localStorage.setItem('user', JSON.stringify(res.data));
20
+ })
21
+ .catch(() => logout())
22
+ .finally(() => setLoading(false));
23
+ } else {
24
+ setLoading(false);
25
+ }
26
+ }, []);
27
+
28
+ const login = async (email, password) => {
29
+ const res = await api.post('/auth/login', { email, password });
30
+ localStorage.setItem('token', res.data.token);
31
+ localStorage.setItem('user', JSON.stringify(res.data.user));
32
+ setUser(res.data.user);
33
+ return res.data;
34
+ };
35
+
36
+ const register = async (data) => {
37
+ const res = await api.post('/auth/register', data);
38
+ return res.data;
39
+ };
40
+
41
+ const logout = () => {
42
+ localStorage.removeItem('token');
43
+ localStorage.removeItem('user');
44
+ setUser(null);
45
+ };
46
+
47
+ return (
48
+ <AuthContext.Provider value={{ user, loading, login, register, logout }}>
49
+ {children}
50
+ </AuthContext.Provider>
51
+ );
52
+ }
53
+
54
+ export const useAuth = () => useContext(AuthContext);