frontend/src/lib/components/borne/items.svelte
<script lang="ts">
import type { Item, ItemState, MenuCategory, MenuItem } from '$lib/api';
import { api } from '$lib/config/config';
import { itemsApi } from '$lib/requests/requests';
import { formatPrice } from '$lib/utils';
import { onMount } from 'svelte';
import { fade, fly } from 'svelte/transition';
export let state: ItemState = 'buyable';
export let category: string = '';
export let click: (item: Item) => void;
// Update item when clicking on it
function clickWrapper(item: Item) {
reloadItems(false);
// Update current item
for (let i = 0; i < items.length; i++) {
if (items[i].id === item.id) {
item = items[i];
break;
}
}
click(item);
}
let items: Item[] = [];
let page: number = 0;
let maxPage: number = 0;
let nextPage = () => {
if (page < maxPage) {
page++;
reloadItems();
}
};
let prevPage = () => {
if (page > 0) {
page--;
reloadItems();
}
};
let limit: number = 12;
type MenuPopup = {
items: MenuItem[] | undefined;
categories: MenuCategory[] | undefined;
};
let menuPopup: MenuPopup | undefined;
onMount(() => {
reloadItems();
});
function reloadItems(anim: boolean = true) {
itemsApi()
.getCategoryItems(category, page, limit, state, { withCredentials: true })
.then((res) => {
maxPage = res.data.max_page ?? 0;
page = res.data.page ?? 0;
limit = res.data.limit ?? 15;
if (anim) {
let newItems = res.data.items ?? [];
items = [];
setTimeout(() => {
items = newItems;
}, 10);
} else {
items = res.data.items ?? [];
}
})
.catch((err) => {
console.log(err);
});
}
let direction = 1;
</script>
{#if menuPopup}
<!-- Show overlay -->
<button
class="absolute w-screen h-screen top-0 left-0 bg-black bg-opacity-50 flex flex-col items-center justify-center"
in:fade={{ duration: 200 }}
out:fade={{ duration: 200 }}
style="z-index: 100;"
on:click={() => {
menuPopup = undefined;
}}
/>
<div class="absolute w-full h-full top-0 left-0 flex justify-center items-center">
<div
class="flex flex-col items-center bg-white rounded-lg shadow-lg p-4"
style="z-index: 101;"
in:fade={{ duration: 200 }}
>
<div class="flex flex-col w-full justify-center">
<div class="text-xl font-bold self-center">Contenu :</div>
<div class="grid grid-cols-4 justify-between items-center w-full px-4 mt-4">
{#each menuPopup.categories ?? [] as cat}
<div class="flex flex-col justify-center">
<img
draggable="false"
class="w-32 h-32 object-contain self-center bg-gray-200"
src={api() + cat.picture_uri}
alt={cat.name}
/>
<span class="w-full text-lg font-bold text-center">{cat.amount} {cat.name}</span>
</div>
{/each}
{#each menuPopup.items ?? [] as item}
<div class="flex flex-col justify-center">
<img
draggable="false"
class="w-32 h-32 object-contain bg-gray-200"
src={api() + item.picture_uri}
alt={item.name}
/>
<span class="w-full text-lg font-bold text-center">{item.amount} {item.name}</span>
</div>
{/each}
</div>
</div>
</div>
</div>
{/if}
<!-- horizontal & overflows -->
{#if items.length === 0}
<div class="col-span-7 flex flex-col items-center justify-center">
<span class="text-3xl text-white" in:fade={{ duration: 200, delay: 100 }}>Aucun article</span>
</div>
{:else}
<div
class="grid grid-cols-4 gap-8 w-full p-16"
in:fly={{ x: -direction * 300, duration: 500 }}
out:fly={{ x: direction * 300, duration: 500 }}
>
{#each items as item}
<!-- image wil be in a button box -->
<button
class="relative w-50 h-50 flex-shrink-0 flex flex-col items-center justify-center rounded-lg text-white transition-colors duration-300"
>
<!-- add info svg on the top right -->
{#if item.is_menu}
<button
class="relative top-0 right-0 w-10 h-10"
on:click={() => {
menuPopup = {
items: item.menu_items,
categories: item.menu_categories
};
}}
>
<iconify-icon class="text-white align-middle text-2xl" icon="akar-icons:info" />
</button>
{/if}
<button
on:click={() => {
// check we are not clicking on the info button
clickWrapper(item);
}}
>
<img
draggable="false"
class="w-full h-32 object-contain"
src={api() + item.picture_uri}
alt={item.name}
/>
<div class="flex flex-col">
<span class="text-lg font-bold">{item.name}</span>
<span class="text-sm">Prix: {formatPrice(item.display_price ?? 999)}</span>
</div>
</button>
{#if item.amount_left <= 0}
<!-- Stock épuisé icon -->
<img
class="absolute top-[50%] left-[50%] -translate-x-[50%] -translate-y-[70%] w-28 h-28 drop-shadow-2xl"
alt="oof"
src="/epuise.webp"
/>
{/if}
</button>
{/each}
</div>
{/if}
<!-- Navigation -->
<div class="absolute bottom-5 left-[50%] -translate-x-[50%] flex flex-col justify-center">
<div class="text-3xl text-white text-center">
{page}/{maxPage}
</div>
<div class="flex flex-row gap-4 justify-center items-center w-full h-16">
<button
class="w-10 h-10 border-2 border-gray-300 rounded-full"
on:click={() => {
prevPage();
}}
>
<iconify-icon class="text-white align-middle text-2xl" icon="akar-icons:chevron-left" />
</button>
<button
class="w-10 h-10 text-center border-2 border-gray-300 rounded-full"
on:click={() => {
nextPage();
}}
>
<iconify-icon class="text-white align-middle text-2xl" icon="akar-icons:chevron-right" />
</button>
</div>
</div>