refactor: abstract edit page components and functions

This commit is contained in:
rasmusq
2025-11-27 21:17:30 +01:00
parent 86c0665aed
commit 93c5b839d3
4 changed files with 172 additions and 162 deletions

View File

@@ -0,0 +1,55 @@
<script lang="ts">
import { Button } from '$lib/components/ui/button';
import { enhance } from '$app/forms';
import { languageStore } from '$lib/stores/language.svelte';
let {
isAuthenticated,
isOwner,
hasClaimed
}: {
isAuthenticated: boolean;
isOwner: boolean;
hasClaimed: boolean;
} = $props();
const t = $derived(languageStore.t);
</script>
{#if isAuthenticated}
<div class="mb-6">
{#if isOwner}
<Button
disabled
variant="outline"
class="w-full md:w-auto opacity-60 cursor-not-allowed"
>
{t.wishlist.youOwnThis}
</Button>
<p class="text-sm text-muted-foreground mt-2">
{t.wishlist.alreadyInDashboard}
</p>
{:else}
<form
method="POST"
action={hasClaimed ? "?/unclaimWishlist" : "?/claimWishlist"}
use:enhance
>
<Button
type="submit"
variant={hasClaimed ? "outline" : "default"}
class="w-full md:w-auto"
>
{hasClaimed ? "Unclaim Wishlist" : "Claim Wishlist"}
</Button>
</form>
<p class="text-sm text-muted-foreground mt-2">
{#if hasClaimed}
You have claimed this wishlist. It will appear in your dashboard.
{:else}
Claim this wishlist to add it to your dashboard for easy access.
{/if}
</p>
{/if}
</div>
{/if}

View File

@@ -0,0 +1,46 @@
<script lang="ts">
import { Button } from '$lib/components/ui/button';
import { enhance } from '$app/forms';
import UnlockButton from '$lib/components/ui/UnlockButton.svelte';
import { languageStore } from '$lib/stores/language.svelte';
let {
unlocked = $bindable()
}: {
unlocked: boolean;
} = $props();
const t = $derived(languageStore.t);
</script>
<div class="mt-12 pt-8 border-t border-border space-y-4">
<div class="flex flex-col md:flex-row gap-4 justify-between items-stretch md:items-center">
<UnlockButton bind:unlocked />
{#if unlocked}
<form
method="POST"
action="?/deleteWishlist"
use:enhance={({ cancel }) => {
if (!confirm(t.wishlist.deleteConfirm)) {
cancel();
return;
}
return async ({ result }) => {
if (result.type === "success") {
window.location.href = "/dashboard";
}
};
}}
>
<Button
type="submit"
variant="destructive"
class="w-full md:w-auto"
>
{t.wishlist.deleteWishlist}
</Button>
</form>
{/if}
</div>
</div>

View File

@@ -0,0 +1,57 @@
/**
* Utility functions for updating wishlist properties
*/
type UpdateField = {
[key: string]: string;
};
async function updateWishlist(field: UpdateField): Promise<boolean> {
const response = await fetch("?/updateWishlist", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams(field),
});
if (!response.ok) {
console.error(`Failed to update wishlist: ${Object.keys(field)[0]}`);
return false;
}
return true;
}
export async function updateTitle(title: string): Promise<boolean> {
return updateWishlist({ title });
}
export async function updateDescription(description: string | null): Promise<boolean> {
return updateWishlist({ description: description || "" });
}
export async function updateColor(color: string | null): Promise<boolean> {
return updateWishlist({ color: color || "" });
}
export async function updateEndDate(endDate: string | null): Promise<boolean> {
return updateWishlist({ endDate: endDate || "" });
}
export async function reorderItems(items: Array<{ id: string; order: number }>): Promise<boolean> {
const response = await fetch("?/reorderItems", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
items: JSON.stringify(items),
}),
});
if (!response.ok) {
console.error("Failed to update item order");
return false;
}
return true;
}

View File

@@ -8,17 +8,14 @@
import WishlistHeader from "$lib/components/wishlist/WishlistHeader.svelte"; import WishlistHeader from "$lib/components/wishlist/WishlistHeader.svelte";
import WishlistActionButtons from "$lib/components/wishlist/WishlistActionButtons.svelte"; import WishlistActionButtons from "$lib/components/wishlist/WishlistActionButtons.svelte";
import EditableItemsList from "$lib/components/wishlist/EditableItemsList.svelte"; import EditableItemsList from "$lib/components/wishlist/EditableItemsList.svelte";
import ClaimWishlistSection from "$lib/components/wishlist/ClaimWishlistSection.svelte";
import DangerZone from "$lib/components/wishlist/DangerZone.svelte";
import type { Item } from "$lib/server/schema"; import type { Item } from "$lib/server/schema";
import { Button } from "$lib/components/ui/button";
import { enhance } from "$app/forms";
import { languageStore } from '$lib/stores/language.svelte';
import SearchBar from "$lib/components/ui/SearchBar.svelte"; import SearchBar from "$lib/components/ui/SearchBar.svelte";
import UnlockButton from "$lib/components/ui/UnlockButton.svelte"; import * as wishlistUpdates from "$lib/utils/wishlistUpdates";
let { data }: { data: PageData } = $props(); let { data }: { data: PageData } = $props();
const t = $derived(languageStore.t);
let showAddForm = $state(false); let showAddForm = $state(false);
let rearranging = $state(false); let rearranging = $state(false);
let editingItem = $state<Item | null>(null); let editingItem = $state<Item | null>(null);
@@ -76,88 +73,7 @@
id: item.id, id: item.id,
order: index, order: index,
})); }));
await wishlistUpdates.reorderItems(updates);
const response = await fetch("?/reorderItems", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
items: JSON.stringify(updates),
}),
});
if (!response.ok) {
console.error("Failed to update item order");
}
}
async function handleTitleUpdate(title: string): Promise<boolean> {
const response = await fetch("?/updateWishlist", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
title: title,
}),
});
if (!response.ok) {
console.error("Failed to update wishlist title");
return false;
}
return true;
}
async function handleDescriptionUpdate(description: string | null): Promise<boolean> {
const response = await fetch("?/updateWishlist", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
description: description || "",
}),
});
if (!response.ok) {
console.error("Failed to update wishlist description");
return false;
}
return true;
}
async function handleColorUpdate(color: string | null) {
const response = await fetch("?/updateWishlist", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
color: color || "",
}),
});
if (!response.ok) {
console.error("Failed to update wishlist color");
}
}
async function handleEndDateUpdate(endDate: string | null) {
const response = await fetch("?/updateWishlist", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
endDate: endDate || "",
}),
});
if (!response.ok) {
console.error("Failed to update wishlist end date");
}
} }
function handleToggleAddForm() { function handleToggleAddForm() {
@@ -198,10 +114,10 @@
<WishlistHeader <WishlistHeader
wishlist={data.wishlist} wishlist={data.wishlist}
onTitleUpdate={handleTitleUpdate} onTitleUpdate={wishlistUpdates.updateTitle}
onDescriptionUpdate={handleDescriptionUpdate} onDescriptionUpdate={wishlistUpdates.updateDescription}
onColorUpdate={handleColorUpdate} onColorUpdate={wishlistUpdates.updateColor}
onEndDateUpdate={handleEndDateUpdate} onEndDateUpdate={wishlistUpdates.updateEndDate}
/> />
<ShareLinks <ShareLinks
@@ -209,43 +125,11 @@
ownerUrl="/wishlist/{data.wishlist.ownerToken}/edit" ownerUrl="/wishlist/{data.wishlist.ownerToken}/edit"
/> />
{#if data.isAuthenticated} <ClaimWishlistSection
<div class="mb-6"> isAuthenticated={data.isAuthenticated}
{#if data.isOwner} isOwner={data.isOwner}
<Button hasClaimed={data.hasClaimed}
disabled />
variant="outline"
class="w-full md:w-auto opacity-60 cursor-not-allowed"
>
{t.wishlist.youOwnThis}
</Button>
<p class="text-sm text-muted-foreground mt-2">
{t.wishlist.alreadyInDashboard}
</p>
{:else}
<form
method="POST"
action={data.hasClaimed ? "?/unclaimWishlist" : "?/claimWishlist"}
use:enhance
>
<Button
type="submit"
variant={data.hasClaimed ? "outline" : "default"}
class="w-full md:w-auto"
>
{data.hasClaimed ? "Unclaim Wishlist" : "Claim Wishlist"}
</Button>
</form>
<p class="text-sm text-muted-foreground mt-2">
{#if data.hasClaimed}
You have claimed this wishlist. It will appear in your dashboard.
{:else}
Claim this wishlist to add it to your dashboard for easy access.
{/if}
</p>
{/if}
</div>
{/if}
<WishlistActionButtons <WishlistActionButtons
bind:rearranging={rearranging} bind:rearranging={rearranging}
@@ -284,37 +168,5 @@
onReorder={handleReorder} onReorder={handleReorder}
/> />
<div class="mt-12 pt-8 border-t border-border space-y-4"> <DangerZone bind:unlocked={rearranging} />
<div class="flex flex-col md:flex-row gap-4 justify-between items-stretch md:items-center">
<UnlockButton bind:unlocked={rearranging} />
{#if rearranging}
<form
method="POST"
action="?/deleteWishlist"
use:enhance={({ cancel }) => {
if (
!confirm(t.wishlist.deleteConfirm)
) {
cancel();
return;
}
return async ({ result }) => {
if (result.type === "success") {
window.location.href = "/dashboard";
}
};
}}
>
<Button
type="submit"
variant="destructive"
class="w-full md:w-auto"
>
{t.wishlist.deleteWishlist}
</Button>
</form>
{/if}
</div>
</div>
</PageContainer> </PageContainer>