355 lines
9.3 KiB
TypeScript
355 lines
9.3 KiB
TypeScript
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
import type { Database, DepartmentDashboard, DashboardPanel, DepartmentChecklist, DepartmentChecklistItem, DepartmentNote, ModuleType, LayoutPreset } from '$lib/supabase/types';
|
|
import { createLogger } from '$lib/utils/logger';
|
|
|
|
const log = createLogger('api.department-dashboard');
|
|
|
|
// ============================================================
|
|
// Dashboard
|
|
// ============================================================
|
|
|
|
export interface DashboardWithPanels extends DepartmentDashboard {
|
|
panels: DashboardPanel[];
|
|
}
|
|
|
|
export async function fetchDashboard(
|
|
supabase: SupabaseClient<Database>,
|
|
departmentId: string
|
|
): Promise<DashboardWithPanels | null> {
|
|
const { data, error } = await supabase
|
|
.from('department_dashboards')
|
|
.select('*, panels:dashboard_panels(*)')
|
|
.eq('department_id', departmentId)
|
|
.single();
|
|
|
|
if (error) {
|
|
if (error.code === 'PGRST116') return null;
|
|
log.error('fetchDashboard failed', { error, data: { departmentId } });
|
|
throw error;
|
|
}
|
|
|
|
const dashboard = data as any;
|
|
return {
|
|
...dashboard,
|
|
panels: (dashboard.panels ?? []).sort((a: DashboardPanel, b: DashboardPanel) => a.position - b.position),
|
|
};
|
|
}
|
|
|
|
export async function updateDashboardLayout(
|
|
supabase: SupabaseClient<Database>,
|
|
dashboardId: string,
|
|
layout: LayoutPreset
|
|
): Promise<DepartmentDashboard> {
|
|
const { data, error } = await supabase
|
|
.from('department_dashboards')
|
|
.update({ layout, updated_at: new Date().toISOString() })
|
|
.eq('id', dashboardId)
|
|
.select()
|
|
.single();
|
|
|
|
if (error) {
|
|
log.error('updateDashboardLayout failed', { error, data: { dashboardId, layout } });
|
|
throw error;
|
|
}
|
|
return data as unknown as DepartmentDashboard;
|
|
}
|
|
|
|
// ============================================================
|
|
// Panels
|
|
// ============================================================
|
|
|
|
export async function addPanel(
|
|
supabase: SupabaseClient<Database>,
|
|
dashboardId: string,
|
|
module: ModuleType,
|
|
position: number,
|
|
width: string = 'half'
|
|
): Promise<DashboardPanel> {
|
|
const { data, error } = await supabase
|
|
.from('dashboard_panels')
|
|
.insert({ dashboard_id: dashboardId, module, position, width })
|
|
.select()
|
|
.single();
|
|
|
|
if (error) {
|
|
log.error('addPanel failed', { error, data: { dashboardId, module } });
|
|
throw error;
|
|
}
|
|
return data as unknown as DashboardPanel;
|
|
}
|
|
|
|
export async function updatePanel(
|
|
supabase: SupabaseClient<Database>,
|
|
panelId: string,
|
|
params: Partial<Pick<DashboardPanel, 'position' | 'width' | 'config'>>
|
|
): Promise<DashboardPanel> {
|
|
const { data, error } = await supabase
|
|
.from('dashboard_panels')
|
|
.update(params)
|
|
.eq('id', panelId)
|
|
.select()
|
|
.single();
|
|
|
|
if (error) {
|
|
log.error('updatePanel failed', { error, data: { panelId } });
|
|
throw error;
|
|
}
|
|
return data as unknown as DashboardPanel;
|
|
}
|
|
|
|
export async function removePanel(
|
|
supabase: SupabaseClient<Database>,
|
|
panelId: string
|
|
): Promise<void> {
|
|
const { error } = await supabase
|
|
.from('dashboard_panels')
|
|
.delete()
|
|
.eq('id', panelId);
|
|
|
|
if (error) {
|
|
log.error('removePanel failed', { error, data: { panelId } });
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// ============================================================
|
|
// Checklists
|
|
// ============================================================
|
|
|
|
export interface ChecklistWithItems extends DepartmentChecklist {
|
|
items: DepartmentChecklistItem[];
|
|
}
|
|
|
|
export async function fetchChecklists(
|
|
supabase: SupabaseClient<Database>,
|
|
departmentId: string
|
|
): Promise<ChecklistWithItems[]> {
|
|
const { data: checklists, error } = await supabase
|
|
.from('department_checklists')
|
|
.select('*')
|
|
.eq('department_id', departmentId)
|
|
.order('sort_order');
|
|
|
|
if (error) {
|
|
log.error('fetchChecklists failed', { error, data: { departmentId } });
|
|
throw error;
|
|
}
|
|
|
|
if (!checklists || checklists.length === 0) return [];
|
|
|
|
const checklistIds = checklists.map(c => c.id);
|
|
const { data: items, error: itemsError } = await supabase
|
|
.from('department_checklist_items')
|
|
.select('*')
|
|
.in('checklist_id', checklistIds)
|
|
.order('sort_order');
|
|
|
|
if (itemsError) {
|
|
log.error('fetchChecklistItems failed', { error: itemsError });
|
|
throw itemsError;
|
|
}
|
|
|
|
const itemsByChecklist: Record<string, DepartmentChecklistItem[]> = {};
|
|
for (const item of (items ?? [])) {
|
|
if (!itemsByChecklist[item.checklist_id]) itemsByChecklist[item.checklist_id] = [];
|
|
itemsByChecklist[item.checklist_id].push(item as unknown as DepartmentChecklistItem);
|
|
}
|
|
|
|
return checklists.map(c => ({
|
|
...(c as unknown as DepartmentChecklist),
|
|
items: itemsByChecklist[c.id] ?? [],
|
|
}));
|
|
}
|
|
|
|
export async function createChecklist(
|
|
supabase: SupabaseClient<Database>,
|
|
departmentId: string,
|
|
title: string,
|
|
userId?: string
|
|
): Promise<DepartmentChecklist> {
|
|
const { data, error } = await supabase
|
|
.from('department_checklists')
|
|
.insert({ department_id: departmentId, title, created_by: userId ?? null })
|
|
.select()
|
|
.single();
|
|
|
|
if (error) {
|
|
log.error('createChecklist failed', { error, data: { departmentId, title } });
|
|
throw error;
|
|
}
|
|
return data as unknown as DepartmentChecklist;
|
|
}
|
|
|
|
export async function deleteChecklist(
|
|
supabase: SupabaseClient<Database>,
|
|
checklistId: string
|
|
): Promise<void> {
|
|
const { error } = await supabase
|
|
.from('department_checklists')
|
|
.delete()
|
|
.eq('id', checklistId);
|
|
|
|
if (error) {
|
|
log.error('deleteChecklist failed', { error, data: { checklistId } });
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
export async function renameChecklist(
|
|
supabase: SupabaseClient<Database>,
|
|
checklistId: string,
|
|
title: string
|
|
): Promise<DepartmentChecklist> {
|
|
const { data, error } = await supabase
|
|
.from('department_checklists')
|
|
.update({ title })
|
|
.eq('id', checklistId)
|
|
.select()
|
|
.single();
|
|
|
|
if (error) {
|
|
log.error('renameChecklist failed', { error, data: { checklistId, title } });
|
|
throw error;
|
|
}
|
|
return data as unknown as DepartmentChecklist;
|
|
}
|
|
|
|
// ============================================================
|
|
// Checklist Items
|
|
// ============================================================
|
|
|
|
export async function addChecklistItem(
|
|
supabase: SupabaseClient<Database>,
|
|
checklistId: string,
|
|
content: string,
|
|
sortOrder: number = 0
|
|
): Promise<DepartmentChecklistItem> {
|
|
const { data, error } = await supabase
|
|
.from('department_checklist_items')
|
|
.insert({ checklist_id: checklistId, content, sort_order: sortOrder })
|
|
.select()
|
|
.single();
|
|
|
|
if (error) {
|
|
log.error('addChecklistItem failed', { error, data: { checklistId, content } });
|
|
throw error;
|
|
}
|
|
return data as unknown as DepartmentChecklistItem;
|
|
}
|
|
|
|
export async function updateChecklistItem(
|
|
supabase: SupabaseClient<Database>,
|
|
itemId: string,
|
|
params: Partial<Pick<DepartmentChecklistItem, 'content' | 'is_completed' | 'assigned_to' | 'due_date' | 'sort_order'>>
|
|
): Promise<DepartmentChecklistItem> {
|
|
const { data, error } = await supabase
|
|
.from('department_checklist_items')
|
|
.update({ ...params, updated_at: new Date().toISOString() })
|
|
.eq('id', itemId)
|
|
.select()
|
|
.single();
|
|
|
|
if (error) {
|
|
log.error('updateChecklistItem failed', { error, data: { itemId } });
|
|
throw error;
|
|
}
|
|
return data as unknown as DepartmentChecklistItem;
|
|
}
|
|
|
|
export async function deleteChecklistItem(
|
|
supabase: SupabaseClient<Database>,
|
|
itemId: string
|
|
): Promise<void> {
|
|
const { error } = await supabase
|
|
.from('department_checklist_items')
|
|
.delete()
|
|
.eq('id', itemId);
|
|
|
|
if (error) {
|
|
log.error('deleteChecklistItem failed', { error, data: { itemId } });
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
export async function toggleChecklistItem(
|
|
supabase: SupabaseClient<Database>,
|
|
itemId: string,
|
|
isCompleted: boolean
|
|
): Promise<DepartmentChecklistItem> {
|
|
return updateChecklistItem(supabase, itemId, { is_completed: isCompleted });
|
|
}
|
|
|
|
// ============================================================
|
|
// Notes
|
|
// ============================================================
|
|
|
|
export async function fetchNotes(
|
|
supabase: SupabaseClient<Database>,
|
|
departmentId: string
|
|
): Promise<DepartmentNote[]> {
|
|
const { data, error } = await supabase
|
|
.from('department_notes')
|
|
.select('*')
|
|
.eq('department_id', departmentId)
|
|
.order('sort_order');
|
|
|
|
if (error) {
|
|
log.error('fetchNotes failed', { error, data: { departmentId } });
|
|
throw error;
|
|
}
|
|
return (data ?? []) as unknown as DepartmentNote[];
|
|
}
|
|
|
|
export async function createNote(
|
|
supabase: SupabaseClient<Database>,
|
|
departmentId: string,
|
|
title: string,
|
|
userId?: string
|
|
): Promise<DepartmentNote> {
|
|
const { data, error } = await supabase
|
|
.from('department_notes')
|
|
.insert({ department_id: departmentId, title, created_by: userId ?? null })
|
|
.select()
|
|
.single();
|
|
|
|
if (error) {
|
|
log.error('createNote failed', { error, data: { departmentId, title } });
|
|
throw error;
|
|
}
|
|
return data as unknown as DepartmentNote;
|
|
}
|
|
|
|
export async function updateNote(
|
|
supabase: SupabaseClient<Database>,
|
|
noteId: string,
|
|
params: Partial<Pick<DepartmentNote, 'title' | 'content' | 'sort_order'>>
|
|
): Promise<DepartmentNote> {
|
|
const { data, error } = await supabase
|
|
.from('department_notes')
|
|
.update({ ...params, updated_at: new Date().toISOString() })
|
|
.eq('id', noteId)
|
|
.select()
|
|
.single();
|
|
|
|
if (error) {
|
|
log.error('updateNote failed', { error, data: { noteId } });
|
|
throw error;
|
|
}
|
|
return data as unknown as DepartmentNote;
|
|
}
|
|
|
|
export async function deleteNote(
|
|
supabase: SupabaseClient<Database>,
|
|
noteId: string
|
|
): Promise<void> {
|
|
const { error } = await supabase
|
|
.from('department_notes')
|
|
.delete()
|
|
.eq('id', noteId);
|
|
|
|
if (error) {
|
|
log.error('deleteNote failed', { error, data: { noteId } });
|
|
throw error;
|
|
}
|
|
}
|