easy-starter 1.2.1 → 2.1.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.
- package/README.md +33 -80
- package/bin/index.js +251 -49
- package/package.json +20 -11
- package/template/.cursorrules +42 -10
- package/template/README.md +129 -25
- package/template/env +0 -1
- package/template/gitignore +2 -1
- package/template/index.html +15 -0
- package/template/package.json +14 -8
- package/template/{src → public}/site.webmanifest +6 -12
- package/template/scripts/deploy.ts +3 -2
- package/template/server/index.tsx +264 -56
- package/template/server/mail.tsx +120 -0
- package/template/src/App.tsx +33 -7
- package/template/src/Router.tsx +13 -4
- package/template/src/components/Img.tsx +127 -0
- package/template/src/index.tsx +28 -1
- package/template/src/pages/Index.tsx +36 -6
- package/template/src/pages/NotFound.tsx +1 -1
- package/template/src/pages/ResetPassword.tsx +124 -0
- package/template/src/pages/admin/Admin.tsx +332 -85
- package/template/src/pages/admin/AnalyticsTab.tsx +71 -0
- package/template/src/pages/admin/ErrorsTab.tsx +46 -0
- package/template/src/pages/admin/UsersTab.tsx +54 -0
- package/template/src/services/api.ts +5 -7
- package/template/src/services/common.ts +34 -1
- package/template/src/styles.css +667 -0
- package/template/tsconfig.json +7 -2
- package/template/types.ts +44 -3
- package/template/vite.config.ts +29 -0
- package/template/.parcelrc +0 -6
- package/template/src/images/icon.png +0 -0
- package/template/src/index.html +0 -16
- package/template/src/styles.scss +0 -292
package/template/types.ts
CHANGED
|
@@ -1,16 +1,51 @@
|
|
|
1
|
-
import { APIDefinition, Endpoint
|
|
1
|
+
import { APIDefinition, Endpoint } from "typed-client-server-api";
|
|
2
|
+
|
|
3
|
+
// --- AUTH START ---
|
|
4
|
+
import { UserDB } from "easy-user-auth/types";
|
|
5
|
+
|
|
6
|
+
export type UserProfile = {
|
|
7
|
+
name: string;
|
|
8
|
+
role: "admin" | "user";
|
|
9
|
+
};
|
|
10
|
+
// --- AUTH END ---
|
|
11
|
+
|
|
12
|
+
// --- OGIMAGE START ---
|
|
13
|
+
export type OgImageRecord = {
|
|
14
|
+
dateCreated: string;
|
|
15
|
+
file: {
|
|
16
|
+
type: "EASY_DB_FILE";
|
|
17
|
+
url: string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
// --- OGIMAGE END ---
|
|
2
21
|
|
|
3
22
|
// The API type defines the contract between your React frontend and Express backend.
|
|
4
23
|
// Every property here represents an endpoint. Both the client and server use this
|
|
5
|
-
// to ensure type safety.
|
|
6
|
-
// warn you if you forgot to update the frontend or backend!
|
|
24
|
+
// to ensure type safety.
|
|
7
25
|
export type API = APIDefinition<{
|
|
8
26
|
getItems: Endpoint<{}, Item[]>;
|
|
9
27
|
getItem: Endpoint<{ id: string }, Item>;
|
|
10
28
|
addItem: Endpoint<Omit<Item, "_id">, string>;
|
|
11
29
|
removeItem: Endpoint<{ id: string }, boolean>;
|
|
12
30
|
|
|
31
|
+
/* --- ADMIN_NO_AUTH START --- */
|
|
13
32
|
getEasyAnalyticsData: Endpoint<{ password: string, month: string }, any[]>;
|
|
33
|
+
/* --- ADMIN_NO_AUTH END --- */
|
|
34
|
+
// --- ADMIN_WITH_AUTH START ---
|
|
35
|
+
getEasyAnalyticsData: Endpoint<{ month: string }, any[]>;
|
|
36
|
+
// --- ADMIN_WITH_AUTH END ---
|
|
37
|
+
|
|
38
|
+
/* --- ADMIN_ERRORS_NO_AUTH START --- */
|
|
39
|
+
getEasyAnalyticsErrors: Endpoint<{ password: string, month: string }, any[]>;
|
|
40
|
+
/* --- ADMIN_ERRORS_NO_AUTH END --- */
|
|
41
|
+
// --- ADMIN_ERRORS_WITH_AUTH START ---
|
|
42
|
+
getEasyAnalyticsErrors: Endpoint<{ month: string }, any[]>;
|
|
43
|
+
// --- ADMIN_ERRORS_WITH_AUTH END ---
|
|
44
|
+
|
|
45
|
+
// --- ADMIN_USERS START ---
|
|
46
|
+
getUsers: Endpoint<{}, { _id: string, email: string, name: string, role: "admin" | "user" }[]>;
|
|
47
|
+
updateUserRole: Endpoint<{ userId: string, role: "admin" | "user" }, boolean>;
|
|
48
|
+
// --- ADMIN_USERS END ---
|
|
14
49
|
}>;
|
|
15
50
|
|
|
16
51
|
export type Item = {
|
|
@@ -23,4 +58,10 @@ export type Item = {
|
|
|
23
58
|
// Each property represents a 'table' (or a separate JSON file).
|
|
24
59
|
export type Database = {
|
|
25
60
|
item: Item;
|
|
61
|
+
// --- AUTH START ---
|
|
62
|
+
user: UserDB<UserProfile>;
|
|
63
|
+
// --- AUTH END ---
|
|
64
|
+
// --- OGIMAGE START ---
|
|
65
|
+
"og-image": OgImageRecord;
|
|
66
|
+
// --- OGIMAGE END ---
|
|
26
67
|
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { defineConfig } from "vite";
|
|
2
|
+
import react from "@vitejs/plugin-react";
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
plugins: [react()],
|
|
6
|
+
define: {
|
|
7
|
+
"process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV || "development"),
|
|
8
|
+
"global": "globalThis",
|
|
9
|
+
},
|
|
10
|
+
server: {
|
|
11
|
+
port: 5173,
|
|
12
|
+
proxy: {
|
|
13
|
+
"/api": {
|
|
14
|
+
target: "http://localhost:1111",
|
|
15
|
+
changeOrigin: true,
|
|
16
|
+
},
|
|
17
|
+
"/files": {
|
|
18
|
+
target: "http://localhost:1111",
|
|
19
|
+
changeOrigin: true,
|
|
20
|
+
},
|
|
21
|
+
// --- RESIZER START ---
|
|
22
|
+
"/images": {
|
|
23
|
+
target: "http://localhost:1111",
|
|
24
|
+
changeOrigin: true,
|
|
25
|
+
},
|
|
26
|
+
// --- RESIZER END ---
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
});
|
package/template/.parcelrc
DELETED
|
Binary file
|
package/template/src/index.html
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
-
<link rel="apple-touch-icon" sizes="180x180" href="./images/icon.png?width=180&height=180" />
|
|
7
|
-
<link rel="icon" type="image/png" sizes="32x32" href="./images/icon.png?width=32&height=32" />
|
|
8
|
-
<link rel="icon" type="image/png" sizes="16x16" href="./images/icon.png?width=16&height=16" />
|
|
9
|
-
<link rel="manifest" href="./site.webmanifest" />
|
|
10
|
-
<link rel="stylesheet" href="./styles.scss" />
|
|
11
|
-
</head>
|
|
12
|
-
<body>
|
|
13
|
-
<div id="root"></div>
|
|
14
|
-
<script type="module" src="index.tsx"></script>
|
|
15
|
-
</body>
|
|
16
|
-
</html>
|
package/template/src/styles.scss
DELETED
|
@@ -1,292 +0,0 @@
|
|
|
1
|
-
// Premium Dark Theme
|
|
2
|
-
$bg: #09090b;
|
|
3
|
-
$surface: #18181b;
|
|
4
|
-
$primary: #6366f1; // Indigo
|
|
5
|
-
$primary-hover: #818cf8;
|
|
6
|
-
$text: #f4f4f5;
|
|
7
|
-
$text-muted: #a1a1aa;
|
|
8
|
-
$border: #27272a;
|
|
9
|
-
|
|
10
|
-
body {
|
|
11
|
-
font-family: 'Inter', system-ui, sans-serif;
|
|
12
|
-
background-color: $bg;
|
|
13
|
-
color: $text;
|
|
14
|
-
margin: 0;
|
|
15
|
-
padding: 0;
|
|
16
|
-
line-height: 1.6;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
a {
|
|
20
|
-
color: $primary;
|
|
21
|
-
text-decoration: none;
|
|
22
|
-
font-weight: bold;
|
|
23
|
-
transition: color 0.2s;
|
|
24
|
-
|
|
25
|
-
&:hover {
|
|
26
|
-
color: $primary-hover;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
main {
|
|
31
|
-
max-width: 800px;
|
|
32
|
-
margin: 50px auto;
|
|
33
|
-
padding: 40px;
|
|
34
|
-
background: $surface;
|
|
35
|
-
border-radius: 16px;
|
|
36
|
-
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
|
|
37
|
-
border: 1px solid $border;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
header {
|
|
41
|
-
text-align: center;
|
|
42
|
-
margin-bottom: 40px;
|
|
43
|
-
|
|
44
|
-
h1 {
|
|
45
|
-
color: $primary;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
p {
|
|
49
|
-
color: $text-muted;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
figure {
|
|
53
|
-
width: 140px;
|
|
54
|
-
height: 140px;
|
|
55
|
-
margin: 0 auto 20px;
|
|
56
|
-
background-image: url("./images/icon.png?as=webp");
|
|
57
|
-
background-size: contain;
|
|
58
|
-
background-repeat: no-repeat;
|
|
59
|
-
background-position: center;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
section {
|
|
64
|
-
margin-top: 40px;
|
|
65
|
-
padding: 30px;
|
|
66
|
-
background: rgba(255, 255, 255, 0.02);
|
|
67
|
-
border-radius: 12px;
|
|
68
|
-
border: 1px solid $border;
|
|
69
|
-
|
|
70
|
-
ul {
|
|
71
|
-
list-style: none;
|
|
72
|
-
padding: 0;
|
|
73
|
-
|
|
74
|
-
li {
|
|
75
|
-
display: flex;
|
|
76
|
-
align-items: center;
|
|
77
|
-
justify-content: space-between;
|
|
78
|
-
padding: 15px 20px;
|
|
79
|
-
background: $bg;
|
|
80
|
-
border-radius: 8px;
|
|
81
|
-
margin-bottom: 12px;
|
|
82
|
-
border: 1px solid $border;
|
|
83
|
-
transition: transform 0.2s, border-color 0.2s;
|
|
84
|
-
|
|
85
|
-
&:hover {
|
|
86
|
-
transform: translateY(-2px);
|
|
87
|
-
border-color: $primary;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
nav {
|
|
91
|
-
display: flex;
|
|
92
|
-
gap: 8px;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
article {
|
|
98
|
-
padding: 0px 20px 10px;
|
|
99
|
-
background: $bg;
|
|
100
|
-
border-radius: 8px;
|
|
101
|
-
border: 1px solid $primary;
|
|
102
|
-
box-shadow: 0 0 15px rgba(99, 102, 241, 0.15);
|
|
103
|
-
|
|
104
|
-
h3 {
|
|
105
|
-
color: $primary;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
p {
|
|
109
|
-
margin: 5px 0;
|
|
110
|
-
color: $text;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
form {
|
|
115
|
-
display: flex;
|
|
116
|
-
gap: 12px;
|
|
117
|
-
flex-wrap: wrap;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Global Controls
|
|
122
|
-
input {
|
|
123
|
-
padding: 12px 16px;
|
|
124
|
-
background: $bg;
|
|
125
|
-
border: 1px solid $border;
|
|
126
|
-
border-radius: 8px;
|
|
127
|
-
color: $text;
|
|
128
|
-
outline: none;
|
|
129
|
-
transition: border-color 0.2s;
|
|
130
|
-
flex: 1;
|
|
131
|
-
min-width: 150px;
|
|
132
|
-
|
|
133
|
-
&:focus {
|
|
134
|
-
border-color: $primary;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
button {
|
|
139
|
-
padding: 12px 24px;
|
|
140
|
-
background: $primary;
|
|
141
|
-
color: #fff;
|
|
142
|
-
border: none;
|
|
143
|
-
border-radius: 8px;
|
|
144
|
-
cursor: pointer;
|
|
145
|
-
font-weight: bold;
|
|
146
|
-
transition: background 0.2s, transform 0.1s;
|
|
147
|
-
|
|
148
|
-
&:hover:not(:disabled) {
|
|
149
|
-
background: $primary-hover;
|
|
150
|
-
transform: scale(1.02);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
&.btn-danger {
|
|
154
|
-
background: #ef4444;
|
|
155
|
-
|
|
156
|
-
&:hover:not(:disabled) {
|
|
157
|
-
background: #dc2626;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// --- ADMIN START ---
|
|
163
|
-
// --- ADMIN DASHBOARD ---
|
|
164
|
-
.admin-login {
|
|
165
|
-
padding: 40px;
|
|
166
|
-
max-width: 400px;
|
|
167
|
-
margin: 0 auto;
|
|
168
|
-
text-align: center;
|
|
169
|
-
|
|
170
|
-
h2 {
|
|
171
|
-
color: $text;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
.back-link {
|
|
175
|
-
margin-bottom: 20px;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
form {
|
|
179
|
-
display: flex;
|
|
180
|
-
flex-direction: column;
|
|
181
|
-
gap: 15px;
|
|
182
|
-
margin-top: 20px;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
.admin-container {
|
|
188
|
-
padding: 40px;
|
|
189
|
-
max-width: 1200px;
|
|
190
|
-
margin: 0 auto;
|
|
191
|
-
|
|
192
|
-
.header {
|
|
193
|
-
display: flex;
|
|
194
|
-
justify-content: space-between;
|
|
195
|
-
align-items: center;
|
|
196
|
-
margin-bottom: 30px;
|
|
197
|
-
|
|
198
|
-
h2 {
|
|
199
|
-
margin: 0;
|
|
200
|
-
color: $text;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
a {
|
|
204
|
-
color: $primary;
|
|
205
|
-
text-decoration: none;
|
|
206
|
-
font-weight: bold;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
.controls {
|
|
211
|
-
display: flex;
|
|
212
|
-
gap: 15px;
|
|
213
|
-
align-items: center;
|
|
214
|
-
margin-bottom: 30px;
|
|
215
|
-
flex-wrap: wrap;
|
|
216
|
-
|
|
217
|
-
strong {
|
|
218
|
-
font-size: 1.1rem;
|
|
219
|
-
color: $text;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
.btn-logout {
|
|
223
|
-
margin-left: auto;
|
|
224
|
-
background: #ef4444;
|
|
225
|
-
|
|
226
|
-
&:hover {
|
|
227
|
-
background: #dc2626;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
.chart-container {
|
|
233
|
-
margin-bottom: 40px;
|
|
234
|
-
background: rgba(255, 255, 255, 0.02);
|
|
235
|
-
padding: 25px;
|
|
236
|
-
border-radius: 12px;
|
|
237
|
-
border: 1px solid $border;
|
|
238
|
-
|
|
239
|
-
h3 {
|
|
240
|
-
margin: 0 0 20px;
|
|
241
|
-
font-size: 1.1rem;
|
|
242
|
-
color: $text;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
.chart {
|
|
246
|
-
display: flex;
|
|
247
|
-
align-items: flex-end;
|
|
248
|
-
height: 150px;
|
|
249
|
-
gap: 4px;
|
|
250
|
-
|
|
251
|
-
.bar-wrapper {
|
|
252
|
-
flex: 1;
|
|
253
|
-
display: flex;
|
|
254
|
-
flex-direction: column;
|
|
255
|
-
align-items: center;
|
|
256
|
-
justify-content: flex-end;
|
|
257
|
-
height: 100%;
|
|
258
|
-
|
|
259
|
-
.count {
|
|
260
|
-
font-size: 11px;
|
|
261
|
-
color: $text;
|
|
262
|
-
margin-bottom: 4px;
|
|
263
|
-
font-weight: bold;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
.date {
|
|
267
|
-
font-size: 10px;
|
|
268
|
-
margin-top: 8px;
|
|
269
|
-
color: $text-muted;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
.bar {
|
|
273
|
-
width: 100%;
|
|
274
|
-
max-width: 24px;
|
|
275
|
-
border-radius: 4px 4px 0 0;
|
|
276
|
-
transition: height 0.3s ease;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
.bar.active {
|
|
280
|
-
background: $primary;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
.grid {
|
|
287
|
-
display: grid;
|
|
288
|
-
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
289
|
-
gap: 20px;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
// --- ADMIN END ---
|