Merge pull request 'feat: create space' (#6) from feat/create-space into main
Reviewed-on: #6
This commit is contained in:
commit
ca51d87d17
3 changed files with 70 additions and 5 deletions
|
|
@ -3,6 +3,7 @@ package handler
|
||||||
import (
|
import (
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"git.juancwu.dev/juancwu/budgit/internal/ctxkeys"
|
"git.juancwu.dev/juancwu/budgit/internal/ctxkeys"
|
||||||
"git.juancwu.dev/juancwu/budgit/internal/service"
|
"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))
|
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)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ func SetupRoutes(a *app.App) http.Handler {
|
||||||
mux.HandleFunc("POST /auth/onboarding", authRateLimiter(middleware.RequireAuth(auth.CompleteOnboarding)))
|
mux.HandleFunc("POST /auth/onboarding", authRateLimiter(middleware.RequireAuth(auth.CompleteOnboarding)))
|
||||||
|
|
||||||
mux.HandleFunc("GET /app/dashboard", middleware.RequireAuth(dashboard.DashboardPage))
|
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("GET /app/settings", middleware.RequireAuth(settings.SettingsPage))
|
||||||
mux.HandleFunc("POST /app/settings/password", authRateLimiter(middleware.RequireAuth(settings.SetPassword)))
|
mux.HandleFunc("POST /app/settings/password", authRateLimiter(middleware.RequireAuth(settings.SetPassword)))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.juancwu.dev/juancwu/budgit/internal/model"
|
"git.juancwu.dev/juancwu/budgit/internal/model"
|
||||||
"git.juancwu.dev/juancwu/budgit/internal/ui/layouts"
|
"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/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) {
|
templ Dashboard(spaces []*model.Space, totalBalance int) {
|
||||||
|
|
@ -43,11 +49,48 @@ templ Dashboard(spaces []*model.Space, totalBalance int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option to create a new space
|
// Option to create a new space
|
||||||
@card.Card(card.Props{ Class: "h-full border-dashed" }) {
|
@dialog.Dialog(dialog.Props{ID: "create-space-dialog"}) {
|
||||||
@card.Content(card.ContentProps{ Class: "h-full flex flex-col items-center justify-center py-12" }) {
|
@dialog.Trigger() {
|
||||||
<p class="text-muted-foreground mb-4">Need another space?</p>
|
@card.Card(card.Props{ Class: "h-full border-dashed cursor-pointer transition-colors hover:border-primary" }) {
|
||||||
// TODO: Add a button or link to create a new space
|
@card.Content(card.ContentProps{ Class: "h-full flex flex-col items-center justify-center py-12" }) {
|
||||||
<span class="text-sm font-medium opacity-50">Create Space (Coming Soon)</span>
|
@icon.Plus(icon.Props{Class: "h-8 w-8 text-muted-foreground mb-2"})
|
||||||
|
<p class="text-muted-foreground">Create a new space</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@dialog.Content() {
|
||||||
|
@dialog.Header() {
|
||||||
|
@dialog.Title() {
|
||||||
|
Create Space
|
||||||
|
}
|
||||||
|
@dialog.Description() {
|
||||||
|
Create a new space to organize expenses and shopping lists.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<form hx-post="/app/spaces" hx-swap="none" class="space-y-4">
|
||||||
|
@csrf.Token()
|
||||||
|
<div class="space-y-2">
|
||||||
|
@label.Label(label.Props{For: "space-name"}) {
|
||||||
|
Name
|
||||||
|
}
|
||||||
|
@input.Input(input.Props{
|
||||||
|
ID: "space-name",
|
||||||
|
Name: "name",
|
||||||
|
Type: input.TypeText,
|
||||||
|
Placeholder: "e.g. Household, Trip, Roommates",
|
||||||
|
})
|
||||||
|
</div>
|
||||||
|
@dialog.Footer() {
|
||||||
|
@dialog.Close(dialog.CloseProps{For: "create-space-dialog"}) {
|
||||||
|
@button.Button(button.Props{Variant: button.VariantOutline, Type: button.TypeButton}) {
|
||||||
|
Cancel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@button.Button(button.Props{Type: button.TypeSubmit}) {
|
||||||
|
Create
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</form>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue