mern-template-tool 1.0.0 → 1.0.1

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.
@@ -73,92 +73,70 @@ const EditResource = () => {
73
73
  if (loading) return <Loading message="Loading resource..." />;
74
74
 
75
75
  return (
76
- <div className="container" style={{ maxWidth: '650px' }}>
77
- <div className="card">
78
- <h2>Edit Resource</h2>
79
- <p style={{ marginBottom: '1.5rem', fontSize: '0.9rem' }}>
80
- Update fields or replace existing file attachment
81
- </p>
82
-
83
- {error && <div className="alert alert-error">{error}</div>}
84
-
85
- <form onSubmit={handleSubmit}>
86
- <div className="form-group">
87
- <label htmlFor="title">Title *</label>
88
- <input
89
- id="title"
90
- type="text"
91
- required
92
- className="form-control"
93
- value={title}
94
- onChange={(e) => setTitle(e.target.value)}
95
- />
96
- </div>
97
-
98
- <div className="form-group">
99
- <label htmlFor="category">Category</label>
100
- <select
101
- id="category"
102
- className="form-control"
103
- value={category}
104
- onChange={(e) => setCategory(e.target.value)}
105
- >
106
- <option value="General">General</option>
107
- <option value="Documentation">Documentation</option>
108
- <option value="Assignment">Assignment</option>
109
- <option value="Project">Project</option>
110
- <option value="Exam">Exam</option>
111
- </select>
112
- </div>
113
-
114
- <div className="form-group">
115
- <label htmlFor="description">Description</label>
116
- <textarea
117
- id="description"
118
- rows={4}
119
- className="form-control"
120
- value={description}
121
- onChange={(e) => setDescription(e.target.value)}
122
- />
123
- </div>
124
-
125
- <div className="form-group">
126
- <label htmlFor="file">
127
- Replace File (Optional - Leave blank to keep current)
128
- </label>
129
- {existingFileName && (
130
- <p style={{ fontSize: '0.85rem', color: '#38bdf8', marginBottom: '0.5rem' }}>
131
- Current File: {existingFileName}
132
- </p>
133
- )}
134
- <input
135
- id="file"
136
- type="file"
137
- className="form-control"
138
- onChange={handleFileChange}
139
- accept=".pdf,.png,.jpg,.jpeg,.doc,.docx"
140
- />
141
- </div>
142
-
143
- <div style={{ display: 'flex', gap: '1rem', marginTop: '1.5rem' }}>
144
- <button
145
- type="submit"
146
- className="btn btn-primary"
147
- disabled={saving}
148
- style={{ flex: 1 }}
149
- >
150
- {saving ? 'Saving changes...' : 'Save Changes'}
151
- </button>
152
- <button
153
- type="button"
154
- onClick={() => navigate(`/resources/${id}`)}
155
- className="btn btn-secondary"
156
- >
157
- Cancel
158
- </button>
159
- </div>
160
- </form>
161
- </div>
76
+ <div>
77
+ <h2>Edit Resource</h2>
78
+ <p>Modify resource details</p>
79
+
80
+ {error && <p><strong>Error: {error}</strong></p>}
81
+
82
+ <form onSubmit={handleSubmit}>
83
+ <div>
84
+ <label htmlFor="title">Title *: </label>
85
+ <input
86
+ id="title"
87
+ type="text"
88
+ required
89
+ value={title}
90
+ onChange={(e) => setTitle(e.target.value)}
91
+ />
92
+ </div>
93
+ <br />
94
+ <div>
95
+ <label htmlFor="category">Category: </label>
96
+ <select
97
+ id="category"
98
+ value={category}
99
+ onChange={(e) => setCategory(e.target.value)}
100
+ >
101
+ <option value="General">General</option>
102
+ <option value="Documentation">Documentation</option>
103
+ <option value="Assignment">Assignment</option>
104
+ <option value="Project">Project</option>
105
+ <option value="Exam">Exam</option>
106
+ </select>
107
+ </div>
108
+ <br />
109
+ <div>
110
+ <label htmlFor="description">Description: </label>
111
+ <br />
112
+ <textarea
113
+ id="description"
114
+ rows={4}
115
+ cols={50}
116
+ value={description}
117
+ onChange={(e) => setDescription(e.target.value)}
118
+ />
119
+ </div>
120
+ <br />
121
+ <div>
122
+ <label htmlFor="file">Replace Attached File (Optional): </label>
123
+ {existingFileName && <p>Current: <em>{existingFileName}</em></p>}
124
+ <input
125
+ id="file"
126
+ type="file"
127
+ onChange={handleFileChange}
128
+ accept=".pdf,.png,.jpg,.jpeg,.doc,.docx"
129
+ />
130
+ </div>
131
+ <br />
132
+ <button type="submit" disabled={saving}>
133
+ {saving ? 'Saving...' : 'Save Changes'}
134
+ </button>
135
+ {' '}
136
+ <button type="button" onClick={() => navigate(`/resources/${id}`)}>
137
+ Cancel
138
+ </button>
139
+ </form>
162
140
  </div>
163
141
  );
164
142
  };
@@ -39,61 +39,47 @@ const Login = () => {
39
39
  };
40
40
 
41
41
  return (
42
- <div className="container" style={{ maxWidth: '450px', marginTop: '2rem' }}>
43
- <div className="card">
44
- <h2 style={{ textAlign: 'center', marginBottom: '0.5rem' }}>Welcome Back</h2>
45
- <p style={{ textAlign: 'center', marginBottom: '1.5rem', fontSize: '0.9rem' }}>
46
- Sign in to access your MERN dashboard
47
- </p>
42
+ <div>
43
+ <h2>User Login</h2>
44
+ <p>Sign in to access your MERN account</p>
48
45
 
49
- {error && <div className="alert alert-error">{error}</div>}
46
+ {error && <p><strong>Error: {error}</strong></p>}
50
47
 
51
- <form onSubmit={handleSubmit}>
52
- <div className="form-group">
53
- <label htmlFor="email">Email Address</label>
54
- <input
55
- id="email"
56
- type="email"
57
- name="email"
58
- required
59
- className="form-control"
60
- value={formData.email}
61
- onChange={handleChange}
62
- placeholder="user@example.com"
63
- />
64
- </div>
48
+ <form onSubmit={handleSubmit}>
49
+ <div>
50
+ <label htmlFor="email">Email Address: </label>
51
+ <input
52
+ id="email"
53
+ type="email"
54
+ name="email"
55
+ required
56
+ value={formData.email}
57
+ onChange={handleChange}
58
+ placeholder="user@example.com"
59
+ />
60
+ </div>
61
+ <br />
62
+ <div>
63
+ <label htmlFor="password">Password: </label>
64
+ <input
65
+ id="password"
66
+ type="password"
67
+ name="password"
68
+ required
69
+ value={formData.password}
70
+ onChange={handleChange}
71
+ placeholder="password"
72
+ />
73
+ </div>
74
+ <br />
75
+ <button type="submit" disabled={loading}>
76
+ {loading ? 'Signing in...' : 'Sign In'}
77
+ </button>
78
+ </form>
65
79
 
66
- <div className="form-group">
67
- <label htmlFor="password">Password</label>
68
- <input
69
- id="password"
70
- type="password"
71
- name="password"
72
- required
73
- className="form-control"
74
- value={formData.password}
75
- onChange={handleChange}
76
- placeholder="••••••••"
77
- />
78
- </div>
79
-
80
- <button
81
- type="submit"
82
- className="btn btn-primary"
83
- style={{ width: '100%', marginTop: '0.5rem' }}
84
- disabled={loading}
85
- >
86
- {loading ? 'Signing in...' : 'Sign In'}
87
- </button>
88
- </form>
89
-
90
- <p style={{ textAlign: 'center', marginTop: '1.5rem', fontSize: '0.9rem' }}>
91
- Don't have an account?{' '}
92
- <Link to="/register" style={{ color: 'var(--primary)' }}>
93
- Register here
94
- </Link>
95
- </p>
96
- </div>
80
+ <p>
81
+ Don't have an account? <Link to="/register">Register here</Link>
82
+ </p>
97
83
  </div>
98
84
  );
99
85
  };
@@ -3,17 +3,10 @@ import { Link } from 'react-router-dom';
3
3
 
4
4
  const NotFound = () => {
5
5
  return (
6
- <div className="container" style={{ textAlign: 'center', marginTop: '4rem' }}>
7
- <div className="card" style={{ maxWidth: '500px', margin: '0 auto', padding: '3rem 2rem' }}>
8
- <h1 style={{ fontSize: '3rem', color: 'var(--primary)', marginBottom: '0.5rem' }}>404</h1>
9
- <h2>Page Not Found</h2>
10
- <p style={{ margin: '1rem 0 2rem 0' }}>
11
- The page you are looking for does not exist or has been moved.
12
- </p>
13
- <Link to="/" className="btn btn-primary">
14
- Return to Home
15
- </Link>
16
- </div>
6
+ <div>
7
+ <h2>404 - Page Not Found</h2>
8
+ <p>The requested page does not exist.</p>
9
+ <Link to="/">&larr; Return to Home</Link>
17
10
  </div>
18
11
  );
19
12
  };
@@ -5,46 +5,16 @@ const Profile = () => {
5
5
  const { user } = useAuth();
6
6
 
7
7
  return (
8
- <div className="container" style={{ maxWidth: '600px', marginTop: '2rem' }}>
9
- <div className="card">
10
- <h2>User Profile</h2>
11
- <p style={{ marginBottom: '1.5rem', fontSize: '0.9rem' }}>
12
- Verified details from JWT Authentication (GET /api/auth/me)
13
- </p>
14
-
15
- <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
16
- <div>
17
- <label style={{ fontSize: '0.8rem', color: 'var(--text-secondary)' }}>Full Name</label>
18
- <p style={{ fontSize: '1.1rem', fontWeight: '600', color: 'var(--text-primary)' }}>{user?.name}</p>
19
- </div>
20
-
21
- <div>
22
- <label style={{ fontSize: '0.8rem', color: 'var(--text-secondary)' }}>Email Address</label>
23
- <p style={{ fontSize: '1.1rem', fontWeight: '600', color: 'var(--text-primary)' }}>{user?.email}</p>
24
- </div>
25
-
26
- <div>
27
- <label style={{ fontSize: '0.8rem', color: 'var(--text-secondary)' }}>Assigned Role</label>
28
- <p style={{ marginTop: '0.25rem' }}>
29
- <span className="badge badge-primary">{user?.role}</span>
30
- </p>
31
- </div>
32
-
33
- <div>
34
- <label style={{ fontSize: '0.8rem', color: 'var(--text-secondary)' }}>User ID (MongoDB ObjectId)</label>
35
- <p style={{ fontFamily: 'monospace', fontSize: '0.9rem', color: 'var(--text-secondary)' }}>
36
- {user?.id || user?._id}
37
- </p>
38
- </div>
39
-
40
- <div>
41
- <label style={{ fontSize: '0.8rem', color: 'var(--text-secondary)' }}>Account Created At</label>
42
- <p style={{ fontSize: '0.9rem', color: 'var(--text-secondary)' }}>
43
- {user?.createdAt ? new Date(user.createdAt).toLocaleString() : 'Active Session'}
44
- </p>
45
- </div>
46
- </div>
47
- </div>
8
+ <div>
9
+ <h2>User Profile</h2>
10
+ <p>Current authenticated user details (from GET /api/auth/me):</p>
11
+ <ul>
12
+ <li><strong>Full Name:</strong> {user?.name}</li>
13
+ <li><strong>Email:</strong> {user?.email}</li>
14
+ <li><strong>Role:</strong> {user?.role}</li>
15
+ <li><strong>User ID:</strong> {user?.id || user?._id}</li>
16
+ <li><strong>Created:</strong> {user?.createdAt ? new Date(user.createdAt).toLocaleString() : 'N/A'}</li>
17
+ </ul>
48
18
  </div>
49
19
  );
50
20
  };
@@ -43,90 +43,74 @@ const Register = () => {
43
43
  };
44
44
 
45
45
  return (
46
- <div className="container" style={{ maxWidth: '480px', marginTop: '2rem' }}>
47
- <div className="card">
48
- <h2 style={{ textAlign: 'center', marginBottom: '0.5rem' }}>Create Account</h2>
49
- <p style={{ textAlign: 'center', marginBottom: '1.5rem', fontSize: '0.9rem' }}>
50
- Register for student or admin privileges
51
- </p>
46
+ <div>
47
+ <h2>User Registration</h2>
48
+ <p>Register as a student or admin</p>
52
49
 
53
- {error && <div className="alert alert-error">{error}</div>}
50
+ {error && <p><strong>Error: {error}</strong></p>}
54
51
 
55
- <form onSubmit={handleSubmit}>
56
- <div className="form-group">
57
- <label htmlFor="name">Full Name</label>
58
- <input
59
- id="name"
60
- type="text"
61
- name="name"
62
- required
63
- className="form-control"
64
- value={formData.name}
65
- onChange={handleChange}
66
- placeholder="John Doe"
67
- />
68
- </div>
69
-
70
- <div className="form-group">
71
- <label htmlFor="email">Email Address</label>
72
- <input
73
- id="email"
74
- type="email"
75
- name="email"
76
- required
77
- className="form-control"
78
- value={formData.email}
79
- onChange={handleChange}
80
- placeholder="john@example.com"
81
- />
82
- </div>
83
-
84
- <div className="form-group">
85
- <label htmlFor="password">Password (min 6 characters)</label>
86
- <input
87
- id="password"
88
- type="password"
89
- name="password"
90
- required
91
- minLength={6}
92
- className="form-control"
93
- value={formData.password}
94
- onChange={handleChange}
95
- placeholder="••••••••"
96
- />
97
- </div>
98
-
99
- <div className="form-group">
100
- <label htmlFor="role">Role</label>
101
- <select
102
- id="role"
103
- name="role"
104
- className="form-control"
105
- value={formData.role}
106
- onChange={handleChange}
107
- >
108
- <option value="student">Student (Standard User)</option>
109
- <option value="admin">Admin (Elevated Permissions)</option>
110
- </select>
111
- </div>
112
-
113
- <button
114
- type="submit"
115
- className="btn btn-primary"
116
- style={{ width: '100%', marginTop: '0.5rem' }}
117
- disabled={loading}
52
+ <form onSubmit={handleSubmit}>
53
+ <div>
54
+ <label htmlFor="name">Full Name: </label>
55
+ <input
56
+ id="name"
57
+ type="text"
58
+ name="name"
59
+ required
60
+ value={formData.name}
61
+ onChange={handleChange}
62
+ placeholder="Your name"
63
+ />
64
+ </div>
65
+ <br />
66
+ <div>
67
+ <label htmlFor="email">Email Address: </label>
68
+ <input
69
+ id="email"
70
+ type="email"
71
+ name="email"
72
+ required
73
+ value={formData.email}
74
+ onChange={handleChange}
75
+ placeholder="user@example.com"
76
+ />
77
+ </div>
78
+ <br />
79
+ <div>
80
+ <label htmlFor="password">Password (min 6 characters): </label>
81
+ <input
82
+ id="password"
83
+ type="password"
84
+ name="password"
85
+ required
86
+ minLength={6}
87
+ value={formData.password}
88
+ onChange={handleChange}
89
+ placeholder="password"
90
+ />
91
+ </div>
92
+ <br />
93
+ <div>
94
+ <label htmlFor="role">Role: </label>
95
+ <select
96
+ id="role"
97
+ name="role"
98
+ value={formData.role}
99
+ onChange={handleChange}
118
100
  >
119
- {loading ? 'Creating account...' : 'Register'}
120
- </button>
121
- </form>
101
+ <option value="student">student</option>
102
+ <option value="admin">admin</option>
103
+ </select>
104
+ </div>
105
+ <br />
106
+ <button type="submit" disabled={loading}>
107
+ {loading ? 'Registering...' : 'Register'}
108
+ </button>
109
+ </form>
122
110
 
123
- <p style={{ textAlign: 'center', marginTop: '1.5rem', fontSize: '0.9rem' }}>
124
- Already have an account?{' '}
125
- <Link to="/login" style={{ color: 'var(--primary)' }}>
126
- Login here
127
- </Link>
128
- </p>
129
- </div>
111
+ <p>
112
+ Already have an account? <Link to="/login">Login here</Link>
113
+ </p>
130
114
  </div>
131
115
  );
132
116
  };
@@ -49,14 +49,10 @@ const ResourceDetails = () => {
49
49
  if (loading) return <Loading message="Loading resource details..." />;
50
50
  if (error) {
51
51
  return (
52
- <div className="container" style={{ textAlign: 'center', marginTop: '3rem' }}>
53
- <div className="card">
54
- <h2>Error</h2>
55
- <p>{error}</p>
56
- <Link to="/resources" className="btn btn-secondary" style={{ marginTop: '1rem' }}>
57
- Back to Resources
58
- </Link>
59
- </div>
52
+ <div>
53
+ <h2>Error</h2>
54
+ <p>{error}</p>
55
+ <Link to="/resources">&larr; Back to Resources</Link>
60
56
  </div>
61
57
  );
62
58
  }
@@ -70,85 +66,52 @@ const ResourceDetails = () => {
70
66
  const canModify = isOwner || isAdmin;
71
67
 
72
68
  return (
73
- <div className="container" style={{ maxWidth: '800px' }}>
74
- <Link to="/resources" style={{ color: 'var(--text-secondary)', textDecoration: 'none', marginBottom: '1rem', display: 'inline-block' }}>
75
- &larr; Back to all resources
76
- </Link>
69
+ <div>
70
+ <p>
71
+ <Link to="/resources">&larr; Back to all resources</Link>
72
+ </p>
77
73
 
78
- <div className="card">
79
- <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: '1rem' }}>
80
- <div>
81
- <span className="badge badge-primary">{resource.category}</span>
82
- <h1 style={{ marginTop: '0.5rem', fontSize: '1.8rem' }}>{resource.title}</h1>
83
- </div>
84
- {canModify && (
85
- <div style={{ display: 'flex', gap: '0.5rem' }}>
86
- <Link to={`/resources/${resource._id}/edit`} className="btn btn-secondary btn-sm">
87
- Edit
88
- </Link>
89
- <button
90
- onClick={handleDelete}
91
- className="btn btn-danger btn-sm"
92
- disabled={deleteLoading}
93
- >
94
- {deleteLoading ? 'Deleting...' : 'Delete'}
95
- </button>
96
- </div>
97
- )}
98
- </div>
74
+ <h2>{resource.title}</h2>
75
+ <p><strong>Category:</strong> {resource.category}</p>
76
+ <p><strong>Description:</strong></p>
77
+ <p>{resource.description || 'No description provided.'}</p>
99
78
 
100
- <p style={{ fontSize: '1.05rem', whiteSpace: 'pre-line', color: 'var(--text-primary)', margin: '1.5rem 0' }}>
101
- {resource.description || 'No description provided.'}
102
- </p>
79
+ {resource.fileUrl && (
80
+ <fieldset>
81
+ <legend>Attached File</legend>
82
+ <p>File Name: <strong>{resource.fileName || 'Attached file'}</strong></p>
83
+ {resource.fileSize && (
84
+ <p>File Size: {(resource.fileSize / 1024).toFixed(1)} KB</p>
85
+ )}
86
+ <p>
87
+ <a
88
+ href={`http://localhost:5000/api/resources/${resource._id}/download`}
89
+ target="_blank"
90
+ rel="noreferrer"
91
+ >
92
+ [ Download File ]
93
+ </a>
94
+ </p>
95
+ </fieldset>
96
+ )}
103
97
 
104
- {resource.fileUrl && (
105
- <div
106
- style={{
107
- background: 'rgba(255, 255, 255, 0.05)',
108
- padding: '1.25rem',
109
- borderRadius: '6px',
110
- border: '1px solid var(--card-border)',
111
- marginBottom: '1.5rem',
112
- }}
113
- >
114
- <h4 style={{ marginBottom: '0.5rem' }}>Attached File:</h4>
115
- <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
116
- <div>
117
- <p style={{ color: '#fff', fontWeight: '500' }}>📄 {resource.fileName || 'Attached document'}</p>
118
- {resource.fileSize && (
119
- <p style={{ fontSize: '0.8rem' }}>Size: {(resource.fileSize / 1024).toFixed(1)} KB</p>
120
- )}
121
- </div>
122
- <a
123
- href={`http://localhost:5000/api/resources/${resource._id}/download`}
124
- target="_blank"
125
- rel="noreferrer"
126
- className="btn btn-primary btn-sm"
127
- >
128
- Download File
129
- </a>
130
- </div>
131
- </div>
132
- )}
98
+ <br />
99
+ <p>
100
+ <strong>Created by:</strong> {resource.createdBy?.name || 'Unknown'} ({resource.createdBy?.email})
101
+ {' | '}
102
+ <strong>Date:</strong> {new Date(resource.createdAt).toLocaleString()}
103
+ </p>
133
104
 
134
- <div
135
- style={{
136
- borderTop: '1px solid var(--card-border)',
137
- paddingTop: '1rem',
138
- display: 'flex',
139
- justifyContent: 'space-between',
140
- fontSize: '0.85rem',
141
- color: 'var(--text-secondary)',
142
- }}
143
- >
144
- <div>
145
- <span>Created by: <strong>{resource.createdBy?.name || 'Unknown'}</strong> ({resource.createdBy?.email})</span>
146
- </div>
147
- <div>
148
- <span>Date: {new Date(resource.createdAt).toLocaleString()}</span>
149
- </div>
105
+ {canModify && (
106
+ <div>
107
+ <hr />
108
+ <Link to={`/resources/${resource._id}/edit`}>[ Edit Resource ]</Link>
109
+ {' '}
110
+ <button type="button" onClick={handleDelete} disabled={deleteLoading}>
111
+ {deleteLoading ? 'Deleting...' : 'Delete Resource'}
112
+ </button>
150
113
  </div>
151
- </div>
114
+ )}
152
115
  </div>
153
116
  );
154
117
  };