diff options
| author | Zach Berwaldt <zberwaldt@tutamail.com> | 2024-03-21 11:23:42 -0400 |
|---|---|---|
| committer | Zach Berwaldt <zberwaldt@tutamail.com> | 2024-03-21 11:23:42 -0400 |
| commit | fd6f6f169f9ff9a1247228fb34dc9654a9584915 (patch) | |
| tree | 4241792d4b41bc025807cab001a18a2d355e7ea3 /fe/src/lib/forms | |
| parent | 655aeaac60c7dec09276f469904752c7dc58c8c5 (diff) | |
fix bugs, redo layout, reorg.
Diffstat (limited to 'fe/src/lib/forms')
| -rw-r--r-- | fe/src/lib/forms/AddForm.svelte | 25 | ||||
| -rw-r--r-- | fe/src/lib/forms/LoginForm.svelte | 86 | ||||
| -rw-r--r-- | fe/src/lib/forms/PreferencesForm.svelte | 119 | ||||
| -rw-r--r-- | fe/src/lib/forms/index.ts | 10 |
4 files changed, 222 insertions, 18 deletions
diff --git a/fe/src/lib/forms/AddForm.svelte b/fe/src/lib/forms/AddForm.svelte index f85cce6..bbc8356 100644 --- a/fe/src/lib/forms/AddForm.svelte +++ b/fe/src/lib/forms/AddForm.svelte | |||
| @@ -11,21 +11,10 @@ | |||
| 11 | const statistic: Statistic = newStatistic(); | 11 | const statistic: Statistic = newStatistic(); |
| 12 | 12 | ||
| 13 | function newStatistic(): Statistic { | 13 | function newStatistic(): Statistic { |
| 14 | let now = new Date(), | 14 | let date = new Date().toString(); |
| 15 | month, | ||
| 16 | day, | ||
| 17 | year; | ||
| 18 | |||
| 19 | month = `${now.getMonth() + 1}`; | ||
| 20 | day = `${now.getDate()}`; | ||
| 21 | year = now.getFullYear(); | ||
| 22 | if (month.length < 2) month = "0" + month; | ||
| 23 | if (day.length < 2) day = "0" + day; | ||
| 24 | |||
| 25 | const date = [year, month, day].join("-"); | ||
| 26 | 15 | ||
| 27 | return { | 16 | return { |
| 28 | user_id: $user!.uuid, | 17 | user_id: $user!.id, |
| 29 | date, | 18 | date, |
| 30 | quantity: 1 | 19 | quantity: 1 |
| 31 | }; | 20 | }; |
| @@ -37,7 +26,7 @@ | |||
| 37 | 26 | ||
| 38 | async function handleSubmitStat() | 27 | async function handleSubmitStat() |
| 39 | { | 28 | { |
| 40 | const { date, quantity } = statistic; | 29 | const { user_id, date, quantity } = statistic; |
| 41 | await fetch(apiURL("stats"), { | 30 | await fetch(apiURL("stats"), { |
| 42 | method: "POST", | 31 | method: "POST", |
| 43 | headers: { | 32 | headers: { |
| @@ -45,7 +34,7 @@ | |||
| 45 | }, | 34 | }, |
| 46 | body: JSON.stringify({ | 35 | body: JSON.stringify({ |
| 47 | date: new Date(date), | 36 | date: new Date(date), |
| 48 | user_id: 2, | 37 | user_id, |
| 49 | quantity | 38 | quantity |
| 50 | }) | 39 | }) |
| 51 | }); | 40 | }); |
| @@ -54,12 +43,12 @@ | |||
| 54 | 43 | ||
| 55 | </script> | 44 | </script> |
| 56 | 45 | ||
| 57 | <dialog {open} on:submit={handleSubmitStat}> | 46 | <dialog id="addForm" {open} on:submit={handleSubmitStat}> |
| 58 | <h2>Add Water</h2> | 47 | <h2>Add Water</h2> |
| 59 | <form method="dialog"> | 48 | <form method="dialog"> |
| 60 | <div class="form input group"> | 49 | <div class="form input group"> |
| 61 | <label for="date">Date:</label> | 50 | <label for="date">Date:</label> |
| 62 | <input bind:value={statistic.date} id="date" name="date" type="date" /> | 51 | <input bind:value={statistic.date} id="date" name="date" type="datetime-local" /> |
| 63 | </div> | 52 | </div> |
| 64 | <div class="form input group"> | 53 | <div class="form input group"> |
| 65 | <label for="quantity">Quantity:</label> | 54 | <label for="quantity">Quantity:</label> |
| @@ -75,4 +64,4 @@ | |||
| 75 | <button on:click={closeDialog}>Cancel</button> | 64 | <button on:click={closeDialog}>Cancel</button> |
| 76 | <button type="submit">Submit</button> | 65 | <button type="submit">Submit</button> |
| 77 | </form> | 66 | </form> |
| 78 | </dialog> \ No newline at end of file | 67 | </dialog> |
diff --git a/fe/src/lib/forms/LoginForm.svelte b/fe/src/lib/forms/LoginForm.svelte new file mode 100644 index 0000000..88d4479 --- /dev/null +++ b/fe/src/lib/forms/LoginForm.svelte | |||
| @@ -0,0 +1,86 @@ | |||
| 1 | <script lang="ts"> | ||
| 2 | import { token, user, preferences } from "../../stores/auth"; | ||
| 3 | import Card from "../Card.svelte"; | ||
| 4 | import { apiURL } from "../../utils"; | ||
| 5 | |||
| 6 | let credentials: CredentialObject = { | ||
| 7 | username: "", | ||
| 8 | password: "", | ||
| 9 | }; | ||
| 10 | |||
| 11 | let error: string | null = null; | ||
| 12 | |||
| 13 | interface CredentialObject { | ||
| 14 | username: string; | ||
| 15 | password: string; | ||
| 16 | } | ||
| 17 | |||
| 18 | function prepareCredentials({ | ||
| 19 | username, | ||
| 20 | password, | ||
| 21 | }: CredentialObject): string { | ||
| 22 | return btoa(`${username}:${password}`); | ||
| 23 | } | ||
| 24 | |||
| 25 | async function onSubmit(e: Event) { | ||
| 26 | if (!credentials.username || !credentials.password) { | ||
| 27 | error = "please enter your username and password"; | ||
| 28 | return; | ||
| 29 | } | ||
| 30 | const auth = prepareCredentials(credentials); | ||
| 31 | |||
| 32 | const response = await fetch(apiURL("auth"), { | ||
| 33 | method: "POST", | ||
| 34 | headers: { | ||
| 35 | Authorization: `Basic ${auth}`, | ||
| 36 | }, | ||
| 37 | }); | ||
| 38 | |||
| 39 | if (response.status === 401) { | ||
| 40 | error = "Your username or password is wrong"; | ||
| 41 | return; | ||
| 42 | } | ||
| 43 | |||
| 44 | if (response.ok) { | ||
| 45 | const { | ||
| 46 | token: apiToken, | ||
| 47 | user: userData, | ||
| 48 | preferences: userPreferences, | ||
| 49 | } = await response.json(); | ||
| 50 | user.setUser(userData); | ||
| 51 | preferences.setPreference(userPreferences); | ||
| 52 | token.authenticate(apiToken); | ||
| 53 | } | ||
| 54 | |||
| 55 | error = null; | ||
| 56 | } | ||
| 57 | </script> | ||
| 58 | |||
| 59 | <Card> | ||
| 60 | <form class="form" on:submit|preventDefault={onSubmit}> | ||
| 61 | <div class="form input group"> | ||
| 62 | <label for="username">Username</label> | ||
| 63 | <input | ||
| 64 | bind:value={credentials.username} | ||
| 65 | id="username" | ||
| 66 | name="username" | ||
| 67 | type="text" | ||
| 68 | autocomplete="username" | ||
| 69 | /> | ||
| 70 | </div> | ||
| 71 | <div class="form input group"> | ||
| 72 | <label for="password">Password</label> | ||
| 73 | <input | ||
| 74 | bind:value={credentials.password} | ||
| 75 | id="password" | ||
| 76 | name="password" | ||
| 77 | type="password" | ||
| 78 | autocomplete="current-password" | ||
| 79 | /> | ||
| 80 | </div> | ||
| 81 | {#if error} | ||
| 82 | <p class="error">{error}</p> | ||
| 83 | {/if} | ||
| 84 | <button type="submit">Log in</button> | ||
| 85 | </form> | ||
| 86 | </Card> | ||
diff --git a/fe/src/lib/forms/PreferencesForm.svelte b/fe/src/lib/forms/PreferencesForm.svelte new file mode 100644 index 0000000..79663d1 --- /dev/null +++ b/fe/src/lib/forms/PreferencesForm.svelte | |||
| @@ -0,0 +1,119 @@ | |||
| 1 | <script lang="ts"> | ||
| 2 | import { user, preferences, token } from "../../stores/auth"; | ||
| 3 | import { createEventDispatcher, onDestroy, onMount } from "svelte"; | ||
| 4 | import type { User } from "../../types"; | ||
| 5 | import { apiURL } from "../../utils"; | ||
| 6 | |||
| 7 | export let open: boolean; | ||
| 8 | |||
| 9 | let sizes: Array<any>; | ||
| 10 | let selectedSize: number = 1; | ||
| 11 | let color: string = "#000000"; | ||
| 12 | |||
| 13 | const dispatch = createEventDispatcher(); | ||
| 14 | |||
| 15 | const unsubscribe = preferences.subscribe( | ||
| 16 | (value: any) => { | ||
| 17 | if (value) { | ||
| 18 | color = value.color; | ||
| 19 | selectedSize = value.size_id; | ||
| 20 | } | ||
| 21 | }, | ||
| 22 | ); | ||
| 23 | |||
| 24 | function closeDialog() { | ||
| 25 | dispatch("close"); | ||
| 26 | } | ||
| 27 | |||
| 28 | async function updateUserPreferences() { | ||
| 29 | const res = await fetch(apiURL("user/preferences"), { | ||
| 30 | method: "PATCH", | ||
| 31 | headers: { | ||
| 32 | Authorization: `Bearer ${$token}`, | ||
| 33 | }, | ||
| 34 | body: JSON.stringify($preferences), | ||
| 35 | }); | ||
| 36 | } | ||
| 37 | |||
| 38 | async function getUserPreferences() { | ||
| 39 | const res = await fetch(apiURL(`user/${($user as User)!.id}/preferences`), | ||
| 40 | { | ||
| 41 | method: "GET", | ||
| 42 | headers: { | ||
| 43 | Authorization: `Bearer ${$token}`, | ||
| 44 | }, | ||
| 45 | }, | ||
| 46 | ); | ||
| 47 | const updatePreferences = await res.json(); | ||
| 48 | preferences.set(updatePreferences); | ||
| 49 | } | ||
| 50 | |||
| 51 | async function onPreferencesSave(): Promise<void> { | ||
| 52 | preferences.update((value) => ({ | ||
| 53 | ...value!, | ||
| 54 | size_id: selectedSize, | ||
| 55 | color: color, | ||
| 56 | })); | ||
| 57 | |||
| 58 | await updateUserPreferences(); | ||
| 59 | await getUserPreferences(); | ||
| 60 | |||
| 61 | dispatch("close"); | ||
| 62 | } | ||
| 63 | |||
| 64 | onMount(() => { | ||
| 65 | fetch(apiURL("sizes"), { | ||
| 66 | method: "GET", | ||
| 67 | headers: { | ||
| 68 | Authorization: `Bearer ${$token}`, | ||
| 69 | }, | ||
| 70 | }) | ||
| 71 | .then((res) => res.json()) | ||
| 72 | .then((val) => (sizes = val)); | ||
| 73 | }); | ||
| 74 | |||
| 75 | onDestroy(() => { | ||
| 76 | unsubscribe(); | ||
| 77 | }); | ||
| 78 | </script> | ||
| 79 | |||
| 80 | <dialog {open} on:submit|preventDefault={onPreferencesSave}> | ||
| 81 | <h2>User Preferences</h2> | ||
| 82 | <form method="dialog"> | ||
| 83 | <div class="form input group"> | ||
| 84 | <label for="color">Color</label> | ||
| 85 | <input | ||
| 86 | id="color" | ||
| 87 | name="color" | ||
| 88 | type="color" | ||
| 89 | bind:value={color} | ||
| 90 | /> | ||
| 91 | </div> | ||
| 92 | <div class="form input group"> | ||
| 93 | <label for="size">Bottle Size</label> | ||
| 94 | <select | ||
| 95 | bind:value={selectedSize} | ||
| 96 | > | ||
| 97 | {#if sizes} | ||
| 98 | {#each sizes as size} | ||
| 99 | <option value={size.id}>{size.size} {size.unit}</option> | ||
| 100 | {/each} | ||
| 101 | {/if} | ||
| 102 | </select> | ||
| 103 | </div> | ||
| 104 | <button on:click={closeDialog}>Cancel</button> | ||
| 105 | <button type="submit">Save</button> | ||
| 106 | </form> | ||
| 107 | </dialog> | ||
| 108 | |||
| 109 | <style> | ||
| 110 | /* dialog { | ||
| 111 | background: white; | ||
| 112 | color: black; | ||
| 113 | } | ||
| 114 | |||
| 115 | input[type="color"] { | ||
| 116 | width: 4em; | ||
| 117 | height: 4em; | ||
| 118 | } */ | ||
| 119 | </style> | ||
diff --git a/fe/src/lib/forms/index.ts b/fe/src/lib/forms/index.ts new file mode 100644 index 0000000..ac4e63b --- /dev/null +++ b/fe/src/lib/forms/index.ts | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | import AddForm from "./AddForm.svelte"; | ||
| 2 | import LoginForm from "./LoginForm.svelte"; | ||
| 3 | import PreferencesForm from "./PreferencesForm.svelte"; | ||
| 4 | |||
| 5 | |||
| 6 | export { | ||
| 7 | AddForm, | ||
| 8 | LoginForm, | ||
| 9 | PreferencesForm | ||
| 10 | }; \ No newline at end of file | ||
