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:
+53
-29
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user