diff --git a/src/lib/composables/drag-drop.svelte.ts b/src/lib/composables/drag-drop.svelte.ts index 10ec8b62..7613ca55 100644 --- a/src/lib/composables/drag-drop.svelte.ts +++ b/src/lib/composables/drag-drop.svelte.ts @@ -30,11 +30,13 @@ export interface DragOperation { container: string position: number itemId: string + type?: GridItemType } target: { container: string position: number itemId?: string + type?: GridItemType } status: 'pending' | 'synced' | 'failed' retryCount: number @@ -192,7 +194,7 @@ export function createDragDropContext(handlers: DragDropHandlers = {}) { return 'move' } - function endDrag(targetHasItem: boolean = false) { + function endDrag(targetItem?: GridItem) { try { console.group('🏁 Drag End') console.log('Final state:', { ...state }) @@ -200,17 +202,19 @@ export function createDragDropContext(handlers: DragDropHandlers = {}) { if (state.validDrop && state.draggedItem && state.hoveredOver) { const operation: DragOperation = { id: crypto.randomUUID(), - type: determineOperationType(state.draggedItem.source, state.hoveredOver, targetHasItem), + type: determineOperationType(state.draggedItem.source, state.hoveredOver, !!targetItem), timestamp: Date.now(), source: { container: state.draggedItem.source.container, position: state.draggedItem.source.position, - itemId: state.draggedItem.data.id + itemId: state.draggedItem.data.id, + type: state.draggedItem.source.type }, target: { container: state.hoveredOver.container, position: state.hoveredOver.position, - itemId: targetHasItem ? 'has-item' : undefined + itemId: targetItem?.id || undefined, + type: state.hoveredOver.type }, status: 'pending', retryCount: 0 @@ -316,7 +320,7 @@ export function createDragDropContext(handlers: DragDropHandlers = {}) { } console.groupEnd() - endDrag(!!targetItem) + endDrag(targetItem) return true } diff --git a/src/routes/api/parties/[id]/grid_characters/+server.ts b/src/routes/api/parties/[id]/grid_characters/+server.ts new file mode 100644 index 00000000..57669375 --- /dev/null +++ b/src/routes/api/parties/[id]/grid_characters/+server.ts @@ -0,0 +1,39 @@ +import { json, type RequestHandler } from '@sveltejs/kit' +import { buildUrl } from '$lib/api/core' + +/** + * POST /api/parties/[id]/grid_characters - Add character to party + * Proxies to Rails API with proper authentication + */ + +export const POST: RequestHandler = async ({ request, params, fetch, cookies }) => { + try { + const body = await request.json() + const editKey = request.headers.get('X-Edit-Key') + + // Forward to Rails API + const response = await fetch(buildUrl('/characters'), { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${cookies.get('access_token')}`, + ...(editKey ? { 'X-Edit-Key': editKey } : {}) + }, + body: JSON.stringify({ + character: { + party_id: params.id, + ...body + } + }) + }) + + const data = await response.json() + return json(data, { status: response.status }) + } catch (error) { + console.error('Error adding character:', error) + return json( + { error: 'Failed to add character' }, + { status: 500 } + ) + } +} \ No newline at end of file diff --git a/src/routes/api/parties/[id]/grid_characters/[characterId]/position/+server.ts b/src/routes/api/parties/[id]/grid_characters/[characterId]/position/+server.ts new file mode 100644 index 00000000..2a4c7edf --- /dev/null +++ b/src/routes/api/parties/[id]/grid_characters/[characterId]/position/+server.ts @@ -0,0 +1,31 @@ +import { json } from '@sveltejs/kit' +import type { RequestHandler } from './$types' +import { buildUrl } from '$lib/api/core' + +export const PUT: RequestHandler = async ({ params, request, fetch, cookies }) => { + const { id: partyId, characterId } = params + const body = await request.json() + const editKey = request.headers.get('X-Edit-Key') + + // Forward the request to the Rails API + const apiResponse = await fetch( + buildUrl(`/parties/${partyId}/grid_characters/${characterId}/position`), + { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${cookies.get('access_token')}`, + ...(editKey ? { 'X-Edit-Key': editKey } : {}) + }, + body: JSON.stringify(body) + } + ) + + if (!apiResponse.ok) { + const error = await apiResponse.json().catch(() => ({ error: 'Failed to update character position' })) + return json(error, { status: apiResponse.status }) + } + + const data = await apiResponse.json() + return json(data) +} \ No newline at end of file diff --git a/src/routes/api/parties/[id]/grid_characters/swap/+server.ts b/src/routes/api/parties/[id]/grid_characters/swap/+server.ts new file mode 100644 index 00000000..5f2867fe --- /dev/null +++ b/src/routes/api/parties/[id]/grid_characters/swap/+server.ts @@ -0,0 +1,31 @@ +import { json } from '@sveltejs/kit' +import type { RequestHandler } from './$types' +import { buildUrl } from '$lib/api/core' + +export const POST: RequestHandler = async ({ params, request, fetch, cookies }) => { + const { id: partyId } = params + const body = await request.json() + const editKey = request.headers.get('X-Edit-Key') + + // Forward the request to the Rails API + const apiResponse = await fetch( + buildUrl(`/parties/${partyId}/grid_characters/swap`), + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${cookies.get('access_token')}`, + ...(editKey ? { 'X-Edit-Key': editKey } : {}) + }, + body: JSON.stringify(body) + } + ) + + if (!apiResponse.ok) { + const error = await apiResponse.json().catch(() => ({ error: 'Failed to swap characters' })) + return json(error, { status: apiResponse.status }) + } + + const data = await apiResponse.json() + return json(data) +} \ No newline at end of file diff --git a/src/routes/api/parties/[id]/grid_summons/+server.ts b/src/routes/api/parties/[id]/grid_summons/+server.ts new file mode 100644 index 00000000..85abd0c8 --- /dev/null +++ b/src/routes/api/parties/[id]/grid_summons/+server.ts @@ -0,0 +1,39 @@ +import { json, type RequestHandler } from '@sveltejs/kit' +import { buildUrl } from '$lib/api/core' + +/** + * POST /api/parties/[id]/grid_summons - Add summon to party + * Proxies to Rails API with proper authentication + */ + +export const POST: RequestHandler = async ({ request, params, fetch, cookies }) => { + try { + const body = await request.json() + const editKey = request.headers.get('X-Edit-Key') + + // Forward to Rails API + const response = await fetch(buildUrl('/summons'), { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${cookies.get('access_token')}`, + ...(editKey ? { 'X-Edit-Key': editKey } : {}) + }, + body: JSON.stringify({ + summon: { + party_id: params.id, + ...body + } + }) + }) + + const data = await response.json() + return json(data, { status: response.status }) + } catch (error) { + console.error('Error adding summon:', error) + return json( + { error: 'Failed to add summon' }, + { status: 500 } + ) + } +} \ No newline at end of file diff --git a/src/routes/api/parties/[id]/grid_summons/[summonId]/position/+server.ts b/src/routes/api/parties/[id]/grid_summons/[summonId]/position/+server.ts new file mode 100644 index 00000000..9f9d7856 --- /dev/null +++ b/src/routes/api/parties/[id]/grid_summons/[summonId]/position/+server.ts @@ -0,0 +1,31 @@ +import { json } from '@sveltejs/kit' +import type { RequestHandler } from './$types' +import { buildUrl } from '$lib/api/core' + +export const PUT: RequestHandler = async ({ params, request, fetch, cookies }) => { + const { id: partyId, summonId } = params + const body = await request.json() + const editKey = request.headers.get('X-Edit-Key') + + // Forward the request to the Rails API + const apiResponse = await fetch( + buildUrl(`/parties/${partyId}/grid_summons/${summonId}/position`), + { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${cookies.get('access_token')}`, + ...(editKey ? { 'X-Edit-Key': editKey } : {}) + }, + body: JSON.stringify(body) + } + ) + + if (!apiResponse.ok) { + const error = await apiResponse.json().catch(() => ({ error: 'Failed to update summon position' })) + return json(error, { status: apiResponse.status }) + } + + const data = await apiResponse.json() + return json(data) +} \ No newline at end of file diff --git a/src/routes/api/parties/[id]/grid_summons/swap/+server.ts b/src/routes/api/parties/[id]/grid_summons/swap/+server.ts new file mode 100644 index 00000000..3d8576ec --- /dev/null +++ b/src/routes/api/parties/[id]/grid_summons/swap/+server.ts @@ -0,0 +1,31 @@ +import { json } from '@sveltejs/kit' +import type { RequestHandler } from './$types' +import { buildUrl } from '$lib/api/core' + +export const POST: RequestHandler = async ({ params, request, fetch, cookies }) => { + const { id: partyId } = params + const body = await request.json() + const editKey = request.headers.get('X-Edit-Key') + + // Forward the request to the Rails API + const apiResponse = await fetch( + buildUrl(`/parties/${partyId}/grid_summons/swap`), + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${cookies.get('access_token')}`, + ...(editKey ? { 'X-Edit-Key': editKey } : {}) + }, + body: JSON.stringify(body) + } + ) + + if (!apiResponse.ok) { + const error = await apiResponse.json().catch(() => ({ error: 'Failed to swap summons' })) + return json(error, { status: apiResponse.status }) + } + + const data = await apiResponse.json() + return json(data) +} \ No newline at end of file diff --git a/src/routes/api/parties/[id]/grid_weapons/+server.ts b/src/routes/api/parties/[id]/grid_weapons/+server.ts new file mode 100644 index 00000000..829204a0 --- /dev/null +++ b/src/routes/api/parties/[id]/grid_weapons/+server.ts @@ -0,0 +1,39 @@ +import { json, type RequestHandler } from '@sveltejs/kit' +import { buildUrl } from '$lib/api/core' + +/** + * POST /api/parties/[id]/grid_weapons - Add weapon to party + * Proxies to Rails API with proper authentication + */ + +export const POST: RequestHandler = async ({ request, params, fetch, cookies }) => { + try { + const body = await request.json() + const editKey = request.headers.get('X-Edit-Key') + + // Forward to Rails API + const response = await fetch(buildUrl('/weapons'), { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${cookies.get('access_token')}`, + ...(editKey ? { 'X-Edit-Key': editKey } : {}) + }, + body: JSON.stringify({ + weapon: { + party_id: params.id, + ...body + } + }) + }) + + const data = await response.json() + return json(data, { status: response.status }) + } catch (error) { + console.error('Error adding weapon:', error) + return json( + { error: 'Failed to add weapon' }, + { status: 500 } + ) + } +} \ No newline at end of file diff --git a/src/routes/api/parties/[id]/grid_weapons/[weaponId]/position/+server.ts b/src/routes/api/parties/[id]/grid_weapons/[weaponId]/position/+server.ts new file mode 100644 index 00000000..793f1338 --- /dev/null +++ b/src/routes/api/parties/[id]/grid_weapons/[weaponId]/position/+server.ts @@ -0,0 +1,31 @@ +import { json } from '@sveltejs/kit' +import type { RequestHandler } from './$types' +import { buildUrl } from '$lib/api/core' + +export const PUT: RequestHandler = async ({ params, request, fetch, cookies }) => { + const { id: partyId, weaponId } = params + const body = await request.json() + const editKey = request.headers.get('X-Edit-Key') + + // Forward the request to the Rails API + const apiResponse = await fetch( + buildUrl(`/parties/${partyId}/grid_weapons/${weaponId}/position`), + { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${cookies.get('access_token')}`, + ...(editKey ? { 'X-Edit-Key': editKey } : {}) + }, + body: JSON.stringify(body) + } + ) + + if (!apiResponse.ok) { + const error = await apiResponse.json().catch(() => ({ error: 'Failed to update weapon position' })) + return json(error, { status: apiResponse.status }) + } + + const data = await apiResponse.json() + return json(data) +} \ No newline at end of file diff --git a/src/routes/api/parties/[id]/grid_weapons/swap/+server.ts b/src/routes/api/parties/[id]/grid_weapons/swap/+server.ts new file mode 100644 index 00000000..be32c641 --- /dev/null +++ b/src/routes/api/parties/[id]/grid_weapons/swap/+server.ts @@ -0,0 +1,31 @@ +import { json } from '@sveltejs/kit' +import type { RequestHandler } from './$types' +import { buildUrl } from '$lib/api/core' + +export const POST: RequestHandler = async ({ params, request, fetch, cookies }) => { + const { id: partyId } = params + const body = await request.json() + const editKey = request.headers.get('X-Edit-Key') + + // Forward the request to the Rails API + const apiResponse = await fetch( + buildUrl(`/parties/${partyId}/grid_weapons/swap`), + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${cookies.get('access_token')}`, + ...(editKey ? { 'X-Edit-Key': editKey } : {}) + }, + body: JSON.stringify(body) + } + ) + + if (!apiResponse.ok) { + const error = await apiResponse.json().catch(() => ({ error: 'Failed to swap weapons' })) + return json(error, { status: apiResponse.status }) + } + + const data = await apiResponse.json() + return json(data) +} \ No newline at end of file diff --git a/src/routes/test/drag-drop/+page.svelte b/src/routes/test/drag-drop/+page.svelte new file mode 100644 index 00000000..74734c9b --- /dev/null +++ b/src/routes/test/drag-drop/+page.svelte @@ -0,0 +1,688 @@ + + + + +
+
+

Drag & Drop Test

+
+ {#if syncStatus === 'pending'} + ⏳ {dragContext.getQueuedOperations().length} pending operations + {:else} + βœ… All synced + {/if} +
+
+ +
+

Character Grid

+
+ {#each characters as char, idx} + + +
+ {#if char} +
πŸ‘€
+
{char.character.name?.en}
+ {:else} +
Empty
+ {/if} +
+
+
+ {/each} +
+ +

Extra Characters

+
+ {#each extraCharacters as char, idx} + + +
+ {#if char} +
πŸ‘€
+
{char.character.name?.en}
+ {:else} +
Empty
+ {/if} +
+
+
+ {/each} +
+
+ +
+

Weapon Grid

+
+
+

Mainhand

+
+ {#if weapons[0]} +
βš”οΈ
+
{weapons[0].weapon.name?.en}
+ {:else} +
Empty
+ {/if} +
+
+
+ {#each weapons.slice(1, 10) as weapon, idx} + + +
+ {#if weapon} +
βš”οΈ
+
{weapon.weapon.name?.en}
+ {:else} +
Empty
+ {/if} +
+
+
+ {/each} +
+
+
+ +
+

Summon Grid

+
+
+

Main

+
+ {#if summons[0]} +
πŸ‰
+
{summons[0].summon.name?.en}
+ {/if} +
+
+ +
+ {#each summons.slice(1, 5) as summon, idx} + + +
+ {#if summon} +
πŸ‰
+
{summon.summon.name?.en}
+ {:else} +
Empty
+ {/if} +
+
+
+ {/each} +
+ +
+

Friend

+
+ {#if summons[6]} +
πŸ‰
+
{summons[6].summon.name?.en}
+ {/if} +
+
+
+ +

Subaura

+
+ {#each subauras as summon, idx} + + +
+ {#if summon} +
πŸ‰
+
{summon.summon.name?.en}
+ {:else} +
Empty
+ {/if} +
+
+
+ {/each} +
+
+ +
+

Operations Log

+
{JSON.stringify(operations, null, 2)}
+
+
+ + \ No newline at end of file