mern-template-tool 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 (47) hide show
  1. package/README.md +93 -0
  2. package/bin/index.js +112 -0
  3. package/package.json +33 -0
  4. package/templates/README.md +160 -0
  5. package/templates/_gitignore +8 -0
  6. package/templates/client/index.html +13 -0
  7. package/templates/client/package.json +20 -0
  8. package/templates/client/src/App.jsx +72 -0
  9. package/templates/client/src/components/Loading.jsx +14 -0
  10. package/templates/client/src/components/Navbar.jsx +58 -0
  11. package/templates/client/src/context/AuthContext.jsx +109 -0
  12. package/templates/client/src/index.css +244 -0
  13. package/templates/client/src/main.jsx +16 -0
  14. package/templates/client/src/pages/CreateResource.jsx +142 -0
  15. package/templates/client/src/pages/Dashboard.jsx +117 -0
  16. package/templates/client/src/pages/EditResource.jsx +166 -0
  17. package/templates/client/src/pages/Login.jsx +101 -0
  18. package/templates/client/src/pages/NotFound.jsx +21 -0
  19. package/templates/client/src/pages/Profile.jsx +52 -0
  20. package/templates/client/src/pages/Register.jsx +134 -0
  21. package/templates/client/src/pages/ResourceDetails.jsx +156 -0
  22. package/templates/client/src/pages/Resources.jsx +181 -0
  23. package/templates/client/src/routes/ProtectedRoute.jsx +34 -0
  24. package/templates/client/src/services/api.js +71 -0
  25. package/templates/client/vite.config.js +20 -0
  26. package/templates/server/.env.example +5 -0
  27. package/templates/server/config/db.js +13 -0
  28. package/templates/server/controllers/authController.js +131 -0
  29. package/templates/server/controllers/resourceController.js +254 -0
  30. package/templates/server/middleware/adminMiddleware.js +14 -0
  31. package/templates/server/middleware/authMiddleware.js +53 -0
  32. package/templates/server/middleware/errorMiddleware.js +55 -0
  33. package/templates/server/middleware/sessionAuthMiddleware.js +12 -0
  34. package/templates/server/middleware/uploadMiddleware.js +61 -0
  35. package/templates/server/middleware/validateMiddleware.js +61 -0
  36. package/templates/server/models/Resource.js +43 -0
  37. package/templates/server/models/User.js +48 -0
  38. package/templates/server/package.json +25 -0
  39. package/templates/server/routes/adminRoutes.js +138 -0
  40. package/templates/server/routes/authRoutes.js +21 -0
  41. package/templates/server/routes/resourceRoutes.js +27 -0
  42. package/templates/server/server.js +89 -0
  43. package/templates/server/utils/jwt.js +18 -0
  44. package/templates/server/views/dashboard.ejs +128 -0
  45. package/templates/server/views/login.ejs +48 -0
  46. package/templates/server/views/partials/footer.ejs +6 -0
  47. package/templates/server/views/partials/header.ejs +60 -0
package/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # mern-template-tool
2
+
3
+ > An exam-oriented, offline-first MERN stack project generator for college practical examinations and fast prototyping.
4
+
5
+ Execute anywhere with:
6
+ ```bash
7
+ npx mern-template-tool <project-name>
8
+ ```
9
+
10
+ ---
11
+
12
+ ## ⚡ What is `mern-template`?
13
+
14
+ During practical examinations, internet access is often limited to a brief window. `mern-template` provides a complete, robust, ready-to-run MERN stack boilerplate with all standard requirements pre-implemented:
15
+
16
+ - 🔒 **Dual Authentication**: JWT Authentication for React SPA & `express-session` for EJS Admin Portal.
17
+ - 👥 **Role-Based Authorization**: Student & Admin roles with protected routes and ownership checking.
18
+ - 📦 **Generic CRUD Model**: Complete MVC structure with search, filtering, and sorting (`req.query`).
19
+ - 🛡️ **Validation & Security**: `express-validator` rules, `bcryptjs` password hashing, safe JSON responses.
20
+ - 📎 **Multer File Handling**: Local disk storage, file validation, and file download endpoints.
21
+ - ⚛️ **Modern React Frontend**: React 18, Vite, React Router 6, React Context API state, native `fetch` service.
22
+ - 🔌 **Zero External Cloud Dependencies**: Local MongoDB & local filesystem storage, ready for air-gapped exam environments.
23
+
24
+ ---
25
+
26
+ ## 🚀 Quick Usage
27
+
28
+ ```bash
29
+ # 1. Generate project
30
+ npx mern-template my-exam-project
31
+
32
+ # 2. Start Backend Server
33
+ cd my-exam-project/server
34
+ cp .env.example .env
35
+ npm install
36
+ npm run dev
37
+
38
+ # 3. Start Frontend Client (in a second terminal)
39
+ cd ../client
40
+ npm install
41
+ npm run dev
42
+ ```
43
+
44
+ ---
45
+
46
+ ## 📋 Generated Project Structure
47
+
48
+ ```text
49
+ my-exam-project/
50
+ ├── client/ # React Single Page Application (Vite)
51
+ │ ├── src/
52
+ │ │ ├── components/ # Navbar, Loading
53
+ │ │ ├── context/ # AuthContext (React Context API)
54
+ │ │ ├── pages/ # Login, Register, Dashboard, Profile, Resources, etc.
55
+ │ │ ├── routes/ # ProtectedRoute
56
+ │ │ ├── services/ # api.js (native fetch helper)
57
+ │ │ ├── App.jsx # Router definition
58
+ │ │ ├── index.css # Modern dark UI theme
59
+ │ │ └── main.jsx # Entry point
60
+ │ ├── package.json
61
+ │ └── vite.config.js
62
+ │
63
+ ├── server/ # Express API & EJS Admin Portal
64
+ │ ├── config/ # db.js (Mongoose connection)
65
+ │ ├── controllers/ # authController.js, resourceController.js
66
+ │ ├── middleware/ # authMiddleware, adminMiddleware, uploadMiddleware, validateMiddleware, errorMiddleware, sessionAuthMiddleware
67
+ │ ├── models/ # User.js, Resource.js
68
+ │ ├── routes/ # authRoutes.js, resourceRoutes.js, adminRoutes.js
69
+ │ ├── utils/ # jwt.js
70
+ │ ├── views/ # EJS admin templates (login.ejs, dashboard.ejs, partials/)
71
+ │ ├── uploads/ # Local Multer upload storage
72
+ │ ├── .env.example # Environment variables
73
+ │ ├── package.json
74
+ │ └── server.js # Express application entrypoint
75
+ │
76
+ └── README.md # Includes exam cheat sheet & modification guides
77
+ ```
78
+
79
+ ---
80
+
81
+ ## 📦 Publishing to npm
82
+
83
+ To publish this package to npm:
84
+ ```bash
85
+ npm login
86
+ npm pack --dry-run
87
+ npm publish --access public
88
+ ```
89
+
90
+ ---
91
+
92
+ ## 📄 License
93
+ MIT
package/bin/index.js ADDED
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ function printUsage() {
7
+ console.log(`
8
+ \x1b[36m\x1b[1mMERN Template Generator\x1b[0m (Exam-Ready & Offline First)
9
+
10
+ Usage:
11
+ npx mern-template-tool <project-name>
12
+
13
+ Examples:
14
+ npx mern-template-tool my-app
15
+ npx mern-template-tool employee-management
16
+ npx mern-template-tool library-app
17
+ `);
18
+ }
19
+
20
+ function copyDirRecursive(source, target) {
21
+ if (!fs.existsSync(target)) {
22
+ fs.mkdirSync(target, { recursive: true });
23
+ }
24
+
25
+ const entries = fs.readdirSync(source, { withFileTypes: true });
26
+
27
+ for (const entry of entries) {
28
+ const srcPath = path.join(source, entry.name);
29
+ // Handle dotfiles if renamed or standard (like _gitignore -> .gitignore or .env.example)
30
+ let destName = entry.name;
31
+ if (entry.name === '_gitignore') {
32
+ destName = '.gitignore';
33
+ }
34
+ const destPath = path.join(target, destName);
35
+
36
+ if (entry.isDirectory()) {
37
+ copyDirRecursive(srcPath, destPath);
38
+ } else {
39
+ fs.copyFileSync(srcPath, destPath);
40
+ }
41
+ }
42
+ }
43
+
44
+ function main() {
45
+ const args = process.argv.slice(2);
46
+
47
+ if (args.length === 0 || args.includes('-h') || args.includes('--help')) {
48
+ printUsage();
49
+ process.exit(args.length === 0 ? 1 : 0);
50
+ }
51
+
52
+ const projectName = args[0].trim();
53
+
54
+ if (!projectName || /^[./\\]+$/.test(projectName)) {
55
+ console.error('\x1b[31mError: Please provide a valid project folder name.\x1b[0m');
56
+ process.exit(1);
57
+ }
58
+
59
+ const targetDir = path.resolve(process.cwd(), projectName);
60
+
61
+ if (fs.existsSync(targetDir)) {
62
+ const existingFiles = fs.readdirSync(targetDir);
63
+ if (existingFiles.length > 0) {
64
+ console.error(`\x1b[31mError: Directory '${projectName}' already exists and is not empty.\x1b[0m`);
65
+ console.error('Please specify a new directory name or empty the existing directory.');
66
+ process.exit(1);
67
+ }
68
+ }
69
+
70
+ const templateDir = path.resolve(__dirname, '..', 'templates');
71
+
72
+ if (!fs.existsSync(templateDir)) {
73
+ console.error(`\x1b[31mError: Template directory not found at ${templateDir}\x1b[0m`);
74
+ process.exit(1);
75
+ }
76
+
77
+ console.log(`\n\x1b[32m✔\x1b[0m Generating MERN stack project in \x1b[33m${targetDir}\x1b[0m...\n`);
78
+
79
+ try {
80
+ copyDirRecursive(templateDir, targetDir);
81
+
82
+ // Make sure uploads directory exists in server
83
+ const uploadsDir = path.join(targetDir, 'server', 'uploads');
84
+ if (!fs.existsSync(uploadsDir)) {
85
+ fs.mkdirSync(uploadsDir, { recursive: true });
86
+ }
87
+
88
+ console.log(`\x1b[32m✔\x1b[0m Project successfully generated!\n`);
89
+ console.log(`\x1b[1mNext Steps to run your project:\x1b[0m`);
90
+ console.log(`\x1b[36m--------------------------------------------------\x1b[0m`);
91
+ console.log(`1. Navigate to your project directory:`);
92
+ console.log(` \x1b[33mcd ${projectName}\x1b[0m\n`);
93
+ console.log(`2. Setup and Start Backend Server:`);
94
+ console.log(` \x1b[33mcd server\x1b[0m`);
95
+ console.log(` \x1b[33mcp .env.example .env\x1b[0m (or copy .env.example to .env on Windows)`);
96
+ console.log(` \x1b[33mnpm install\x1b[0m`);
97
+ console.log(` \x1b[33mnpm run dev\x1b[0m (Runs on http://localhost:5000)\n`);
98
+ console.log(`3. Setup and Start React Client:`);
99
+ console.log(` \x1b[33mcd ../client\x1b[0m`);
100
+ console.log(` \x1b[33mnpm install\x1b[0m`);
101
+ console.log(` \x1b[33mnpm run dev\x1b[0m (Runs on http://localhost:5173)\n`);
102
+ console.log(`4. Access Admin EJS Application:`);
103
+ console.log(` Open \x1b[34mhttp://localhost:5000/admin/login\x1b[0m\n`);
104
+ console.log(`\x1b[36m--------------------------------------------------\x1b[0m`);
105
+ console.log(`\x1b[32mAll features (JWT Auth, Sessions, EJS, Multer, Generic CRUD, React Context) are ready to use!\x1b[0m\n`);
106
+ } catch (err) {
107
+ console.error(`\x1b[31mError generating project:\x1b[0m`, err);
108
+ process.exit(1);
109
+ }
110
+ }
111
+
112
+ main();
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "mern-template-tool",
3
+ "version": "1.0.0",
4
+ "description": "Exam-oriented MERN stack project generator with JWT, sessions, EJS, React Context, Multer, and generic CRUD",
5
+ "main": "bin/index.js",
6
+ "bin": {
7
+ "mern-template-tool": "bin/index.js",
8
+ "mern-template": "bin/index.js"
9
+ },
10
+ "keywords": [
11
+ "mern",
12
+ "template",
13
+ "generator",
14
+ "react",
15
+ "express",
16
+ "mongodb",
17
+ "exam",
18
+ "jwt",
19
+ "auth",
20
+ "ejs",
21
+ "multer"
22
+ ],
23
+ "author": "smit-mendapara",
24
+ "license": "MIT",
25
+ "files": [
26
+ "bin",
27
+ "templates",
28
+ "README.md"
29
+ ],
30
+ "engines": {
31
+ "node": ">=16.0.0"
32
+ }
33
+ }
@@ -0,0 +1,160 @@
1
+ # Generated MERN Project (Exam-Ready & Offline First)
2
+
3
+ This is a production-ready, exam-oriented full-stack MERN application generated by `mern-template`. It provides complete authentication (JWT & express-session), authorization, MVC generic resource CRUD, search/filtering/sorting, express-validator validation, Multer file upload/download, centralized error handling, React Context state management, and protected React routing.
4
+
5
+ ---
6
+
7
+ ## 📁 Project Structure
8
+
9
+ ```text
10
+ .
11
+ ├── client/ # React Single Page Application (Vite)
12
+ │ ├── src/
13
+ │ │ ├── components/ # Reusable UI (Navbar, Loading)
14
+ │ │ ├── context/ # AuthContext (React Context API)
15
+ │ │ ├── pages/ # Login, Register, Dashboard, Profile, Resources, etc.
16
+ │ │ ├── routes/ # ProtectedRoute wrapper
17
+ │ │ ├── services/ # api.js (native fetch HTTP client)
18
+ │ │ ├── App.jsx # Main router
19
+ │ │ ├── index.css # Modern CSS styling
20
+ │ │ └── main.jsx # React root
21
+ │ ├── package.json
22
+ │ └── vite.config.js
23
+ │
24
+ ├── server/ # Express.js REST API & EJS Admin Portal
25
+ │ ├── config/ # db.js (Mongoose connection)
26
+ │ ├── controllers/ # authController.js, resourceController.js
27
+ │ ├── middleware/ # authMiddleware, adminMiddleware, uploadMiddleware, validateMiddleware, errorMiddleware, sessionAuthMiddleware
28
+ │ ├── models/ # User.js, Resource.js
29
+ │ ├── routes/ # authRoutes.js, resourceRoutes.js, adminRoutes.js
30
+ │ ├── utils/ # jwt.js
31
+ │ ├── views/ # EJS admin templates (login.ejs, dashboard.ejs, partials/)
32
+ │ ├── uploads/ # Local file storage for Multer
33
+ │ ├── .env.example # Environment variables example
34
+ │ ├── package.json
35
+ │ └── server.js # Express application entrypoint
36
+ │
37
+ └── README.md
38
+ ```
39
+
40
+ ---
41
+
42
+ ## 🚀 Quick Start Guide
43
+
44
+ ### 1. Prerequisites
45
+ - **Node.js** (v16+)
46
+ - **MongoDB** running locally on `mongodb://127.0.0.1:27017`
47
+
48
+ ### 2. Backend Setup
49
+ ```bash
50
+ cd server
51
+ cp .env.example .env # (On Windows cmd: copy .env.example .env)
52
+ npm install
53
+ npm run dev # Server starts on http://localhost:5000
54
+ ```
55
+
56
+ ### 3. Frontend Setup
57
+ ```bash
58
+ cd client
59
+ npm install
60
+ npm run dev # Client starts on http://localhost:5173
61
+ ```
62
+
63
+ ---
64
+
65
+ ## 🌐 Endpoints & URLs Reference
66
+
67
+ ### 🔐 React Application
68
+ - **URL**: `http://localhost:5173`
69
+ - **Features**: Registration, Login, Dashboard, Profile, Resource Listing, Create/Edit/Delete, File Upload, Instant Search & Sorting.
70
+
71
+ ### 🛡️ EJS Admin Portal (Session Authentication)
72
+ - **Login URL**: `http://localhost:5000/admin/login`
73
+ - **Dashboard URL**: `http://localhost:5000/admin/dashboard`
74
+ - **Default Behavior**: Register an admin user via React (or seed one), then sign in through the EJS portal to manage users and inspect system statistics.
75
+
76
+ ### 📡 REST API Routes
77
+ | Method | Endpoint | Access | Description |
78
+ |---|---|---|---|
79
+ | `POST` | `/api/auth/register` | Public | Register student or admin |
80
+ | `POST` | `/api/auth/login` | Public | Login & receive JWT |
81
+ | `GET` | `/api/auth/me` | Protected (JWT) | Get authenticated user profile |
82
+ | `GET` | `/api/resources` | Public | Get resources (supports `?search=&category=&sort=`) |
83
+ | `GET` | `/api/resources/:id` | Public | Get single resource by ID |
84
+ | `GET` | `/api/resources/:id/download`| Public | Download attached file |
85
+ | `POST` | `/api/resources` | Protected (JWT) | Create resource (`multipart/form-data` with file) |
86
+ | `PUT` | `/api/resources/:id` | Protected (Owner/Admin) | Update resource & replace file |
87
+ | `DELETE` | `/api/resources/:id` | Protected (Owner/Admin) | Delete resource & remove file |
88
+
89
+ ---
90
+
91
+ ## 💡 Practical Exam Quick Reference & Cheatsheet
92
+
93
+ ### 1. Express Request Objects
94
+ - `req.body`: Form or JSON payload sent in `POST`/`PUT` requests.
95
+ - `req.params`: URL parameters (e.g. `req.params.id` from `/api/resources/:id`).
96
+ - `req.query`: Query string parameters (e.g. `req.query.search` from `/api/resources?search=mern`).
97
+ - `req.file`: Attached file uploaded via Multer (`upload.single('file')`).
98
+ - `req.user`: User payload attached by JWT auth middleware (`req.user._id`, `req.user.role`).
99
+
100
+ ### 2. Mongoose Model Methods
101
+ ```js
102
+ // Find all matching criteria
103
+ const items = await Model.find({ category: 'Exam' }).sort({ createdAt: -1 });
104
+
105
+ // Find single document by ID
106
+ const item = await Model.findById(req.params.id);
107
+
108
+ // Create document
109
+ const newItem = await Model.create({ title: 'Sample', createdBy: req.user._id });
110
+
111
+ // Update document by ID
112
+ const updated = await Model.findByIdAndUpdate(req.params.id, req.body, { new: true });
113
+
114
+ // Delete document by ID
115
+ await Model.findByIdAndDelete(req.params.id);
116
+ ```
117
+
118
+ ### 3. Password Hashing (bcryptjs)
119
+ ```js
120
+ const bcrypt = require('bcryptjs');
121
+ // Hash password
122
+ const salt = await bcrypt.genSalt(10);
123
+ const hashedPassword = await bcrypt.hash(password, salt);
124
+
125
+ // Compare password
126
+ const isMatch = await bcrypt.compare(plainPassword, hashedPassword);
127
+ ```
128
+
129
+ ### 4. JWT Generation & Verification
130
+ ```js
131
+ const jwt = require('jsonwebtoken');
132
+ // Generate Token
133
+ const token = jwt.sign({ id: user._id, role: user.role }, process.env.JWT_SECRET, { expiresIn: '1d' });
134
+
135
+ // Verify Token
136
+ const decoded = jwt.verify(token, process.env.JWT_SECRET);
137
+ ```
138
+
139
+ ### 5. React Hooks & Context
140
+ ```jsx
141
+ import { useState, useEffect } from 'react';
142
+ import { useAuth } from '../context/AuthContext';
143
+ import { useNavigate, useParams } from 'react-router-dom';
144
+
145
+ const { user, isAuthenticated, logout } = useAuth();
146
+ const navigate = useNavigate();
147
+ const { id } = useParams();
148
+ ```
149
+
150
+ ---
151
+
152
+ ## 🛠️ How to Customize for Specific Exam Questions
153
+
154
+ 1. **Renaming the Resource Model**:
155
+ - To build a **Library / Book Management System**: Rename `Resource.js` to `Book.js`, change `title` to `bookName`, `category` to `genre`, and `description` to `author`.
156
+ - To build an **Employee System**: Rename `Resource.js` to `Employee.js`, fields to `name`, `department`, `salary`, `designation`.
157
+ 2. **Adding New Routes**:
158
+ - Create router in `server/routes/xyzRoutes.js` and register in `server.js` using `app.use('/api/xyz', xyzRouter);`.
159
+ 3. **Updating Frontend Form Fields**:
160
+ - Update `client/src/pages/CreateResource.jsx` and `EditResource.jsx` with input elements matching your new model fields.
@@ -0,0 +1,8 @@
1
+ node_modules
2
+ .env
3
+ uploads/*
4
+ !uploads/.gitkeep
5
+ dist
6
+ dist-ssr
7
+ *.local
8
+ .DS_Store
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>MERN Application</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.jsx"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "client",
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
+ "react": "^18.3.1",
13
+ "react-dom": "^18.3.1",
14
+ "react-router-dom": "^6.23.1"
15
+ },
16
+ "devDependencies": {
17
+ "@vitejs/plugin-react": "^4.3.0",
18
+ "vite": "^5.2.11"
19
+ }
20
+ }
@@ -0,0 +1,72 @@
1
+ import React from 'react';
2
+ import { Routes, Route, Navigate } from 'react-router-dom';
3
+ import Navbar from './components/Navbar';
4
+ import ProtectedRoute from './routes/ProtectedRoute';
5
+
6
+ // Pages
7
+ import Login from './pages/Login';
8
+ import Register from './pages/Register';
9
+ import Dashboard from './pages/Dashboard';
10
+ import Profile from './pages/Profile';
11
+ import Resources from './pages/Resources';
12
+ import ResourceDetails from './pages/ResourceDetails';
13
+ import CreateResource from './pages/CreateResource';
14
+ import EditResource from './pages/EditResource';
15
+ import NotFound from './pages/NotFound';
16
+
17
+ function App() {
18
+ return (
19
+ <div className="app">
20
+ <Navbar />
21
+ <main>
22
+ <Routes>
23
+ {/* Public Routes */}
24
+ <Route path="/" element={<Navigate to="/resources" replace />} />
25
+ <Route path="/login" element={<Login />} />
26
+ <Route path="/register" element={<Register />} />
27
+ <Route path="/resources" element={<Resources />} />
28
+ <Route path="/resources/:id" element={<ResourceDetails />} />
29
+
30
+ {/* Protected Routes (JWT required) */}
31
+ <Route
32
+ path="/dashboard"
33
+ element={
34
+ <ProtectedRoute>
35
+ <Dashboard />
36
+ </ProtectedRoute>
37
+ }
38
+ />
39
+ <Route
40
+ path="/profile"
41
+ element={
42
+ <ProtectedRoute>
43
+ <Profile />
44
+ </ProtectedRoute>
45
+ }
46
+ />
47
+ <Route
48
+ path="/resources/new"
49
+ element={
50
+ <ProtectedRoute>
51
+ <CreateResource />
52
+ </ProtectedRoute>
53
+ }
54
+ />
55
+ <Route
56
+ path="/resources/:id/edit"
57
+ element={
58
+ <ProtectedRoute>
59
+ <EditResource />
60
+ </ProtectedRoute>
61
+ }
62
+ />
63
+
64
+ {/* 404 Catch All */}
65
+ <Route path="*" element={<NotFound />} />
66
+ </Routes>
67
+ </main>
68
+ </div>
69
+ );
70
+ }
71
+
72
+ export default App;
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+
3
+ const Loading = ({ message = 'Loading...' }) => {
4
+ return (
5
+ <div className="spinner-container">
6
+ <div style={{ textAlign: 'center' }}>
7
+ <div className="spinner" style={{ margin: '0 auto 1rem auto' }}></div>
8
+ <p style={{ color: 'var(--text-secondary)', fontSize: '0.9rem' }}>{message}</p>
9
+ </div>
10
+ </div>
11
+ );
12
+ };
13
+
14
+ export default Loading;
@@ -0,0 +1,58 @@
1
+ import React from 'react';
2
+ import { Link, NavLink, useNavigate } from 'react-router-dom';
3
+ import { useAuth } from '../context/AuthContext';
4
+
5
+ const Navbar = () => {
6
+ const { user, isAuthenticated, logout } = useAuth();
7
+ const navigate = useNavigate();
8
+
9
+ const handleLogout = () => {
10
+ logout();
11
+ navigate('/login');
12
+ };
13
+
14
+ return (
15
+ <nav className="navbar">
16
+ <Link to="/" className="nav-brand">
17
+ 🚀 MERN App
18
+ </Link>
19
+
20
+ <div className="nav-links">
21
+ <NavLink to="/resources">Resources</NavLink>
22
+
23
+ {isAuthenticated ? (
24
+ <>
25
+ <NavLink to="/dashboard">Dashboard</NavLink>
26
+ <NavLink to="/resources/new">Create Resource</NavLink>
27
+ <NavLink to="/profile">Profile ({user?.name})</NavLink>
28
+ <button
29
+ onClick={handleLogout}
30
+ className="btn btn-secondary btn-sm"
31
+ style={{ marginLeft: '0.5rem' }}
32
+ >
33
+ Logout
34
+ </button>
35
+ </>
36
+ ) : (
37
+ <>
38
+ <NavLink to="/login">Login</NavLink>
39
+ <NavLink to="/register" className="btn btn-primary btn-sm">
40
+ Register
41
+ </NavLink>
42
+ </>
43
+ )}
44
+
45
+ <a
46
+ href="http://localhost:5000/admin/login"
47
+ target="_blank"
48
+ rel="noreferrer"
49
+ style={{ fontSize: '0.8rem', color: '#a5b4fc', borderLeft: '1px solid #334155', paddingLeft: '0.75rem' }}
50
+ >
51
+ Admin Portal (EJS) &rarr;
52
+ </a>
53
+ </div>
54
+ </nav>
55
+ );
56
+ };
57
+
58
+ export default Navbar;