agregar IDs jerarquicos a componentes + regla #6 en AGENTS.md

This commit is contained in:
2026-05-27 20:51:24 -05:00
parent 1e3ed6ac02
commit 66b3e24fec
6 changed files with 16 additions and 13 deletions
+1
View File
@@ -25,6 +25,7 @@
3. **No usar `.catch(() => {})`** sin loguear el error. Siempre `console.error()`. 3. **No usar `.catch(() => {})`** sin loguear el error. Siempre `console.error()`.
4. **Los nombres de funciones y archivos van en español** cuando son semánticos del dominio (ej: `syncHUsToTurso` → OK, pero labels visibles van en i18n). 4. **Los nombres de funciones y archivos van en español** cuando son semánticos del dominio (ej: `syncHUsToTurso` → OK, pero labels visibles van en i18n).
5. **Toda función debe tener observabilidad**: loguear entrada con `[Alpha]` + acción + datos clave, loguear errores con `[Alpha]` + descripción + error completo. Sin logs no se puede diagnosticar. 5. **Toda función debe tener observabilidad**: loguear entrada con `[Alpha]` + acción + datos clave, loguear errores con `[Alpha]` + descripción + error completo. Sin logs no se puede diagnosticar.
6. **IDs jerárquicos obligatorios en componentes**: todo elemento raíz de sección debe tener `id="{view}-{section}-{element}"` en kebab-case. Ej: `dashboard-stats-epics`, `login-card`, `users-table`. Permite ubicar cualquier componente por su ID sin ambigüedad.
## Sincronización entre máquinas (Mac Mini ↔ MacBook Air) ## Sincronización entre máquinas (Mac Mini ↔ MacBook Air)
+2 -1
View File
@@ -87,7 +87,8 @@ kappa-hub/
2. **KAPPA retorna tipos mixtos** — usar `String()` siempre 2. **KAPPA retorna tipos mixtos** — usar `String()` siempre
3. **No silenciar errores** — siempre `console.error()` 3. **No silenciar errores** — siempre `console.error()`
4. **Observabilidad obligatoria** — toda función debe loguear `[Alpha]` + acción + datos clave + errores completos 4. **Observabilidad obligatoria** — toda función debe loguear `[Alpha]` + acción + datos clave + errores completos
5. **Build verde siempre**`bun run build` + `cargo build` sin errores 5. **IDs jerárquicos** — componentes con `id="{view}-{section}-{element}"` en kebab-case. Ej: `dashboard-stats-epics`
6. **Build verde siempre**`bun run build` + `cargo build` sin errores
## Documentos clave ## Documentos clave
+8 -8
View File
@@ -66,8 +66,8 @@ const statusLabel = (status: unknown) => {
</div> </div>
<!-- Stats --> <!-- Stats -->
<div class="grid gap-3 @xl:grid-cols-2 @3xl:grid-cols-4"> <div id="dashboard-stats" class="grid gap-3 @xl:grid-cols-2 @3xl:grid-cols-4">
<Card class="bg-gradient-to-t from-primary/5 to-card shadow-xs dark:bg-card"> <Card id="dashboard-stats-epics" class="bg-gradient-to-t from-primary/5 to-card shadow-xs dark:bg-card">
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2"> <CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle class="text-sm font-medium">{{ t('dashboard.epics') }}</CardTitle> <CardTitle class="text-sm font-medium">{{ t('dashboard.epics') }}</CardTitle>
<Layers class="size-4 text-muted-foreground" /> <Layers class="size-4 text-muted-foreground" />
@@ -78,7 +78,7 @@ const statusLabel = (status: unknown) => {
</CardContent> </CardContent>
</Card> </Card>
<Card class="bg-gradient-to-t from-primary/5 to-card shadow-xs dark:bg-card"> <Card id="dashboard-stats-hus" class="bg-gradient-to-t from-primary/5 to-card shadow-xs dark:bg-card">
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2"> <CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle class="text-sm font-medium">{{ t('dashboard.hus') }}</CardTitle> <CardTitle class="text-sm font-medium">{{ t('dashboard.hus') }}</CardTitle>
<FileText class="size-4 text-muted-foreground" /> <FileText class="size-4 text-muted-foreground" />
@@ -89,7 +89,7 @@ const statusLabel = (status: unknown) => {
</CardContent> </CardContent>
</Card> </Card>
<Card class="bg-gradient-to-t from-primary/5 to-card shadow-xs dark:bg-card"> <Card id="dashboard-stats-inprogress" class="bg-gradient-to-t from-primary/5 to-card shadow-xs dark:bg-card">
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2"> <CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle class="text-sm font-medium">{{ t('dashboard.inProgress') }}</CardTitle> <CardTitle class="text-sm font-medium">{{ t('dashboard.inProgress') }}</CardTitle>
<Activity class="size-4 text-muted-foreground" /> <Activity class="size-4 text-muted-foreground" />
@@ -100,7 +100,7 @@ const statusLabel = (status: unknown) => {
</CardContent> </CardContent>
</Card> </Card>
<Card class="bg-gradient-to-t from-primary/5 to-card shadow-xs dark:bg-card"> <Card id="dashboard-stats-sessions" class="bg-gradient-to-t from-primary/5 to-card shadow-xs dark:bg-card">
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2"> <CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle class="text-sm font-medium">{{ t('dashboard.sessions') }}</CardTitle> <CardTitle class="text-sm font-medium">{{ t('dashboard.sessions') }}</CardTitle>
<Clock class="size-4 text-muted-foreground" /> <Clock class="size-4 text-muted-foreground" />
@@ -113,7 +113,7 @@ const statusLabel = (status: unknown) => {
</div> </div>
<!-- Description --> <!-- Description -->
<Card> <Card id="dashboard-description">
<CardHeader class="pb-2"> <CardHeader class="pb-2">
<CardTitle class="text-sm font-medium">{{ t('dashboard.description') }}</CardTitle> <CardTitle class="text-sm font-medium">{{ t('dashboard.description') }}</CardTitle>
</CardHeader> </CardHeader>
@@ -140,7 +140,7 @@ const statusLabel = (status: unknown) => {
<!-- Epics --> <!-- Epics -->
<template v-else-if="workItems.epics.length > 0"> <template v-else-if="workItems.epics.length > 0">
<div> <div>
<h3 class="text-sm font-semibold text-muted-foreground uppercase tracking-wider mb-3"> <h3 id="dashboard-epics-heading" class="text-sm font-semibold text-muted-foreground uppercase tracking-wider mb-3">
{{ t('dashboard.epicsCount', { count: workItems.totalEpics }) }} {{ t('dashboard.epicsCount', { count: workItems.totalEpics }) }}
</h3> </h3>
<div class="space-y-2"> <div class="space-y-2">
@@ -173,7 +173,7 @@ const statusLabel = (status: unknown) => {
</template> </template>
<!-- HUs Table --> <!-- HUs Table -->
<Card> <Card id="dashboard-hus-table">
<CardHeader class="flex flex-row items-center justify-between pb-2"> <CardHeader class="flex flex-row items-center justify-between pb-2">
<CardTitle class="text-sm font-medium">{{ t('dashboard.userStoriesTitle') }}</CardTitle> <CardTitle class="text-sm font-medium">{{ t('dashboard.userStoriesTitle') }}</CardTitle>
<Badge variant="outline" class="text-xs">{{ t('dashboard.husCount', { count: workItems.userStories.length }) }}</Badge> <Badge variant="outline" class="text-xs">{{ t('dashboard.husCount', { count: workItems.userStories.length }) }}</Badge>
+1 -1
View File
@@ -41,7 +41,7 @@ async function handleLogin() {
<div class="flex min-h-svh flex-col items-center justify-center gap-6 bg-background p-6"> <div class="flex min-h-svh flex-col items-center justify-center gap-6 bg-background p-6">
<img src="/Alpha.svg" alt="Alpha" class="h-10 w-auto dark:invert" /> <img src="/Alpha.svg" alt="Alpha" class="h-10 w-auto dark:invert" />
<Card class="w-full max-w-sm bg-gradient-to-t from-primary/5 to-card shadow-xs dark:bg-card"> <Card id="login-card" class="w-full max-w-sm bg-gradient-to-t from-primary/5 to-card shadow-xs dark:bg-card">
<CardHeader> <CardHeader>
<CardTitle class="text-2xl">{{ t('login.title') }}</CardTitle> <CardTitle class="text-2xl">{{ t('login.title') }}</CardTitle>
</CardHeader> </CardHeader>
+1 -1
View File
@@ -80,7 +80,7 @@ function getStatusVariant(status?: string) {
</div> </div>
<!-- Project Grid --> <!-- Project Grid -->
<div v-else class="grid grid-cols-1 gap-4 @lg:grid-cols-2 @3xl:grid-cols-3"> <div id="projects-grid" v-else class="grid grid-cols-1 gap-4 @lg:grid-cols-2 @3xl:grid-cols-3">
<Card <Card
v-for="p in projects.projects" v-for="p in projects.projects"
:key="p.id" :key="p.id"
+3 -2
View File
@@ -175,7 +175,7 @@ onMounted(() => {
<!-- Content --> <!-- Content -->
<template v-else> <template v-else>
<!-- Stats --> <!-- Stats -->
<div class="grid grid-cols-2 gap-3 @xl:grid-cols-3 @3xl:grid-cols-6"> <div id="users-stats" class="grid grid-cols-2 gap-3 @xl:grid-cols-3 @3xl:grid-cols-6">
<Card v-for="role in teamRoles" :key="role" class="bg-gradient-to-t from-primary/5 to-card shadow-xs dark:bg-card"> <Card v-for="role in teamRoles" :key="role" class="bg-gradient-to-t from-primary/5 to-card shadow-xs dark:bg-card">
<CardHeader class="p-4 pb-2"> <CardHeader class="p-4 pb-2">
<CardTitle class="flex items-center gap-2 text-sm font-medium"> <CardTitle class="flex items-center gap-2 text-sm font-medium">
@@ -198,7 +198,7 @@ onMounted(() => {
<h3 class="text-sm font-semibold text-muted-foreground uppercase tracking-wider mb-3"> <h3 class="text-sm font-semibold text-muted-foreground uppercase tracking-wider mb-3">
{{ t('users.teamMembers', { count: teamMembers.length }) }} {{ t('users.teamMembers', { count: teamMembers.length }) }}
</h3> </h3>
<div class="grid grid-cols-1 gap-3 @xl:grid-cols-2 @3xl:grid-cols-3"> <div id="users-team-cards" class="grid grid-cols-1 gap-3 @xl:grid-cols-2 @3xl:grid-cols-3">
<Card <Card
v-for="user in teamMembers" v-for="user in teamMembers"
:key="user.id" :key="user.id"
@@ -259,6 +259,7 @@ onMounted(() => {
{{ t('users.allUsers') }} {{ t('users.allUsers') }}
</h3> </h3>
<div <div
id="users-table"
class="ag-theme-alpha-shadcn w-full rounded-lg border overflow-hidden" class="ag-theme-alpha-shadcn w-full rounded-lg border overflow-hidden"
:style="{ height: Math.max(300, Math.min(600, store.users.length * 45 + 45)) + 'px' }" :style="{ height: Math.max(300, Math.min(600, store.users.length * 45 + 45)) + 'px' }"
> >