feat: add GiftCard and GiftsList components

This commit is contained in:
Norbert Maciaszek
2025-11-17 20:08:11 +01:00
parent 112df6f6ec
commit 9f0ced6e29
3 changed files with 114 additions and 0 deletions

View File

@@ -0,0 +1,48 @@
<script lang="ts">
import { formatCurrency } from '$lib/helpers/formatCurrency';
import Badge from '../atoms/Badge.svelte';
import Heading from '../atoms/Heading.svelte';
import { fly } from 'svelte/transition';
let gift: Gift = $props();
const bgByStatus = {
planned: 'bg-gray-600',
decided: 'bg-red-900',
bought: 'bg-yellow-800',
ready: 'bg-blue-900',
wrapped: 'bg-green-900'
};
</script>
<div
class={`my-6 rounded-2xl p-4 shadow-sm transition-all duration-200 ease-in-out ${bgByStatus[gift.status]}`}
in:fly={{ x: 100 }}
out:fly={{ x: -100 }}
>
<div class="flex flex-col gap-2">
<div class="flex flex-wrap items-center justify-between gap-2">
<div class="flex items-center gap-2">
<Heading size="small" spacing="none">
{gift.title}
</Heading>
</div>
{#if gift.link}
<div class="flex flex-wrap items-center gap-2">
{#each gift.link.split('\n') as line, index}
<a href={line} target="_blank" onclick={(e) => e.stopPropagation()}>
<Badge>Link {index + 1}</Badge>
</a>
{/each}
</div>
{/if}
</div>
<div class="flex flex-wrap items-center gap-2">
<span>Koszt: {formatCurrency(gift.cost)}</span>
</div>
{#if gift.description}
<p class="text-sm text-gray-200">{gift.description}</p>
{/if}
</div>
</div>

View File

@@ -0,0 +1,62 @@
<script lang="ts">
import { flip } from 'svelte/animate';
import GiftCard from '../molecules/GiftCard.svelte';
import { cubicInOut } from 'svelte/easing';
type Props = {
gifts: Gift[];
};
let { gifts }: Props = $props();
let filter = $state<Gift['status'] | 'all'>('all');
let sort = $state<keyof Pick<Gift, 'title' | 'cost' | 'person' | 'status'>>('title');
let sortedGifts = $derived.by(() => {
let newSortedGifts = [...gifts];
if (sort === 'cost') {
newSortedGifts.sort((a, b) => a.cost - b.cost);
} else if (sort === 'person') {
newSortedGifts.sort((a, b) => a.expand.person.name.localeCompare(b.expand.person.name));
} else if (sort === 'status') {
newSortedGifts.sort((a, b) => a.status.localeCompare(b.status));
}
return newSortedGifts;
});
let filteredGifts = $derived.by(() => {
if (filter === 'all') return sortedGifts;
return sortedGifts.filter((gift) => gift.status === filter);
});
let renderGifts = $derived(filteredGifts);
</script>
<section>
<div class="flex flex-row items-center justify-between gap-4">
<select
class="rounded-xl border border-gray-300 px-4 py-2 transition-all outline-none focus:border-transparent focus:ring-2 focus:ring-blue-500"
bind:value={filter}
>
<option value="all">Wszystkie</option>
<option value="planned">Planowane</option>
<option value="decided">Do kupienia</option>
<option value="bought">Kupione</option>
<option value="wrapped">Gotowe</option>
</select>
<select
class="rounded-xl border border-gray-300 px-4 py-2 transition-all outline-none focus:border-transparent focus:ring-2 focus:ring-blue-500"
bind:value={sort}
>
<option value="title">Tytuł</option>
<option value="cost">Koszt</option>
<option value="person">Osoba</option>
<option value="status">Status</option>
</select>
</div>
{#each renderGifts as gift (gift.id)}
<div animate:flip={{ duration: 400, easing: cubicInOut }}>
<GiftCard {...gift} />
</div>
{/each}
</section>

View File

@@ -1,6 +1,8 @@
<script>
import YearOverview from '$lib/components/organisms/YearOverview.svelte';
import YearNav from '$lib/components/molecules/YearNav.svelte';
import Heading from '$lib/components/atoms/Heading.svelte';
import GiftsList from '$lib/components/organisms/GiftsList.svelte';
let { data } = $props();
</script>
@@ -8,3 +10,5 @@
<h1>{data.year.year}</h1>
<YearOverview heading="Podsumowanie roku" data={data.year} />
<YearNav years={data.years} />
<Heading>Prezenty tego roku</Heading>
<GiftsList gifts={data.year.expand.gifts} />