conectar frontend con Turso: puente tauri-db.ts + UsersStore con sync KAPPA↔Turso y campos locales
This commit is contained in:
@@ -0,0 +1,126 @@
|
|||||||
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
|
||||||
|
export interface AlphaUserRecord {
|
||||||
|
id: number
|
||||||
|
email: string
|
||||||
|
first_name: string
|
||||||
|
last_name: string
|
||||||
|
role: string | null
|
||||||
|
seniority: string | null
|
||||||
|
cell_id: number | null
|
||||||
|
is_active: boolean
|
||||||
|
created_at: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CellRecord {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
description: string | null
|
||||||
|
created_at: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProjectMemberRecord {
|
||||||
|
id: number
|
||||||
|
user_id: number
|
||||||
|
initiative_id: number
|
||||||
|
initiative_name: string | null
|
||||||
|
role: string | null
|
||||||
|
assigned_at: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AbsenceRecord {
|
||||||
|
id: number
|
||||||
|
user_id: number
|
||||||
|
start_date: string
|
||||||
|
end_date: string
|
||||||
|
type: string
|
||||||
|
reason: string | null
|
||||||
|
created_at: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DailyLogRecord {
|
||||||
|
id: number
|
||||||
|
user_id: number
|
||||||
|
initiative_id: number
|
||||||
|
date: string
|
||||||
|
work_item_id: number | null
|
||||||
|
what_worked_on: string | null
|
||||||
|
progress_pct: number | null
|
||||||
|
impediments: string | null
|
||||||
|
plan_for_tomorrow: string | null
|
||||||
|
hours_spent: number | null
|
||||||
|
created_at: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PerformanceRecord {
|
||||||
|
id: number
|
||||||
|
user_id: number
|
||||||
|
initiative_id: number | null
|
||||||
|
period: string
|
||||||
|
planned_sp: number | null
|
||||||
|
completed_sp: number | null
|
||||||
|
planned_hours: number | null
|
||||||
|
actual_hours: number | null
|
||||||
|
velocity: number | null
|
||||||
|
spi: number | null
|
||||||
|
cpi: number | null
|
||||||
|
compliance_pct: number | null
|
||||||
|
impediment_count: number | null
|
||||||
|
calculated_at: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const tauriDb = {
|
||||||
|
// Users
|
||||||
|
getUsers(): Promise<AlphaUserRecord[]> {
|
||||||
|
return invoke('get_users')
|
||||||
|
},
|
||||||
|
saveUser(user: AlphaUserRecord): Promise<number> {
|
||||||
|
return invoke('save_user', { user })
|
||||||
|
},
|
||||||
|
updateUserFields(id: number, role: string | null, seniority: string | null, cell_id: number | null): Promise<void> {
|
||||||
|
return invoke('update_user_fields', { id, role, seniority, cellId: cell_id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// Cells
|
||||||
|
getCells(): Promise<CellRecord[]> {
|
||||||
|
return invoke('get_cells')
|
||||||
|
},
|
||||||
|
saveCell(cell: CellRecord): Promise<number> {
|
||||||
|
return invoke('save_cell', { cell })
|
||||||
|
},
|
||||||
|
|
||||||
|
// Project Members
|
||||||
|
getProjectMembers(userId: number): Promise<ProjectMemberRecord[]> {
|
||||||
|
return invoke('get_project_members', { userId })
|
||||||
|
},
|
||||||
|
saveProjectMembers(userId: number, members: ProjectMemberRecord[]): Promise<void> {
|
||||||
|
return invoke('save_project_members', { userId, members })
|
||||||
|
},
|
||||||
|
|
||||||
|
// Absences
|
||||||
|
getAbsences(userId: number): Promise<AbsenceRecord[]> {
|
||||||
|
return invoke('get_absences', { userId })
|
||||||
|
},
|
||||||
|
saveAbsence(absence: AbsenceRecord): Promise<number> {
|
||||||
|
return invoke('save_absence', { absence })
|
||||||
|
},
|
||||||
|
deleteAbsence(id: number): Promise<void> {
|
||||||
|
return invoke('delete_absence', { id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// Daily Logs
|
||||||
|
getDailyLogs(userId: number, initiativeId?: number): Promise<DailyLogRecord[]> {
|
||||||
|
return invoke('get_daily_logs', { userId, initiativeId: initiativeId ?? null })
|
||||||
|
},
|
||||||
|
saveDailyLog(log: DailyLogRecord): Promise<number> {
|
||||||
|
return invoke('save_daily_log', { log })
|
||||||
|
},
|
||||||
|
|
||||||
|
// Performance
|
||||||
|
getPerformance(userId: number, initiativeId?: number): Promise<PerformanceRecord[]> {
|
||||||
|
return invoke('get_performance', { userId, initiativeId })
|
||||||
|
},
|
||||||
|
savePerformance(snap: PerformanceRecord): Promise<number> {
|
||||||
|
return invoke('save_performance', { snap })
|
||||||
|
},
|
||||||
|
}
|
||||||
+68
-3
@@ -1,6 +1,7 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { kappa } from '@/services/kappa-api'
|
import { kappa } from '@/services/kappa-api'
|
||||||
|
import { tauriDb, type AlphaUserRecord, type CellRecord } from '@/services/tauri-db'
|
||||||
import type { KappaEmployee } from '@/types/kappa'
|
import type { KappaEmployee } from '@/types/kappa'
|
||||||
|
|
||||||
export interface AlphaUser {
|
export interface AlphaUser {
|
||||||
@@ -11,6 +12,7 @@ export interface AlphaUser {
|
|||||||
full_name: string
|
full_name: string
|
||||||
is_active: boolean
|
is_active: boolean
|
||||||
cell?: string
|
cell?: string
|
||||||
|
cell_id?: number
|
||||||
role?: string
|
role?: string
|
||||||
seniority?: string
|
seniority?: string
|
||||||
projects: string[]
|
projects: string[]
|
||||||
@@ -21,6 +23,7 @@ export interface AlphaUser {
|
|||||||
export const useUsersStore = defineStore('users', () => {
|
export const useUsersStore = defineStore('users', () => {
|
||||||
const users = ref<AlphaUser[]>([])
|
const users = ref<AlphaUser[]>([])
|
||||||
const employees = ref<KappaEmployee[]>([])
|
const employees = ref<KappaEmployee[]>([])
|
||||||
|
const cells = ref<CellRecord[]>([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const error = ref<string | null>(null)
|
const error = ref<string | null>(null)
|
||||||
const totalPages = ref(1)
|
const totalPages = ref(1)
|
||||||
@@ -42,12 +45,15 @@ export const useUsersStore = defineStore('users', () => {
|
|||||||
loading.value = true
|
loading.value = true
|
||||||
error.value = null
|
error.value = null
|
||||||
try {
|
try {
|
||||||
const [rawUsers, page1] = await Promise.all([
|
const [rawUsers, page1, localUsers, localCells] = await Promise.all([
|
||||||
kappa.getUsers(),
|
kappa.getUsers(),
|
||||||
kappa.getEmployees(1),
|
kappa.getEmployees(1),
|
||||||
|
tauriDb.getUsers().catch(() => [] as AlphaUserRecord[]),
|
||||||
|
tauriDb.getCells().catch(() => [] as CellRecord[]),
|
||||||
])
|
])
|
||||||
|
|
||||||
employees.value = page1.results
|
employees.value = page1.results
|
||||||
|
cells.value = localCells
|
||||||
|
|
||||||
totalPages.value = Math.ceil(page1.count / 25)
|
totalPages.value = Math.ceil(page1.count / 25)
|
||||||
if (totalPages.value > 1) {
|
if (totalPages.value > 1) {
|
||||||
@@ -61,7 +67,11 @@ export const useUsersStore = defineStore('users', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
users.value = buildUsers(rawUsers, employees.value)
|
const localMap = new Map(localUsers.map(u => [u.id, u]))
|
||||||
|
users.value = buildUsers(rawUsers, employees.value, localMap)
|
||||||
|
|
||||||
|
// Sync KAPPA users into Turso
|
||||||
|
syncUsersToTurso(users.value)
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
error.value = e.message
|
error.value = e.message
|
||||||
} finally {
|
} finally {
|
||||||
@@ -69,9 +79,13 @@ export const useUsersStore = defineStore('users', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildUsers(raw: unknown[], emps: KappaEmployee[]): AlphaUser[] {
|
function buildUsers(raw: unknown[], emps: KappaEmployee[], localMap: Map<number, AlphaUserRecord>): AlphaUser[] {
|
||||||
|
const cellMap = new Map(cells.value.map(c => [c.id, c.name]))
|
||||||
|
|
||||||
return raw.map((u: any) => {
|
return raw.map((u: any) => {
|
||||||
const userEmps = emps.filter(e => e.user === u.id)
|
const userEmps = emps.filter(e => e.user === u.id)
|
||||||
|
const local = localMap.get(u.id)
|
||||||
|
const cellName = local?.cell_id ? cellMap.get(local.cell_id) : undefined
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: u.id,
|
id: u.id,
|
||||||
@@ -80,6 +94,10 @@ export const useUsersStore = defineStore('users', () => {
|
|||||||
last_name: u.last_name || '',
|
last_name: u.last_name || '',
|
||||||
full_name: u.full_name || `${u.first_name || ''} ${u.last_name || ''}`.trim() || u.email,
|
full_name: u.full_name || `${u.first_name || ''} ${u.last_name || ''}`.trim() || u.email,
|
||||||
is_active: u.is_active !== undefined ? u.is_active : true,
|
is_active: u.is_active !== undefined ? u.is_active : true,
|
||||||
|
cell: cellName,
|
||||||
|
cell_id: local?.cell_id ?? undefined,
|
||||||
|
role: local?.role ?? undefined,
|
||||||
|
seniority: local?.seniority ?? undefined,
|
||||||
projects: userEmps
|
projects: userEmps
|
||||||
.filter(e => e.initiative_name)
|
.filter(e => e.initiative_name)
|
||||||
.map(e => e.initiative_name!),
|
.map(e => e.initiative_name!),
|
||||||
@@ -89,6 +107,37 @@ export const useUsersStore = defineStore('users', () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function syncUsersToTurso(userList: AlphaUser[]) {
|
||||||
|
for (const u of userList) {
|
||||||
|
await tauriDb.saveUser({
|
||||||
|
id: u.id,
|
||||||
|
email: u.email,
|
||||||
|
first_name: u.first_name,
|
||||||
|
last_name: u.last_name,
|
||||||
|
role: u.role ?? null,
|
||||||
|
seniority: u.seniority ?? null,
|
||||||
|
cell_id: u.cell_id ?? null,
|
||||||
|
is_active: u.is_active,
|
||||||
|
created_at: null,
|
||||||
|
}).catch(() => {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateUserFields(id: number, role: string | null, seniority: string | null, cellId: number | null) {
|
||||||
|
await tauriDb.updateUserFields(id, role, seniority, cellId)
|
||||||
|
const idx = users.value.findIndex(u => u.id === id)
|
||||||
|
if (idx !== -1) {
|
||||||
|
const cellName = cellId ? cells.value.find(c => c.id === cellId)?.name : undefined
|
||||||
|
users.value[idx] = {
|
||||||
|
...users.value[idx],
|
||||||
|
role: role ?? undefined,
|
||||||
|
seniority: seniority ?? undefined,
|
||||||
|
cell: cellName,
|
||||||
|
cell_id: cellId ?? undefined,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function updateLocal(id: number, data: Partial<AlphaUser>) {
|
function updateLocal(id: number, data: Partial<AlphaUser>) {
|
||||||
const idx = users.value.findIndex(u => u.id === id)
|
const idx = users.value.findIndex(u => u.id === id)
|
||||||
if (idx !== -1) {
|
if (idx !== -1) {
|
||||||
@@ -96,9 +145,23 @@ export const useUsersStore = defineStore('users', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function createCell(name: string, description?: string): Promise<CellRecord> {
|
||||||
|
const cell: CellRecord = {
|
||||||
|
id: 0,
|
||||||
|
name,
|
||||||
|
description: description ?? null,
|
||||||
|
created_at: null,
|
||||||
|
}
|
||||||
|
const id = await tauriDb.saveCell(cell)
|
||||||
|
const created = { ...cell, id }
|
||||||
|
cells.value.push(created)
|
||||||
|
return created
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
users,
|
users,
|
||||||
employees,
|
employees,
|
||||||
|
cells,
|
||||||
loading,
|
loading,
|
||||||
error,
|
error,
|
||||||
totalPages,
|
totalPages,
|
||||||
@@ -107,5 +170,7 @@ export const useUsersStore = defineStore('users', () => {
|
|||||||
devsByProject,
|
devsByProject,
|
||||||
fetchAll,
|
fetchAll,
|
||||||
updateLocal,
|
updateLocal,
|
||||||
|
updateUserFields,
|
||||||
|
createCell,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user