hensei-web/src/routes/(app)/database/raid-groups/[id]/+page.svelte

213 lines
5.3 KiB
Svelte

<svelte:options runes={true} />
<script lang="ts">
import { goto } from '$app/navigation'
import { page } from '$app/stores'
import { createQuery } from '@tanstack/svelte-query'
import { raidAdapter } from '$lib/api/adapters/raid.adapter'
import Button from '$lib/components/ui/Button.svelte'
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
import DetailItem from '$lib/components/ui/DetailItem.svelte'
import SidebarHeader from '$lib/components/ui/SidebarHeader.svelte'
import type { PageData } from './$types'
function displayName(input: any): string {
if (!input) return '—'
const maybe = input.name ?? input
if (typeof maybe === 'string') return maybe
if (maybe && typeof maybe === 'object') return maybe.en || maybe.ja || '—'
return '—'
}
interface Props {
data: PageData
}
let { data }: Props = $props()
// Get group ID from URL
const groupId = $derived($page.params.id)
// Query for group data
const groupQuery = createQuery(() => ({
queryKey: ['raid-groups', groupId],
queryFn: () => raidAdapter.getGroupById(groupId ?? ''),
enabled: !!groupId
}))
const group = $derived(groupQuery.data)
const userRole = $derived(data.role || 0)
const canEdit = $derived(userRole >= 7)
// Navigate to edit
function handleEdit() {
goto(`/database/raid-groups/${groupId}/edit`)
}
// Navigate back
function handleBack() {
goto('/database/raid-groups')
}
// Navigate to raid detail
function handleRaidClick(slug: string) {
goto(`/database/raids/${slug}`)
}
</script>
<div class="page">
{#if groupQuery.isLoading}
<div class="loading-state">
<p>Loading raid group...</p>
</div>
{:else if groupQuery.isError}
<div class="error-state">
<p>Failed to load raid group</p>
<Button variant="secondary" onclick={handleBack}>Back to Groups</Button>
</div>
{:else if group}
<SidebarHeader title={displayName(group)}>
{#snippet leftAccessory()}
<Button variant="secondary" size="small" onclick={handleBack}>Back</Button>
{/snippet}
{#snippet rightAccessory()}
{#if canEdit}
<Button variant="primary" size="small" onclick={handleEdit}>Edit</Button>
{/if}
{/snippet}
</SidebarHeader>
<section class="details">
<DetailsContainer title="Group Details">
<DetailItem label="Name (EN)" value={group.name.en || '-'} />
<DetailItem label="Name (JA)" value={group.name.ja || '-'} />
<DetailItem label="Section" value={group.section?.toString() ?? '-'} />
<DetailItem label="Order" value={group.order?.toString() ?? '-'} />
<DetailItem label="Difficulty" value={group.difficulty?.toString() ?? '-'} />
</DetailsContainer>
<DetailsContainer title="Flags">
<DetailItem label="HL">
<span class="badge" class:active={group.hl}>{group.hl ? 'Yes' : 'No'}</span>
</DetailItem>
<DetailItem label="Extra">
<span class="badge" class:active={group.extra}>{group.extra ? 'Yes' : 'No'}</span>
</DetailItem>
<DetailItem label="Guidebooks">
<span class="badge" class:active={group.guidebooks}>{group.guidebooks ? 'Yes' : 'No'}</span>
</DetailItem>
</DetailsContainer>
{#if group.raids && group.raids.length > 0}
<DetailsContainer title="Member Raids ({group.raids.length})">
<div class="raids-list">
{#each group.raids as raid}
<button class="raid-item" onclick={() => handleRaidClick(raid.slug)}>
<span class="raid-name">{displayName(raid)}</span>
<span class="raid-level">Lv. {raid.level ?? '-'}</span>
</button>
{/each}
</div>
</DetailsContainer>
{/if}
</section>
{:else}
<div class="not-found">
<h2>Raid Group Not Found</h2>
<p>The raid group you're looking for could not be found.</p>
<Button variant="secondary" onclick={handleBack}>Back to Groups</Button>
</div>
{/if}
</div>
<style lang="scss">
@use '$src/themes/colors' as colors;
@use '$src/themes/effects' as effects;
@use '$src/themes/layout' as layout;
@use '$src/themes/spacing' as spacing;
@use '$src/themes/typography' as typography;
.page {
background: white;
border: 0.5px solid rgba(0, 0, 0, 0.18);
border-radius: layout.$page-corner;
box-shadow: effects.$page-elevation;
}
.loading-state,
.error-state {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 200px;
gap: spacing.$unit-2x;
color: var(--text-secondary);
}
.not-found {
text-align: center;
padding: spacing.$unit-4x;
h2 {
margin-bottom: spacing.$unit;
}
p {
color: var(--text-secondary);
margin-bottom: spacing.$unit-2x;
}
}
.details {
display: flex;
flex-direction: column;
}
.badge {
display: inline-block;
padding: 2px 8px;
border-radius: 4px;
font-size: typography.$font-small;
background: #f0f0f0;
color: #666;
&.active {
background: #28a745;
color: white;
}
}
.raids-list {
display: flex;
flex-direction: column;
gap: spacing.$unit;
padding: spacing.$unit-2x;
}
.raid-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: spacing.$unit spacing.$unit-2x;
background: #f8f9fa;
border: 1px solid #e5e5e5;
border-radius: layout.$item-corner;
cursor: pointer;
text-align: left;
&:hover {
background: #e9ecef;
}
.raid-name {
font-weight: typography.$bold;
}
.raid-level {
color: var(--text-secondary);
font-size: typography.$font-small;
}
}
</style>