millas 0.1.9 → 0.2.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/package.json +1 -1
- package/src/admin/Admin.js +36 -11
- package/src/admin/views/layouts/base.njk +867 -246
- package/src/admin/views/pages/dashboard.njk +32 -26
- package/src/admin/views/pages/form.njk +228 -111
- package/src/admin/views/pages/list.njk +226 -50
|
@@ -4,30 +4,61 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>{% block title %}{{ pageTitle }}{% endblock %} — {{ adminTitle }}</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700;1,9..40,400&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
7
10
|
<style>
|
|
8
11
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
12
|
+
|
|
9
13
|
:root {
|
|
10
|
-
|
|
11
|
-
--
|
|
12
|
-
--
|
|
13
|
-
--
|
|
14
|
-
--
|
|
15
|
-
--
|
|
16
|
-
--
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
--
|
|
20
|
-
--
|
|
21
|
-
--
|
|
22
|
-
--
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
--
|
|
26
|
-
--
|
|
27
|
-
--
|
|
14
|
+
/* ── Palette ── */
|
|
15
|
+
--bg: #f4f5f7;
|
|
16
|
+
--surface: #ffffff;
|
|
17
|
+
--surface2: #f8f9fb;
|
|
18
|
+
--surface3: #eef0f4;
|
|
19
|
+
--border: #e3e6ec;
|
|
20
|
+
--border-soft: #edf0f5;
|
|
21
|
+
|
|
22
|
+
/* ── Brand ── */
|
|
23
|
+
--primary: #2563eb;
|
|
24
|
+
--primary-h: #1d4ed8;
|
|
25
|
+
--primary-soft: #eff4ff;
|
|
26
|
+
--primary-dim: #dbeafe;
|
|
27
|
+
|
|
28
|
+
/* ── Text ── */
|
|
29
|
+
--text: #111827;
|
|
30
|
+
--text-soft: #374151;
|
|
31
|
+
--text-muted: #6b7280;
|
|
32
|
+
--text-xmuted: #9ca3af;
|
|
33
|
+
|
|
34
|
+
/* ── Semantic ── */
|
|
35
|
+
--success: #16a34a;
|
|
36
|
+
--success-bg: #f0fdf4;
|
|
37
|
+
--success-border:#bbf7d0;
|
|
38
|
+
--danger: #dc2626;
|
|
39
|
+
--danger-bg: #fef2f2;
|
|
40
|
+
--danger-border:#fecaca;
|
|
41
|
+
--warning: #d97706;
|
|
42
|
+
--warning-bg: #fffbeb;
|
|
43
|
+
--warning-border:#fed7aa;
|
|
44
|
+
--info: #0284c7;
|
|
45
|
+
--info-bg: #f0f9ff;
|
|
46
|
+
--info-border: #bae6fd;
|
|
47
|
+
|
|
48
|
+
/* ── Shape ── */
|
|
49
|
+
--radius: 8px;
|
|
50
|
+
--radius-sm: 6px;
|
|
51
|
+
--radius-lg: 12px;
|
|
52
|
+
--shadow-sm: 0 1px 3px rgba(0,0,0,.08), 0 1px 2px rgba(0,0,0,.04);
|
|
53
|
+
--shadow: 0 4px 12px rgba(0,0,0,.08), 0 2px 4px rgba(0,0,0,.04);
|
|
54
|
+
--shadow-lg: 0 12px 32px rgba(0,0,0,.12), 0 4px 8px rgba(0,0,0,.06);
|
|
55
|
+
|
|
56
|
+
/* ── Sidebar ── */
|
|
57
|
+
--sidebar-w: 232px;
|
|
28
58
|
}
|
|
59
|
+
|
|
29
60
|
body {
|
|
30
|
-
font-family: '
|
|
61
|
+
font-family: 'DM Sans', system-ui, sans-serif;
|
|
31
62
|
background: var(--bg);
|
|
32
63
|
color: var(--text);
|
|
33
64
|
display: flex;
|
|
@@ -35,324 +66,842 @@
|
|
|
35
66
|
overflow: hidden;
|
|
36
67
|
font-size: 14px;
|
|
37
68
|
line-height: 1.5;
|
|
69
|
+
-webkit-font-smoothing: antialiased;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* ════════════════════════════════════════
|
|
73
|
+
ICONS (inline SVG sprite system)
|
|
74
|
+
════════════════════════════════════════ */
|
|
75
|
+
.icon {
|
|
76
|
+
display: inline-flex;
|
|
77
|
+
align-items: center;
|
|
78
|
+
justify-content: center;
|
|
79
|
+
flex-shrink: 0;
|
|
38
80
|
}
|
|
81
|
+
.icon svg {
|
|
82
|
+
width: 1em;
|
|
83
|
+
height: 1em;
|
|
84
|
+
fill: none;
|
|
85
|
+
stroke: currentColor;
|
|
86
|
+
stroke-width: 1.75;
|
|
87
|
+
stroke-linecap: round;
|
|
88
|
+
stroke-linejoin: round;
|
|
89
|
+
}
|
|
90
|
+
.icon-sm svg { stroke-width: 2; }
|
|
91
|
+
.icon-14 { font-size: 14px; }
|
|
92
|
+
.icon-15 { font-size: 15px; }
|
|
93
|
+
.icon-16 { font-size: 16px; }
|
|
94
|
+
.icon-18 { font-size: 18px; }
|
|
95
|
+
.icon-20 { font-size: 20px; }
|
|
39
96
|
|
|
40
|
-
/*
|
|
97
|
+
/* ════════════════════════════════════════
|
|
98
|
+
SIDEBAR
|
|
99
|
+
════════════════════════════════════════ */
|
|
41
100
|
#sidebar {
|
|
42
|
-
width:
|
|
101
|
+
width: var(--sidebar-w);
|
|
102
|
+
min-width: var(--sidebar-w);
|
|
43
103
|
background: var(--surface);
|
|
44
104
|
border-right: 1px solid var(--border);
|
|
45
|
-
display: flex;
|
|
46
|
-
|
|
105
|
+
display: flex;
|
|
106
|
+
flex-direction: column;
|
|
107
|
+
overflow-y: auto;
|
|
108
|
+
overflow-x: hidden;
|
|
47
109
|
}
|
|
110
|
+
|
|
48
111
|
.sidebar-brand {
|
|
49
|
-
padding:
|
|
50
|
-
border-bottom: 1px solid var(--border);
|
|
51
|
-
display: flex;
|
|
112
|
+
padding: 18px 16px 16px;
|
|
113
|
+
border-bottom: 1px solid var(--border-soft);
|
|
114
|
+
display: flex;
|
|
115
|
+
align-items: center;
|
|
116
|
+
gap: 10px;
|
|
52
117
|
}
|
|
53
|
-
.brand-
|
|
54
|
-
width:
|
|
55
|
-
background:
|
|
56
|
-
border-radius:
|
|
118
|
+
.brand-logo {
|
|
119
|
+
width: 34px; height: 34px;
|
|
120
|
+
background: var(--primary);
|
|
121
|
+
border-radius: 9px;
|
|
57
122
|
display: flex; align-items: center; justify-content: center;
|
|
58
|
-
|
|
123
|
+
color: #fff;
|
|
124
|
+
font-size: 16px;
|
|
125
|
+
flex-shrink: 0;
|
|
126
|
+
box-shadow: 0 2px 8px rgba(37,99,235,.35);
|
|
127
|
+
}
|
|
128
|
+
.brand-text { line-height: 1.25; overflow: hidden; }
|
|
129
|
+
.brand-name {
|
|
130
|
+
font-size: 13.5px;
|
|
131
|
+
font-weight: 700;
|
|
132
|
+
color: var(--text);
|
|
133
|
+
white-space: nowrap;
|
|
134
|
+
overflow: hidden;
|
|
135
|
+
text-overflow: ellipsis;
|
|
59
136
|
}
|
|
60
|
-
.brand-
|
|
61
|
-
.brand-name { font-size: 14px; font-weight: 700; color: var(--text); }
|
|
62
|
-
.brand-sub { font-size: 11px; color: var(--text-muted); }
|
|
137
|
+
.brand-sub { font-size: 11px; color: var(--text-muted); }
|
|
63
138
|
|
|
64
|
-
.nav-section { padding:
|
|
139
|
+
.nav-section { padding: 10px 10px 4px; }
|
|
65
140
|
.nav-label {
|
|
66
|
-
font-size:
|
|
67
|
-
|
|
68
|
-
|
|
141
|
+
font-size: 10.5px;
|
|
142
|
+
font-weight: 600;
|
|
143
|
+
color: var(--text-xmuted);
|
|
144
|
+
text-transform: uppercase;
|
|
145
|
+
letter-spacing: 0.7px;
|
|
146
|
+
padding: 0 8px 6px;
|
|
69
147
|
}
|
|
70
148
|
.nav-item {
|
|
71
|
-
display: flex;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
149
|
+
display: flex;
|
|
150
|
+
align-items: center;
|
|
151
|
+
gap: 9px;
|
|
152
|
+
padding: 7px 10px;
|
|
153
|
+
border-radius: var(--radius-sm);
|
|
154
|
+
color: var(--text-muted);
|
|
155
|
+
text-decoration: none;
|
|
156
|
+
font-size: 13.5px;
|
|
157
|
+
font-weight: 500;
|
|
158
|
+
transition: background .12s, color .12s;
|
|
159
|
+
cursor: pointer;
|
|
160
|
+
border: none;
|
|
161
|
+
background: none;
|
|
162
|
+
width: 100%;
|
|
163
|
+
text-align: left;
|
|
164
|
+
}
|
|
165
|
+
.nav-item:hover { background: var(--surface2); color: var(--text-soft); }
|
|
166
|
+
.nav-item.active { background: var(--primary-soft); color: var(--primary); }
|
|
167
|
+
.nav-item.active .nav-icon { color: var(--primary); }
|
|
168
|
+
|
|
169
|
+
.nav-icon {
|
|
170
|
+
color: var(--text-xmuted);
|
|
171
|
+
transition: color .12s;
|
|
172
|
+
width: 18px;
|
|
173
|
+
display: flex;
|
|
174
|
+
align-items: center;
|
|
175
|
+
justify-content: center;
|
|
176
|
+
flex-shrink: 0;
|
|
177
|
+
}
|
|
178
|
+
.nav-item:hover .nav-icon { color: var(--text-soft); }
|
|
179
|
+
.nav-item.active .nav-icon { color: var(--primary); }
|
|
180
|
+
|
|
81
181
|
.nav-count {
|
|
82
|
-
margin-left: auto;
|
|
83
|
-
|
|
84
|
-
|
|
182
|
+
margin-left: auto;
|
|
183
|
+
font-size: 11px;
|
|
184
|
+
font-weight: 500;
|
|
185
|
+
background: var(--surface3);
|
|
186
|
+
color: var(--text-muted);
|
|
187
|
+
padding: 1px 6px;
|
|
188
|
+
border-radius: 99px;
|
|
189
|
+
min-width: 20px;
|
|
190
|
+
text-align: center;
|
|
85
191
|
}
|
|
86
192
|
|
|
87
193
|
.sidebar-footer {
|
|
88
194
|
margin-top: auto;
|
|
89
|
-
padding: 16px;
|
|
90
|
-
border-top: 1px solid var(--border);
|
|
195
|
+
padding: 12px 16px;
|
|
196
|
+
border-top: 1px solid var(--border-soft);
|
|
197
|
+
}
|
|
198
|
+
.sidebar-version {
|
|
199
|
+
font-size: 11px;
|
|
200
|
+
color: var(--text-xmuted);
|
|
91
201
|
}
|
|
92
|
-
.sidebar-version { font-size: 11px; color: var(--text-muted); text-align: center; }
|
|
93
202
|
|
|
94
|
-
/*
|
|
95
|
-
|
|
203
|
+
/* ════════════════════════════════════════
|
|
204
|
+
MAIN AREA
|
|
205
|
+
════════════════════════════════════════ */
|
|
206
|
+
#main {
|
|
207
|
+
flex: 1;
|
|
208
|
+
display: flex;
|
|
209
|
+
flex-direction: column;
|
|
210
|
+
overflow: hidden;
|
|
211
|
+
min-width: 0;
|
|
212
|
+
}
|
|
96
213
|
|
|
214
|
+
/* ── Topbar ── */
|
|
97
215
|
#topbar {
|
|
98
216
|
background: var(--surface);
|
|
99
217
|
border-bottom: 1px solid var(--border);
|
|
100
|
-
padding: 0 24px;
|
|
101
|
-
|
|
102
|
-
|
|
218
|
+
padding: 0 24px;
|
|
219
|
+
height: 54px;
|
|
220
|
+
display: flex;
|
|
221
|
+
align-items: center;
|
|
222
|
+
gap: 12px;
|
|
223
|
+
flex-shrink: 0;
|
|
224
|
+
box-shadow: var(--shadow-sm);
|
|
225
|
+
}
|
|
226
|
+
.topbar-title {
|
|
227
|
+
font-size: 15px;
|
|
228
|
+
font-weight: 600;
|
|
229
|
+
flex: 1;
|
|
230
|
+
color: var(--text);
|
|
231
|
+
display: flex;
|
|
232
|
+
align-items: center;
|
|
233
|
+
gap: 8px;
|
|
103
234
|
}
|
|
104
|
-
.topbar-title { font-size: 15px; font-weight: 600; flex: 1; }
|
|
105
235
|
.topbar-actions { display: flex; gap: 8px; align-items: center; }
|
|
106
236
|
|
|
107
|
-
|
|
237
|
+
/* ── Content ── */
|
|
238
|
+
#content {
|
|
239
|
+
flex: 1;
|
|
240
|
+
overflow-y: auto;
|
|
241
|
+
padding: 24px;
|
|
242
|
+
}
|
|
108
243
|
|
|
109
|
-
/*
|
|
244
|
+
/* ════════════════════════════════════════
|
|
245
|
+
BREADCRUMB
|
|
246
|
+
════════════════════════════════════════ */
|
|
110
247
|
.breadcrumb {
|
|
111
|
-
display: flex;
|
|
112
|
-
|
|
113
|
-
|
|
248
|
+
display: flex;
|
|
249
|
+
align-items: center;
|
|
250
|
+
gap: 5px;
|
|
251
|
+
font-size: 12px;
|
|
252
|
+
color: var(--text-muted);
|
|
253
|
+
margin-bottom: 18px;
|
|
114
254
|
}
|
|
115
255
|
.breadcrumb a { color: var(--text-muted); text-decoration: none; }
|
|
116
|
-
.breadcrumb a:hover { color: var(--
|
|
256
|
+
.breadcrumb a:hover { color: var(--primary); }
|
|
117
257
|
.breadcrumb-sep { color: var(--border); }
|
|
258
|
+
.breadcrumb-current { color: var(--text-soft); font-weight: 500; }
|
|
118
259
|
|
|
119
|
-
/*
|
|
260
|
+
/* ════════════════════════════════════════
|
|
261
|
+
ALERTS
|
|
262
|
+
════════════════════════════════════════ */
|
|
120
263
|
.alert {
|
|
121
|
-
padding:
|
|
122
|
-
|
|
123
|
-
|
|
264
|
+
padding: 11px 16px;
|
|
265
|
+
border-radius: var(--radius-sm);
|
|
266
|
+
font-size: 13px;
|
|
267
|
+
margin-bottom: 18px;
|
|
268
|
+
display: flex;
|
|
269
|
+
align-items: center;
|
|
270
|
+
gap: 9px;
|
|
271
|
+
border: 1px solid transparent;
|
|
124
272
|
}
|
|
125
|
-
.alert-success { background:
|
|
126
|
-
.alert-error { background:
|
|
127
|
-
.alert-warning { background:
|
|
128
|
-
.alert-info { background:
|
|
273
|
+
.alert-success { background: var(--success-bg); color: var(--success); border-color: var(--success-border); }
|
|
274
|
+
.alert-error { background: var(--danger-bg); color: var(--danger); border-color: var(--danger-border); }
|
|
275
|
+
.alert-warning { background: var(--warning-bg); color: var(--warning); border-color: var(--warning-border); }
|
|
276
|
+
.alert-info { background: var(--info-bg); color: var(--info); border-color: var(--info-border); }
|
|
277
|
+
.alert-close {
|
|
278
|
+
margin-left: auto;
|
|
279
|
+
background: none;
|
|
280
|
+
border: none;
|
|
281
|
+
cursor: pointer;
|
|
282
|
+
color: inherit;
|
|
283
|
+
opacity: .6;
|
|
284
|
+
padding: 0;
|
|
285
|
+
line-height: 1;
|
|
286
|
+
font-size: 16px;
|
|
287
|
+
}
|
|
288
|
+
.alert-close:hover { opacity: 1; }
|
|
129
289
|
|
|
130
|
-
/*
|
|
290
|
+
/* ════════════════════════════════════════
|
|
291
|
+
CARDS
|
|
292
|
+
════════════════════════════════════════ */
|
|
131
293
|
.card {
|
|
132
294
|
background: var(--surface);
|
|
133
295
|
border: 1px solid var(--border);
|
|
134
|
-
border-radius: var(--radius);
|
|
296
|
+
border-radius: var(--radius-lg);
|
|
135
297
|
overflow: hidden;
|
|
298
|
+
box-shadow: var(--shadow-sm);
|
|
136
299
|
}
|
|
137
300
|
.card-header {
|
|
138
|
-
padding:
|
|
139
|
-
border-bottom: 1px solid var(--border);
|
|
140
|
-
display: flex;
|
|
141
|
-
|
|
301
|
+
padding: 14px 20px;
|
|
302
|
+
border-bottom: 1px solid var(--border-soft);
|
|
303
|
+
display: flex;
|
|
304
|
+
align-items: center;
|
|
305
|
+
justify-content: space-between;
|
|
306
|
+
gap: 12px;
|
|
307
|
+
flex-wrap: wrap;
|
|
308
|
+
background: var(--surface);
|
|
309
|
+
}
|
|
310
|
+
.card-title {
|
|
311
|
+
font-size: 13.5px;
|
|
312
|
+
font-weight: 600;
|
|
313
|
+
color: var(--text);
|
|
314
|
+
display: flex;
|
|
315
|
+
align-items: center;
|
|
316
|
+
gap: 7px;
|
|
142
317
|
}
|
|
143
|
-
.card-
|
|
144
|
-
.card-body { padding: 20px; }
|
|
318
|
+
.card-body { padding: 20px; }
|
|
145
319
|
|
|
146
|
-
/*
|
|
320
|
+
/* ════════════════════════════════════════
|
|
321
|
+
STAT CARDS
|
|
322
|
+
════════════════════════════════════════ */
|
|
147
323
|
.stats-grid {
|
|
148
324
|
display: grid;
|
|
149
|
-
grid-template-columns: repeat(auto-fill, minmax(
|
|
150
|
-
gap:
|
|
325
|
+
grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
|
|
326
|
+
gap: 14px;
|
|
327
|
+
margin-bottom: 22px;
|
|
151
328
|
}
|
|
152
329
|
.stat-card {
|
|
153
330
|
background: var(--surface);
|
|
154
331
|
border: 1px solid var(--border);
|
|
155
|
-
border-radius: var(--radius);
|
|
156
|
-
padding: 20px;
|
|
157
|
-
display: flex;
|
|
332
|
+
border-radius: var(--radius-lg);
|
|
333
|
+
padding: 18px 20px;
|
|
334
|
+
display: flex;
|
|
335
|
+
flex-direction: column;
|
|
336
|
+
gap: 6px;
|
|
337
|
+
transition: box-shadow .15s, border-color .15s;
|
|
338
|
+
text-decoration: none;
|
|
339
|
+
box-shadow: var(--shadow-sm);
|
|
158
340
|
}
|
|
159
|
-
.stat-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
341
|
+
.stat-card:hover {
|
|
342
|
+
box-shadow: var(--shadow);
|
|
343
|
+
border-color: var(--primary-dim);
|
|
344
|
+
}
|
|
345
|
+
.stat-icon-wrap {
|
|
346
|
+
width: 36px; height: 36px;
|
|
347
|
+
border-radius: 9px;
|
|
348
|
+
background: var(--primary-soft);
|
|
349
|
+
display: flex; align-items: center; justify-content: center;
|
|
350
|
+
color: var(--primary);
|
|
351
|
+
margin-bottom: 4px;
|
|
352
|
+
}
|
|
353
|
+
.stat-label {
|
|
354
|
+
font-size: 11.5px;
|
|
355
|
+
color: var(--text-muted);
|
|
356
|
+
font-weight: 500;
|
|
357
|
+
text-transform: uppercase;
|
|
358
|
+
letter-spacing: 0.4px;
|
|
359
|
+
}
|
|
360
|
+
.stat-value {
|
|
361
|
+
font-size: 26px;
|
|
362
|
+
font-weight: 700;
|
|
363
|
+
color: var(--text);
|
|
364
|
+
line-height: 1;
|
|
365
|
+
letter-spacing: -0.5px;
|
|
366
|
+
}
|
|
367
|
+
.stat-sub { font-size: 12px; color: var(--text-muted); }
|
|
163
368
|
|
|
164
|
-
/*
|
|
369
|
+
/* ════════════════════════════════════════
|
|
370
|
+
TABLE
|
|
371
|
+
════════════════════════════════════════ */
|
|
165
372
|
.table-wrap { overflow-x: auto; }
|
|
166
373
|
table { width: 100%; border-collapse: collapse; }
|
|
167
374
|
th {
|
|
168
|
-
text-align: left;
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
375
|
+
text-align: left;
|
|
376
|
+
padding: 9px 16px;
|
|
377
|
+
font-size: 11px;
|
|
378
|
+
font-weight: 600;
|
|
379
|
+
text-transform: uppercase;
|
|
380
|
+
letter-spacing: 0.5px;
|
|
381
|
+
color: var(--text-muted);
|
|
382
|
+
background: var(--surface2);
|
|
172
383
|
border-bottom: 1px solid var(--border);
|
|
173
384
|
white-space: nowrap;
|
|
174
385
|
}
|
|
175
386
|
th.sortable { cursor: pointer; user-select: none; }
|
|
176
|
-
th.sortable:hover { color: var(--text); }
|
|
177
|
-
th.sort-
|
|
178
|
-
|
|
387
|
+
th.sortable:hover { color: var(--text-soft); }
|
|
388
|
+
th.sort-active { color: var(--primary); }
|
|
389
|
+
.sort-indicator { display: inline-flex; flex-direction: column; gap: 1px; margin-left: 4px; vertical-align: middle; }
|
|
390
|
+
.sort-indicator span { width: 0; height: 0; border-left: 4px solid transparent; border-right: 4px solid transparent; opacity: .3; }
|
|
391
|
+
.sort-indicator .up { border-bottom: 5px solid currentColor; }
|
|
392
|
+
.sort-indicator .down { border-top: 5px solid currentColor; }
|
|
393
|
+
th.sort-asc .sort-indicator .up { opacity: 1; }
|
|
394
|
+
th.sort-desc .sort-indicator .down { opacity: 1; }
|
|
179
395
|
td {
|
|
180
|
-
padding:
|
|
181
|
-
|
|
396
|
+
padding: 11px 16px;
|
|
397
|
+
font-size: 13px;
|
|
398
|
+
border-bottom: 1px solid var(--border-soft);
|
|
182
399
|
vertical-align: middle;
|
|
400
|
+
color: var(--text-soft);
|
|
183
401
|
}
|
|
184
402
|
tr:last-child td { border-bottom: none; }
|
|
185
403
|
tr:hover td { background: var(--surface2); }
|
|
186
|
-
.col-
|
|
404
|
+
.col-check { width: 44px; }
|
|
405
|
+
.col-actions { width: 100px; text-align: right; }
|
|
406
|
+
.td-primary { color: var(--text); font-weight: 500; }
|
|
407
|
+
|
|
408
|
+
/* ── Row checkbox ── */
|
|
409
|
+
.row-check {
|
|
410
|
+
width: 16px; height: 16px;
|
|
411
|
+
cursor: pointer;
|
|
412
|
+
accent-color: var(--primary);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/* ── Action menu ── */
|
|
416
|
+
.action-menu { position: relative; display: inline-block; }
|
|
417
|
+
.action-menu-btn {
|
|
418
|
+
background: none;
|
|
419
|
+
border: 1px solid var(--border);
|
|
420
|
+
border-radius: var(--radius-sm);
|
|
421
|
+
padding: 4px 8px;
|
|
422
|
+
cursor: pointer;
|
|
423
|
+
color: var(--text-muted);
|
|
424
|
+
display: flex;
|
|
425
|
+
align-items: center;
|
|
426
|
+
gap: 3px;
|
|
427
|
+
font-size: 12px;
|
|
428
|
+
transition: all .12s;
|
|
429
|
+
}
|
|
430
|
+
.action-menu-btn:hover { background: var(--surface2); color: var(--text-soft); border-color: var(--border); }
|
|
431
|
+
.action-dropdown {
|
|
432
|
+
position: absolute;
|
|
433
|
+
right: 0;
|
|
434
|
+
top: calc(100% + 4px);
|
|
435
|
+
background: var(--surface);
|
|
436
|
+
border: 1px solid var(--border);
|
|
437
|
+
border-radius: var(--radius);
|
|
438
|
+
box-shadow: var(--shadow-lg);
|
|
439
|
+
min-width: 140px;
|
|
440
|
+
z-index: 50;
|
|
441
|
+
overflow: hidden;
|
|
442
|
+
display: none;
|
|
443
|
+
}
|
|
444
|
+
.action-dropdown.open { display: block; }
|
|
445
|
+
.action-dropdown a,
|
|
446
|
+
.action-dropdown button {
|
|
447
|
+
display: flex;
|
|
448
|
+
align-items: center;
|
|
449
|
+
gap: 8px;
|
|
450
|
+
padding: 8px 14px;
|
|
451
|
+
font-size: 13px;
|
|
452
|
+
color: var(--text-soft);
|
|
453
|
+
text-decoration: none;
|
|
454
|
+
background: none;
|
|
455
|
+
border: none;
|
|
456
|
+
width: 100%;
|
|
457
|
+
text-align: left;
|
|
458
|
+
cursor: pointer;
|
|
459
|
+
font-family: inherit;
|
|
460
|
+
transition: background .1s;
|
|
461
|
+
}
|
|
462
|
+
.action-dropdown a:hover,
|
|
463
|
+
.action-dropdown button:hover { background: var(--surface2); }
|
|
464
|
+
.action-dropdown .sep { height: 1px; background: var(--border-soft); margin: 3px 0; }
|
|
465
|
+
.action-dropdown .danger { color: var(--danger); }
|
|
187
466
|
|
|
188
|
-
/*
|
|
467
|
+
/* ════════════════════════════════════════
|
|
468
|
+
BADGES
|
|
469
|
+
════════════════════════════════════════ */
|
|
189
470
|
.badge {
|
|
190
|
-
display: inline-flex;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
471
|
+
display: inline-flex;
|
|
472
|
+
align-items: center;
|
|
473
|
+
gap: 4px;
|
|
474
|
+
padding: 2px 8px;
|
|
475
|
+
border-radius: 99px;
|
|
476
|
+
font-size: 11.5px;
|
|
477
|
+
font-weight: 600;
|
|
478
|
+
white-space: nowrap;
|
|
479
|
+
border: 1px solid transparent;
|
|
480
|
+
}
|
|
481
|
+
.badge-blue { background: #eff6ff; color: #1d4ed8; border-color: #bfdbfe; }
|
|
482
|
+
.badge-red { background: #fef2f2; color: #b91c1c; border-color: #fecaca; }
|
|
483
|
+
.badge-green { background: #f0fdf4; color: #15803d; border-color: #bbf7d0; }
|
|
484
|
+
.badge-yellow { background: #fffbeb; color: #b45309; border-color: #fde68a; }
|
|
485
|
+
.badge-purple { background: #faf5ff; color: #7e22ce; border-color: #e9d5ff; }
|
|
486
|
+
.badge-gray { background: #f9fafb; color: #6b7280; border-color: #e5e7eb; }
|
|
487
|
+
.badge-orange { background: #fff7ed; color: #c2410c; border-color: #fed7aa; }
|
|
488
|
+
|
|
489
|
+
/* ════════════════════════════════════════
|
|
490
|
+
BUTTONS
|
|
491
|
+
════════════════════════════════════════ */
|
|
202
492
|
.btn {
|
|
203
|
-
display: inline-flex;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
493
|
+
display: inline-flex;
|
|
494
|
+
align-items: center;
|
|
495
|
+
gap: 6px;
|
|
496
|
+
padding: 7px 14px;
|
|
497
|
+
border-radius: var(--radius-sm);
|
|
498
|
+
font-size: 13px;
|
|
499
|
+
font-weight: 500;
|
|
500
|
+
cursor: pointer;
|
|
501
|
+
border: 1px solid transparent;
|
|
502
|
+
transition: all .12s;
|
|
503
|
+
text-decoration: none;
|
|
504
|
+
font-family: inherit;
|
|
505
|
+
line-height: 1;
|
|
506
|
+
white-space: nowrap;
|
|
507
|
+
}
|
|
508
|
+
.btn-primary {
|
|
509
|
+
background: var(--primary);
|
|
510
|
+
color: #fff;
|
|
511
|
+
border-color: var(--primary);
|
|
512
|
+
box-shadow: 0 1px 3px rgba(37,99,235,.3);
|
|
513
|
+
}
|
|
514
|
+
.btn-primary:hover { background: var(--primary-h); border-color: var(--primary-h); }
|
|
515
|
+
.btn-ghost {
|
|
516
|
+
background: transparent;
|
|
517
|
+
color: var(--text-soft);
|
|
518
|
+
border-color: var(--border);
|
|
519
|
+
}
|
|
212
520
|
.btn-ghost:hover { background: var(--surface2); color: var(--text); }
|
|
213
|
-
.btn-danger {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
.
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
521
|
+
.btn-danger {
|
|
522
|
+
background: transparent;
|
|
523
|
+
color: var(--danger);
|
|
524
|
+
border-color: var(--border);
|
|
525
|
+
}
|
|
526
|
+
.btn-danger:hover { background: var(--danger-bg); border-color: var(--danger-border); }
|
|
527
|
+
.btn-success {
|
|
528
|
+
background: var(--success);
|
|
529
|
+
color: #fff;
|
|
530
|
+
border-color: var(--success);
|
|
531
|
+
}
|
|
532
|
+
.btn-success:hover { opacity: .9; }
|
|
533
|
+
.btn-sm { padding: 5px 10px; font-size: 12px; }
|
|
534
|
+
.btn-xs { padding: 3px 8px; font-size: 11px; }
|
|
535
|
+
.btn-icon { padding: 6px; }
|
|
536
|
+
.btn:disabled { opacity: .5; cursor: not-allowed; pointer-events: none; }
|
|
537
|
+
|
|
538
|
+
/* ════════════════════════════════════════
|
|
539
|
+
FORMS
|
|
540
|
+
════════════════════════════════════════ */
|
|
541
|
+
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
|
|
542
|
+
.form-group { display: flex; flex-direction: column; gap: 5px; }
|
|
543
|
+
.form-group.full { grid-column: 1 / -1; }
|
|
544
|
+
.form-group.w-third { grid-column: span 1; }
|
|
545
|
+
.form-label { font-size: 12.5px; font-weight: 500; color: var(--text-soft); }
|
|
223
546
|
.form-label .required { color: var(--danger); margin-left: 2px; }
|
|
224
547
|
.form-control {
|
|
225
|
-
background: var(--
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
548
|
+
background: var(--surface);
|
|
549
|
+
border: 1px solid var(--border);
|
|
550
|
+
color: var(--text);
|
|
551
|
+
border-radius: var(--radius-sm);
|
|
552
|
+
padding: 8px 11px;
|
|
553
|
+
font-size: 13.5px;
|
|
554
|
+
width: 100%;
|
|
555
|
+
outline: none;
|
|
556
|
+
font-family: inherit;
|
|
557
|
+
transition: border .12s, box-shadow .12s;
|
|
558
|
+
}
|
|
559
|
+
.form-control:hover { border-color: #c4c9d4; }
|
|
560
|
+
.form-control:focus {
|
|
561
|
+
border-color: var(--primary);
|
|
562
|
+
box-shadow: 0 0 0 3px var(--primary-soft);
|
|
229
563
|
}
|
|
230
|
-
.form-control
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
564
|
+
.form-control.error {
|
|
565
|
+
border-color: var(--danger);
|
|
566
|
+
box-shadow: 0 0 0 3px var(--danger-bg);
|
|
567
|
+
}
|
|
568
|
+
.form-control::placeholder { color: var(--text-xmuted); }
|
|
569
|
+
select.form-control { cursor: pointer; }
|
|
570
|
+
textarea.form-control { resize: vertical; min-height: 90px; }
|
|
571
|
+
.form-help { font-size: 11.5px; color: var(--text-muted); }
|
|
572
|
+
.form-error { font-size: 11.5px; color: var(--danger); display: flex; align-items: center; gap: 4px; }
|
|
234
573
|
|
|
235
|
-
/* ──
|
|
574
|
+
/* ── Checkbox / radio ── */
|
|
575
|
+
.check-group { display: flex; align-items: center; gap: 8px; }
|
|
576
|
+
.check-input {
|
|
577
|
+
width: 16px; height: 16px;
|
|
578
|
+
accent-color: var(--primary);
|
|
579
|
+
cursor: pointer;
|
|
580
|
+
}
|
|
581
|
+
.check-label { font-size: 13.5px; color: var(--text-soft); cursor: pointer; }
|
|
582
|
+
|
|
583
|
+
/* ── Search ── */
|
|
236
584
|
.search-wrap { position: relative; }
|
|
237
|
-
.search-
|
|
238
|
-
position: absolute;
|
|
239
|
-
|
|
240
|
-
|
|
585
|
+
.search-icon-inner {
|
|
586
|
+
position: absolute;
|
|
587
|
+
left: 10px;
|
|
588
|
+
top: 50%;
|
|
589
|
+
transform: translateY(-50%);
|
|
590
|
+
color: var(--text-muted);
|
|
591
|
+
pointer-events: none;
|
|
592
|
+
font-size: 14px;
|
|
593
|
+
display: flex;
|
|
594
|
+
}
|
|
595
|
+
.search-input { padding-left: 34px !important; width: 220px; }
|
|
596
|
+
|
|
597
|
+
/* ════════════════════════════════════════
|
|
598
|
+
TOOLBAR (list page)
|
|
599
|
+
════════════════════════════════════════ */
|
|
600
|
+
.toolbar {
|
|
601
|
+
display: flex;
|
|
602
|
+
align-items: center;
|
|
603
|
+
gap: 8px;
|
|
604
|
+
padding: 12px 18px;
|
|
605
|
+
border-bottom: 1px solid var(--border-soft);
|
|
606
|
+
flex-wrap: wrap;
|
|
607
|
+
}
|
|
608
|
+
.toolbar-left { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0; }
|
|
609
|
+
.toolbar-right { display: flex; align-items: center; gap: 8px; }
|
|
610
|
+
|
|
611
|
+
/* ── Bulk action bar ── */
|
|
612
|
+
.bulk-bar {
|
|
613
|
+
display: none;
|
|
614
|
+
align-items: center;
|
|
615
|
+
gap: 10px;
|
|
616
|
+
padding: 10px 18px;
|
|
617
|
+
background: var(--primary-soft);
|
|
618
|
+
border-bottom: 1px solid var(--primary-dim);
|
|
619
|
+
font-size: 13px;
|
|
241
620
|
}
|
|
242
|
-
.
|
|
621
|
+
.bulk-bar.visible { display: flex; }
|
|
622
|
+
.bulk-count { font-weight: 600; color: var(--primary); }
|
|
243
623
|
|
|
244
|
-
/*
|
|
624
|
+
/* ════════════════════════════════════════
|
|
625
|
+
FILTER PANEL
|
|
626
|
+
════════════════════════════════════════ */
|
|
627
|
+
.filter-row {
|
|
628
|
+
display: flex;
|
|
629
|
+
align-items: flex-end;
|
|
630
|
+
gap: 10px;
|
|
631
|
+
padding: 12px 18px;
|
|
632
|
+
border-bottom: 1px solid var(--border-soft);
|
|
633
|
+
flex-wrap: wrap;
|
|
634
|
+
background: var(--surface2);
|
|
635
|
+
}
|
|
636
|
+
.filter-group { display: flex; flex-direction: column; gap: 4px; }
|
|
637
|
+
.filter-label { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .4px; color: var(--text-muted); }
|
|
638
|
+
.filter-control {
|
|
639
|
+
background: var(--surface);
|
|
640
|
+
border: 1px solid var(--border);
|
|
641
|
+
color: var(--text);
|
|
642
|
+
border-radius: var(--radius-sm);
|
|
643
|
+
padding: 6px 10px;
|
|
644
|
+
font-size: 12.5px;
|
|
645
|
+
outline: none;
|
|
646
|
+
font-family: inherit;
|
|
647
|
+
transition: border .12s;
|
|
648
|
+
}
|
|
649
|
+
.filter-control:focus { border-color: var(--primary); }
|
|
650
|
+
|
|
651
|
+
/* ════════════════════════════════════════
|
|
652
|
+
PAGINATION
|
|
653
|
+
════════════════════════════════════════ */
|
|
245
654
|
.pagination {
|
|
246
|
-
display: flex;
|
|
247
|
-
|
|
655
|
+
display: flex;
|
|
656
|
+
align-items: center;
|
|
657
|
+
gap: 3px;
|
|
658
|
+
padding: 12px 18px;
|
|
659
|
+
border-top: 1px solid var(--border-soft);
|
|
660
|
+
flex-wrap: wrap;
|
|
248
661
|
}
|
|
249
662
|
.page-info { font-size: 12px; color: var(--text-muted); margin-left: auto; }
|
|
250
663
|
.page-btn {
|
|
251
|
-
width: 30px;
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
664
|
+
min-width: 30px;
|
|
665
|
+
height: 30px;
|
|
666
|
+
padding: 0 6px;
|
|
667
|
+
display: inline-flex;
|
|
668
|
+
align-items: center;
|
|
669
|
+
justify-content: center;
|
|
670
|
+
border-radius: var(--radius-sm);
|
|
671
|
+
border: 1px solid var(--border);
|
|
672
|
+
background: transparent;
|
|
673
|
+
color: var(--text-soft);
|
|
674
|
+
cursor: pointer;
|
|
675
|
+
font-size: 12px;
|
|
676
|
+
font-family: inherit;
|
|
677
|
+
transition: all .12s;
|
|
678
|
+
text-decoration: none;
|
|
257
679
|
}
|
|
258
|
-
.page-btn:hover:not(:disabled) { background: var(--surface2); color: var(--text); }
|
|
680
|
+
.page-btn:hover:not(:disabled) { background: var(--surface2); color: var(--text); border-color: #c4c9d4; }
|
|
259
681
|
.page-btn.active { background: var(--primary); border-color: var(--primary); color: #fff; }
|
|
260
|
-
.page-btn:disabled { opacity:
|
|
682
|
+
.page-btn:disabled { opacity: .35; cursor: not-allowed; }
|
|
683
|
+
.page-ellipsis { color: var(--text-muted); font-size: 12px; padding: 0 3px; }
|
|
261
684
|
|
|
262
|
-
/*
|
|
685
|
+
/* ════════════════════════════════════════
|
|
686
|
+
MODAL
|
|
687
|
+
════════════════════════════════════════ */
|
|
263
688
|
.modal-overlay {
|
|
264
|
-
position: fixed;
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
689
|
+
position: fixed;
|
|
690
|
+
inset: 0;
|
|
691
|
+
background: rgba(17,24,39,.5);
|
|
692
|
+
display: flex;
|
|
693
|
+
align-items: center;
|
|
694
|
+
justify-content: center;
|
|
695
|
+
z-index: 200;
|
|
696
|
+
padding: 24px;
|
|
697
|
+
opacity: 0;
|
|
698
|
+
pointer-events: none;
|
|
699
|
+
transition: opacity .18s;
|
|
700
|
+
backdrop-filter: blur(2px);
|
|
269
701
|
}
|
|
270
702
|
.modal-overlay.open { opacity: 1; pointer-events: all; }
|
|
271
703
|
.modal {
|
|
272
704
|
background: var(--surface);
|
|
273
705
|
border: 1px solid var(--border);
|
|
274
|
-
border-radius:
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
706
|
+
border-radius: var(--radius-lg);
|
|
707
|
+
width: 100%;
|
|
708
|
+
max-width: 520px;
|
|
709
|
+
max-height: 90vh;
|
|
710
|
+
overflow-y: auto;
|
|
711
|
+
transform: translateY(10px) scale(.99);
|
|
712
|
+
transition: transform .2s;
|
|
713
|
+
box-shadow: var(--shadow-lg);
|
|
278
714
|
}
|
|
279
|
-
.modal-overlay.open .modal { transform: translateY(0); }
|
|
715
|
+
.modal-overlay.open .modal { transform: translateY(0) scale(1); }
|
|
716
|
+
.modal-sm { max-width: 400px; }
|
|
717
|
+
.modal-lg { max-width: 700px; }
|
|
280
718
|
.modal-header {
|
|
281
|
-
padding:
|
|
282
|
-
|
|
283
|
-
|
|
719
|
+
padding: 18px 22px;
|
|
720
|
+
border-bottom: 1px solid var(--border-soft);
|
|
721
|
+
display: flex;
|
|
722
|
+
justify-content: space-between;
|
|
723
|
+
align-items: center;
|
|
724
|
+
position: sticky;
|
|
725
|
+
top: 0;
|
|
726
|
+
background: var(--surface);
|
|
727
|
+
z-index: 1;
|
|
284
728
|
}
|
|
285
|
-
.modal-title { font-size: 15px; font-weight: 600; }
|
|
286
|
-
.modal-body { padding: 20px
|
|
729
|
+
.modal-title { font-size: 15px; font-weight: 600; color: var(--text); }
|
|
730
|
+
.modal-body { padding: 20px 22px; }
|
|
287
731
|
.modal-footer {
|
|
288
|
-
padding:
|
|
289
|
-
|
|
290
|
-
|
|
732
|
+
padding: 14px 22px;
|
|
733
|
+
border-top: 1px solid var(--border-soft);
|
|
734
|
+
display: flex;
|
|
735
|
+
justify-content: flex-end;
|
|
736
|
+
gap: 8px;
|
|
737
|
+
position: sticky;
|
|
738
|
+
bottom: 0;
|
|
739
|
+
background: var(--surface);
|
|
291
740
|
}
|
|
292
741
|
.close-btn {
|
|
293
|
-
background: none;
|
|
294
|
-
|
|
742
|
+
background: none;
|
|
743
|
+
border: none;
|
|
744
|
+
color: var(--text-muted);
|
|
745
|
+
cursor: pointer;
|
|
746
|
+
padding: 4px;
|
|
747
|
+
line-height: 1;
|
|
748
|
+
border-radius: 4px;
|
|
749
|
+
display: flex;
|
|
750
|
+
align-items: center;
|
|
751
|
+
transition: background .1s, color .1s;
|
|
295
752
|
}
|
|
296
|
-
.close-btn:hover { color: var(--text); }
|
|
753
|
+
.close-btn:hover { background: var(--surface3); color: var(--text); }
|
|
297
754
|
|
|
298
|
-
/*
|
|
755
|
+
/* ════════════════════════════════════════
|
|
756
|
+
EMPTY STATE
|
|
757
|
+
════════════════════════════════════════ */
|
|
299
758
|
.empty-state {
|
|
300
|
-
text-align: center;
|
|
759
|
+
text-align: center;
|
|
760
|
+
padding: 56px 24px;
|
|
301
761
|
color: var(--text-muted);
|
|
302
762
|
}
|
|
303
|
-
.empty-icon
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
763
|
+
.empty-icon {
|
|
764
|
+
width: 52px;
|
|
765
|
+
height: 52px;
|
|
766
|
+
background: var(--surface3);
|
|
767
|
+
border-radius: 14px;
|
|
768
|
+
display: flex;
|
|
769
|
+
align-items: center;
|
|
770
|
+
justify-content: center;
|
|
771
|
+
margin: 0 auto 16px;
|
|
772
|
+
color: var(--text-xmuted);
|
|
773
|
+
}
|
|
774
|
+
.empty-title { font-size: 15px; font-weight: 600; color: var(--text-soft); margin-bottom: 7px; }
|
|
775
|
+
.empty-desc { font-size: 13px; max-width: 320px; margin: 0 auto 20px; line-height: 1.6; }
|
|
311
776
|
|
|
312
|
-
/*
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
.
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
.
|
|
321
|
-
|
|
322
|
-
|
|
777
|
+
/* ════════════════════════════════════════
|
|
778
|
+
CELL TYPES
|
|
779
|
+
════════════════════════════════════════ */
|
|
780
|
+
.bool-yes {
|
|
781
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
782
|
+
width: 20px; height: 20px; border-radius: 99px;
|
|
783
|
+
background: var(--success-bg); color: var(--success);
|
|
784
|
+
}
|
|
785
|
+
.bool-no {
|
|
786
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
787
|
+
width: 20px; height: 20px; border-radius: 99px;
|
|
788
|
+
background: var(--danger-bg); color: var(--danger);
|
|
789
|
+
}
|
|
790
|
+
.cell-muted { color: var(--text-xmuted); font-style: italic; }
|
|
791
|
+
.cell-mono { font-family: 'DM Mono', monospace; font-size: 12px; }
|
|
792
|
+
.cell-image { width: 34px; height: 34px; border-radius: 7px; object-fit: cover; border: 1px solid var(--border); }
|
|
323
793
|
|
|
324
|
-
/*
|
|
794
|
+
/* ════════════════════════════════════════
|
|
795
|
+
TOAST
|
|
796
|
+
════════════════════════════════════════ */
|
|
325
797
|
#toast-root {
|
|
326
|
-
position: fixed;
|
|
327
|
-
|
|
328
|
-
|
|
798
|
+
position: fixed;
|
|
799
|
+
bottom: 22px;
|
|
800
|
+
right: 22px;
|
|
801
|
+
display: flex;
|
|
802
|
+
flex-direction: column;
|
|
803
|
+
gap: 8px;
|
|
804
|
+
z-index: 500;
|
|
805
|
+
pointer-events: none;
|
|
329
806
|
}
|
|
330
807
|
.toast {
|
|
331
|
-
background: var(--
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
808
|
+
background: var(--text);
|
|
809
|
+
color: #fff;
|
|
810
|
+
border-radius: var(--radius);
|
|
811
|
+
padding: 11px 16px;
|
|
812
|
+
font-size: 13px;
|
|
813
|
+
max-width: 320px;
|
|
814
|
+
box-shadow: var(--shadow-lg);
|
|
815
|
+
pointer-events: all;
|
|
816
|
+
display: flex;
|
|
817
|
+
align-items: center;
|
|
818
|
+
gap: 9px;
|
|
819
|
+
animation: toastIn .2s ease;
|
|
820
|
+
}
|
|
821
|
+
.toast-success .toast-dot { color: #4ade80; }
|
|
822
|
+
.toast-error .toast-dot { color: #f87171; }
|
|
823
|
+
@keyframes toastIn { from { transform: translateX(16px); opacity: 0; } }
|
|
824
|
+
|
|
825
|
+
/* ════════════════════════════════════════
|
|
826
|
+
UTILITIES
|
|
827
|
+
════════════════════════════════════════ */
|
|
828
|
+
.flex { display: flex; }
|
|
829
|
+
.flex-col { flex-direction: column; }
|
|
830
|
+
.items-center { align-items: center; }
|
|
831
|
+
.justify-between { justify-content: space-between; }
|
|
832
|
+
.gap-1 { gap: 4px; }
|
|
833
|
+
.gap-2 { gap: 8px; }
|
|
834
|
+
.gap-3 { gap: 12px; }
|
|
835
|
+
.gap-4 { gap: 16px; }
|
|
836
|
+
.ml-auto { margin-left: auto; }
|
|
837
|
+
.text-muted { color: var(--text-muted); }
|
|
838
|
+
.text-sm { font-size: 12px; }
|
|
839
|
+
.text-xs { font-size: 11px; }
|
|
840
|
+
.fw-500 { font-weight: 500; }
|
|
841
|
+
.fw-600 { font-weight: 600; }
|
|
842
|
+
.mb-3 { margin-bottom: 12px; }
|
|
843
|
+
.mb-4 { margin-bottom: 16px; }
|
|
844
|
+
.mb-5 { margin-bottom: 20px; }
|
|
845
|
+
.mb-6 { margin-bottom: 24px; }
|
|
846
|
+
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
847
|
+
|
|
848
|
+
/* ════════════════════════════════════════
|
|
849
|
+
RESPONSIVE
|
|
850
|
+
════════════════════════════════════════ */
|
|
343
851
|
@media (max-width: 768px) {
|
|
344
852
|
#sidebar { display: none; }
|
|
345
853
|
.form-grid { grid-template-columns: 1fr; }
|
|
854
|
+
.search-input { width: 160px; }
|
|
346
855
|
}
|
|
347
856
|
</style>
|
|
348
857
|
{% block head %}{% endblock %}
|
|
349
858
|
</head>
|
|
350
859
|
<body>
|
|
351
860
|
|
|
352
|
-
{#
|
|
861
|
+
{# ══════════════════════════════════════
|
|
862
|
+
SVG ICON DEFINITIONS (hidden sprite)
|
|
863
|
+
══════════════════════════════════════ #}
|
|
864
|
+
<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
|
|
865
|
+
<symbol id="ic-home" viewBox="0 0 24 24"><path d="M3 9.5L12 3l9 6.5V20a1 1 0 01-1 1H5a1 1 0 01-1-1V9.5z"/><path d="M9 21V12h6v9"/></symbol>
|
|
866
|
+
<symbol id="ic-table" viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M9 3v18"/></symbol>
|
|
867
|
+
<symbol id="ic-users" viewBox="0 0 24 24"><path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75"/></symbol>
|
|
868
|
+
<symbol id="ic-file" viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><path d="M14 2v6h6M16 13H8M16 17H8M10 9H8"/></symbol>
|
|
869
|
+
<symbol id="ic-tag" viewBox="0 0 24 24"><path d="M20.59 13.41l-7.17 7.17a2 2 0 01-2.83 0L2 12V2h10l8.59 8.59a2 2 0 010 2.82z"/><circle cx="7" cy="7" r="1.5" fill="currentColor" stroke="none"/></symbol>
|
|
870
|
+
<symbol id="ic-settings" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/></symbol>
|
|
871
|
+
<symbol id="ic-plus" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></symbol>
|
|
872
|
+
<symbol id="ic-edit" viewBox="0 0 24 24"><path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z"/></symbol>
|
|
873
|
+
<symbol id="ic-trash" viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6M10 11v6M14 11v6"/><path d="M9 6V4a1 1 0 011-1h4a1 1 0 011 1v2"/></symbol>
|
|
874
|
+
<symbol id="ic-search" viewBox="0 0 24 24"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></symbol>
|
|
875
|
+
<symbol id="ic-filter" viewBox="0 0 24 24"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/></symbol>
|
|
876
|
+
<symbol id="ic-chevron-left" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></symbol>
|
|
877
|
+
<symbol id="ic-chevron-right" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></symbol>
|
|
878
|
+
<symbol id="ic-chevron-down" viewBox="0 0 24 24"><polyline points="6 9 12 15 18 9"/></symbol>
|
|
879
|
+
<symbol id="ic-x" viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></symbol>
|
|
880
|
+
<symbol id="ic-check" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></symbol>
|
|
881
|
+
<symbol id="ic-alert-circle" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></symbol>
|
|
882
|
+
<symbol id="ic-info" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></symbol>
|
|
883
|
+
<symbol id="ic-eye" viewBox="0 0 24 24"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></symbol>
|
|
884
|
+
<symbol id="ic-download" viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></symbol>
|
|
885
|
+
<symbol id="ic-upload" viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></symbol>
|
|
886
|
+
<symbol id="ic-refresh" viewBox="0 0 24 24"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0114.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0020.49 15"/></symbol>
|
|
887
|
+
<symbol id="ic-more-vertical" viewBox="0 0 24 24"><circle cx="12" cy="5" r="1" fill="currentColor" stroke="none"/><circle cx="12" cy="12" r="1" fill="currentColor" stroke="none"/><circle cx="12" cy="19" r="1" fill="currentColor" stroke="none"/></symbol>
|
|
888
|
+
<symbol id="ic-grid" viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></symbol>
|
|
889
|
+
<symbol id="ic-list" viewBox="0 0 24 24"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></symbol>
|
|
890
|
+
<symbol id="ic-save" viewBox="0 0 24 24"><path d="M19 21H5a2 2 0 01-2-2V5a2 2 0 012-2h11l5 5v11a2 2 0 01-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></symbol>
|
|
891
|
+
<symbol id="ic-arrow-left" viewBox="0 0 24 24"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></symbol>
|
|
892
|
+
<symbol id="ic-database" viewBox="0 0 24 24"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></symbol>
|
|
893
|
+
<symbol id="ic-activity" viewBox="0 0 24 24"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></symbol>
|
|
894
|
+
<symbol id="ic-log-out" viewBox="0 0 24 24"><path d="M9 21H5a2 2 0 01-2-2V5a2 2 0 012-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></symbol>
|
|
895
|
+
</svg>
|
|
896
|
+
|
|
897
|
+
{# ══════════════════════════════════════
|
|
898
|
+
SIDEBAR
|
|
899
|
+
══════════════════════════════════════ #}
|
|
353
900
|
<nav id="sidebar">
|
|
354
901
|
<div class="sidebar-brand">
|
|
355
|
-
<div class="brand-
|
|
902
|
+
<div class="brand-logo">
|
|
903
|
+
<span class="icon icon-18">{% include "icons/database.svg" ignore missing %}<svg viewBox="0 0 24 24"><use href="#ic-database"/></svg></span>
|
|
904
|
+
</div>
|
|
356
905
|
<div class="brand-text">
|
|
357
906
|
<div class="brand-name">{{ adminTitle }}</div>
|
|
358
907
|
<div class="brand-sub">Admin Panel</div>
|
|
@@ -362,7 +911,10 @@
|
|
|
362
911
|
<div class="nav-section">
|
|
363
912
|
<div class="nav-label">Overview</div>
|
|
364
913
|
<a href="{{ adminPrefix }}/" class="nav-item {% if activePage == 'dashboard' %}active{% endif %}">
|
|
365
|
-
<span class="nav-icon"
|
|
914
|
+
<span class="nav-icon icon icon-16">
|
|
915
|
+
<svg viewBox="0 0 24 24"><use href="#ic-grid"/></svg>
|
|
916
|
+
</span>
|
|
917
|
+
Dashboard
|
|
366
918
|
</a>
|
|
367
919
|
</div>
|
|
368
920
|
|
|
@@ -371,7 +923,9 @@
|
|
|
371
923
|
<div class="nav-label">Resources</div>
|
|
372
924
|
{% for resource in resources %}
|
|
373
925
|
<a href="{{ adminPrefix }}/{{ resource.slug }}" class="nav-item {% if activeResource == resource.slug %}active{% endif %}">
|
|
374
|
-
<span class="nav-icon
|
|
926
|
+
<span class="nav-icon icon icon-16">
|
|
927
|
+
<svg viewBox="0 0 24 24"><use href="#ic-table"/></svg>
|
|
928
|
+
</span>
|
|
375
929
|
{{ resource.label }}
|
|
376
930
|
</a>
|
|
377
931
|
{% endfor %}
|
|
@@ -379,11 +933,16 @@
|
|
|
379
933
|
{% endif %}
|
|
380
934
|
|
|
381
935
|
<div class="sidebar-footer">
|
|
382
|
-
<div class="sidebar-version
|
|
936
|
+
<div class="sidebar-version flex items-center gap-1">
|
|
937
|
+
<span class="icon icon-12" style="color:var(--text-xmuted)"><svg viewBox="0 0 24 24"><use href="#ic-activity"/></svg></span>
|
|
938
|
+
Millas v0.1.2
|
|
939
|
+
</div>
|
|
383
940
|
</div>
|
|
384
941
|
</nav>
|
|
385
942
|
|
|
386
|
-
{#
|
|
943
|
+
{# ══════════════════════════════════════
|
|
944
|
+
MAIN
|
|
945
|
+
══════════════════════════════════════ #}
|
|
387
946
|
<div id="main">
|
|
388
947
|
<header id="topbar">
|
|
389
948
|
<span class="topbar-title">{% block topbar_title %}{{ pageTitle }}{% endblock %}</span>
|
|
@@ -394,10 +953,22 @@
|
|
|
394
953
|
|
|
395
954
|
<div id="content">
|
|
396
955
|
{% if flash.success %}
|
|
397
|
-
<div class="alert alert-success"
|
|
956
|
+
<div class="alert alert-success" id="flash-alert">
|
|
957
|
+
<span class="icon icon-16"><svg viewBox="0 0 24 24"><use href="#ic-check"/></svg></span>
|
|
958
|
+
<span>{{ flash.success }}</span>
|
|
959
|
+
<button class="alert-close" onclick="this.closest('.alert').remove()">
|
|
960
|
+
<span class="icon icon-14"><svg viewBox="0 0 24 24"><use href="#ic-x"/></svg></span>
|
|
961
|
+
</button>
|
|
962
|
+
</div>
|
|
398
963
|
{% endif %}
|
|
399
964
|
{% if flash.error %}
|
|
400
|
-
<div class="alert alert-error"
|
|
965
|
+
<div class="alert alert-error" id="flash-alert">
|
|
966
|
+
<span class="icon icon-16"><svg viewBox="0 0 24 24"><use href="#ic-alert-circle"/></svg></span>
|
|
967
|
+
<span>{{ flash.error }}</span>
|
|
968
|
+
<button class="alert-close" onclick="this.closest('.alert').remove()">
|
|
969
|
+
<span class="icon icon-14"><svg viewBox="0 0 24 24"><use href="#ic-x"/></svg></span>
|
|
970
|
+
</button>
|
|
971
|
+
</div>
|
|
401
972
|
{% endif %}
|
|
402
973
|
|
|
403
974
|
{% block content %}{% endblock %}
|
|
@@ -405,22 +976,30 @@
|
|
|
405
976
|
</div>
|
|
406
977
|
|
|
407
978
|
<div id="toast-root"></div>
|
|
408
|
-
<div id="modal-root"></div>
|
|
409
979
|
|
|
410
980
|
<script>
|
|
411
|
-
// ── Toast
|
|
981
|
+
// ── Toast ────────────────────────────────────────────────────
|
|
412
982
|
function toast(msg, type = 'success') {
|
|
983
|
+
const icons = {
|
|
984
|
+
success: '<polyline points="20 6 9 17 4 12"/>',
|
|
985
|
+
error: '<circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/>',
|
|
986
|
+
};
|
|
413
987
|
const el = document.createElement('div');
|
|
414
|
-
el.className =
|
|
415
|
-
el.innerHTML =
|
|
988
|
+
el.className = `toast toast-${type}`;
|
|
989
|
+
el.innerHTML = `
|
|
990
|
+
<span class="toast-dot icon icon-15" style="flex-shrink:0">
|
|
991
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
992
|
+
${icons[type] || icons.success}
|
|
993
|
+
</svg>
|
|
994
|
+
</span>
|
|
995
|
+
<span>${msg}</span>`;
|
|
416
996
|
document.getElementById('toast-root').appendChild(el);
|
|
417
|
-
setTimeout(() => el.
|
|
997
|
+
setTimeout(() => el.style.opacity = '0', 3000);
|
|
998
|
+
setTimeout(() => el.remove(), 3300);
|
|
418
999
|
}
|
|
419
1000
|
|
|
420
|
-
// ── Modal
|
|
421
|
-
function openModal(id)
|
|
422
|
-
document.getElementById(id)?.classList.add('open');
|
|
423
|
-
}
|
|
1001
|
+
// ── Modal ────────────────────────────────────────────────────
|
|
1002
|
+
function openModal(id) { document.getElementById(id)?.classList.add('open'); }
|
|
424
1003
|
function closeModal(id) {
|
|
425
1004
|
if (id) document.getElementById(id)?.classList.remove('open');
|
|
426
1005
|
else document.querySelectorAll('.modal-overlay').forEach(m => m.classList.remove('open'));
|
|
@@ -432,36 +1011,78 @@
|
|
|
432
1011
|
if (e.key === 'Escape') closeModal();
|
|
433
1012
|
});
|
|
434
1013
|
|
|
435
|
-
// ──
|
|
1014
|
+
// ── Action dropdowns ────────────────────────────────────────
|
|
1015
|
+
document.addEventListener('click', e => {
|
|
1016
|
+
const btn = e.target.closest('.action-menu-btn');
|
|
1017
|
+
if (btn) {
|
|
1018
|
+
e.stopPropagation();
|
|
1019
|
+
const menu = btn.nextElementSibling;
|
|
1020
|
+
const isOpen = menu.classList.contains('open');
|
|
1021
|
+
document.querySelectorAll('.action-dropdown.open').forEach(d => d.classList.remove('open'));
|
|
1022
|
+
if (!isOpen) menu.classList.add('open');
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
1025
|
+
if (!e.target.closest('.action-dropdown')) {
|
|
1026
|
+
document.querySelectorAll('.action-dropdown.open').forEach(d => d.classList.remove('open'));
|
|
1027
|
+
}
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
// ── Delete confirmation ──────────────────────────────────────
|
|
436
1031
|
function confirmDelete(url, label) {
|
|
437
1032
|
const overlay = document.createElement('div');
|
|
438
1033
|
overlay.className = 'modal-overlay open';
|
|
439
|
-
overlay.innerHTML =
|
|
440
|
-
<div class="modal
|
|
1034
|
+
overlay.innerHTML = `
|
|
1035
|
+
<div class="modal modal-sm">
|
|
441
1036
|
<div class="modal-header">
|
|
442
|
-
<span class="modal-title">
|
|
443
|
-
|
|
1037
|
+
<span class="modal-title flex items-center gap-2">
|
|
1038
|
+
<span class="icon icon-16" style="color:var(--danger)">
|
|
1039
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
1040
|
+
<polyline points="3 6 5 6 21 6"/>
|
|
1041
|
+
<path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6M10 11v6M14 11v6"/>
|
|
1042
|
+
<path d="M9 6V4a1 1 0 011-1h4a1 1 0 011 1v2"/>
|
|
1043
|
+
</svg>
|
|
1044
|
+
</span>
|
|
1045
|
+
Delete ${label}
|
|
1046
|
+
</span>
|
|
1047
|
+
<button class="close-btn" onclick="this.closest('.modal-overlay').remove()">
|
|
1048
|
+
<span class="icon icon-16"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></span>
|
|
1049
|
+
</button>
|
|
444
1050
|
</div>
|
|
445
1051
|
<div class="modal-body">
|
|
446
|
-
<p style="color:var(--text-soft)">
|
|
447
|
-
Are you sure you want to delete <strong
|
|
1052
|
+
<p style="color:var(--text-soft);line-height:1.6">
|
|
1053
|
+
Are you sure you want to delete <strong style="color:var(--text)">${label}</strong>?
|
|
448
1054
|
This action cannot be undone.
|
|
449
1055
|
</p>
|
|
450
1056
|
</div>
|
|
451
1057
|
<div class="modal-footer">
|
|
452
1058
|
<button class="btn btn-ghost" onclick="this.closest('.modal-overlay').remove()">Cancel</button>
|
|
453
|
-
<button class="btn btn-danger" onclick="submitDelete('
|
|
1059
|
+
<button class="btn btn-danger" onclick="submitDelete('${url}')">
|
|
1060
|
+
<span class="icon icon-14"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6"/></svg></span>
|
|
1061
|
+
Delete
|
|
1062
|
+
</button>
|
|
454
1063
|
</div>
|
|
455
|
-
</div
|
|
1064
|
+
</div>`;
|
|
1065
|
+
overlay.addEventListener('click', e => { if (e.target === overlay) overlay.remove(); });
|
|
456
1066
|
document.body.appendChild(overlay);
|
|
457
1067
|
}
|
|
458
1068
|
function submitDelete(url) {
|
|
459
1069
|
const form = document.createElement('form');
|
|
460
|
-
form.method = 'POST';
|
|
1070
|
+
form.method = 'POST';
|
|
1071
|
+
form.action = url;
|
|
461
1072
|
form.innerHTML = '<input name="_method" value="DELETE">';
|
|
462
1073
|
document.body.appendChild(form);
|
|
463
1074
|
form.submit();
|
|
464
1075
|
}
|
|
1076
|
+
|
|
1077
|
+
// ── Auto-dismiss flash after 5s ──────────────────────────────
|
|
1078
|
+
const flashAlert = document.getElementById('flash-alert');
|
|
1079
|
+
if (flashAlert) {
|
|
1080
|
+
setTimeout(() => {
|
|
1081
|
+
flashAlert.style.transition = 'opacity .4s';
|
|
1082
|
+
flashAlert.style.opacity = '0';
|
|
1083
|
+
setTimeout(() => flashAlert.remove(), 400);
|
|
1084
|
+
}, 5000);
|
|
1085
|
+
}
|
|
465
1086
|
</script>
|
|
466
1087
|
{% block scripts %}{% endblock %}
|
|
467
1088
|
</body>
|