WIP: Changes main page to not include sidebar, that moves to project view.

This commit is contained in:
2026-01-03 16:24:53 -05:00
parent 1d155546ae
commit 1aeb6144d5
15 changed files with 383 additions and 236 deletions

View File

@@ -1,19 +1,21 @@
import Elementary
public struct MainPage<Inner: HTML>: SendableHTMLDocument where Inner: Sendable {
public var title: String { "Manual-D" }
public var lang: String { "en" }
let inner: Inner
let activeTab: Sidebar.ActiveTab
let showSidebar: Bool
// let activeTab: Sidebar.ActiveTab
// let showSidebar: Bool
init(
active activeTab: Sidebar.ActiveTab,
showSidebar: Bool = true,
// active activeTab: Sidebar.ActiveTab,
// showSidebar: Bool = true,
_ inner: () -> Inner
) {
self.activeTab = activeTab
self.showSidebar = showSidebar
// self.activeTab = activeTab
// self.showSidebar = showSidebar
self.inner = inner()
}
@@ -27,16 +29,8 @@ public struct MainPage<Inner: HTML>: SendableHTMLDocument where Inner: Sendable
}
public var body: some HTML {
// div(.class("bg-white dark:bg-gray-800 dark:text-white")) {
div {
div(.class("flex flex-row")) {
if showSidebar {
Sidebar(active: activeTab)
}
main(.class("flex flex-col h-screen w-full px-6 py-10")) {
inner
}
}
inner
}
script(.src("https://unpkg.com/lucide@latest")) {}
script {

View File

@@ -0,0 +1,59 @@
import Elementary
import ElementaryHTMX
import ManualDCore
import Styleguide
struct ProjectDetail: HTML, Sendable {
let project: Project
var body: some HTML {
div(
.class(
"""
border border-gray-200 rounded-lg shadow-lg space-y-4 p-4 m-4
"""
)
) {
Row {
h1(.class("text-2xl font-bold")) { "Project" }
EditButton()
.attributes(
.hx.get(route: .project(.form(dismiss: false))),
.hx.target("#projectForm"),
.hx.swap(.outerHTML)
)
}
Row {
Label("Name")
span { project.name }
}
.attributes(.class("border-b border-gray-200"))
Row {
Label("Address")
span { project.streetAddress }
}
.attributes(.class("border-b border-gray-200"))
Row {
Label("City")
span { project.city }
}
.attributes(.class("border-b border-gray-200"))
Row {
Label("State")
span { project.state }
}
.attributes(.class("border-b border-gray-200"))
Row {
Label("Zip")
span { project.zipCode }
}
}
div(.id("projectForm")) {}
}
}

View File

@@ -19,7 +19,11 @@ struct ProjectForm: HTML, Sendable {
var body: some HTML {
ModalForm(id: "projectForm", dismiss: dismiss) {
h1(.class("text-3xl font-bold pb-6 ps-2")) { "Project" }
form(.class("space-y-4 p-4")) {
form(
.class("space-y-4 p-4"),
.method(.post),
.action(route: .project(.index))
) {
div {
label(.for("name")) { "Name" }
Input(id: "name", placeholder: "Name")

View File

@@ -3,57 +3,113 @@ import ElementaryHTMX
import ManualDCore
import Styleguide
struct ProjectView: HTML, Sendable {
let project: Project
struct ProjectView<Inner: HTML>: HTML, Sendable where Inner: Sendable {
let projectID: Project.ID
let activeTab: Sidebar.ActiveTab
let inner: Inner
init(
projectID: Project.ID,
activeTab: Sidebar.ActiveTab,
@HTMLBuilder inner: () -> Inner
) {
self.projectID = projectID
self.activeTab = activeTab
self.inner = inner()
}
var body: some HTML {
div(
div {
div(.class("flex flex-row")) {
Sidebar(active: activeTab, projectID: projectID)
main(.class("flex flex-col h-screen w-full px-6 py-10")) {
inner
}
}
}
}
}
// TODO: Update to use DaisyUI drawer.
struct Sidebar: HTML {
let active: ActiveTab
let projectID: Project.ID
var body: some HTML {
aside(
.class(
"""
border border-gray-200 rounded-lg shadow-lg space-y-4 p-4 m-4
h-screen sticky top-0 max-w-[280px] flex-none
border-r-2 border-gray-200
shadow-lg
"""
)
) {
Row {
h1(.class("text-2xl font-bold")) { "Project" }
EditButton()
.attributes(
.hx.get(route: .project(.form(dismiss: false))),
.hx.target("#projectForm"),
.hx.swap(.outerHTML)
)
}
// TODO: Move somewhere outside of the sidebar.
Row {
Label("Name")
span { project.name }
Label("Theme")
input(.type(.checkbox), .class("toggle theme-controller"), .value("light"))
}
.attributes(.class("border-b border-gray-200"))
.attributes(.class("p-4"))
Row {
Label("Address")
span { project.streetAddress }
}
.attributes(.class("border-b border-gray-200"))
row(title: "Project", icon: .mapPin, route: .project(.index))
.attributes(.data("active", value: active == .projects ? "true" : "false"))
Row {
Label("City")
span { project.city }
}
.attributes(.class("border-b border-gray-200"))
row(title: "Rooms", icon: .doorClosed, route: .room(.index(projectID)))
.attributes(.data("active", value: active == .rooms ? "true" : "false"))
Row {
Label("State")
span { project.state }
}
.attributes(.class("border-b border-gray-200"))
row(title: "Equivalent Lengths", icon: .rulerDimensionLine, route: .effectiveLength(.index))
.attributes(.data("active", value: active == .effectiveLength ? "true" : "false"))
Row {
Label("Zip")
span { project.zipCode }
row(title: "Friction Rate", icon: .squareFunction, route: .frictionRate(.index))
.attributes(.data("active", value: active == .frictionRate ? "true" : "false"))
row(title: "Duct Sizes", icon: .wind, href: "#")
.attributes(.data("active", value: active == .ductSizing ? "true" : "false"))
}
}
// TODO: Use SiteRoute.View routes as href.
private func row(
title: String,
icon: Icon.Key,
href: String
) -> some HTML<HTMLTag.a> {
a(
.class(
"""
flex w-full items-center gap-4
hover:bg-gray-300 hover:text-gray-800
data-[active=true]:bg-gray-300 data-[active=true]:text-gray-800
px-4 py-2
"""
),
.href(href)
) {
Icon(icon)
span(.class("text-xl")) {
title
}
}
}
div(.id("projectForm")) {}
private func row(
title: String,
icon: Icon.Key,
route: SiteRoute.View
) -> some HTML<HTMLTag.a> {
row(title: title, icon: icon, href: SiteRoute.View.router.path(for: route))
}
}
extension Sidebar {
enum ActiveTab: Equatable, Sendable {
case projects
case rooms
case effectiveLength
case frictionRate
case ductSizing
}
}

View File

@@ -24,7 +24,10 @@ struct ProjectsTable: HTML, Sendable {
.data("tip", value: "Add project")
) {
button(
.class("btn btn-primary w-[40px] text-2xl")
.class("btn btn-primary w-[40px] text-2xl"),
.hx.get(route: .project(.form(dismiss: false))),
.hx.target("#projectForm"),
.hx.swap(.outerHTML)
) {
"+"
}
@@ -39,6 +42,7 @@ struct ProjectsTable: HTML, Sendable {
th { Label("Date") }
th { Label("Name") }
th { Label("Address") }
th {}
}
}
tbody {
@@ -46,6 +50,8 @@ struct ProjectsTable: HTML, Sendable {
}
}
}
ProjectForm(dismiss: true)
}
}
}
@@ -57,9 +63,15 @@ extension ProjectsTable {
var body: some HTML {
for project in projects.items {
tr(.id("\(project.id)")) {
td { "\(project.createdAt)" }
td { DateView(project.createdAt) }
td { "\(project.name)" }
td { "\(project.streetAddress)" }
td {
a(
.class("btn btn-success"),
.href(route: .project(.detail(project.id)))
) { ">" }
}
}
}
// Have a row that when revealed fetches the next page,

View File

@@ -1,86 +0,0 @@
import Elementary
import ManualDCore
import Styleguide
// TODO: Update to use DaisyUI drawer.
struct Sidebar: HTML {
let active: ActiveTab
var body: some HTML {
aside(
.class(
"""
h-screen sticky top-0 max-w-[280px] flex-none
border-r-2 border-gray-200
shadow-lg
"""
)
) {
// TODO: Move somewhere outside of the sidebar.
Row {
Label("Theme")
input(.type(.checkbox), .class("toggle theme-controller"), .value("light"))
}
.attributes(.class("p-4"))
row(title: "Project", icon: .mapPin, route: .project(.index))
.attributes(.data("active", value: active == .projects ? "true" : "false"))
row(title: "Rooms", icon: .doorClosed, route: .room(.index))
.attributes(.data("active", value: active == .rooms ? "true" : "false"))
row(title: "Equivalent Lengths", icon: .rulerDimensionLine, route: .effectiveLength(.index))
.attributes(.data("active", value: active == .effectiveLength ? "true" : "false"))
row(title: "Friction Rate", icon: .squareFunction, route: .frictionRate(.index))
.attributes(.data("active", value: active == .frictionRate ? "true" : "false"))
row(title: "Duct Sizes", icon: .wind, href: "#")
.attributes(.data("active", value: active == .ductSizing ? "true" : "false"))
}
}
// TODO: Use SiteRoute.View routes as href.
private func row(
title: String,
icon: Icon.Key,
href: String
) -> some HTML<HTMLTag.a> {
a(
.class(
"""
flex w-full items-center gap-4
hover:bg-gray-300 hover:text-gray-800
data-[active=true]:bg-gray-300 data-[active=true]:text-gray-800
px-4 py-2
"""
),
.href(href)
) {
Icon(icon)
span(.class("text-xl")) {
title
}
}
}
private func row(
title: String,
icon: Icon.Key,
route: SiteRoute.View
) -> some HTML<HTMLTag.a> {
row(title: title, icon: icon, href: SiteRoute.View.router.path(for: route))
}
}
extension Sidebar {
enum ActiveTab: Equatable, Sendable {
case projects
case rooms
case effectiveLength
case frictionRate
case ductSizing
}
}