i18n: add Paraglide messages for all events pages (EN + ET)

This commit is contained in:
AlacrisDevs
2026-02-07 10:16:13 +02:00
parent 36496e8cdb
commit fe6ec6e0af
6 changed files with 232 additions and 94 deletions

View File

@@ -251,5 +251,74 @@
"entity_kanban_column": "column", "entity_kanban_column": "column",
"entity_member": "member", "entity_member": "member",
"entity_role": "role", "entity_role": "role",
"entity_invite": "invite" "entity_invite": "invite",
"entity_event": "event",
"nav_events": "Events",
"events_title": "Events",
"events_subtitle": "Organize and manage your events",
"events_new": "New Event",
"events_create": "Create Event",
"events_empty_title": "No events yet",
"events_empty_desc": "Create your first event to get started",
"events_no_dates": "No dates set",
"events_tab_all": "All Events",
"events_tab_planning": "Planning",
"events_tab_active": "Active",
"events_tab_completed": "Completed",
"events_tab_archived": "Archived",
"events_status_planning": "Planning",
"events_status_active": "Active",
"events_status_completed": "Completed",
"events_status_archived": "Archived",
"events_form_name": "Event Name",
"events_form_name_placeholder": "e.g., Summer Conference 2026",
"events_form_description": "Description",
"events_form_description_placeholder": "Brief description of the event...",
"events_form_start_date": "Start Date",
"events_form_end_date": "End Date",
"events_form_venue": "Venue",
"events_form_venue_placeholder": "e.g., Convention Center",
"events_form_venue_address_placeholder": "Venue address",
"events_form_color": "Color",
"events_form_select_color": "Select color {color}",
"events_creating": "Creating...",
"events_saving": "Saving...",
"events_deleting": "Deleting...",
"events_updated": "Event updated",
"events_created": "Event \"{name}\" created",
"events_deleted": "Event deleted",
"events_delete_title": "Delete Event?",
"events_delete_desc": "This will permanently delete {name} and all its data. This action cannot be undone.",
"events_delete_confirm": "Delete Event",
"events_days_ago": "{count} days ago",
"events_today": "Today!",
"events_tomorrow": "Tomorrow",
"events_in_days": "In {count} days",
"events_overview": "Overview",
"events_modules": "Modules",
"events_details": "Event Details",
"events_start_date": "Start Date",
"events_end_date": "End Date",
"events_venue": "Venue",
"events_not_set": "Not set",
"events_all_events": "All Events",
"events_team": "Team",
"events_team_count": "Team ({count})",
"events_team_manage": "Manage",
"events_team_empty": "No team members assigned yet",
"events_more_members": "+{count} more",
"events_mod_tasks": "Tasks",
"events_mod_tasks_desc": "Manage tasks, milestones, and progress",
"events_mod_files": "Files",
"events_mod_files_desc": "Documents, contracts, and media",
"events_mod_schedule": "Schedule",
"events_mod_schedule_desc": "Event timeline and program",
"events_mod_budget": "Budget",
"events_mod_budget_desc": "Income, expenses, and tracking",
"events_mod_guests": "Guests",
"events_mod_guests_desc": "Guest list and registration",
"events_mod_team": "Team",
"events_mod_team_desc": "Team members and shift scheduling",
"events_mod_sponsors": "Sponsors",
"events_mod_sponsors_desc": "Sponsors, partners, and deliverables"
} }

View File

@@ -251,5 +251,74 @@
"entity_kanban_column": "veeru", "entity_kanban_column": "veeru",
"entity_member": "liikme", "entity_member": "liikme",
"entity_role": "rolli", "entity_role": "rolli",
"entity_invite": "kutse" "entity_invite": "kutse",
"entity_event": "ürituse",
"nav_events": "Üritused",
"events_title": "Üritused",
"events_subtitle": "Korralda ja halda oma üritusi",
"events_new": "Uus üritus",
"events_create": "Loo üritus",
"events_empty_title": "Üritusi pole veel",
"events_empty_desc": "Loo oma esimene üritus alustamiseks",
"events_no_dates": "Kuupäevad määramata",
"events_tab_all": "Kõik üritused",
"events_tab_planning": "Planeerimisel",
"events_tab_active": "Aktiivne",
"events_tab_completed": "Lõpetatud",
"events_tab_archived": "Arhiveeritud",
"events_status_planning": "Planeerimisel",
"events_status_active": "Aktiivne",
"events_status_completed": "Lõpetatud",
"events_status_archived": "Arhiveeritud",
"events_form_name": "Ürituse nimi",
"events_form_name_placeholder": "nt Suvekonverents 2026",
"events_form_description": "Kirjeldus",
"events_form_description_placeholder": "Ürituse lühikirjeldus...",
"events_form_start_date": "Alguskuupäev",
"events_form_end_date": "Lõppkuupäev",
"events_form_venue": "Toimumiskoht",
"events_form_venue_placeholder": "nt Konverentsikeskus",
"events_form_venue_address_placeholder": "Toimumiskoha aadress",
"events_form_color": "Värv",
"events_form_select_color": "Vali värv {color}",
"events_creating": "Loomine...",
"events_saving": "Salvestamine...",
"events_deleting": "Kustutamine...",
"events_updated": "Üritus uuendatud",
"events_created": "Üritus \"{name}\" loodud",
"events_deleted": "Üritus kustutatud",
"events_delete_title": "Kustuta üritus?",
"events_delete_desc": "See kustutab jäädavalt ürituse {name} ja kõik selle andmed. Seda toimingut ei saa tagasi võtta.",
"events_delete_confirm": "Kustuta üritus",
"events_days_ago": "{count} päeva tagasi",
"events_today": "Täna!",
"events_tomorrow": "Homme",
"events_in_days": "{count} päeva pärast",
"events_overview": "Ülevaade",
"events_modules": "Moodulid",
"events_details": "Ürituse andmed",
"events_start_date": "Alguskuupäev",
"events_end_date": "Lõppkuupäev",
"events_venue": "Toimumiskoht",
"events_not_set": "Määramata",
"events_all_events": "Kõik üritused",
"events_team": "Meeskond",
"events_team_count": "Meeskond ({count})",
"events_team_manage": "Halda",
"events_team_empty": "Meeskonnaliikmeid pole veel määratud",
"events_more_members": "+{count} veel",
"events_mod_tasks": "Ülesanded",
"events_mod_tasks_desc": "Halda ülesandeid, verstaposte ja edenemist",
"events_mod_files": "Failid",
"events_mod_files_desc": "Dokumendid, lepingud ja meedia",
"events_mod_schedule": "Ajakava",
"events_mod_schedule_desc": "Ürituse ajakava ja programm",
"events_mod_budget": "Eelarve",
"events_mod_budget_desc": "Tulud, kulud ja jälgimine",
"events_mod_guests": "Külalised",
"events_mod_guests_desc": "Külaliste nimekiri ja registreerimine",
"events_mod_team": "Meeskond",
"events_mod_team_desc": "Meeskonnaliikmed ja vahetuste planeerimine",
"events_mod_sponsors": "Sponsorid",
"events_mod_sponsors_desc": "Sponsorid, partnerid ja kohustused"
} }

View File

@@ -125,7 +125,7 @@
: []), : []),
{ {
href: `/${data.org.slug}/events`, href: `/${data.org.slug}/events`,
label: "Events", label: m.nav_events(),
icon: "celebration", icon: "celebration",
}, },
{ {

View File

@@ -6,6 +6,7 @@
import type { SupabaseClient } from "@supabase/supabase-js"; import type { SupabaseClient } from "@supabase/supabase-js";
import type { Database } from "$lib/supabase/types"; import type { Database } from "$lib/supabase/types";
import { toasts } from "$lib/stores/ui"; import { toasts } from "$lib/stores/ui";
import * as m from "$lib/paraglide/messages";
interface EventItem { interface EventItem {
id: string; id: string;
@@ -48,13 +49,13 @@
let newEventColor = $state("#00A3E0"); let newEventColor = $state("#00A3E0");
let creating = $state(false); let creating = $state(false);
const statusTabs = [ const statusTabs = $derived([
{ value: "all", label: "All Events", icon: "apps" }, { value: "all", label: m.events_tab_all(), icon: "apps" },
{ value: "planning", label: "Planning", icon: "edit_note" }, { value: "planning", label: m.events_tab_planning(), icon: "edit_note" },
{ value: "active", label: "Active", icon: "play_circle" }, { value: "active", label: m.events_tab_active(), icon: "play_circle" },
{ value: "completed", label: "Completed", icon: "check_circle" }, { value: "completed", label: m.events_tab_completed(), icon: "check_circle" },
{ value: "archived", label: "Archived", icon: "archive" }, { value: "archived", label: m.events_tab_archived(), icon: "archive" },
]; ]);
const presetColors = [ const presetColors = [
"#00A3E0", "#00A3E0",
@@ -100,7 +101,7 @@
start: string | null, start: string | null,
end: string | null, end: string | null,
): string { ): string {
if (!start && !end) return "No dates set"; if (!start && !end) return m.events_no_dates();
if (start && !end) return formatDate(start); if (start && !end) return formatDate(start);
if (!start && end) return `Until ${formatDate(end)}`; if (!start && end) return `Until ${formatDate(end)}`;
return `${formatDate(start)}${formatDate(end)}`; return `${formatDate(start)}${formatDate(end)}`;
@@ -134,7 +135,7 @@
if (error) throw error; if (error) throw error;
toasts.success(`Event "${created.name}" created`); toasts.success(m.events_created({ name: created.name }));
showCreateModal = false; showCreateModal = false;
resetForm(); resetForm();
goto(`/${data.org.slug}/events/${created.slug}`); goto(`/${data.org.slug}/events/${created.slug}`);
@@ -166,7 +167,7 @@
</script> </script>
<svelte:head> <svelte:head>
<title>Events | {data.org.name}</title> <title>{m.events_title()} | {data.org.name}</title>
</svelte:head> </svelte:head>
<div class="flex flex-col h-full"> <div class="flex flex-col h-full">
@@ -175,9 +176,9 @@
class="flex items-center justify-between px-6 py-5 border-b border-light/5" class="flex items-center justify-between px-6 py-5 border-b border-light/5"
> >
<div> <div>
<h1 class="text-h1 font-heading text-white">Events</h1> <h1 class="text-h1 font-heading text-white">{m.events_title()}</h1>
<p class="text-body-sm text-light/50 mt-1"> <p class="text-body-sm text-light/50 mt-1">
Organize and manage your events {m.events_subtitle()}
</p> </p>
</div> </div>
{#if isEditor} {#if isEditor}
@@ -191,7 +192,7 @@
style="font-size: 20px; font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 20;" style="font-size: 20px; font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 20;"
>add</span >add</span
> >
New Event {m.events_new()}
</button> </button>
{/if} {/if}
</header> </header>
@@ -228,9 +229,9 @@
style="font-size: 64px; font-variation-settings: 'FILL' 0, 'wght' 300, 'GRAD' 0, 'opsz' 48;" style="font-size: 64px; font-variation-settings: 'FILL' 0, 'wght' 300, 'GRAD' 0, 'opsz' 48;"
>celebration</span >celebration</span
> >
<p class="text-h3 font-heading mb-2">No events yet</p> <p class="text-h3 font-heading mb-2">{m.events_empty_title()}</p>
<p class="text-body text-light/30"> <p class="text-body text-light/30">
Create your first event to get started {m.events_empty_desc()}
</p> </p>
{#if isEditor} {#if isEditor}
<button <button
@@ -243,7 +244,7 @@
style="font-size: 20px; font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 20;" style="font-size: 20px; font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 20;"
>add</span >add</span
> >
Create Event {m.events_create()}
</button> </button>
{/if} {/if}
</div> </div>
@@ -346,18 +347,18 @@
onclick={(e) => e.target === e.currentTarget && (showCreateModal = false)} onclick={(e) => e.target === e.currentTarget && (showCreateModal = false)}
role="dialog" role="dialog"
aria-modal="true" aria-modal="true"
aria-label="Create Event" aria-label={m.events_create()}
> >
<div <div
class="bg-night rounded-2xl w-full max-w-lg shadow-2xl border border-light/10" class="bg-night rounded-2xl w-full max-w-lg shadow-2xl border border-light/10"
> >
<div class="flex items-center justify-between p-5 border-b border-light/5"> <div class="flex items-center justify-between p-5 border-b border-light/5">
<h2 class="text-h3 font-heading text-white">Create Event</h2> <h2 class="text-h3 font-heading text-white">{m.events_create()}</h2>
<button <button
type="button" type="button"
class="text-light/40 hover:text-white transition-colors" class="text-light/40 hover:text-white transition-colors"
onclick={() => (showCreateModal = false)} onclick={() => (showCreateModal = false)}
aria-label="Close" aria-label={m.btn_close()}
> >
<span <span
class="material-symbols-rounded" class="material-symbols-rounded"
@@ -379,13 +380,13 @@
<label <label
for="event-name" for="event-name"
class="text-body-sm text-light/60 font-body" class="text-body-sm text-light/60 font-body"
>Event Name</label >{m.events_form_name()}</label
> >
<input <input
id="event-name" id="event-name"
type="text" type="text"
bind:value={newEventName} bind:value={newEventName}
placeholder="e.g., Summer Conference 2026" placeholder={m.events_form_name_placeholder()}
class="bg-dark border border-light/10 rounded-xl px-3 py-2 text-body text-white placeholder:text-light/30 focus:outline-none focus:border-primary" class="bg-dark border border-light/10 rounded-xl px-3 py-2 text-body text-white placeholder:text-light/30 focus:outline-none focus:border-primary"
required required
/> />
@@ -396,12 +397,12 @@
<label <label
for="event-desc" for="event-desc"
class="text-body-sm text-light/60 font-body" class="text-body-sm text-light/60 font-body"
>Description</label >{m.events_form_description()}</label
> >
<textarea <textarea
id="event-desc" id="event-desc"
bind:value={newEventDescription} bind:value={newEventDescription}
placeholder="Brief description of the event..." placeholder={m.events_form_description_placeholder()}
rows="2" rows="2"
class="bg-dark border border-light/10 rounded-xl px-3 py-2 text-body text-white placeholder:text-light/30 focus:outline-none focus:border-primary resize-none" class="bg-dark border border-light/10 rounded-xl px-3 py-2 text-body text-white placeholder:text-light/30 focus:outline-none focus:border-primary resize-none"
></textarea> ></textarea>
@@ -413,7 +414,7 @@
<label <label
for="event-start" for="event-start"
class="text-body-sm text-light/60 font-body" class="text-body-sm text-light/60 font-body"
>Start Date</label >{m.events_form_start_date()}</label
> >
<input <input
id="event-start" id="event-start"
@@ -426,7 +427,7 @@
<label <label
for="event-end" for="event-end"
class="text-body-sm text-light/60 font-body" class="text-body-sm text-light/60 font-body"
>End Date</label >{m.events_form_end_date()}</label
> >
<input <input
id="event-end" id="event-end"
@@ -442,13 +443,13 @@
<label <label
for="event-venue" for="event-venue"
class="text-body-sm text-light/60 font-body" class="text-body-sm text-light/60 font-body"
>Venue</label >{m.events_form_venue()}</label
> >
<input <input
id="event-venue" id="event-venue"
type="text" type="text"
bind:value={newEventVenue} bind:value={newEventVenue}
placeholder="e.g., Convention Center" placeholder={m.events_form_venue_placeholder()}
class="bg-dark border border-light/10 rounded-xl px-3 py-2 text-body text-white placeholder:text-light/30 focus:outline-none focus:border-primary" class="bg-dark border border-light/10 rounded-xl px-3 py-2 text-body text-white placeholder:text-light/30 focus:outline-none focus:border-primary"
/> />
</div> </div>
@@ -457,7 +458,7 @@
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<!-- svelte-ignore a11y_label_has_associated_control --> <!-- svelte-ignore a11y_label_has_associated_control -->
<label class="text-body-sm text-light/60 font-body" <label class="text-body-sm text-light/60 font-body"
>Color</label >{m.events_form_color()}</label
> >
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
{#each presetColors as color} {#each presetColors as color}
@@ -469,7 +470,7 @@
: 'border-transparent hover:border-light/30'}" : 'border-transparent hover:border-light/30'}"
style="background-color: {color}" style="background-color: {color}"
onclick={() => (newEventColor = color)} onclick={() => (newEventColor = color)}
aria-label="Select color {color}" aria-label={m.events_form_select_color({ color })}
></button> ></button>
{/each} {/each}
</div> </div>
@@ -487,14 +488,14 @@
resetForm(); resetForm();
}} }}
> >
Cancel {m.btn_cancel()}
</button> </button>
<button <button
type="submit" type="submit"
disabled={!newEventName.trim() || creating} disabled={!newEventName.trim() || creating}
class="px-4 py-2 bg-primary text-background rounded-xl font-body text-body-sm hover:bg-primary-hover transition-colors disabled:opacity-50 disabled:cursor-not-allowed" class="px-4 py-2 bg-primary text-background rounded-xl font-body text-body-sm hover:bg-primary-hover transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
> >
{creating ? "Creating..." : "Create Event"} {creating ? m.events_creating() : m.events_create()}
</button> </button>
</div> </div>
</form> </form>

View File

@@ -3,6 +3,7 @@
import { Avatar } from "$lib/components/ui"; import { Avatar } from "$lib/components/ui";
import type { Snippet } from "svelte"; import type { Snippet } from "svelte";
import type { Event, EventMember } from "$lib/api/events"; import type { Event, EventMember } from "$lib/api/events";
import * as m from "$lib/paraglide/messages";
interface Props { interface Props {
data: { data: {
@@ -30,43 +31,43 @@
const modules = $derived([ const modules = $derived([
{ {
href: basePath, href: basePath,
label: "Overview", label: m.events_overview(),
icon: "dashboard", icon: "dashboard",
exact: true, exact: true,
}, },
{ {
href: `${basePath}/tasks`, href: `${basePath}/tasks`,
label: "Tasks", label: m.events_mod_tasks(),
icon: "task_alt", icon: "task_alt",
}, },
{ {
href: `${basePath}/files`, href: `${basePath}/files`,
label: "Files", label: m.events_mod_files(),
icon: "folder", icon: "folder",
}, },
{ {
href: `${basePath}/schedule`, href: `${basePath}/schedule`,
label: "Schedule", label: m.events_mod_schedule(),
icon: "calendar_today", icon: "calendar_today",
}, },
{ {
href: `${basePath}/budget`, href: `${basePath}/budget`,
label: "Budget", label: m.events_mod_budget(),
icon: "account_balance_wallet", icon: "account_balance_wallet",
}, },
{ {
href: `${basePath}/guests`, href: `${basePath}/guests`,
label: "Guests", label: m.events_mod_guests(),
icon: "groups", icon: "groups",
}, },
{ {
href: `${basePath}/team`, href: `${basePath}/team`,
label: "Team", label: m.events_mod_team(),
icon: "badge", icon: "badge",
}, },
{ {
href: `${basePath}/sponsors`, href: `${basePath}/sponsors`,
label: "Sponsors", label: m.events_mod_sponsors(),
icon: "handshake", icon: "handshake",
}, },
]); ]);
@@ -155,7 +156,7 @@
<!-- Event Team Preview --> <!-- Event Team Preview -->
<div class="p-3 border-t border-light/5"> <div class="p-3 border-t border-light/5">
<p class="text-[11px] text-light/40 mb-2 px-1"> <p class="text-[11px] text-light/40 mb-2 px-1">
Team ({data.eventMembers.length}) {m.events_team_count({ count: String(data.eventMembers.length) })}
</p> </p>
<div class="flex flex-wrap gap-1 px-1"> <div class="flex flex-wrap gap-1 px-1">
{#each data.eventMembers.slice(0, 8) as member} {#each data.eventMembers.slice(0, 8) as member}
@@ -189,7 +190,7 @@
style="font-size: 16px; font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 16;" style="font-size: 16px; font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 16;"
>arrow_back</span >arrow_back</span
> >
All Events {m.events_all_events()}
</a> </a>
</aside> </aside>

View File

@@ -6,6 +6,7 @@
import type { Database } from "$lib/supabase/types"; import type { Database } from "$lib/supabase/types";
import { toasts } from "$lib/stores/ui"; import { toasts } from "$lib/stores/ui";
import type { Event, EventMember } from "$lib/api/events"; import type { Event, EventMember } from "$lib/api/events";
import * as m from "$lib/paraglide/messages";
interface Props { interface Props {
data: { data: {
@@ -63,74 +64,74 @@
`/${data.org.slug}/events/${data.event.slug}`, `/${data.org.slug}/events/${data.event.slug}`,
); );
const statusOptions = [ const statusOptions = $derived([
{ value: "planning", label: "Planning", icon: "edit_note", color: "text-amber-400" }, { value: "planning", label: m.events_status_planning(), icon: "edit_note", color: "text-amber-400" },
{ value: "active", label: "Active", icon: "play_circle", color: "text-emerald-400" }, { value: "active", label: m.events_status_active(), icon: "play_circle", color: "text-emerald-400" },
{ value: "completed", label: "Completed", icon: "check_circle", color: "text-blue-400" }, { value: "completed", label: m.events_status_completed(), icon: "check_circle", color: "text-blue-400" },
{ value: "archived", label: "Archived", icon: "archive", color: "text-light/40" }, { value: "archived", label: m.events_status_archived(), icon: "archive", color: "text-light/40" },
]; ]);
const moduleCards = $derived([ const moduleCards = $derived([
{ {
href: `${basePath}/tasks`, href: `${basePath}/tasks`,
label: "Tasks", label: m.events_mod_tasks(),
icon: "task_alt", icon: "task_alt",
description: "Manage tasks, milestones, and progress", description: m.events_mod_tasks_desc(),
color: "text-emerald-400", color: "text-emerald-400",
bg: "bg-emerald-400/10", bg: "bg-emerald-400/10",
}, },
{ {
href: `${basePath}/files`, href: `${basePath}/files`,
label: "Files", label: m.events_mod_files(),
icon: "folder", icon: "folder",
description: "Documents, contracts, and media", description: m.events_mod_files_desc(),
color: "text-blue-400", color: "text-blue-400",
bg: "bg-blue-400/10", bg: "bg-blue-400/10",
}, },
{ {
href: `${basePath}/schedule`, href: `${basePath}/schedule`,
label: "Schedule", label: m.events_mod_schedule(),
icon: "calendar_today", icon: "calendar_today",
description: "Event timeline and program", description: m.events_mod_schedule_desc(),
color: "text-purple-400", color: "text-purple-400",
bg: "bg-purple-400/10", bg: "bg-purple-400/10",
}, },
{ {
href: `${basePath}/budget`, href: `${basePath}/budget`,
label: "Budget", label: m.events_mod_budget(),
icon: "account_balance_wallet", icon: "account_balance_wallet",
description: "Income, expenses, and tracking", description: m.events_mod_budget_desc(),
color: "text-amber-400", color: "text-amber-400",
bg: "bg-amber-400/10", bg: "bg-amber-400/10",
}, },
{ {
href: `${basePath}/guests`, href: `${basePath}/guests`,
label: "Guests", label: m.events_mod_guests(),
icon: "groups", icon: "groups",
description: "Guest list and registration", description: m.events_mod_guests_desc(),
color: "text-pink-400", color: "text-pink-400",
bg: "bg-pink-400/10", bg: "bg-pink-400/10",
}, },
{ {
href: `${basePath}/team`, href: `${basePath}/team`,
label: "Team", label: m.events_mod_team(),
icon: "badge", icon: "badge",
description: "Team members and shift scheduling", description: m.events_mod_team_desc(),
color: "text-teal-400", color: "text-teal-400",
bg: "bg-teal-400/10", bg: "bg-teal-400/10",
}, },
{ {
href: `${basePath}/sponsors`, href: `${basePath}/sponsors`,
label: "Sponsors", label: m.events_mod_sponsors(),
icon: "handshake", icon: "handshake",
description: "Sponsors, partners, and deliverables", description: m.events_mod_sponsors_desc(),
color: "text-orange-400", color: "text-orange-400",
bg: "bg-orange-400/10", bg: "bg-orange-400/10",
}, },
]); ]);
function formatDate(dateStr: string | null): string { function formatDate(dateStr: string | null): string {
if (!dateStr) return "Not set"; if (!dateStr) return m.events_not_set();
return new Date(dateStr).toLocaleDateString(undefined, { return new Date(dateStr).toLocaleDateString(undefined, {
weekday: "short", weekday: "short",
month: "long", month: "long",
@@ -146,10 +147,10 @@
const diff = Math.ceil( const diff = Math.ceil(
(start.getTime() - now.getTime()) / (1000 * 60 * 60 * 24), (start.getTime() - now.getTime()) / (1000 * 60 * 60 * 24),
); );
if (diff < 0) return `${Math.abs(diff)} days ago`; if (diff < 0) return m.events_days_ago({ count: String(Math.abs(diff)) });
if (diff === 0) return "Today!"; if (diff === 0) return m.events_today();
if (diff === 1) return "Tomorrow"; if (diff === 1) return m.events_tomorrow();
return `In ${diff} days`; return m.events_in_days({ count: String(diff) });
} }
async function handleSave() { async function handleSave() {
@@ -171,7 +172,7 @@
if (error) throw error; if (error) throw error;
toasts.success("Event updated"); toasts.success(m.events_updated());
editing = false; editing = false;
// Refresh the page data // Refresh the page data
goto(`/${data.org.slug}/events/${data.event.slug}`, { goto(`/${data.org.slug}/events/${data.event.slug}`, {
@@ -194,7 +195,7 @@
if (error) throw error; if (error) throw error;
toasts.success("Event deleted"); toasts.success(m.events_deleted());
goto(`/${data.org.slug}/events`); goto(`/${data.org.slug}/events`);
} catch (e: any) { } catch (e: any) {
toasts.error(e.message || "Failed to delete event"); toasts.error(e.message || "Failed to delete event");
@@ -299,7 +300,7 @@
class="px-3 py-1.5 text-body-sm text-light/60 hover:text-white transition-colors" class="px-3 py-1.5 text-body-sm text-light/60 hover:text-white transition-colors"
onclick={() => (editing = false)} onclick={() => (editing = false)}
> >
Cancel {m.btn_cancel()}
</button> </button>
<button <button
type="button" type="button"
@@ -307,13 +308,13 @@
disabled={saving} disabled={saving}
onclick={handleSave} onclick={handleSave}
> >
{saving ? "Saving..." : "Save"} {saving ? m.events_saving() : m.btn_save()}
</button> </button>
{:else} {:else}
<button <button
type="button" type="button"
class="p-2 text-light/40 hover:text-white transition-colors rounded-lg hover:bg-dark/50" class="p-2 text-light/40 hover:text-white transition-colors rounded-lg hover:bg-dark/50"
title="Edit event" title={m.btn_edit()}
onclick={() => (editing = true)} onclick={() => (editing = true)}
> >
<span <span
@@ -325,7 +326,7 @@
<button <button
type="button" type="button"
class="p-2 text-light/40 hover:text-error transition-colors rounded-lg hover:bg-error/10" class="p-2 text-light/40 hover:text-error transition-colors rounded-lg hover:bg-error/10"
title="Delete event" title={m.btn_delete()}
onclick={() => (showDeleteConfirm = true)} onclick={() => (showDeleteConfirm = true)}
> >
<span <span
@@ -365,13 +366,13 @@
<input <input
type="text" type="text"
bind:value={editVenueName} bind:value={editVenueName}
placeholder="Venue name" placeholder={m.events_form_venue_placeholder()}
class="bg-dark border border-light/10 rounded-xl px-3 py-2 text-body-sm text-white placeholder:text-light/30 focus:outline-none focus:border-primary" class="bg-dark border border-light/10 rounded-xl px-3 py-2 text-body-sm text-white placeholder:text-light/30 focus:outline-none focus:border-primary"
/> />
<input <input
type="text" type="text"
bind:value={editVenueAddress} bind:value={editVenueAddress}
placeholder="Venue address" placeholder={m.events_form_venue_address_placeholder()}
class="bg-dark border border-light/10 rounded-xl px-3 py-2 text-body-sm text-white placeholder:text-light/30 focus:outline-none focus:border-primary" class="bg-dark border border-light/10 rounded-xl px-3 py-2 text-body-sm text-white placeholder:text-light/30 focus:outline-none focus:border-primary"
/> />
</div> </div>
@@ -388,7 +389,7 @@
{/if} {/if}
<!-- Module Cards Grid --> <!-- Module Cards Grid -->
<h2 class="text-h3 font-heading text-white mb-4">Modules</h2> <h2 class="text-h3 font-heading text-white mb-4">{m.events_modules()}</h2>
<div <div
class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3"
> >
@@ -421,7 +422,7 @@
<!-- Info Card --> <!-- Info Card -->
<div class="bg-dark/30 border border-light/5 rounded-xl p-5"> <div class="bg-dark/30 border border-light/5 rounded-xl p-5">
<h3 class="text-body font-heading text-white mb-3"> <h3 class="text-body font-heading text-white mb-3">
Event Details {m.events_details()}
</h3> </h3>
<div class="flex flex-col gap-2.5"> <div class="flex flex-col gap-2.5">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
@@ -432,7 +433,7 @@
> >
<div> <div>
<p class="text-[11px] text-light/40"> <p class="text-[11px] text-light/40">
Start Date {m.events_start_date()}
</p> </p>
<p class="text-body-sm text-white"> <p class="text-body-sm text-white">
{formatDate(data.event.start_date)} {formatDate(data.event.start_date)}
@@ -446,7 +447,7 @@
>event</span >event</span
> >
<div> <div>
<p class="text-[11px] text-light/40">End Date</p> <p class="text-[11px] text-light/40">{m.events_end_date()}</p>
<p class="text-body-sm text-white"> <p class="text-body-sm text-white">
{formatDate(data.event.end_date)} {formatDate(data.event.end_date)}
</p> </p>
@@ -460,7 +461,7 @@
>location_on</span >location_on</span
> >
<div> <div>
<p class="text-[11px] text-light/40">Venue</p> <p class="text-[11px] text-light/40">{m.events_venue()}</p>
<p class="text-body-sm text-white"> <p class="text-body-sm text-white">
{data.event.venue_name} {data.event.venue_name}
</p> </p>
@@ -479,12 +480,12 @@
<div class="bg-dark/30 border border-light/5 rounded-xl p-5"> <div class="bg-dark/30 border border-light/5 rounded-xl p-5">
<div class="flex items-center justify-between mb-3"> <div class="flex items-center justify-between mb-3">
<h3 class="text-body font-heading text-white"> <h3 class="text-body font-heading text-white">
Team ({data.eventMembers.length}) {m.events_team_count({ count: String(data.eventMembers.length) })}
</h3> </h3>
<a <a
href="{basePath}/team" href="{basePath}/team"
class="text-[12px] text-primary hover:underline" class="text-[12px] text-primary hover:underline"
>Manage</a >{m.events_team_manage()}</a
> >
</div> </div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
@@ -518,12 +519,12 @@
href="{basePath}/team" href="{basePath}/team"
class="text-body-sm text-primary hover:underline text-center pt-1" class="text-body-sm text-primary hover:underline text-center pt-1"
> >
+{data.eventMembers.length - 6} more {m.events_more_members({ count: String(data.eventMembers.length - 6) })}
</a> </a>
{/if} {/if}
{#if data.eventMembers.length === 0} {#if data.eventMembers.length === 0}
<p class="text-body-sm text-light/30 text-center py-4"> <p class="text-body-sm text-light/30 text-center py-4">
No team members assigned yet {m.events_team_empty()}
</p> </p>
{/if} {/if}
</div> </div>
@@ -543,17 +544,14 @@
e.target === e.currentTarget && (showDeleteConfirm = false)} e.target === e.currentTarget && (showDeleteConfirm = false)}
role="dialog" role="dialog"
aria-modal="true" aria-modal="true"
aria-label="Delete Event" aria-label={m.events_delete_title()}
> >
<div <div
class="bg-night rounded-2xl w-full max-w-sm shadow-2xl border border-light/10 p-6" class="bg-night rounded-2xl w-full max-w-sm shadow-2xl border border-light/10 p-6"
> >
<h2 class="text-h3 font-heading text-white mb-2">Delete Event?</h2> <h2 class="text-h3 font-heading text-white mb-2">{m.events_delete_title()}</h2>
<p class="text-body-sm text-light/50 mb-6"> <p class="text-body-sm text-light/50 mb-6">
This will permanently delete <strong class="text-white" {m.events_delete_desc({ name: data.event.name })}
>{data.event.name}</strong
>
and all its data. This action cannot be undone.
</p> </p>
<div class="flex items-center justify-end gap-3"> <div class="flex items-center justify-end gap-3">
<button <button
@@ -561,7 +559,7 @@
class="px-4 py-2 text-body-sm text-light/60 hover:text-white transition-colors" class="px-4 py-2 text-body-sm text-light/60 hover:text-white transition-colors"
onclick={() => (showDeleteConfirm = false)} onclick={() => (showDeleteConfirm = false)}
> >
Cancel {m.btn_cancel()}
</button> </button>
<button <button
type="button" type="button"
@@ -569,7 +567,7 @@
disabled={deleting} disabled={deleting}
onclick={handleDelete} onclick={handleDelete}
> >
{deleting ? "Deleting..." : "Delete Event"} {deleting ? m.events_deleting() : m.events_delete_confirm()}
</button> </button>
</div> </div>
</div> </div>