diff options
author | Zach Berwaldt <zberwaldt@tutamail.com> | 2024-03-15 18:49:43 -0400 |
---|---|---|
committer | Zach Berwaldt <zberwaldt@tutamail.com> | 2024-03-15 18:49:43 -0400 |
commit | 9cae9c1d2a0b4f7fa72f3075541b9ffafe1a7275 (patch) | |
tree | 960fa4f96a1328861a06d97180da8601af6855da /fe/src/lib | |
parent | 8fab2d03bce82e4dee798ebffb1e93c557f62a4b (diff) |
Add routes for preference, clean up and add types
Diffstat (limited to 'fe/src/lib')
-rw-r--r-- | fe/src/lib/Card.svelte | 1 | ||||
-rw-r--r-- | fe/src/lib/Chart.svelte | 63 | ||||
-rw-r--r-- | fe/src/lib/DataView.svelte | 40 | ||||
-rw-r--r-- | fe/src/lib/Layout.svelte | 8 | ||||
-rw-r--r-- | fe/src/lib/LoginForm.svelte | 2 | ||||
-rw-r--r-- | fe/src/lib/PreferencesForm.svelte | 145 | ||||
-rw-r--r-- | fe/src/lib/errors.ts | 6 | ||||
-rw-r--r-- | fe/src/lib/utils.ts | 2 |
8 files changed, 193 insertions, 74 deletions
diff --git a/fe/src/lib/Card.svelte b/fe/src/lib/Card.svelte index d7cd900..cd1e02c 100644 --- a/fe/src/lib/Card.svelte +++ b/fe/src/lib/Card.svelte | |||
@@ -1,6 +1,5 @@ | |||
1 | <script lang="ts"> | 1 | <script lang="ts"> |
2 | export let title = ""; | 2 | export let title = ""; |
3 | export let height: number | undefined = undefined; | ||
4 | </script> | 3 | </script> |
5 | 4 | ||
6 | <div class="card"> | 5 | <div class="card"> |
diff --git a/fe/src/lib/Chart.svelte b/fe/src/lib/Chart.svelte new file mode 100644 index 0000000..b19d932 --- /dev/null +++ b/fe/src/lib/Chart.svelte | |||
@@ -0,0 +1,63 @@ | |||
1 | <script lang="ts"> | ||
2 | import { onDestroy } from "svelte"; | ||
3 | import ChartJS from "chart.js/auto"; | ||
4 | |||
5 | export let data; | ||
6 | export let labels; | ||
7 | export let type = 'bar'; | ||
8 | |||
9 | let ref: HTMLCanvasElement; | ||
10 | let chart | ||
11 | |||
12 | function setupChart(result) { | ||
13 | [labels, data] = result; | ||
14 | chart = new ChartJS(ref, { | ||
15 | type, | ||
16 | data: { | ||
17 | labels, | ||
18 | datasets: [ | ||
19 | { | ||
20 | label: "Totals", | ||
21 | data, | ||
22 | backgroundColor: "rgba(255, 192, 192, 0.2)" | ||
23 | } | ||
24 | ] | ||
25 | }, | ||
26 | options: { | ||
27 | responsive: true, | ||
28 | maintainAspectRatio: false, | ||
29 | scales: { | ||
30 | y: { | ||
31 | suggestedMax: 30, | ||
32 | beginAtZero: true, | ||
33 | ticks: { | ||
34 | autoSkip: true, | ||
35 | stepSize: 5 | ||
36 | } | ||
37 | } | ||
38 | }, | ||
39 | plugins: { | ||
40 | legend: { | ||
41 | display: false | ||
42 | }, | ||
43 | title: { | ||
44 | display: true, | ||
45 | text: "Weekly Breakdown" | ||
46 | }, | ||
47 | subtitle: { | ||
48 | display: true, | ||
49 | text: "Water consumption over the last week", | ||
50 | padding: {bottom: 10} | ||
51 | } | ||
52 | } | ||
53 | } | ||
54 | }); | ||
55 | |||
56 | onDestroy(() => { | ||
57 | if (chart) chart.destroy(); | ||
58 | chart = null; | ||
59 | }) | ||
60 | } | ||
61 | </script> | ||
62 | |||
63 | <canvas bind:this={ref} /> \ No newline at end of file | ||
diff --git a/fe/src/lib/DataView.svelte b/fe/src/lib/DataView.svelte index 0a6b81b..5e81a5a 100644 --- a/fe/src/lib/DataView.svelte +++ b/fe/src/lib/DataView.svelte | |||
@@ -1,10 +1,11 @@ | |||
1 | <script lang="ts"> | 1 | <script lang="ts"> |
2 | import { onDestroy, onMount } from "svelte"; | 2 | import { onDestroy, onMount } from "svelte"; |
3 | import HttpClient from "../http"; | 3 | import http from "../http"; |
4 | import { token } from "../stores/auth"; | 4 | import { token } from "../stores/auth"; |
5 | import { addFormOpen } from "../stores/forms"; | 5 | import { addFormOpen } from "../stores/forms"; |
6 | import Table from "./Table.svelte"; | 6 | import Table from "./Table.svelte"; |
7 | import Chart from "chart.js/auto"; | 7 | import ChartJS from "chart.js/auto"; |
8 | import Chart from './Chart.svelte' | ||
8 | import Card from "./Card.svelte"; | 9 | import Card from "./Card.svelte"; |
9 | import Column from "./Column.svelte"; | 10 | import Column from "./Column.svelte"; |
10 | import AddForm from "./forms/AddForm.svelte"; | 11 | import AddForm from "./forms/AddForm.svelte"; |
@@ -46,8 +47,8 @@ | |||
46 | 47 | ||
47 | if (res.ok) { | 48 | if (res.ok) { |
48 | const json = await res.json(); | 49 | const json = await res.json(); |
49 | let labels = json.map(d => d.name); | 50 | let labels = json.map((d: any) => d.name); |
50 | let data = json.map(d => d.total); | 51 | let data = json.map((d: any) => d.total); |
51 | return [labels, data]; | 52 | return [labels, data]; |
52 | } else { | 53 | } else { |
53 | throw new Error("There was a problem with your request"); | 54 | throw new Error("There was a problem with your request"); |
@@ -65,8 +66,8 @@ | |||
65 | 66 | ||
66 | if (res.ok) { | 67 | if (res.ok) { |
67 | const json = await res.json(); | 68 | const json = await res.json(); |
68 | let labels = json.map(d => d.date); | 69 | let labels = json.map((d: any) => d.date); |
69 | let data = json.map(d => d.total); | 70 | let data = json.map((d: any) => d.total); |
70 | return [labels, data]; | 71 | return [labels, data]; |
71 | } else { | 72 | } else { |
72 | throw new Error("There was a problem with your request"); | 73 | throw new Error("There was a problem with your request"); |
@@ -84,9 +85,9 @@ | |||
84 | fetchDailyUserStatistics().then(updateDailyUserTotalsChart).catch(err => console.error(err)); | 85 | fetchDailyUserStatistics().then(updateDailyUserTotalsChart).catch(err => console.error(err)); |
85 | } | 86 | } |
86 | 87 | ||
87 | function setupWeeklyTotalsChart(result) { | 88 | function setupWeeklyTotalsChart(result: any) { |
88 | [lastSevenDays, lastSevenDaysData] = result; | 89 | [lastSevenDays, lastSevenDaysData] = result; |
89 | lineChart = new Chart(lineCanvasRef, { | 90 | lineChart = new ChartJS(lineCanvasRef, { |
90 | type: "line", | 91 | type: "line", |
91 | data: { | 92 | data: { |
92 | labels: lastSevenDays, | 93 | labels: lastSevenDays, |
@@ -129,10 +130,10 @@ | |||
129 | }); | 130 | }); |
130 | } | 131 | } |
131 | 132 | ||
132 | function setupDailyUserTotalsChart(result) { | 133 | function setupDailyUserTotalsChart(result: any) { |
133 | [userTotalsLabels, userTotalsData] = result; | 134 | [userTotalsLabels, userTotalsData] = result; |
134 | 135 | ||
135 | barChart = new Chart(barCanvasRef, { | 136 | barChart = new ChartJS(barCanvasRef, { |
136 | type: "bar", | 137 | type: "bar", |
137 | data: { | 138 | data: { |
138 | labels: userTotalsLabels, | 139 | labels: userTotalsLabels, |
@@ -177,13 +178,13 @@ | |||
177 | }); | 178 | }); |
178 | } | 179 | } |
179 | 180 | ||
180 | function updateWeeklyTotalsChart(result) { | 181 | function updateWeeklyTotalsChart(result: any) { |
181 | [, lastSevenDaysData] = result; | 182 | [, lastSevenDaysData] = result; |
182 | lineChart.data.datasets[0].data = lastSevenDaysData; | 183 | lineChart.data.datasets[0].data = lastSevenDaysData; |
183 | lineChart.update(); | 184 | lineChart.update(); |
184 | } | 185 | } |
185 | 186 | ||
186 | function updateDailyUserTotalsChart(result) { | 187 | function updateDailyUserTotalsChart(result: any) { |
187 | [, userTotalsData] = result; | 188 | [, userTotalsData] = result; |
188 | barChart.data.datasets[0].data = userTotalsData; | 189 | barChart.data.datasets[0].data = userTotalsData; |
189 | barChart.update(); | 190 | barChart.update(); |
@@ -205,6 +206,7 @@ | |||
205 | 206 | ||
206 | <Column --width="500px"> | 207 | <Column --width="500px"> |
207 | <Card --height="300px"> | 208 | <Card --height="300px"> |
209 | <!--<Chart />--> | ||
208 | <canvas bind:this={barCanvasRef} /> | 210 | <canvas bind:this={barCanvasRef} /> |
209 | </Card> | 211 | </Card> |
210 | <Card --height="300px"> | 212 | <Card --height="300px"> |
@@ -223,17 +225,3 @@ | |||
223 | </Card> | 225 | </Card> |
224 | </Column> | 226 | </Column> |
225 | <!-- <Chart /> --> | 227 | <!-- <Chart /> --> |
226 | |||
227 | |||
228 | <style> | ||
229 | dialog { | ||
230 | background: red; | ||
231 | box-shadow: 0 20px 5em 10px rgba(0, 0, 0, 0.8); | ||
232 | } | ||
233 | |||
234 | dialog::backdrop { | ||
235 | padding: 20px; | ||
236 | box-shadow: 20px 20px rgba(0, 0, 0, 0.8); | ||
237 | background-color: red; | ||
238 | } | ||
239 | </style> | ||
diff --git a/fe/src/lib/Layout.svelte b/fe/src/lib/Layout.svelte index f208f34..2728dd3 100644 --- a/fe/src/lib/Layout.svelte +++ b/fe/src/lib/Layout.svelte | |||
@@ -1,9 +1,13 @@ | |||
1 | <script> | 1 | <script> |
2 | import { authenticated, token } from "../stores/auth"; | 2 | import { authenticated, token, user, preferences } from "../stores/auth"; |
3 | import PreferencesForm from "./PreferencesForm.svelte"; | 3 | import PreferencesForm from "./PreferencesForm.svelte"; |
4 | import { addFormOpen } from "../stores/forms"; | 4 | import { addFormOpen } from "../stores/forms"; |
5 | 5 | ||
6 | const logout = () => token.unauthenticate(); | 6 | const logout = () => { |
7 | preferences.reset(); | ||
8 | user.reset(); | ||
9 | token.unauthenticate(); | ||
10 | } | ||
7 | let preferenceFormOpen = false; | 11 | let preferenceFormOpen = false; |
8 | 12 | ||
9 | function showPreferencesDialog() { | 13 | function showPreferencesDialog() { |
diff --git a/fe/src/lib/LoginForm.svelte b/fe/src/lib/LoginForm.svelte index bf6d9ad..8c3c288 100644 --- a/fe/src/lib/LoginForm.svelte +++ b/fe/src/lib/LoginForm.svelte | |||
@@ -47,7 +47,7 @@ | |||
47 | preferences: userPreferences, | 47 | preferences: userPreferences, |
48 | } = await response.json(); | 48 | } = await response.json(); |
49 | user.setUser(userData); | 49 | user.setUser(userData); |
50 | preferences.set(userPreferences); | 50 | preferences.setPreference(userPreferences); |
51 | token.authenticate(apiToken); | 51 | token.authenticate(apiToken); |
52 | } | 52 | } |
53 | 53 | ||
diff --git a/fe/src/lib/PreferencesForm.svelte b/fe/src/lib/PreferencesForm.svelte index 95e04c1..875393c 100644 --- a/fe/src/lib/PreferencesForm.svelte +++ b/fe/src/lib/PreferencesForm.svelte | |||
@@ -1,51 +1,118 @@ | |||
1 | <script lang="ts"> | 1 | <script lang="ts"> |
2 | import { preferences } from '../stores/auth'; | 2 | import { user, preferences, token } from "../stores/auth"; |
3 | import type { Size, Preference } from '../types'; | 3 | import { createEventDispatcher, onDestroy, onMount } from "svelte"; |
4 | import { createEventDispatcher } from 'svelte'; | 4 | import type { User } from "../types"; |
5 | export let open: boolean; | 5 | |
6 | 6 | export let open: boolean; | |
7 | let preference: Preference = { | 7 | |
8 | color: "#00FF00", | 8 | let sizes: Array<any>; |
9 | size: { | 9 | let selectedSize: number = 1; |
10 | size: 8, | 10 | let color: string = "#000000"; |
11 | unit: 'oz' | 11 | |
12 | } | 12 | const dispatch = createEventDispatcher(); |
13 | } | 13 | |
14 | 14 | const unsubscribe = preferences.subscribe( | |
15 | const dispatch = createEventDispatcher(); | 15 | (value: any) => { |
16 | 16 | console.log('update value: ', value); | |
17 | preferences.subscribe((value) => { | 17 | color = value.color; |
18 | preference = value; | 18 | selectedSize = value.size_id; |
19 | }, | ||
20 | ); | ||
21 | |||
22 | function closeDialog() { | ||
23 | dispatch("close"); | ||
24 | } | ||
25 | |||
26 | async function updateUserPreferences() { | ||
27 | const res = await fetch("http://localhost:8080/api/v1/user/preferences", { | ||
28 | method: "PATCH", | ||
29 | headers: { | ||
30 | Authorization: `Bearer ${$token}`, | ||
31 | }, | ||
32 | body: JSON.stringify($preferences), | ||
19 | }); | 33 | }); |
34 | } | ||
20 | 35 | ||
21 | function onPreferencesSave(): void { | 36 | async function getUserPreferences() { |
22 | preferences.set(preference); | 37 | const res = await fetch( |
23 | dispatch('close') | 38 | `http://localhost:8080/api/v1/user/${($user as User)!.id}/preferences`, |
24 | } | 39 | { |
40 | method: "GET", | ||
41 | headers: { | ||
42 | Authorization: `Bearer ${$token}`, | ||
43 | }, | ||
44 | }, | ||
45 | ); | ||
46 | const updatePreferences = await res.json(); | ||
47 | preferences.set(updatePreferences); | ||
48 | } | ||
25 | 49 | ||
50 | async function onPreferencesSave(): Promise<void> { | ||
51 | preferences.update((value) => ({ | ||
52 | ...value!, | ||
53 | size_id: selectedSize, | ||
54 | color: color, | ||
55 | })); | ||
56 | |||
57 | await updateUserPreferences(); | ||
58 | await getUserPreferences(); | ||
59 | |||
60 | dispatch("close"); | ||
61 | } | ||
62 | |||
63 | onMount(() => { | ||
64 | fetch("http://localhost:8080/api/v1/sizes", { | ||
65 | method: "GET", | ||
66 | headers: { | ||
67 | Authorization: `Bearer ${$token}`, | ||
68 | }, | ||
69 | }) | ||
70 | .then((res) => res.json()) | ||
71 | .then((val) => (sizes = val)); | ||
72 | }); | ||
73 | |||
74 | onDestroy(() => { | ||
75 | unsubscribe(); | ||
76 | }); | ||
26 | </script> | 77 | </script> |
78 | |||
27 | <dialog {open} on:submit|preventDefault={onPreferencesSave}> | 79 | <dialog {open} on:submit|preventDefault={onPreferencesSave}> |
28 | <h2>User Preferences</h2> | 80 | <h2>User Preferences</h2> |
29 | <form method="dialog"> | 81 | <form method="dialog"> |
30 | <div class="form input group"> | 82 | <div class="form input group"> |
31 | <label for="color">Color</label> | 83 | <label for="color">Color</label> |
32 | <input id="color" name="color" type="color" bind:value={preference.color}/> | 84 | <input |
33 | </div> | 85 | id="color" |
34 | <div class="form input group"> | 86 | name="color" |
35 | <label for="size">Bottle Size</label> | 87 | type="color" |
36 | <input id="size" name="size" type="number" min="8" max="48" step="8" bind:value={preference.size.size}/> | 88 | bind:value={color} |
37 | </div> | 89 | /> |
38 | <button type="submit">Save</button> | 90 | </div> |
39 | </form> | 91 | <div class="form input group"> |
92 | <label for="size">Bottle Size</label> | ||
93 | <select | ||
94 | bind:value={selectedSize} | ||
95 | > | ||
96 | {#if sizes} | ||
97 | {#each sizes as size} | ||
98 | <option value={size.id}>{size.size} {size.unit}</option> | ||
99 | {/each} | ||
100 | {/if} | ||
101 | </select> | ||
102 | </div> | ||
103 | <button on:click={closeDialog}>Cancel</button> | ||
104 | <button type="submit">Save</button> | ||
105 | </form> | ||
40 | </dialog> | 106 | </dialog> |
107 | |||
41 | <style> | 108 | <style> |
42 | dialog { | 109 | dialog { |
43 | background: white; | 110 | background: white; |
44 | color: black; | 111 | color: black; |
45 | } | 112 | } |
46 | 113 | ||
47 | input[type="color"] { | 114 | input[type="color"] { |
48 | width: 100%; | 115 | width: 4em; |
49 | height: 100%; | 116 | height: 4em; |
50 | } | 117 | } |
51 | </style> | 118 | </style> |
diff --git a/fe/src/lib/errors.ts b/fe/src/lib/errors.ts index d44bec5..81f7145 100644 --- a/fe/src/lib/errors.ts +++ b/fe/src/lib/errors.ts | |||
@@ -1,7 +1,5 @@ | |||
1 | export class UnauthorizedError extends Error { | 1 | export class UnauthorizedError extends Error { |
2 | constructor(message?: string, options?: ErrorOptions) { | 2 | constructor(message?: string) { |
3 | super(message, options); | 3 | super(message); |
4 | } | 4 | } |
5 | } | 5 | } |
6 | |||
7 | |||
diff --git a/fe/src/lib/utils.ts b/fe/src/lib/utils.ts index 22d4e9a..e78556c 100644 --- a/fe/src/lib/utils.ts +++ b/fe/src/lib/utils.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | export function processFormInput(form: HTMLFormElement) { | 1 | export function processFormInput(form: HTMLFormElement) { |
2 | const formData = new FormData(form); | 2 | const formData: FormData = new FormData(form); |
3 | const data: Record<string, any> = {}; | 3 | const data: Record<string, any> = {}; |
4 | for (let field of formData) { | 4 | for (let field of formData) { |
5 | const [key, value] = field; | 5 | const [key, value] = field; |