create-myexam-app 1.0.20 → 1.0.21

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 (47) hide show
  1. package/package.json +1 -1
  2. package/projects/SCMS/backend/.env +2 -2
  3. package/projects/SCMS/backend/config/db.js +12 -0
  4. package/projects/SCMS/backend/controllers/authController.js +90 -0
  5. package/projects/SCMS/backend/controllers/deliveryController.js +79 -0
  6. package/projects/SCMS/backend/controllers/productController.js +74 -0
  7. package/projects/SCMS/backend/controllers/reportsController.js +77 -0
  8. package/projects/SCMS/backend/controllers/shipmentController.js +80 -0
  9. package/projects/SCMS/backend/controllers/supplierController.js +58 -0
  10. package/projects/SCMS/backend/middleware/auth.js +32 -0
  11. package/projects/SCMS/backend/models/User.js +28 -0
  12. package/projects/SCMS/backend/models/delivery.js +33 -0
  13. package/projects/SCMS/backend/models/product.js +43 -0
  14. package/projects/SCMS/backend/models/shipment.js +34 -0
  15. package/projects/SCMS/backend/models/supplier.js +36 -0
  16. package/projects/SCMS/backend/package-lock.json +2190 -778
  17. package/projects/SCMS/backend/package.json +23 -23
  18. package/projects/SCMS/backend/routes/SupplierRoutes.js +15 -0
  19. package/projects/SCMS/backend/routes/authRoutes.js +11 -0
  20. package/projects/SCMS/backend/routes/deliveryRoutes.js +18 -0
  21. package/projects/SCMS/backend/routes/productRoutes.js +15 -0
  22. package/projects/SCMS/backend/routes/protectedRoutes.js +10 -0
  23. package/projects/SCMS/backend/routes/reportsRoutes.js +8 -0
  24. package/projects/SCMS/backend/routes/shipmentRoutes.js +18 -0
  25. package/projects/SCMS/backend/server.js +28 -8
  26. package/projects/SCMS/frontend/index.html +1 -1
  27. package/projects/SCMS/frontend/package-lock.json +781 -152
  28. package/projects/SCMS/frontend/package.json +5 -1
  29. package/projects/SCMS/frontend/src/App.jsx +28 -115
  30. package/projects/SCMS/frontend/src/components/DashboardLayout.jsx +103 -0
  31. package/projects/SCMS/frontend/src/components/ProtectedRoute.jsx +30 -0
  32. package/projects/SCMS/frontend/src/index.css +110 -107
  33. package/projects/SCMS/frontend/src/pages/DashboardHome.jsx +34 -0
  34. package/projects/SCMS/frontend/src/pages/Delivery.jsx +183 -0
  35. package/projects/SCMS/frontend/src/pages/Login.jsx +81 -0
  36. package/projects/SCMS/frontend/src/pages/Profile.jsx +62 -0
  37. package/projects/SCMS/frontend/src/pages/Register.jsx +110 -0
  38. package/projects/SCMS/frontend/src/pages/Reports.jsx +94 -0
  39. package/projects/SCMS/frontend/src/pages/Shipment.jsx +182 -0
  40. package/projects/SCMS/frontend/src/pages/Supplier.jsx +165 -0
  41. package/projects/SCMS/frontend/vite.config.js +2 -2
  42. package/projects/SCMS/backend/db/connectDB.js +0 -11
  43. package/projects/SCMS/frontend/public/icons.svg +0 -24
  44. package/projects/SCMS/frontend/src/App.css +0 -184
  45. package/projects/SCMS/frontend/src/assets/hero.png +0 -0
  46. package/projects/SCMS/frontend/src/assets/react.svg +0 -1
  47. package/projects/SCMS/frontend/src/assets/vite.svg +0 -1
@@ -10,8 +10,12 @@
10
10
  "preview": "vite preview"
11
11
  },
12
12
  "dependencies": {
13
+ "@tailwindcss/vite": "^4.3.0",
14
+ "axios": "^1.16.1",
13
15
  "react": "^19.2.6",
14
- "react-dom": "^19.2.6"
16
+ "react-dom": "^19.2.6",
17
+ "react-router-dom": "^7.15.1",
18
+ "tailwindcss": "^4.3.0"
15
19
  },
16
20
  "devDependencies": {
17
21
  "@eslint/js": "^10.0.1",
@@ -1,122 +1,35 @@
1
- import { useState } from 'react'
2
- import reactLogo from './assets/react.svg'
3
- import viteLogo from './assets/vite.svg'
4
- import heroImg from './assets/hero.png'
5
- import './App.css'
1
+ import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
2
+ import DashboardLayout from './components/DashboardLayout';
3
+ import DashboardHome from './pages/DashboardHome';
4
+ import Register from './pages/Register';
5
+ import Login from './pages/Login';
6
+ import Profile from './pages/Profile';
7
+ import Supplier from './pages/Supplier';
8
+ import Shipment from './pages/Shipment';
9
+ import Delivery from './pages/Delivery';
10
+ import Reports from './pages/Reports';
11
+ import ProtectedRoute from './components/ProtectedRoute';
6
12
 
7
13
  function App() {
8
- const [count, setCount] = useState(0)
9
-
10
14
  return (
11
- <>
12
- <section id="center">
13
- <div className="hero">
14
- <img src={heroImg} className="base" width="170" height="179" alt="" />
15
- <img src={reactLogo} className="framework" alt="React logo" />
16
- <img src={viteLogo} className="vite" alt="Vite logo" />
17
- </div>
18
- <div>
19
- <h1>Get started</h1>
20
- <p>
21
- Edit <code>src/App.jsx</code> and save to test <code>HMR</code>
22
- </p>
23
- </div>
24
- <button
25
- type="button"
26
- className="counter"
27
- onClick={() => setCount((count) => count + 1)}
28
- >
29
- Count is {count}
30
- </button>
31
- </section>
32
-
33
- <div className="ticks"></div>
15
+ <BrowserRouter>
16
+ <Routes>
17
+ <Route path="/login" element={<Login />} />
18
+ <Route path="/register" element={<Register />} />
34
19
 
35
- <section id="next-steps">
36
- <div id="docs">
37
- <svg className="icon" role="presentation" aria-hidden="true">
38
- <use href="/icons.svg#documentation-icon"></use>
39
- </svg>
40
- <h2>Documentation</h2>
41
- <p>Your questions, answered</p>
42
- <ul>
43
- <li>
44
- <a href="https://vite.dev/" target="_blank">
45
- <img className="logo" src={viteLogo} alt="" />
46
- Explore Vite
47
- </a>
48
- </li>
49
- <li>
50
- <a href="https://react.dev/" target="_blank">
51
- <img className="button-icon" src={reactLogo} alt="" />
52
- Learn more
53
- </a>
54
- </li>
55
- </ul>
56
- </div>
57
- <div id="social">
58
- <svg className="icon" role="presentation" aria-hidden="true">
59
- <use href="/icons.svg#social-icon"></use>
60
- </svg>
61
- <h2>Connect with us</h2>
62
- <p>Join the Vite community</p>
63
- <ul>
64
- <li>
65
- <a href="https://github.com/vitejs/vite" target="_blank">
66
- <svg
67
- className="button-icon"
68
- role="presentation"
69
- aria-hidden="true"
70
- >
71
- <use href="/icons.svg#github-icon"></use>
72
- </svg>
73
- GitHub
74
- </a>
75
- </li>
76
- <li>
77
- <a href="https://chat.vite.dev/" target="_blank">
78
- <svg
79
- className="button-icon"
80
- role="presentation"
81
- aria-hidden="true"
82
- >
83
- <use href="/icons.svg#discord-icon"></use>
84
- </svg>
85
- Discord
86
- </a>
87
- </li>
88
- <li>
89
- <a href="https://x.com/vite_js" target="_blank">
90
- <svg
91
- className="button-icon"
92
- role="presentation"
93
- aria-hidden="true"
94
- >
95
- <use href="/icons.svg#x-icon"></use>
96
- </svg>
97
- X.com
98
- </a>
99
- </li>
100
- <li>
101
- <a href="https://bsky.app/profile/vite.dev" target="_blank">
102
- <svg
103
- className="button-icon"
104
- role="presentation"
105
- aria-hidden="true"
106
- >
107
- <use href="/icons.svg#bluesky-icon"></use>
108
- </svg>
109
- Bluesky
110
- </a>
111
- </li>
112
- </ul>
113
- </div>
114
- </section>
20
+ <Route path="/dashboard" element={<ProtectedRoute><DashboardLayout /></ProtectedRoute>}>
21
+ <Route index element={<DashboardHome />} />
22
+ <Route path="supplier" element={<Supplier />} />
23
+ <Route path="shipment" element={<Shipment />} />
24
+ <Route path="delivery" element={<Delivery />} />
25
+ <Route path="reports" element={<Reports />} />
26
+ <Route path="profile" element={<Profile />} />
27
+ </Route>
115
28
 
116
- <div className="ticks"></div>
117
- <section id="spacer"></section>
118
- </>
119
- )
29
+ <Route path="/" element={<Navigate to="/dashboard" replace />} />
30
+ </Routes>
31
+ </BrowserRouter>
32
+ );
120
33
  }
121
34
 
122
- export default App
35
+ export default App;
@@ -0,0 +1,103 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { NavLink, Outlet, useNavigate } from 'react-router-dom';
3
+ import axios from 'axios';
4
+
5
+ const navItems = [
6
+ { to: '/dashboard', label: 'Overview', end: true },
7
+ { to: '/dashboard/supplier', label: 'Supplier' },
8
+ { to: '/dashboard/shipment', label: 'Shipment' },
9
+ { to: '/dashboard/delivery', label: 'Delivery' },
10
+ { to: '/dashboard/reports', label: 'Reports' },
11
+ { to: '/dashboard/profile', label: 'Profile' },
12
+ ];
13
+
14
+ function DashboardLayout() {
15
+ const [user, setUser] = useState(null);
16
+ const [error, setError] = useState('');
17
+ const navigate = useNavigate();
18
+
19
+ useEffect(() => {
20
+ const storedUser = localStorage.getItem('user');
21
+ if (storedUser) {
22
+ setUser(JSON.parse(storedUser));
23
+ }
24
+
25
+ axios
26
+ .get('http://localhost:5001/api/auth/me', {
27
+ headers: { Authorization: `Bearer ${localStorage.getItem('token')}` },
28
+ })
29
+ .then((response) => {
30
+ setUser(response.data.user);
31
+ localStorage.setItem('user', JSON.stringify(response.data.user));
32
+ })
33
+ .catch((err) => {
34
+ if (!storedUser) {
35
+ setError(err.response?.data?.message || 'Could not load user');
36
+ }
37
+ });
38
+ }, []);
39
+
40
+ const handleLogout = () => {
41
+ localStorage.removeItem('token');
42
+ localStorage.removeItem('user');
43
+ navigate('/login');
44
+ };
45
+
46
+ return (
47
+ <div className="min-h-screen bg-brand-50">
48
+ <aside className="fixed inset-y-0 left-0 w-60 bg-brand-950 text-slate-100 flex flex-col border-r border-brand-800">
49
+ <div className="px-5 py-5 border-b border-brand-800">
50
+ <p className="text-xs font-semibold uppercase tracking-wider text-brand-400">SCMS</p>
51
+ <h1 className="text-base font-semibold text-white mt-0.5">Supply Chain</h1>
52
+ </div>
53
+
54
+ <nav className="flex-1 px-3 py-4 space-y-0.5">
55
+ {navItems.map(({ to, label, end }) => (
56
+ <NavLink
57
+ key={to}
58
+ to={to}
59
+ end={end}
60
+ className={({ isActive }) =>
61
+ isActive ? `nav-link ${'nav-link-active'}` : 'nav-link'
62
+ }
63
+ >
64
+ {label}
65
+ </NavLink>
66
+ ))}
67
+ </nav>
68
+
69
+ <div className="px-4 py-4 border-t border-brand-800">
70
+ {error && <p className="text-xs text-red-400 mb-2">{error}</p>}
71
+ {user && (
72
+ <>
73
+ <p className="text-sm font-medium text-white truncate">{user.name}</p>
74
+ <p className="text-xs text-slate-400 truncate mt-0.5">{user.email}</p>
75
+ </>
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-brand-700 rounded-lg px-3 py-2 hover:bg-brand-800 transition-colors"
81
+ >
82
+ Log out
83
+ </button>
84
+ </div>
85
+ </aside>
86
+
87
+ <div className="ml-60 flex min-h-screen flex-col">
88
+ <header className="bg-white border-b border-slate-200/80 px-6 py-4">
89
+ <p className="text-xs text-muted">Welcome back</p>
90
+ <h2 className="text-lg font-semibold text-brand-900">
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,30 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { Navigate } from 'react-router-dom';
3
+
4
+ export default function ProtectedRoute({ children }) {
5
+ const [loading, setLoading] = useState(true);
6
+ const [authorized, setAuthorized] = useState(false);
7
+
8
+ useEffect(() => {
9
+ const token = localStorage.getItem('token');
10
+ if (!token) {
11
+ setLoading(false);
12
+ setAuthorized(false);
13
+ return;
14
+ }
15
+
16
+ fetch('http://localhost:5001/api/protected', {
17
+ headers: { Authorization: `Bearer ${token}` },
18
+ })
19
+ .then((res) => {
20
+ if (res.ok) setAuthorized(true);
21
+ else setAuthorized(false);
22
+ })
23
+ .catch(() => setAuthorized(false))
24
+ .finally(() => setLoading(false));
25
+ }, []);
26
+
27
+ if (loading) return <div>Loading...</div>;
28
+ if (!authorized) return <Navigate to="/login" replace />;
29
+ return children;
30
+ }
@@ -1,111 +1,114 @@
1
- :root {
2
- --text: #6b6375;
3
- --text-h: #08060d;
4
- --bg: #fff;
5
- --border: #e5e4e7;
6
- --code-bg: #f4f3ec;
7
- --accent: #aa3bff;
8
- --accent-bg: rgba(170, 59, 255, 0.1);
9
- --accent-border: rgba(170, 59, 255, 0.5);
10
- --social-bg: rgba(244, 243, 236, 0.5);
11
- --shadow:
12
- rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px;
13
-
14
- --sans: system-ui, 'Segoe UI', Roboto, sans-serif;
15
- --heading: system-ui, 'Segoe UI', Roboto, sans-serif;
16
- --mono: ui-monospace, Consolas, monospace;
17
-
18
- font: 18px/145% var(--sans);
19
- letter-spacing: 0.18px;
20
- color-scheme: light dark;
21
- color: var(--text);
22
- background: var(--bg);
23
- font-synthesis: none;
24
- text-rendering: optimizeLegibility;
25
- -webkit-font-smoothing: antialiased;
26
- -moz-osx-font-smoothing: grayscale;
27
-
28
- @media (max-width: 1024px) {
29
- font-size: 16px;
30
- }
31
- }
32
-
33
- @media (prefers-color-scheme: dark) {
34
- :root {
35
- --text: #9ca3af;
36
- --text-h: #f3f4f6;
37
- --bg: #16171d;
38
- --border: #2e303a;
39
- --code-bg: #1f2028;
40
- --accent: #c084fc;
41
- --accent-bg: rgba(192, 132, 252, 0.15);
42
- --accent-border: rgba(192, 132, 252, 0.5);
43
- --social-bg: rgba(47, 48, 58, 0.5);
44
- --shadow:
45
- rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px;
46
- }
47
-
48
- #social .button-icon {
49
- filter: invert(1) brightness(2);
50
- }
1
+ @import 'tailwindcss';
2
+
3
+ @theme {
4
+ --color-brand-950: #0a1628;
5
+ --color-brand-900: #0f2035;
6
+ --color-brand-800: #152d45;
7
+ --color-brand-700: #1a4558;
8
+ --color-brand-600: #1f6b6b;
9
+ --color-brand-500: #2a8585;
10
+ --color-brand-400: #4a9e9e;
11
+ --color-brand-100: #e8f2f2;
12
+ --color-brand-50: #f4f9f9;
13
+ --color-surface: #fafbfc;
14
+ --color-muted: #64748b;
15
+ --font-sans: 'Segoe UI', system-ui, -apple-system, sans-serif;
51
16
  }
52
17
 
53
18
  body {
54
- margin: 0;
55
- }
56
-
57
- #root {
58
- width: 1126px;
59
- max-width: 100%;
60
- margin: 0 auto;
61
- text-align: center;
62
- border-inline: 1px solid var(--border);
63
- min-height: 100svh;
64
- display: flex;
65
- flex-direction: column;
66
- box-sizing: border-box;
67
- }
68
-
69
- h1,
70
- h2 {
71
- font-family: var(--heading);
72
- font-weight: 500;
73
- color: var(--text-h);
74
- }
75
-
76
- h1 {
77
- font-size: 56px;
78
- letter-spacing: -1.68px;
79
- margin: 32px 0;
80
- @media (max-width: 1024px) {
81
- font-size: 36px;
82
- margin: 20px 0;
83
- }
84
- }
85
- h2 {
86
- font-size: 24px;
87
- line-height: 118%;
88
- letter-spacing: -0.24px;
89
- margin: 0 0 8px;
90
- @media (max-width: 1024px) {
91
- font-size: 20px;
92
- }
93
- }
94
- p {
95
- margin: 0;
96
- }
97
-
98
- code,
99
- .counter {
100
- font-family: var(--mono);
101
- display: inline-flex;
102
- border-radius: 4px;
103
- color: var(--text-h);
104
- }
105
-
106
- code {
107
- font-size: 15px;
108
- line-height: 135%;
109
- padding: 4px 8px;
110
- background: var(--code-bg);
19
+ @apply bg-surface text-brand-900 antialiased;
20
+ font-family: var(--font-sans);
21
+ }
22
+
23
+ /* Auth pages */
24
+ .auth-page {
25
+ @apply min-h-screen flex items-center justify-center px-4 bg-brand-50;
26
+ }
27
+
28
+ .auth-card {
29
+ @apply w-full max-w-md bg-white rounded-xl border border-brand-100 shadow-sm p-8;
30
+ }
31
+
32
+ /* Forms */
33
+ .input {
34
+ @apply w-full border border-slate-200 rounded-lg px-3 py-2 text-sm
35
+ text-brand-900 placeholder:text-slate-400
36
+ focus:outline-none focus:ring-2 focus:ring-brand-600/30 focus:border-brand-600
37
+ transition-colors;
38
+ }
39
+
40
+ .btn-primary {
41
+ @apply bg-brand-700 text-white text-sm font-medium px-4 py-2 rounded-lg
42
+ hover:bg-brand-800 disabled:opacity-50 transition-colors;
43
+ }
44
+
45
+ .btn-primary-full {
46
+ @apply bg-brand-700 text-white text-sm font-medium w-full py-2.5 rounded-lg
47
+ hover:bg-brand-800 disabled:opacity-50 transition-colors;
48
+ }
49
+
50
+ .btn-ghost {
51
+ @apply text-sm font-medium text-brand-600 hover:text-brand-800 transition-colors;
52
+ }
53
+
54
+ .btn-danger {
55
+ @apply text-sm font-medium text-red-600 hover:text-red-700 transition-colors;
56
+ }
57
+
58
+ /* Layout */
59
+ .page-content {
60
+ @apply max-w-4xl mx-auto;
61
+ }
62
+
63
+ .page-title {
64
+ @apply text-xl font-semibold text-brand-900 mb-1;
65
+ }
66
+
67
+ .page-subtitle {
68
+ @apply text-sm text-muted mb-6;
69
+ }
70
+
71
+ .card {
72
+ @apply bg-white rounded-xl border border-slate-200/80 shadow-sm;
73
+ }
74
+
75
+ .card-body {
76
+ @apply p-5;
77
+ }
78
+
79
+ .form-card {
80
+ @apply bg-white rounded-xl border border-slate-200/80 shadow-sm p-5 space-y-3 mb-6;
81
+ }
82
+
83
+ .list-card {
84
+ @apply bg-white rounded-xl border border-slate-200/80 shadow-sm p-5 space-y-1;
85
+ }
86
+
87
+ .stat-card {
88
+ @apply bg-white rounded-xl border border-slate-200/80 shadow-sm p-5;
89
+ }
90
+
91
+ .stat-card h2 {
92
+ @apply text-sm font-semibold text-brand-700 mb-3;
93
+ }
94
+
95
+ .stat-card p {
96
+ @apply text-sm text-slate-600;
97
+ }
98
+
99
+ .alert-success {
100
+ @apply mb-4 text-sm text-brand-700 bg-brand-50 border border-brand-100 rounded-lg px-3 py-2;
101
+ }
102
+
103
+ .alert-error {
104
+ @apply mb-4 text-sm text-red-600 bg-red-50 border border-red-100 rounded-lg px-3 py-2;
105
+ }
106
+
107
+ .nav-link {
108
+ @apply block rounded-lg px-3 py-2 text-sm font-medium text-slate-400
109
+ hover:bg-brand-800/60 hover:text-white transition-colors;
110
+ }
111
+
112
+ .nav-link-active {
113
+ @apply bg-brand-700 text-white;
111
114
  }
@@ -0,0 +1,34 @@
1
+ import { Link } from 'react-router-dom';
2
+
3
+ const quickLinks = [
4
+ { to: '/dashboard/supplier', label: 'Suppliers', desc: 'Manage supplier records' },
5
+ { to: '/dashboard/shipment', label: 'Shipments', desc: 'Track shipment status' },
6
+ { to: '/dashboard/delivery', label: 'Deliveries', desc: 'Record deliveries' },
7
+ { to: '/dashboard/reports', label: 'Reports', desc: 'View summary reports' },
8
+ ];
9
+
10
+ function DashboardHome() {
11
+ return (
12
+ <div className="page-content">
13
+ <h1 className="page-title">Overview</h1>
14
+ <p className="page-subtitle">Manage your supply chain from one place.</p>
15
+
16
+ <div className="grid gap-4 sm:grid-cols-2">
17
+ {quickLinks.map(({ to, label, desc }) => (
18
+ <Link
19
+ key={to}
20
+ to={to}
21
+ className="card card-body hover:border-brand-600/40 transition-colors group"
22
+ >
23
+ <h3 className="text-sm font-semibold text-brand-800 group-hover:text-brand-700">
24
+ {label}
25
+ </h3>
26
+ <p className="text-sm text-muted mt-1">{desc}</p>
27
+ </Link>
28
+ ))}
29
+ </div>
30
+ </div>
31
+ );
32
+ }
33
+
34
+ export default DashboardHome;