chore: supabase CLI setup, migrations, type regeneration - Install supabase CLI as dev dependency - Fix migration 023: use gen_random_uuid() instead of uuid_generate_v4() - Push migrations 023 + 024 to remote Supabase - Regenerate TypeScript types from live DB schema - Remove all (supabase as any) workaround casts across 6 files - Add convenience type aliases to generated types file - Align EventMember/EventRole/EventDepartment interfaces with DB nullability - Add npm scripts: db:push, db:types, db:migrate - svelte-check: 0 errors, vitest: 112/112 passed

This commit is contained in:
AlacrisDevs
2026-02-07 13:05:47 +02:00
parent d304129e5c
commit 202f0fe9a1
10 changed files with 368 additions and 76 deletions

View File

@@ -26,10 +26,10 @@ export interface EventMember {
id: string;
event_id: string;
user_id: string;
role: 'lead' | 'manager' | 'member';
role: string;
role_id: string | null;
notes: string | null;
assigned_at: string;
assigned_at: string | null;
}
export interface EventRole {
@@ -39,7 +39,7 @@ export interface EventRole {
color: string;
sort_order: number;
is_default: boolean;
created_at: string;
created_at: string | null;
}
export interface EventDepartment {
@@ -49,14 +49,14 @@ export interface EventDepartment {
color: string;
description: string | null;
sort_order: number;
created_at: string;
created_at: string | null;
}
export interface EventMemberDepartment {
id: string;
event_member_id: string;
department_id: string;
assigned_at: string;
assigned_at: string | null;
}
export interface EventMemberWithDetails extends EventMember {
@@ -262,41 +262,41 @@ export async function fetchEventMembers(
// Fetch profiles separately (same pattern as org_members)
const userIds = members.map((m: any) => m.user_id);
const { data: profiles } = await (supabase as any)
const { data: profiles } = await supabase
.from('profiles')
.select('id, email, full_name, avatar_url, phone, discord_handle, shirt_size, hoodie_size')
.in('id', userIds);
const profileMap = Object.fromEntries((profiles ?? []).map((p: any) => [p.id, p]));
const profileMap = Object.fromEntries((profiles ?? []).map(p => [p.id, p]));
// Fetch roles for this event
const { data: roles } = await (supabase as any)
const { data: roles } = await supabase
.from('event_roles')
.select('*')
.eq('event_id', eventId);
const roleMap = Object.fromEntries((roles ?? []).map((r: any) => [r.id, r]));
const roleMap = Object.fromEntries((roles ?? []).map(r => [r.id, r]));
// Fetch member-department assignments
const memberIds = members.map((m: any) => m.id);
const { data: memberDepts } = await (supabase as any)
const { data: memberDepts } = await supabase
.from('event_member_departments')
.select('*')
.in('event_member_id', memberIds);
// Fetch departments for this event
const { data: departments } = await (supabase as any)
const { data: departments } = await supabase
.from('event_departments')
.select('*')
.eq('event_id', eventId);
const deptMap = Object.fromEntries((departments ?? []).map((d: any) => [d.id, d]));
const deptMap = Object.fromEntries((departments ?? []).map(d => [d.id, d]));
// Build member-to-departments map
const memberDeptMap: Record<string, EventDepartment[]> = {};
for (const md of (memberDepts ?? [])) {
const dept = deptMap[(md as any).department_id];
const dept = deptMap[md.department_id];
if (dept) {
if (!memberDeptMap[(md as any).event_member_id]) memberDeptMap[(md as any).event_member_id] = [];
memberDeptMap[(md as any).event_member_id].push(dept as unknown as EventDepartment);
if (!memberDeptMap[md.event_member_id]) memberDeptMap[md.event_member_id] = [];
memberDeptMap[md.event_member_id].push(dept as unknown as EventDepartment);
}
}
@@ -314,7 +314,7 @@ export async function addEventMember(
userId: string,
params: { role?: 'lead' | 'manager' | 'member'; role_id?: string; notes?: string } = {}
): Promise<EventMember> {
const { data, error } = await (supabase as any)
const { data, error } = await supabase
.from('event_members')
.upsert({
event_id: eventId,
@@ -358,7 +358,7 @@ export async function fetchEventRoles(
supabase: SupabaseClient<Database>,
eventId: string
): Promise<EventRole[]> {
const { data, error } = await (supabase as any)
const { data, error } = await supabase
.from('event_roles')
.select('*')
.eq('event_id', eventId)
@@ -376,7 +376,7 @@ export async function createEventRole(
eventId: string,
params: { name: string; color?: string; sort_order?: number }
): Promise<EventRole> {
const { data, error } = await (supabase as any)
const { data, error } = await supabase
.from('event_roles')
.insert({
event_id: eventId,
@@ -399,7 +399,7 @@ export async function updateEventRole(
roleId: string,
params: Partial<Pick<EventRole, 'name' | 'color' | 'sort_order' | 'is_default'>>
): Promise<EventRole> {
const { data, error } = await (supabase as any)
const { data, error } = await supabase
.from('event_roles')
.update(params)
.eq('id', roleId)
@@ -417,7 +417,7 @@ export async function deleteEventRole(
supabase: SupabaseClient<Database>,
roleId: string
): Promise<void> {
const { error } = await (supabase as any)
const { error } = await supabase
.from('event_roles')
.delete()
.eq('id', roleId);
@@ -436,7 +436,7 @@ export async function fetchEventDepartments(
supabase: SupabaseClient<Database>,
eventId: string
): Promise<EventDepartment[]> {
const { data, error } = await (supabase as any)
const { data, error } = await supabase
.from('event_departments')
.select('*')
.eq('event_id', eventId)
@@ -454,7 +454,7 @@ export async function createEventDepartment(
eventId: string,
params: { name: string; color?: string; description?: string; sort_order?: number }
): Promise<EventDepartment> {
const { data, error } = await (supabase as any)
const { data, error } = await supabase
.from('event_departments')
.insert({
event_id: eventId,
@@ -478,7 +478,7 @@ export async function updateEventDepartment(
deptId: string,
params: Partial<Pick<EventDepartment, 'name' | 'color' | 'description' | 'sort_order'>>
): Promise<EventDepartment> {
const { data, error } = await (supabase as any)
const { data, error } = await supabase
.from('event_departments')
.update(params)
.eq('id', deptId)
@@ -496,7 +496,7 @@ export async function deleteEventDepartment(
supabase: SupabaseClient<Database>,
deptId: string
): Promise<void> {
const { error } = await (supabase as any)
const { error } = await supabase
.from('event_departments')
.delete()
.eq('id', deptId);
@@ -516,7 +516,7 @@ export async function assignMemberDepartment(
eventMemberId: string,
departmentId: string
): Promise<EventMemberDepartment> {
const { data, error } = await (supabase as any)
const { data, error } = await supabase
.from('event_member_departments')
.upsert(
{ event_member_id: eventMemberId, department_id: departmentId },
@@ -537,7 +537,7 @@ export async function unassignMemberDepartment(
eventMemberId: string,
departmentId: string
): Promise<void> {
const { error } = await (supabase as any)
const { error } = await supabase
.from('event_member_departments')
.delete()
.eq('event_member_id', eventMemberId)

View File

@@ -360,26 +360,106 @@ export type Database = {
},
]
}
event_departments: {
Row: {
color: string
created_at: string | null
description: string | null
event_id: string
id: string
name: string
sort_order: number
}
Insert: {
color?: string
created_at?: string | null
description?: string | null
event_id: string
id?: string
name: string
sort_order?: number
}
Update: {
color?: string
created_at?: string | null
description?: string | null
event_id?: string
id?: string
name?: string
sort_order?: number
}
Relationships: [
{
foreignKeyName: "event_departments_event_id_fkey"
columns: ["event_id"]
isOneToOne: false
referencedRelation: "events"
referencedColumns: ["id"]
},
]
}
event_member_departments: {
Row: {
assigned_at: string | null
department_id: string
event_member_id: string
id: string
}
Insert: {
assigned_at?: string | null
department_id: string
event_member_id: string
id?: string
}
Update: {
assigned_at?: string | null
department_id?: string
event_member_id?: string
id?: string
}
Relationships: [
{
foreignKeyName: "event_member_departments_department_id_fkey"
columns: ["department_id"]
isOneToOne: false
referencedRelation: "event_departments"
referencedColumns: ["id"]
},
{
foreignKeyName: "event_member_departments_event_member_id_fkey"
columns: ["event_member_id"]
isOneToOne: false
referencedRelation: "event_members"
referencedColumns: ["id"]
},
]
}
event_members: {
Row: {
assigned_at: string | null
event_id: string
id: string
notes: string | null
role: string
role_id: string | null
user_id: string
}
Insert: {
assigned_at?: string | null
event_id: string
id?: string
notes?: string | null
role?: string
role_id?: string | null
user_id: string
}
Update: {
assigned_at?: string | null
event_id?: string
id?: string
notes?: string | null
role?: string
role_id?: string | null
user_id?: string
}
Relationships: [
@@ -390,6 +470,51 @@ export type Database = {
referencedRelation: "events"
referencedColumns: ["id"]
},
{
foreignKeyName: "event_members_role_id_fkey"
columns: ["role_id"]
isOneToOne: false
referencedRelation: "event_roles"
referencedColumns: ["id"]
},
]
}
event_roles: {
Row: {
color: string
created_at: string | null
event_id: string
id: string
is_default: boolean
name: string
sort_order: number
}
Insert: {
color?: string
created_at?: string | null
event_id: string
id?: string
is_default?: boolean
name: string
sort_order?: number
}
Update: {
color?: string
created_at?: string | null
event_id?: string
id?: string
is_default?: boolean
name?: string
sort_order?: number
}
Relationships: [
{
foreignKeyName: "event_roles_event_id_fkey"
columns: ["event_id"]
isOneToOne: false
referencedRelation: "events"
referencedColumns: ["id"]
},
]
}
events: {
@@ -982,25 +1107,37 @@ export type Database = {
Row: {
avatar_url: string | null
created_at: string | null
discord_handle: string | null
email: string
full_name: string | null
hoodie_size: string | null
id: string
phone: string | null
shirt_size: string | null
updated_at: string | null
}
Insert: {
avatar_url?: string | null
created_at?: string | null
discord_handle?: string | null
email: string
full_name?: string | null
hoodie_size?: string | null
id: string
phone?: string | null
shirt_size?: string | null
updated_at?: string | null
}
Update: {
avatar_url?: string | null
created_at?: string | null
discord_handle?: string | null
email?: string
full_name?: string | null
hoodie_size?: string | null
id?: string
phone?: string | null
shirt_size?: string | null
updated_at?: string | null
}
Relationships: []
@@ -1296,29 +1433,15 @@ export const Constants = {
},
} as const
// ── Convenience type aliases ─────────────────────────────────────────
export type MemberRole = 'owner' | 'admin' | 'editor' | 'viewer';
type PublicTables = Database['public']['Tables']
export type Organization = PublicTables['organizations']['Row']
export type OrgMember = PublicTables['org_members']['Row']
export type OrgRole = PublicTables['org_roles']['Row']
export type OrgInvite = PublicTables['org_invites']['Row']
export type Profile = PublicTables['profiles']['Row']
export type Document = PublicTables['documents']['Row']
export type DocumentLock = PublicTables['document_locks']['Row']
export type CalendarEvent = PublicTables['calendar_events']['Row']
export type KanbanBoard = PublicTables['kanban_boards']['Row']
export type KanbanColumn = PublicTables['kanban_columns']['Row']
export type KanbanCard = PublicTables['kanban_cards']['Row']
export type KanbanComment = PublicTables['kanban_comments']['Row']
export type KanbanLabel = PublicTables['kanban_labels']['Row']
export type KanbanChecklistItem = PublicTables['kanban_checklist_items']['Row']
export type Tag = PublicTables['tags']['Row']
export type Team = PublicTables['teams']['Row']
export type OrgGoogleCalendar = PublicTables['org_google_calendars']['Row']
export type ActivityLog = PublicTables['activity_log']['Row']
export type UserPreferences = PublicTables['user_preferences']['Row']
export type MatrixCredentials = PublicTables['matrix_credentials']['Row']
export type EventRow = PublicTables['events']['Row']
export type EventMemberRow = PublicTables['event_members']['Row']
// ============================================================
// Convenience type aliases (used across the codebase)
// ============================================================
export type Profile = Tables<'profiles'>
export type Organization = Tables<'organizations'>
export type Document = Tables<'documents'>
export type KanbanBoard = Tables<'kanban_boards'>
export type KanbanColumn = Tables<'kanban_columns'>
export type KanbanCard = Tables<'kanban_cards'>
export type CalendarEvent = Tables<'calendar_events'>
export type OrgRole = Tables<'org_roles'>
export type MemberRole = string

View File

@@ -49,7 +49,7 @@ export const load: LayoutServerLoad = async ({ params, locals }) => {
.eq('org_id', org.id)
.order('created_at', { ascending: false })
.limit(10),
(locals.supabase as any)
locals.supabase
.from('profiles')
.select('id, email, full_name, avatar_url, phone, discord_handle, shirt_size, hoodie_size')
.eq('id', user.id)
@@ -111,13 +111,13 @@ export const load: LayoutServerLoad = async ({ params, locals }) => {
let memberProfilesMap: Record<string, { id: string; email: string; full_name: string | null; avatar_url: string | null; phone: string | null; discord_handle: string | null; shirt_size: string | null; hoodie_size: string | null }> = {};
if (memberUserIds.length > 0) {
const { data: memberProfiles } = await (locals.supabase as any)
const { data: memberProfiles } = await locals.supabase
.from('profiles')
.select('id, email, full_name, avatar_url, phone, discord_handle, shirt_size, hoodie_size')
.in('id', memberUserIds);
if (memberProfiles) {
memberProfilesMap = Object.fromEntries(memberProfiles.map((p: any) => [p.id, p]));
memberProfilesMap = Object.fromEntries(memberProfiles.map(p => [p.id, p]));
}
}

View File

@@ -175,7 +175,7 @@
async function saveProfile() {
isSaving = true;
const { error } = await (supabase as any)
const { error } = await supabase
.from("profiles")
.update({
full_name: fullName || null,

View File

@@ -72,7 +72,7 @@
if (!newEventName.trim()) return;
creating = true;
try {
const { data: created, error } = await (supabase as any)
const { data: created, error } = await supabase
.from("events")
.insert({
org_id: data.org.id,

View File

@@ -151,7 +151,7 @@
async function handleSave() {
saving = true;
try {
const { error } = await (supabase as any)
const { error } = await supabase
.from("events")
.update({
name: editName.trim(),
@@ -183,7 +183,7 @@
async function handleDelete() {
deleting = true;
try {
const { error } = await (supabase as any)
const { error } = await supabase
.from("events")
.delete()
.eq("id", data.event.id);

View File

@@ -141,7 +141,7 @@
if (!selectedUserId) return;
adding = true;
try {
const { data: inserted, error } = await (supabase as any)
const { data: inserted, error } = await supabase
.from("event_members")
.upsert(
{
@@ -159,7 +159,7 @@
// Assign departments
for (const deptId of selectedDeptIds) {
await (supabase as any)
await supabase
.from("event_member_departments")
.upsert(
{ event_member_id: inserted.id, department_id: deptId },
@@ -209,7 +209,7 @@
updatingMember = true;
try {
// Update member record
const { error } = await (supabase as any)
const { error } = await supabase
.from("event_members")
.update({
role_id: editRoleId || null,
@@ -224,14 +224,14 @@
const toAdd = editDeptIds.filter((id) => !oldDeptIds.includes(id));
for (const deptId of toRemove) {
await (supabase as any)
await supabase
.from("event_member_departments")
.delete()
.eq("event_member_id", editingMember.id)
.eq("department_id", deptId);
}
for (const deptId of toAdd) {
await (supabase as any)
await supabase
.from("event_member_departments")
.upsert(
{ event_member_id: editingMember.id, department_id: deptId },
@@ -268,7 +268,7 @@
if (!memberToRemove) return;
removing = true;
try {
const { error } = await (supabase as any)
const { error } = await supabase
.from("event_members")
.delete()
.eq("id", memberToRemove.id);
@@ -298,7 +298,7 @@
savingDept = true;
try {
if (editingDept) {
const { data: updated, error } = await (supabase as any)
const { data: updated, error } = await supabase
.from("event_departments")
.update({ name: deptName.trim(), color: deptColor })
.eq("id", editingDept.id)
@@ -310,7 +310,7 @@
);
toasts.success(m.team_dept_updated());
} else {
const { data: created, error } = await (supabase as any)
const { data: created, error } = await supabase
.from("event_departments")
.insert({
event_id: data.event.id,
@@ -334,7 +334,7 @@
async function handleDeleteDept(dept: EventDepartment) {
try {
const { error } = await (supabase as any)
const { error } = await supabase
.from("event_departments")
.delete()
.eq("id", dept.id);
@@ -364,7 +364,7 @@
savingRole = true;
try {
if (editingRole) {
const { data: updated, error } = await (supabase as any)
const { data: updated, error } = await supabase
.from("event_roles")
.update({ name: roleName.trim(), color: roleColor })
.eq("id", editingRole.id)
@@ -376,7 +376,7 @@
);
toasts.success(m.team_role_updated());
} else {
const { data: created, error } = await (supabase as any)
const { data: created, error } = await supabase
.from("event_roles")
.insert({
event_id: data.event.id,
@@ -400,7 +400,7 @@
async function handleDeleteRole(role: EventRole) {
try {
const { error } = await (supabase as any)
const { error } = await supabase
.from("event_roles")
.delete()
.eq("id", role.id);