proyectos: status true/false ahora muestra Activo/Inactivo via i18n
This commit is contained in:
+30
-4
@@ -11,6 +11,8 @@ pub struct Project {
|
||||
pub status: String,
|
||||
pub start_date: Option<String>,
|
||||
pub end_date: Option<String>,
|
||||
pub hus_count: Option<i64>,
|
||||
pub epics_count: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
@@ -155,7 +157,7 @@ async fn get_conn(db_path: &str) -> Result<libsql::Connection, String> {
|
||||
let conn = db.connect().map_err(|e| format!("Connect: {e}"))?;
|
||||
|
||||
conn.execute_batch(
|
||||
"CREATE TABLE IF NOT EXISTS projects (
|
||||
" CREATE TABLE IF NOT EXISTS projects (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
key TEXT,
|
||||
@@ -163,6 +165,8 @@ async fn get_conn(db_path: &str) -> Result<libsql::Connection, String> {
|
||||
status TEXT DEFAULT 'active',
|
||||
start_date TEXT,
|
||||
end_date TEXT,
|
||||
hus_count INTEGER DEFAULT 0,
|
||||
epics_count INTEGER DEFAULT 0,
|
||||
created_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
@@ -325,7 +329,7 @@ pub mod commands {
|
||||
let conn = get_conn(&db_path).await?;
|
||||
|
||||
let mut rows = conn
|
||||
.query("SELECT id, name, key, description, status, start_date, end_date FROM projects ORDER BY name", ())
|
||||
.query("SELECT id, name, key, description, status, start_date, end_date, hus_count, epics_count FROM projects ORDER BY name", ())
|
||||
.await
|
||||
.map_err(|e| format!("Query: {e}"))?;
|
||||
|
||||
@@ -339,6 +343,8 @@ pub mod commands {
|
||||
status: row.get::<String>(4).unwrap_or_else(|_| "active".into()),
|
||||
start_date: row.get::<String>(5).ok(),
|
||||
end_date: row.get::<String>(6).ok(),
|
||||
hus_count: row.get::<i64>(7).ok(),
|
||||
epics_count: row.get::<i64>(8).ok(),
|
||||
});
|
||||
}
|
||||
Ok(projects)
|
||||
@@ -350,8 +356,8 @@ pub mod commands {
|
||||
let conn = get_conn(&db_path).await?;
|
||||
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO projects (id, name, key, description, status, start_date, end_date)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
|
||||
"INSERT OR REPLACE INTO projects (id, name, key, description, status, start_date, end_date, hus_count, epics_count)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)",
|
||||
libsql::params![
|
||||
project.id,
|
||||
project.name,
|
||||
@@ -360,6 +366,8 @@ pub mod commands {
|
||||
project.status,
|
||||
project.start_date,
|
||||
project.end_date,
|
||||
project.hus_count,
|
||||
project.epics_count,
|
||||
],
|
||||
)
|
||||
.await
|
||||
@@ -368,6 +376,24 @@ pub mod commands {
|
||||
Ok(conn.last_insert_rowid())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn update_project_counts(state: State<'_, Mutex<String>>, initiative_id: i64) -> Result<(), String> {
|
||||
let db_path = state.lock().map_err(|e| e.to_string())?.clone();
|
||||
let conn = get_conn(&db_path).await?;
|
||||
|
||||
conn.execute(
|
||||
"UPDATE projects SET
|
||||
hus_count = (SELECT COUNT(*) FROM user_stories WHERE initiative_id = ?1),
|
||||
epics_count = (SELECT COUNT(*) FROM epics WHERE initiative_id = ?1)
|
||||
WHERE id = ?1",
|
||||
libsql::params![initiative_id],
|
||||
)
|
||||
.await
|
||||
.map_err(|e| format!("Update counts: {e}"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn delete_project(state: State<'_, Mutex<String>>, id: i64) -> Result<(), String> {
|
||||
let db_path = state.lock().map_err(|e| e.to_string())?.clone();
|
||||
|
||||
@@ -22,6 +22,7 @@ fn main() {
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
commands::get_projects,
|
||||
commands::save_project,
|
||||
commands::update_project_counts,
|
||||
commands::delete_project,
|
||||
commands::get_work_items,
|
||||
commands::save_work_item,
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"common": {
|
||||
"loading": "Loading...",
|
||||
"retry": "Retry",
|
||||
"backToProjects": "← Back to Projects",
|
||||
"backToProjects": "Back to Projects",
|
||||
"noDescription": "No description"
|
||||
},
|
||||
"sidebar": {
|
||||
@@ -79,6 +79,8 @@
|
||||
"selectProject": "Select a project from the sidebar"
|
||||
},
|
||||
"status": {
|
||||
"active": "Active",
|
||||
"inactive": "Inactive",
|
||||
"backlog": "Backlog",
|
||||
"todo": "To do",
|
||||
"inProgress": "In progress",
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"common": {
|
||||
"loading": "Cargando...",
|
||||
"retry": "Reintentar",
|
||||
"backToProjects": "← Volver a Proyectos",
|
||||
"backToProjects": "Volver a Proyectos",
|
||||
"noDescription": "Sin descripción"
|
||||
},
|
||||
"sidebar": {
|
||||
@@ -79,6 +79,8 @@
|
||||
"selectProject": "Seleccioná un proyecto del panel lateral"
|
||||
},
|
||||
"status": {
|
||||
"active": "Activo",
|
||||
"inactive": "Inactivo",
|
||||
"backlog": "Backlog",
|
||||
"todo": "Por hacer",
|
||||
"inProgress": "En progreso",
|
||||
|
||||
@@ -18,6 +18,8 @@ export interface ProjectRecord {
|
||||
status: string
|
||||
start_date: string | null
|
||||
end_date: string | null
|
||||
hus_count: number | null
|
||||
epics_count: number | null
|
||||
}
|
||||
|
||||
export interface EpicRecord {
|
||||
@@ -141,6 +143,9 @@ export const tauriDb = {
|
||||
saveProject(project: ProjectRecord): Promise<number> {
|
||||
return safeInvoke('save_project', { project })
|
||||
},
|
||||
updateProjectCounts(initiativeId: number): Promise<void> {
|
||||
return safeInvoke('update_project_counts', { initiativeId })
|
||||
},
|
||||
deleteProject(id: number): Promise<void> {
|
||||
return safeInvoke('delete_project', { id })
|
||||
},
|
||||
|
||||
@@ -49,6 +49,8 @@ export const useProjectsStore = defineStore('projects', () => {
|
||||
status: p.status || 'active',
|
||||
start_date: p.start_date ?? null,
|
||||
end_date: p.end_date ?? null,
|
||||
hus_count: null,
|
||||
epics_count: null,
|
||||
}).catch(() => {})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +114,8 @@ export const useWorkItemsStore = defineStore('workitems', () => {
|
||||
if (isFirstVisit || newEpics.length > 0) {
|
||||
await syncEpicsToTurso(id, isFirstVisit ? epicData : newEpics)
|
||||
}
|
||||
// Actualizar contadores en projects
|
||||
await tauriDb.updateProjectCounts(id).catch(() => {})
|
||||
|
||||
firstVisit.value.add(id)
|
||||
} catch (e: any) {
|
||||
|
||||
@@ -84,7 +84,7 @@ const statusLabel = (status: unknown) => {
|
||||
<FileText class="size-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div class="text-2xl font-bold">{{ workItems.totalHUs }}</div>
|
||||
<div id="dashboard-stats-hus-count" class="text-2xl font-bold">{{ workItems.totalHUs }}</div>
|
||||
<p class="text-xs text-muted-foreground">{{ t('dashboard.husSubtitle') }}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@@ -136,7 +136,7 @@ const tabContent: Record<string, { title: string; description: string; cards: {
|
||||
<SiteHeader :active-tab="viewingProject ? 'projects' : activeTab" />
|
||||
<div class="flex flex-1 flex-col">
|
||||
<div class="@container/main flex flex-1 flex-col gap-2">
|
||||
<div class="flex flex-col gap-4 py-4 md:gap-6 md:py-6">
|
||||
<div class="flex flex-col gap-0 py-4 md:gap-0 md:py-6">
|
||||
<template v-if="viewingProject">
|
||||
<div class="px-4 lg:px-6">
|
||||
<button
|
||||
|
||||
@@ -15,6 +15,20 @@ import { Skeleton } from "@/components/ui/skeleton"
|
||||
const { t } = useI18n()
|
||||
const projects = useProjectsStore()
|
||||
|
||||
function getStatusVariant(status?: string) {
|
||||
const s = String(status ?? '').toLowerCase()
|
||||
if (s === 'true' || ['active', 'completado', 'done', 'completed'].includes(s)) return 'default'
|
||||
if (s === 'false' || ['inactive', 'paused', 'cancelled'].includes(s)) return 'secondary'
|
||||
return 'outline'
|
||||
}
|
||||
|
||||
function getStatusLabel(status?: string) {
|
||||
const s = String(status ?? '').toLowerCase()
|
||||
if (s === 'true' || s === 'active') return t('status.active')
|
||||
if (s === 'false' || s === 'inactive') return t('status.inactive')
|
||||
return s || '—'
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
'select-project': [id: number]
|
||||
}>()
|
||||
@@ -23,14 +37,6 @@ onMounted(() => {
|
||||
projects.fetchProjects()
|
||||
})
|
||||
|
||||
function getStatusVariant(status?: string) {
|
||||
switch (status) {
|
||||
case 'active': return 'default'
|
||||
case 'completed': return 'secondary'
|
||||
case 'paused': return 'outline'
|
||||
default: return 'default'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -95,8 +101,8 @@ function getStatusVariant(status?: string) {
|
||||
<CardTitle class="text-base">
|
||||
{{ p.initiative_name || p.name || t('projects.unnamedFallback', { id: p.id }) }}
|
||||
</CardTitle>
|
||||
<Badge :variant="getStatusVariant(p.status)">
|
||||
{{ p.status || 'active' }}
|
||||
<Badge id="projects-status-badge" :variant="getStatusVariant(p.status)">
|
||||
{{ getStatusLabel(p.status) }}
|
||||
</Badge>
|
||||
</div>
|
||||
<CardDescription class="line-clamp-2">
|
||||
|
||||
Reference in New Issue
Block a user