From 5d54e727445c2894fe939cff0c117f9babeaf873 Mon Sep 17 00:00:00 2001 From: Tim Wundenberg Date: Sat, 27 Jul 2024 21:10:31 +0200 Subject: [PATCH] #8 add view and api to track activities --- api/main.go | 49 +--- api/middleware/auth.go | 5 + api/middleware/cors.go | 20 ++ api/migrations/001_initial_schema.up.sql | 11 +- api/migrations/002_initial_schema.up.sql | 4 - api/migrations/003_initial_schema.up.sql | 4 - api/workout/workout.go | 90 ++++++ view/package-lock.json | 354 +++++++++++++++++++++++ view/package.json | 2 + view/src/routes/+page.svelte | 3 +- view/src/routes/app/+page.svelte | 131 +++++++++ view/vite.config.ts | 6 +- 12 files changed, 625 insertions(+), 54 deletions(-) create mode 100644 api/middleware/cors.go delete mode 100644 api/migrations/002_initial_schema.up.sql delete mode 100644 api/migrations/003_initial_schema.up.sql create mode 100644 api/workout/workout.go create mode 100644 view/src/routes/app/+page.svelte diff --git a/api/main.go b/api/main.go index 977f466..097b429 100644 --- a/api/main.go +++ b/api/main.go @@ -3,22 +3,15 @@ package main import ( "api/context" "api/middleware" - "api/utils" + "api/workout" + "database/sql" "log" "net/http" - "firebase.google.com/go/auth" _ "github.com/mattn/go-sqlite3" ) -type Person struct { - ID int - Name string -} - -type Context context.Context - func main() { context.InitializeDB() @@ -29,16 +22,11 @@ func main() { } defer db.Close() - var context = Context{ - DB: db, - } - - var router = http.NewServeMux() - router.HandleFunc("GET /", context.HelloWorld) + var router = getRouter(db) var server = http.Server{ Addr: ":8080", - Handler: middleware.Logging(middleware.EnsureAuth(router)), + Handler: middleware.Logging(middleware.EnableCors(middleware.EnsureAuth(router))), } log.Println("Starting server at", server.Addr) @@ -48,28 +36,9 @@ func main() { } } -func (ctx *Context) HelloWorld(w http.ResponseWriter, r *http.Request) { - token := r.Context().Value(middleware.TOKEN_KEY).(*auth.Token) - log.Println(token.UID) - - sqlStmt := `select COUNT(*) from person;` - - result, err := ctx.DB.Query(sqlStmt) - if err != nil { - log.Fatal(err) - } - - var count int - if result.Next() { - if result.Scan(&count) != nil { - log.Fatal(err) - } - } else { - log.Fatal("No rows found") - } - - err = utils.WriteJSON(w, count) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } +func getRouter(db *sql.DB) *http.ServeMux { + var router = http.NewServeMux() + router.HandleFunc("POST /workout", workout.NewWorkout(db)) + router.HandleFunc("GET /workout", workout.GetWorkouts(db)) + return router } diff --git a/api/middleware/auth.go b/api/middleware/auth.go index ff87fc7..6ae5ded 100644 --- a/api/middleware/auth.go +++ b/api/middleware/auth.go @@ -13,6 +13,11 @@ const TOKEN_KEY ContextKey = "token" func EnsureAuth(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { tokenStr := r.Header.Get("Authorization") + if (tokenStr == "") || (len(tokenStr) < len("Bearer ")) { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + tokenStr = tokenStr[len("Bearer "):] token, err := utils.VerifyToken(tokenStr) diff --git a/api/middleware/cors.go b/api/middleware/cors.go new file mode 100644 index 0000000..6bbb4ba --- /dev/null +++ b/api/middleware/cors.go @@ -0,0 +1,20 @@ +package middleware + +import ( + "net/http" +) + +func EnableCors(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", "http://localhost:5173") + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, DELETE") + w.Header().Set("Access-Control-Allow-Headers", "Authorization") + + if r.Method == "OPTIONS" { + w.WriteHeader(http.StatusOK) + return + } + + next.ServeHTTP(w, r) + }) +} diff --git a/api/migrations/001_initial_schema.up.sql b/api/migrations/001_initial_schema.up.sql index 71d9c0a..9e3e5c9 100644 --- a/api/migrations/001_initial_schema.up.sql +++ b/api/migrations/001_initial_schema.up.sql @@ -1,5 +1,8 @@ -create table person (id integer not null primary key, name text); - -insert into person (id, name) -values (1, 'John'); +CREATE TABLE workout ( + user_id INTEGER NOT NULL, + date TEXT NOT NULL, + type TEXT NOT NULL, + sets INTEGER NOT NULL, + reps INTEGER NOT NULL +); diff --git a/api/migrations/002_initial_schema.up.sql b/api/migrations/002_initial_schema.up.sql deleted file mode 100644 index 48ce987..0000000 --- a/api/migrations/002_initial_schema.up.sql +++ /dev/null @@ -1,4 +0,0 @@ - - -insert into person (id, name) -values (2, 'John'); diff --git a/api/migrations/003_initial_schema.up.sql b/api/migrations/003_initial_schema.up.sql deleted file mode 100644 index 448d0f7..0000000 --- a/api/migrations/003_initial_schema.up.sql +++ /dev/null @@ -1,4 +0,0 @@ - - -insert into person (id, name) -values (3, 'Katja'); diff --git a/api/workout/workout.go b/api/workout/workout.go new file mode 100644 index 0000000..b4a47a8 --- /dev/null +++ b/api/workout/workout.go @@ -0,0 +1,90 @@ +package workout + +import ( + "api/middleware" + "api/utils" + "database/sql" + "net/http" + "strconv" + "time" + + "firebase.google.com/go/auth" +) + +func NewWorkout(db *sql.DB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var dateStr = r.FormValue("date") + var typeStr = r.FormValue("type") + var setsStr = r.FormValue("sets") + var repsStr = r.FormValue("reps") + + if dateStr == "" || typeStr == "" || setsStr == "" || repsStr == "" { + http.Error(w, "Missing required fields", http.StatusBadRequest) + return + } + + date, err := time.Parse("2006-01-02", dateStr) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + sets, err := strconv.Atoi(setsStr) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + reps, err := strconv.Atoi(repsStr) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + token := r.Context().Value(middleware.TOKEN_KEY).(*auth.Token) + + _, err = db.Exec("INSERT INTO workout (user_id, date, type, sets, reps) VALUES (?, ?, ?, ?, ?)", token.UID, date, typeStr, sets, reps) + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } +} + +func GetWorkouts(db *sql.DB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + token := r.Context().Value(middleware.TOKEN_KEY).(*auth.Token) + var userId = token.UID + + rows, err := db.Query("SELECT date, type, sets, reps FROM workout WHERE user_id = ?", userId) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + var workouts = make([]map[string]interface{}, 0) + for rows.Next() { + var date string + var workoutType string + var sets int + var reps int + + err = rows.Scan(&date, &workoutType, &sets, &reps) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + workout := map[string]interface{}{ + "date": date, + "type": workoutType, + "sets": sets, + "reps": reps, + } + workouts = append(workouts, workout) + } + + utils.WriteJSON(w, workouts) + } +} diff --git a/view/package-lock.json b/view/package-lock.json index f20fab9..3d65bce 100644 --- a/view/package-lock.json +++ b/view/package-lock.json @@ -8,6 +8,7 @@ "name": "template", "version": "0.0.2", "devDependencies": { + "@iconify/json": "^2.2.231", "@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-static": "^3.0.2", "@sveltejs/kit": "^2.0.0", @@ -29,6 +30,7 @@ "tslib": "^2.4.1", "typescript": "^5.0.0", "typescript-eslint": "^8.0.0-alpha.20", + "unplugin-icons": "^0.19.0", "vite": "^5.0.3" } }, @@ -57,6 +59,27 @@ "node": ">=6.0.0" } }, + "node_modules/@antfu/install-pkg": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-0.3.3.tgz", + "integrity": "sha512-nHHsk3NXQ6xkCfiRRC8Nfrg8pU5kkr3P3Y9s9dKqiuRmBD0Yap7fymNDjGFKeWhZQHqqbCS5CfeMy9wtExM24w==", + "dev": true, + "dependencies": { + "@jsdevtools/ez-spawn": "^3.0.4" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@antfu/utils": { + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-0.7.10.tgz", + "integrity": "sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/@esbuild/linux-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", @@ -837,6 +860,50 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@iconify/json": { + "version": "2.2.231", + "resolved": "https://registry.npmjs.org/@iconify/json/-/json-2.2.231.tgz", + "integrity": "sha512-+KlOkI3CuwSuG8H3EIeC7f5LTsm73aggoh1GA9Uh4YCl65zvTgYyFwCxJXnR2vVeCoAlO2UtCtjHjNwOWchf4g==", + "dev": true, + "dependencies": { + "@iconify/types": "*", + "pathe": "^1.1.2" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true + }, + "node_modules/@iconify/utils": { + "version": "2.1.25", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.1.25.tgz", + "integrity": "sha512-Y+iGko8uv/Fz5bQLLJyNSZGOdMW0G7cnlEX1CiNcKsRXX9cq/y/vwxrIAtLCZhKHr3m0VJmsjVPsvnM4uX8YLg==", + "dev": true, + "dependencies": { + "@antfu/install-pkg": "^0.1.1", + "@antfu/utils": "^0.7.7", + "@iconify/types": "^2.0.0", + "debug": "^4.3.4", + "kolorist": "^1.8.0", + "local-pkg": "^0.5.0", + "mlly": "^1.6.1" + } + }, + "node_modules/@iconify/utils/node_modules/@antfu/install-pkg": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-0.1.1.tgz", + "integrity": "sha512-LyB/8+bSfa0DFGC06zpCEfs89/XoWZwws5ygEa5D+Xsm3OfI+aXQ86VgVG7Acyef+rSZ5HE7J8rrxzrQeM3PjQ==", + "dev": true, + "dependencies": { + "execa": "^5.1.1", + "find-up": "^5.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -929,6 +996,21 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@jsdevtools/ez-spawn": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@jsdevtools/ez-spawn/-/ez-spawn-3.0.4.tgz", + "integrity": "sha512-f5DRIOZf7wxogefH03RjMPMdBF7ADTWUMoOs9kaJo06EfwF+aFhMZMDZxHg/Xe12hptN9xoZjGso2fdjapBRIA==", + "dev": true, + "dependencies": { + "call-me-maybe": "^1.0.1", + "cross-spawn": "^7.0.3", + "string-argv": "^0.3.1", + "type-detect": "^4.0.8" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1671,6 +1753,12 @@ "node": ">=8.0.0" } }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", + "dev": true + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1862,6 +1950,12 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/confbox": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz", + "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==", + "dev": true + }, "node_modules/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", @@ -2331,6 +2425,35 @@ "node": ">=0.10.0" } }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2567,6 +2690,18 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -2684,6 +2819,15 @@ "dev": true, "peer": true }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, "node_modules/idb": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", @@ -2836,6 +2980,18 @@ "@types/estree": "*" } }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2920,6 +3076,12 @@ "integrity": "sha512-tBECoUqNFbyAY4RrbqsBQqDFpGXAEbdD5QKr8kACx3+rnArmuuR22nKQWKazvp07N9yjTyDZaw/20UIH8tL9DQ==", "dev": true }, + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", + "dev": true + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -2948,6 +3110,22 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dev": true, + "dependencies": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/locate-character": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", @@ -3019,6 +3197,12 @@ "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", "dev": true }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3041,6 +3225,15 @@ "node": ">=8.6" } }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -3092,6 +3285,18 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mlly": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.1.tgz", + "integrity": "sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==", + "dev": true, + "dependencies": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.1.1", + "ufo": "^1.5.3" + } + }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", @@ -3175,6 +3380,18 @@ "node": ">=0.10.0" } }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -3202,6 +3419,21 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -3325,6 +3557,12 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, "node_modules/periscopic": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", @@ -3372,6 +3610,17 @@ "node": ">= 6" } }, + "node_modules/pkg-types": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.3.tgz", + "integrity": "sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==", + "dev": true, + "dependencies": { + "confbox": "^0.1.7", + "mlly": "^1.7.1", + "pathe": "^1.1.2" + } + }, "node_modules/postcss": { "version": "8.4.40", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.40.tgz", @@ -3927,6 +4176,15 @@ "node": ">=0.10.0" } }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -4017,6 +4275,15 @@ "node": ">=8" } }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -4512,6 +4779,15 @@ "node": ">= 0.8.0" } }, + "node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/typescript": { "version": "5.5.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", @@ -4548,6 +4824,12 @@ } } }, + "node_modules/ufo": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", + "dev": true + }, "node_modules/undici": { "version": "5.28.4", "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", @@ -4568,6 +4850,63 @@ "dev": true, "peer": true }, + "node_modules/unplugin": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.12.0.tgz", + "integrity": "sha512-KeczzHl2sATPQUx1gzo+EnUkmN4VmGBYRRVOZSGvGITE9rGHRDGqft6ONceP3vgXcyJ2XjX5axG5jMWUwNCYLw==", + "dev": true, + "dependencies": { + "acorn": "^8.12.1", + "chokidar": "^3.6.0", + "webpack-sources": "^3.2.3", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/unplugin-icons": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/unplugin-icons/-/unplugin-icons-0.19.0.tgz", + "integrity": "sha512-u5g/gIZPZEj1wUGEQxe9nzftOSqmblhusc+sL3cawIRoIt/xWpE6XYcPOfAeFTYNjSbRrX/3QiX89PFiazgU1w==", + "dev": true, + "dependencies": { + "@antfu/install-pkg": "^0.3.3", + "@antfu/utils": "^0.7.7", + "@iconify/utils": "^2.1.23", + "debug": "^4.3.4", + "kolorist": "^1.8.0", + "local-pkg": "^0.5.0", + "unplugin": "^1.10.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@svgr/core": ">=7.0.0", + "@svgx/core": "^1.0.1", + "@vue/compiler-sfc": "^3.0.2 || ^2.7.0", + "vue-template-compiler": "^2.6.12", + "vue-template-es2015-compiler": "^1.9.0" + }, + "peerDependenciesMeta": { + "@svgr/core": { + "optional": true + }, + "@svgx/core": { + "optional": true + }, + "@vue/compiler-sfc": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + }, + "vue-template-es2015-compiler": { + "optional": true + } + } + }, "node_modules/update-browserslist-db": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", @@ -4682,6 +5021,21 @@ } } }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "dev": true + }, "node_modules/websocket-driver": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", diff --git a/view/package.json b/view/package.json index 86eeffd..04c8ae9 100644 --- a/view/package.json +++ b/view/package.json @@ -12,6 +12,7 @@ "format": "prettier --write ." }, "devDependencies": { + "@iconify/json": "^2.2.231", "@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-static": "^3.0.2", "@sveltejs/kit": "^2.0.0", @@ -33,6 +34,7 @@ "tslib": "^2.4.1", "typescript": "^5.0.0", "typescript-eslint": "^8.0.0-alpha.20", + "unplugin-icons": "^0.19.0", "vite": "^5.0.3" }, "type": "module" diff --git a/view/src/routes/+page.svelte b/view/src/routes/+page.svelte index 86108b0..2b243a7 100644 --- a/view/src/routes/+page.svelte +++ b/view/src/routes/+page.svelte @@ -1,3 +1,4 @@ -
+

Welcome to ME-FIT

+ Getting Started
diff --git a/view/src/routes/app/+page.svelte b/view/src/routes/app/+page.svelte new file mode 100644 index 0000000..48742b8 --- /dev/null +++ b/view/src/routes/app/+page.svelte @@ -0,0 +1,131 @@ + + +
+

Track your workout

+ + + + + + + + + {#if errorStr} +

{errorStr}

+ {/if} +
+ +
+

Workout history

+ + + + + + + + + + + + {#each workouts as workout} + + + + + + + {/each} + +
DateTypeSetsReps
{workout.date}{workout.type}{workout.sets}{workout.reps}
+
diff --git a/view/vite.config.ts b/view/vite.config.ts index bbf8c7d..22d51e4 100644 --- a/view/vite.config.ts +++ b/view/vite.config.ts @@ -1,6 +1,10 @@ import { sveltekit } from '@sveltejs/kit/vite'; import { defineConfig } from 'vite'; +import Icons from 'unplugin-icons/vite' export default defineConfig({ - plugins: [sveltekit()] + plugins: [ + sveltekit(), + Icons({ compiler: 'svelte' }) + ], });