aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZach Berwaldt <zberwaldt@tutamail.com>2024-03-06 21:53:07 -0500
committerZach Berwaldt <zberwaldt@tutamail.com>2024-03-06 21:53:07 -0500
commit5fa57845052655883120ba4d19a85d8756fb8d8c (patch)
treed2d5ad1dd3fd8d9acaca9ced09612b50218f06b0
parentcf2113e77edabf8e3a632c7b76c769752039ba88 (diff)
[FEAT] Refactor API main file and models
This commit refactors the `main.go` file in the API directory, as well as the related models in the `models.go` file. The changes include: - Reordering imports and removing unnecessary imports - Fixing error messages to be more descriptive - Handling database connections more efficiently with deferred closures - Handling errors and returning appropriate error responses - Adding proper JSON bindings for POST requests - Adding new views in the database scripts for aggregated statistics and daily user statistics No changes were made to imports and requires.
-rw-r--r--api/main.go215
-rw-r--r--api/models.go (renamed from api/lib/models.go)18
-rw-r--r--db/scripts/water_init.sql27
-rw-r--r--fe/src/lib/DataView.svelte130
-rw-r--r--fe/src/lib/forms/AddForm.svelte13
5 files changed, 241 insertions, 162 deletions
diff --git a/api/main.go b/api/main.go
index 57feb09..17a3c3a 100644
--- a/api/main.go
+++ b/api/main.go
@@ -1,18 +1,17 @@
1package main 1package main
2 2
3import ( 3import (
4 "net/http"
5 "crypto/rand" 4 "crypto/rand"
6 "encoding/base64"
7 "database/sql" 5 "database/sql"
8 "strings" 6 "encoding/base64"
9 "errors" 7 "errors"
10 "log" 8 "log"
9 "net/http"
10 "strings"
11 11
12 "github.com/gin-gonic/gin" 12 "github.com/gin-gonic/gin"
13 _ "github.com/mattn/go-sqlite3" 13 _ "github.com/mattn/go-sqlite3"
14 "golang.org/x/crypto/bcrypt" 14 "golang.org/x/crypto/bcrypt"
15 "water/api/lib"
16) 15)
17 16
18func CORSMiddleware() gin.HandlerFunc { 17func CORSMiddleware() gin.HandlerFunc {
@@ -22,15 +21,12 @@ func CORSMiddleware() gin.HandlerFunc {
22 c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With") 21 c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
23 c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT") 22 c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")
24 23
25 log.Println("I am here")
26
27 if c.Request.Method == "OPTIONS" { 24 if c.Request.Method == "OPTIONS" {
28 log.Println(c.Request.Header) 25 log.Println(c.Request.Header)
29 c.AbortWithStatus(204) 26 c.AbortWithStatus(http.StatusNoContent)
30 return 27 return
31 } 28 }
32 29
33 log.Println(c.Request.Header)
34 c.Next() 30 c.Next()
35 } 31 }
36} 32}
@@ -38,7 +34,10 @@ func CORSMiddleware() gin.HandlerFunc {
38// generatToken will g 34// generatToken will g
39func generateToken() string { 35func generateToken() string {
40 token := make([]byte, 32) 36 token := make([]byte, 32)
41 rand.Read(token) 37 _, err := rand.Read(token)
38 if err != nil {
39 return ""
40 }
42 return base64.StdEncoding.EncodeToString(token) 41 return base64.StdEncoding.EncodeToString(token)
43} 42}
44 43
@@ -53,13 +52,13 @@ func establishDBConnection() *sql.DB {
53func checkForTokenInContext(c *gin.Context) (string, error) { 52func checkForTokenInContext(c *gin.Context) (string, error) {
54 authorizationHeader := c.GetHeader("Authorization") 53 authorizationHeader := c.GetHeader("Authorization")
55 if authorizationHeader == "" { 54 if authorizationHeader == "" {
56 return "", errors.New("Authorization header is missing") 55 return "", errors.New("authorization header is missing")
57 } 56 }
58 57
59 parts := strings.Split(authorizationHeader, " ") 58 parts := strings.Split(authorizationHeader, " ")
60 59
61 if len(parts) != 2 || parts[0] != "Bearer" { 60 if len(parts) != 2 || parts[0] != "Bearer" {
62 return "", errors.New("Invalid Authorization header format") 61 return "", errors.New("invalid Authorization header format")
63 } 62 }
64 63
65 return parts[1], nil 64 return parts[1], nil
@@ -98,15 +97,21 @@ func setupRouter() *gin.Engine {
98 } 97 }
99 98
100 db := establishDBConnection() 99 db := establishDBConnection()
101 defer db.Close() 100 defer func(db *sql.DB) {
101 err := db.Close()
102 if err != nil {
103 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
104 return
105 }
106 }(db)
102 107
103 var user models.User 108 var user User
104 var preference models.Preference 109 var preference Preference
105 var size models.Size 110 var size Size
106 111
107 row := db.QueryRow("SELECT name, uuid, password, color, size, unit FROM Users u INNER JOIN Preferences p ON p.user_id = u.id INNER JOIN Sizes s ON p.size_id = s.id WHERE u.name = ?", username) 112 row := db.QueryRow("SELECT name, uuid, password, color, size, unit FROM Users u INNER JOIN Preferences p ON p.user_id = u.id INNER JOIN Sizes s ON p.size_id = s.id WHERE u.name = ?", username)
108 if err := row.Scan(&user.Name, &user.UUID, &user.Password, &preference.Color, &size.Size, &size.Unit); err != nil { 113 if err := row.Scan(&user.Name, &user.UUID, &user.Password, &preference.Color, &size.Size, &size.Unit); err != nil {
109 if err == sql.ErrNoRows { 114 if errors.Is(err, sql.ErrNoRows) {
110 c.AbortWithStatus(http.StatusUnauthorized) 115 c.AbortWithStatus(http.StatusUnauthorized)
111 return 116 return
112 } 117 }
@@ -129,128 +134,147 @@ func setupRouter() *gin.Engine {
129 { 134 {
130 stats.GET("/", func(c *gin.Context) { 135 stats.GET("/", func(c *gin.Context) {
131 db := establishDBConnection() 136 db := establishDBConnection()
132 defer db.Close() 137 defer func(db *sql.DB) {
138 err := db.Close()
139 if err != nil {
140 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
141 return
142 }
143 }(db)
133 144
134 rows, err := db.Query("SELECT s.date, s.quantity, u.uuid, u.name FROM Statistics s INNER JOIN Users u ON u.id = s.user_id") 145 rows, err := db.Query("SELECT s.date, s.quantity, u.uuid, u.name FROM Statistics s INNER JOIN Users u ON u.id = s.user_id")
135 if err != nil { 146 if err != nil {
136 c.JSON(500, gin.H{"error": err.Error()}) 147 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
137 return 148 return
138 } 149 }
139 defer rows.Close() 150 defer func(rows *sql.Rows) {
151 err := rows.Close()
152 if err != nil {
153 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
154 return
155 }
156 }(rows)
140 157
141 var data []models.Statistic 158 var data []Statistic
142 159
143 for rows.Next() { 160 for rows.Next() {
144 var stat models.Statistic 161 var stat Statistic
145 var user models.User 162 var user User
146 if err := rows.Scan(&stat.Date, &stat.Quantity, &user.UUID, &user.Name); err != nil { 163 if err := rows.Scan(&stat.Date, &stat.Quantity, &user.UUID, &user.Name); err != nil {
147 c.JSON(500, gin.H{"error": err.Error()}) 164 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
148 return 165 return
149 } 166 }
150 stat.User = user 167 stat.User = user
151 data = append(data, stat) 168 data = append(data, stat)
152 } 169 }
153 170
171 c.JSON(http.StatusOK, data)
172 })
154 173
155 // TODO: return to this and figure out how to best collect the data you are looking for for each user (zach and parker) 174 stats.POST("/", func(c *gin.Context) {
156 rows, err = db.Query("SELECT date(s.date), SUM(s.quantity) as total FROM Statistics s WHERE s.date >= date('now', '-7 days') GROUP BY DATE(s.date)") 175 var stat StatisticPost
157 if err != nil { 176
158 c.JSON(500, gin.H{"error": err.Error()}) 177 if err := c.BindJSON(&stat); err != nil {
178 c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
159 return 179 return
160 } 180 }
161 defer rows.Close()
162 181
163 var dailySummaries []models.DailySummary 182 db := establishDBConnection()
164 for rows.Next() { 183 defer func(db *sql.DB) {
165 var summary models.DailySummary 184 err := db.Close()
166 if err := rows.Scan(&summary.Date, &summary.Total); err != nil { 185 if err != nil {
167 c.JSON(500, gin.H{"error": err.Error()}) 186 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
168 return 187 return
169 } 188 }
170 dailySummaries = append(dailySummaries, summary) 189 }(db)
171 }
172 190
173 c.JSON(http.StatusOK, gin.H{"stats": data, "totals": dailySummaries}) 191 result, err := db.Exec("INSERT INTO statistics (date, user_id, quantity) values (?, ?, ?)", stat.Date, stat.UserID, stat.Quantity)
174 rows, err = db.Query("SELECT s.date, SUM(s.quantity) as total, u.uuid, u.name FROM Statistics s INNER JOIN Users u ON u.id = s.user_id WHERE s.date >= date('now', '-7 days') GROUP BY s.date, s.user_id")
175 192
176 if err != nil { 193 if err != nil {
177 c.JSON(500, gin.H{"error": err.Error()}) 194 c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
178 return
179 } 195 }
180 defer rows.Close()
181 196
182 var totals []interface{} 197 id, err := result.LastInsertId()
183 for rows.Next() { 198 if err != nil {
184 var stat models.Statistic 199 c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
185 var user models.User
186 if err := rows.Scan(&stat.Date, &stat.Quantity, &user.UUID, &user.Name); err != nil {
187 c.JSON(500, gin.H{"error": err.Error()})
188 return
189 }
190 stat.User = user
191 totals = append(totals, stat)
192 } 200 }
193 201
194 c.JSON(http.StatusOK, gin.H{"stats": data, "totals": totals}) 202 c.JSON(http.StatusCreated, gin.H{"status": "created", "id": id})
195 }) 203 })
196 204
197 stats.POST("/", func(c *gin.Context) { 205 stats.GET("weekly/", func(c *gin.Context) {
198 var stat models.Statistic 206 db := establishDBConnection()
207 defer func(db *sql.DB) {
208 err := db.Close()
209 if err != nil {
210 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
211 return
212 }
213 }(db)
199 214
200 if err := c.BindJSON(&stat); err != nil { 215 rows, err := db.Query("SELECT date, total FROM `WeeklyStatisticsView`")
201 c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) 216 if err != nil {
217 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
202 return 218 return
203 } 219 }
220 defer func(rows *sql.Rows) {
221 err := rows.Close()
222 if err != nil {
223 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
224 return
225 }
226 }(rows)
227
228 var data []WeeklyStatistic
229 for rows.Next() {
230 var weeklyStat WeeklyStatistic
231 if err := rows.Scan(&weeklyStat.Date, &weeklyStat.Total); err != nil {
232 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
233 }
234 data = append(data, weeklyStat)
235 }
236
237 c.JSON(http.StatusOK, data)
238 })
204 239
240 stats.GET("totals/", func(c *gin.Context) {
205 db := establishDBConnection() 241 db := establishDBConnection()
206 defer db.Close() 242 defer func(db *sql.DB) {
243 err := db.Close()
244 if err != nil {
245 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
246 return
247 }
248 }(db)
207 249
208 result, err := db.Exec("INSERT INTO statistics (date, user_id, quantity) values (?, ?, ?)", stat.Date, 1, stat.Quantity) 250 rows, err := db.Query("SELECT name, total FROM DailyUserStatistics")
209 251
210 if err != nil { 252 if err != nil {
211 c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) 253 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
254 return
212 } 255 }
256 defer func(rows *sql.Rows) {
257 err := rows.Close()
258 if err != nil {
259 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
260 return
261 }
262 }(rows)
213 263
214 id, err := result.LastInsertId() 264 var data []DailyUserTotals
215 if err != nil { 265 for rows.Next() {
216 c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) 266 var stat DailyUserTotals
267 if err := rows.Scan(&stat.Name, &stat.Total); err != nil {
268 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
269 return
270 }
271 data = append(data, stat)
217 } 272 }
218 273
219 c.JSON(http.StatusCreated, gin.H{"status": "created", "id": id}) 274 c.JSON(http.StatusOK, data)
220 })
221 275
222 stats.GET("/totals/", func(c *gin.Context) {
223 c.JSON(http.StatusOK, gin.H{"status": "ok"})
224 }) 276 })
225 277
226 // stats.GET("/totals/", func(c *gin.Context) {
227 // db := establishDBConnection()
228 // defer db.Close()
229 //
230 // rows, err := db.Query("SELECT s.date, SUM(s.quantity) as total, u.uuid, u.name FROM Statistics s INNER JOIN Users u ON u.id = s.user_id WHERE s.date >= date('now', '-7 days') GROUP BY s.date, s.user_id")
231 //
232 // if err != nil {
233 // c.JSON(500, gin.H{"error": err.Error()})
234 // return
235 // }
236 // defer rows.Close()
237 //
238 // var data []models.Statistic
239 // for rows.Next() {
240 // var stat models.Statistic
241 // var user models.User
242 // if err := rows.Scan(&stat.Date, &stat.Quantity, &user.UUID, &user.Name); err != nil {
243 // c.JSON(500, gin.H{"error": err.Error()})
244 // return
245 // }
246 // stat.User = user
247 // data = append(data, stat)
248 // }
249 //
250 // c.JSON(http.StatusOK, data)
251 //
252 // })
253
254 stats.GET("user/:uuid", func(c *gin.Context) { 278 stats.GET("user/:uuid", func(c *gin.Context) {
255 c.JSON(http.StatusOK, gin.H{"status": "ok", "uuid": c.Param("uuid")}) 279 c.JSON(http.StatusOK, gin.H{"status": "ok", "uuid": c.Param("uuid")})
256 }) 280 })
@@ -270,5 +294,8 @@ func setupRouter() *gin.Engine {
270func main() { 294func main() {
271 r := setupRouter() 295 r := setupRouter()
272 // Listen and Server in 0.0.0.0:8080 296 // Listen and Server in 0.0.0.0:8080
273 r.Run(":8080") 297 err := r.Run(":8080")
298 if err != nil {
299 return
300 }
274} 301}
diff --git a/api/lib/models.go b/api/models.go
index f959519..0845d1d 100644
--- a/api/lib/models.go
+++ b/api/models.go
@@ -1,4 +1,4 @@
1package models 1package main
2 2
3import ( 3import (
4 "time" 4 "time"
@@ -12,6 +12,12 @@ type Statistic struct {
12 Quantity int `json:"quantity"` 12 Quantity int `json:"quantity"`
13} 13}
14 14
15type StatisticPost struct {
16 Date time.Time `json:"date"`
17 Quantity int64 `json:"quantity"`
18 UserID int64 `json:"user_id"`
19}
20
15type User struct { 21type User struct {
16 ID int64 `json:"-"` 22 ID int64 `json:"-"`
17 Name string `json:"name"` 23 Name string `json:"name"`
@@ -39,3 +45,13 @@ type Size struct {
39 Size int64 `json:"size"` 45 Size int64 `json:"size"`
40 Unit string `json:"unit"` 46 Unit string `json:"unit"`
41} 47}
48
49type WeeklyStatistic struct {
50 Date string `json:"date"`
51 Total int64 `json:"total"`
52}
53
54type DailyUserTotals struct {
55 Name string `json:"name"`
56 Total int64 `json:"total"`
57} \ No newline at end of file
diff --git a/db/scripts/water_init.sql b/db/scripts/water_init.sql
index 6a4de24..3b79ed5 100644
--- a/db/scripts/water_init.sql
+++ b/db/scripts/water_init.sql
@@ -18,7 +18,7 @@ CREATE TABLE IF NOT EXISTS Statistics (
18CREATE TABLE IF NOT EXISTS Preferences ( 18CREATE TABLE IF NOT EXISTS Preferences (
19 id INTEGER PRIMARY KEY, 19 id INTEGER PRIMARY KEY,
20 color TEXT NOT NULL DEFAULT "#000000", 20 color TEXT NOT NULL DEFAULT "#000000",
21 user_id INT NOT NULL, 21 user_id INT UNIQUE NOT NULL,
22 size_id INT NOT NULL DEFAULT 1, 22 size_id INT NOT NULL DEFAULT 1,
23 FOREIGN KEY(user_id) REFERENCES Users(id) ON DELETE CASCADE, 23 FOREIGN KEY(user_id) REFERENCES Users(id) ON DELETE CASCADE,
24 FOREIGN KEY(size_id) REFERENCES Sizes(id) 24 FOREIGN KEY(size_id) REFERENCES Sizes(id)
@@ -70,3 +70,28 @@ END;
70-- 70--
71CREATE VIEW IF NOT EXISTS aggregated_stats AS 71CREATE VIEW IF NOT EXISTS aggregated_stats AS
72 SELECT u.uuid, SUM(s.quantity * s.size) from Statistics s INNER JOIN Users u ON u.id = s.user_id INNER JOIN Preferences p ON p.user_id = u.id INNER JOIN Size s ON s.id = p.size_id; 72 SELECT u.uuid, SUM(s.quantity * s.size) from Statistics s INNER JOIN Users u ON u.id = s.user_id INNER JOIN Preferences p ON p.user_id = u.id INNER JOIN Size s ON s.id = p.size_id;
73
74CREATE VIEW IF NOT EXISTS `DailyUserStatistics` AS
75SELECT users.name, IFNULL(SUM(statistics.quantity), 0) as total, preferences.color as color
76FROM users
77LEFT JOIN statistics ON users.id = statistics.user_id AND DATE(statistics.date) = DATE('now', '-1 day')
78LEFT JOIN preferences ON users.id = preferences.user_id
79GROUP BY users.name;
80
81
82CREATE VIEW IF NOT EXISTS `WeeklyStatisticsView` AS
83 WITH DateSequence(Dates) AS
84 (
85 SELECT Date(CURRENT_DATE, '-7 day')
86 UNION ALL
87 SELECT Date(Dates, '+1 day')
88 FROM DateSequence
89 WHERE Date(Dates, '+1 day') < Date(CURRENT_DATE)
90 )
91SELECT DateSequence.Dates as 'date',
92 IFNULL(SUM(statistics.quantity), 0) AS 'total'
93FROM DateSequence
94LEFT JOIN statistics
95ON Date(statistics.date) = DateSequence.Dates
96GROUP BY DateSequence.Dates
97ORDER BY DateSequence.Dates; \ No newline at end of file
diff --git a/fe/src/lib/DataView.svelte b/fe/src/lib/DataView.svelte
index 2b1b8b9..7d62a43 100644
--- a/fe/src/lib/DataView.svelte
+++ b/fe/src/lib/DataView.svelte
@@ -9,8 +9,6 @@
9 import AddForm from "./forms/AddForm.svelte"; 9 import AddForm from "./forms/AddForm.svelte";
10 10
11 let json: Promise<any>; 11 let json: Promise<any>;
12 let totals: Promise<any>;
13 let userStats: Promise<any>;
14 12
15 let barCanvasRef: HTMLCanvasElement; 13 let barCanvasRef: HTMLCanvasElement;
16 let lineCanvasRef: HTMLCanvasElement; 14 let lineCanvasRef: HTMLCanvasElement;
@@ -18,6 +16,10 @@
18 let lineChart: any; 16 let lineChart: any;
19 17
20 let lastSevenDays: string[]; 18 let lastSevenDays: string[];
19 let lastSevenDaysData: number[];
20
21 let userTotalsLabels: string[];
22 let userTotalsData: number[];
21 23
22 async function fetchData() { 24 async function fetchData() {
23 const res = await fetch("http://localhost:8080/api/v1/stats/", { 25 const res = await fetch("http://localhost:8080/api/v1/stats/", {
@@ -33,24 +35,27 @@
33 } 35 }
34 } 36 }
35 37
36 async function fetchTotals() { 38 async function fetchDailyUserStatistics() {
37 const res = await fetch("http://localhost:8080/api/v1/stats/totals/", { 39 const res = await fetch("http://localhost:8080/api/v1/stats/totals/", {
38 method: 'GET', 40 method: "GET",
39 mode: 'no-cors',
40 headers: { 41 headers: {
41 Authorization: `Bearer ${$token}` 42 Authorization: `Bearer ${$token}`
42 } 43 }
43 }); 44 });
44 45
45 if (res.ok) { 46 if (res.ok) {
46 totals = res.json(); 47 const json = await res.json();
48 let labels = json.map(d => d.name);
49 let data = json.map(d => d.total);
50 return [labels, data];
47 } else { 51 } else {
48 throw new Error("There was a problem with your request"); 52 throw new Error("There was a problem with your request");
49 } 53 }
54
50 } 55 }
51 56
52 async function fetchStatsForUser() { 57 async function fetchWeeklyTotals() {
53 const res = await fetch("http://localhost:8080/api/v1/stats/user/1aa668f3-7527-4a67-9c24-fdf307542eeb", { 58 const res = await fetch("http://localhost:8080/api/v1/stats/weekly/", {
54 method: "GET", 59 method: "GET",
55 headers: { 60 headers: {
56 Authorization: `Bearer ${$token}` 61 Authorization: `Bearer ${$token}`
@@ -58,22 +63,15 @@
58 }); 63 });
59 64
60 if (res.ok) { 65 if (res.ok) {
61 userStats = res.json(); 66 const json = await res.json();
67 let labels = json.map(d => d.date);
68 let data = json.map(d => d.total);
69 return [labels, data];
62 } else { 70 } else {
63 throw new Error("There was a problem with your request"); 71 throw new Error("There was a problem with your request");
64 } 72 }
65 } 73 }
66 74
67 function getLastSevenDays() {
68 const result = [];
69 for (let i = 0; i < 7; i++) {
70 let d = new Date();
71 d.setDate(d.getDate() - i);
72 result.push(d.toISOString().substring(0, 10));
73 }
74 return result;
75 }
76
77 function closeDialog() { 75 function closeDialog() {
78 addFormOpen.set(false); 76 addFormOpen.set(false);
79 } 77 }
@@ -81,61 +79,79 @@
81 function onStatisticAdd() { 79 function onStatisticAdd() {
82 closeDialog(); 80 closeDialog();
83 fetchData(); 81 fetchData();
82 fetchWeeklyTotals().then(updateWeeklyTotalsChart).catch(err => console.error(err));
83 fetchDailyUserStatistics().then(updateDailyUserTotalsChart).catch(err => console.error(err));
84 } 84 }
85 85
86 onMount(() => { 86 function setupWeeklyTotalsChart(result) {
87 fetchData(); 87 [lastSevenDays, lastSevenDaysData] = result;
88// fetchTotals(); 88 lineChart = new Chart(lineCanvasRef, {
89 fetchStatsForUser(); 89 type: "line",
90 lastSevenDays = getLastSevenDays();
91 barChart = new Chart(barCanvasRef, {
92 type: "bar",
93 data: { 90 data: {
94 labels: lastSevenDays, 91 labels: lastSevenDays,
95 datasets: [ 92 datasets: [
96 { 93 {
97 label: "Zach", 94 label: "Totals",
98 data: [1, 2, 8, 2, 5, 5, 1], 95 data: lastSevenDaysData,
99 backgroundColor: "rgba(255, 192, 192, 0.2)", 96 backgroundColor: "rgba(255, 192, 192, 0.2)"
100 borderColor: "rgba(75, 192, 192, 1)",
101 borderWidth: 1
102 }, {
103 label: "Parker",
104 data: [6, 1, 1, 4, 3, 5, 1],
105 backgroundColor: "rgba(75, 192, 192, 0.2)",
106 borderColor: "rgba(75, 192, 192, 1)",
107 borderWidth: 1
108 } 97 }
109 ] 98 ]
110 }, 99 },
111 options: { 100 options: {
112 responsive: true 101 responsive: true,
102 plugins: {
103 legend: {
104 display: false
105 }
106 }
113 } 107 }
114 }); 108 });
115 lineChart = new Chart(lineCanvasRef, { 109 }
116 type: "line", 110
111 function setupDailyUserTotalsChart(result) {
112 [userTotalsLabels, userTotalsData] = result;
113
114 barChart = new Chart(barCanvasRef, {
115 type: "bar",
117 data: { 116 data: {
118 labels: lastSevenDays, 117 labels: userTotalsLabels,
119 datasets: [ 118 datasets: [
120 { 119 {
121 label: "Zach", 120 data: userTotalsData,
122 data: [1, 2, 8, 2, 5, 5, 1], 121 backgroundColor: [
123 backgroundColor: "rgba(255, 192, 192, 0.2)", 122 "#330000",
124 borderColor: "rgba(75, 192, 192, 1)", 123 "rgba(100, 200, 192, 0.2)"
125 borderWidth: 1 124 ]
126 }, {
127 label: "Parker",
128 data: [6, 1, 1, 4, 3, 5, 1],
129 backgroundColor: "rgba(75, 192, 192, 0.2)",
130 borderColor: "rgba(75, 192, 192, 1)",
131 borderWidth: 1
132 } 125 }
133 ] 126 ]
134 }, 127 },
135 options: { 128 options: {
136 responsive: true 129 responsive: true,
130 plugins: {
131 legend: {
132 display: false
133 }
134 }
137 } 135 }
138 }); 136 });
137 }
138
139 function updateWeeklyTotalsChart(result) {
140 [,lastSevenDaysData] = result;
141 lineChart.data.datasets[0].data = lastSevenDaysData;
142 lineChart.update();
143 }
144
145 function updateDailyUserTotalsChart(result) {
146 [,userTotalsData] = result;
147 barChart.data.datasets[0].data = userTotalsData;
148 barChart.update();
149 }
150
151 onMount(() => {
152 fetchData();
153 fetchWeeklyTotals().then(setupWeeklyTotalsChart);
154 fetchDailyUserStatistics().then(setupDailyUserTotalsChart);
139 }); 155 });
140 156
141 onDestroy(() => { 157 onDestroy(() => {
@@ -164,14 +180,6 @@
164 <p>{error}</p> 180 <p>{error}</p>
165 {/await} 181 {/await}
166 </Card> 182 </Card>
167 <Card>
168 <button on:click={() => fetchTotals()}>Get totals</button>
169 {#await totals then data}
170 {JSON.stringify(data)}
171 {:catch error}
172 <p>{error}</p>
173 {/await}
174 </Card>
175</Column> 183</Column>
176<!-- <Chart /> --> 184<!-- <Chart /> -->
177 185
diff --git a/fe/src/lib/forms/AddForm.svelte b/fe/src/lib/forms/AddForm.svelte
index f22e5f4..4520b1b 100644
--- a/fe/src/lib/forms/AddForm.svelte
+++ b/fe/src/lib/forms/AddForm.svelte
@@ -34,20 +34,23 @@
34 dispatch("close"); 34 dispatch("close");
35 } 35 }
36 36
37 async function handleSubmitStat() { 37 async function handleSubmitStat()
38 const response = await fetch("http://localhost:8080/api/v1/stats/", { 38 {
39 const { date, quantity } = statistic;
40 await fetch("http://localhost:8080/api/v1/stats/", {
39 method: "POST", 41 method: "POST",
40 headers: { 42 headers: {
41 Authorization: `Bearer ${$token}` 43 Authorization: `Bearer ${$token}`
42 }, 44 },
43 body: JSON.stringify({ 45 body: JSON.stringify({
44 date: new Date(), 46 date: new Date(date),
45 user_id: 1, 47 user_id: 2,
46 quantity: 3 48 quantity
47 }) 49 })
48 }); 50 });
49 dispatch("submit"); 51 dispatch("submit");
50 } 52 }
53
51</script> 54</script>
52 55
53<dialog {open} on:submit={handleSubmitStat}> 56<dialog {open} on:submit={handleSubmitStat}>