From 202f0fe9a139b3f705c831cacc4b9dff9c93231d Mon Sep 17 00:00:00 2001 From: AlacrisDevs Date: Sat, 7 Feb 2026 13:05:47 +0200 Subject: [PATCH] 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 --- package-lock.json | 165 +++++++++++++++++ package.json | 8 +- src/lib/api/events.ts | 52 +++--- src/lib/supabase/types.ts | 175 +++++++++++++++--- src/routes/[orgSlug]/+layout.server.ts | 6 +- src/routes/[orgSlug]/account/+page.svelte | 2 +- src/routes/[orgSlug]/events/+page.svelte | 2 +- .../[orgSlug]/events/[eventSlug]/+page.svelte | 4 +- .../events/[eventSlug]/team/+page.svelte | 24 +-- .../migrations/023_event_team_management.sql | 6 +- 10 files changed, 368 insertions(+), 76 deletions(-) diff --git a/package-lock.json b/package-lock.json index c9700c3..36a5ad8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,6 +35,7 @@ "@types/twemoji": "^13.1.1", "@vitest/browser-playwright": "^4.0.18", "playwright": "^1.58.0", + "supabase": "^2.76.1", "svelte": "^5.48.2", "svelte-check": "^4.3.5", "tailwindcss": "^4.1.18", @@ -558,6 +559,19 @@ "node": ">=12" } }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -2513,6 +2527,23 @@ "node": "*" } }, + "node_modules/bin-links": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-6.0.0.tgz", + "integrity": "sha512-X4CiKlcV2GjnCMwnKAfbVWpHa++65th9TuzAEYtZoATiOE2DQKhSp4CJlyLoTqdhBKlXjpXjCTYPNNFS33Fi6w==", + "dev": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^8.0.0", + "npm-normalize-package-bin": "^5.0.0", + "proc-log": "^6.0.0", + "read-cmd-shim": "^6.0.0", + "write-file-atomic": "^7.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -2563,6 +2594,16 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -2572,6 +2613,16 @@ "node": ">=6" } }, + "node_modules/cmd-shim": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-8.0.0.tgz", + "integrity": "sha512-Jk/BK6NCapZ58BKUxlSI+ouKRbjH1NLZCgJkYoab+vEHUY3f6OzpNBN9u7HFSv9J6TRDGs4PLOHezoKGaFRSCA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3194,6 +3245,16 @@ "node": ">=20.0.0" } }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", @@ -3791,6 +3852,19 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", @@ -3874,6 +3948,16 @@ "url": "https://opencollective.com/node-fetch" } }, + "node_modules/npm-normalize-package-bin": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-5.0.0.tgz", + "integrity": "sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/obug": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", @@ -4082,6 +4166,16 @@ "node": ">=4" } }, + "node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/prosemirror-changeset": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.3.1.tgz", @@ -4289,6 +4383,16 @@ "node": ">=6" } }, + "node_modules/read-cmd-shim": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-6.0.0.tgz", + "integrity": "sha512-1zM5HuOfagXCBWMN83fuFI/x+T/UhZ7k+KIzhrHXcQoeX5+7gmaDYjELQHmmzIodumBHeByBJT4QYS7ufAgs7A==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -4627,6 +4731,26 @@ "node": ">=8" } }, + "node_modules/supabase": { + "version": "2.76.1", + "resolved": "https://registry.npmjs.org/supabase/-/supabase-2.76.1.tgz", + "integrity": "sha512-wWN7trvmcFfI/T4Jr1t4eKSD3JUCMsisssDAoywwMP7HlF4lVrAxQyROah3uBRV05RuEFhO74mFlYr4+koGb0Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bin-links": "^6.0.0", + "https-proxy-agent": "^7.0.2", + "node-fetch": "^3.3.2", + "tar": "7.5.7" + }, + "bin": { + "supabase": "bin/supabase" + }, + "engines": { + "npm": ">=8" + } + }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -4722,6 +4846,23 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/tar": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", + "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -5237,6 +5378,20 @@ "node": ">=8" } }, + "node_modules/write-file-atomic": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-7.0.0.tgz", + "integrity": "sha512-YnlPC6JqnZl6aO4uRc+dx5PHguiR9S6WeoLtpxNT9wIG+BDya7ZNE1q7KOjVgaA73hKhKLpVPgJ5QA9THQ5BRg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/ws": { "version": "8.19.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", @@ -5258,6 +5413,16 @@ } } }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/zimmerframe": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", diff --git a/package.json b/package.json index 34328aa..d66fd41 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,10 @@ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "test:unit": "vitest", - "test": "npm run test:unit -- --run" + "test": "npm run test:unit -- --run", + "db:push": "npx supabase db push", + "db:types": "npx supabase gen types --lang=typescript --project-id zlworzrghsrokdkuckez --schema public > src/lib/supabase/types.ts", + "db:migrate": "npm run db:push && npm run db:types" }, "devDependencies": { "@inlang/paraglide-js": "^2.10.0", @@ -26,6 +29,7 @@ "@types/twemoji": "^13.1.1", "@vitest/browser-playwright": "^4.0.18", "playwright": "^1.58.0", + "supabase": "^2.76.1", "svelte": "^5.48.2", "svelte-check": "^4.3.5", "tailwindcss": "^4.1.18", @@ -49,4 +53,4 @@ "matrix-js-sdk": "^40.2.0-rc.0", "twemoji": "^14.0.2" } -} +} \ No newline at end of file diff --git a/src/lib/api/events.ts b/src/lib/api/events.ts index 8fef5d9..11f903b 100644 --- a/src/lib/api/events.ts +++ b/src/lib/api/events.ts @@ -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 = {}; 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 { - 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, eventId: string ): Promise { - 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 { - 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> ): Promise { - 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, roleId: string ): Promise { - 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, eventId: string ): Promise { - 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 { - 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> ): Promise { - 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, deptId: string ): Promise { - 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 { - 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 { - const { error } = await (supabase as any) + const { error } = await supabase .from('event_member_departments') .delete() .eq('event_member_id', eventMemberId) diff --git a/src/lib/supabase/types.ts b/src/lib/supabase/types.ts index b7df722..2ba25a0 100644 --- a/src/lib/supabase/types.ts +++ b/src/lib/supabase/types.ts @@ -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 diff --git a/src/routes/[orgSlug]/+layout.server.ts b/src/routes/[orgSlug]/+layout.server.ts index 229d6d5..276441e 100644 --- a/src/routes/[orgSlug]/+layout.server.ts +++ b/src/routes/[orgSlug]/+layout.server.ts @@ -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 = {}; 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])); } } diff --git a/src/routes/[orgSlug]/account/+page.svelte b/src/routes/[orgSlug]/account/+page.svelte index e3df42b..42003a7 100644 --- a/src/routes/[orgSlug]/account/+page.svelte +++ b/src/routes/[orgSlug]/account/+page.svelte @@ -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, diff --git a/src/routes/[orgSlug]/events/+page.svelte b/src/routes/[orgSlug]/events/+page.svelte index 3d60936..290b705 100644 --- a/src/routes/[orgSlug]/events/+page.svelte +++ b/src/routes/[orgSlug]/events/+page.svelte @@ -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, diff --git a/src/routes/[orgSlug]/events/[eventSlug]/+page.svelte b/src/routes/[orgSlug]/events/[eventSlug]/+page.svelte index bb94357..4de5e1e 100644 --- a/src/routes/[orgSlug]/events/[eventSlug]/+page.svelte +++ b/src/routes/[orgSlug]/events/[eventSlug]/+page.svelte @@ -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); diff --git a/src/routes/[orgSlug]/events/[eventSlug]/team/+page.svelte b/src/routes/[orgSlug]/events/[eventSlug]/team/+page.svelte index f1a3be8..21780f8 100644 --- a/src/routes/[orgSlug]/events/[eventSlug]/team/+page.svelte +++ b/src/routes/[orgSlug]/events/[eventSlug]/team/+page.svelte @@ -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); diff --git a/supabase/migrations/023_event_team_management.sql b/supabase/migrations/023_event_team_management.sql index 2b7afbe..d6c8902 100644 --- a/supabase/migrations/023_event_team_management.sql +++ b/supabase/migrations/023_event_team_management.sql @@ -5,7 +5,7 @@ -- 1. Event Roles: customizable per-event position types -- ============================================================ CREATE TABLE event_roles ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), event_id UUID NOT NULL REFERENCES events(id) ON DELETE CASCADE, name TEXT NOT NULL, color TEXT NOT NULL DEFAULT '#6366f1', @@ -21,7 +21,7 @@ CREATE INDEX idx_event_roles_event ON event_roles(event_id); -- 2. Event Departments: teams/areas within an event -- ============================================================ CREATE TABLE event_departments ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), event_id UUID NOT NULL REFERENCES events(id) ON DELETE CASCADE, name TEXT NOT NULL, color TEXT NOT NULL DEFAULT '#00A3E0', @@ -44,7 +44,7 @@ ALTER TABLE event_members -- 4. Member-Department assignments (many-to-many) -- ============================================================ CREATE TABLE event_member_departments ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), event_member_id UUID NOT NULL REFERENCES event_members(id) ON DELETE CASCADE, department_id UUID NOT NULL REFERENCES event_departments(id) ON DELETE CASCADE, assigned_at TIMESTAMPTZ DEFAULT now(),