diff --git a/src/routes/(app)/crew/+layout.server.ts b/src/routes/(app)/crew/+layout.server.ts new file mode 100644 index 00000000..f24f897e --- /dev/null +++ b/src/routes/(app)/crew/+layout.server.ts @@ -0,0 +1,14 @@ +import { redirect } from '@sveltejs/kit' +import type { LayoutServerLoad } from './$types' + +export const load: LayoutServerLoad = async ({ locals, url }) => { + // Check authentication first + if (!locals.session.isAuthenticated) { + throw redirect(302, '/auth/login') + } + + return { + user: locals.session.user, + account: locals.session.account + } +} diff --git a/src/routes/(app)/crew/+layout.svelte b/src/routes/(app)/crew/+layout.svelte new file mode 100644 index 00000000..42f5e75c --- /dev/null +++ b/src/routes/(app)/crew/+layout.svelte @@ -0,0 +1,41 @@ + + + + +
+ {@render children?.()} +
+ + diff --git a/src/routes/(app)/crew/+page.svelte b/src/routes/(app)/crew/+page.svelte new file mode 100644 index 00000000..8c0d671e --- /dev/null +++ b/src/routes/(app)/crew/+page.svelte @@ -0,0 +1,770 @@ + + + + + + Crew | Hensei + + +
+
+ {#if crewQuery.isLoading} +
+

Loading...

+
+ {:else if crewQuery.isError || !crewStore.isInCrew} + +
+
+

+ Crews let you team up with other players, track Guild War scores, and share strategies. +

+ +
+ +
+
+ + {#if invitationsQuery.data && invitationsQuery.data.length > 0} +
+
    + {#each invitationsQuery.data as invitation} + {#if invitation.crew && invitation.invitedBy} +
  • +
    + {invitation.crew.name} + + from {invitation.invitedBy.username} + +
    + +
  • + {/if} + {/each} +
+
+ {/if} +
+ {:else} + +
+ + {#snippet actions()} + {#if crewStore.isOfficer} + + {/if} + {/snippet} + + +
+ + {crewStore.crew?.memberCount ?? 0} + Members + +
+ + {#if crewStore.isCaptain} + Captain + {:else if crewStore.isViceCaptain} + Vice Captain + {:else} + Member + {/if} + + Your Role +
+
+ + +
+ Unite and Fight +
+ + {#if eventsQuery.isLoading} +
+

Loading events...

+
+ {:else if eventsQuery.data && eventsQuery.data.length > 0} +
    + {#each eventsQuery.data as event} +
  • goto(`/crew/events/${event.eventNumber}`)}> +
    + {event.eventNumber} + + {elementLabels[event.element] ?? 'Unknown'} + +
    + + {formatDate(event.startDate)} – {formatDate(event.endDate)} + + {formatEventStatus(event.status, event.startDate)} +
  • + {/each} +
+ {:else} +

No events yet

+ {/if} +
+ {/if} +
+
+ + + !open && handleCloseModal()}> + {#snippet children()} + + + + {#snippet children()} + + {/snippet} + + + + {#snippet children()} + + + {/snippet} + + {/snippet} + + + + !open && handleCloseSettingsModal()}> + {#snippet children()} + + + + {#snippet children()} + + {/snippet} + + + + {#snippet children()} + + + {/snippet} + + {/snippet} + + + diff --git a/src/routes/(app)/crew/create/+page.svelte b/src/routes/(app)/crew/create/+page.svelte new file mode 100644 index 00000000..8a43a2db --- /dev/null +++ b/src/routes/(app)/crew/create/+page.svelte @@ -0,0 +1,320 @@ + + + + + + Create Crew | Hensei + + +
+ {#if crewQuery.isLoading} +
+

Loading...

+
+ {:else} +
+ + +
+ {#if errors.form} +
+ {errors.form} +
+ {/if} + +
+ + + {#if errors.name} + {errors.name} + {/if} + {name.length}/100 characters +
+ +
+ + + {#if errors.gamertag} + {errors.gamertag} + {/if} + + Short tag displayed next to member usernames (optional) + +
+ +
+ + + {#if errors.granblueCrewId} + {errors.granblueCrewId} + {/if} + + The numeric ID from your in-game crew (optional) + +
+ +
+ + + {#if errors.description} + {errors.description} + {/if} + {description.length}/500 characters +
+ +
+ + +
+
+
+ {/if} +
+ + diff --git a/src/routes/(app)/crew/join/+page.svelte b/src/routes/(app)/crew/join/+page.svelte new file mode 100644 index 00000000..7e75a93a --- /dev/null +++ b/src/routes/(app)/crew/join/+page.svelte @@ -0,0 +1,308 @@ + + + + + + Join Crew | Hensei + + +
+ + + {#if invitationsQuery.isLoading} +
+

Loading invitations...

+
+ {:else if invitationsQuery.isError} +
+

Failed to load invitations

+
+ {:else if !invitationsQuery.data || invitationsQuery.data.length === 0} +
+

You don't have any pending invitations.

+

+ Ask a crew captain or vice captain to send you an invitation. +

+ +
+ {:else} +
+ {#each invitationsQuery.data as invitation} + {@const expired = isExpired(invitation.expiresAt)} + {@const highlighted = invitation.id === selectedInvitationId} + {@const crew = invitation.crew} + {@const invitedBy = invitation.invitedBy} + + {#if crew && invitedBy} +
+
+

{crew.name}

+ {#if crew.gamertag} + [{crew.gamertag}] + {/if} +
+ + {#if 'description' in crew && crew.description} +

{crew.description}

+ {/if} + +
+ + Invited by {invitedBy.username} + + + {formatDate(invitation.createdAt)} + +
+ + {#if 'memberCount' in crew && crew.memberCount !== undefined} +
+ + {crew.memberCount} member{crew.memberCount === 1 ? '' : 's'} + +
+ {/if} + + {#if expired} +
+ This invitation has expired. +
+ {:else} +
+ Expires: {formatDate(invitation.expiresAt)} +
+ +
+ + +
+ {/if} +
+ {/if} + {/each} +
+ {/if} +
+ + diff --git a/src/routes/(app)/crew/settings/+page.svelte b/src/routes/(app)/crew/settings/+page.svelte new file mode 100644 index 00000000..14fce1b1 --- /dev/null +++ b/src/routes/(app)/crew/settings/+page.svelte @@ -0,0 +1,555 @@ + + + + + + Crew Settings | Hensei + + +
+ + +
+ {#if errors.form} +
+ {errors.form} +
+ {/if} + +
+ + + {#if errors.name} + {errors.name} + {/if} +
+ +
+ + + {#if errors.gamertag} + {errors.gamertag} + {/if} + Short tag displayed next to member usernames +
+ +
+ + +
+ +
+ + + {#if errors.description} + {errors.description} + {/if} +
+ +
+ +
+
+ + +
+

Danger Zone

+ + {#if crewStore.isCaptain} +
+
+

Transfer Captain

+

Transfer ownership of the crew to another member.

+
+ +
+ {/if} + + {#if crewStore.canLeaveCrew} +
+
+

Leave Crew

+

Leave this crew. You'll need an invitation to rejoin.

+
+ +
+ {:else if crewStore.isCaptain} +

As captain, you must transfer ownership before leaving the crew.

+ {/if} +
+
+ + + + + + + Leave Crew + + Are you sure you want to leave {crewStore.crew?.name}? You'll need an invitation to rejoin. + +
+ Cancel + +
+
+
+
+ + + + + + + Transfer Captain + + Select a member to become the new captain. You will become a regular member. + + + {#if transferCandidates.length === 0} +

+ No eligible members to transfer to. The crew needs at least one other member. +

+ {:else} +
+ {#each transferCandidates as member} + + {/each} +
+ {/if} + +
+ Cancel + +
+
+
+
+ + diff --git a/src/routes/(app)/database/gw-events/+page.svelte b/src/routes/(app)/database/gw-events/+page.svelte new file mode 100644 index 00000000..957d6562 --- /dev/null +++ b/src/routes/(app)/database/gw-events/+page.svelte @@ -0,0 +1,329 @@ + + + + +
+
+
+ +
+ +
+
+ +
+ {#if eventsQuery.isLoading} +
+
Loading...
+
+ {/if} + + + + + + + + + + + {#if filteredEvents.length === 0 && !eventsQuery.isLoading} + + + + {:else} + {#each filteredEvents as event} + handleRowClick(event)} class="clickable"> + + + + + {/each} + {/if} + +
#ElementDates
+ {searchTerm ? 'No events match your search' : 'No GW events yet'} +
+ {event.eventNumber} + + + {elementLabels[event.element] ?? 'Unknown'} + + + + {formatDate(event.startDate)} - {formatDate(event.endDate)} + +
+
+ + +
+
+ + diff --git a/src/routes/(app)/database/gw-events/[id]/+page.svelte b/src/routes/(app)/database/gw-events/[id]/+page.svelte new file mode 100644 index 00000000..124de625 --- /dev/null +++ b/src/routes/(app)/database/gw-events/[id]/+page.svelte @@ -0,0 +1,206 @@ + + + + +
+ {#if eventQuery.isLoading} +
+

Loading event...

+
+ {:else if eventQuery.isError} +
+

Failed to load event

+ +
+ {:else if event} + + {#snippet leftAccessory()} + + {/snippet} + {#snippet rightAccessory()} + {#if canEdit} + + {/if} + {/snippet} + + +
+ + + + + {elementLabels[event.element] ?? 'Unknown'} + + + + + + + {#if event.createdAt} + + + {#if event.updatedAt} + + {/if} + + {/if} +
+ {:else} +
+

Event Not Found

+

The event you're looking for could not be found.

+ +
+ {/if} +
+ + diff --git a/src/routes/(app)/database/gw-events/[id]/edit/+page.svelte b/src/routes/(app)/database/gw-events/[id]/edit/+page.svelte new file mode 100644 index 00000000..960f8978 --- /dev/null +++ b/src/routes/(app)/database/gw-events/[id]/edit/+page.svelte @@ -0,0 +1,223 @@ + + + + +
+ {#if eventQuery.isLoading} +
+

Loading event...

+
+ {:else if event} + + {#snippet leftAccessory()} + + {/snippet} + {#snippet rightAccessory()} + + {/snippet} + + + {#if saveError} +
{saveError}
+ {/if} + +
+ + + + + + +
+ {:else} +
+

Event Not Found

+

The event you're looking for could not be found.

+ +
+ {/if} +
+ + diff --git a/src/routes/(app)/database/gw-events/new/+page.svelte b/src/routes/(app)/database/gw-events/new/+page.svelte new file mode 100644 index 00000000..87914d96 --- /dev/null +++ b/src/routes/(app)/database/gw-events/new/+page.svelte @@ -0,0 +1,153 @@ + + + + +
+ + {#snippet leftAccessory()} + + {/snippet} + {#snippet rightAccessory()} + + {/snippet} + + + {#if saveError} +
{saveError}
+ {/if} + +
+ + + + + + +
+
+ +