hu_drafts en Dexie + push individual a KAPPA + project-analyzer
- db.ts v4: tabla hu_drafts (id, projectId, title, description, syncStatus) - hu-drafts-db.ts: CRUD Dexie para drafts - project-analyzer.ts: saveAsDrafts() guarda en BD local, no crea en KAPPA - DashboardView: borradores desde Dexie con boton Enviar individual - pushDraft: endpoint /api/userstorys/create/ con payload exacto - pushDraft: elimina draft solo si KAPPA responde ok
This commit is contained in:
+25
-9
@@ -28,37 +28,53 @@ export interface SessionRecord {
|
||||
export interface SessionSummaryRecord {
|
||||
sessionId: number
|
||||
summary: string
|
||||
objectives: string // JSON array
|
||||
tasks: string // JSON array
|
||||
commitments: string // JSON array
|
||||
decisions: string // JSON array
|
||||
keyPoints: string // JSON array
|
||||
objectives: string
|
||||
tasks: string
|
||||
commitments: string
|
||||
decisions: string
|
||||
keyPoints: string
|
||||
modelUsed: string
|
||||
}
|
||||
|
||||
export interface ProjectStateRecord {
|
||||
projectId: number
|
||||
summary: string
|
||||
objectives: string // JSON array (unificado)
|
||||
tasks: string // JSON array (consolidado)
|
||||
commitments: string // JSON array (consolidado)
|
||||
objectives: string
|
||||
tasks: string
|
||||
commitments: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface HuDraftRecord {
|
||||
id: string
|
||||
projectId: number
|
||||
title: string
|
||||
description: string
|
||||
acceptanceCriteria: string
|
||||
priority: string
|
||||
type: string
|
||||
sourceSessionId?: number
|
||||
syncStatus: 'draft' | 'pushing' | 'pushed'
|
||||
kappaId?: number
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
const db = new Dexie('alpha-core') as Dexie & {
|
||||
settings: Dexie.Table<SettingEntry, string>
|
||||
project_docs: Dexie.Table<ProjectDocRecord, number>
|
||||
sessions: Dexie.Table<SessionRecord, number>
|
||||
session_summaries: Dexie.Table<SessionSummaryRecord, number>
|
||||
project_state: Dexie.Table<ProjectStateRecord, number>
|
||||
hu_drafts: Dexie.Table<HuDraftRecord, string>
|
||||
}
|
||||
|
||||
db.version(3).stores({
|
||||
db.version(4).stores({
|
||||
settings: '&key',
|
||||
project_docs: '&projectId, projectName, updatedAt',
|
||||
sessions: '++id, projectId, date',
|
||||
session_summaries: '&sessionId',
|
||||
project_state: '&projectId',
|
||||
hu_drafts: '&id, projectId, syncStatus',
|
||||
})
|
||||
|
||||
export default db
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import db, { type HuDraftRecord } from '@/services/db'
|
||||
export type { HuDraftRecord }
|
||||
|
||||
export async function getDrafts(projectId: number): Promise<HuDraftRecord[]> {
|
||||
return db.hu_drafts.where('projectId').equals(projectId).toArray()
|
||||
}
|
||||
|
||||
export async function getDraft(id: string): Promise<HuDraftRecord | undefined> {
|
||||
return db.hu_drafts.get(id)
|
||||
}
|
||||
|
||||
export async function saveDraft(d: HuDraftRecord) {
|
||||
await db.hu_drafts.put(d)
|
||||
}
|
||||
|
||||
export async function deleteDraft(id: string) {
|
||||
await db.hu_drafts.delete(id)
|
||||
}
|
||||
|
||||
export async function getUnpushedDrafts(projectId: number): Promise<HuDraftRecord[]> {
|
||||
return db.hu_drafts.where({ projectId, syncStatus: 'draft' }).toArray()
|
||||
}
|
||||
|
||||
export function createDraftId(): string {
|
||||
return crypto.randomUUID()
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import { callAI } from '@/services/ai'
|
||||
import { getSessionsByProject, getSessionSummary, getProjectState } from '@/services/transcriptions-db'
|
||||
import { kappa } from '@/services/kappa-api'
|
||||
import { saveDraft, createDraftId } from '@/services/hu-drafts-db'
|
||||
import type { EnrichedUserStory } from '@/stores/workitems'
|
||||
|
||||
interface AnalysisHU {
|
||||
export interface AnalysisHU {
|
||||
title: string
|
||||
description: string
|
||||
acceptance_criteria: string[]
|
||||
@@ -109,51 +109,43 @@ export async function analyzeProject(
|
||||
}
|
||||
|
||||
/**
|
||||
* Crea las HUs propuestas en KAPPA (solo las que no existen).
|
||||
* Devuelve cuántas se crearon.
|
||||
* Guarda las HUs propuestas como borradores en la BD local.
|
||||
* No crea nada en KAPPA — el usuario revisa y envía desde HuDrafts.
|
||||
*/
|
||||
export async function createMissingHUs(
|
||||
export async function saveAsDrafts(
|
||||
projectId: number,
|
||||
analysis: AnalysisResult,
|
||||
existingHUs: EnrichedUserStory[],
|
||||
): Promise<{ created: number; skipped: number; errors: string[] }> {
|
||||
let created = 0
|
||||
sourceSessionId?: number,
|
||||
): Promise<{ saved: number; skipped: number }> {
|
||||
let saved = 0
|
||||
let skipped = 0
|
||||
const errors: string[] = []
|
||||
|
||||
const existingTitles = new Set(existingHUs.map(h => (h._cleanTitle || h.title).toLowerCase().trim()))
|
||||
|
||||
for (const hu of analysis.hus) {
|
||||
const normalizedTitle = hu.title.toLowerCase().trim()
|
||||
|
||||
// Verificar duplicado por título similar
|
||||
const isDuplicate = existingHUs.some(ex => {
|
||||
const et = (ex._cleanTitle || ex.title).toLowerCase().trim()
|
||||
return et === normalizedTitle || et.includes(normalizedTitle) || normalizedTitle.includes(et)
|
||||
})
|
||||
|
||||
if (isDuplicate) {
|
||||
skipped++
|
||||
continue
|
||||
}
|
||||
if (isDuplicate) { skipped++; continue }
|
||||
|
||||
try {
|
||||
await kappa.createUserStory({
|
||||
title: hu.title,
|
||||
description: hu.description + (hu.acceptance_criteria.length > 0
|
||||
? `\n\n**Criterios de aceptación:**\n${hu.acceptance_criteria.map(c => `- ${c}`).join('\n')}`
|
||||
: ''),
|
||||
initiative: projectId,
|
||||
priority: hu.priority || 'Media',
|
||||
status: 'todo',
|
||||
})
|
||||
created++
|
||||
} catch (e: any) {
|
||||
errors.push(`${hu.title}: ${e.message}`)
|
||||
}
|
||||
await saveDraft({
|
||||
id: createDraftId(),
|
||||
projectId,
|
||||
title: hu.title,
|
||||
description: hu.description,
|
||||
acceptanceCriteria: hu.acceptance_criteria.join('\n'),
|
||||
priority: hu.priority,
|
||||
type: 'U',
|
||||
sourceSessionId,
|
||||
syncStatus: 'draft',
|
||||
createdAt: new Date().toISOString(),
|
||||
})
|
||||
saved++
|
||||
}
|
||||
|
||||
return { created, skipped, errors }
|
||||
return { saved, skipped }
|
||||
}
|
||||
|
||||
function extractJSON(text: string): string {
|
||||
|
||||
Reference in New Issue
Block a user