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
+19 -1
View File
@@ -111,6 +111,22 @@ export interface CellMemberRecord {
addedAt: string
}
export interface DexieUserStory {
id: number
initiative_id: number
title: string
status: string | null
priority: string | null
assigned_to: number | null
code: string | null
description: string | null
acceptance_criteria: string | null
story_points: number | null
sprint: string | null
has_impairment: boolean | number
created_at: string | null
}
const db = new Dexie('alpha-core') as Dexie & {
settings: Dexie.Table<SettingEntry, string>
project_docs: Dexie.Table<ProjectDocRecord, number>
@@ -123,9 +139,10 @@ const db = new Dexie('alpha-core') as Dexie & {
lookups: Dexie.Table<LookupRecord, string>
cells: Dexie.Table<CellRecord, string>
cell_members: Dexie.Table<CellMemberRecord, string>
user_stories: Dexie.Table<DexieUserStory, number>
}
db.version(7).stores({
db.version(8).stores({
settings: '&key',
project_docs: '&projectId, projectName, updatedAt',
sessions: '++id, projectId, date',
@@ -137,6 +154,7 @@ db.version(7).stores({
lookups: '&[type+id], type',
cells: '&id',
cell_members: '[cellId+userId], cellId, userId',
user_stories: '&id, initiative_id',
})
export default db
+80
View File
@@ -0,0 +1,80 @@
import { saveLookup, getLookups } from '@/services/users-db'
export interface StatusDef {
id: number
name: string
description: string
}
const STATUSES: StatusDef[] = [
{ id: 1, name: 'Por hacer', description: 'Tarea pendiente por iniciar' },
{ id: 2, name: 'En progreso', description: 'Tarea actualmente en desarrollo' },
{ id: 3, name: 'En revisión', description: 'Tarea en proceso de revisión' },
{ id: 4, name: 'En pruebas', description: 'Tarea en fase de pruebas' },
{ id: 5, name: 'Completado', description: 'Tarea finalizada' },
{ id: 6, name: 'Bloqueado', description: 'Tarea bloqueada por dependencias' },
{ id: 7, name: 'Cancelado', description: 'Tarea cancelada definitivamente' },
]
const STATUS_MAP: Record<string, number> = {
'todo': 1,
'por hacer': 1,
'in_progress': 2,
'doing': 2,
'wip': 2,
'active': 2,
'in progress': 2,
'en progreso': 2,
'true': 2,
'review': 3,
'revisión': 3,
'testing': 4,
'pruebas': 4,
'done': 5,
'completed': 5,
'closed': 5,
'finalizado': 5,
'blocked': 6,
'bloqueado': 6,
'cancelled': 7,
'cancelado': 7,
'false': 1,
}
let cachedStatuses: Map<number, StatusDef> | null = null
export function resolveStatusName(raw: unknown): string {
const str = raw != null ? String(raw).trim().toLowerCase() : ''
if (!str) return 'Por hacer'
const id = STATUS_MAP[str]
if (!id) return String(raw)
const def = STATUSES.find(s => s.id === id)
return def?.name ?? String(raw)
}
export function resolveStatusId(raw: unknown): number {
const str = raw != null ? String(raw).trim().toLowerCase() : ''
if (!str) return 1
return STATUS_MAP[str] ?? 1
}
export async function seedStatusLookups(): Promise<void> {
for (const s of STATUSES) {
await saveLookup('status', s.id, s.name, s.description)
}
cachedStatuses = new Map(STATUSES.map(s => [s.id, s]))
}
export async function loadStatusLookups(): Promise<StatusDef[]> {
const records = await getLookups('status')
if (records.length > 0) {
cachedStatuses = new Map(records.map(r => [r.id, { id: r.id, name: r.name, description: r.description ?? '' }]))
return Array.from(cachedStatuses.values())
}
return STATUSES
}
export function getCachedStatuses(): StatusDef[] {
if (cachedStatuses) return Array.from(cachedStatuses.values())
return STATUSES
}
+13
View File
@@ -43,6 +43,19 @@ async function fallbackInvoke<T>(cmd: string, args?: Record<string, unknown>): P
if (u) try { await (db as any).table('alpha_users').put(u) } catch {}
return null as unknown as T
}
// ─── User Stories → Dexie ──────────────────────────
case 'get_user_stories': {
const initiativeId = (args as any)?.initiativeId
try {
const all = await (db as any).table('user_stories').toArray()
return (initiativeId ? all.filter((r: any) => r.initiative_id === initiativeId) : all) as unknown as T
} catch { return [] as unknown as T }
}
case 'save_user_story': {
const s = (args as any)?.story
if (s) try { await (db as any).table('user_stories').put(s) } catch {}
return null as unknown as T
}
// ─── Por defecto: no hay fallback ─────────────────
default:
return null as unknown as T