lkd-web-kit 0.10.5 → 0.10.7
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 +2 -2
- package/scripts/install-agent-skills.mjs +2 -2
- package/src/distributed-skills/create-modal-component/SKILL.md +168 -0
- package/src/distributed-skills/create-modal-component/agents/openai.yaml +4 -0
- package/src/distributed-skills/create-nextjs-page/SKILL.md +104 -0
- package/src/distributed-skills/create-nextjs-page/agents/openai.yaml +3 -0
- package/src/distributed-skills/create-svg-icon/SKILL.md +41 -0
- package/src/distributed-skills/create-svg-icon/agents/openai.yaml +3 -0
- package/src/distributed-skills/create-table-component/SKILL.md +189 -0
- package/src/distributed-skills/create-table-component/agents/openai.yaml +3 -0
- package/src/distributed-skills/rhf-lkd-forms/SKILL.md +186 -0
- package/.agents/skills/mantine-combobox/SKILL.md +0 -73
- package/.agents/skills/mantine-combobox/references/api.md +0 -199
- package/.agents/skills/mantine-combobox/references/patterns.md +0 -279
- package/.agents/skills/mantine-custom-components/SKILL.md +0 -112
- package/.agents/skills/mantine-custom-components/references/api.md +0 -407
- package/.agents/skills/mantine-custom-components/references/patterns.md +0 -431
- package/.agents/skills/publish-lkd-web-kit/SKILL.md +0 -165
- package/.agents/skills/publish-lkd-web-kit/agents/openai.yaml +0 -4
- package/.agents/skills/publish-lkd-web-kit/references/npm-trusted-publishing.md +0 -82
- package/.agents/skills/update-lkd-dependencies/SKILL.md +0 -88
- package/.agents/skills/update-lkd-dependencies/agents/openai.yaml +0 -4
- package/.agents/skills/update-lkd-dependencies/scripts/collect-npm-metadata.mjs +0 -73
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lkd-web-kit",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.7",
|
|
4
4
|
"description": "A template for creating React component libraries with Vite.",
|
|
5
5
|
"author": "LKD",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"engines": {
|
|
27
27
|
"node": ">=22.12.0"
|
|
28
28
|
},
|
|
29
|
-
"files": ["dist", "
|
|
29
|
+
"files": ["dist", "src/distributed-skills", "scripts/install-agent-skills.mjs"],
|
|
30
30
|
"scripts": {
|
|
31
31
|
"test": "vitest run --passWithNoTests",
|
|
32
32
|
"test:watch": "vitest",
|
|
@@ -4,11 +4,11 @@ import { dirname, join } from 'node:path'
|
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
5
|
|
|
6
6
|
const scriptDir = dirname(fileURLToPath(import.meta.url))
|
|
7
|
-
const source = join(scriptDir, '..', '
|
|
7
|
+
const source = join(scriptDir, '..', 'src', 'distributed-skills')
|
|
8
8
|
const target = join(process.cwd(), '.agents', 'skills')
|
|
9
9
|
|
|
10
10
|
if (!existsSync(source)) {
|
|
11
|
-
throw new Error(`No se encontraron skills en ${source}`)
|
|
11
|
+
throw new Error(`No se encontraron skills distribuibles en ${source}`)
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
mkdirSync(target, { recursive: true })
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-modal-component
|
|
3
|
+
description: Crear modales en proyectos internos con el modal manager global de lkd-web-kit. Usar cuando Codex deba agregar, modificar o registrar modales bajo src/components/modals, envolverlos con withModalManager, elegir entre un modal interno del proyecto o Modal de Mantine, organizar el modal en carpeta propia, tipar props para showModal, actualizar dynamicModals, MyModalManagerProvider, module augmentation o loadModals.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Crear Modal Component
|
|
7
|
+
|
|
8
|
+
Usar este flujo para modales abiertos con `useModalManager().showModal(...)`.
|
|
9
|
+
|
|
10
|
+
## Lectura Obligatoria
|
|
11
|
+
|
|
12
|
+
1. Leer `docs/lkd-web-kit.md` y `docs/local-components.md`.
|
|
13
|
+
2. Leer `src/components/modals/index.ts`.
|
|
14
|
+
3. Leer `src/contexts/MyModalManagerContext/index.tsx`.
|
|
15
|
+
4. Leer `src/types/lkd-web-kit.d.ts`.
|
|
16
|
+
5. Buscar si existe un modal interno en `src/components/ui` o componentes compartidos antes de importar `Modal` de Mantine.
|
|
17
|
+
6. Leer un modal existente del mismo dominio en `src/components/modals/{dominio}`.
|
|
18
|
+
7. Si se toca `next/dynamic` o lazy loading, leer `node_modules/next/dist/docs/01-app/02-guides/lazy-loading.md`.
|
|
19
|
+
|
|
20
|
+
## Arquitectura Actual
|
|
21
|
+
|
|
22
|
+
- `lkd-web-kit` exporta `ModalManagerProvider`, `useModalManager`, `withModalManager`, `ModalRegistryItem`, `ModalKey` y tipos relacionados.
|
|
23
|
+
- El proyecto no usa `src/contexts/ModalManagerContext`; no recrearlo.
|
|
24
|
+
- `src/contexts/MyModalManagerContext/index.tsx` adapta el provider del kit y le pasa `dynamicModals`.
|
|
25
|
+
- `src/types/lkd-web-kit.d.ts` hace module augmentation para que `showModal` infiera keys y props desde `dynamicModals`.
|
|
26
|
+
- `src/app/PageProviders.tsx` envuelve la app con `MyModalManagerProvider` y propaga `loadModals`.
|
|
27
|
+
|
|
28
|
+
## Estructura
|
|
29
|
+
|
|
30
|
+
- Crear modales en `src/components/modals/{dominio}/{NombreModal}/index.tsx` cuando tengan formulario, schema, hooks internos, assets o subcomponentes.
|
|
31
|
+
- Usar archivo plano `src/components/modals/{dominio}/{NombreModal}.tsx` solo para modales triviales de una pieza, siguiendo el patron existente.
|
|
32
|
+
- Colocar piezas privadas junto al modal: `schema.ts`, `helpers.ts`, subcomponentes o assets dentro de la misma carpeta.
|
|
33
|
+
- No mover logica compartida a `src/components/ui` salvo que ya exista mas de un consumidor real.
|
|
34
|
+
|
|
35
|
+
## Implementacion
|
|
36
|
+
|
|
37
|
+
1. Definir props exportadas si el modal recibe datos desde `showModal`:
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
export interface MyModalProps {
|
|
41
|
+
itemId: string;
|
|
42
|
+
onSuccess?: () => void;
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
2. Envolver el componente con `withModalManager<Props>`.
|
|
47
|
+
3. Recibir `modalProps` desde el wrapper y pasarlo al modal raiz.
|
|
48
|
+
4. Antes de usar `Modal` de `@mantine/core`, corroborar si el proyecto tiene un modal interno que ya envuelve Mantine y usarlo si cubre el caso.
|
|
49
|
+
5. Usar clases Tailwind para spacing, radius, shadow, margin y padding; evitar props Mantine equivalentes si `className` resuelve el caso.
|
|
50
|
+
6. Usar wrappers locales del proyecto o `lkd-web-kit` antes que primitivas Mantine directas.
|
|
51
|
+
7. No usar `any`.
|
|
52
|
+
|
|
53
|
+
Modal base con modal interno:
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import { withModalManager } from "lkd-web-kit";
|
|
57
|
+
import ButtonAE from "src/components/ui/ButtonAE";
|
|
58
|
+
import InternalModal from "src/components/ui/InternalModal";
|
|
59
|
+
|
|
60
|
+
export interface MyModalProps {
|
|
61
|
+
onConfirm?: () => void;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const MyModal = withModalManager<MyModalProps>(
|
|
65
|
+
({ onConfirm, modalProps }) => {
|
|
66
|
+
return (
|
|
67
|
+
<InternalModal {...modalProps} size="lg">
|
|
68
|
+
<InternalModal.Header>Titulo</InternalModal.Header>
|
|
69
|
+
<InternalModal.Body className="grid gap-4">Contenido</InternalModal.Body>
|
|
70
|
+
<InternalModal.Footer>
|
|
71
|
+
<ButtonAE
|
|
72
|
+
onClick={() => {
|
|
73
|
+
onConfirm?.();
|
|
74
|
+
modalProps.onClose?.();
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
Guardar
|
|
78
|
+
</ButtonAE>
|
|
79
|
+
</InternalModal.Footer>
|
|
80
|
+
</InternalModal>
|
|
81
|
+
);
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
export default MyModal;
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Fallback con Mantine, solo si no hay modal interno aplicable:
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
import { Modal } from "@mantine/core";
|
|
92
|
+
import { withModalManager } from "lkd-web-kit";
|
|
93
|
+
|
|
94
|
+
const MyModal = withModalManager(({ modalProps }) => (
|
|
95
|
+
<Modal {...modalProps} title="Titulo">
|
|
96
|
+
Contenido
|
|
97
|
+
</Modal>
|
|
98
|
+
));
|
|
99
|
+
|
|
100
|
+
export default MyModal;
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Registro
|
|
104
|
+
|
|
105
|
+
1. Exportar las props publicas desde el modal.
|
|
106
|
+
2. Importar el tipo de props en `src/components/modals/index.ts`:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import type { MyModalProps } from "./domain/MyModal";
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
3. Registrar con `dynamicModal<Props>("domain/MyModal")`:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
"domain.myModal": dynamicModal<MyModalProps>("domain/MyModal"),
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
4. Usar una key estable con formato `{dominio}.{accion}`.
|
|
119
|
+
5. No tocar `src/types/lkd-web-kit.d.ts` si `dynamicModals` sigue siendo la fuente unica. La augmentacion ya apunta a `typeof dynamicModals`.
|
|
120
|
+
6. Si se cambia la forma del registro, mantener `ModalRegistryItem<T>` y que cada item tenga `component`, `load` y `path`.
|
|
121
|
+
|
|
122
|
+
## Contexto y Precarga
|
|
123
|
+
|
|
124
|
+
- Para rutas que ya usan `PageProviders`, pasar `loadModals` ahi:
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
<PageProviders loadModals={["domain.myModal"]}>{children}</PageProviders>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
- `loadModals` acepta una key exacta o un prefijo, por ejemplo `"development"` para precargar todos los modales cuyo key empieza con ese prefijo.
|
|
131
|
+
- Para layouts que no usan `PageProviders`, envolver el arbol con `MyModalManagerProvider`:
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
import { MyModalManagerProvider } from "src/contexts/MyModalManagerContext";
|
|
135
|
+
|
|
136
|
+
<MyModalManagerProvider loadModals={["domain.myModal"]}>
|
|
137
|
+
{children}
|
|
138
|
+
</MyModalManagerProvider>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
- Evitar providers anidados salvo que haya una razon real: un provider interno aisla estado y puede cerrar o reemplazar modales inesperadamente.
|
|
142
|
+
- Invocar siempre con `useModalManager` desde `lkd-web-kit`, nunca importando el modal directo desde una vista:
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
import { useModalManager } from "lkd-web-kit";
|
|
146
|
+
|
|
147
|
+
const { showModal, closeModal } = useModalManager();
|
|
148
|
+
showModal("domain.myModal", { itemId: "123" });
|
|
149
|
+
closeModal("domain.myModal");
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Formularios Dentro de Modales
|
|
153
|
+
|
|
154
|
+
- Usar la skill `rhf-lkd-forms` si el modal contiene formulario.
|
|
155
|
+
- Pasar `onSuccess={modalProps.onClose}` o cerrar en el submit exitoso.
|
|
156
|
+
- En errores de API, mostrar la alerta esperada y luego hacer `throw error`.
|
|
157
|
+
- No poner mensajes textuales en reglas base de Zod.
|
|
158
|
+
|
|
159
|
+
## Checklist
|
|
160
|
+
|
|
161
|
+
- El modal exporta default envuelto con `withModalManager`.
|
|
162
|
+
- Las props publicas estan exportadas y registradas en `dynamicModals`.
|
|
163
|
+
- `showModal("key", props)` queda tipado sin `any`.
|
|
164
|
+
- `useModalManager` se importa desde `lkd-web-kit`.
|
|
165
|
+
- La ruta esta bajo `PageProviders` o `MyModalManagerProvider`.
|
|
166
|
+
- `loadModals` se agrega solo cuando conviene precargar.
|
|
167
|
+
- Se reutiliza el modal interno del proyecto salvo necesidad concreta de Mantine directo.
|
|
168
|
+
- Se ejecuta una verificacion focalizada si el cambio no es trivial.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-nextjs-page
|
|
3
|
+
description: Crear o refactorizar paginas de Next.js App Router manteniendo app limpio y delegando la UI a componentes de pagina en src/components/pages. Usar cuando Codex deba crear page.tsx, mover UI fuera de app, organizar subcomponentes privados de una pagina, envolver paginas con PageProviders o normalizar nombres/rutas de componentes Page en proyectos que consumen lkd-web-kit.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Create Next.js Page
|
|
7
|
+
|
|
8
|
+
Usar este skill para crear o refactorizar paginas de Next.js App Router en proyectos que usan `lkd-web-kit`.
|
|
9
|
+
|
|
10
|
+
## Lectura obligatoria
|
|
11
|
+
|
|
12
|
+
1. Leer la documentacion relevante de Next.js antes de tocar rutas. Si existe `node_modules/next/dist/docs/`, usarla como fuente local.
|
|
13
|
+
2. Leer la documentacion local del proyecto sobre `lkd-web-kit`, componentes compartidos y patrones de UI si existe.
|
|
14
|
+
3. Revisar una pagina cercana antes de editar para respetar imports, providers, carga de datos, traducciones y convenciones del proyecto.
|
|
15
|
+
|
|
16
|
+
## Regla principal
|
|
17
|
+
|
|
18
|
+
- Mantener `app` como capa de routing de Next.js.
|
|
19
|
+
- En `app`, dejar solo `layout.tsx`, `page.tsx` y archivos especiales de Next.js cuando correspondan: `not-found.tsx`, `loading.tsx`, `error.tsx`, `route.ts`, `template.tsx`, `default.tsx` o metadata files.
|
|
20
|
+
- No colocar vistas, subcomponentes, hooks privados, helpers de UI, schemas, columnas de tabla ni componentes de pagina dentro de `app`.
|
|
21
|
+
- Colocar la UI real de cada ruta en `src/components/pages`.
|
|
22
|
+
|
|
23
|
+
## Estructura de pagina
|
|
24
|
+
|
|
25
|
+
Para una pagina simple:
|
|
26
|
+
|
|
27
|
+
```txt
|
|
28
|
+
src/app/[locale]/(app-layout)/development/page.tsx
|
|
29
|
+
src/components/pages/app/DevelopmentPage.tsx
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Para una pagina con subcomponentes privados:
|
|
33
|
+
|
|
34
|
+
```txt
|
|
35
|
+
src/app/[locale]/(app-layout)/development/page.tsx
|
|
36
|
+
src/components/pages/app/DevelopmentPage/index.tsx
|
|
37
|
+
src/components/pages/app/DevelopmentPage/Header.tsx
|
|
38
|
+
src/components/pages/app/DevelopmentPage/helpers.ts
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- Usar archivo plano `{PageName}Page.tsx` si el componente es de una pieza.
|
|
42
|
+
- Usar carpeta `{PageName}Page/index.tsx` si hay subcomponentes, helpers, schemas o piezas privadas.
|
|
43
|
+
- Mantener dentro de esa carpeta solo lo privado de esa pagina.
|
|
44
|
+
- Mover a componentes compartidos solo cuando exista reutilizacion real.
|
|
45
|
+
|
|
46
|
+
## PageProviders
|
|
47
|
+
|
|
48
|
+
Cada `page.tsx` debe retornar el componente Page envuelto con `PageProviders`.
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
import PageProviders from "src/app/PageProviders";
|
|
52
|
+
import DevelopmentPage from "src/components/pages/app/DevelopmentPage";
|
|
53
|
+
|
|
54
|
+
const Page = async () => {
|
|
55
|
+
return (
|
|
56
|
+
<PageProviders>
|
|
57
|
+
<DevelopmentPage />
|
|
58
|
+
</PageProviders>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export default Page;
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
- Usar el import real de `PageProviders` del proyecto si difiere del ejemplo.
|
|
66
|
+
- Pasar a `PageProviders` solo las props que existan y sean necesarias en ese proyecto.
|
|
67
|
+
- Mantener en `page.tsx` solo responsabilidades de entrypoint: params, metadata, fetch server-side, redirects/notFound y providers.
|
|
68
|
+
- No duplicar providers dentro del componente de pagina salvo que el proyecto tenga una razon documentada.
|
|
69
|
+
|
|
70
|
+
## Mapeo de area
|
|
71
|
+
|
|
72
|
+
Derivar `{area}` desde el route group principal cuando exista:
|
|
73
|
+
|
|
74
|
+
- `(app-layout)` -> `app`
|
|
75
|
+
- `(admin-layout)` -> `admin`
|
|
76
|
+
- `(home-layout)` -> `home`
|
|
77
|
+
- `(developer-layout)` -> `developer`
|
|
78
|
+
- `(agency-layout)` -> `agency`
|
|
79
|
+
|
|
80
|
+
Regla general:
|
|
81
|
+
|
|
82
|
+
- Si el route group termina en `-layout`, quitar ese sufijo.
|
|
83
|
+
- Si no termina en `-layout`, usar el nombre limpio del group sin parentesis.
|
|
84
|
+
- Si no hay route group claro, usar el primer segmento estable de la ruta.
|
|
85
|
+
- Ignorar `[locale]`, route groups y segmentos dinamicos para elegir el area.
|
|
86
|
+
|
|
87
|
+
## Nombres
|
|
88
|
+
|
|
89
|
+
- Usar PascalCase y sufijo `Page`.
|
|
90
|
+
- Para rutas estaticas, usar el ultimo segmento estable: `(app-layout)/development/page.tsx` -> `DevelopmentPage`.
|
|
91
|
+
- Para rutas dinamicas, usar el ultimo segmento estatico util y agregar `DetailPage`: `development/[development_slug]/page.tsx` -> `DevelopmentDetailPage`.
|
|
92
|
+
- Para rutas dinamicas anidadas, preferir el ultimo segmento estatico util: `development/[development_slug]/property/[property_slug]` -> `PropertyDetailPage`.
|
|
93
|
+
- Si el nombre colisiona o pierde contexto, anteponer el segmento padre: `DevelopmentPropertyDetailPage`.
|
|
94
|
+
- No incluir nombres de route groups, `[locale]` ni nombres de parametros como `Slug` salvo que haga falta para evitar ambiguedad real.
|
|
95
|
+
|
|
96
|
+
## Checklist
|
|
97
|
+
|
|
98
|
+
- `app` queda limpio y sin UI privada.
|
|
99
|
+
- `page.tsx` importa un componente desde `src/components/pages/{area}`.
|
|
100
|
+
- `page.tsx` envuelve siempre con `PageProviders`.
|
|
101
|
+
- Las props de `PageProviders` corresponden al proyecto actual, no a otro consumidor.
|
|
102
|
+
- El componente de pagina no usa `any`.
|
|
103
|
+
- Los subcomponentes privados viven junto a la pagina, no en `app`.
|
|
104
|
+
- Se ejecuta una verificacion focalizada cuando el cambio toca codigo de aplicacion.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-svg-icon
|
|
3
|
+
description: Create SVG icon files in projects that expose SVGs from src/icons. Use when Codex must add a new icon under src/icons from user-provided SVG markup, normalize SVG colors to currentColor for Tailwind text-* styling, and export the icon alias from src/icons/index.ts.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Create SVG Icon
|
|
7
|
+
|
|
8
|
+
Use this skill when the user provides SVG markup and the desired icon name, usually in the form `Icon{Name}`.
|
|
9
|
+
|
|
10
|
+
## Required Workflow
|
|
11
|
+
|
|
12
|
+
1. Read `src/icons/index.ts` before editing.
|
|
13
|
+
2. Determine the component export name from the user input.
|
|
14
|
+
- Expected format: `Icon{Name}`.
|
|
15
|
+
- Keep the export alias with the `Icon` prefix.
|
|
16
|
+
3. Verify the icon does not already exist.
|
|
17
|
+
- Check for the exact alias in `src/icons/index.ts`.
|
|
18
|
+
- Check for the target SVG filename in `src/icons/`.
|
|
19
|
+
- If either exists, cancel the operation and notify the user.
|
|
20
|
+
4. Build the SVG filename.
|
|
21
|
+
- Remove the leading `Icon` prefix.
|
|
22
|
+
- Convert the remaining name to kebab-case.
|
|
23
|
+
- Save it as `src/icons/{name}.svg`.
|
|
24
|
+
- Example: `IconBuildingTower` -> `src/icons/building-tower.svg`.
|
|
25
|
+
5. Normalize the provided SVG.
|
|
26
|
+
- Replace every explicit visual color value with `currentColor`.
|
|
27
|
+
- Include colors expressed as hex, rgb(), rgba(), hsl(), hsla(), named colors, or inline style values.
|
|
28
|
+
- Apply this to attributes such as `fill`, `stroke`, `color`, `stop-color`, and CSS declarations inside `style`.
|
|
29
|
+
- Preserve `fill="none"`, `stroke="none"`, `currentColor`, `transparent`, masks, clipping, sizing, viewBox, paths, and structural SVG attributes.
|
|
30
|
+
- Do not convert opacity values to `currentColor`.
|
|
31
|
+
6. Create the SVG file in `src/icons/`.
|
|
32
|
+
7. Add the alias in `src/icons/index.ts` following the existing export style in that file.
|
|
33
|
+
8. Report the created SVG path and the added export alias.
|
|
34
|
+
|
|
35
|
+
## Guardrails
|
|
36
|
+
|
|
37
|
+
- Do not use `any`.
|
|
38
|
+
- Do not create React wrapper components for the icon unless the existing icon system requires it.
|
|
39
|
+
- Do not overwrite an existing icon file or alias.
|
|
40
|
+
- Do not invent SVG path data; use the SVG supplied by the user.
|
|
41
|
+
- Keep edits scoped to `src/icons/{name}.svg` and `src/icons/index.ts` unless the user asks for something else.
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-table-component
|
|
3
|
+
description: Crear, extraer o refactorizar tablas/listados tabulares en proyectos internos usando el patron estricto de componente encapsulado con MyTable, TableWrapper y createColumnHelper. Usar cuando Codex deba mostrar datos en tabla, mover columnas/hook de servicio a un componente de tabla, agregar paginacion, header/footer de tabla, acciones por fila, tablas verticales o adaptar una vista para no usar MyTablePagination.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Create Table Component
|
|
7
|
+
|
|
8
|
+
Usar este skill para crear o refactorizar tablas del proyecto.
|
|
9
|
+
|
|
10
|
+
## Workflow obligatorio
|
|
11
|
+
|
|
12
|
+
1. Leer primero `docs/local-components.md` y `docs/lkd-web-kit.md`.
|
|
13
|
+
2. Si toca Next.js, leer la documentacion relevante en `node_modules/next/dist/docs/`.
|
|
14
|
+
3. Revisar un ejemplo cercano antes de editar; preferir `Table{EntityPlural}.tsx`.
|
|
15
|
+
4. Crear o mantener la tabla en un componente encapsulado junto a la vista/feature que la consume.
|
|
16
|
+
5. Usar `createColumnHelper<RowData>()`; no declarar `any`.
|
|
17
|
+
6. Importar `MyTable` y `TableWrapper*` desde `lkd-web-kit`.
|
|
18
|
+
7. Usar `MyTable` para renderizar la tabla.
|
|
19
|
+
8. Usar `TableWrapper` como raiz visual de la tabla.
|
|
20
|
+
9. No usar `MyTablePagination` para nuevas tablas.
|
|
21
|
+
|
|
22
|
+
## Checklist antes de escribir columnas
|
|
23
|
+
|
|
24
|
+
- Definir si la tabla es horizontal o `variant="vertical"`.
|
|
25
|
+
- Definir que representa cada header: solo deben ser columnas/campos reales de la tabla.
|
|
26
|
+
- Distinguir contenido superior/inferior de estructura tabular: titulos, subtitulos, emails, filtros, secciones y acciones van en `TableWrapperHeader`/`TableWrapperFooter`, no en columnas falsas.
|
|
27
|
+
- Definir si la data representa registros completos o pares label/value. Para `variant="vertical"`, preferir registros completos con campos reales.
|
|
28
|
+
|
|
29
|
+
## Decisiones que hay que preguntar
|
|
30
|
+
|
|
31
|
+
Si el usuario no lo especifica y no se puede inferir del codigo cercano, preguntar:
|
|
32
|
+
|
|
33
|
+
- Si la tabla lleva contenido arriba de la tabla.
|
|
34
|
+
- Si la tabla lleva contenido abajo de la tabla.
|
|
35
|
+
- Si la tabla lleva paginacion.
|
|
36
|
+
- Si la tabla debe ser horizontal o `variant="vertical"`.
|
|
37
|
+
|
|
38
|
+
No asumir que `TableWrapperHeader` implica titulo. Usarlo para cualquier contenido superior: titulo, filtros, acciones, botones, tabs, resumen, subtitulo, email o encabezado de seccion.
|
|
39
|
+
|
|
40
|
+
No asumir que `TableWrapperFooter` implica paginacion. Usarlo para cualquier contenido inferior: paginacion, totales, leyendas, acciones o resumen.
|
|
41
|
+
|
|
42
|
+
Si hay titulo, usar `TableWrapperTitle` dentro de `TableWrapperHeader`.
|
|
43
|
+
|
|
44
|
+
Si hay paginacion, usar `TableWrapperPagination` dentro de `TableWrapperFooter`.
|
|
45
|
+
|
|
46
|
+
## Paginacion
|
|
47
|
+
|
|
48
|
+
- Usar `TableWrapperPagination`, nunca `MyTablePagination` en tablas nuevas.
|
|
49
|
+
- Usar `variant="short"` para paginacion compacta con texto tipo `1-10 de 15 filas`; queda alineada a la derecha por defecto.
|
|
50
|
+
- Para `variant="short"`, pasar siempre `pageSize`. Si los datos vienen async, usar un fallback estable como `pageSize={currentPage?.pageSize ?? 10}`.
|
|
51
|
+
- No armar el texto de rango en la vista si alcanza con metadata: pasar `totalRows`, `pageSize` y `currentRows`.
|
|
52
|
+
- Usar `text` solo como override manual del texto calculado.
|
|
53
|
+
- Mantener `variant="long"` o sin `variant` para la paginacion completa.
|
|
54
|
+
|
|
55
|
+
## Tablas verticales
|
|
56
|
+
|
|
57
|
+
Para `MyTable variant="vertical"`, modelar los campos como columnas reales y la data como una fila/registro completo. Los nombres de campos van en `column.header`; los valores van en `column.cell`.
|
|
58
|
+
|
|
59
|
+
No simular una tabla vertical con una columna `entry`, `{ field, value }` o un `grid` dentro de una celda.
|
|
60
|
+
|
|
61
|
+
Ejemplo:
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
type UserInformationRow = {
|
|
65
|
+
name: string | undefined;
|
|
66
|
+
lastName: string | undefined;
|
|
67
|
+
email: string | undefined;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const columnHelper = createColumnHelper<UserInformationRow>();
|
|
71
|
+
|
|
72
|
+
const columns = [
|
|
73
|
+
columnHelper.accessor("name", {
|
|
74
|
+
header: "Nombre",
|
|
75
|
+
cell: (cell) => cell.getValue() ?? "-",
|
|
76
|
+
}),
|
|
77
|
+
columnHelper.accessor("lastName", {
|
|
78
|
+
header: "Apellido",
|
|
79
|
+
cell: (cell) => cell.getValue() ?? "-",
|
|
80
|
+
}),
|
|
81
|
+
columnHelper.accessor("email", {
|
|
82
|
+
header: "Email",
|
|
83
|
+
cell: (cell) => cell.getValue() ?? "-",
|
|
84
|
+
}),
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
<TableWrapper>
|
|
88
|
+
<TableWrapperHeader>
|
|
89
|
+
<TableWrapperTitle>Informacion del usuario</TableWrapperTitle>
|
|
90
|
+
<span>{user.email}</span>
|
|
91
|
+
<div>Datos personales</div>
|
|
92
|
+
</TableWrapperHeader>
|
|
93
|
+
<MyTable columns={columns} data={[user]} variant="vertical" />
|
|
94
|
+
</TableWrapper>;
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Encapsulacion
|
|
98
|
+
|
|
99
|
+
- Mover `columns` al componente `Table{EntityPlural}`.
|
|
100
|
+
- Si la tabla consulta datos, mover el hook/servicio al componente de tabla.
|
|
101
|
+
- Si la tabla pagina, mover `pageIndex` y `setPageIndex` al componente de tabla.
|
|
102
|
+
- El padre solo debe pasar entradas externas reales: filtros ya aplicados, ids, callbacks o flags.
|
|
103
|
+
- Al cambiar filtros externos, resetear la pagina dentro del componente de tabla con `useEffect` si aplica.
|
|
104
|
+
|
|
105
|
+
## Patron base
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
"use client";
|
|
109
|
+
|
|
110
|
+
import { createColumnHelper } from "@tanstack/react-table";
|
|
111
|
+
import {
|
|
112
|
+
MyTable,
|
|
113
|
+
TableWrapper,
|
|
114
|
+
TableWrapperFooter,
|
|
115
|
+
TableWrapperHeader,
|
|
116
|
+
TableWrapperPagination,
|
|
117
|
+
TableWrapperTitle,
|
|
118
|
+
} from "lkd-web-kit";
|
|
119
|
+
import { useMemo, useState } from "react";
|
|
120
|
+
|
|
121
|
+
type ItemTableData = {
|
|
122
|
+
id: string;
|
|
123
|
+
name: string;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
type TableItemsProps = {
|
|
127
|
+
selectedFilters: Record<string, unknown>;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const columnHelper = createColumnHelper<ItemTableData>();
|
|
131
|
+
|
|
132
|
+
const TableItems = ({ selectedFilters }: TableItemsProps) => {
|
|
133
|
+
const [pageIndex, setPageIndex] = useState(0);
|
|
134
|
+
|
|
135
|
+
const columns = useMemo(
|
|
136
|
+
() => [
|
|
137
|
+
columnHelper.accessor("name", {
|
|
138
|
+
header: "Nombre",
|
|
139
|
+
cell: (cell) => cell.getValue(),
|
|
140
|
+
}),
|
|
141
|
+
],
|
|
142
|
+
[],
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<TableWrapper>
|
|
147
|
+
<TableWrapperHeader>
|
|
148
|
+
<TableWrapperTitle>Items</TableWrapperTitle>
|
|
149
|
+
</TableWrapperHeader>
|
|
150
|
+
<MyTable columns={columns} data={[]} />
|
|
151
|
+
<TableWrapperFooter>
|
|
152
|
+
<TableWrapperPagination
|
|
153
|
+
total={1}
|
|
154
|
+
value={pageIndex + 1}
|
|
155
|
+
onChange={(page) => setPageIndex(page - 1)}
|
|
156
|
+
/>
|
|
157
|
+
</TableWrapperFooter>
|
|
158
|
+
</TableWrapper>
|
|
159
|
+
);
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export default TableItems;
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Ejemplo short:
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
<TableWrapperPagination
|
|
169
|
+
variant="short"
|
|
170
|
+
total={pageData?.total.pages ?? 1}
|
|
171
|
+
value={(pageData?.currentPage.pageIndex ?? 0) + 1}
|
|
172
|
+
totalRows={pageData?.total.elements}
|
|
173
|
+
pageSize={pageData?.currentPage.pageSize ?? 10}
|
|
174
|
+
currentRows={pageData?.currentPage.elements}
|
|
175
|
+
onChange={(page) => setPageIndex(page - 1)}
|
|
176
|
+
/>;
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Antipatrones
|
|
180
|
+
|
|
181
|
+
- No dejar columnas o hooks de servicio de la tabla en la vista padre.
|
|
182
|
+
- No crear wrappers nuevos si `TableWrapper` y `MyTable` alcanzan.
|
|
183
|
+
- No usar `Paper` directamente para la tabla cuando el patron pide `TableWrapper`.
|
|
184
|
+
- No usar `MyTablePagination` en codigo nuevo.
|
|
185
|
+
- No usar `paginationVariant` ni `paginationText` en `TableWrapperPagination`; usar `variant` y `text`.
|
|
186
|
+
- No calcular `1-10 de 15 filas` en la vista si `TableWrapperPagination` puede calcularlo.
|
|
187
|
+
- No agregar abstracciones compartidas antes de tener reutilizacion real.
|
|
188
|
+
- No crear `MyTableVertical` si `MyTable variant="vertical"` resuelve el caso.
|
|
189
|
+
- No poner titulos de seccion como "Datos personales" o "Informacion del usuario" como headers de columna.
|