DashboardView: asignación real desde KAPPA + status_name desde lookup + columna asignado como texto

- KappaUserStory ampliado con assigned_to, asignado_a, assigned_name
- EnrichedUserStory ahora tiene _assignedUserId, _assignedName, _statusName
- parseAssignedUser() extrae user ID desde múltiples formatos de KAPPA
- syncHUsToTurso persiste assigned_to en BD local
- DashboardView: columna Asignado muestra nombre real desde KAPPA (texto, no Select)
- statuses-db.ts: lookup de estados con seed en Dexie + resolveStatusName()
  seguro para booleanos/números (String() antes de trim)
- tauri-db.ts: fallback Dexie para get/save user_stories (funciona en bun dev)
- db.ts: nueva tabla user_stories en Dexie (version 8)
- Filtros de tabla alineados a la derecha (justify-end)
This commit is contained in:
2026-05-29 02:52:27 -05:00
parent 416784b2fd
commit 2f8d79a624
9 changed files with 340 additions and 16 deletions
+34 -2
View File
@@ -6,6 +6,7 @@ import { tauriDb, type UserStoryRecord, type EpicRecord, type ImpairmentRecord }
import { stripHtml } from '@/services/clean-html'
import { criteriaToJson, parseQuillList } from '@/services/clean-html'
import { parseHierarchy, stripHierarchy, getItemType, type ItemType } from '@/services/hierarchy'
import { resolveStatusName, seedStatusLookups } from '@/services/statuses-db'
import type { KappaUserStory, KappaLogbookEntry, KappaPlanningEntry, KappaEpicDevelopment, KappaPending } from '@/types/kappa'
export interface EnrichedUserStory extends KappaUserStory {
@@ -14,6 +15,9 @@ export interface EnrichedUserStory extends KappaUserStory {
_cleanTitle: string
_criteriaList: string[]
has_impairment: boolean
_assignedUserId: number | null
_assignedName: string
_statusName: string
}
export interface EnrichedEpic extends KappaEpicDevelopment {
@@ -59,10 +63,29 @@ export const useWorkItemsStore = defineStore('workitems', () => {
}
}
function enrichHU(hu: { title?: string; id?: number; acceptance_criteria?: string | null; criterios_aceptacion?: string | null }, initiativeId: number): EnrichedUserStory {
function parseAssignedUser(hu: any): { id: number | null; name: string } {
// Intenta múltiples formatos que KAPPA puede devolver
if (hu.assigned_to != null && hu.assigned_to !== '') {
const id = Number(hu.assigned_to)
if (!isNaN(id)) return { id, name: hu.assigned_name || '' }
}
if (hu.asignado_a != null) {
if (Array.isArray(hu.asignado_a)) {
const first = hu.asignado_a[0]
if (first != null) {
const id = typeof first === 'object' ? Number(first.id) : Number(first)
if (!isNaN(id)) return { id, name: typeof first === 'object' ? first.name || first.full_name || '' : '' }
}
}
}
return { id: null, name: '' }
}
function enrichHU(hu: { title?: string; id?: number; status?: string | null; acceptance_criteria?: string | null; criterios_aceptacion?: string | null; assigned_to?: number | null; asignado_a?: number[] | string[] | null; assigned_name?: string }, initiativeId: number): EnrichedUserStory {
const h = parseHierarchy(hu.title || '')
const rawCriteria = hu.acceptance_criteria || hu.criterios_aceptacion || ''
const criteriaList = rawCriteria ? parseQuillList(rawCriteria) : []
const { id: assignedUserId, name: assignedName } = parseAssignedUser(hu)
return {
id: hu.id ?? 0,
title: hu.title || '',
@@ -74,6 +97,9 @@ export const useWorkItemsStore = defineStore('workitems', () => {
_cleanTitle: h ? stripHierarchy(hu.title || '') : (hu.title || ''),
_criteriaList: criteriaList,
has_impairment: false,
_assignedUserId: assignedUserId,
_assignedName: assignedName,
_statusName: resolveStatusName(hu.status),
}
}
@@ -104,6 +130,9 @@ export const useWorkItemsStore = defineStore('workitems', () => {
}
const isFirstVisit = !firstVisit.value.has(id)
if (isFirstVisit) {
seedStatusLookups().catch(() => {})
}
try {
// 1. Cargar desde Turso (instantáneo)
@@ -116,6 +145,7 @@ export const useWorkItemsStore = defineStore('workitems', () => {
epics.value = localEpics.map(e => enrichEpic(e, id))
userStories.value = localHUs.map(hu => enrichHU(hu, id))
}
// 2. Consultar KAPPA (siempre, para detectar cambios)
@@ -180,6 +210,8 @@ export const useWorkItemsStore = defineStore('workitems', () => {
hasImpairment = impairments.some(p => !p.status)
} catch {}
const { id: assignedUserId } = parseAssignedUser(hu)
await tauriDb.saveUserStory({
id: huId,
initiative_id: projectId,
@@ -193,7 +225,7 @@ export const useWorkItemsStore = defineStore('workitems', () => {
story_points: hu.story_points ?? null,
estimated_hours: null,
actual_hours: null,
assigned_to: null,
assigned_to: assignedUserId,
sprint: safeStr(hu.sprint),
has_impairment: hasImpairment,
item_type: null,