diff --git a/internal/handler/home.go b/internal/handler/home.go index 42d6dd5..431ddb9 100644 --- a/internal/handler/home.go +++ b/internal/handler/home.go @@ -14,14 +14,14 @@ func NewHomeHandler() *homeHandler { return &homeHandler{} } -// HomePage will redirect to /auth if not authenticated or to /app/dashboard if authenticated. +// HomePage will redirect to /auth if not authenticated or to /app/spaces if authenticated. func (h *homeHandler) HomePage(w http.ResponseWriter, r *http.Request) { user := ctxkeys.User(r.Context()) if user == nil { http.Redirect(w, r, "/auth", http.StatusSeeOther) return } - http.Redirect(w, r, "/app/dashboard", http.StatusSeeOther) + http.Redirect(w, r, "/app/spaces", http.StatusSeeOther) } func (h *homeHandler) PrivacyPage(w http.ResponseWriter, r *http.Request) { diff --git a/internal/handler/home_test.go b/internal/handler/home_test.go index 8f80168..6a7e78d 100644 --- a/internal/handler/home_test.go +++ b/internal/handler/home_test.go @@ -38,5 +38,5 @@ func TestHomeHandler_HomePage_Authenticated(t *testing.T) { h.HomePage(w, req) assert.Equal(t, http.StatusSeeOther, w.Code) - assert.Equal(t, "/app/dashboard", w.Header().Get("Location")) + assert.Equal(t, "/app/spaces", w.Header().Get("Location")) } diff --git a/internal/handler/redirect.go b/internal/handler/redirect.go new file mode 100644 index 0000000..7c47547 --- /dev/null +++ b/internal/handler/redirect.go @@ -0,0 +1,13 @@ +package handler + +import "net/http" + +type redirectHandler struct{} + +func NewRedirectHandler() *redirectHandler { + return &redirectHandler{} +} + +func (h *redirectHandler) Spaces(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, "/app/spaces", http.StatusMovedPermanently) +} diff --git a/internal/handler/redirect_test.go b/internal/handler/redirect_test.go new file mode 100644 index 0000000..c1a5d0a --- /dev/null +++ b/internal/handler/redirect_test.go @@ -0,0 +1,21 @@ +package handler + +import ( + "net/http" + "net/http/httptest" + "testing" + + "git.juancwu.dev/juancwu/budgit/internal/testutil" + "github.com/stretchr/testify/assert" +) + +func TestRedirectHandler_RederictToSpaces(t *testing.T) { + h := NewRedirectHandler() + + req := testutil.NewRequest(t, http.MethodPost, "/app/dashboard", nil) + w := httptest.NewRecorder() + h.Spaces(w, req) + + assert.Equal(t, http.StatusMovedPermanently, w.Code) + assert.Equal(t, "/app/spaces", w.Header().Get("Location")) +} diff --git a/internal/handler/space.go b/internal/handler/space.go new file mode 100644 index 0000000..30a22d9 --- /dev/null +++ b/internal/handler/space.go @@ -0,0 +1,21 @@ +package handler + +import ( + "net/http" + + "git.juancwu.dev/juancwu/budgit/internal/service" + "git.juancwu.dev/juancwu/budgit/internal/ui" + "git.juancwu.dev/juancwu/budgit/internal/ui/pages" +) + +type spaceHandler struct { + spaceService *service.SpaceService +} + +func NewSpaceHandler(spaceService *service.SpaceService) *spaceHandler { + return &spaceHandler{spaceService: spaceService} +} + +func (h *spaceHandler) SpacesPage(w http.ResponseWriter, r *http.Request) { + ui.Render(w, r, pages.Spaces()) +} diff --git a/internal/routes/routes.go b/internal/routes/routes.go index 843dfab..3105eee 100644 --- a/internal/routes/routes.go +++ b/internal/routes/routes.go @@ -16,6 +16,8 @@ func SetupRoutes(a *app.App) http.Handler { authH := handler.NewAuthHandler(a.AuthService, a.InviteService, a.SpaceService) homeH := handler.NewHomeHandler() settingsH := handler.NewSettingsHandler(a.AuthService, a.UserService) + spaceH := handler.NewSpaceHandler(a.SpaceService) + redirectH := handler.NewRedirectHandler() r := router.New() @@ -43,6 +45,9 @@ func SetupRoutes(a *app.App) http.Handler { r.Get("/terms", homeH.TermsPage) r.Get("/join/{token}", authH.JoinSpace) + // Permanent redirects + r.Get("/app/dashboard", redirectH.Spaces) + // Auth - guest routes r.Group("/auth", func(g *router.Group) { g.Use(middleware.RequireGuest) @@ -69,6 +74,10 @@ func SetupRoutes(a *app.App) http.Handler { r.Group("/app", func(g *router.Group) { g.Use(middleware.RequireAuth) + g.SubGroup("/spaces", func(g *router.Group) { + g.Get("", spaceH.SpacesPage) + }) + g.SubGroup("/settings", func(g *router.Group) { g.Get("", settingsH.SettingsPage) diff --git a/internal/testutil/http.go b/internal/testutil/http.go index 91c5125..5e20511 100644 --- a/internal/testutil/http.go +++ b/internal/testutil/http.go @@ -46,6 +46,24 @@ func AuthenticatedContext(user *model.User) context.Context { func NewAuthenticatedRequest(t *testing.T, method, target string, user *model.User, formValues url.Values) *http.Request { t.Helper() + req := NewRequest(t, method, target, formValues) + ctx := AuthenticatedContext(user) + req = req.WithContext(ctx) + + return req +} + +// NewHTMXRequest adds HX-Request header to a request. +func NewHTMXRequest(req *http.Request) *http.Request { + req.Header.Set("HX-Request", "true") + return req +} + +// NewRequest creates an HTTP request. +// CSRF token is automatically added to form values for POST requests. +func NewRequest(t *testing.T, method, target string, formValues url.Values) *http.Request { + t.Helper() + var req *http.Request if method == http.MethodPost || method == http.MethodPut || method == http.MethodPatch || method == http.MethodDelete { @@ -60,14 +78,5 @@ func NewAuthenticatedRequest(t *testing.T, method, target string, user *model.Us req = httptest.NewRequest(method, target, nil) } - ctx := AuthenticatedContext(user) - req = req.WithContext(ctx) - - return req -} - -// NewHTMXRequest adds HX-Request header to a request. -func NewHTMXRequest(req *http.Request) *http.Request { - req.Header.Set("HX-Request", "true") return req } diff --git a/internal/ui/pages/spaces.templ b/internal/ui/pages/spaces.templ new file mode 100644 index 0000000..d2c90db --- /dev/null +++ b/internal/ui/pages/spaces.templ @@ -0,0 +1,9 @@ +package pages + +import "git.juancwu.dev/juancwu/budgit/internal/ui/layouts" + +templ Spaces() { + @layouts.App("Dashboard") { +
Spaces
+ } +}