create-jinmankn-app 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 (117) hide show
  1. package/bin/index.js +76 -0
  2. package/package.json +20 -0
  3. package/templates/blueprint/BLUEPRINT_REPRODUCTION_PROMPT.md +996 -0
  4. package/templates/blueprint/HOW_IT_WORKS.md +286 -0
  5. package/templates/blueprint/README.md +123 -0
  6. package/templates/blueprint/backend/config/db.js +12 -0
  7. package/templates/blueprint/backend/controllers/authController.js +90 -0
  8. package/templates/blueprint/backend/controllers/itemController.js +74 -0
  9. package/templates/blueprint/backend/middleware/auth.js +32 -0
  10. package/templates/blueprint/backend/middleware/errorHandler.js +23 -0
  11. package/templates/blueprint/backend/models/Item.js +26 -0
  12. package/templates/blueprint/backend/models/User.js +28 -0
  13. package/templates/blueprint/backend/package-lock.json +2190 -0
  14. package/templates/blueprint/backend/package.json +23 -0
  15. package/templates/blueprint/backend/routes/authRoutes.js +11 -0
  16. package/templates/blueprint/backend/routes/healthRoutes.js +9 -0
  17. package/templates/blueprint/backend/routes/itemRoutes.js +21 -0
  18. package/templates/blueprint/backend/server.js +29 -0
  19. package/templates/blueprint/frontend/.env.example +1 -0
  20. package/templates/blueprint/frontend/index.html +13 -0
  21. package/templates/blueprint/frontend/package-lock.json +2844 -0
  22. package/templates/blueprint/frontend/package.json +23 -0
  23. package/templates/blueprint/frontend/public/favicon.svg +4 -0
  24. package/templates/blueprint/frontend/src/App.jsx +78 -0
  25. package/templates/blueprint/frontend/src/assets/logo.svg +4 -0
  26. package/templates/blueprint/frontend/src/components/DashboardLayout.jsx +103 -0
  27. package/templates/blueprint/frontend/src/components/ProtectedRoute.jsx +18 -0
  28. package/templates/blueprint/frontend/src/index.css +1 -0
  29. package/templates/blueprint/frontend/src/main.jsx +13 -0
  30. package/templates/blueprint/frontend/src/pages/DashboardHome.jsx +74 -0
  31. package/templates/blueprint/frontend/src/pages/Items.jsx +243 -0
  32. package/templates/blueprint/frontend/src/pages/Login.jsx +101 -0
  33. package/templates/blueprint/frontend/src/pages/Profile.jsx +79 -0
  34. package/templates/blueprint/frontend/src/pages/Register.jsx +122 -0
  35. package/templates/blueprint/frontend/src/pages/Report.jsx +124 -0
  36. package/templates/blueprint/frontend/vite.config.js +10 -0
  37. package/templates/blueprint/package.json +13 -0
  38. package/templates/blueprint/scripts/pack-blueprint.ps1 +18 -0
  39. package/templates/chom/Backend/app.js +25 -0
  40. package/templates/chom/Backend/package-lock.json +1551 -0
  41. package/templates/chom/Backend/package.json +23 -0
  42. package/templates/chom/Backend/seedAdmin.js +21 -0
  43. package/templates/chom/Backend/src/controllers/payment.c.js +57 -0
  44. package/templates/chom/Backend/src/controllers/students.c.js +58 -0
  45. package/templates/chom/Backend/src/controllers/users.c.js +62 -0
  46. package/templates/chom/Backend/src/middleware/authentication.js +18 -0
  47. package/templates/chom/Backend/src/models/payment.m.js +13 -0
  48. package/templates/chom/Backend/src/models/students.m.js +10 -0
  49. package/templates/chom/Backend/src/models/users.m.js +11 -0
  50. package/templates/chom/Backend/src/routes/users.r.js +21 -0
  51. package/templates/chom/Frontend/README.md +16 -0
  52. package/templates/chom/Frontend/eslint.config.js +21 -0
  53. package/templates/chom/Frontend/index.html +13 -0
  54. package/templates/chom/Frontend/package-lock.json +3075 -0
  55. package/templates/chom/Frontend/package.json +31 -0
  56. package/templates/chom/Frontend/public/favicon.svg +1 -0
  57. package/templates/chom/Frontend/public/icons.svg +24 -0
  58. package/templates/chom/Frontend/src/App.css +189 -0
  59. package/templates/chom/Frontend/src/App.jsx +28 -0
  60. package/templates/chom/Frontend/src/api/api.jsx +27 -0
  61. package/templates/chom/Frontend/src/assets/hero.png +0 -0
  62. package/templates/chom/Frontend/src/assets/react.svg +1 -0
  63. package/templates/chom/Frontend/src/assets/vite.svg +1 -0
  64. package/templates/chom/Frontend/src/components/Navbar.jsx +21 -0
  65. package/templates/chom/Frontend/src/index.css +8 -0
  66. package/templates/chom/Frontend/src/main.jsx +10 -0
  67. package/templates/chom/Frontend/src/pages/Dashboard.jsx +21 -0
  68. package/templates/chom/Frontend/src/pages/Landing.jsx +39 -0
  69. package/templates/chom/Frontend/src/pages/Login.jsx +49 -0
  70. package/templates/chom/Frontend/src/pages/Overview.jsx +42 -0
  71. package/templates/chom/Frontend/src/pages/Register.jsx +76 -0
  72. package/templates/chom/Frontend/src/pages/Students.jsx +14 -0
  73. package/templates/chom/Frontend/vite.config.js +8 -0
  74. package/templates/chom/package.json +13 -0
  75. package/templates/hospital-faisal/backend/.env.example +9 -0
  76. package/templates/hospital-faisal/backend/config/db.js +96 -0
  77. package/templates/hospital-faisal/backend/controllers/appointmentController.js +164 -0
  78. package/templates/hospital-faisal/backend/controllers/authController.js +106 -0
  79. package/templates/hospital-faisal/backend/controllers/hospitalReportController.js +72 -0
  80. package/templates/hospital-faisal/backend/controllers/medicalReportController.js +105 -0
  81. package/templates/hospital-faisal/backend/controllers/patientController.js +98 -0
  82. package/templates/hospital-faisal/backend/database/schema.sql +47 -0
  83. package/templates/hospital-faisal/backend/middleware/auth.js +30 -0
  84. package/templates/hospital-faisal/backend/middleware/errorHandler.js +23 -0
  85. package/templates/hospital-faisal/backend/middleware/role.js +6 -0
  86. package/templates/hospital-faisal/backend/package-lock.json +2092 -0
  87. package/templates/hospital-faisal/backend/package.json +23 -0
  88. package/templates/hospital-faisal/backend/routes/appointmentRoutes.js +25 -0
  89. package/templates/hospital-faisal/backend/routes/authRoutes.js +12 -0
  90. package/templates/hospital-faisal/backend/routes/healthRoutes.js +9 -0
  91. package/templates/hospital-faisal/backend/routes/hospitalReportRoutes.js +10 -0
  92. package/templates/hospital-faisal/backend/routes/medicalReportRoutes.js +16 -0
  93. package/templates/hospital-faisal/backend/routes/patientRoutes.js +22 -0
  94. package/templates/hospital-faisal/backend/server.js +46 -0
  95. package/templates/hospital-faisal/frontend/.env.example +1 -0
  96. package/templates/hospital-faisal/frontend/index.html +10 -0
  97. package/templates/hospital-faisal/frontend/package-lock.json +2844 -0
  98. package/templates/hospital-faisal/frontend/package.json +23 -0
  99. package/templates/hospital-faisal/frontend/public/favicon.svg +4 -0
  100. package/templates/hospital-faisal/frontend/src/App.jsx +56 -0
  101. package/templates/hospital-faisal/frontend/src/api.js +20 -0
  102. package/templates/hospital-faisal/frontend/src/assets/logo.svg +4 -0
  103. package/templates/hospital-faisal/frontend/src/components/DashboardLayout.jsx +114 -0
  104. package/templates/hospital-faisal/frontend/src/components/ProtectedRoute.jsx +18 -0
  105. package/templates/hospital-faisal/frontend/src/components/RoleRoute.jsx +14 -0
  106. package/templates/hospital-faisal/frontend/src/index.css +1 -0
  107. package/templates/hospital-faisal/frontend/src/main.jsx +13 -0
  108. package/templates/hospital-faisal/frontend/src/pages/Appointments.jsx +305 -0
  109. package/templates/hospital-faisal/frontend/src/pages/DashboardHome.jsx +105 -0
  110. package/templates/hospital-faisal/frontend/src/pages/Login.jsx +98 -0
  111. package/templates/hospital-faisal/frontend/src/pages/MedicalReports.jsx +182 -0
  112. package/templates/hospital-faisal/frontend/src/pages/Patients.jsx +237 -0
  113. package/templates/hospital-faisal/frontend/src/pages/Profile.jsx +78 -0
  114. package/templates/hospital-faisal/frontend/src/pages/Register.jsx +133 -0
  115. package/templates/hospital-faisal/frontend/src/pages/Report.jsx +167 -0
  116. package/templates/hospital-faisal/frontend/vite.config.js +10 -0
  117. package/templates/hospital-faisal/package.json +13 -0
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "blueprint-frontend",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "axios": "^1.7.9",
13
+ "react": "^18.3.1",
14
+ "react-dom": "^18.3.1",
15
+ "react-router-dom": "^6.28.1"
16
+ },
17
+ "devDependencies": {
18
+ "@tailwindcss/vite": "^4.1.8",
19
+ "@vitejs/plugin-react": "^4.3.4",
20
+ "tailwindcss": "^4.1.8",
21
+ "vite": "^6.0.6"
22
+ }
23
+ }
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none">
2
+ <rect width="32" height="32" rx="6" fill="#2563eb"/>
3
+ <text x="16" y="22" text-anchor="middle" fill="white" font-size="14" font-family="system-ui,sans-serif" font-weight="700">B</text>
4
+ </svg>
@@ -0,0 +1,78 @@
1
+ import { Routes, Route, Navigate } from 'react-router-dom';
2
+
3
+ import ProtectedRoute from './components/ProtectedRoute';
4
+
5
+ import DashboardLayout from './components/DashboardLayout';
6
+
7
+ import Login from './pages/Login';
8
+
9
+ import Register from './pages/Register';
10
+
11
+ import DashboardHome from './pages/DashboardHome';
12
+
13
+ import Items from './pages/Items';
14
+
15
+ import Report from './pages/Report';
16
+
17
+ import Profile from './pages/Profile';
18
+
19
+
20
+
21
+ function App() {
22
+
23
+ return (
24
+
25
+ <Routes>
26
+
27
+ <Route path="/login" element={<Login />} />
28
+
29
+ <Route path="/register" element={<Register />} />
30
+
31
+
32
+
33
+ <Route
34
+
35
+ path="/dashboard"
36
+
37
+ element={
38
+
39
+ <ProtectedRoute>
40
+
41
+ <DashboardLayout />
42
+
43
+ </ProtectedRoute>
44
+
45
+ }
46
+
47
+ >
48
+
49
+ {/* dashboard routes — add a <Route> here when you add a new <Link> in DashboardLayout.jsx */}
50
+
51
+ <Route index element={<DashboardHome />} />
52
+
53
+ <Route path="items" element={<Items />} />
54
+
55
+ <Route path="report" element={<Report />} />
56
+
57
+ <Route path="profile" element={<Profile />} />
58
+
59
+ </Route>
60
+
61
+
62
+
63
+ <Route path="/items" element={<Navigate to="/dashboard/items" replace />} />
64
+
65
+ <Route path="/" element={<Navigate to="/dashboard" replace />} />
66
+
67
+ <Route path="*" element={<Navigate to="/dashboard" replace />} />
68
+
69
+ </Routes>
70
+
71
+ );
72
+
73
+ }
74
+
75
+
76
+
77
+ export default App;
78
+
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40" fill="none">
2
+ <rect width="40" height="40" rx="8" fill="#1e293b"/>
3
+ <text x="20" y="27" text-anchor="middle" fill="white" font-size="18" font-family="system-ui,sans-serif" font-weight="700">B</text>
4
+ </svg>
@@ -0,0 +1,103 @@
1
+ import { useEffect, useState } from 'react';
2
+ import axios from 'axios';
3
+ import { Link, Outlet, useNavigate } from 'react-router-dom';
4
+ import logo from '../assets/logo.svg';
5
+
6
+
7
+ function DashboardLayout() {
8
+ const navigate = useNavigate();
9
+ const [user, setUser] = useState(null);
10
+ const [error, setError] = useState('');
11
+
12
+ const handleRetrieve = async () => {
13
+ const storedUser = localStorage.getItem('user');
14
+ if (storedUser) {
15
+ setUser(JSON.parse(storedUser));
16
+ }
17
+
18
+ try {
19
+ const response = await axios.get('http://localhost:5000/api/auth/me', {
20
+ headers: {
21
+ Authorization: `Bearer ${localStorage.getItem('token')}`,
22
+ },
23
+ });
24
+ setUser(response.data.user);
25
+ localStorage.setItem('user', JSON.stringify(response.data.user));
26
+ } catch (err) {
27
+ if (!storedUser) {
28
+ setError(err.response?.data?.message || 'Could not load user');
29
+ }
30
+ console.error('Server Error:', err.response?.data || err.message);
31
+ }
32
+ };
33
+
34
+ useEffect(() => {
35
+ handleRetrieve();
36
+ }, []);
37
+
38
+ const handleLogout = () => {
39
+ localStorage.removeItem('token');
40
+ localStorage.removeItem('user');
41
+ navigate('/login');
42
+ };
43
+
44
+ return (
45
+ <div className="min-h-screen bg-slate-100">
46
+ <aside className="fixed inset-y-0 left-0 w-64 bg-slate-900 text-slate-100 flex flex-col">
47
+ <div className="px-5 py-6 border-b border-slate-700">
48
+ {/* <img src={logo} alt="" className="h-9 w-9 mb-3" /> */}
49
+ <p className="text-xs font-semibold uppercase tracking-wider text-slate-400">Blueprint</p>
50
+ <h1 className="text-lg font-bold text-white mt-1">Dashboard</h1>
51
+ </div>
52
+
53
+ {/* Sidebar links — add or edit <Link> lines here. Change the text to rename a link. */}
54
+ <div className="flex-1 px-3 py-4 space-y-1">
55
+ <Link to="/dashboard" className="block rounded-lg px-3 py-2.5 text-sm font-medium text-slate-300 hover:bg-slate-800 hover:text-white transition-colors">
56
+ Overview
57
+ </Link>
58
+ <Link to="/dashboard/items" className="block rounded-lg px-3 py-2.5 text-sm font-medium text-slate-300 hover:bg-slate-800 hover:text-white transition-colors">
59
+ Items
60
+ </Link>
61
+ <Link to="/dashboard/profile" className="block rounded-lg px-3 py-2.5 text-sm font-medium text-slate-300 hover:bg-slate-800 hover:text-white transition-colors">
62
+ Profile
63
+ </Link>
64
+ <Link to="/dashboard/report" className="block rounded-lg px-3 py-2.5 text-sm font-medium text-slate-300 hover:bg-slate-800 hover:text-white transition-colors">
65
+ Report
66
+ </Link>
67
+ </div>
68
+
69
+ <div className="px-4 py-4 border-t border-slate-700">
70
+ {error && <p className="text-xs text-red-400 mb-2">{error}</p>}
71
+ {user && (
72
+ <p className="text-sm font-medium text-white truncate">{user.name}</p>
73
+ )}
74
+ {user && (
75
+ <p className="text-xs text-slate-400 truncate mt-0.5">{user.email}</p>
76
+ )}
77
+ <button
78
+ type="button"
79
+ onClick={handleLogout}
80
+ className="mt-3 w-full text-sm font-medium text-slate-300 hover:text-white border border-slate-600 rounded-lg px-3 py-2 hover:bg-slate-800 transition-colors"
81
+ >
82
+ Log out
83
+ </button>
84
+ </div>
85
+ </aside>
86
+
87
+ <div className="ml-64 flex min-h-screen flex-col">
88
+ <header className="bg-white border-b border-slate-200 px-6 py-4">
89
+ <p className="text-sm text-slate-500">Welcome back</p>
90
+ <h2 className="text-xl font-semibold text-slate-800">
91
+ {user ? `Hello, ${user.name}` : 'Dashboard'}
92
+ </h2>
93
+ </header>
94
+
95
+ <main className="flex-1 p-6 overflow-auto">
96
+ <Outlet />
97
+ </main>
98
+ </div>
99
+ </div>
100
+ );
101
+ }
102
+
103
+ export default DashboardLayout;
@@ -0,0 +1,18 @@
1
+ // this component is helping us to protect our routes so that only authenticated users can access the routes
2
+ import { Navigate } from 'react-router-dom';
3
+
4
+ // this function is firstly checking if the user is authenticated by checking the token in the localStorage
5
+ // if the user is not authenticated, it will redirect the user to the login page
6
+ // if the user is authenticated, it will return the children components
7
+ //and the children component is the component that we want to protect
8
+ function ProtectedRoute({ children }) {
9
+ const token = localStorage.getItem('token');
10
+
11
+ if (!token) {
12
+ return <Navigate to="/login" replace />;
13
+ }
14
+
15
+ return children;
16
+ }
17
+
18
+ export default ProtectedRoute;
@@ -0,0 +1 @@
1
+ @import 'tailwindcss';
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom/client';
3
+ import { BrowserRouter } from 'react-router-dom';
4
+ import App from './App';
5
+ import './index.css';
6
+
7
+ ReactDOM.createRoot(document.getElementById('root')).render(
8
+ <React.StrictMode>
9
+ <BrowserRouter>
10
+ <App />
11
+ </BrowserRouter>
12
+ </React.StrictMode>
13
+ );
@@ -0,0 +1,74 @@
1
+ import { useEffect, useState } from 'react';
2
+ import axios from 'axios';
3
+ import { Link } from 'react-router-dom';
4
+
5
+ function DashboardHome() {
6
+ const [itemCount, setItemCount] = useState(null);
7
+ const [error, setError] = useState('');
8
+
9
+ const handleRetrieve = async () => {
10
+ try {
11
+ const response = await axios.get('http://localhost:5000/api/items', {
12
+ headers: {
13
+ Authorization: `Bearer ${localStorage.getItem('token')}`,
14
+ },
15
+ });
16
+ setItemCount(response.data.items.length);
17
+ } catch (err) {
18
+ setError(err.response?.data?.message || 'Could not load overview');
19
+ console.error('Server Error:', err.response?.data || err.message);
20
+ }
21
+ };
22
+
23
+ useEffect(() => {
24
+ handleRetrieve();
25
+ }, []);
26
+
27
+ return (
28
+ <div className="max-w-4xl">
29
+ <h1 className="text-2xl font-bold text-slate-800 mb-2">Overview</h1>
30
+ <p className="text-slate-600 mb-8">
31
+ Use the sidebar to move between sections of your dashboard.
32
+ </p>
33
+
34
+ {error && (
35
+ <p className="mb-4 text-sm text-red-600 bg-red-50 border border-red-200 rounded px-3 py-2">
36
+ {error}
37
+ </p>
38
+ )}
39
+
40
+ <div className="grid gap-4 sm:grid-cols-2">
41
+ <div className="bg-white rounded-lg shadow-sm border border-slate-200 p-6">
42
+ <p className="text-sm font-medium text-slate-500">Total items</p>
43
+ <p className="text-3xl font-bold text-slate-800 mt-2">
44
+ {itemCount === null ? '—' : itemCount}
45
+ </p>
46
+ <Link
47
+ to="/dashboard/items"
48
+ className="inline-block mt-4 text-sm font-medium text-blue-600 hover:underline"
49
+ >
50
+ Manage items →
51
+ </Link>
52
+ </div>
53
+
54
+ <div className="bg-white rounded-lg shadow-sm border border-slate-200 p-6">
55
+ <p className="text-sm font-medium text-slate-500">Quick links</p>
56
+ <ul className="mt-3 space-y-2 text-sm">
57
+ <li>
58
+ <Link to="/dashboard/items" className="text-blue-600 hover:underline">
59
+ Items — create and edit
60
+ </Link>
61
+ </li>
62
+ <li>
63
+ <Link to="/dashboard/profile" className="text-blue-600 hover:underline">
64
+ Profile — account details
65
+ </Link>
66
+ </li>
67
+ </ul>
68
+ </div>
69
+ </div>
70
+ </div>
71
+ );
72
+ }
73
+
74
+ export default DashboardHome;
@@ -0,0 +1,243 @@
1
+ import { useEffect, useState } from 'react';
2
+ import axios from 'axios';
3
+
4
+ function Items() {
5
+ const [items, setItems] = useState([]);
6
+ const [title, setTitle] = useState('');
7
+ const [description, setDescription] = useState('');
8
+ const [error, setError] = useState('');
9
+ const [message, setMessage] = useState('');
10
+ const [editId, setEditId] = useState(null);
11
+ const [loading, setLoading] = useState(true);
12
+ const [submitting, setSubmitting] = useState(false);
13
+
14
+ const getAuthHeaders = () => ({
15
+ Authorization: `Bearer ${localStorage.getItem('token')}`,
16
+ });
17
+
18
+ const handleRetrieve = async () => {
19
+ try {
20
+ const response = await axios.get('http://localhost:5000/api/items', {
21
+ headers: getAuthHeaders(),
22
+ });
23
+ setItems(response.data.items);
24
+ } catch (err) {
25
+ setError(err.response?.data?.message || 'Failed to load items');
26
+ console.error('Server Error:', err.response?.data || err.message);
27
+ } finally {
28
+ setLoading(false);
29
+ }
30
+ };
31
+
32
+ useEffect(() => {
33
+ handleRetrieve();
34
+ }, []);
35
+
36
+ const handleDelete = async (id) => {
37
+ if (!window.confirm('Delete this item?')) return;
38
+
39
+ setError('');
40
+ try {
41
+ await axios.delete(`http://localhost:5000/api/items/${id}`, {
42
+ headers: getAuthHeaders(),
43
+ });
44
+ setMessage('Item deleted successfully');
45
+ if (editId === id) {
46
+ setTitle('');
47
+ setDescription('');
48
+ setEditId(null);
49
+ }
50
+ handleRetrieve();
51
+ } catch (err) {
52
+ setError(err.response?.data?.message || 'Delete failed');
53
+ console.error('Server Error:', err.response?.data || err.message);
54
+ }
55
+ };
56
+
57
+ const handleEdit = (item) => {
58
+ setEditId(item._id);
59
+ setTitle(item.title);
60
+ setDescription(item.description || '');
61
+ setMessage('');
62
+ setError('');
63
+ };
64
+
65
+ const handleUpdate = async (e) => {
66
+ e.preventDefault();
67
+ if (!editId) return;
68
+
69
+ setSubmitting(true);
70
+ setError('');
71
+ try {
72
+ await axios.put(
73
+ `http://localhost:5000/api/items/${editId}`,
74
+ { title, description },
75
+ { headers: getAuthHeaders() },
76
+ );
77
+ setMessage('Item updated successfully');
78
+ setTitle('');
79
+ setDescription('');
80
+ setEditId(null);
81
+ handleRetrieve();
82
+ } catch (err) {
83
+ setError(err.response?.data?.message || 'Update failed');
84
+ console.error('Server Error:', err.response?.data || err.message);
85
+ } finally {
86
+ setSubmitting(false);
87
+ }
88
+ };
89
+
90
+ const handleSubmit = async (e) => {
91
+ e.preventDefault();
92
+ setError('');
93
+ setMessage('');
94
+
95
+ if (editId) {
96
+ await handleUpdate(e);
97
+ return;
98
+ }
99
+
100
+ setSubmitting(true);
101
+ try {
102
+ await axios.post(
103
+ 'http://localhost:5000/api/items',
104
+ { title, description },
105
+ { headers: getAuthHeaders() },
106
+ );
107
+ setMessage('Item added successfully');
108
+ setTitle('');
109
+ setDescription('');
110
+ handleRetrieve();
111
+ } catch (err) {
112
+ setError(err.response?.data?.message || 'Adding item failed');
113
+ console.error('Server Error:', err.response?.data || err.message);
114
+ } finally {
115
+ setSubmitting(false);
116
+ }
117
+ };
118
+
119
+ const handleCancelEdit = () => {
120
+ setTitle('');
121
+ setDescription('');
122
+ setEditId(null);
123
+ setError('');
124
+ setMessage('');
125
+ };
126
+
127
+ return (
128
+ <div className="max-w-3xl">
129
+ <h1 className="text-2xl font-bold text-slate-800 mb-6">Items</h1>
130
+
131
+ {message && (
132
+ <p className="mb-4 text-sm text-green-700 bg-green-50 border border-green-200 rounded px-3 py-2">
133
+ {message}
134
+ </p>
135
+ )}
136
+
137
+ {error && (
138
+ <p className="mb-4 text-sm text-red-600 bg-red-50 border border-red-200 rounded px-3 py-2">
139
+ {error}
140
+ </p>
141
+ )}
142
+
143
+ <section className="bg-white rounded-lg shadow-md p-6 mb-8">
144
+ <h2 className="text-lg font-semibold text-slate-800 mb-4">
145
+ {editId ? 'Edit item' : 'New item'}
146
+ </h2>
147
+ <form onSubmit={handleSubmit} className="space-y-4">
148
+ <div>
149
+ <label htmlFor="title" className="block text-sm font-medium text-slate-700 mb-1">
150
+ Title
151
+ </label>
152
+ <input
153
+ id="title"
154
+ name="title"
155
+ type="text"
156
+ required
157
+ value={title}
158
+ onChange={(e) => setTitle(e.target.value)}
159
+ className="w-full border border-slate-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
160
+ />
161
+ </div>
162
+ <div>
163
+ <label
164
+ htmlFor="description"
165
+ className="block text-sm font-medium text-slate-700 mb-1"
166
+ >
167
+ Description
168
+ </label>
169
+ <textarea
170
+ id="description"
171
+ name="description"
172
+ rows={3}
173
+ value={description}
174
+ onChange={(e) => setDescription(e.target.value)}
175
+ className="w-full border border-slate-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
176
+ />
177
+ </div>
178
+ <div className="flex gap-2">
179
+ <button
180
+ type="submit"
181
+ disabled={submitting}
182
+ className="bg-blue-600 text-white font-medium px-4 py-2 rounded hover:bg-blue-700 disabled:opacity-50"
183
+ >
184
+ {submitting ? 'Saving...' : editId ? 'Update' : 'Create'}
185
+ </button>
186
+ {editId && (
187
+ <button
188
+ type="button"
189
+ onClick={handleCancelEdit}
190
+ className="border border-slate-300 text-slate-700 font-medium px-4 py-2 rounded hover:bg-slate-50"
191
+ >
192
+ Cancel
193
+ </button>
194
+ )}
195
+ </div>
196
+ </form>
197
+ </section>
198
+
199
+ <section>
200
+ <h2 className="text-lg font-semibold text-slate-800 mb-4">Your items</h2>
201
+ {loading ? (
202
+ <p className="text-slate-500">Loading...</p>
203
+ ) : items.length === 0 ? (
204
+ <p className="text-slate-500">No items yet. Create one above.</p>
205
+ ) : (
206
+ <ul className="space-y-3">
207
+ {items.map((item) => (
208
+ <li
209
+ key={item._id}
210
+ className="bg-white rounded-lg shadow-sm border border-slate-200 p-4 flex justify-between gap-4"
211
+ >
212
+ <div>
213
+ <h3 className="font-medium text-slate-800">{item.title}</h3>
214
+ {item.description && (
215
+ <p className="text-sm text-slate-600 mt-1">{item.description}</p>
216
+ )}
217
+ </div>
218
+ <div className="flex gap-2 shrink-0">
219
+ <button
220
+ type="button"
221
+ onClick={() => handleEdit(item)}
222
+ className="text-sm text-blue-600 hover:underline"
223
+ >
224
+ Edit
225
+ </button>
226
+ <button
227
+ type="button"
228
+ onClick={() => handleDelete(item._id)}
229
+ className="text-sm text-red-600 hover:underline"
230
+ >
231
+ Delete
232
+ </button>
233
+ </div>
234
+ </li>
235
+ ))}
236
+ </ul>
237
+ )}
238
+ </section>
239
+ </div>
240
+ );
241
+ }
242
+
243
+ export default Items;
@@ -0,0 +1,101 @@
1
+ import { useState } from 'react';
2
+ import axios from 'axios';
3
+ import { Link, useNavigate } from 'react-router-dom';
4
+
5
+ function Login() {
6
+ const navigate = useNavigate();
7
+ const [email, setEmail] = useState('');
8
+ const [password, setPassword] = useState('');
9
+ const [error, setError] = useState('');
10
+ const [message, setMessage] = useState('');
11
+ const [loading, setLoading] = useState(false);
12
+
13
+ const handleSubmit = async (e) => {
14
+ e.preventDefault();
15
+ setError('');
16
+ setMessage('');
17
+ setLoading(true);
18
+
19
+ try {
20
+ const response = await axios.post('http://localhost:5000/api/auth/login', { email, password });
21
+ localStorage.setItem('token', response.data.token);
22
+ localStorage.setItem('user', JSON.stringify(response.data.user));
23
+ setMessage('Login successful');
24
+ navigate('/dashboard');
25
+ } catch (err) {
26
+ setError(err.response?.data?.message || 'Login failed');
27
+ console.error('Server Error:', err.response?.data || err.message);
28
+ } finally {
29
+ setLoading(false);
30
+ }
31
+ };
32
+
33
+ return (
34
+ <div className="min-h-screen flex items-center justify-center px-4">
35
+ <div className="w-full max-w-md bg-white rounded-lg shadow-md p-8">
36
+ <h1 className="text-2xl font-bold text-slate-800 mb-6">Sign in</h1>
37
+
38
+ {message && (
39
+ <p className="mb-4 text-sm text-green-700 bg-green-50 border border-green-200 rounded px-3 py-2">
40
+ {message}
41
+ </p>
42
+ )}
43
+
44
+ {error && (
45
+ <p className="mb-4 text-sm text-red-600 bg-red-50 border border-red-200 rounded px-3 py-2">
46
+ {error}
47
+ </p>
48
+ )}
49
+
50
+ <form onSubmit={handleSubmit} className="space-y-4">
51
+ <div>
52
+ <label htmlFor="email" className="block text-sm font-medium text-slate-700 mb-1">
53
+ Email
54
+ </label>
55
+ <input
56
+ id="email"
57
+ name="email"
58
+ type="email"
59
+ required
60
+ value={email}
61
+ onChange={(e) => setEmail(e.target.value)}
62
+ className="w-full border border-slate-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
63
+ />
64
+ </div>
65
+
66
+ <div>
67
+ <label htmlFor="password" className="block text-sm font-medium text-slate-700 mb-1">
68
+ Password
69
+ </label>
70
+ <input
71
+ id="password"
72
+ name="password"
73
+ type="password"
74
+ required
75
+ value={password}
76
+ onChange={(e) => setPassword(e.target.value)}
77
+ className="w-full border border-slate-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
78
+ />
79
+ </div>
80
+
81
+ <button
82
+ type="submit"
83
+ disabled={loading}
84
+ className="w-full bg-blue-600 text-white font-medium py-2 rounded hover:bg-blue-700 disabled:opacity-50"
85
+ >
86
+ {loading ? 'Signing in...' : 'Sign in'}
87
+ </button>
88
+ </form>
89
+
90
+ <p className="mt-4 text-sm text-slate-600 text-center">
91
+ No account?{' '}
92
+ <Link to="/register" className="text-blue-600 hover:underline">
93
+ Register
94
+ </Link>
95
+ </p>
96
+ </div>
97
+ </div>
98
+ );
99
+ }
100
+
101
+ export default Login;