diff --git a/messages/en.json b/messages/en.json index 2a07f22..101b623 100644 --- a/messages/en.json +++ b/messages/en.json @@ -175,6 +175,14 @@ "account_display_name": "Display Name", "account_display_name_placeholder": "Your name", "account_email": "Email", + "account_phone": "Phone", + "account_phone_placeholder": "+372 ...", + "account_discord": "Discord", + "account_discord_placeholder": "username", + "account_contact_info": "Contact & Sizing", + "account_shirt_size": "Shirt Size", + "account_hoodie_size": "Hoodie Size", + "account_size_placeholder": "Select size", "account_save_profile": "Save Profile", "account_appearance": "Appearance", "account_theme": "Theme", diff --git a/messages/et.json b/messages/et.json index 70e5ec9..ec3232a 100644 --- a/messages/et.json +++ b/messages/et.json @@ -175,6 +175,14 @@ "account_display_name": "Kuvatav nimi", "account_display_name_placeholder": "Sinu nimi", "account_email": "E-post", + "account_phone": "Telefon", + "account_phone_placeholder": "+372 ...", + "account_discord": "Discord", + "account_discord_placeholder": "kasutajanimi", + "account_contact_info": "Kontakt ja suurused", + "account_shirt_size": "Särgi suurus", + "account_hoodie_size": "Pusa suurus", + "account_size_placeholder": "Vali suurus", "account_save_profile": "Salvesta profiil", "account_appearance": "Välimus", "account_theme": "Teema", diff --git a/src/lib/api/events.ts b/src/lib/api/events.ts index a3fc8c5..8fef5d9 100644 --- a/src/lib/api/events.ts +++ b/src/lib/api/events.ts @@ -60,7 +60,7 @@ export interface EventMemberDepartment { } export interface EventMemberWithDetails extends EventMember { - profile?: { id: string; email: string; full_name: string | null; avatar_url: string | null }; + profile?: { 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 }; event_role?: EventRole; departments: EventDepartment[]; } @@ -262,12 +262,12 @@ 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 + const { data: profiles } = await (supabase as any) .from('profiles') - .select('id, email, full_name, avatar_url') + .select('id, email, full_name, avatar_url, phone, discord_handle, shirt_size, hoodie_size') .in('id', userIds); - const profileMap = Object.fromEntries((profiles ?? []).map(p => [p.id, p])); + const profileMap = Object.fromEntries((profiles ?? []).map((p: any) => [p.id, p])); // Fetch roles for this event const { data: roles } = await (supabase as any) diff --git a/src/routes/[orgSlug]/+layout.server.ts b/src/routes/[orgSlug]/+layout.server.ts index 153acd2..229d6d5 100644 --- a/src/routes/[orgSlug]/+layout.server.ts +++ b/src/routes/[orgSlug]/+layout.server.ts @@ -49,9 +49,9 @@ export const load: LayoutServerLoad = async ({ params, locals }) => { .eq('org_id', org.id) .order('created_at', { ascending: false }) .limit(10), - locals.supabase + (locals.supabase as any) .from('profiles') - .select('id, email, full_name, avatar_url') + .select('id, email, full_name, avatar_url, phone, discord_handle, shirt_size, hoodie_size') .eq('id', user.id) .single(), locals.supabase @@ -108,16 +108,16 @@ export const load: LayoutServerLoad = async ({ params, locals }) => { // Fetch profiles separately since org_members.user_id FK points to auth.users, not profiles const memberUserIds = (rawMembers ?? []).map(m => m.user_id).filter((id): id is string => id !== null); - let memberProfilesMap: Record = {}; + let memberProfilesMap: Record = {}; if (memberUserIds.length > 0) { - const { data: memberProfiles } = await locals.supabase + const { data: memberProfiles } = await (locals.supabase as any) .from('profiles') - .select('id, email, full_name, avatar_url') + .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 => [p.id, p])); + memberProfilesMap = Object.fromEntries(memberProfiles.map((p: any) => [p.id, p])); } } diff --git a/src/routes/[orgSlug]/account/+page.svelte b/src/routes/[orgSlug]/account/+page.svelte index e73afaf..e3df42b 100644 --- a/src/routes/[orgSlug]/account/+page.svelte +++ b/src/routes/[orgSlug]/account/+page.svelte @@ -16,6 +16,10 @@ 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; }; preferences: { id: string; @@ -34,10 +38,16 @@ // Profile state let fullName = $state(data.profile.full_name ?? ""); let avatarUrl = $state(data.profile.avatar_url ?? null); + let phone = $state(data.profile.phone ?? ""); + let discordHandle = $state(data.profile.discord_handle ?? ""); + let shirtSize = $state(data.profile.shirt_size ?? ""); + let hoodieSize = $state(data.profile.hoodie_size ?? ""); let isSaving = $state(false); let isUploading = $state(false); let avatarInput = $state(null); + const clothingSizes = ["XS", "S", "M", "L", "XL", "XXL", "3XL"]; + // Preferences state let theme = $state(data.preferences?.theme ?? "dark"); let accentColor = $state(data.preferences?.accent_color ?? "#00A3E0"); @@ -57,6 +67,10 @@ $effect(() => { fullName = data.profile.full_name ?? ""; avatarUrl = data.profile.avatar_url ?? null; + phone = data.profile.phone ?? ""; + discordHandle = data.profile.discord_handle ?? ""; + shirtSize = data.profile.shirt_size ?? ""; + hoodieSize = data.profile.hoodie_size ?? ""; theme = data.preferences?.theme ?? "dark"; accentColor = data.preferences?.accent_color ?? "#00A3E0"; useOrgTheme = data.preferences?.use_org_theme ?? true; @@ -161,9 +175,15 @@ async function saveProfile() { isSaving = true; - const { error } = await supabase + const { error } = await (supabase as any) .from("profiles") - .update({ full_name: fullName || null }) + .update({ + full_name: fullName || null, + phone: phone || null, + discord_handle: discordHandle || null, + shirt_size: shirtSize || null, + hoodie_size: hoodieSize || null, + }) .eq("id", data.profile.id); if (error) { @@ -305,6 +325,58 @@ + +
+

+ {m.account_contact_info()} +

+ + + + + +
+
+ {m.account_shirt_size()} + +
+
+ {m.account_hoodie_size()} + +
+
+ +
+ +
+
+

diff --git a/src/routes/[orgSlug]/events/[eventSlug]/team/+page.svelte b/src/routes/[orgSlug]/events/[eventSlug]/team/+page.svelte index 175a09b..f1a3be8 100644 --- a/src/routes/[orgSlug]/events/[eventSlug]/team/+page.svelte +++ b/src/routes/[orgSlug]/events/[eventSlug]/team/+page.svelte @@ -21,6 +21,10 @@ 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; } | null; } @@ -626,6 +630,20 @@ {/each}

+
+ {#if member.profile?.phone} + phone{member.profile.phone} + {/if} + {#if member.profile?.discord_handle} + chat{member.profile.discord_handle} + {/if} + {#if member.profile?.shirt_size} + T: {member.profile.shirt_size} + {/if} + {#if member.profile?.hoodie_size} + H: {member.profile.hoodie_size} + {/if} +
{#if isEditor} diff --git a/supabase/migrations/024_profile_extended_fields.sql b/supabase/migrations/024_profile_extended_fields.sql new file mode 100644 index 0000000..b1d4ab2 --- /dev/null +++ b/supabase/migrations/024_profile_extended_fields.sql @@ -0,0 +1,8 @@ +-- Extended profile fields: contact info and clothing sizes +-- These are collected during onboarding and visible to event team managers + +ALTER TABLE profiles + ADD COLUMN phone TEXT, + ADD COLUMN discord_handle TEXT, + ADD COLUMN shirt_size TEXT, + ADD COLUMN hoodie_size TEXT;