Skip to content

Commit 1ff6dd4

Browse files
authored
Enhanced Hospital Management System with Zustand and Lucide React
1 parent 7bdc82a commit 1ff6dd4

File tree

13 files changed

+946
-260
lines changed

13 files changed

+946
-260
lines changed

front_end/package-lock.json

Lines changed: 45 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

front_end/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@
77
"@testing-library/jest-dom": "^6.6.3",
88
"@testing-library/react": "^16.3.0",
99
"@testing-library/user-event": "^13.5.0",
10+
"@types/react": "^19.1.8",
1011
"axios": "^1.9.0",
11-
"lucide-react": "^0.507.0",
12+
"lucide-react": "^0.525.0",
1213
"react": "^19.1.0",
1314
"react-dom": "^19.1.0",
1415
"react-router-dom": "^7.5.3",
1516
"react-scripts": "5.0.1",
1617
"recharts": "^2.15.3",
17-
"web-vitals": "^2.1.4"
18+
"web-vitals": "^2.1.4",
19+
"zustand": "^5.0.6"
1820
},
1921
"scripts": {
2022
"start": "react-scripts start",
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import React from 'react';
2+
import { Menu, Bell, Search, Settings } from 'lucide-react';
3+
import useStore from '../../store/useStore';
4+
5+
const Header = ({ title, subtitle }) => {
6+
const { ui, setUI } = useStore();
7+
8+
return (
9+
<header className="bg-white shadow-sm border-b border-gray-200 px-6 py-4">
10+
<div className="flex items-center justify-between">
11+
{/* Left side */}
12+
<div className="flex items-center space-x-4">
13+
<button
14+
onClick={() => setUI({ sidebarOpen: !ui.sidebarOpen })}
15+
className="lg:hidden p-2 rounded-lg hover:bg-gray-100 transition-colors"
16+
>
17+
<Menu className="w-6 h-6 text-gray-600" />
18+
</button>
19+
20+
<div>
21+
<h1 className="text-2xl font-bold text-gray-800">{title}</h1>
22+
{subtitle && <p className="text-sm text-gray-600 mt-1">{subtitle}</p>}
23+
</div>
24+
</div>
25+
26+
{/* Right side */}
27+
<div className="flex items-center space-x-4">
28+
{/* Search */}
29+
<div className="hidden md:flex items-center space-x-2 bg-gray-100 rounded-lg px-3 py-2">
30+
<Search className="w-4 h-4 text-gray-500" />
31+
<input
32+
type="text"
33+
placeholder="Search..."
34+
className="bg-transparent border-none outline-none text-sm text-gray-700 placeholder-gray-500"
35+
/>
36+
</div>
37+
38+
{/* Notifications */}
39+
<button className="relative p-2 rounded-lg hover:bg-gray-100 transition-colors">
40+
<Bell className="w-5 h-5 text-gray-600" />
41+
{ui.notifications.length > 0 && (
42+
<span className="absolute -top-1 -right-1 w-5 h-5 bg-red-500 text-white text-xs rounded-full flex items-center justify-center">
43+
{ui.notifications.length}
44+
</span>
45+
)}
46+
</button>
47+
48+
{/* Settings */}
49+
<button className="p-2 rounded-lg hover:bg-gray-100 transition-colors">
50+
<Settings className="w-5 h-5 text-gray-600" />
51+
</button>
52+
</div>
53+
</div>
54+
</header>
55+
);
56+
};
57+
58+
export default Header;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import React from 'react';
2+
import Sidebar from './Sidebar';
3+
import Header from './Header';
4+
import NotificationToast from '../UI/NotificationToast';
5+
6+
const Layout = ({ children, title, subtitle }) => {
7+
return (
8+
<div className="min-h-screen bg-gray-50 flex">
9+
<Sidebar />
10+
<div className="flex-1 flex flex-col lg:ml-0">
11+
<Header title={title} subtitle={subtitle} />
12+
<main className="flex-1 overflow-auto">
13+
{children}
14+
</main>
15+
</div>
16+
<NotificationToast />
17+
</div>
18+
);
19+
};
20+
21+
export default Layout;
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import React from 'react';
2+
import { useNavigate, useLocation } from 'react-router-dom';
3+
import {
4+
Home,
5+
Users,
6+
Building2,
7+
Truck,
8+
DollarSign,
9+
Package,
10+
FlaskConical,
11+
Stethoscope,
12+
X,
13+
Menu
14+
} from 'lucide-react';
15+
import useStore from '../../store/useStore';
16+
17+
const Sidebar = () => {
18+
const navigate = useNavigate();
19+
const location = useLocation();
20+
const { ui, setUI } = useStore();
21+
22+
const menuItems = [
23+
{ path: '/', icon: Home, label: 'Dashboard', color: 'text-blue-600' },
24+
{ path: '/patient', icon: Users, label: 'Patients', color: 'text-green-600' },
25+
{ path: '/ward', icon: Building2, label: 'Wards', color: 'text-purple-600' },
26+
{ path: '/ambu', icon: Truck, label: 'AmbuTrack', color: 'text-orange-600' },
27+
{ path: '/fin', icon: DollarSign, label: 'Finance', color: 'text-blue-600' },
28+
{ path: '/med', icon: Package, label: 'Pharmacy', color: 'text-red-600' },
29+
{ path: '/lab', icon: FlaskConical, label: 'Laboratory', color: 'text-indigo-600' },
30+
{ path: '/doc', icon: Stethoscope, label: 'Doctors', color: 'text-teal-600' },
31+
];
32+
33+
const handleNavigation = (path) => {
34+
navigate(path);
35+
if (window.innerWidth < 1024) {
36+
setUI({ sidebarOpen: false });
37+
}
38+
};
39+
40+
return (
41+
<>
42+
{/* Mobile overlay */}
43+
{ui.sidebarOpen && (
44+
<div
45+
className="fixed inset-0 bg-black bg-opacity-50 z-40 lg:hidden"
46+
onClick={() => setUI({ sidebarOpen: false })}
47+
/>
48+
)}
49+
50+
{/* Sidebar */}
51+
<div className={`
52+
fixed top-0 left-0 h-full w-64 bg-white shadow-xl z-50 transform transition-transform duration-300 ease-in-out
53+
${ui.sidebarOpen ? 'translate-x-0' : '-translate-x-full'}
54+
lg:translate-x-0 lg:static lg:z-auto
55+
`}>
56+
{/* Header */}
57+
<div className="flex items-center justify-between p-6 border-b border-gray-200">
58+
<div className="flex items-center space-x-3">
59+
<div className="w-10 h-10 bg-gradient-to-br from-blue-500 to-purple-600 rounded-xl flex items-center justify-center">
60+
<Stethoscope className="w-6 h-6 text-white" />
61+
</div>
62+
<div>
63+
<h1 className="text-xl font-bold text-gray-800">HealVista</h1>
64+
<p className="text-xs text-gray-500">Hospital Management</p>
65+
</div>
66+
</div>
67+
<button
68+
onClick={() => setUI({ sidebarOpen: false })}
69+
className="lg:hidden p-2 rounded-lg hover:bg-gray-100 transition-colors"
70+
>
71+
<X className="w-5 h-5 text-gray-600" />
72+
</button>
73+
</div>
74+
75+
{/* Navigation */}
76+
<nav className="p-4 space-y-2">
77+
{menuItems.map((item) => {
78+
const Icon = item.icon;
79+
const isActive = location.pathname === item.path;
80+
81+
return (
82+
<button
83+
key={item.path}
84+
onClick={() => handleNavigation(item.path)}
85+
className={`
86+
w-full flex items-center space-x-3 px-4 py-3 rounded-xl transition-all duration-200
87+
${isActive
88+
? 'bg-gradient-to-r from-blue-50 to-purple-50 border border-blue-200 shadow-sm'
89+
: 'hover:bg-gray-50'
90+
}
91+
`}
92+
>
93+
<Icon className={`w-5 h-5 ${isActive ? item.color : 'text-gray-600'}`} />
94+
<span className={`font-medium ${isActive ? 'text-gray-800' : 'text-gray-600'}`}>
95+
{item.label}
96+
</span>
97+
</button>
98+
);
99+
})}
100+
</nav>
101+
102+
{/* Footer */}
103+
<div className="absolute bottom-0 left-0 right-0 p-4 border-t border-gray-200">
104+
<div className="flex items-center space-x-3 p-3 rounded-xl bg-gray-50">
105+
<div className="w-8 h-8 bg-gradient-to-br from-green-400 to-blue-500 rounded-full flex items-center justify-center">
106+
<span className="text-white text-sm font-semibold">DS</span>
107+
</div>
108+
<div className="flex-1 min-w-0">
109+
<p className="text-sm font-medium text-gray-800 truncate">Dr. Smith</p>
110+
<p className="text-xs text-gray-500">Administrator</p>
111+
</div>
112+
</div>
113+
</div>
114+
</div>
115+
</>
116+
);
117+
};
118+
119+
export default Sidebar;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import React from 'react';
2+
import { Loader2 } from 'lucide-react';
3+
4+
const Button = ({
5+
children,
6+
variant = 'primary',
7+
size = 'md',
8+
loading = false,
9+
disabled = false,
10+
icon: Icon,
11+
className = '',
12+
...props
13+
}) => {
14+
const baseClasses = 'inline-flex items-center justify-center font-medium rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2';
15+
16+
const variants = {
17+
primary: 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500 shadow-sm',
18+
secondary: 'bg-gray-100 text-gray-700 hover:bg-gray-200 focus:ring-gray-500',
19+
success: 'bg-green-600 text-white hover:bg-green-700 focus:ring-green-500 shadow-sm',
20+
danger: 'bg-red-600 text-white hover:bg-red-700 focus:ring-red-500 shadow-sm',
21+
warning: 'bg-yellow-600 text-white hover:bg-yellow-700 focus:ring-yellow-500 shadow-sm',
22+
outline: 'border border-gray-300 bg-white text-gray-700 hover:bg-gray-50 focus:ring-blue-500',
23+
ghost: 'text-gray-600 hover:bg-gray-100 focus:ring-gray-500',
24+
};
25+
26+
const sizes = {
27+
sm: 'px-3 py-1.5 text-sm',
28+
md: 'px-4 py-2 text-sm',
29+
lg: 'px-6 py-3 text-base',
30+
xl: 'px-8 py-4 text-lg',
31+
};
32+
33+
const isDisabled = disabled || loading;
34+
35+
return (
36+
<button
37+
className={`
38+
${baseClasses}
39+
${variants[variant]}
40+
${sizes[size]}
41+
${isDisabled ? 'opacity-50 cursor-not-allowed' : ''}
42+
${className}
43+
`}
44+
disabled={isDisabled}
45+
{...props}
46+
>
47+
{loading ? (
48+
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
49+
) : Icon ? (
50+
<Icon className="w-4 h-4 mr-2" />
51+
) : null}
52+
{children}
53+
</button>
54+
);
55+
};
56+
57+
export default Button;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import React from 'react';
2+
3+
const Card = ({
4+
children,
5+
className = '',
6+
hover = false,
7+
padding = 'p-6',
8+
onClick,
9+
gradient = false
10+
}) => {
11+
return (
12+
<div
13+
className={`
14+
bg-white rounded-xl shadow-sm border border-gray-200 transition-all duration-200
15+
${hover ? 'hover:shadow-md hover:-translate-y-1 cursor-pointer' : ''}
16+
${gradient ? 'bg-gradient-to-br from-white to-gray-50' : ''}
17+
${padding}
18+
${className}
19+
`}
20+
onClick={onClick}
21+
>
22+
{children}
23+
</div>
24+
);
25+
};
26+
27+
export default Card;

0 commit comments

Comments
 (0)