easy-starter 2.0.0 → 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.
@@ -78,7 +78,7 @@ export default function Admin({ path }: AdminProps) {
78
78
  if (data) {
79
79
  setUsersList(data);
80
80
  } else if (err) {
81
- setUsersError(err.message || String(err));
81
+ setUsersError((err as any)?.message || String(err));
82
82
  }
83
83
  } catch (e) {
84
84
  setUsersError(String(e));
@@ -92,7 +92,7 @@ export default function Admin({ path }: AdminProps) {
92
92
  if (success) {
93
93
  setUsersList(prev => prev.map(u => u._id === userId ? { ...u, role: newRole } : u));
94
94
  } else if (err) {
95
- alert("Failed to update role: " + err.message);
95
+ alert("Failed to update role: " + ((err as any)?.message || String(err)));
96
96
  }
97
97
  } catch (e) {
98
98
  alert(String(e));
@@ -109,7 +109,7 @@ export default function Admin({ path }: AdminProps) {
109
109
  if (data) {
110
110
  setErrorsList(data);
111
111
  } else if (err) {
112
- setErrorLogsError(err.message || String(err));
112
+ setErrorLogsError((err as any)?.message || String(err));
113
113
  }
114
114
  } catch (e) {
115
115
  setErrorLogsError(String(e));
@@ -145,7 +145,7 @@ export default function Admin({ path }: AdminProps) {
145
145
  if (data) {
146
146
  setRecords(data as any);
147
147
  } else if (err) {
148
- setError(err.message || String(err));
148
+ setError((err as any)?.message || String(err));
149
149
  }
150
150
  // --- ANALYTICS END ---
151
151
  } catch (e) {
@@ -164,24 +164,24 @@ export default function Admin({ path }: AdminProps) {
164
164
 
165
165
  if (!isLoggedIn || user?.role !== "admin") {
166
166
  return (
167
- <div className="admin-login" style={{ padding: 40, textAlign: 'center' }}>
167
+ <div className="admin-login">
168
168
  {!isLoggedIn ? (
169
169
  <>
170
170
  <h2>Admin Login Required</h2>
171
171
  <p>Please log in using an account with administrative permissions.</p>
172
- <div style={{ display: 'flex', gap: 15, justifyContent: 'center', marginTop: 20 }}>
172
+ <div className="btn-center-group">
173
173
  <button onClick={showUserDialog}>Log In</button>
174
174
  <Link href="/">Back to website</Link>
175
175
  </div>
176
- <div style={{ marginTop: 30, fontSize: '0.85em', color: 'var(--text-muted)' }}>
176
+ <div className="tip-box">
177
177
  💡 <strong>Tip:</strong> You can create the first admin by manually changing <code>"role": "user"</code> to <code>"role": "admin"</code> in <code>easy-db/user.json</code> for your user record.
178
178
  </div>
179
179
  </>
180
180
  ) : (
181
181
  <>
182
182
  <h2>Access Denied</h2>
183
- <p>You do not have administrative permissions. (Logged in as <strong>{user?.name || user?.email}</strong>)</p>
184
- <div style={{ display: 'flex', gap: 15, justifyContent: 'center', marginTop: 20 }}>
183
+ <p>You do not have administrative permissions. (Logged in as <strong>{user?.name || (user as any)?.email}</strong>)</p>
184
+ <div className="btn-center-group">
185
185
  <button onClick={() => loginUser(null)}>Log Out</button>
186
186
  <Link href="/">Back to website</Link>
187
187
  </div>
@@ -192,7 +192,7 @@ export default function Admin({ path }: AdminProps) {
192
192
  }
193
193
  // --- ADMIN_WITH_AUTH END ---
194
194
 
195
- // --- ADMIN_NO_AUTH START ---
195
+ /* --- ADMIN_NO_AUTH START --- */
196
196
  const [password, setPassword] = useState("");
197
197
  const [loggedIn, setLoggedIn] = useState(false);
198
198
 
@@ -201,11 +201,11 @@ export default function Admin({ path }: AdminProps) {
201
201
  setLoadingErrors(true);
202
202
  setErrorLogsError("");
203
203
  try {
204
- const [data, err] = await api.getEasyAnalyticsErrors({ password: pwd, month: m });
204
+ const [data, err] = await (api as any).getEasyAnalyticsErrors({ password: pwd, month: m });
205
205
  if (data) {
206
206
  setErrorsList(data);
207
207
  } else if (err) {
208
- setErrorLogsError(err.message || String(err));
208
+ setErrorLogsError((err as any)?.message || String(err));
209
209
  }
210
210
  } catch (e) {
211
211
  setErrorLogsError(String(e));
@@ -227,25 +227,25 @@ export default function Admin({ path }: AdminProps) {
227
227
  setError("");
228
228
  try {
229
229
  // --- ANALYTICS START ---
230
- const [data, err] = await api.getEasyAnalyticsData({ password: pwd, month: m });
230
+ const [data, err] = await (api as any).getEasyAnalyticsData({ password: pwd, month: m });
231
231
  if (data) {
232
232
  setRecords(data as any);
233
233
  setLoggedIn(true);
234
234
  localStorage.setItem("admin_pwd", pwd);
235
235
  } else if (err) {
236
- setError(err.message || String(err));
236
+ setError((err as any)?.message || String(err));
237
237
  setLoggedIn(false);
238
238
  localStorage.removeItem("admin_pwd");
239
239
  }
240
240
  // --- ANALYTICS END ---
241
241
  // --- NO_ANALYTICS START ---
242
242
  // --- ADMIN_ERRORS START ---
243
- const [data, err] = await api.getEasyAnalyticsErrors({ password: pwd, month: m });
244
- if (data || !err) {
243
+ const [errData, fetchErr] = await (api as any).getEasyAnalyticsErrors({ password: pwd, month: m });
244
+ if (errData || !fetchErr) {
245
245
  setLoggedIn(true);
246
246
  localStorage.setItem("admin_pwd", pwd);
247
247
  } else {
248
- setError(err.message || String(err));
248
+ setError((fetchErr as any)?.message || String(fetchErr));
249
249
  setLoggedIn(false);
250
250
  localStorage.removeItem("admin_pwd");
251
251
  }
@@ -295,29 +295,28 @@ export default function Admin({ path }: AdminProps) {
295
295
 
296
296
  if (!loggedIn) {
297
297
  return (
298
- <div className="admin-login" style={{ padding: 40, textAlign: 'center' }}>
298
+ <div className="admin-login">
299
299
  <h2>Admin Login Required</h2>
300
- <form onSubmit={handleLogin} style={{ display: 'flex', flexDirection: 'column', gap: 10, maxWidth: 300, margin: '20px auto' }}>
300
+ <form onSubmit={handleLogin} className="admin-login-form">
301
301
  <input
302
302
  type="password"
303
303
  placeholder="Enter admin password"
304
304
  value={password}
305
305
  onChange={e => setPassword(e.target.value)}
306
- style={{ padding: 10, background: 'var(--surface)', border: '1px solid var(--border)', color: 'var(--text)', borderRadius: 4 }}
307
306
  required
308
307
  />
309
- <button type="submit" disabled={loading} style={{ padding: 10, background: 'var(--primary)', color: '#fff', border: 'none', borderRadius: 4, cursor: 'pointer' }}>
308
+ <button type="submit" disabled={loading}>
310
309
  {loading ? "Logging in..." : "Login"}
311
310
  </button>
312
311
  </form>
313
- {error && <p style={{ color: "#ef4444" }}>{error}</p>}
314
- <div style={{ marginTop: 20 }}>
312
+ {error && <p className="text-error">{error}</p>}
313
+ <div className="admin-link-wrapper">
315
314
  <Link href="/">Back to website</Link>
316
315
  </div>
317
316
  </div>
318
317
  );
319
318
  }
320
- // --- ADMIN_NO_AUTH END ---
319
+ /* --- ADMIN_NO_AUTH END --- */
321
320
 
322
321
  return (
323
322
  <div className="admin-container">
@@ -326,20 +325,11 @@ export default function Admin({ path }: AdminProps) {
326
325
  <Link href="/">Back to website</Link>
327
326
  </div>
328
327
 
329
- <div className="admin-tabs" style={{ display: 'flex', gap: 10, margin: '20px 0', borderBottom: '1px solid var(--border)', paddingBottom: 10 }}>
328
+ <div className="admin-tabs">
330
329
  {/* --- ANALYTICS START --- */}
331
330
  <Link
332
331
  href="/admin/analytics"
333
- style={{
334
- padding: '8px 16px',
335
- background: activeTab === "analytics" ? 'var(--primary)' : 'transparent',
336
- color: activeTab === "analytics" ? '#fff' : 'var(--text-muted)',
337
- border: 'none',
338
- borderRadius: 4,
339
- cursor: 'pointer',
340
- fontWeight: 'bold',
341
- textDecoration: 'none'
342
- }}
332
+ className={`tab-btn ${activeTab === "analytics" ? "active" : ""}`}
343
333
  >
344
334
  Analytics
345
335
  </Link>
@@ -347,16 +337,7 @@ export default function Admin({ path }: AdminProps) {
347
337
  {/* --- ADMIN_ERRORS START --- */}
348
338
  <Link
349
339
  href="/admin/errors"
350
- style={{
351
- padding: '8px 16px',
352
- background: activeTab === "errors" ? 'var(--primary)' : 'transparent',
353
- color: activeTab === "errors" ? '#fff' : 'var(--text-muted)',
354
- border: 'none',
355
- borderRadius: 4,
356
- cursor: 'pointer',
357
- fontWeight: 'bold',
358
- textDecoration: 'none'
359
- }}
340
+ className={`tab-btn ${activeTab === "errors" ? "active" : ""}`}
360
341
  >
361
342
  Error Logs
362
343
  </Link>
@@ -364,16 +345,7 @@ export default function Admin({ path }: AdminProps) {
364
345
  {/* --- ADMIN_USERS START --- */}
365
346
  <Link
366
347
  href="/admin/users"
367
- style={{
368
- padding: '8px 16px',
369
- background: activeTab === "users" ? 'var(--primary)' : 'transparent',
370
- color: activeTab === "users" ? '#fff' : 'var(--text-muted)',
371
- border: 'none',
372
- borderRadius: 4,
373
- cursor: 'pointer',
374
- fontWeight: 'bold',
375
- textDecoration: 'none'
376
- }}
348
+ className={`tab-btn ${activeTab === "users" ? "active" : ""}`}
377
349
  >
378
350
  User Management
379
351
  </Link>
@@ -406,8 +378,8 @@ export default function Admin({ path }: AdminProps) {
406
378
  {/* --- ADMIN_NO_AUTH END --- */}
407
379
  </div>
408
380
 
409
- {loading && activeTab === "analytics" && <p style={{ textAlign: "center" }}>Loading...</p>}
410
- {error && activeTab === "analytics" && <p style={{ color: "#ef4444", textAlign: "center" }}>{error}</p>}
381
+ {loading && activeTab === "analytics" && <p>Loading...</p>}
382
+ {error && activeTab === "analytics" && <p className="text-error">{error}</p>}
411
383
 
412
384
  {/* --- ANALYTICS START --- */}
413
385
  {activeTab === "analytics" && (
@@ -430,9 +402,9 @@ export default function Admin({ path }: AdminProps) {
430
402
  {/* --- NO_ANALYTICS START --- */}
431
403
  {/* --- NO_ADMIN_ERRORS START --- */}
432
404
  {/* --- NO_ADMIN_USERS START --- */}
433
- <div style={{ textAlign: 'center', padding: 40, border: '1px dashed var(--border)', borderRadius: 8 }}>
405
+ <div className="admin-card">
434
406
  <h3>Generic Admin Panel</h3>
435
- <p style={{ color: 'var(--text-muted)' }}>Welcome to the admin panel.</p>
407
+ <p>Welcome to the admin panel.</p>
436
408
  </div>
437
409
  {/* --- NO_ADMIN_USERS END --- */}
438
410
  {/* --- NO_ADMIN_ERRORS END --- */}
@@ -61,10 +61,10 @@ export default function AnalyticsTab({ records, month }: AnalyticsTabProps) {
61
61
  </div>
62
62
 
63
63
  <div className="grid">
64
- <GroupTable title="Most visited paths" data={byURL} columnName="Path" />
65
- <GroupTable title="Referrers" data={byReferrer} columnName="Source" />
66
- <GroupTable title="Browsers" data={byBrowserLike} columnName="Browser" />
67
- <GroupTable title="Display resolutions" data={byWidth} columnName="Resolution" />
64
+ <GroupTable title="Most visited paths" data={byURL} />
65
+ <GroupTable title="Referrers" data={byReferrer} />
66
+ <GroupTable title="Browsers" data={byBrowserLike} />
67
+ <GroupTable title="Display resolutions" data={byWidth} />
68
68
  </div>
69
69
  </div>
70
70
  );
@@ -9,32 +9,32 @@ type ErrorsTabProps = {
9
9
  export default function ErrorsTab({ errors, loading, errorMsg }: ErrorsTabProps) {
10
10
  return (
11
11
  <div>
12
- <h3 style={{ marginBottom: 15 }}>Logged Errors</h3>
12
+ <h3>Logged Errors</h3>
13
13
  {loading && <p>Loading error logs...</p>}
14
- {errorMsg && <p style={{ color: "#ef4444" }}>{errorMsg}</p>}
14
+ {errorMsg && <p className="text-error">{errorMsg}</p>}
15
15
  {!loading && !errorMsg && errors.length === 0 && (
16
- <p style={{ color: "var(--text-muted)" }}>No errors logged for this month.</p>
16
+ <p>No errors logged for this month.</p>
17
17
  )}
18
18
  {!loading && !errorMsg && errors.length > 0 && (
19
- <div style={{ display: 'flex', flexDirection: 'column', gap: 15 }}>
19
+ <div className="error-list">
20
20
  {errors.map((errRecord: any, i: number) => (
21
- <div key={i} style={{ padding: 15, background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 6 }}>
22
- <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10, fontSize: '0.9em' }}>
23
- <span style={{ color: 'var(--primary)', fontWeight: 'bold' }}>{errRecord.name || "Error"}</span>
24
- <span style={{ color: 'var(--text-muted)' }}>{errRecord.created ? new Date(errRecord.created).toLocaleString() : ""}</span>
21
+ <div key={i} className="error-item">
22
+ <div className="error-item-header">
23
+ <span className="error-name">{errRecord.name || "Error"}</span>
24
+ <span>{errRecord.created ? new Date(errRecord.created).toLocaleString() : ""}</span>
25
25
  </div>
26
- <p style={{ margin: '5px 0', fontFamily: 'monospace', whiteSpace: 'pre-wrap', wordBreak: 'break-all', color: '#f87171' }}>
26
+ <p className="error-message">
27
27
  {errRecord.message}
28
28
  </p>
29
29
  {errRecord.stack && (
30
- <details style={{ marginTop: 10 }}>
31
- <summary style={{ cursor: 'pointer', color: 'var(--text-muted)', fontSize: '0.85em' }}>Show Stack Trace</summary>
32
- <pre style={{ marginTop: 5, padding: 10, background: '#09090b', overflowX: 'auto', fontSize: '0.8em', color: 'var(--text-muted)' }}>
30
+ <details className="error-details">
31
+ <summary>Show Stack Trace</summary>
32
+ <pre>
33
33
  {errRecord.stack}
34
34
  </pre>
35
35
  </details>
36
36
  )}
37
- <div style={{ marginTop: 10, fontSize: '0.8em', color: 'var(--text-muted)' }}>
37
+ <div>
38
38
  URL: {errRecord.url} | User Agent: {errRecord.userAgent}
39
39
  </div>
40
40
  </div>
@@ -10,41 +10,34 @@ type UsersTabProps = {
10
10
  export default function UsersTab({ users, loading, errorMsg, onRoleChange }: UsersTabProps) {
11
11
  return (
12
12
  <div>
13
- <h3 style={{ marginBottom: 15 }}>User Management</h3>
13
+ <h3>User Management</h3>
14
14
  {loading && <p>Loading users...</p>}
15
- {errorMsg && <p style={{ color: "#ef4444" }}>{errorMsg}</p>}
15
+ {errorMsg && <p className="text-error">{errorMsg}</p>}
16
16
  {!loading && !errorMsg && (
17
- <div style={{ overflowX: 'auto' }}>
18
- <table style={{ width: '100%', borderCollapse: 'collapse', textAlign: 'left' }}>
17
+ <div className="table-wrapper">
18
+ <table className="admin-table">
19
19
  <thead>
20
- <tr style={{ borderBottom: '2px solid var(--border)' }}>
21
- <th style={{ padding: 10 }}>Email</th>
22
- <th style={{ padding: 10 }}>Name</th>
23
- <th style={{ padding: 10 }}>Role</th>
24
- <th style={{ padding: 10 }}>Actions</th>
20
+ <tr>
21
+ <th>Email</th>
22
+ <th>Name</th>
23
+ <th>Role</th>
24
+ <th>Actions</th>
25
25
  </tr>
26
26
  </thead>
27
27
  <tbody>
28
28
  {users.map((usr: any) => (
29
- <tr key={usr._id} style={{ borderBottom: '1px solid var(--border)' }}>
30
- <td style={{ padding: 10 }}>{usr.email}</td>
31
- <td style={{ padding: 10 }}>{usr.name || "N/A"}</td>
32
- <td style={{ padding: 10 }}>
33
- <span style={{
34
- padding: '2px 6px',
35
- borderRadius: 4,
36
- fontSize: '0.85em',
37
- background: usr.role === 'admin' ? 'rgba(99, 102, 241, 0.2)' : 'rgba(255, 255, 255, 0.05)',
38
- color: usr.role === 'admin' ? 'var(--primary-hover)' : 'var(--text-muted)'
39
- }}>
29
+ <tr key={usr._id}>
30
+ <td>{usr.email}</td>
31
+ <td>{usr.name || "N/A"}</td>
32
+ <td>
33
+ <span className={`role-badge ${usr.role === 'admin' ? 'admin' : ''}`}>
40
34
  {usr.role || "user"}
41
35
  </span>
42
36
  </td>
43
- <td style={{ padding: 10 }}>
37
+ <td>
44
38
  <select
45
39
  value={usr.role || "user"}
46
40
  onChange={(e) => onRoleChange(usr._id, e.target.value as any)}
47
- style={{ padding: '4px 8px', background: 'var(--surface)', border: '1px solid var(--border)', color: 'var(--text)', borderRadius: 4 }}
48
41
  >
49
42
  <option value="user">User</option>
50
43
  <option value="admin">Admin</option>
@@ -16,11 +16,21 @@ type UseHeadProps = {
16
16
  };
17
17
 
18
18
  export function useHead({ title, description, image, canonical, lang }: UseHeadProps) {
19
+ // --- OGIMAGE START ---
20
+ const defaultOgImage = typeof window !== "undefined"
21
+ ? `${window.location.origin}/og-image.png?path=${encodeURIComponent(window.location.pathname)}`
22
+ : "/og-image.png";
23
+ const resolvedImage = image || defaultOgImage;
24
+ // --- OGIMAGE END ---
25
+ /* --- NO_OGIMAGE START --- */
26
+ const resolvedImage = image || "/images/icon.png";
27
+ /* --- NO_OGIMAGE END --- */
28
+
19
29
  // --- SSR START ---
20
30
  useHeaders({
21
31
  title,
22
32
  description,
23
- image: image || `/images/icon.png`, // Images must be placed in the public directory to be accessible
33
+ image: resolvedImage,
24
34
  canonical: canonical || (typeof window !== "undefined" ? window.location.origin + window.location.pathname : ""),
25
35
  lang,
26
36
  });
@@ -458,3 +458,210 @@ button {
458
458
  margin-top: 4px;
459
459
  }
460
460
  /* --- AUTH END --- */
461
+
462
+ /* Helper & UI Utility Classes */
463
+ .auth-bar {
464
+ display: flex;
465
+ justify-content: flex-end;
466
+ padding: 10px 0;
467
+ border-bottom: 1px solid var(--border);
468
+ margin-bottom: 20px;
469
+ }
470
+
471
+ .auth-user-info {
472
+ display: flex;
473
+ align-items: center;
474
+ gap: 15px;
475
+ }
476
+
477
+ .admin-link-wrapper {
478
+ margin-top: 20px;
479
+ }
480
+
481
+ .not-found {
482
+ padding: 40px;
483
+ text-align: center;
484
+ }
485
+
486
+ .loading-fallback {
487
+ padding: 40px;
488
+ text-align: center;
489
+ color: var(--text-muted);
490
+ }
491
+
492
+ .text-error {
493
+ color: #ef4444;
494
+ }
495
+
496
+ .text-success {
497
+ color: #34d399;
498
+ }
499
+
500
+ .alert-success {
501
+ background: rgba(16, 185, 129, 0.1);
502
+ border: 1px solid rgba(16, 185, 129, 0.2);
503
+ color: #34d399;
504
+ }
505
+
506
+ .reset-password-main {
507
+ max-width: 450px;
508
+ margin: 60px auto;
509
+ }
510
+
511
+ .btn-group {
512
+ display: flex;
513
+ gap: 15px;
514
+ margin-top: 10px;
515
+
516
+ & > * {
517
+ flex: 1;
518
+ }
519
+ }
520
+
521
+ .btn-center-group {
522
+ display: flex;
523
+ gap: 15px;
524
+ justify-content: center;
525
+ margin-top: 20px;
526
+ }
527
+
528
+ .tip-box {
529
+ margin-top: 30px;
530
+ font-size: 0.85em;
531
+ color: var(--text-muted);
532
+ }
533
+
534
+ .admin-tabs {
535
+ display: flex;
536
+ gap: 10px;
537
+ margin: 20px 0;
538
+ border-bottom: 1px solid var(--border);
539
+ padding-bottom: 10px;
540
+ }
541
+
542
+ .tab-btn {
543
+ padding: 8px 16px;
544
+ background: transparent;
545
+ border: none;
546
+ color: var(--text-muted);
547
+ font-weight: 500;
548
+ cursor: pointer;
549
+ border-radius: 6px;
550
+ transition: background 0.2s, color 0.2s;
551
+
552
+ &:hover {
553
+ color: var(--text);
554
+ background: rgba(255, 255, 255, 0.05);
555
+ }
556
+
557
+ &.active {
558
+ background: var(--primary);
559
+ color: #fff;
560
+ }
561
+ }
562
+
563
+ .admin-card {
564
+ padding: 40px;
565
+ text-align: center;
566
+ border: 1px dashed var(--border);
567
+ border-radius: 8px;
568
+ }
569
+
570
+ .admin-login-form {
571
+ display: flex;
572
+ flex-direction: column;
573
+ gap: 10px;
574
+ max-width: 300px;
575
+ margin: 20px auto;
576
+
577
+ input {
578
+ padding: 10px;
579
+ }
580
+
581
+ button {
582
+ padding: 10px;
583
+ }
584
+ }
585
+
586
+ .error-list {
587
+ display: flex;
588
+ flex-direction: column;
589
+ gap: 15px;
590
+ }
591
+
592
+ .error-item {
593
+ padding: 15px;
594
+ background: var(--surface);
595
+ border: 1px solid var(--border);
596
+ border-radius: 6px;
597
+ }
598
+
599
+ .error-item-header {
600
+ display: flex;
601
+ justify-content: space-between;
602
+ margin-bottom: 10px;
603
+ font-size: 0.9em;
604
+ }
605
+
606
+ .error-name {
607
+ color: var(--primary);
608
+ font-weight: bold;
609
+ }
610
+
611
+ .error-message {
612
+ margin: 5px 0;
613
+ font-family: monospace;
614
+ white-space: pre-wrap;
615
+ word-break: break-all;
616
+ color: #f87171;
617
+ }
618
+
619
+ .error-details {
620
+ margin-top: 10px;
621
+
622
+ summary {
623
+ cursor: pointer;
624
+ color: var(--text-muted);
625
+ font-size: 0.85em;
626
+ }
627
+
628
+ pre {
629
+ margin-top: 5px;
630
+ padding: 10px;
631
+ background: #09090b;
632
+ overflow-x: auto;
633
+ font-size: 0.8em;
634
+ color: var(--text-muted);
635
+ }
636
+ }
637
+
638
+ .table-wrapper {
639
+ overflow-x: auto;
640
+ }
641
+
642
+ .role-badge {
643
+ padding: 2px 6px;
644
+ border-radius: 4px;
645
+ font-size: 0.85em;
646
+ background: rgba(255, 255, 255, 0.05);
647
+ color: var(--text-muted);
648
+
649
+ &.admin {
650
+ background: rgba(99, 102, 241, 0.2);
651
+ color: var(--primary-hover);
652
+ }
653
+ }
654
+
655
+ .admin-table {
656
+ width: 100%;
657
+ border-collapse: collapse;
658
+ text-align: left;
659
+
660
+ tr {
661
+ border-bottom: 1px solid var(--border);
662
+ }
663
+
664
+ th, td {
665
+ padding: 12px 16px;
666
+ }
667
+ }
@@ -14,7 +14,11 @@
14
14
  "strictNullChecks": true,
15
15
  "skipLibCheck": true,
16
16
  "forceConsistentCasingInFileNames": true,
17
- "resolveJsonModule": true
17
+ "resolveJsonModule": true,
18
+ "types": [
19
+ "node",
20
+ "vite/client"
21
+ ]
18
22
  },
19
23
  "include": [
20
24
  "src/**/*",
package/template/types.ts CHANGED
@@ -28,16 +28,16 @@ export type API = APIDefinition<{
28
28
  addItem: Endpoint<Omit<Item, "_id">, string>;
29
29
  removeItem: Endpoint<{ id: string }, boolean>;
30
30
 
31
- // --- ADMIN_NO_AUTH START ---
31
+ /* --- ADMIN_NO_AUTH START --- */
32
32
  getEasyAnalyticsData: Endpoint<{ password: string, month: string }, any[]>;
33
- // --- ADMIN_NO_AUTH END ---
33
+ /* --- ADMIN_NO_AUTH END --- */
34
34
  // --- ADMIN_WITH_AUTH START ---
35
35
  getEasyAnalyticsData: Endpoint<{ month: string }, any[]>;
36
36
  // --- ADMIN_WITH_AUTH END ---
37
37
 
38
- // --- ADMIN_ERRORS_NO_AUTH START ---
38
+ /* --- ADMIN_ERRORS_NO_AUTH START --- */
39
39
  getEasyAnalyticsErrors: Endpoint<{ password: string, month: string }, any[]>;
40
- // --- ADMIN_ERRORS_NO_AUTH END ---
40
+ /* --- ADMIN_ERRORS_NO_AUTH END --- */
41
41
  // --- ADMIN_ERRORS_WITH_AUTH START ---
42
42
  getEasyAnalyticsErrors: Endpoint<{ month: string }, any[]>;
43
43
  // --- ADMIN_ERRORS_WITH_AUTH END ---
@@ -1,9 +1,8 @@
1
1
  import { defineConfig } from "vite";
2
2
  import react from "@vitejs/plugin-react";
3
- import { imagetools } from "vite-imagetools";
4
3
 
5
4
  export default defineConfig({
6
- plugins: [react(), imagetools()],
5
+ plugins: [react()],
7
6
  define: {
8
7
  "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV || "development"),
9
8
  "global": "globalThis",
@@ -19,6 +18,12 @@ export default defineConfig({
19
18
  target: "http://localhost:1111",
20
19
  changeOrigin: true,
21
20
  },
21
+ // --- RESIZER START ---
22
+ "/images": {
23
+ target: "http://localhost:1111",
24
+ changeOrigin: true,
25
+ },
26
+ // --- RESIZER END ---
22
27
  },
23
28
  },
24
29
  });
Binary file
Binary file
Binary file
Binary file
Binary file