lkd-web-kit 0.10.6 → 0.10.8
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/lkd-create-modal-component/SKILL.md +168 -0
- package/src/distributed-skills/lkd-create-modal-component/agents/openai.yaml +4 -0
- package/src/distributed-skills/lkd-create-nextjs-page/SKILL.md +84 -0
- package/src/distributed-skills/lkd-create-nextjs-page/agents/openai.yaml +3 -0
- package/src/distributed-skills/lkd-create-svg-icon/SKILL.md +41 -0
- package/src/distributed-skills/lkd-create-svg-icon/agents/openai.yaml +3 -0
- package/src/distributed-skills/lkd-create-table-component/SKILL.md +189 -0
- package/src/distributed-skills/lkd-create-table-component/agents/openai.yaml +3 -0
- package/src/distributed-skills/lkd-layout-mantine-tailwind/SKILL.md +99 -0
- package/src/distributed-skills/lkd-layout-mantine-tailwind/agents/openai.yaml +4 -0
- package/src/distributed-skills/lkd-rhf-lkd-forms/SKILL.md +186 -0
- package/.agents/skills/create-nextjs-page/SKILL.md +0 -104
- package/.agents/skills/create-nextjs-page/agents/openai.yaml +0 -3
- 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.8",
|
|
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: lkd-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,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lkd-create-nextjs-page
|
|
3
|
+
description: Crear o refactorizar paginas de Next.js App Router manteniendo el directorio app enfocado en routing y delegando la UI a componentes Page en src/components/pages. Usar al crear page.tsx, migrar vistas fuera de app, organizar subcomponentes privados, aplicar providers de pagina o normalizar nombres y 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 y un layout cercanos antes de editar para respetar aliases, providers, carga de datos, traducciones y convenciones locales.
|
|
15
|
+
|
|
16
|
+
## Regla principal
|
|
17
|
+
|
|
18
|
+
- Mantener `app` como capa de routing de Next.js.
|
|
19
|
+
- En `app`, dejar `page.tsx`, `layout.tsx` y archivos especiales de Next.js cuando correspondan: `not-found.tsx`, `loading.tsx`, `error.tsx`, `route.ts`, `template.tsx`, `default.tsx` y archivos de metadata.
|
|
20
|
+
- No colocar vistas, subcomponentes, hooks privados, helpers de UI, schemas ni columnas de tabla dentro de `app`.
|
|
21
|
+
- Colocar la UI real de cada ruta en `src/components/pages`.
|
|
22
|
+
- No crear carpetas de features genericas en la raiz de `pages`, como `components`, `widgets` o `recommendations`. Colocar las piezas privadas dentro de la Page dueña o moverlas a componentes compartidos cuando exista reutilizacion real.
|
|
23
|
+
|
|
24
|
+
## Estructura
|
|
25
|
+
|
|
26
|
+
Derivar el area desde el route group principal cuando exista. Si termina en `-layout`, quitar ese sufijo; si no hay route group, usar el primer segmento estatico de la ruta. Ignorar locale, route groups y segmentos dinamicos.
|
|
27
|
+
|
|
28
|
+
```txt
|
|
29
|
+
src/app/(dashboard-layout)/reports/page.tsx
|
|
30
|
+
src/components/pages/dashboard/reports/ReportsPage.tsx
|
|
31
|
+
|
|
32
|
+
src/app/products/[productId]/page.tsx
|
|
33
|
+
src/components/pages/products/ProductPage.tsx
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Para una Page con piezas privadas:
|
|
37
|
+
|
|
38
|
+
```txt
|
|
39
|
+
src/components/pages/dashboard/reports/ReportsPage/index.tsx
|
|
40
|
+
src/components/pages/dashboard/reports/ReportsPage/ReportFilters.tsx
|
|
41
|
+
src/components/pages/dashboard/reports/ReportsPage/helpers.ts
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- Usar archivo plano `{PageName}Page.tsx` si el componente es de una pieza.
|
|
45
|
+
- Usar carpeta `{PageName}Page/index.tsx` si tiene subcomponentes, helpers, schemas o piezas privadas.
|
|
46
|
+
- Usar PascalCase y el sufijo `Page` para todo componente que represente una pagina.
|
|
47
|
+
- Para rutas dinamicas, nombrar la Page por el ultimo concepto estable, no por el nombre del parametro. En rutas anidadas, reflejar los segmentos estaticos que den contexto.
|
|
48
|
+
- Si hay colision o ambiguedad real, anteponer el segmento padre.
|
|
49
|
+
- No usar `index.tsx` como Page principal directamente bajo el area o la ruta.
|
|
50
|
+
- Mover a componentes compartidos solo cuando haya mas de un consumidor real o exista un patron local establecido.
|
|
51
|
+
|
|
52
|
+
## Providers
|
|
53
|
+
|
|
54
|
+
Si el proyecto ya define `PageProviders`, envolver cada `page.tsx` con ese componente y pasar solo props soportadas y necesarias.
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import PageProviders from "@/app/PageProviders";
|
|
58
|
+
import ReportsPage from "@/components/pages/dashboard/reports/ReportsPage";
|
|
59
|
+
|
|
60
|
+
export default async function Page() {
|
|
61
|
+
return (
|
|
62
|
+
<PageProviders>
|
|
63
|
+
<ReportsPage />
|
|
64
|
+
</PageProviders>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
- Adaptar los aliases, import y props al proyecto consumidor. No crear `PageProviders` solo para cumplir este skill.
|
|
70
|
+
- No usar `PageProviders` en un `layout.tsx` compartido por varias pages. Si el layout necesita un provider, importar y aplicar solamente el provider especifico del proyecto.
|
|
71
|
+
- Excepcion: un layout dedicado exclusivamente a una page puede usar `PageProviders` cuando evita duplicar providers o carga de datos de esa unica ruta.
|
|
72
|
+
- Mantener `PageProviders` en `page.tsx` cuando la pagina necesita props propias, como datos de servidor, traducciones, catalogos o precarga de modales.
|
|
73
|
+
- Mantener en `page.tsx` solo responsabilidades de entrypoint: params, metadata, fetch server-side, redirects, `notFound` y providers.
|
|
74
|
+
- No duplicar providers dentro del componente Page salvo que el proyecto lo documente.
|
|
75
|
+
|
|
76
|
+
## Checklist
|
|
77
|
+
|
|
78
|
+
- `app` queda sin UI ni logica privada de ruta.
|
|
79
|
+
- `page.tsx` importa un componente desde `src/components/pages`.
|
|
80
|
+
- El componente de pagina termina en `Page` y sus piezas privadas estan dentro de su carpeta `*Page` cuando corresponde.
|
|
81
|
+
- Se usa el provider existente del proyecto en el limite correcto: page por defecto, layout solo si es exclusivo de una unica page.
|
|
82
|
+
- No quedan imports a la ubicacion anterior de vistas.
|
|
83
|
+
- No se usa `any`.
|
|
84
|
+
- Ejecutar una verificacion focalizada para el cambio realizado.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lkd-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: lkd-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.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lkd-layout-mantine-tailwind
|
|
3
|
+
description: Create or refactor React/Next.js UI layout in this project using Mantine Layout components to express visual structure and Tailwind classes for styling, spacing, responsive behavior, columns, and display properties. Use when Codex needs to create or adjust JSX with Container, Center, Flex, Group, Stack, SimpleGrid, Grid, or Paper, especially in Page or UI components that mix Mantine and Tailwind.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Layout Mantine Tailwind
|
|
7
|
+
|
|
8
|
+
Usar este skill para maquetar componentes visuales en `aestrenar-web` con Mantine y Tailwind sin mezclar responsabilidades.
|
|
9
|
+
|
|
10
|
+
## Lectura Obligatoria
|
|
11
|
+
|
|
12
|
+
1. Leer `docs/lkd-web-kit.md` y `docs/local-components.md` antes de crear componentes nuevos o reemplazar UI existente.
|
|
13
|
+
2. Si se toca una pagina o componente de Next.js, leer la documentacion local relevante en `node_modules/next/dist/docs/`.
|
|
14
|
+
3. Revisar uno o dos componentes cercanos antes de editar para seguir imports, wrappers locales y tono visual.
|
|
15
|
+
|
|
16
|
+
## Regla Principal
|
|
17
|
+
|
|
18
|
+
- Usar componentes Layout de Mantine para que el JSX deje claro el tipo de display o contenedor.
|
|
19
|
+
- Usar Tailwind para estilos, medidas, espaciado, columnas, alineacion y responsive.
|
|
20
|
+
- No usar props visuales de Mantine cuando una clase Tailwind resuelve lo mismo.
|
|
21
|
+
|
|
22
|
+
## Componentes Layout
|
|
23
|
+
|
|
24
|
+
- `Container`: contenedor de ancho de pagina o seccion.
|
|
25
|
+
- `Paper`: superficie visual cuando ya existe una tarjeta/panel real.
|
|
26
|
+
- `Stack`: layout vertical.
|
|
27
|
+
- `Group`: layout horizontal, grupos de acciones, pills, header row o wrapping.
|
|
28
|
+
- `SimpleGrid`: grillas simples responsive. Definir columnas con Tailwind: `grid-cols-1 sm:grid-cols-2 xl:grid-cols-4`.
|
|
29
|
+
- `Grid`: usar solo si hace falta una capacidad propia de `Grid` que `SimpleGrid` no cubre.
|
|
30
|
+
- `Center`: centrar contenido en ambos ejes cuando ese sea el objetivo del layout.
|
|
31
|
+
- `Flex`: usar solo cuando se requiera cambiar entre `flex-row` y `flex-col` segun responsive, con clases Tailwind.
|
|
32
|
+
|
|
33
|
+
## Tailwind
|
|
34
|
+
|
|
35
|
+
Usar clases Tailwind para:
|
|
36
|
+
|
|
37
|
+
- `m*`, `p*`, `gap-*`, `space-*`.
|
|
38
|
+
- `rounded-*`, `shadow-*`, `border`, colores y fondos.
|
|
39
|
+
- `text-*`, `font-*`, `leading-*`, `tracking-*`.
|
|
40
|
+
- `h-*`, `w-*`, `min-*`, `max-*`.
|
|
41
|
+
- `items-*`, `justify-*`, `flex-wrap`, `grid-cols-*`, `col-span-*`.
|
|
42
|
+
- Breakpoints responsive: `sm:`, `md:`, `lg:`, `xl:`.
|
|
43
|
+
|
|
44
|
+
No usar props Mantine equivalentes como `m`, `p`, `px`, `py`, `radius`, `shadow`, `w`, `h`, `gap`, `justify`, `align` o `cols` si Tailwind lo expresa claramente.
|
|
45
|
+
|
|
46
|
+
## Texto y Semantica
|
|
47
|
+
|
|
48
|
+
- No usar `Text` ni `Title` de Mantine.
|
|
49
|
+
- Usar elementos HTML semanticos (`h1`, `h2`, `p`, `section`, `article`, `footer`, `header`) cuando correspondan.
|
|
50
|
+
- Usar la prop `component` solo para cambiar semantica real: `component="main"`, `component="section"`, `component="article"`, `component="footer"`.
|
|
51
|
+
- Omitir `component` si el resultado seria `div`, porque es el default.
|
|
52
|
+
|
|
53
|
+
## Patrones
|
|
54
|
+
|
|
55
|
+
Grilla simple:
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
<SimpleGrid className="grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-4">
|
|
59
|
+
{items.map((item) => (
|
|
60
|
+
<Card key={item.id} data={item} />
|
|
61
|
+
))}
|
|
62
|
+
</SimpleGrid>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Layout vertical:
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
<Stack component="section" className="w-full gap-5">
|
|
69
|
+
<Header />
|
|
70
|
+
<Content />
|
|
71
|
+
</Stack>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Grupo horizontal:
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
<Group className="items-start justify-between gap-3">
|
|
78
|
+
<h2 className="font-bold text-xl">Titulo</h2>
|
|
79
|
+
<ButtonAE>Accion</ButtonAE>
|
|
80
|
+
</Group>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Responsive row/column:
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
<Flex className="flex-col gap-2 md:flex-row md:items-center md:justify-between">
|
|
87
|
+
<Summary />
|
|
88
|
+
<Actions />
|
|
89
|
+
</Flex>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Checklist
|
|
93
|
+
|
|
94
|
+
- El JSX muestra el layout con Mantine, no con `div` anonimos cuando hay `Stack`, `Group`, `SimpleGrid`, `Flex`, `Center`, `Container` o `Paper` aplicable.
|
|
95
|
+
- Las columnas de grilla estan en Tailwind, no en `cols`.
|
|
96
|
+
- No hay `component="div"`.
|
|
97
|
+
- No hay `Text` ni `Title` de Mantine.
|
|
98
|
+
- No se agregaron abstracciones compartidas si el componente es privado de una pagina.
|
|
99
|
+
- Se ejecuto una verificacion focalizada, como `npx biome lint <archivo>`.
|