@edc-motor/admin-kit 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/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # @edc-motor/admin-kit
2
+
3
+ Kit del panel de administración de **EdC Motor** (Espadas de Ceniza Motor):
4
+ layout completo (sidebar colapsable, panel derecho contextual, migas), gestores
5
+ CRUD declarativos (`defineResource`), y los gestores del motor listos para
6
+ montar — imágenes/previews, PDF, páginas y bloques, configuración de la web,
7
+ usuarios y copias de seguridad. Habla con la API de
8
+ [`edc-motor/core`](https://packagist.org/packages/edc-motor/core) y se apoya en
9
+ [`@edc-motor/ui`](https://www.npmjs.com/package/@edc-motor/ui).
10
+
11
+ ## Instalación
12
+
13
+ ```bash
14
+ npm install @edc-motor/admin-kit
15
+ ```
16
+
17
+ Es un **paquete fuente** (`main: src/index.ts`): lo compila la app consumidora
18
+ con Vite (necesita `sass-embedded` y `@vitejs/plugin-vue`, ver el README de
19
+ `@edc-motor/ui`). En el `vite.config.ts` del admin, añade también su SCSS:
20
+
21
+ ```ts
22
+ loadPaths: [
23
+ 'node_modules/@edc-motor/ui/scss',
24
+ 'node_modules/@edc-motor/admin-kit/scss',
25
+ ],
26
+ ```
27
+
28
+ ## Versionado
29
+
30
+ Versión *de tren* con `edc-motor/core` y `@edc-motor/ui`: los tres paquetes
31
+ comparten número y se etiquetan juntos en el monorepo
32
+ [`bildurre/boardgame_motor`](https://github.com/bildurre/boardgame_motor),
33
+ donde viven el código, las guías, los issues y los pull requests.
34
+
35
+ ## Licencia
36
+
37
+ [GPL-3.0-only](LICENSE).
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@edc-motor/admin-kit",
3
+ "version": "0.2.0",
4
+ "description": "EdC Motor — kit del panel de administración: layout, gestores CRUD, imágenes, PDF, páginas, configuración, usuarios y copias (paquete fuente: lo compila el consumidor con Vite)",
5
+ "license": "GPL-3.0-only",
6
+ "type": "module",
7
+ "main": "src/index.ts",
8
+ "types": "src/index.ts",
9
+ "exports": {
10
+ ".": "./src/index.ts",
11
+ "./scss/*": "./scss/*"
12
+ },
13
+ "files": [
14
+ "src",
15
+ "scss",
16
+ "LICENSE",
17
+ "README.md"
18
+ ],
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/bildurre/boardgame_motor.git",
22
+ "directory": "packages/admin-kit"
23
+ },
24
+ "homepage": "https://github.com/bildurre/boardgame_motor/tree/main/packages/admin-kit#readme",
25
+ "bugs": "https://github.com/bildurre/boardgame_motor/issues",
26
+ "keywords": [
27
+ "vue",
28
+ "admin",
29
+ "boardgame",
30
+ "edc-motor"
31
+ ],
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "dependencies": {
36
+ "@edc-motor/ui": "^0.2.0",
37
+ "axios": "^1.7.0",
38
+ "vue-draggable-plus": "^0.6.1"
39
+ },
40
+ "peerDependencies": {
41
+ "@lucide/vue": "^1.0.0",
42
+ "vue": "^3.5.0",
43
+ "vue-router": "^5.0.0"
44
+ }
45
+ }
@@ -0,0 +1,10 @@
1
+ @use "components/admin-layout";
2
+ @use "components/grid";
3
+ @use "components/entity-card";
4
+ @use "components/filter-bar";
5
+ @use "components/empty-state";
6
+ @use "components/right-sidebar";
7
+ @use "components/manager-card";
8
+ @use "components/preview-manager";
9
+ @use "components/pdf-manager";
10
+ @use "components/page-blocks";
@@ -0,0 +1,310 @@
1
+ @use "tokens" as *;
2
+
3
+ // Portado de kontuan (_app-layout.scss) y adaptado al motor.
4
+
5
+ .app-layout {
6
+ display: flex;
7
+ height: 100vh;
8
+ overflow: hidden;
9
+ background: $bg;
10
+ color: $text-1;
11
+ }
12
+
13
+ .sidebar {
14
+ width: $layout-sidebar-width;
15
+ background: $surface;
16
+ border-right: 1px solid $border;
17
+ display: flex;
18
+ flex-direction: column;
19
+ transition: width 0.2s ease;
20
+ flex-shrink: 0;
21
+ height: 100vh;
22
+ }
23
+
24
+ .sidebar-collapsed .sidebar {
25
+ width: 64px;
26
+ }
27
+
28
+ .sidebar-header {
29
+ display: flex;
30
+ align-items: stretch;
31
+ justify-content: space-between;
32
+ padding: 0;
33
+ border-bottom: 1px solid $border;
34
+ height: 56px;
35
+
36
+ &--collapsed {
37
+ justify-content: center;
38
+ }
39
+ }
40
+
41
+ .sidebar-logo-link {
42
+ display: inline-flex;
43
+ align-items: center;
44
+ text-decoration: none;
45
+ flex: 1;
46
+ min-width: 0;
47
+ padding: 0 $space-3;
48
+ }
49
+
50
+ .sidebar-header .sidebar-toggle {
51
+ flex-shrink: 0;
52
+ width: auto;
53
+ aspect-ratio: 1;
54
+ height: 100%;
55
+ border-radius: 0;
56
+ border: none;
57
+ border-left: 1px solid $border;
58
+ }
59
+
60
+ .sidebar-header--collapsed .sidebar-toggle {
61
+ width: 100%;
62
+ aspect-ratio: auto;
63
+ border-left: none;
64
+ }
65
+
66
+ .sidebar-toggle {
67
+ display: inline-flex;
68
+ align-items: center;
69
+ justify-content: center;
70
+ width: 28px;
71
+ height: 28px;
72
+ padding: 0;
73
+ background: transparent;
74
+ border: 1px solid $border;
75
+ border-radius: $radius-sm;
76
+ color: $accent-500;
77
+ cursor: pointer;
78
+ transition: background-color 0.15s ease, color 0.15s ease;
79
+
80
+ &:hover {
81
+ background: $surface-2;
82
+ color: $accent-600;
83
+ }
84
+ }
85
+
86
+ .sidebar-preferences {
87
+ display: flex;
88
+ align-items: center;
89
+ justify-content: space-between;
90
+ gap: $space-2;
91
+ padding: 0 $space-3;
92
+ }
93
+
94
+ .sidebar-divider {
95
+ border: none;
96
+ border-top: 1px solid $border;
97
+ margin: $space-3;
98
+ }
99
+
100
+ .sidebar-nav {
101
+ flex: 1;
102
+ padding: $space-3 $space-2;
103
+ overflow-y: auto;
104
+
105
+ ul {
106
+ list-style: none;
107
+ padding: 0;
108
+ margin: 0;
109
+ }
110
+ }
111
+
112
+ .sidebar-items {
113
+ display: flex;
114
+ flex-direction: column;
115
+ gap: 2px;
116
+ }
117
+
118
+ // Enlaces de navegación (los rellena el slot #nav).
119
+ .nav-item {
120
+ display: flex;
121
+ align-items: center;
122
+ gap: $space-3;
123
+ padding: $space-2 $space-3;
124
+ border-radius: $radius-md;
125
+ color: $text-2;
126
+ text-decoration: none;
127
+ font-size: $fs-14;
128
+ font-weight: $k-fw-medium;
129
+ transition: all 0.15s ease;
130
+
131
+ .nav-icon,
132
+ svg {
133
+ flex-shrink: 0;
134
+ }
135
+
136
+ &:hover {
137
+ background: $ui-hover-bg;
138
+ color: $text-1;
139
+ }
140
+
141
+ &.router-link-active,
142
+ &.active {
143
+ background: $accent-100;
144
+ color: $accent-600;
145
+ }
146
+ }
147
+
148
+ .sidebar-user {
149
+ display: flex;
150
+ align-items: center;
151
+ justify-content: space-between;
152
+ min-height: 56px;
153
+ padding: $space-2 $space-3;
154
+ border-top: 1px solid $border;
155
+ gap: $space-2;
156
+ flex-shrink: 0;
157
+
158
+ &--collapsed {
159
+ justify-content: center;
160
+ padding: $space-2;
161
+ }
162
+ }
163
+
164
+ .main-wrapper {
165
+ flex: 1;
166
+ display: flex;
167
+ flex-direction: column;
168
+ min-width: 0;
169
+ height: 100vh;
170
+ overflow: hidden;
171
+ }
172
+
173
+ .main-body {
174
+ flex: 1;
175
+ display: flex;
176
+ min-height: 0;
177
+ position: relative;
178
+ }
179
+
180
+ .navbar {
181
+ height: 56px;
182
+ display: flex;
183
+ align-items: center;
184
+ justify-content: space-between;
185
+ padding: 0 $space-6;
186
+ background: $surface;
187
+ border-bottom: 1px solid $border;
188
+ flex-shrink: 0;
189
+ gap: $space-3;
190
+ }
191
+
192
+ .navbar-left {
193
+ display: flex;
194
+ align-items: center;
195
+ gap: $space-2;
196
+ min-width: 0;
197
+ }
198
+
199
+ // La marca en la barra superior (solo móvil: el sidebar está oculto).
200
+ .navbar-brand {
201
+ display: inline-flex;
202
+ align-items: center;
203
+ text-decoration: none;
204
+ flex-shrink: 0;
205
+ }
206
+
207
+ .navbar-title {
208
+ font-size: $fs-16;
209
+ font-weight: $k-fw-semibold;
210
+ color: $text-1;
211
+ overflow: hidden;
212
+ text-overflow: ellipsis;
213
+ white-space: nowrap;
214
+ }
215
+
216
+ .navbar-right {
217
+ display: flex;
218
+ align-items: center;
219
+ gap: $space-3;
220
+ flex-shrink: 0;
221
+ }
222
+
223
+ .main-content {
224
+ flex: 1;
225
+ padding: $layout-page-padding;
226
+ width: 100%;
227
+ min-width: 0;
228
+ overflow-y: auto;
229
+ container-type: inline-size;
230
+ container-name: content;
231
+ }
232
+
233
+ .sidebar-overlay {
234
+ position: fixed;
235
+ top: 56px;
236
+ left: 0;
237
+ right: 0;
238
+ bottom: 0;
239
+ background: rgba(0, 0, 0, 0.4);
240
+ z-index: 90;
241
+ }
242
+
243
+ .hamburger {
244
+ display: flex;
245
+ align-items: center;
246
+ justify-content: center;
247
+ background: none;
248
+ border: none;
249
+ cursor: pointer;
250
+ padding: $space-1;
251
+ color: $text-2;
252
+ border-radius: $radius-md;
253
+
254
+ &:hover {
255
+ background: $ui-hover-bg;
256
+ color: $text-1;
257
+ }
258
+ }
259
+
260
+ // ---- Colapsado (escritorio): solo iconos centrados ----
261
+ .sidebar-collapsed:not(.is-mobile) {
262
+ .nav-item {
263
+ justify-content: center;
264
+ gap: 0;
265
+ }
266
+ .nav-label {
267
+ display: none;
268
+ }
269
+ }
270
+
271
+ // ---- Modo móvil (< bp-md): el sidebar se vuelve drawer ----
272
+ .is-mobile {
273
+ .sidebar {
274
+ position: fixed;
275
+ top: 56px;
276
+ left: 0;
277
+ bottom: 0;
278
+ height: auto;
279
+ z-index: 100;
280
+ width: $layout-sidebar-width;
281
+ max-width: 100%;
282
+ transform: translateX(-100%);
283
+ transition: transform 0.25s ease;
284
+
285
+ &--mobile-open {
286
+ transform: translateX(0);
287
+ }
288
+ }
289
+
290
+ .sidebar-header {
291
+ display: none;
292
+ }
293
+
294
+ .sidebar-toggle {
295
+ display: none;
296
+ }
297
+
298
+ .navbar {
299
+ padding: 0 $space-3;
300
+ }
301
+
302
+ .navbar-right {
303
+ gap: $space-2;
304
+ flex-shrink: 0;
305
+ }
306
+
307
+ .main-content {
308
+ padding: $space-4;
309
+ }
310
+ }
@@ -0,0 +1,32 @@
1
+ @use "tokens" as *;
2
+
3
+ // Estado vacío (portado de kontuan; tokens adaptados a EdC).
4
+ .empty-state {
5
+ display: flex;
6
+ flex-direction: column;
7
+ align-items: center;
8
+ justify-content: center;
9
+ padding: $space-8 $space-4;
10
+ text-align: center;
11
+
12
+ &__icon {
13
+ margin-bottom: $space-4;
14
+ color: $text-3;
15
+ svg { width: 40px; height: 40px; }
16
+ }
17
+
18
+ &__title {
19
+ font-size: $fs-18;
20
+ font-weight: $k-fw-semibold;
21
+ color: $text-1;
22
+ margin: 0 0 $space-1;
23
+ }
24
+
25
+ &__description {
26
+ font-size: $fs-14;
27
+ color: $text-3;
28
+ max-width: 360px;
29
+ }
30
+
31
+ &__action { margin-top: $space-5; }
32
+ }
@@ -0,0 +1,86 @@
1
+ @use "tokens" as *;
2
+
3
+ // Tarjeta de entidad: mezcla kontuan (contenedor + hover) y CDL (cabecera con
4
+ // acciones + divisoria, contenido con badges/meta).
5
+ .entity-card {
6
+ display: flex;
7
+ flex-direction: column;
8
+ background: $surface;
9
+ border: 1px solid $border;
10
+ border-radius: $radius-lg;
11
+ box-shadow: $shadow-sm;
12
+ overflow: hidden;
13
+ transition: border-color 0.15s ease, box-shadow 0.15s ease;
14
+ min-width: 0;
15
+
16
+ &--clickable {
17
+ cursor: pointer;
18
+ &:hover { border-color: $border-strong; box-shadow: $shadow-md; }
19
+ }
20
+ &--muted { opacity: 0.65; }
21
+ &.is-active { border-color: $accent-500; }
22
+
23
+ // Franja superior opcional (emblema/imagen).
24
+ &__media {
25
+ position: relative;
26
+ aspect-ratio: 16 / 9;
27
+ display: flex;
28
+ align-items: center;
29
+ justify-content: center;
30
+ overflow: hidden;
31
+ background: $surface-2;
32
+
33
+ img { width: 100%; height: 100%; object-fit: cover; }
34
+ }
35
+
36
+ // Cabecera: título + acciones, con divisoria (CDL).
37
+ &__header {
38
+ display: flex;
39
+ align-items: center;
40
+ gap: $space-2;
41
+ padding: $space-3 $space-4;
42
+ border-bottom: 1px solid $border;
43
+ min-width: 0;
44
+ }
45
+
46
+ &__title {
47
+ flex: 1;
48
+ margin: 0;
49
+ font-size: $fs-16;
50
+ font-weight: $k-fw-semibold;
51
+ color: $text-1;
52
+ overflow: hidden;
53
+ text-overflow: ellipsis;
54
+ white-space: nowrap;
55
+ min-width: 0;
56
+ }
57
+
58
+ &__actions {
59
+ display: flex;
60
+ align-items: center;
61
+ gap: 2px;
62
+ flex-shrink: 0;
63
+ }
64
+
65
+ &__content {
66
+ display: flex;
67
+ flex-direction: column;
68
+ gap: $space-2;
69
+ padding: $space-3 $space-4;
70
+ }
71
+
72
+ &__badges {
73
+ display: flex;
74
+ flex-wrap: wrap;
75
+ gap: $space-1;
76
+ }
77
+
78
+ &__meta {
79
+ display: flex;
80
+ flex-wrap: wrap;
81
+ align-items: center;
82
+ gap: $space-3;
83
+ font-size: $fs-13;
84
+ color: $text-3;
85
+ }
86
+ }
@@ -0,0 +1,53 @@
1
+ @use "tokens" as *;
2
+
3
+ // Barra de filtros estilo kontuan (búsqueda con icono). Va por encima de las tabs.
4
+ .filter-bar {
5
+ display: flex;
6
+ align-items: center;
7
+ gap: $space-3;
8
+ flex-wrap: wrap;
9
+ margin-bottom: $space-4;
10
+
11
+ &__search {
12
+ position: relative;
13
+ flex: 1;
14
+ min-width: 200px;
15
+ }
16
+
17
+ &__search-icon {
18
+ position: absolute;
19
+ right: $space-3;
20
+ top: 50%;
21
+ transform: translateY(-50%);
22
+ color: $text-3;
23
+ pointer-events: none;
24
+ }
25
+
26
+ &__search-input {
27
+ width: 100%;
28
+ height: 40px;
29
+ padding: 0 36px 0 $space-3;
30
+ background: $surface-2;
31
+ color: $text-1;
32
+ border: 1px solid $border;
33
+ border-radius: $radius-md;
34
+ font-family: $k-font-sans;
35
+ font-size: $fs-14;
36
+ transition: border-color 0.15s, box-shadow 0.15s;
37
+
38
+ &::placeholder { color: $text-3; }
39
+ &:hover:not(:focus) { border-color: $border-strong; }
40
+ &:focus {
41
+ outline: none;
42
+ border-color: $accent-500;
43
+ box-shadow: 0 0 0 3px color-mix(in srgb, $accent-500 25%, transparent);
44
+ }
45
+ }
46
+
47
+ &__extra {
48
+ display: flex;
49
+ align-items: center;
50
+ gap: $space-2;
51
+ flex-shrink: 0;
52
+ }
53
+ }
@@ -0,0 +1,31 @@
1
+ @use "tokens" as *;
2
+
3
+ // Grid responsive (portado de kontuan). Columnas por breakpoint del contenedor
4
+ // `content` para que coincida con el ancho real disponible.
5
+ .grid {
6
+ display: grid;
7
+ grid-template-columns: 1fr;
8
+
9
+ &--gap-sm { gap: $space-3; }
10
+ &--gap-md { gap: $space-4; }
11
+ &--gap-lg { gap: $space-5; }
12
+
13
+ @for $i from 1 through 6 {
14
+ &--base-#{$i} { grid-template-columns: repeat(#{$i}, 1fr); }
15
+ }
16
+ @for $i from 1 through 6 {
17
+ &--sm-#{$i} {
18
+ @container content (min-width: #{$bp-sm}) { grid-template-columns: repeat(#{$i}, 1fr); }
19
+ }
20
+ }
21
+ @for $i from 1 through 6 {
22
+ &--md-#{$i} {
23
+ @container content (min-width: #{$bp-md}) { grid-template-columns: repeat(#{$i}, 1fr); }
24
+ }
25
+ }
26
+ @for $i from 1 through 6 {
27
+ &--lg-#{$i} {
28
+ @container content (min-width: #{$bp-lg}) { grid-template-columns: repeat(#{$i}, 1fr); }
29
+ }
30
+ }
31
+ }