diff --git a/src/lib/components/party/Party.svelte b/src/lib/components/party/Party.svelte index f5b37067..034b6414 100644 --- a/src/lib/components/party/Party.svelte +++ b/src/lib/components/party/Party.svelte @@ -54,12 +54,17 @@ import Icon from '$lib/components/Icon.svelte' import DescriptionRenderer from '$lib/components/DescriptionRenderer.svelte' import { openDescriptionSidebar } from '$lib/features/description/openDescriptionSidebar.svelte' + import { + openPartyEditSidebar, + type PartyEditValues + } from '$lib/features/party/openPartyEditSidebar.svelte' + import PartyInfoGrid from '$lib/components/party/info/PartyInfoGrid.svelte' import { DropdownMenu } from 'bits-ui' import DropdownItem from '$lib/components/ui/dropdown/DropdownItem.svelte' import JobSection from '$lib/components/job/JobSection.svelte' import { Gender } from '$lib/utils/jobUtils' import { openJobSelectionSidebar, openJobSkillSelectionSidebar } from '$lib/features/job/openJobSidebar.svelte' - import { partyAdapter } from '$lib/api/adapters/party.adapter' + import { partyAdapter, type UpdatePartyParams } from '$lib/api/adapters/party.adapter' import { extractErrorMessage } from '$lib/utils/errors' import { transformSkillsToArray } from '$lib/utils/jobSkills' import { findNextEmptySlot, SLOT_NOT_FOUND } from '$lib/utils/gridHelpers' @@ -269,6 +274,16 @@ return result.canEdit }) + // Element mapping for theming (used for party element which is numeric) + const ELEMENT_MAP: Record = { + 1: 'wind', + 2: 'fire', + 3: 'water', + 4: 'earth', + 5: 'dark', + 6: 'light' + } + // Derived elements for character image logic const mainWeapon = $derived( (party?.weapons ?? []).find((w) => w?.mainhand || w?.position === -1) @@ -276,6 +291,10 @@ const mainWeaponElement = $derived(mainWeapon?.element ?? mainWeapon?.weapon?.element) const partyElement = $derived((party as any)?.element) + // User's element preference (string) - used for UI theming + type ElementType = 'wind' | 'fire' | 'water' | 'earth' | 'dark' | 'light' + const userElement = $derived(party.user?.avatar?.element as ElementType | undefined) + // Check if any items in the party are linked to collection (for sync menu option) const hasCollectionLinks = $derived.by(() => { const hasLinkedWeapons = (party?.weapons ?? []).some((w) => w?.collectionWeaponId) @@ -333,7 +352,7 @@ } // Party operations - async function updatePartyDetails(updates: Partial) { + async function updatePartyDetails(updates: Omit) { if (!canEdit()) return loading = true @@ -341,7 +360,8 @@ try { // Use TanStack Query mutation to update party - await updatePartyMutation.mutateAsync({ shortcode: party.shortcode, ...updates }) + // Note: API expects UUID (id), shortcode is for cache invalidation + await updatePartyMutation.mutateAsync({ id: party.id, shortcode: party.shortcode, ...updates }) // Party will be updated via cache invalidation } catch (err: any) { error = err.message || 'Failed to update party' @@ -417,6 +437,45 @@ }) } + function openSettingsPanel() { + if (!canEdit()) return + + const initialValues: PartyEditValues = { + name: party.name ?? '', + fullAuto: party.fullAuto ?? false, + autoGuard: party.autoGuard ?? false, + autoSummon: party.autoSummon ?? false, + chargeAttack: party.chargeAttack ?? true, + clearTime: party.clearTime ?? null, + buttonCount: party.buttonCount ?? null, + chainCount: party.chainCount ?? null, + summonCount: party.summonCount ?? null, + videoUrl: party.videoUrl ?? null, + raid: party.raid ?? null, + raidId: party.raid?.id ?? null + } + + openPartyEditSidebar({ + initialValues, + element: userElement, + onSave: async (values) => { + await updatePartyDetails({ + name: values.name, + fullAuto: values.fullAuto, + autoGuard: values.autoGuard, + autoSummon: values.autoSummon, + chargeAttack: values.chargeAttack, + clearTime: values.clearTime, + buttonCount: values.buttonCount, + chainCount: values.chainCount, + summonCount: values.summonCount, + videoUrl: values.videoUrl, + raidId: values.raidId + }) + } + }) + } + async function deleteParty() { // Only allow deletion if user owns the party if (party.user?.id !== authUserId) return @@ -888,7 +947,7 @@ {#if canEdit()} - + {#if hasCollectionLinks} @@ -926,41 +985,12 @@ - {#if party.description || party.raid} -
- {#if party.description} -
e.key === 'Enter' && openDescriptionPanel()} - aria-label="View full description" - > -

Description

-
- -
-
- {/if} - - {#if party.raid} -
-

Raid

-
- - {typeof party.raid.name === 'string' - ? party.raid.name - : party.raid.name?.en || party.raid.name?.ja || 'Unknown Raid'} - - {#if party.raid.group} - Difficulty: {party.raid.group.difficulty} - {/if} -
-
- {/if} -
- {/if} + + + + +
+ +
+ +
+ + +
+ +
+ + +
+ {#if isLoading} +
+ Loading raids... +
+ {:else} + + {/if} +
+
+ + diff --git a/src/lib/components/sidebar/PartyEditSidebar.svelte b/src/lib/components/sidebar/PartyEditSidebar.svelte new file mode 100644 index 00000000..367f52e6 --- /dev/null +++ b/src/lib/components/sidebar/PartyEditSidebar.svelte @@ -0,0 +1,342 @@ + + +
+
+ + +
+ + + + {#snippet children()} + + {/snippet} + + + + + + + + {#snippet children()} + + {/snippet} + + + {#snippet children()} + + {/snippet} + + + {#snippet children()} + + {/snippet} + + + {#snippet children()} + + {/snippet} + + +
+ + diff --git a/src/lib/features/party/openPartyEditSidebar.svelte.ts b/src/lib/features/party/openPartyEditSidebar.svelte.ts new file mode 100644 index 00000000..b0859f31 --- /dev/null +++ b/src/lib/features/party/openPartyEditSidebar.svelte.ts @@ -0,0 +1,34 @@ +import { sidebar } from '$lib/stores/sidebar.svelte' +import PartyEditSidebar, { + type PartyEditValues +} from '$lib/components/sidebar/PartyEditSidebar.svelte' + +type ElementType = 'wind' | 'fire' | 'water' | 'earth' | 'dark' | 'light' + +interface PartyEditSidebarOptions { + /** Current party values for editing */ + initialValues: PartyEditValues + /** Party element for switch theming */ + element?: ElementType + /** Callback when user saves changes */ + onSave: (values: PartyEditValues) => void +} + +/** + * Opens the party edit sidebar for editing battle settings, performance metrics, and video URL. + */ +export function openPartyEditSidebar(options: PartyEditSidebarOptions) { + const { initialValues, element, onSave } = options + + sidebar.openWithComponent('Edit Party Settings', PartyEditSidebar, { + initialValues, + element, + onSave + }) +} + +export function closePartyEditSidebar() { + sidebar.close() +} + +export type { PartyEditValues }