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
+
+ // 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.Label() {
+
+ { profile.Name }
+ { user.Email }
+
+ }
+
+ @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()
+
+ }
+ }
+}