diff --git a/Taskfile.yml b/Taskfile.yml index 1327422..f43599b 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,13 +1,14 @@ version: "3" vars: TEMPL_PROXYBIND: '{{.TEMPL_PROXYBIND | default "127.0.0.1"}}' + TEMPL_PROXYPORT: '{{.TEMPL_PROXYPORT | default "7331"}}' TEMPL_PROXY: '{{.TEMPL_PROXY | default "http://127.0.0.1:9000"}}' tasks: # Development Tools templ: desc: Run templ with integrated server and hot reload cmds: - - go tool templ generate --watch --cmd="go run ./cmd/server/main.go" --proxybind="{{.TEMPL_PROXYBIND}}" --proxy="{{.TEMPL_PROXY}}" --open-browser=false + - go tool templ generate --watch --cmd="go run ./cmd/server/main.go" --proxybind="{{.TEMPL_PROXYBIND}}" --proxyport="{{.TEMPL_PROXYPORT}}" --proxy="{{.TEMPL_PROXY}}" --open-browser=false tailwind-clean: desc: Clean tailwind output cmds: diff --git a/internal/handler/dashboard.go b/internal/handler/dashboard.go index e3f29ce..4c2d479 100644 --- a/internal/handler/dashboard.go +++ b/internal/handler/dashboard.go @@ -3,6 +3,7 @@ package handler import ( "log/slog" "net/http" + "strings" "git.juancwu.dev/juancwu/budgit/internal/ctxkeys" "git.juancwu.dev/juancwu/budgit/internal/service" @@ -43,3 +44,23 @@ func (h *dashboardHandler) DashboardPage(w http.ResponseWriter, r *http.Request) ui.Render(w, r, pages.Dashboard(spaces, totalBalance)) } + +func (h *dashboardHandler) CreateSpace(w http.ResponseWriter, r *http.Request) { + user := ctxkeys.User(r.Context()) + + name := strings.TrimSpace(r.FormValue("name")) + if name == "" { + http.Error(w, "Space name is required", http.StatusBadRequest) + return + } + + space, err := h.spaceService.CreateSpace(name, user.ID) + if err != nil { + slog.Error("failed to create space", "error", err, "user_id", user.ID) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + w.Header().Set("HX-Redirect", "/app/spaces/"+space.ID) + w.WriteHeader(http.StatusOK) +} diff --git a/internal/routes/routes.go b/internal/routes/routes.go index be2bc19..55b8ba1 100644 --- a/internal/routes/routes.go +++ b/internal/routes/routes.go @@ -55,6 +55,7 @@ func SetupRoutes(a *app.App) http.Handler { mux.HandleFunc("POST /auth/onboarding", authRateLimiter(middleware.RequireAuth(auth.CompleteOnboarding))) mux.HandleFunc("GET /app/dashboard", middleware.RequireAuth(dashboard.DashboardPage)) + mux.HandleFunc("POST /app/spaces", middleware.RequireAuth(dashboard.CreateSpace)) mux.HandleFunc("GET /app/settings", middleware.RequireAuth(settings.SettingsPage)) mux.HandleFunc("POST /app/settings/password", authRateLimiter(middleware.RequireAuth(settings.SetPassword))) diff --git a/internal/ui/pages/app_dashboard.templ b/internal/ui/pages/app_dashboard.templ index 8ac0743..8144972 100644 --- a/internal/ui/pages/app_dashboard.templ +++ b/internal/ui/pages/app_dashboard.templ @@ -4,7 +4,13 @@ import ( "fmt" "git.juancwu.dev/juancwu/budgit/internal/model" "git.juancwu.dev/juancwu/budgit/internal/ui/layouts" + "git.juancwu.dev/juancwu/budgit/internal/ui/components/button" "git.juancwu.dev/juancwu/budgit/internal/ui/components/card" + "git.juancwu.dev/juancwu/budgit/internal/ui/components/csrf" + "git.juancwu.dev/juancwu/budgit/internal/ui/components/dialog" + "git.juancwu.dev/juancwu/budgit/internal/ui/components/icon" + "git.juancwu.dev/juancwu/budgit/internal/ui/components/input" + "git.juancwu.dev/juancwu/budgit/internal/ui/components/label" ) templ Dashboard(spaces []*model.Space, totalBalance int) { @@ -43,11 +49,48 @@ templ Dashboard(spaces []*model.Space, totalBalance int) { } // Option to create a new space - @card.Card(card.Props{ Class: "h-full border-dashed" }) { - @card.Content(card.ContentProps{ Class: "h-full flex flex-col items-center justify-center py-12" }) { -
Need another space?
- // TODO: Add a button or link to create a new space - Create Space (Coming Soon) + @dialog.Dialog(dialog.Props{ID: "create-space-dialog"}) { + @dialog.Trigger() { + @card.Card(card.Props{ Class: "h-full border-dashed cursor-pointer transition-colors hover:border-primary" }) { + @card.Content(card.ContentProps{ Class: "h-full flex flex-col items-center justify-center py-12" }) { + @icon.Plus(icon.Props{Class: "h-8 w-8 text-muted-foreground mb-2"}) +Create a new space
+ } + } + } + @dialog.Content() { + @dialog.Header() { + @dialog.Title() { + Create Space + } + @dialog.Description() { + Create a new space to organize expenses and shopping lists. + } + } + } }