cells: modulo de celulas + miembros + reemplazo de roles en UsersView

- db.ts v7: tablas cells + cell_members con compound key
- cells-db.ts: CRUD celulas, miembros, getAllCellsWithCounts
- UsersView: rediseñado con cards de celulas en vez de roles
- UsersView: crear/editar/eliminar celulas (normal/transversal)
- UsersView: añadir/remover miembros por celula
- UsersView: tabla de usuarios muestra celulas a las que pertenece
This commit is contained in:
2026-05-29 01:30:50 -05:00
parent 63770685da
commit 388fa09f3e
8 changed files with 340 additions and 253 deletions
+65
View File
@@ -0,0 +1,65 @@
import db, { type CellRecord, type CellMemberRecord } from '@/services/db'
export type { CellRecord, CellMemberRecord }
// ─── Cells ──────────────────────────────────────────────
export async function getCells(): Promise<CellRecord[]> {
return db.cells.toArray()
}
export async function getCell(id: string): Promise<CellRecord | undefined> {
return db.cells.get(id)
}
export async function saveCell(c: CellRecord) {
await db.cells.put(c)
}
export async function deleteCell(id: string) {
await db.cells.delete(id)
await db.cell_members.where('cellId').equals(id).delete()
}
export function createCellId(): string {
return crypto.randomUUID()
}
// ─── Cell Members ─────────────────────────────────────────
export async function getMembers(cellId: string): Promise<CellMemberRecord[]> {
return db.cell_members.where('cellId').equals(cellId).toArray()
}
export async function addMember(m: CellMemberRecord) {
await db.cell_members.put(m)
}
export async function removeMember(cellId: string, userId: number) {
await db.table('cell_members').delete([cellId, userId] as any)
}
export async function getUserCells(userId: number): Promise<CellRecord[]> {
const members = await db.cell_members.where('userId').equals(userId).toArray()
const cellIds = members.map(m => m.cellId)
const cells: CellRecord[] = []
for (const id of cellIds) {
const c = await db.cells.get(id)
if (c) cells.push(c)
}
return cells
}
export async function getCellWithMembers(cellId: string): Promise<{ cell: CellRecord | undefined; members: CellMemberRecord[] }> {
const [cell, members] = await Promise.all([db.cells.get(cellId), db.cell_members.where('cellId').equals(cellId).toArray()])
return { cell, members }
}
export async function getAllCellsWithCounts(): Promise<(CellRecord & { memberCount: number })[]> {
const cells = await db.cells.toArray()
const counts = await db.cell_members.toArray()
const countMap = new Map<string, number>()
for (const m of counts) {
countMap.set(m.cellId, (countMap.get(m.cellId) || 0) + 1)
}
return cells.map(c => ({ ...c, memberCount: countMap.get(c.id) || 0 }))
}
+20 -1
View File
@@ -96,6 +96,21 @@ export interface LookupRecord {
description: string | null
}
export interface CellRecord {
id: string // UUID
name: string
description: string
type: 'normal' | 'transversal'
createdAt: string
}
export interface CellMemberRecord {
cellId: string
userId: number
roleInCell: string // 'dev' | 'qa' | 'ba' | 'pm' | 'tl'
addedAt: string
}
const db = new Dexie('alpha-core') as Dexie & {
settings: Dexie.Table<SettingEntry, string>
project_docs: Dexie.Table<ProjectDocRecord, number>
@@ -106,9 +121,11 @@ const db = new Dexie('alpha-core') as Dexie & {
qa_plans: Dexie.Table<QAPlanRecord, string>
users: Dexie.Table<UserRecord, number>
lookups: Dexie.Table<LookupRecord, string>
cells: Dexie.Table<CellRecord, string>
cell_members: Dexie.Table<CellMemberRecord, string>
}
db.version(6).stores({
db.version(7).stores({
settings: '&key',
project_docs: '&projectId, projectName, updatedAt',
sessions: '++id, projectId, date',
@@ -118,6 +135,8 @@ db.version(6).stores({
qa_plans: '&id, projectId',
users: '&id',
lookups: '&[type+id], type',
cells: '&id',
cell_members: '[cellId+userId], cellId, userId',
})
export default db
+1
View File
@@ -1,4 +1,5 @@
import db, { type UserRecord, type LookupRecord } from '@/services/db'
export type { UserRecord, LookupRecord }
// ─── Users ───────────────────────────────────────────────