Dashboard shadcn-vue sidebar + i18n + NavProjects conectado a KAPPA API

- Dashboard-01 block de shadcn-vue instalado (sidebar con tabs)
- vue-i18n para traducciones ES/EN (detecta idioma del navegador)
- NavProjects ahora usa initiative_name de KAPPA API
- Dashboard stats conectados a API (HUs, sesiones, planeaciones)
- Work items table con datos reales de KAPPA
- Login: toggle password con icono de ojo
- Toggle theme restaurado en SiteHeader
- i18n con locale/en.json y locale/es.json
-Nuevos componentes: NavMain, NavDocuments, NavSecondary en dashboard/
- NavUser原来的 - NavUser原来的
This commit is contained in:
Ricardo Gonzalez
2026-05-23 14:59:17 -05:00
parent 8312389dab
commit 640f0ea889
27 changed files with 1558 additions and 103 deletions
+53 -29
View File
@@ -1,7 +1,8 @@
<script setup lang="ts">
import { computed } from 'vue'
import { computed, watch } from 'vue'
import { useProjectsStore } from '@/stores/projects'
import { Activity, CreditCard, DollarSign, FileText, Users } from 'lucide-vue-next'
import { useWorkItemsStore } from '@/stores/workitems'
import { Activity, CreditCard, FileText, Users } from 'lucide-vue-next'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import {
@@ -14,7 +15,25 @@ import {
} from '@/components/ui/table'
const projects = useProjectsStore()
const workItems = useWorkItemsStore()
const project = computed(() => projects.selected)
watch(
() => projects.selectedId,
async (id) => {
if (id) {
await workItems.fetchWorkItems(id)
}
},
{ immediate: true }
)
const recentSessions = computed(() => {
return [...workItems.logbooks]
.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
.slice(0, 5)
})
</script>
<template>
@@ -37,7 +56,7 @@ const project = computed(() => projects.selected)
<FileText class="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div class="text-2xl font-bold"></div>
<div class="text-2xl font-bold">{{ workItems.totalHUs }}</div>
<p class="text-xs text-muted-foreground">Total HUs del proyecto</p>
</CardContent>
</Card>
@@ -48,30 +67,30 @@ const project = computed(() => projects.selected)
<Activity class="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div class="text-2xl font-bold"></div>
<div class="text-2xl font-bold">{{ workItems.inProgressHUs }}</div>
<p class="text-xs text-muted-foreground">HUs activas</p>
</CardContent>
</Card>
<Card>
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle class="text-sm font-medium">Riesgos detectados</CardTitle>
<CreditCard class="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div class="text-2xl font-bold"></div>
<p class="text-xs text-muted-foreground">Desde transcripciones</p>
</CardContent>
</Card>
<Card>
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle class="text-sm font-medium">Sesiones</CardTitle>
<Users class="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div class="text-2xl font-bold"></div>
<p class="text-xs text-muted-foreground">Transcripciones procesadas</p>
<div class="text-2xl font-bold">{{ workItems.totalSessions }}</div>
<p class="text-xs text-muted-foreground">Bitácoras registradas</p>
</CardContent>
</Card>
<Card>
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle class="text-sm font-medium">Planeaciones</CardTitle>
<CreditCard class="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div class="text-2xl font-bold">{{ workItems.plannings.length }}</div>
<p class="text-xs text-muted-foreground">Entradas de planeación</p>
</CardContent>
</Card>
</div>
@@ -105,18 +124,19 @@ const project = computed(() => projects.selected)
<CardTitle class="text-sm font-medium">Actividad reciente</CardTitle>
</CardHeader>
<CardContent>
<div class="space-y-4">
<div v-for="i in 3" :key="i" class="flex items-center gap-4">
<div v-if="recentSessions.length > 0" class="space-y-4">
<div v-for="session in recentSessions" :key="session.id" class="flex items-center gap-4">
<div class="h-2 w-2 rounded-full bg-muted-foreground/30" />
<div class="flex-1 space-y-1">
<p class="text-sm font-medium leading-none">Sesión {{ 4 - i }}</p>
<p class="text-sm font-medium leading-none truncate">{{ session.description?.slice(0, 50) || 'Sin descripción' }}</p>
<p class="text-xs text-muted-foreground">
{{ new Date(2026, 4, 15 - i * 4).toLocaleDateString('es-CO', { month: 'long', day: 'numeric' }) }}
{{ new Date(session.date).toLocaleDateString('es-CO', { month: 'long', day: 'numeric', year: 'numeric' }) }}
<span v-if="session.hours"> · {{ session.hours }}h</span>
</p>
</div>
</div>
</div>
<p class="text-xs text-muted-foreground/50 mt-4 italic">Datos de KAPPA pendientes de carga</p>
<p v-else class="text-xs text-muted-foreground/50 italic">Sin actividad reciente</p>
</CardContent>
</Card>
</div>
@@ -124,10 +144,11 @@ const project = computed(() => projects.selected)
<Card>
<CardHeader class="flex flex-row items-center justify-between">
<CardTitle class="text-sm font-medium">Work Items</CardTitle>
<Badge variant="outline" class="text-[10px]">Próximamente</Badge>
<Badge v-if="workItems.loading" variant="outline" class="text-[10px]">Cargando...</Badge>
<Badge v-else variant="outline" class="text-[10px]">{{ workItems.userStories.length }} HUs</Badge>
</CardHeader>
<CardContent>
<Table>
<Table v-if="workItems.userStories.length > 0">
<TableHeader>
<TableRow>
<TableHead>Código</TableHead>
@@ -137,16 +158,19 @@ const project = computed(() => projects.selected)
</TableRow>
</TableHeader>
<TableBody>
<TableRow v-for="i in 5" :key="i">
<TableCell class="font-mono text-xs text-muted-foreground">HU-{{ String(i).padStart(3, '0') }}</TableCell>
<TableCell class="text-muted-foreground/50 italic">Pendiente de carga</TableCell>
<TableRow v-for="story in workItems.userStories.slice(0, 10)" :key="story.id">
<TableCell class="font-mono text-xs text-muted-foreground">{{ story.code || `HU-${story.id}` }}</TableCell>
<TableCell class="text-sm truncate max-w-[200px]">{{ story.title }}</TableCell>
<TableCell>
<Badge variant="outline" class="text-[10px]">backlog</Badge>
<Badge variant="outline" class="text-[10px] capitalize">{{ story.status || 'backlog' }}</Badge>
</TableCell>
<TableCell class="text-right text-muted-foreground/50"></TableCell>
<TableCell class="text-right text-muted-foreground/70">{{ story.priority || '—' }}</TableCell>
</TableRow>
</TableBody>
</Table>
<p v-else class="text-xs text-muted-foreground/50 italic text-center py-4">
Sin historias de usuario
</p>
</CardContent>
</Card>
</div>