From e02e0e517679483caef8a44df2ed04b568acbf2b Mon Sep 17 00:00:00 2001 From: juancwu Date: Wed, 14 Jan 2026 02:04:23 +0000 Subject: [PATCH] add app route layout --- internal/ui/layouts/app.templ | 205 ++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 internal/ui/layouts/app.templ diff --git a/internal/ui/layouts/app.templ b/internal/ui/layouts/app.templ new file mode 100644 index 0000000..1586b0b --- /dev/null +++ b/internal/ui/layouts/app.templ @@ -0,0 +1,205 @@ +package layouts + +import ( + "git.juancwu.dev/juancwu/budgit/internal/ctxkeys" + "git.juancwu.dev/juancwu/budgit/internal/model" + "git.juancwu.dev/juancwu/budgit/internal/ui/blocks" + "git.juancwu.dev/juancwu/budgit/internal/ui/components/avatar" + "git.juancwu.dev/juancwu/budgit/internal/ui/components/breadcrumb" + "git.juancwu.dev/juancwu/budgit/internal/ui/components/csrf" + "git.juancwu.dev/juancwu/budgit/internal/ui/components/dropdown" + "git.juancwu.dev/juancwu/budgit/internal/ui/components/icon" + "git.juancwu.dev/juancwu/budgit/internal/ui/components/sidebar" + "strings" +) + +templ App(title string) { + {{ cfg := ctxkeys.Config(ctx) }} + @Base(SEOProps{ + Title: title, + Description: "Application Dashboard", + Path: ctxkeys.URLPath(ctx), + }) { + @sidebar.Layout() { + @sidebar.Sidebar() { + @sidebar.Header() { + @sidebar.Menu() { + @sidebar.MenuItem() { + @sidebar.MenuButton(sidebar.MenuButtonProps{ + Size: sidebar.MenuButtonSizeLg, + Href: "/app/dashboard", + }) { + @icon.LayoutDashboard() +
+ { cfg.AppName } + Workspace +
+ } + } + } + } + @sidebar.Content() { + @sidebar.Group() { + @sidebar.GroupLabel() { + Overview + } + @sidebar.Menu() { + @sidebar.MenuItem() { + @sidebar.MenuButton(sidebar.MenuButtonProps{ + Href: "/app/dashboard", + IsActive: ctxkeys.URLPath(ctx) == "/app/dashboard", + Tooltip: "Dashboard", + }) { + @icon.House(icon.Props{Class: "size-4"}) + Dashboard + } + } + @sidebar.MenuItem() { + @sidebar.MenuButton(sidebar.MenuButtonProps{ + Href: "/app/goals", + IsActive: ctxkeys.URLPath(ctx) == "/app/goals", + Tooltip: "Goals", + }) { + @icon.FolderOpen(icon.Props{Class: "size-4"}) + Goals + } + } + } + } + } + @sidebar.Footer() { + @sidebar.Menu() { + @sidebar.MenuItem() { + @sidebar.MenuButton(sidebar.MenuButtonProps{ + Href: "/docs", + Size: sidebar.MenuButtonSizeSm, + Attributes: templ.Attributes{ + "target": "_blank", + "rel": "noopener noreferrer", + }, + }) { + @icon.BookOpen(icon.Props{Class: "size-4"}) + Documentation + } + } + @sidebar.MenuItem() { + @sidebar.MenuButton(sidebar.MenuButtonProps{ + Href: "mailto:" + cfg.SupportEmail, + Size: sidebar.MenuButtonSizeSm, + }) { + @icon.MessageCircleQuestionMark(icon.Props{Class: "size-4"}) + Support + } + } + } + @sidebar.Separator() + @sidebar.Menu() { + @sidebar.MenuItem() { + {{ user := ctxkeys.User(ctx) }} + {{ profile := ctxkeys.Profile(ctx) }} + if user != nil && profile != nil { + @AppSidebarDropdown(user, profile) + } + } + } + } + } + @sidebar.Inset() { + // Top Navigation Bar +
+
+
+ @sidebar.Trigger() + @breadcrumb.Breadcrumb() { + @breadcrumb.List() { + @breadcrumb.Item() { + @breadcrumb.Page() { + { title } + } + } + } + } +
+
+ @blocks.ThemeSwitcher() +
+
+
+ // App Content +
+ { children... } +
+ } + } + } +} + +templ AppSidebarDropdown(user *model.User, profile *model.Profile) { + @dropdown.Dropdown() { + @dropdown.Trigger() { + @sidebar.MenuButton(sidebar.MenuButtonProps{ + Size: sidebar.MenuButtonSizeLg, + }) { + + + @icon.ChevronsUpDown(icon.Props{Class: "ml-auto size-4"}) + } + } + @dropdown.Content(dropdown.ContentProps{ + Class: "w-56", + Placement: dropdown.PlacementTopStart, + }) { + + @dropdown.Separator() + @dropdown.Item(dropdown.ItemProps{ + Href: "/app/settings", + }) { + + @icon.Settings(icon.Props{Size: 16, Class: "mr-2"}) + Settings + + } + @dropdown.Item(dropdown.ItemProps{ + Href: "/app/billing", + }) { + + @icon.CreditCard(icon.Props{Size: 16, Class: "mr-2"}) + Billing + + } + @dropdown.Separator() +
+ @csrf.Token() + @dropdown.Item(dropdown.ItemProps{ + Attributes: templ.Attributes{ + "type": "submit", + }, + }) { + + @icon.LogOut(icon.Props{Size: 16, Class: "mr-2"}) + Log out + + } +
+ } + } +}