siesa-agents 2.1.71 → 2.1.72
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/siesa-agents/bmm/workflows/2-plan-workflows/create-ux-design/workflow_ext.md +36 -1
- package/siesa-agents/bmm/workflows/3-solutioning/create-architecture/workflow_ext.md +2 -0
- package/siesa-agents/bmm/workflows/4-implementation/sprint-status/workflow_ext.md +42 -0
- package/siesa-agents/bmm/workflows/workflow-status/Init/workflow_ext.md +7 -0
- package/siesa-agents/resources/ux-ui/ux-design-specification.md +837 -0
package/package.json
CHANGED
|
@@ -30,7 +30,42 @@ Output to the user:
|
|
|
30
30
|
|
|
31
31
|
---
|
|
32
32
|
|
|
33
|
-
## 2.
|
|
33
|
+
## 2. INITIALIZATION — CHECK FOR EXISTING UX DESIGN DOCUMENT
|
|
34
|
+
|
|
35
|
+
Before executing any workflow step, search for an existing UX design document:
|
|
36
|
+
|
|
37
|
+
1. Look for a file matching `*ux-design-specification*.md` inside the `{planning_artifacts}/` folder.
|
|
38
|
+
- Glob pattern to check: `{planning_artifacts}/*ux-design-specification*.md`
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 3. DECISION BRANCH
|
|
43
|
+
|
|
44
|
+
### If `ux-design-specification.md` EXISTS
|
|
45
|
+
|
|
46
|
+
Use the **AskUserQuestion** tool to present the following options (respect `communication_language` from config):
|
|
47
|
+
|
|
48
|
+
> Se encontró un documento de especificación UX/UI existente en `{planning_artifacts}/ux-design-specification.md`.
|
|
49
|
+
>
|
|
50
|
+
> ¿Qué deseas hacer?
|
|
51
|
+
>
|
|
52
|
+
> **[1] Editar** — Abrir el documento para revisarlo y modificarlo de forma colaborativa.
|
|
53
|
+
> **[2] Resumen** — Generar un resumen ejecutivo del documento actual.
|
|
54
|
+
|
|
55
|
+
Wait for the user's selection and act accordingly:
|
|
56
|
+
|
|
57
|
+
- **Option 1 — Edit:** Read the full contents of `{planning_artifacts}/ux-design-specification.md`, present it to the user section by section, and facilitate collaborative editing. Apply changes directly to the file using the Edit tool.
|
|
58
|
+
- **Option 2 — Summary:** Read the full contents of `{planning_artifacts}/ux-design-specification.md` and produce a concise executive summary covering: design vision, target users, design system decisions, visual foundations, component strategy, and any open design questions.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
### If `ux-design-specification.md` does NOT EXIST
|
|
63
|
+
|
|
64
|
+
LOAD the FULL `@_bmad/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md`, READ its entire contents and follow its directions exactly!
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 4. MANDATORY RULE — PHASE COMMIT AT WORKFLOW END
|
|
34
69
|
|
|
35
70
|
**TRIGGER:** Immediately after all workflow steps complete and the UX Design document has been generated/saved.
|
|
36
71
|
|
|
@@ -39,6 +39,8 @@ Before doing anything else, search for an existing architecture document:
|
|
|
39
39
|
|
|
40
40
|
### If `architecture.md` EXISTS
|
|
41
41
|
|
|
42
|
+
Use the **AskUserQuestion** tool to present the following options:
|
|
43
|
+
|
|
42
44
|
Present the user with the following options (respect `communication_language` from config):
|
|
43
45
|
|
|
44
46
|
> Se encontró un documento de arquitectura existente en `{planning_artifacts}/architecture.md`.
|
|
@@ -1,3 +1,45 @@
|
|
|
1
|
+
# REGLA OBLIGATORIA: MENÚ DE INICIO
|
|
2
|
+
|
|
3
|
+
**TRIGGER:** Al inicio del modo `interactive`, **antes del Step 1**.
|
|
4
|
+
|
|
5
|
+
Invocar `AskUserQuestion` con la siguiente configuración:
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"questions": [
|
|
10
|
+
{
|
|
11
|
+
"question": "¿Qué deseas ejecutar?",
|
|
12
|
+
"header": "Modo",
|
|
13
|
+
"multiSelect": false,
|
|
14
|
+
"options": [
|
|
15
|
+
{
|
|
16
|
+
"label": "Sprint Status + Retrospective",
|
|
17
|
+
"description": "Ejecuta el resumen del sprint y encadena automáticamente con el workflow de Retrospective al finalizar."
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"label": "Solo Sprint Status",
|
|
21
|
+
"description": "Ejecuta únicamente el resumen del sprint sin encadenar con Retrospective."
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- Si elige `Sprint Status + Retrospective`: guardar `run_retrospective = true`
|
|
30
|
+
- Si elige `Solo Sprint Status`: guardar `run_retrospective = false`
|
|
31
|
+
|
|
32
|
+
**Al finalizar el Step 5** (después de que el usuario elija su acción), si `run_retrospective == true`:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
---
|
|
36
|
+
➡️ Encadenando con Retrospective tal como seleccionaste al inicio...
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Luego invocar `/bmad:bmm:workflows:retrospective`.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
1
43
|
# REGLA OBLIGATORIA: SPRINT-STATUS CON SINCRONIZACIÓN DE FEATURE-STATUS
|
|
2
44
|
|
|
3
45
|
**TRIGGER:** Cada vez que se ejecute `/sprint-status`.
|
|
@@ -43,6 +43,13 @@ Copy the file `@_siesa-agents/resources/architecture/architecture-both.md ` to `
|
|
|
43
43
|
- Do not modify the file contents — copy as-is.
|
|
44
44
|
- After copying, confirm the file exists at the destination.
|
|
45
45
|
|
|
46
|
+
Create base UX/UI specifications
|
|
47
|
+
Copy the file `@_siesa-agents/resources/ux-ui/ux-design-specification.md` to `{planning_artifacts}/ux-design-specification.md.md`.
|
|
48
|
+
|
|
49
|
+
- If the destination directory does not exist, create it first.
|
|
50
|
+
- Do not modify the file contents — copy as-is.
|
|
51
|
+
- After copying, confirm the file exists at the destination.
|
|
52
|
+
|
|
46
53
|
Check and clone Frontend:
|
|
47
54
|
```bash
|
|
48
55
|
# Only run if apps/Frontend does NOT exist
|
|
@@ -0,0 +1,837 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: base-ux-design-specification.md
|
|
3
|
+
status: base
|
|
4
|
+
note: >
|
|
5
|
+
This document was automatically generated during siesa-agents installation.
|
|
6
|
+
It represents the Siesa corporate base UX/UI specification applicable to all frontend projects.
|
|
7
|
+
It can be extended with the /create-ux-design workflow for project-specific decisions.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# UX/UI Specification Document — Siesa Corporate Base
|
|
11
|
+
|
|
12
|
+
_This document establishes the mandatory UX/UI decisions for all Siesa frontend projects. It serves as the source of truth for consistent, accessible, and brand-aligned user interfaces._
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 1. Design System Foundation
|
|
17
|
+
|
|
18
|
+
### 1.1 Color Palette
|
|
19
|
+
|
|
20
|
+
#### Brand Colors
|
|
21
|
+
|
|
22
|
+
| Token | Value | Usage |
|
|
23
|
+
|-------|-------|-------|
|
|
24
|
+
| `--color-primary-600` | `#0e79fd` | Main brand color — CTAs, links, focus |
|
|
25
|
+
| `--color-secondary-950` | `#000000` | Brand secondary — NOT for backgrounds or grays |
|
|
26
|
+
| `--color-tertiary-800` | `#154ca9` | Brand tertiary — Secondary actions, accents |
|
|
27
|
+
|
|
28
|
+
#### Full Primary Scale
|
|
29
|
+
|
|
30
|
+
```css
|
|
31
|
+
:root {
|
|
32
|
+
--color-primary-50: #f7fcff;
|
|
33
|
+
--color-primary-100: #dbeefe;
|
|
34
|
+
--color-primary-200: #bfe2fe;
|
|
35
|
+
--color-primary-300: #93d1fd;
|
|
36
|
+
--color-primary-400: #60b6fa;
|
|
37
|
+
--color-primary-500: #3c9bf6;
|
|
38
|
+
--color-primary-600: #0e79fd; /* Main Primary */
|
|
39
|
+
--color-primary-700: #0f6ae3;
|
|
40
|
+
--color-primary-800: #1355b7;
|
|
41
|
+
--color-primary-900: #154990;
|
|
42
|
+
--color-primary-950: #112d57;
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
#### Full Secondary Scale (Brand, NOT Neutrals)
|
|
47
|
+
|
|
48
|
+
```css
|
|
49
|
+
:root {
|
|
50
|
+
--color-secondary-50: #f6f6f6;
|
|
51
|
+
--color-secondary-100: #e7e7e7;
|
|
52
|
+
--color-secondary-200: #d1d1d1;
|
|
53
|
+
--color-secondary-300: #b0b0b0;
|
|
54
|
+
--color-secondary-400: #888888;
|
|
55
|
+
--color-secondary-500: #6d6d6d;
|
|
56
|
+
--color-secondary-600: #5d5d5d;
|
|
57
|
+
--color-secondary-700: #4f4f4f;
|
|
58
|
+
--color-secondary-800: #454545;
|
|
59
|
+
--color-secondary-900: #3d3d3d;
|
|
60
|
+
--color-secondary-950: #000000; /* Main Secondary */
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
#### Full Tertiary Scale
|
|
65
|
+
|
|
66
|
+
```css
|
|
67
|
+
:root {
|
|
68
|
+
--color-tertiary-50: #eef8ff;
|
|
69
|
+
--color-tertiary-100: #d9efff;
|
|
70
|
+
--color-tertiary-200: #bce4ff;
|
|
71
|
+
--color-tertiary-300: #8ed4ff;
|
|
72
|
+
--color-tertiary-400: #58bbff;
|
|
73
|
+
--color-tertiary-500: #329cff;
|
|
74
|
+
--color-tertiary-600: #1b7df5;
|
|
75
|
+
--color-tertiary-700: #1465e1;
|
|
76
|
+
--color-tertiary-800: #154ca9; /* Main Tertiary */
|
|
77
|
+
--color-tertiary-900: #19478f;
|
|
78
|
+
--color-tertiary-950: #051938;
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
#### Semantic Colors
|
|
83
|
+
|
|
84
|
+
| Color | Tailwind | Usage |
|
|
85
|
+
|-------|----------|-------|
|
|
86
|
+
| Success | `green.500` | Confirmations, active states |
|
|
87
|
+
| Warning | `amber.500` | Alerts, cautions |
|
|
88
|
+
| Error | `red.500` | Errors, destructive actions |
|
|
89
|
+
| Info | `cyan.500` | Informational messages |
|
|
90
|
+
| Neutral | `slate.*` | Backgrounds, borders, text — NEVER use secondary brand for this |
|
|
91
|
+
|
|
92
|
+
#### Surfaces & Backgrounds
|
|
93
|
+
|
|
94
|
+
```css
|
|
95
|
+
:root {
|
|
96
|
+
/* Light Theme */
|
|
97
|
+
--color-background: theme('colors.white');
|
|
98
|
+
--color-surface: theme('colors.slate.50');
|
|
99
|
+
--color-surface-secondary: theme('colors.slate.100');
|
|
100
|
+
--color-border: theme('colors.slate.200');
|
|
101
|
+
--color-border-secondary: theme('colors.slate.300');
|
|
102
|
+
|
|
103
|
+
/* Dark Theme */
|
|
104
|
+
--color-background-dark: theme('colors.slate.950');
|
|
105
|
+
--color-surface-dark: theme('colors.slate.900');
|
|
106
|
+
--color-surface-secondary-dark: theme('colors.slate.800');
|
|
107
|
+
--color-border-dark: theme('colors.slate.700');
|
|
108
|
+
--color-border-secondary-dark: theme('colors.slate.600');
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
#### Tailwind Config Extension
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
// tailwind.config.js
|
|
116
|
+
module.exports = {
|
|
117
|
+
darkMode: 'class',
|
|
118
|
+
theme: {
|
|
119
|
+
extend: {
|
|
120
|
+
colors: {
|
|
121
|
+
primary: {
|
|
122
|
+
50: '#f7fcff', 100: '#dbeefe', 200: '#bfe2fe', 300: '#93d1fd',
|
|
123
|
+
400: '#60b6fa', 500: '#3c9bf6', 600: '#0e79fd', 700: '#0f6ae3',
|
|
124
|
+
800: '#1355b7', 900: '#154990', 950: '#112d57',
|
|
125
|
+
},
|
|
126
|
+
secondary: {
|
|
127
|
+
50: '#f6f6f6', 100: '#e7e7e7', 200: '#d1d1d1', 300: '#b0b0b0',
|
|
128
|
+
400: '#888888', 500: '#6d6d6d', 600: '#5d5d5d', 700: '#4f4f4f',
|
|
129
|
+
800: '#454545', 900: '#3d3d3d', 950: '#000000',
|
|
130
|
+
},
|
|
131
|
+
tertiary: {
|
|
132
|
+
50: '#eef8ff', 100: '#d9efff', 200: '#bce4ff', 300: '#8ed4ff',
|
|
133
|
+
400: '#58bbff', 500: '#329cff', 600: '#1b7df5', 700: '#1465e1',
|
|
134
|
+
800: '#154ca9', 900: '#19478f', 950: '#051938',
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
### 1.2 Typography
|
|
145
|
+
|
|
146
|
+
#### Font Family
|
|
147
|
+
|
|
148
|
+
```css
|
|
149
|
+
:root {
|
|
150
|
+
--font-primary: 'Inter_18pt-Regular', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
151
|
+
--font-light: 'Inter_18pt-Light', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
152
|
+
--font-bold: 'Inter_18pt-Bold', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
153
|
+
--font-mono: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Font files (3 weights only):**
|
|
158
|
+
|
|
159
|
+
| Weight | File | CSS Variable |
|
|
160
|
+
|--------|------|--------------|
|
|
161
|
+
| Light (300) | `Inter_18pt-Light.ttf` | `--font-light` |
|
|
162
|
+
| Regular (400) | `Inter_18pt-Regular.ttf` | `--font-primary` |
|
|
163
|
+
| Bold (700) | `Inter_18pt-Bold.ttf` | `--font-bold` |
|
|
164
|
+
|
|
165
|
+
> **Rule:** There are only 3 physical font weights. All Tailwind weight values `medium`, `semibold`, `extrabold`, `black` resolve to the Bold file (700).
|
|
166
|
+
|
|
167
|
+
#### Typographic Scale
|
|
168
|
+
|
|
169
|
+
| Element | Tailwind Classes |
|
|
170
|
+
|---------|-----------------|
|
|
171
|
+
| H1 | `text-4xl font-bold leading-tight tracking-tight` |
|
|
172
|
+
| H2 | `text-3xl font-bold leading-tight tracking-tight` |
|
|
173
|
+
| H3 | `text-2xl font-semibold leading-snug tracking-tight` |
|
|
174
|
+
| H4 | `text-xl font-semibold leading-snug` |
|
|
175
|
+
| H5 | `text-lg font-medium leading-normal` |
|
|
176
|
+
| H6 | `text-base font-medium leading-normal` |
|
|
177
|
+
| Body | `text-base font-normal leading-relaxed` |
|
|
178
|
+
| Body Large | `text-lg font-normal leading-relaxed` |
|
|
179
|
+
| Body Small | `text-sm font-normal leading-normal` |
|
|
180
|
+
| Caption | `text-xs font-light leading-normal` |
|
|
181
|
+
| Label | `text-sm font-medium leading-normal` |
|
|
182
|
+
| Button Primary | `text-base font-semibold leading-none` |
|
|
183
|
+
| Button Secondary | `text-sm font-medium leading-none` |
|
|
184
|
+
| Link | `text-base font-normal leading-normal` |
|
|
185
|
+
| Code | `text-sm font-normal leading-normal font-mono` |
|
|
186
|
+
| Badge | `text-xs font-semibold leading-none` |
|
|
187
|
+
| Tooltip | `text-sm font-normal leading-snug` |
|
|
188
|
+
|
|
189
|
+
**Constraints:**
|
|
190
|
+
- Minimum font size: `16px` (browser default, never go below)
|
|
191
|
+
- Maximum line length: `75ch`
|
|
192
|
+
- One single `<h1>` per page, headings in descending order
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
### 1.3 Dark Mode
|
|
197
|
+
|
|
198
|
+
| Aspect | Value |
|
|
199
|
+
|--------|-------|
|
|
200
|
+
| Method | Tailwind `class` strategy |
|
|
201
|
+
| Selector | `html` element (root) |
|
|
202
|
+
| Framework | `next-themes` (SSR-safe) |
|
|
203
|
+
| Default | System preference (`system`) |
|
|
204
|
+
|
|
205
|
+
#### Standard Dark Mode Class Pairs
|
|
206
|
+
|
|
207
|
+
```yaml
|
|
208
|
+
text_colors:
|
|
209
|
+
primary: "text-zinc-900 dark:text-zinc-50"
|
|
210
|
+
secondary: "text-gray-700 dark:text-gray-300"
|
|
211
|
+
muted: "text-gray-500 dark:text-gray-400"
|
|
212
|
+
disabled: "text-gray-400 dark:text-gray-600"
|
|
213
|
+
|
|
214
|
+
brand_colors:
|
|
215
|
+
primary: "text-primary-600 dark:text-primary-400"
|
|
216
|
+
secondary: "text-secondary-950 dark:text-slate-50"
|
|
217
|
+
tertiary: "text-tertiary-800 dark:text-tertiary-400"
|
|
218
|
+
|
|
219
|
+
surfaces:
|
|
220
|
+
page: "bg-white dark:bg-slate-950"
|
|
221
|
+
card: "bg-slate-50 dark:bg-slate-900"
|
|
222
|
+
elevated: "bg-white dark:bg-slate-800"
|
|
223
|
+
input: "bg-white dark:bg-slate-900"
|
|
224
|
+
|
|
225
|
+
borders:
|
|
226
|
+
subtle: "border-slate-200 dark:border-slate-700"
|
|
227
|
+
prominent: "border-slate-300 dark:border-slate-600"
|
|
228
|
+
tertiary: "border-zinc-400 dark:border-zinc-400"
|
|
229
|
+
focus: "ring-primary-600 dark:ring-primary-400"
|
|
230
|
+
|
|
231
|
+
buttons:
|
|
232
|
+
primary: "bg-primary-600 text-white hover:bg-primary-700 dark:bg-primary-600 dark:hover:bg-primary-500"
|
|
233
|
+
secondary: "bg-slate-200 text-slate-900 hover:bg-slate-300 dark:bg-slate-700 dark:text-slate-100 dark:hover:bg-slate-600"
|
|
234
|
+
ghost: "text-slate-700 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-800"
|
|
235
|
+
|
|
236
|
+
navigation:
|
|
237
|
+
navbar: "bg-white border-slate-200 dark:bg-slate-900 dark:border-slate-700"
|
|
238
|
+
sidebar: "bg-slate-50 border-slate-200 dark:bg-slate-900 dark:border-slate-700"
|
|
239
|
+
active: "bg-primary-50 text-primary-700 border-primary-200 dark:bg-primary-950 dark:text-primary-300 dark:border-primary-800"
|
|
240
|
+
|
|
241
|
+
feedback:
|
|
242
|
+
success: "bg-green-50 text-green-800 border-green-200 dark:bg-green-950 dark:text-green-300 dark:border-green-800"
|
|
243
|
+
warning: "bg-amber-50 text-amber-800 border-amber-200 dark:bg-amber-950 dark:text-amber-300 dark:border-amber-800"
|
|
244
|
+
error: "bg-red-50 text-red-800 border-red-200 dark:bg-red-950 dark:text-red-300 dark:border-red-800"
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## 2. Component Library
|
|
250
|
+
|
|
251
|
+
### 2.1 Component Selection Priority
|
|
252
|
+
|
|
253
|
+
**MANDATORY ORDER — must be followed exactly:**
|
|
254
|
+
|
|
255
|
+
| Priority | Source | Action |
|
|
256
|
+
|----------|--------|--------|
|
|
257
|
+
| 1 | `siesa-ui-kit` (local) | Always check first — any type, any complexity |
|
|
258
|
+
| 2 | Component does not exist | Ask: [1] Use shadcn directly, [2] Create for siesa-ui-kit (requires MR) |
|
|
259
|
+
| 3 | shadcn/ui via MCP registry | Only if user explicitly chose option [1] |
|
|
260
|
+
|
|
261
|
+
> **Rule:** 90% fewer bugs using existing components vs manual creation. Never bypass this order.
|
|
262
|
+
|
|
263
|
+
### 2.2 MasterCrud — Orchestrator Component
|
|
264
|
+
|
|
265
|
+
`MasterCrud` is the highest-level component for master data management. **All master modules MUST use it.**
|
|
266
|
+
|
|
267
|
+
```tsx
|
|
268
|
+
import { MasterCrud } from '@/components/MasterCrud';
|
|
269
|
+
import type { MasterCrudField, CrudService } from '@/components/MasterCrud/MasterCrud.types';
|
|
270
|
+
|
|
271
|
+
<MasterCrud<Product>
|
|
272
|
+
title="Catálogo de Productos"
|
|
273
|
+
entityName="Producto"
|
|
274
|
+
fields={productFields}
|
|
275
|
+
service={productService}
|
|
276
|
+
/>
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
#### MasterCrud Props
|
|
280
|
+
|
|
281
|
+
| Property | Type | Default | Description |
|
|
282
|
+
|----------|------|---------|-------------|
|
|
283
|
+
| `title` | string | — | Module title (Spanish) |
|
|
284
|
+
| `entityName` | string | — | Entity name singular (Spanish) |
|
|
285
|
+
| `fields` | `MasterCrudField<T>[]` | — | Columns and form field config |
|
|
286
|
+
| `service` | `CrudService<T>` | — | Network operations contract |
|
|
287
|
+
| `idField` | `keyof T` | `'id'` | Unique identifier field |
|
|
288
|
+
| `pageSize` | number | `10` | Records per page |
|
|
289
|
+
| `navigationType` | `'modal' \| 'sidebar' \| 'page'` | — | Navigation type for form |
|
|
290
|
+
| `activeByCompany` | boolean | `false` | Enable multi-company selector |
|
|
291
|
+
| `companyRequired` | boolean | `false` | Hide "Global" option |
|
|
292
|
+
| `companies` | `MasterCrudCompany[]` | `[]` | Available companies |
|
|
293
|
+
| `showCreateButton` | boolean | `true` | Show/hide "Nuevo" button |
|
|
294
|
+
| `allowDelete` | boolean | `true` | Enable delete/deactivate |
|
|
295
|
+
| `formColumns` | `1 \| 2` | `2` | Form layout columns |
|
|
296
|
+
| `enableMultiSelect` | boolean | `false` | Enable row checkboxes |
|
|
297
|
+
| `showViewToggle` | boolean | `false` | Toggle between table and card view |
|
|
298
|
+
| `renderForm` | Function | — | Custom form body rendering |
|
|
299
|
+
| `actions` | `MasterCrudAction<T>[]` | `[]` | Extra actions per record |
|
|
300
|
+
|
|
301
|
+
#### Navigation Type Selection Guide
|
|
302
|
+
|
|
303
|
+
| Use Case | `navigationType` |
|
|
304
|
+
|----------|-----------------|
|
|
305
|
+
| Simple forms (≤ 8 fields) | `'modal'` |
|
|
306
|
+
| Medium forms (9–15 fields) | `'sidebar'` |
|
|
307
|
+
| Complex forms (16+ fields, tabs, sections) | `'page'` |
|
|
308
|
+
|
|
309
|
+
#### FieldType Reference
|
|
310
|
+
|
|
311
|
+
| Type | Visual Control | Use For |
|
|
312
|
+
|------|---------------|---------|
|
|
313
|
+
| `'text'` | Standard input | Names, codes, descriptions |
|
|
314
|
+
| `'number'` | Numeric input with controls | Quantities, rates, amounts |
|
|
315
|
+
| `'email'` | Email-validated input | Email addresses |
|
|
316
|
+
| `'date'` | Calendar picker (ISO format) | Dates |
|
|
317
|
+
| `'boolean'` | Toggle switch | Active/inactive flags |
|
|
318
|
+
| `'select'` | Dropdown (requires `options`) | Fixed lists |
|
|
319
|
+
| `'lookup'` | Async search (requires `lookupConfig`) | Master record selection |
|
|
320
|
+
|
|
321
|
+
> **Rule R-LF-001:** For master record selection, ALWAYS use `type: 'lookup'` with LookupField. Never use `type: 'select'` for data from the backend.
|
|
322
|
+
|
|
323
|
+
### 2.3 Form Standards
|
|
324
|
+
|
|
325
|
+
| Technology | Version | Purpose |
|
|
326
|
+
|-----------|---------|---------|
|
|
327
|
+
| React Hook Form | 7.x | Form state management |
|
|
328
|
+
| Zod | 3.x | Schema validation |
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
// Standard form pattern
|
|
332
|
+
import { useForm } from 'react-hook-form';
|
|
333
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
334
|
+
import { z } from 'zod';
|
|
335
|
+
|
|
336
|
+
const schema = z.object({
|
|
337
|
+
nombre: z.string().min(1, 'El nombre es requerido'),
|
|
338
|
+
codigo: z.string().min(1, 'El código es requerido'),
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
const form = useForm({ resolver: zodResolver(schema) });
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## 3. Icons & Assets
|
|
347
|
+
|
|
348
|
+
### 3.1 Icon Libraries
|
|
349
|
+
|
|
350
|
+
| Priority | Library | Version | Usage |
|
|
351
|
+
|----------|---------|---------|-------|
|
|
352
|
+
| Primary | Heroicons | Latest | Default for all UI icons |
|
|
353
|
+
| Secondary | Font Awesome | 6.5+ | When Heroicons lacks the icon |
|
|
354
|
+
|
|
355
|
+
#### Icon Sizes
|
|
356
|
+
|
|
357
|
+
| Name | Tailwind Classes | Use |
|
|
358
|
+
|------|-----------------|-----|
|
|
359
|
+
| Small | `w-4 h-4` | Inline text, badges |
|
|
360
|
+
| Default | `w-5 h-5` | Buttons, menu items |
|
|
361
|
+
| Medium | `w-6 h-6` | Section headers |
|
|
362
|
+
| Large | `w-8 h-8` | Feature illustrations |
|
|
363
|
+
|
|
364
|
+
#### Icon Colors
|
|
365
|
+
|
|
366
|
+
| Context | Class |
|
|
367
|
+
|---------|-------|
|
|
368
|
+
| Inherit from text | `text-current` |
|
|
369
|
+
| Primary brand | `text-primary-600` |
|
|
370
|
+
| Secondary brand | `text-secondary-600` |
|
|
371
|
+
| Neutral | `text-gray-500` |
|
|
372
|
+
|
|
373
|
+
### 3.2 Logo Assets
|
|
374
|
+
|
|
375
|
+
| Variant | File | Usage |
|
|
376
|
+
|---------|------|-------|
|
|
377
|
+
| Full (blue) | `Siesa_Logosimbolo_Azul.svg` | Default header, footer |
|
|
378
|
+
| Full (white) | `Siesa_Logosimbolo_Blanco.svg` | Dark theme header |
|
|
379
|
+
| Symbol (blue) | `Siesa_Simbolo_Azul.svg` | Favicon, collapsed sidebar |
|
|
380
|
+
| Symbol (white) | `Siesa_Simbolo_Blanco.svg` | Dark theme favicon |
|
|
381
|
+
|
|
382
|
+
**Asset locations:**
|
|
383
|
+
- Build-time: `assets/images/logos/`
|
|
384
|
+
- Runtime public: `/images/logos/`
|
|
385
|
+
- Min size (full logo): `120px`
|
|
386
|
+
- Min size (symbol): `24px`
|
|
387
|
+
- Format: SVG always
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## 4. Layout & Navigation Patterns
|
|
392
|
+
|
|
393
|
+
### 4.1 App Shell Structure
|
|
394
|
+
|
|
395
|
+
```
|
|
396
|
+
┌──────────────────────────────────────────────────────┐
|
|
397
|
+
│ TopNav (bg-white / dark:bg-slate-900) │
|
|
398
|
+
│ border-b border-slate-200 / dark:border-slate-700 │
|
|
399
|
+
├──────────────┬───────────────────────────────────────┤
|
|
400
|
+
│ │ │
|
|
401
|
+
│ Sidebar │ Main Content │
|
|
402
|
+
│ (w-64) │ (flex-1 overflow-auto p-6) │
|
|
403
|
+
│ │ │
|
|
404
|
+
│ bg-slate-50 │ bg-white / dark:bg-slate-950 │
|
|
405
|
+
│ dark: │ │
|
|
406
|
+
│ bg-slate-900│ │
|
|
407
|
+
│ │ │
|
|
408
|
+
└──────────────┴───────────────────────────────────────┘
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
```tsx
|
|
412
|
+
// Protected app layout — routes/_app.tsx
|
|
413
|
+
function AppLayout() {
|
|
414
|
+
return (
|
|
415
|
+
<div className="flex h-screen bg-white dark:bg-slate-950">
|
|
416
|
+
<Sidebar />
|
|
417
|
+
<div className="flex-1 flex flex-col overflow-hidden">
|
|
418
|
+
<TopNav />
|
|
419
|
+
<main className="flex-1 overflow-auto p-6">
|
|
420
|
+
<Outlet />
|
|
421
|
+
</main>
|
|
422
|
+
</div>
|
|
423
|
+
</div>
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### 4.2 Route Layout Conventions (TanStack Router)
|
|
429
|
+
|
|
430
|
+
| Prefix | Effect | Example |
|
|
431
|
+
|--------|--------|---------|
|
|
432
|
+
| `_` | Pathless layout (no URL segment) | `_app.tsx` → protected layout |
|
|
433
|
+
| `.` | Flat routing | `orders.$id.tsx` → `/orders/:id` |
|
|
434
|
+
| `-` | Ignored by router (colocated files) | `-components/` |
|
|
435
|
+
| `$` | Dynamic parameter | `$orderId.tsx` → `:orderId` |
|
|
436
|
+
| `__` | Root | `__root.tsx` only |
|
|
437
|
+
|
|
438
|
+
### 4.3 Page Structure
|
|
439
|
+
|
|
440
|
+
```tsx
|
|
441
|
+
// Standard page layout
|
|
442
|
+
<div className="space-y-6">
|
|
443
|
+
{/* Page header */}
|
|
444
|
+
<div className="flex items-center justify-between">
|
|
445
|
+
<div>
|
|
446
|
+
<h1 className="text-2xl font-semibold text-zinc-900 dark:text-zinc-50">
|
|
447
|
+
{pageTitle}
|
|
448
|
+
</h1>
|
|
449
|
+
<p className="text-sm text-gray-500 dark:text-gray-400">
|
|
450
|
+
{pageDescription}
|
|
451
|
+
</p>
|
|
452
|
+
</div>
|
|
453
|
+
<div className="flex gap-2">
|
|
454
|
+
{/* Actions */}
|
|
455
|
+
</div>
|
|
456
|
+
</div>
|
|
457
|
+
|
|
458
|
+
{/* Page content */}
|
|
459
|
+
<div className="bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-slate-700">
|
|
460
|
+
{/* Content */}
|
|
461
|
+
</div>
|
|
462
|
+
</div>
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
---
|
|
466
|
+
|
|
467
|
+
## 5. Interaction & Feedback Patterns
|
|
468
|
+
|
|
469
|
+
### 5.1 Loading States — Skeleton Screens
|
|
470
|
+
|
|
471
|
+
| Library | Version |
|
|
472
|
+
|---------|---------|
|
|
473
|
+
| `react-loading-skeleton` | `^3.4.0` |
|
|
474
|
+
|
|
475
|
+
```yaml
|
|
476
|
+
skeleton_config:
|
|
477
|
+
light:
|
|
478
|
+
base: "theme('colors.slate.200')"
|
|
479
|
+
highlight: "theme('colors.slate.100')"
|
|
480
|
+
dark:
|
|
481
|
+
base: "theme('colors.slate.700')"
|
|
482
|
+
highlight: "theme('colors.slate.600')"
|
|
483
|
+
border_radius: "rounded-md"
|
|
484
|
+
animation_duration: "1.5s"
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
**Rule:** Always use skeleton screens (not spinners) for content placeholders. Spinners are only acceptable for action confirmation (button submit states).
|
|
488
|
+
|
|
489
|
+
### 5.2 Toast Notifications
|
|
490
|
+
|
|
491
|
+
| Library | Placement | Stack |
|
|
492
|
+
|---------|-----------|-------|
|
|
493
|
+
| `sonner` | `bottom-right` | Max 3 visible |
|
|
494
|
+
|
|
495
|
+
```tsx
|
|
496
|
+
// Standard notification messages (Spanish required)
|
|
497
|
+
toast.success('Datos guardados correctamente');
|
|
498
|
+
toast.error('No se pudo completar la operación. Intenta de nuevo.');
|
|
499
|
+
toast.warning('Este cambio afectará registros relacionados.');
|
|
500
|
+
toast.info('El proceso puede tardar unos minutos.');
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### 5.3 Error States
|
|
504
|
+
|
|
505
|
+
```tsx
|
|
506
|
+
// Inline field error
|
|
507
|
+
<p className="text-sm text-red-600 dark:text-red-400 mt-1">
|
|
508
|
+
{error.message}
|
|
509
|
+
</p>
|
|
510
|
+
|
|
511
|
+
// Error boundary fallback
|
|
512
|
+
<div className="flex flex-col items-center justify-center p-8 text-center">
|
|
513
|
+
<h2 className="text-lg font-semibold text-red-600 dark:text-red-400">
|
|
514
|
+
Algo salió mal
|
|
515
|
+
</h2>
|
|
516
|
+
<p className="text-sm text-gray-500 dark:text-gray-400 mt-2">
|
|
517
|
+
Por favor, intenta recargar la página
|
|
518
|
+
</p>
|
|
519
|
+
<Button variant="outline" className="mt-4" onClick={() => window.location.reload()}>
|
|
520
|
+
Recargar
|
|
521
|
+
</Button>
|
|
522
|
+
</div>
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
### 5.4 Empty States
|
|
526
|
+
|
|
527
|
+
```tsx
|
|
528
|
+
// Empty list state
|
|
529
|
+
<div className="flex flex-col items-center justify-center py-12 text-center">
|
|
530
|
+
<IconComponent className="w-12 h-12 text-gray-300 dark:text-gray-600 mb-4" />
|
|
531
|
+
<h3 className="text-base font-medium text-gray-900 dark:text-gray-100">
|
|
532
|
+
No hay {entityNamePlural} registrados
|
|
533
|
+
</h3>
|
|
534
|
+
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">
|
|
535
|
+
Comienza creando el primer registro.
|
|
536
|
+
</p>
|
|
537
|
+
</div>
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### 5.5 Confirmation Dialogs
|
|
541
|
+
|
|
542
|
+
- Use modal dialogs for destructive or irreversible actions
|
|
543
|
+
- Title must state the action explicitly: "¿Eliminar {entidad}?"
|
|
544
|
+
- Description must state the consequence: "Esta acción no se puede deshacer."
|
|
545
|
+
- Destructive button: `text-red-600` or `variant="destructive"`
|
|
546
|
+
- Cancel always on the left, confirm on the right
|
|
547
|
+
|
|
548
|
+
---
|
|
549
|
+
|
|
550
|
+
## 6. Accessibility Standards (WCAG 2.1 AA)
|
|
551
|
+
|
|
552
|
+
### 6.1 Color Contrast
|
|
553
|
+
|
|
554
|
+
| Context | Minimum Ratio |
|
|
555
|
+
|---------|--------------|
|
|
556
|
+
| Normal text (< 18px) | 4.5:1 |
|
|
557
|
+
| Large text (≥ 18px or bold ≥ 14px) | 3.1:1 |
|
|
558
|
+
| UI components, icons | 3:1 |
|
|
559
|
+
|
|
560
|
+
### 6.2 Focus Indicators
|
|
561
|
+
|
|
562
|
+
```css
|
|
563
|
+
/* Standard focus ring */
|
|
564
|
+
focus-visible:ring-2 focus-visible:ring-primary-600 focus-visible:ring-offset-2
|
|
565
|
+
dark:focus-visible:ring-primary-400
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
**Rules:**
|
|
569
|
+
- Never use `outline: none` without a custom focus indicator
|
|
570
|
+
- Focus indicators must be visible in both light and dark modes
|
|
571
|
+
- Minimum focus ring width: `2px`
|
|
572
|
+
|
|
573
|
+
### 6.3 Touch & Interactive Targets
|
|
574
|
+
|
|
575
|
+
| Requirement | Value |
|
|
576
|
+
|-------------|-------|
|
|
577
|
+
| Minimum touch target | 44px × 44px |
|
|
578
|
+
| Minimum font size | 16px |
|
|
579
|
+
| Maximum line length | 75ch |
|
|
580
|
+
|
|
581
|
+
### 6.4 Semantic HTML
|
|
582
|
+
|
|
583
|
+
```tsx
|
|
584
|
+
// ✅ Correct — semantic HTML first
|
|
585
|
+
<nav aria-label="Navegación principal">
|
|
586
|
+
<main>
|
|
587
|
+
<article>
|
|
588
|
+
<section>
|
|
589
|
+
<aside>
|
|
590
|
+
<header>
|
|
591
|
+
<footer>
|
|
592
|
+
|
|
593
|
+
// ✅ Correct — ARIA only when necessary
|
|
594
|
+
<div role="tabpanel" aria-labelledby="tab-products" aria-expanded={isOpen}>
|
|
595
|
+
|
|
596
|
+
// ❌ Incorrect — redundant ARIA
|
|
597
|
+
<button role="button" aria-label="button">Guardar</button>
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
### 6.5 Accessibility Requirements Checklist
|
|
601
|
+
|
|
602
|
+
| Requirement | Standard |
|
|
603
|
+
|-------------|---------|
|
|
604
|
+
| Landmarks | `<nav>`, `<main>`, `<header>`, `<footer>` on every page |
|
|
605
|
+
| Heading hierarchy | Single `<h1>`, descending order, no skips |
|
|
606
|
+
| Form labels | `<label>` associated with every input via `htmlFor` |
|
|
607
|
+
| Alt text | All images with `alt` attribute (empty `alt=""` for decorative) |
|
|
608
|
+
| Skip links | "Saltar al contenido" link before main navigation |
|
|
609
|
+
| Keyboard nav | All interactions reachable and operable via keyboard |
|
|
610
|
+
| Screen reader | Test with VoiceOver / NVDA on critical flows |
|
|
611
|
+
|
|
612
|
+
---
|
|
613
|
+
|
|
614
|
+
## 7. Language Standards
|
|
615
|
+
|
|
616
|
+
### 7.1 Critical Rule — Spanish for All User-Visible Text
|
|
617
|
+
|
|
618
|
+
**All text visible to the end user MUST be in Spanish.**
|
|
619
|
+
|
|
620
|
+
| Content Type | Language |
|
|
621
|
+
|-------------|----------|
|
|
622
|
+
| UI labels, buttons, forms | ✅ Spanish |
|
|
623
|
+
| Error messages, toasts | ✅ Spanish |
|
|
624
|
+
| API response messages shown to user | ✅ Spanish |
|
|
625
|
+
| Validation feedback | ✅ Spanish |
|
|
626
|
+
| Code (variables, functions, classes) | ✅ English |
|
|
627
|
+
| Technical logs, comments, git commits | ✅ English |
|
|
628
|
+
| Developer documentation | ✅ English |
|
|
629
|
+
|
|
630
|
+
```tsx
|
|
631
|
+
// ✅ Correct
|
|
632
|
+
<Button>Guardar</Button>
|
|
633
|
+
toast.success('Datos guardados correctamente');
|
|
634
|
+
|
|
635
|
+
// ❌ Incorrect
|
|
636
|
+
<Button>Save</Button>
|
|
637
|
+
toast.error('Failed to save');
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
### 7.2 Standard Message Tokens
|
|
641
|
+
|
|
642
|
+
```typescript
|
|
643
|
+
// shared/constants/messages.ts
|
|
644
|
+
export const MESSAGES = {
|
|
645
|
+
SUCCESS: {
|
|
646
|
+
SAVED: 'Datos guardados correctamente',
|
|
647
|
+
DELETED: 'Elemento eliminado correctamente',
|
|
648
|
+
UPDATED: 'Información actualizada',
|
|
649
|
+
},
|
|
650
|
+
ERROR: {
|
|
651
|
+
GENERIC: 'Ha ocurrido un error. Por favor, intenta de nuevo',
|
|
652
|
+
NOT_FOUND: 'El recurso solicitado no fue encontrado',
|
|
653
|
+
UNAUTHORIZED: 'No tienes permisos para realizar esta acción',
|
|
654
|
+
VALIDATION: 'Por favor, verifica los datos ingresados',
|
|
655
|
+
CONFLICT: 'No se puede completar la operación debido a un conflicto',
|
|
656
|
+
},
|
|
657
|
+
LOADING: {
|
|
658
|
+
DEFAULT: 'Cargando...',
|
|
659
|
+
SAVING: 'Guardando...',
|
|
660
|
+
PROCESSING: 'Procesando...',
|
|
661
|
+
DELETING: 'Eliminando...',
|
|
662
|
+
},
|
|
663
|
+
CONFIRM: {
|
|
664
|
+
DELETE: '¿Estás seguro de que deseas eliminar este registro?',
|
|
665
|
+
UNSAVED: 'Tienes cambios sin guardar. ¿Deseas descartarlos?',
|
|
666
|
+
},
|
|
667
|
+
} as const;
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
---
|
|
671
|
+
|
|
672
|
+
## 8. Performance Targets
|
|
673
|
+
|
|
674
|
+
### 8.1 Bundle Budgets
|
|
675
|
+
|
|
676
|
+
| Scope | Maximum |
|
|
677
|
+
|-------|---------|
|
|
678
|
+
| Initial bundle | < 500 KB gzipped |
|
|
679
|
+
| Per-route chunk | < 200 KB gzipped |
|
|
680
|
+
| Individual component | < 100 KB gzipped |
|
|
681
|
+
|
|
682
|
+
### 8.2 Core Web Vitals Targets
|
|
683
|
+
|
|
684
|
+
| Metric | Target |
|
|
685
|
+
|--------|--------|
|
|
686
|
+
| FCP (First Contentful Paint) | < 1.5s |
|
|
687
|
+
| LCP (Largest Contentful Paint) | < 2.5s |
|
|
688
|
+
| CLS (Cumulative Layout Shift) | < 0.1 |
|
|
689
|
+
| TTI (Time to Interactive) | < 3s |
|
|
690
|
+
|
|
691
|
+
### 8.3 Optimization Techniques
|
|
692
|
+
|
|
693
|
+
| Technique | Implementation |
|
|
694
|
+
|-----------|---------------|
|
|
695
|
+
| Code splitting | Automatic per route (TanStack Router) |
|
|
696
|
+
| Lazy loading | `React.lazy()` for non-critical components |
|
|
697
|
+
| Tree shaking | Specific imports; no barrel exports in `shared/` |
|
|
698
|
+
| Memoization | `React.memo` for expensive components, `useMemo`/`useCallback` |
|
|
699
|
+
| Virtual scrolling | For lists > 200 items |
|
|
700
|
+
| Image lazy loading | `loading="lazy"` on all non-critical images |
|
|
701
|
+
|
|
702
|
+
---
|
|
703
|
+
|
|
704
|
+
## 9. State Management Patterns
|
|
705
|
+
|
|
706
|
+
### 9.1 State Type Decision Matrix
|
|
707
|
+
|
|
708
|
+
| State Type | When to Use | Technology |
|
|
709
|
+
|-----------|-------------|------------|
|
|
710
|
+
| Server state | API data, paginated lists, caches | TanStack Query |
|
|
711
|
+
| Global client state | Auth, theme, cart, app settings | Zustand |
|
|
712
|
+
| Feature/domain state | Domain-specific UI and data | Zustand (feature store) |
|
|
713
|
+
| Local component state | Form toggles, UI open/close | `useState` / `useReducer` |
|
|
714
|
+
| URL state | Filters, pagination, selected tab | TanStack Router search params |
|
|
715
|
+
|
|
716
|
+
### 9.2 TanStack Query Defaults
|
|
717
|
+
|
|
718
|
+
```typescript
|
|
719
|
+
const queryClient = new QueryClient({
|
|
720
|
+
defaultOptions: {
|
|
721
|
+
queries: {
|
|
722
|
+
staleTime: 60 * 1000, // 1 minute
|
|
723
|
+
refetchOnWindowFocus: false,
|
|
724
|
+
retry: 1,
|
|
725
|
+
},
|
|
726
|
+
},
|
|
727
|
+
});
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
### 9.3 Zustand Store Structure
|
|
731
|
+
|
|
732
|
+
```typescript
|
|
733
|
+
// Feature store pattern
|
|
734
|
+
interface FeatureState {
|
|
735
|
+
// Domain data
|
|
736
|
+
items: Item[];
|
|
737
|
+
selectedItem: Item | null;
|
|
738
|
+
|
|
739
|
+
// UI state
|
|
740
|
+
loading: boolean;
|
|
741
|
+
error: string | null;
|
|
742
|
+
|
|
743
|
+
// Actions
|
|
744
|
+
setSelectedItem: (item: Item | null) => void;
|
|
745
|
+
clearError: () => void;
|
|
746
|
+
}
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
---
|
|
750
|
+
|
|
751
|
+
## 10. Security & Input Handling
|
|
752
|
+
|
|
753
|
+
| Risk | Mitigation |
|
|
754
|
+
|------|------------|
|
|
755
|
+
| XSS | Never use `dangerouslySetInnerHTML`; sanitize all dynamic content |
|
|
756
|
+
| Sensitive data | Never store tokens/credentials in `localStorage` unencrypted |
|
|
757
|
+
| API keys | Never in frontend code — use server-side env variables only |
|
|
758
|
+
| User input | Always validate with Zod on submit before sending to API |
|
|
759
|
+
|
|
760
|
+
---
|
|
761
|
+
|
|
762
|
+
## 11. Naming Conventions
|
|
763
|
+
|
|
764
|
+
| Element | Convention | Example |
|
|
765
|
+
|---------|-----------|---------|
|
|
766
|
+
| Components | PascalCase | `ProductCard`, `UserList` |
|
|
767
|
+
| Component files | kebab-case | `product-card.tsx`, `user-list.tsx` |
|
|
768
|
+
| Directories | kebab-case | `user-management/` |
|
|
769
|
+
| Hooks | `use` + camelCase | `useProductStore`, `useAuth` |
|
|
770
|
+
| Zustand stores | `use{Feature}Store` | `useCartStore` |
|
|
771
|
+
| `data-testid` | kebab-case | `data-testid="product-card"` |
|
|
772
|
+
| CSS custom props | `--kebab-case` | `--color-primary-600` |
|
|
773
|
+
| Constants | SCREAMING_SNAKE_CASE | `MAX_PAGE_SIZE` |
|
|
774
|
+
|
|
775
|
+
---
|
|
776
|
+
|
|
777
|
+
## 12. Implementation Checklist
|
|
778
|
+
|
|
779
|
+
### New Frontend Project
|
|
780
|
+
|
|
781
|
+
```markdown
|
|
782
|
+
## Design System
|
|
783
|
+
- [ ] TailwindCSS 4+ with extended brand color scales (primary, secondary, tertiary)
|
|
784
|
+
- [ ] Dark mode configured: class-based on `html`, `next-themes`
|
|
785
|
+
- [ ] Inter_18pt fonts (Light, Regular, Bold) loaded via `globals.css`
|
|
786
|
+
- [ ] Siesa logos in `assets/images/logos/` (4 SVG variants)
|
|
787
|
+
- [ ] `react-loading-skeleton` installed and configured (light/dark themes)
|
|
788
|
+
- [ ] `sonner` Toaster at `bottom-right` in root layout
|
|
789
|
+
|
|
790
|
+
## Component Library
|
|
791
|
+
- [ ] `siesa-ui-kit` installed and verified for required components
|
|
792
|
+
- [ ] shadcn/ui added only for components not in siesa-ui-kit
|
|
793
|
+
- [ ] `MasterCrud` used for all master data management screens
|
|
794
|
+
- [ ] `LookupField` with `type: 'lookup'` for all backend record selectors
|
|
795
|
+
- [ ] `React Hook Form` + `Zod` for all form validation
|
|
796
|
+
|
|
797
|
+
## Layout
|
|
798
|
+
- [ ] `_app.tsx` layout: `flex h-screen`, Sidebar + TopNav + main
|
|
799
|
+
- [ ] `_auth.tsx` layout: public pages (login, etc.)
|
|
800
|
+
- [ ] All pages use semantic HTML (`<main>`, `<nav>`, `<header>`)
|
|
801
|
+
- [ ] Error boundaries wrapping each major feature section
|
|
802
|
+
|
|
803
|
+
## Accessibility
|
|
804
|
+
- [ ] WCAG 2.1 AA contrast ratios verified (4.5:1 normal, 3.1:1 large)
|
|
805
|
+
- [ ] Focus indicators visible (`ring-2 ring-primary-600`)
|
|
806
|
+
- [ ] Touch targets ≥ 44px
|
|
807
|
+
- [ ] All images have `alt` attributes
|
|
808
|
+
- [ ] Skip link "Saltar al contenido" before navigation
|
|
809
|
+
- [ ] Form inputs have associated `<label>` via `htmlFor`
|
|
810
|
+
- [ ] Single `<h1>` per page, headings in descending order
|
|
811
|
+
|
|
812
|
+
## Language & Content
|
|
813
|
+
- [ ] All user-visible text in Spanish
|
|
814
|
+
- [ ] `MESSAGES` constant file for standard messages
|
|
815
|
+
- [ ] No mixed-language text in UI strings
|
|
816
|
+
|
|
817
|
+
## Performance
|
|
818
|
+
- [ ] Bundle size < 500KB gzipped (initial)
|
|
819
|
+
- [ ] Skeleton screens for all async loading states
|
|
820
|
+
- [ ] `React.memo` on expensive list items
|
|
821
|
+
- [ ] Images have `loading="lazy"` where applicable
|
|
822
|
+
- [ ] Route-based code splitting (automatic with TanStack Router)
|
|
823
|
+
|
|
824
|
+
## State Management
|
|
825
|
+
- [ ] TanStack Query for all server state (API calls)
|
|
826
|
+
- [ ] Zustand stores per feature (not global monolith)
|
|
827
|
+
- [ ] URL params for filters and pagination state
|
|
828
|
+
|
|
829
|
+
## Security
|
|
830
|
+
- [ ] No `dangerouslySetInnerHTML` without sanitization
|
|
831
|
+
- [ ] No credentials or secrets in frontend code
|
|
832
|
+
- [ ] All user inputs validated with Zod before API submission
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
---
|
|
836
|
+
|
|
837
|
+
_This document is the base UX/UI specification. It is automatically applied when initializing new projects. Extend it using the `/create-ux-design` workflow for project-specific decisions._
|