update: improve dashboard and distinction between saved and claimed wishlists

This commit is contained in:
rasmusq
2025-11-26 00:37:30 +01:00
parent f5f204b51a
commit 911f9c4936
5 changed files with 161 additions and 88 deletions
+120 -40
View File
@@ -16,14 +16,20 @@
const t = $derived(languageStore.t);
let myWishlistsUnlocked = $state(false);
let claimedWishlistsUnlocked = $state(false);
let savedWishlistsUnlocked = $state(false);
let myWishlistsSearch = $state('');
let claimedWishlistsSearch = $state('');
let savedWishlistsSearch = $state('');
// Combine owned and claimed wishlists for "My Wishlists"
// Only owned wishlists for "My Wishlists" (exclude claimed)
const allMyWishlists = $derived(() => {
const owned = data.wishlists || [];
// Only include claimed wishlists (those with ownerToken, meaning they were claimed via edit link)
return owned;
});
// Claimed wishlists (those with ownerToken, meaning they were claimed via edit link)
const allClaimedWishlists = $derived(() => {
const claimed = (data.savedWishlists || [])
.filter(saved => saved.wishlist?.ownerToken) // Has edit access
.map(saved => ({
@@ -32,7 +38,7 @@
isClaimed: true,
savedId: saved.id
}));
return [...owned, ...claimed];
return claimed;
});
const sortedWishlists = $derived(() => {
@@ -61,6 +67,32 @@
});
});
const sortedClaimedWishlists = $derived(() => {
const filtered = claimedWishlistsSearch.trim()
? allClaimedWishlists().filter(w =>
w.title.toLowerCase().includes(claimedWishlistsSearch.toLowerCase()) ||
w.description?.toLowerCase().includes(claimedWishlistsSearch.toLowerCase())
)
: allClaimedWishlists();
return [...filtered].sort((a, b) => {
if (a.isFavorite && !b.isFavorite) return -1;
if (!a.isFavorite && b.isFavorite) return 1;
const aHasEndDate = !!a.endDate;
const bHasEndDate = !!b.endDate;
if (aHasEndDate && !bHasEndDate) return -1;
if (!aHasEndDate && bHasEndDate) return 1;
if (aHasEndDate && bHasEndDate) {
return new Date(a.endDate!).getTime() - new Date(b.endDate!).getTime();
}
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
});
});
// Saved wishlists are those WITHOUT ownerToken (saved from public view only)
const sortedSavedWishlists = $derived(() => {
const filtered = savedWishlistsSearch.trim()
@@ -155,33 +187,18 @@
color={wishlist.color}
>
<div class="flex gap-2 flex-wrap">
{#if wishlist.isClaimed}
<!-- For claimed wishlists, use saved favorite toggle -->
<form method="POST" action="?/toggleSavedFavorite" use:enhance={() => {
return async ({ update }) => {
await update({ reset: false });
};
}}>
<input type="hidden" name="savedWishlistId" value={wishlist.savedId} />
<input type="hidden" name="isFavorite" value={wishlist.isFavorite} />
<Button type="submit" size="sm" variant="outline">
<Star class={wishlist.isFavorite ? "fill-yellow-500 text-yellow-500" : ""} />
</Button>
</form>
{:else}
<!-- For owned wishlists, use regular favorite toggle -->
<form method="POST" action="?/toggleFavorite" use:enhance={() => {
return async ({ update }) => {
await update({ reset: false });
};
}}>
<input type="hidden" name="wishlistId" value={wishlist.id} />
<input type="hidden" name="isFavorite" value={wishlist.isFavorite} />
<Button type="submit" size="sm" variant="outline">
<Star class={wishlist.isFavorite ? "fill-yellow-500 text-yellow-500" : ""} />
</Button>
</form>
{/if}
<!-- For owned wishlists, use regular favorite toggle -->
<form method="POST" action="?/toggleFavorite" use:enhance={() => {
return async ({ update }) => {
await update({ reset: false });
};
}}>
<input type="hidden" name="wishlistId" value={wishlist.id} />
<input type="hidden" name="isFavorite" value={wishlist.isFavorite} />
<Button type="submit" size="sm" variant="outline">
<Star class={wishlist.isFavorite ? "fill-yellow-500 text-yellow-500" : ""} />
</Button>
</form>
<Button
size="sm"
onclick={() => (window.location.href = `/wishlist/${wishlist.ownerToken}/edit`)}
@@ -199,23 +216,86 @@
>
{t.dashboard.copyLink}
</Button>
{#if wishlist.isClaimed && myWishlistsUnlocked}
<!-- Add unclaim button for claimed wishlists -->
<form method="POST" action="?/unsaveWishlist" use:enhance={() => {
</div>
</WishlistCard>
{/snippet}
</WishlistGrid>
{#if allClaimedWishlists().length > 0}
<WishlistGrid
title={t.dashboard.claimedWishlists}
description={t.dashboard.claimedWishlistsDescription}
items={sortedClaimedWishlists() || []}
emptyMessage={t.dashboard.emptyClaimedWishlists}
emptyDescription={t.dashboard.emptyClaimedWishlistsDescription}
>
{#snippet headerAction()}
<div class="flex flex-col sm:flex-row gap-2">
<UnlockButton bind:unlocked={claimedWishlistsUnlocked} />
</div>
{/snippet}
{#snippet searchBar()}
{#if allClaimedWishlists().length > 0}
<SearchBar bind:value={claimedWishlistsSearch} />
{/if}
{/snippet}
{#snippet children(wishlist)}
<WishlistCard
title={wishlist.title}
description={getWishlistDescription(wishlist)}
itemCount={wishlist.items?.length || 0}
color={wishlist.color}
>
<div class="flex gap-2 flex-wrap">
<!-- For claimed wishlists, use saved favorite toggle -->
<form method="POST" action="?/toggleSavedFavorite" use:enhance={() => {
return async ({ update }) => {
await update({ reset: false });
};
}}>
<input type="hidden" name="savedWishlistId" value={wishlist.savedId} />
<Button type="submit" size="sm" variant="destructive">
{t.dashboard.unsave}
<input type="hidden" name="isFavorite" value={wishlist.isFavorite} />
<Button type="submit" size="sm" variant="outline">
<Star class={wishlist.isFavorite ? "fill-yellow-500 text-yellow-500" : ""} />
</Button>
</form>
{/if}
</div>
</WishlistCard>
{/snippet}
</WishlistGrid>
<Button
size="sm"
onclick={() => (window.location.href = `/wishlist/${wishlist.ownerToken}/edit`)}
>
{t.dashboard.manage}
</Button>
<Button
size="sm"
variant="outline"
onclick={() => {
navigator.clipboard.writeText(
`${window.location.origin}/wishlist/${wishlist.publicToken}`
);
}}
>
{t.dashboard.copyLink}
</Button>
{#if claimedWishlistsUnlocked}
<!-- Add unclaim button for claimed wishlists -->
<form method="POST" action="?/unsaveWishlist" use:enhance={() => {
return async ({ update }) => {
await update({ reset: false });
};
}}>
<input type="hidden" name="savedWishlistId" value={wishlist.savedId} />
<Button type="submit" size="sm" variant="destructive">
{t.dashboard.unsave}
</Button>
</form>
{/if}
</div>
</WishlistCard>
{/snippet}
</WishlistGrid>
{/if}
<WishlistGrid
title={t.dashboard.savedWishlists}
@@ -23,6 +23,7 @@ export const load: PageServerLoad = async ({ params, locals }) => {
const session = await locals.auth();
let isSaved = false;
let isClaimed = false;
let savedWishlistId: string | null = null;
if (session?.user?.id) {
@@ -33,12 +34,14 @@ export const load: PageServerLoad = async ({ params, locals }) => {
)
});
isSaved = !!saved;
isClaimed = !!saved?.ownerToken; // User has claimed if ownerToken exists in savedWishlists
savedWishlistId = saved?.id || null;
}
return {
wishlist,
isSaved,
isClaimed,
savedWishlistId,
isAuthenticated: !!session?.user
};
+24 -46
View File
@@ -22,7 +22,6 @@
let { data }: { data: PageData } = $props();
let showSaveForm = $state(false);
let searchQuery = $state('');
const t = $derived(languageStore.t);
@@ -55,7 +54,17 @@
{/if}
</div>
{#if data.isAuthenticated}
{#if data.isSaved}
{#if data.isClaimed}
<!-- User has claimed this wishlist - show claimed status -->
<Button
variant="outline"
size="sm"
disabled
>
{t.wishlist.youClaimedThis}
</Button>
{:else if data.isSaved}
<!-- User has saved but not claimed - show unsave button -->
<form method="POST" action="?/unsaveWishlist" use:enhance>
<input
type="hidden"
@@ -63,17 +72,21 @@
value={data.savedWishlistId}
/>
<Button type="submit" variant="outline" size="sm">
Unsave
{t.wishlist.unsaveWishlist}
</Button>
</form>
{:else}
<Button
variant="outline"
size="sm"
onclick={() => (showSaveForm = !showSaveForm)}
>
{showSaveForm ? "Cancel" : "Save Wishlist"}
</Button>
<!-- Not saved - show save button -->
<form method="POST" action="?/saveWishlist" use:enhance={() => {
return async ({ update }) => {
await update({ reset: false });
};
}}>
<input type="hidden" name="wishlistId" value={data.wishlist.id} />
<Button type="submit" variant="outline" size="sm">
{t.wishlist.saveWishlist}
</Button>
</form>
{/if}
{:else}
<Button
@@ -81,48 +94,13 @@
size="sm"
onclick={() => (window.location.href = "/signin")}
>
Sign in to Save
{t.wishlist.signInToSave}
</Button>
{/if}
</div>
</CardContent>
</Card>
<!-- Save Confirmation -->
{#if showSaveForm && !data.isSaved}
<Card>
<CardHeader>
<CardTitle>Save This Wishlist</CardTitle>
<CardDescription
>Save this wishlist to easily find it later in your dashboard</CardDescription
>
</CardHeader>
<CardContent>
<form
method="POST"
action="?/saveWishlist"
use:enhance={() => {
return async ({ update }) => {
await update();
showSaveForm = false;
};
}}
class="space-y-4"
>
<input
type="hidden"
name="wishlistId"
value={data.wishlist.id}
/>
<div class="flex gap-2">
<Button type="submit">Save Wishlist</Button>
<Button type="button" variant="outline" onclick={() => showSaveForm = false}>Cancel</Button>
</div>
</form>
</CardContent>
</Card>
{/if}
<!-- Search Bar -->
{#if data.wishlist.items && data.wishlist.items.length > 0}
<SearchBar bind:value={searchQuery} />