diff options
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 | ||