diff --git a/src/lib/api/adapters/raid.adapter.ts b/src/lib/api/adapters/raid.adapter.ts new file mode 100644 index 00000000..1cbed49f --- /dev/null +++ b/src/lib/api/adapters/raid.adapter.ts @@ -0,0 +1,160 @@ +import { BaseAdapter } from './base.adapter' +import { DEFAULT_ADAPTER_CONFIG } from './config' +import type { RequestOptions } from './types' +import type { + RaidFull, + RaidGroupFull, + RaidGroupFlat, + CreateRaidInput, + UpdateRaidInput, + CreateRaidGroupInput, + UpdateRaidGroupInput, + RaidFilters +} from '$lib/types/api/raid' +import type { Raid, RaidGroup } from '$lib/types/api/entities' + +/** + * Adapter for Raid and RaidGroup API operations + */ +export class RaidAdapter extends BaseAdapter { + // ==================== Raid Operations ==================== + + /** + * Get all raids with optional filtering + */ + async getAll(filters?: RaidFilters, options?: RequestOptions): Promise { + const queryParams: Record = {} + + if (filters) { + if (filters.element !== undefined) queryParams.element = filters.element + if (filters.groupId) queryParams.group_id = filters.groupId + if (filters.difficulty !== undefined) queryParams.difficulty = filters.difficulty + if (filters.hl !== undefined) queryParams.hl = filters.hl + if (filters.extra !== undefined) queryParams.extra = filters.extra + if (filters.guidebooks !== undefined) queryParams.guidebooks = filters.guidebooks + } + + const response = await this.request('/raids', { + ...options, + query: Object.keys(queryParams).length > 0 ? queryParams : undefined + }) + return response + } + + /** + * Get a single raid by slug + */ + async getBySlug(slug: string, options?: RequestOptions): Promise { + const response = await this.request(`/raids/${slug}`, options) + return response + } + + /** + * Create a new raid (editor only) + */ + async create(input: CreateRaidInput, options?: RequestOptions): Promise { + const response = await this.request('/raids', { + ...options, + method: 'POST', + body: JSON.stringify({ raid: input }) + }) + this.clearCache('/raids') + return response + } + + /** + * Update a raid (editor only) + */ + async update(slug: string, input: UpdateRaidInput, options?: RequestOptions): Promise { + const response = await this.request(`/raids/${slug}`, { + ...options, + method: 'PUT', + body: JSON.stringify({ raid: input }) + }) + this.clearCache('/raids') + this.clearCache(`/raids/${slug}`) + return response + } + + /** + * Delete a raid (editor only) + */ + async delete(slug: string, options?: RequestOptions): Promise { + await this.request(`/raids/${slug}`, { + ...options, + method: 'DELETE' + }) + this.clearCache('/raids') + this.clearCache(`/raids/${slug}`) + } + + // ==================== RaidGroup Operations ==================== + + /** + * Get all raid groups with their raids + */ + async getGroups(options?: RequestOptions): Promise { + const response = await this.request('/raid_groups', options) + return response + } + + /** + * Get a single raid group by ID + */ + async getGroupById(id: string, options?: RequestOptions): Promise { + const response = await this.request(`/raid_groups/${id}`, options) + return response + } + + /** + * Create a new raid group (editor only) + */ + async createGroup(input: CreateRaidGroupInput, options?: RequestOptions): Promise { + const response = await this.request('/raid_groups', { + ...options, + method: 'POST', + body: JSON.stringify({ raid_group: input }) + }) + this.clearCache('/raid_groups') + return response + } + + /** + * Update a raid group (editor only) + */ + async updateGroup(id: string, input: UpdateRaidGroupInput, options?: RequestOptions): Promise { + const response = await this.request(`/raid_groups/${id}`, { + ...options, + method: 'PUT', + body: JSON.stringify({ raid_group: input }) + }) + this.clearCache('/raid_groups') + this.clearCache(`/raid_groups/${id}`) + return response + } + + /** + * Delete a raid group (editor only) + */ + async deleteGroup(id: string, options?: RequestOptions): Promise { + await this.request(`/raid_groups/${id}`, { + ...options, + method: 'DELETE' + }) + this.clearCache('/raid_groups') + this.clearCache(`/raid_groups/${id}`) + } + + // ==================== Legacy Endpoints ==================== + + /** + * Get all raid groups with raids (legacy endpoint) + * @deprecated Use getGroups() instead + */ + async getLegacyGroups(options?: RequestOptions): Promise { + const response = await this.request('/raids/groups', options) + return response + } +} + +export const raidAdapter = new RaidAdapter(DEFAULT_ADAPTER_CONFIG) diff --git a/src/lib/types/api/raid.ts b/src/lib/types/api/raid.ts new file mode 100644 index 00000000..d5380945 --- /dev/null +++ b/src/lib/types/api/raid.ts @@ -0,0 +1,89 @@ +/** + * Raid and RaidGroup types + * + * Re-exports base types from entities.ts and adds input types for CRUD operations. + */ + +import type { LocalizedName } from './entities' + +// Re-export from entities +export type { Raid, RaidGroup } from './entities' + +// Extended Raid type (from :full view) +export interface RaidFull { + id: string + slug: string + name: LocalizedName + level: number + element: number + group?: RaidGroupFlat +} + +// Flat RaidGroup (from :flat view, used in nested Raid responses) +export interface RaidGroupFlat { + id: string + name: LocalizedName + section: number | string + order: number + difficulty: number + hl: boolean + extra: boolean + guidebooks: boolean +} + +// Full RaidGroup (from :full view, includes raids) +export interface RaidGroupFull extends RaidGroupFlat { + raids: RaidFull[] +} + +// Input types for creating/updating raids +export interface CreateRaidInput { + name_en: string + name_jp: string + slug: string + level: number + element: number + group_id: string +} + +export interface UpdateRaidInput { + name_en?: string + name_jp?: string + slug?: string + level?: number + element?: number + group_id?: string +} + +// Input types for creating/updating raid groups +export interface CreateRaidGroupInput { + name_en: string + name_jp: string + section: number + order: number + difficulty: number + hl: boolean + extra: boolean + guidebooks: boolean +} + +export interface UpdateRaidGroupInput { + name_en?: string + name_jp?: string + section?: number + order?: number + difficulty?: number + hl?: boolean + extra?: boolean + guidebooks?: boolean +} + +// Filter types for raid queries +export interface RaidFilters { + element?: number + groupId?: string + difficulty?: number + hl?: boolean + extra?: boolean + guidebooks?: boolean +} diff --git a/src/routes/(app)/database/raid-groups/+page.svelte b/src/routes/(app)/database/raid-groups/+page.svelte new file mode 100644 index 00000000..2293e374 --- /dev/null +++ b/src/routes/(app)/database/raid-groups/+page.svelte @@ -0,0 +1,315 @@ + + + + + + +
+
+
+ + +
+ +
+
+ +
+ {#if groupsQuery.isLoading} +
+
Loading...
+
+ {/if} + + + + + + + + + + + + + + {#if filteredGroups.length === 0 && !groupsQuery.isLoading} + + + + {:else} + {#each filteredGroups as group} + handleRowClick(group)} class="clickable"> + + + + + + + + {/each} + {/if} + +
NameDifficultySectionOrderFlagsRaids
+ {searchTerm ? 'No groups match your search' : 'No raid groups yet'} +
+ {displayName(group)} + + {group.difficulty ?? '-'} + + {group.section} + + {group.order} + +
+ {#if group.hl} + HL + {/if} + {#if group.extra} + Extra + {/if} + {#if group.guidebooks} + Guidebooks + {/if} +
+
+ {group.raids?.length ?? 0} +
+
+ + +
+
+ + diff --git a/src/routes/(app)/database/raid-groups/[id]/+page.svelte b/src/routes/(app)/database/raid-groups/[id]/+page.svelte new file mode 100644 index 00000000..7f04c507 --- /dev/null +++ b/src/routes/(app)/database/raid-groups/[id]/+page.svelte @@ -0,0 +1,213 @@ + + + + +
+ {#if groupQuery.isLoading} +
+

Loading raid group...

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

Failed to load raid group

+ +
+ {:else if group} + + {#snippet leftAccessory()} + + {/snippet} + {#snippet rightAccessory()} + {#if canEdit} + + {/if} + {/snippet} + + +
+ + + + + + + + + + + {group.hl ? 'Yes' : 'No'} + + + {group.extra ? 'Yes' : 'No'} + + + {group.guidebooks ? 'Yes' : 'No'} + + + + {#if group.raids && group.raids.length > 0} + +
+ {#each group.raids as raid} + + {/each} +
+
+ {/if} + +
+ {:else} +
+

Raid Group Not Found

+

The raid group you're looking for could not be found.

+ +
+ {/if} +
+ + diff --git a/src/routes/(app)/database/raid-groups/[id]/edit/+page.svelte b/src/routes/(app)/database/raid-groups/[id]/edit/+page.svelte new file mode 100644 index 00000000..91bb8809 --- /dev/null +++ b/src/routes/(app)/database/raid-groups/[id]/edit/+page.svelte @@ -0,0 +1,246 @@ + + + + +
+ {#if groupQuery.isLoading} +
+

Loading raid group...

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

Raid Group Not Found

+

The raid group you're looking for could not be found.

+ +
+ {/if} +
+ + diff --git a/src/routes/(app)/database/raid-groups/new/+page.svelte b/src/routes/(app)/database/raid-groups/new/+page.svelte new file mode 100644 index 00000000..ee0a8b27 --- /dev/null +++ b/src/routes/(app)/database/raid-groups/new/+page.svelte @@ -0,0 +1,179 @@ + + + + +
+ + {#snippet leftAccessory()} + + {/snippet} + {#snippet rightAccessory()} + + {/snippet} + + + {#if saveError} +
{saveError}
+ {/if} + +
+ + + + + + + + + + + + + +
+
+ + diff --git a/src/routes/(app)/database/raids/+page.svelte b/src/routes/(app)/database/raids/+page.svelte new file mode 100644 index 00000000..ad5f13c4 --- /dev/null +++ b/src/routes/(app)/database/raids/+page.svelte @@ -0,0 +1,529 @@ + + + + + + +
+
+
+ + Raids + Groups + + + {#if viewMode === 'raids'} +
+ + + + + +
+ {/if} +
+ + {#if viewMode === 'raids'} +
+ {#if raidsQuery.isLoading} +
+
Loading...
+
+ {/if} + + + + + + + + + + + + + {#if filteredRaids.length === 0 && !raidsQuery.isLoading} + + + + {:else} + {#each filteredRaids as raid} + handleRaidClick(raid)} class="clickable"> + + + + + + + {/each} + {/if} + +
NameLevelElementGroupSlug
+ {searchTerm || hasActiveFilters + ? 'No raids match your filters' + : 'No raids yet'} +
+ {displayName(raid)} + + {raid.level ?? '-'} + + {#if raid.element !== undefined && raid.element !== null} + + {:else} + - + {/if} + + {raid.group ? displayName(raid.group) : '-'} + + {raid.slug} +
+
+ + + {:else} +
+ {#if groupsQuery.isLoading} +
+
Loading...
+
+ {/if} + + + + + + + + + + + + + {#if (groupsQuery.data ?? []).length === 0 && !groupsQuery.isLoading} + + + + {:else} + {#each (groupsQuery.data ?? []) as group} + handleGroupClick(group)} class="clickable"> + + + + + + + {/each} + {/if} + +
NameSectionDifficultyFlagsRaids
No raid groups yet
+ {displayName(group)} + + {group.section ?? '-'} + + {group.difficulty ?? '-'} + +
+ {#if group.hl}HL{/if} + {#if group.extra}Extra{/if} + {#if group.guidebooks}Guidebooks{/if} + {#if !group.hl && !group.extra && !group.guidebooks} + - + {/if} +
+
+ {group.raids?.length ?? 0} +
+
+ + + {/if} +
+
+ + diff --git a/src/routes/(app)/database/raids/[slug]/+page.svelte b/src/routes/(app)/database/raids/[slug]/+page.svelte new file mode 100644 index 00000000..c341907a --- /dev/null +++ b/src/routes/(app)/database/raids/[slug]/+page.svelte @@ -0,0 +1,207 @@ + + + + +
+ {#if raidQuery.isLoading} +
+

Loading raid...

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

Failed to load raid

+ +
+ {:else if raid} + + {#snippet leftAccessory()} + + {/snippet} + {#snippet rightAccessory()} + {#if canEdit} + + {/if} + {/snippet} + + +
+ + + + + + + {#if raid.element !== undefined && raid.element !== null} + + {:else} + - + {/if} + + + + + + {#if raid.group} + + {:else} + - + {/if} + + {#if raid.group} + + + {raid.group.hl ? 'Yes' : 'No'} + + + {raid.group.extra ? 'Yes' : 'No'} + + + {raid.group.guidebooks ? 'Yes' : 'No'} + + {/if} + + +
+ {:else} +
+

Raid Not Found

+

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

+ +
+ {/if} +
+ + diff --git a/src/routes/(app)/database/raids/[slug]/edit/+page.svelte b/src/routes/(app)/database/raids/[slug]/edit/+page.svelte new file mode 100644 index 00000000..c5e922c9 --- /dev/null +++ b/src/routes/(app)/database/raids/[slug]/edit/+page.svelte @@ -0,0 +1,265 @@ + + + + +
+ {#if raidQuery.isLoading} +
+

Loading raid...

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

Raid Not Found

+

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

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