diff options
| author | Adam Soltys <[email protected]> | 2024-02-20 19:53:49 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2024-02-20 19:53:49 -0800 |
| commit | fbf3cef47c612b9c334917f10c810c797fc77da6 (patch) | |
| tree | f10d545fae7305e624c91f5333f513d5dc95d413 /src | |
| parent | 3fbd2514e38d29ff02cd7765d89476bed6530d37 (diff) | |
| download | coinos-ui-fbf3cef47c612b9c334917f10c810c797fc77da6.tar.gz coinos-ui-fbf3cef47c612b9c334917f10c810c797fc77da6.zip | |
items
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/DeleteItem.svelte | 47 | ||||
| -rw-r--r-- | src/components/ItemForm.svelte | 86 | ||||
| -rw-r--r-- | src/lib/upload.js | 3 | ||||
| -rw-r--r-- | src/locales/en.json | 2 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/(profile)/+page.server.js | 11 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/(profile)/+page.svelte | 141 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/items/[id]/+page.server.js | 24 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/items/[id]/+page.svelte | 9 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/items/create/+page.server.js | 17 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/items/create/+page.svelte | 4 |
10 files changed, 234 insertions, 110 deletions
diff --git a/src/components/DeleteItem.svelte b/src/components/DeleteItem.svelte new file mode 100644 index 00000000..8f7f97ca --- /dev/null +++ b/src/components/DeleteItem.svelte @@ -0,0 +1,47 @@ +<script> + import Icon from "$comp/Icon.svelte"; + import { fail, post } from "$lib/utils"; + import { invalidate } from "$app/navigation"; + + export let item; + + let cancel = () => (item = null); + + let submit = async () => { + try { + await post("/api/items/delete", { item }); + invalidate("app:items"); + item = null; + } catch (e) { + fail("Failed to delete item"); + } + }; +</script> + +<div + class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-20" +> + <div + class="relative top-1/3 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white space-y-5" + > + <h1 class="text-center text-2xl font-semibold">Delete item</h1> + <form on:submit|preventDefault={submit}> + <div class="w-full flex"> + <button + type="button" + class="border-2 border-black rounded-xl font-semibold mx-auto py-3 w-40 hover:opacity-80 mx-auto" + on:click={cancel} + on:keydown={cancel} + > + <div class="my-auto">No</div> + </button> + <button + type="submit" + class="border-2 border-black rounded-xl font-semibold mx-auto py-3 w-40 hover:opacity-80 mx-auto bg-black text-white" + > + <div class="my-auto">Yes</div> + </button> + </div> + </form> + </div> +</div> diff --git a/src/components/ItemForm.svelte b/src/components/ItemForm.svelte index 044020b7..71114c29 100644 --- a/src/components/ItemForm.svelte +++ b/src/components/ItemForm.svelte @@ -1,9 +1,12 @@ <script> import { t } from "$lib/translations"; import { upload } from "$lib/upload"; - export let item, token; + import { fail } from "$lib/utils"; + import { applyAction, deserialize } from "$app/forms"; + import Spinner from "$comp/Spinner.svelte"; + export let item; - let fileInput, formElement, file; + let fileInput, formElement, file, submitting, progress; let select = () => fileInput.click(); let src; @@ -30,14 +33,12 @@ if (src) { try { - let { hash } = JSON.parse( - await upload(file, "item", progress, token) - ); + let { hash } = JSON.parse(await upload(file, "item", progress)); data.set("image", hash); await fetch(`/api/public/${hash}.webp`, { - cache: "reload", + cache: "reload", mode: "no-cors", }); } catch (e) { @@ -68,41 +69,66 @@ on:submit|preventDefault={handleSubmit} bind:this={formElement} > + <input type="hidden" name="id" bind:value={item.id} /> + <input type="hidden" name="image" bind:value={item.image} /> + <div> <label for="name" class="font-bold mb-1 block">{$t("items.name")}</label> <input name="name" bind:value={item.name} /> </div> + <div> + <label for="price" class="font-bold mb-1 block">{$t("items.price")}</label> + <input name="price" bind:value={item.price} /> + </div> + + <div class="w-full"> <label for="img" class="font-bold mb-1 block">{$t("items.image")}</label> - <div class="relative w-full h-64"> - {#if item.hash} - <img - src={`/api/public/${item.hash}.webp`} - alt={item.name} - class="mx-auto object-cover h-full w-full" - /> - {:else if src} - <img {src} alt={item.name} class="mx-auto object-cover h-full w-full" /> - {:else} - <div - class="bg-gradient-to-r from-primary to-gradient w-full h-48 mb-4 cursor-pointer hover:opacity-80" - on:click={select} - on:keydown={select} - alt="Banner" - /> - {/if} - <input - type="file" - class="hidden" - bind:this={fileInput} - on:change={(e) => handleFile(e, "item")} - /> + + <div class="grid grid-cols-2 gap-4"> + <div class="h-64 rounded-2xl overflow-hidden"> + {#if src} + <img + {src} + alt={item.name} + class="object-cover w-full h-full" + on:click={select} + on:keydown={select} + /> + {:else if item.image} + <img + src={`/api/public/${item.image}.webp`} + alt={item.name} + class="object-cover w-full h-full" + on:click={select} + on:keydown={select} + /> + {:else} + <div + class="bg-gradient-to-r from-primary to-gradient mb-4 cursor-pointer hover:opacity-80 w-full h-full" + on:click={select} + on:keydown={select} + alt="Banner" + /> + {/if} + </div> </div> + + <input + type="file" + class="hidden" + bind:this={fileInput} + on:change={(e) => handleFile(e, "item")} + /> </div> <button class="rounded-full py-5 px-6 font-bold hover:opacity-80 flex bg-black text-white text-2xl" > - {$t("items.submit")} + {#if submitting} + <Spinner /> + {:else} + {$t("items.submit")} + {/if} </button> </form> diff --git a/src/lib/upload.js b/src/lib/upload.js index 552e4a7a..31c235c6 100644 --- a/src/lib/upload.js +++ b/src/lib/upload.js @@ -1,4 +1,4 @@ -export const upload = async (file, type, progress, token) => { +export const upload = async (file, type, progress) => { let url = `/api/upload/${type}`; let formData = new FormData(); formData.append("file", file); @@ -13,7 +13,6 @@ export const upload = async (file, type, progress, token) => { }); ajax.upload.addEventListener("progress", progress, false); ajax.open("POST", url); - // ajax.setRequestHeader('Authorization', `Bearer ${token}`); ajax.send(formData); }); }; diff --git a/src/locales/en.json b/src/locales/en.json index 24433202..1ad0c931 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -353,7 +353,9 @@ }, "items": { "add": "Add item", + "edit": "Edit item", "name": "Name", + "price": "Price", "submit": "Submit", "image": "Image" } diff --git a/src/routes/(app)/[username]/(profile)/+page.server.js b/src/routes/(app)/[username]/(profile)/+page.server.js new file mode 100644 index 00000000..59eb44d5 --- /dev/null +++ b/src/routes/(app)/[username]/(profile)/+page.server.js @@ -0,0 +1,11 @@ +import { auth, get } from "$lib/utils"; + +export async function load({ cookies, depends, parent }) { + depends("app:items"); + + let { subject } = await parent(); + let items = await get(`/${subject.id}/items`, auth(cookies)); + items.map((i) => (i.quantity = 0)); + + return { items }; +} diff --git a/src/routes/(app)/[username]/(profile)/+page.svelte b/src/routes/(app)/[username]/(profile)/+page.svelte index bb98ddfe..93b7355c 100644 --- a/src/routes/(app)/[username]/(profile)/+page.svelte +++ b/src/routes/(app)/[username]/(profile)/+page.svelte @@ -1,50 +1,27 @@ <script> - import { btc, f } from "$lib/utils"; + import { btc, f, sat } from "$lib/utils"; import { onMount } from "svelte"; import Icon from "$comp/Icon.svelte"; import Balance from "$comp/Balance.svelte"; + import DeleteItem from "$comp/DeleteItem.svelte"; import { t } from "$lib/translations"; export let data; - let { subject, user, rates } = data; + let { subject, user, items, rates } = data; $: refresh(data); - let refresh = (d) => ({ subject, user, rates } = d); - - let items = [ - { - id: 1, - name: "Full Pour", - price: 9, - quantity: 0, - hash: "fa3a9d22eb24b33ae2dee8d7566c1ce25d1c76e18b46d54437621d3e34a3fbc9", - }, - { - id: 2, - name: "Half Pour", - price: 6, - quantity: 0, - hash: "a79c7b921a0dde39076201ae5f24505d5faa5f12f0cfd3b9d86cbc9c77ab0bd1", - }, - { - id: 3, - name: "Taster", - price: 3, - quantity: 0, - hash: "9d8fe68d068f7271cf08d4cb9b033966984549c1bcd827f7516e66e5b537f502", - }, - { - id: 4, - name: "4-packs", - price: 18, - quantity: 0, - hash: "13cf773ae51607e670c2a7c640aa5a1dc8b288554026de08370ed3da44e59493", - }, - ]; + let refresh = (d) => ({ items, subject, user, rates } = d); + let deleting; $: total = items.reduce((a, b) => a + b.price * b.quantity, 0); - let del = () => {}; + let del = (item) => { + deleting = item; + }; </script> +{#if deleting} + <DeleteItem bind:item={deleting} /> +{/if} + <div class="space-y-5"> {#if user?.id === subject.id} <div class="flex justify-center lg:justify-start"> @@ -86,6 +63,22 @@ </div> {/if} + {#if user?.username === subject.username} + <div> + <a href={`/${user.username}/items/create`} class="w-full"> + <button + class="rounded-2xl border py-5 px-6 font-bold hover:opacity-80 flex w-full bg-black text-white" + > + <div class="mx-auto flex"> + <div class="my-auto text-2xl"> + {$t("items.add")} + </div> + </div> + </button> + </a> + </div> + {/if} + <div class="grid grid-cols-2 gap-4"> {#each items as i} <div @@ -93,42 +86,65 @@ on:click={() => i.quantity++} > <div class="relative w-full h-64 overflow-hidden rounded-xl"> - <img - src={`/api/public/${i.hash}.webp`} - alt={i.name} - class="mx-auto object-cover h-full w-full" - /> + {#if i.image} + <img + src={`/api/public/${i.image}.webp`} + alt={i.name} + class="mx-auto object-cover h-full w-full" + /> + {:else} + <div + class="bg-gradient-to-r from-primary to-gradient mb-4 cursor-pointer hover:opacity-80 w-full h-full" + on:click={select} + on:keydown={select} + alt="Banner" + /> + {/if} {#if user?.username === subject.username} <div class="flex gap-2 justify-center absolute top-2 right-2"> <div on:click|stopPropagation={() => i.quantity > 0 && i.quantity--} > <a href={`/${user.username}/items/${i.id}`}> - <button class="bg-white rounded-xl"> - <Icon icon="edit" style="w-10" /> + <button + class="bg-black rounded-full w-12 h-12 bg-opacity-40 hover:bg-opacity-100" + > + <Icon icon="edit" style="w-8 mx-auto invert" /> </button> </a> </div> - <button class="bg-white rounded-xl"> - <Icon icon="trash" style="w-10" on:click={() => del(i)} /> + <button + class="bg-black rounded-full w-12 h-12 bg-opacity-40 hover:bg-opacity-100" + on:click|stopPropagation={() => del(i)} + > + <Icon icon="trash" style="w-8 mx-auto invert" /> </button> </div> {/if} </div> - <div class="bg-white rounded-xl p-2"> - <div class="text-2xl"> - {i.name} — {f(i.price, subject.currency)} - </div> - <div class="flex gap-2 justify-center"> - <div - on:click|stopPropagation={() => i.quantity > 0 && i.quantity--} - > - <Icon icon="minus" style="w-10" /> + <div class="bg-white rounded-xl p-2"> + <div class="flex"> + <div class="text-2xl mr-auto">{i.name}</div> + <div class="flex gap-2 my-auto"> + <div class="font-semibold"> + {f(i.price, subject.currency)} + </div> + <div class="text-secondary"> + {sat(btc(i.price, rates[subject.currency]))} </div> - <div class="text-2xl my-auto">{i.quantity}</div> - <Icon icon="plus" style="w-10" /> </div> </div> + <div class="flex gap-2"> + <div + class="ml-auto" + on:click|stopPropagation={() => i.quantity > 0 && i.quantity--} + > + <Icon icon="minus" style="w-10" /> + </div> + <div class="text-2xl my-auto">{i.quantity}</div> + <Icon icon="plus" style="w-10" /> + </div> + </div> </div> {/each} </div> @@ -151,19 +167,4 @@ </a> </div> {/if} - {#if user?.username === subject.username} - <div> - <a href={`/${user.username}/items/create`} class="w-full"> - <button - class="rounded-2xl border py-5 px-6 font-bold hover:opacity-80 flex w-full bg-black text-white" - > - <div class="mx-auto flex"> - <div class="my-auto text-2xl"> - {$t("items.add")} - </div> - </div> - </button> - </a> - </div> - {/if} </div> diff --git a/src/routes/(app)/[username]/items/[id]/+page.server.js b/src/routes/(app)/[username]/items/[id]/+page.server.js index e1fe2efd..960f9af4 100644 --- a/src/routes/(app)/[username]/items/[id]/+page.server.js +++ b/src/routes/(app)/[username]/items/[id]/+page.server.js @@ -1,6 +1,22 @@ -import { get } from "$lib/utils"; -export let load = async ({ cookies, params: { id } }) => { - let item = await get(`/item/${id}`); - console.log("item", item) +import { fd, auth, get, post } from "$lib/utils"; +import { fail, redirect } from "@sveltejs/kit"; + + export let load = async ({ cookies, params: { id } }) => { + let item = await get(`/items/${id}`); return { item }; }; + +export const actions = { + default: async ({ cookies, request, url }) => { + let form = await fd(request); + try { + await post(`/items`, form, auth(cookies)); + } catch (e) { + console.log(e); + return fail(400, { message: e.message }); + } + + let success = `/${url.pathname.split("/")[1]}`; + redirect(307, success); + }, +}; diff --git a/src/routes/(app)/[username]/items/[id]/+page.svelte b/src/routes/(app)/[username]/items/[id]/+page.svelte index 5a19e892..ddf4cf1d 100644 --- a/src/routes/(app)/[username]/items/[id]/+page.svelte +++ b/src/routes/(app)/[username]/items/[id]/+page.svelte @@ -1,7 +1,14 @@ <script> + import { t } from "$lib/translations"; import ItemForm from "$comp/ItemForm.svelte"; export let data; let { item } = data; + console.log(item) </script> -<ItemForm {item} /> +<div class="container max-w-xl mx-auto"> + <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold mb-10"> + {$t("items.edit")} + </h1> + <ItemForm {item} /> +</div> diff --git a/src/routes/(app)/[username]/items/create/+page.server.js b/src/routes/(app)/[username]/items/create/+page.server.js new file mode 100644 index 00000000..ea0571e7 --- /dev/null +++ b/src/routes/(app)/[username]/items/create/+page.server.js @@ -0,0 +1,17 @@ +import { fail, redirect } from "@sveltejs/kit"; +import { fd, auth, get, post } from "$lib/utils"; + +export const actions = { + default: async ({ cookies, request, url }) => { + let form = await fd(request); + try { + await post("/items", form, auth(cookies)); + } catch (e) { + console.log(e); + return fail(400, { message: e.message }); + } + + let success = `/${url.pathname.split('/')[1]}` + redirect(307, success); + }, +}; diff --git a/src/routes/(app)/[username]/items/create/+page.svelte b/src/routes/(app)/[username]/items/create/+page.svelte index c9aeae1d..46ee8e0c 100644 --- a/src/routes/(app)/[username]/items/create/+page.svelte +++ b/src/routes/(app)/[username]/items/create/+page.svelte @@ -1,8 +1,6 @@ <script> import { t } from "$lib/translations"; import ItemForm from "$comp/ItemForm.svelte"; - export let data; - let { token } = data; let item = {}; </script> @@ -10,5 +8,5 @@ <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold mb-10"> {$t("items.add")} </h1> - <ItemForm {item} {token} /> + <ItemForm {item} /> </div> |
