Mega push vol 6, started adding many awesome stuff, chat broken rn

This commit is contained in:
AlacrisDevs
2026-02-07 15:11:28 +02:00
parent f9dc950394
commit dcee479839
19 changed files with 1634 additions and 209 deletions

266
src/lib/api/event-tasks.ts Normal file
View File

@@ -0,0 +1,266 @@
import type { SupabaseClient } from '@supabase/supabase-js';
import type { Database, EventTaskColumn, EventTask } from '$lib/supabase/types';
import { createLogger } from '$lib/utils/logger';
const log = createLogger('api.event-tasks');
export interface TaskColumnWithTasks extends EventTaskColumn {
cards: EventTask[];
}
// ============================================================
// Columns
// ============================================================
export async function fetchTaskColumns(
supabase: SupabaseClient<Database>,
eventId: string
): Promise<TaskColumnWithTasks[]> {
const { data: columns, error: colErr } = await supabase
.from('event_task_columns')
.select('*')
.eq('event_id', eventId)
.order('position');
if (colErr) {
log.error('Failed to fetch task columns', { error: colErr, data: { eventId } });
throw colErr;
}
const { data: tasks, error: taskErr } = await supabase
.from('event_tasks')
.select('*')
.eq('event_id', eventId)
.order('position');
if (taskErr) {
log.error('Failed to fetch tasks', { error: taskErr, data: { eventId } });
throw taskErr;
}
const tasksByColumn = new Map<string, EventTask[]>();
for (const task of tasks ?? []) {
const arr = tasksByColumn.get(task.column_id) ?? [];
arr.push(task);
tasksByColumn.set(task.column_id, arr);
}
return (columns ?? []).map((col) => ({
...col,
cards: tasksByColumn.get(col.id) ?? [],
}));
}
export async function createTaskColumn(
supabase: SupabaseClient<Database>,
eventId: string,
name: string,
position?: number
): Promise<EventTaskColumn> {
if (position === undefined) {
const { count } = await supabase
.from('event_task_columns')
.select('*', { count: 'exact', head: true })
.eq('event_id', eventId);
position = count ?? 0;
}
const { data, error } = await supabase
.from('event_task_columns')
.insert({ event_id: eventId, name, position })
.select()
.single();
if (error || !data) {
log.error('Failed to create task column', { error, data: { eventId, name } });
throw error;
}
return data;
}
export async function renameTaskColumn(
supabase: SupabaseClient<Database>,
columnId: string,
name: string
): Promise<void> {
const { error } = await supabase
.from('event_task_columns')
.update({ name })
.eq('id', columnId);
if (error) {
log.error('Failed to rename task column', { error, data: { columnId, name } });
throw error;
}
}
export async function deleteTaskColumn(
supabase: SupabaseClient<Database>,
columnId: string
): Promise<void> {
const { error } = await supabase
.from('event_task_columns')
.delete()
.eq('id', columnId);
if (error) {
log.error('Failed to delete task column', { error, data: { columnId } });
throw error;
}
}
// ============================================================
// Tasks
// ============================================================
export async function createTask(
supabase: SupabaseClient<Database>,
eventId: string,
columnId: string,
title: string,
createdBy?: string
): Promise<EventTask> {
const { count } = await supabase
.from('event_tasks')
.select('*', { count: 'exact', head: true })
.eq('column_id', columnId);
const { data, error } = await supabase
.from('event_tasks')
.insert({
event_id: eventId,
column_id: columnId,
title,
position: count ?? 0,
created_by: createdBy ?? null,
})
.select()
.single();
if (error || !data) {
log.error('Failed to create task', { error, data: { eventId, columnId, title } });
throw error;
}
return data;
}
export async function updateTask(
supabase: SupabaseClient<Database>,
taskId: string,
updates: Partial<Pick<EventTask, 'title' | 'description' | 'priority' | 'due_date' | 'color' | 'assignee_id'>>
): Promise<void> {
const { error } = await supabase
.from('event_tasks')
.update({ ...updates, updated_at: new Date().toISOString() })
.eq('id', taskId);
if (error) {
log.error('Failed to update task', { error, data: { taskId, updates } });
throw error;
}
}
export async function deleteTask(
supabase: SupabaseClient<Database>,
taskId: string
): Promise<void> {
const { error } = await supabase
.from('event_tasks')
.delete()
.eq('id', taskId);
if (error) {
log.error('Failed to delete task', { error, data: { taskId } });
throw error;
}
}
export async function moveTask(
supabase: SupabaseClient<Database>,
taskId: string,
newColumnId: string,
newPosition: number
): Promise<void> {
// Fetch all tasks in the target column
const { data: colTasks, error: fetchErr } = await supabase
.from('event_tasks')
.select('id, position')
.eq('column_id', newColumnId)
.order('position');
if (fetchErr) {
log.error('Failed to fetch column tasks for reorder', { error: fetchErr });
throw fetchErr;
}
// Build the new order
const existing = (colTasks ?? []).filter((t) => t.id !== taskId);
existing.splice(newPosition, 0, { id: taskId, position: newPosition });
// Update positions + column for changed tasks
const updates = existing
.map((t, i) => ({ id: t.id, position: i, column_id: newColumnId }))
.filter((t, i) => {
const orig = colTasks?.find((c) => c.id === t.id);
return !orig || orig.position !== i || t.id === taskId;
});
if (updates.length > 0) {
await Promise.all(
updates.map((u) =>
supabase
.from('event_tasks')
.update({ column_id: u.column_id, position: u.position, updated_at: new Date().toISOString() })
.eq('id', u.id)
)
);
}
}
// ============================================================
// Realtime
// ============================================================
export interface RealtimeChangePayload<T = Record<string, unknown>> {
event: 'INSERT' | 'UPDATE' | 'DELETE';
new: T;
old: Partial<T>;
}
export function subscribeToEventTasks(
supabase: SupabaseClient<Database>,
eventId: string,
columnIds: string[],
onColumnChange: (payload: RealtimeChangePayload<EventTaskColumn>) => void,
onTaskChange: (payload: RealtimeChangePayload<EventTask>) => void
) {
const channel = supabase.channel(`event-tasks:${eventId}`);
const columnIdSet = new Set(columnIds);
channel
.on('postgres_changes', { event: '*', schema: 'public', table: 'event_task_columns', filter: `event_id=eq.${eventId}` },
(payload) => onColumnChange({
event: payload.eventType as 'INSERT' | 'UPDATE' | 'DELETE',
new: payload.new as EventTaskColumn,
old: payload.old as Partial<EventTaskColumn>,
})
)
.on('postgres_changes', { event: '*', schema: 'public', table: 'event_tasks', filter: `event_id=eq.${eventId}` },
(payload) => {
const task = (payload.new ?? payload.old) as Partial<EventTask>;
const colId = task.column_id ?? (payload.old as Partial<EventTask>)?.column_id;
if (colId && !columnIdSet.has(colId)) return;
onTaskChange({
event: payload.eventType as 'INSERT' | 'UPDATE' | 'DELETE',
new: payload.new as EventTask,
old: payload.old as Partial<EventTask>,
});
}
)
.subscribe();
return channel;
}