diff --git a/Public/css/output.css b/Public/css/output.css index 82a9555..82f9d63 100644 --- a/Public/css/output.css +++ b/Public/css/output.css @@ -2209,6 +2209,9 @@ .collapse { visibility: collapse; } + .invisible { + visibility: hidden; + } .visible { visibility: visible; } @@ -5581,6 +5584,12 @@ .ms-4 { margin-inline-start: calc(var(--spacing) * 4); } + .ms-6 { + margin-inline-start: calc(var(--spacing) * 6); + } + .ms-8 { + margin-inline-start: calc(var(--spacing) * 8); + } .me-4 { margin-inline-end: calc(var(--spacing) * 4); } @@ -6391,14 +6400,6 @@ width: calc(var(--spacing) * 7); height: calc(var(--spacing) * 7); } - .size-\[50px\] { - width: 50px; - height: 50px; - } - .size-\[150px\] { - width: 150px; - height: 150px; - } .status-lg { @layer daisyui.l1.l2 { width: calc(0.25rem * 3); @@ -6444,9 +6445,6 @@ .h-\[1em\] { height: 1em; } - .h-\[50px\] { - height: 50px; - } .h-auto { height: auto; } @@ -6599,12 +6597,6 @@ .w-64 { width: calc(var(--spacing) * 64); } - .w-\[50px\] { - width: 50px; - } - .w-\[80px\] { - width: 80px; - } .w-auto { width: auto; } @@ -7920,6 +7912,9 @@ .ps-2 { padding-inline-start: calc(var(--spacing) * 2); } + .ps-8 { + padding-inline-start: calc(var(--spacing) * 8); + } .file-input-xl { @layer daisyui.l1.l2 { padding-inline-end: calc(0.25rem * 6); @@ -9477,13 +9472,6 @@ } } } - .hover\:bg-gray-900 { - &:hover { - @media (hover: hover) { - background-color: var(--color-gray-900); - } - } - } .hover\:bg-neutral { &:hover { @media (hover: hover) { @@ -9491,13 +9479,6 @@ } } } - .hover\:bg-neutral-content { - &:hover { - @media (hover: hover) { - background-color: var(--color-neutral-content); - } - } - } .hover\:bg-red-600 { &:hover { @media (hover: hover) { @@ -9505,20 +9486,6 @@ } } } - .hover\:bg-success { - &:hover { - @media (hover: hover) { - background-color: var(--color-success); - } - } - } - .hover\:text-black { - &:hover { - @media (hover: hover) { - color: var(--color-black); - } - } - } .hover\:text-white { &:hover { @media (hover: hover) { @@ -9552,31 +9519,21 @@ outline-color: var(--color-indigo-600); } } - .data-active\:bg-gray-900 { - &[data-active] { - background-color: var(--color-gray-900); - } - } .data-active\:bg-neutral { &[data-active] { background-color: var(--color-neutral); } } - .data-active\:bg-neutral-content { - &[data-active] { - background-color: var(--color-neutral-content); - } - } - .data-active\:text-black { - &[data-active] { - color: var(--color-black); - } - } .data-active\:text-white { &[data-active] { color: var(--color-white); } } + .md\:grid-cols-1 { + @media (width >= 48rem) { + grid-template-columns: repeat(1, minmax(0, 1fr)); + } + } .md\:grid-cols-2 { @media (width >= 48rem) { grid-template-columns: repeat(2, minmax(0, 1fr)); @@ -9635,6 +9592,26 @@ } } } + .lg\:visible { + @media (width >= 64rem) { + visibility: visible; + } + } + .lg\:block { + @media (width >= 64rem) { + display: block; + } + } + .lg\:inline-block { + @media (width >= 64rem) { + display: inline-block; + } + } + .lg\:table-cell { + @media (width >= 64rem) { + display: table-cell; + } + } .lg\:grid-cols-2 { @media (width >= 64rem) { grid-template-columns: repeat(2, minmax(0, 1fr)); @@ -9645,62 +9622,34 @@ grid-template-columns: repeat(3, minmax(0, 1fr)); } } + .xl\:visible { + @media (width >= 80rem) { + visibility: visible; + } + } .xl\:table-cell { @media (width >= 80rem) { display: table-cell; } } + .xl\:grid-cols-2 { + @media (width >= 80rem) { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + } + .xl\:grid-cols-3 { + @media (width >= 80rem) { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + } .\32 xl\:table-cell { @media (width >= 96rem) { display: table-cell; } } - .hover\:dark\:bg-gray-900 { - &:hover { - @media (hover: hover) { - @media (prefers-color-scheme: dark) { - background-color: var(--color-gray-900); - } - } - } - } - .hover\:dark\:bg-neutral { - &:hover { - @media (hover: hover) { - @media (prefers-color-scheme: dark) { - background-color: var(--color-neutral); - } - } - } - } - .hover\:dark\:text-white { - &:hover { - @media (hover: hover) { - @media (prefers-color-scheme: dark) { - color: var(--color-white); - } - } - } - } - .data-active\:dark\:bg-gray-900 { - &[data-active] { - @media (prefers-color-scheme: dark) { - background-color: var(--color-gray-900); - } - } - } - .data-active\:dark\:bg-neutral { - &[data-active] { - @media (prefers-color-scheme: dark) { - background-color: var(--color-neutral); - } - } - } - .data-active\:dark\:text-white { - &[data-active] { - @media (prefers-color-scheme: dark) { - color: var(--color-white); - } + .dark\:text-white { + @media (prefers-color-scheme: dark) { + color: var(--color-white); } } .is-drawer-close\:tooltip { @@ -9836,12 +9785,6 @@ overflow: visible; } } - .is-drawer-close\:text-sm { - &:where(.drawer-toggle:not(:checked) ~ .drawer-side, .drawer-toggle:not(:checked) ~ .drawer-side *) { - font-size: var(--text-sm); - line-height: var(--tw-leading, var(--text-sm--line-height)); - } - } .is-drawer-close\:text-error { &:where(.drawer-toggle:not(:checked) ~ .drawer-side, .drawer-toggle:not(:checked) ~ .drawer-side *) { color: var(--color-error); @@ -9857,11 +9800,6 @@ display: flex; } } - .is-drawer-open\:hidden { - &:where(.drawer-toggle:checked ~ .drawer-side, .drawer-toggle:checked ~ .drawer-side *) { - display: none; - } - } .is-drawer-open\:w-64 { &:where(.drawer-toggle:checked ~ .drawer-side, .drawer-toggle:checked ~ .drawer-side *) { width: calc(var(--spacing) * 64); @@ -9872,6 +9810,11 @@ max-width: 300px; } } + .is-drawer-open\:min-w-\[340px\] { + &:where(.drawer-toggle:checked ~ .drawer-side, .drawer-toggle:checked ~ .drawer-side *) { + min-width: 340px; + } + } .is-drawer-open\:justify-between { &:where(.drawer-toggle:checked ~ .drawer-side, .drawer-toggle:checked ~ .drawer-side *) { justify-content: space-between; @@ -9882,15 +9825,6 @@ justify-content: flex-start; } } - .is-drawer-open\:space-x-2 { - &:where(.drawer-toggle:checked ~ .drawer-side, .drawer-toggle:checked ~ .drawer-side *) { - :where(& > :not(:last-child)) { - --tw-space-x-reverse: 0; - margin-inline-start: calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse)); - margin-inline-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse))); - } - } - } .is-drawer-open\:space-x-4 { &:where(.drawer-toggle:checked ~ .drawer-side, .drawer-toggle:checked ~ .drawer-side *) { :where(& > :not(:last-child)) { @@ -9900,12 +9834,6 @@ } } } - .is-drawer-open\:text-xl { - &:where(.drawer-toggle:checked ~ .drawer-side, .drawer-toggle:checked ~ .drawer-side *) { - font-size: var(--text-xl); - line-height: var(--tw-leading, var(--text-xl--line-height)); - } - } } @layer base { :where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light] { diff --git a/Sources/ViewController/Live.swift b/Sources/ViewController/Live.swift index f375825..22c29e6 100644 --- a/Sources/ViewController/Live.swift +++ b/Sources/ViewController/Live.swift @@ -134,10 +134,18 @@ extension SiteRoute.View.ProjectRoute { let user = try request.currentUser() let project = try await database.projects.create(user.id, form) try await database.componentLoss.createDefaults(projectID: project.id) - return project.id - - } onSuccess: { projectID in - ProjectView(projectID: projectID, activeTab: .rooms) + let rooms = try await database.rooms.fetch(project.id) + let shr = try await database.projects.getSensibleHeatRatio(project.id) + let completedSteps = try await database.projects.getCompletedSteps(project.id) + return (project.id, rooms, shr, completedSteps) + } onSuccess: { (projectID, rooms, shr, completedSteps) in + ProjectView( + projectID: projectID, + activeTab: .rooms, + completedSteps: completedSteps + ) { + RoomsView(rooms: rooms, sensibleHeatRatio: shr) + } } case .delete(let id): @@ -148,18 +156,15 @@ extension SiteRoute.View.ProjectRoute { } case .update(let id, let form): - return await ResultView { - try await database.projects.update(id, form).id - } onSuccess: { projectID in - return ProjectView(projectID: projectID, activeTab: .project) + return await projectView(on: request, projectID: id) { + _ = try await database.projects.update(id, form) } case .detail(let projectID, let route): switch route { case .index(let tab): - return request.view { - ProjectView(projectID: projectID, activeTab: tab) - } + // FIX: Handle tab + return await projectView(on: request, projectID: projectID) case .componentLoss(let route): return await route.renderView(on: request, projectID: projectID) case .ductSizing(let route): @@ -169,7 +174,7 @@ extension SiteRoute.View.ProjectRoute { case .equivalentLength(let route): return await route.renderView(on: request, projectID: projectID) case .frictionRate(let route): - return route.renderView(on: request, projectID: projectID) + return await route.renderView(on: request, projectID: projectID) case .rooms(let route): return await route.renderView(on: request, projectID: projectID) } @@ -177,6 +182,31 @@ extension SiteRoute.View.ProjectRoute { } + func projectView( + on request: ViewController.Request, + projectID: Project.ID, + catching: @escaping @Sendable () async throws -> Void = {} + ) async -> AnySendableHTML { + @Dependency(\.database) var database + + return await request.view { + await ResultView { + try await catching() + guard let project = try await database.projects.get(projectID) else { + throw NotFoundError() + } + return ( + try await database.projects.getCompletedSteps(project.id), + project + ) + } onSuccess: { (steps, project) in + ProjectView(projectID: projectID, activeTab: .project, completedSteps: steps) { + ProjectDetail(project: project) + } + } + } + } + } extension SiteRoute.View.ProjectRoute.EquipmentInfoRoute { @@ -188,26 +218,48 @@ extension SiteRoute.View.ProjectRoute.EquipmentInfoRoute { switch self { case .index: - return request.view { - ProjectView(projectID: projectID, activeTab: .equipment) - } + return await equipmentView(on: request, projectID: projectID) + + // TODO: Remove form route, not needed. case .form(let dismiss): return await ResultView { try await database.equipment.fetch(projectID) } onSuccess: { equipment in EquipmentInfoForm(dismiss: dismiss, projectID: projectID, equipmentInfo: equipment) } + case .submit(let form): return await ResultView { try await database.equipment.create(form) } onSuccess: { equipment in EquipmentInfoView(equipmentInfo: equipment, projectID: projectID) } + case .update(let id, let updates): - return await ResultView { - try await database.equipment.update(id, updates) - } onSuccess: { equipment in - EquipmentInfoView(equipmentInfo: equipment, projectID: projectID) + return await equipmentView(on: request, projectID: projectID) { + _ = try await database.equipment.update(id, updates) + } + } + } + + func equipmentView( + on request: ViewController.Request, + projectID: Project.ID, + catching: @escaping @Sendable () async throws -> Void = {} + ) async -> AnySendableHTML { + @Dependency(\.database) var database + + return await request.view { + await ResultView { + try await catching() + return ( + try await database.projects.getCompletedSteps(projectID), + try await database.equipment.fetch(projectID) + ) + } onSuccess: { (steps, equipment) in + ProjectView(projectID: projectID, activeTab: .equipment, completedSteps: steps) { + EquipmentInfoView(equipmentInfo: equipment, projectID: projectID) + } } } } @@ -239,37 +291,47 @@ extension SiteRoute.View.ProjectRoute.RoomRoute { } case .index: - return request.view { - ProjectView(projectID: projectID, activeTab: .rooms) - } + return await roomsView(on: request, projectID: projectID) case .submit(let form): - return await request.view { - await ResultView { - request.logger.debug("New room form submitted.") - // FIX: Just return a room row?? - let _ = try await database.rooms.create(form) - } onSuccess: { - ProjectView(projectID: projectID, activeTab: .rooms) - } + // FIX: Just return a room row. + return await roomsView(on: request, projectID: projectID) { + _ = try await database.rooms.create(form) } case .update(let id, let form): - return await ResultView { - let _ = try await database.rooms.update(id, form) - } onSuccess: { - ProjectView(projectID: projectID, activeTab: .rooms) + return await roomsView(on: request, projectID: projectID) { + _ = try await database.rooms.update(id, form) } case .updateSensibleHeatRatio(let form): - return await request.view { - await ResultView { - let _ = try await database.projects.update( - form.projectID, - .init(sensibleHeatRatio: form.sensibleHeatRatio) - ) - } onSuccess: { - ProjectView(projectID: projectID, activeTab: .rooms) + return await roomsView(on: request, projectID: projectID) { + _ = try await database.projects.update( + form.projectID, + .init(sensibleHeatRatio: form.sensibleHeatRatio) + ) + } + } + } + + func roomsView( + on request: ViewController.Request, + projectID: Project.ID, + catching: @escaping @Sendable () async throws -> Void = {} + ) async -> AnySendableHTML { + @Dependency(\.database) var database + + return await request.view { + await ResultView { + try await catching() + return ( + try await database.projects.getCompletedSteps(projectID), + try await database.rooms.fetch(projectID), + try await database.projects.getSensibleHeatRatio(projectID) + ) + } onSuccess: { (steps, rooms, shr) in + ProjectView(projectID: projectID, activeTab: .rooms, completedSteps: steps) { + RoomsView(rooms: rooms, sensibleHeatRatio: shr) } } } @@ -280,13 +342,13 @@ extension SiteRoute.View.ProjectRoute.FrictionRateRoute { func renderView( on request: ViewController.Request, projectID: Project.ID - ) -> AnySendableHTML { + ) async -> AnySendableHTML { + @Dependency(\.database) var database + @Dependency(\.manualD) var manualD switch self { case .index: - return request.view { - ProjectView(projectID: projectID, activeTab: .frictionRate) - } + return await view(on: request, projectID: projectID) case .form(let type, let dismiss): // FIX: Forms need to reference existing items. @@ -299,6 +361,45 @@ extension SiteRoute.View.ProjectRoute.FrictionRateRoute { } } } + + func view( + on request: ViewController.Request, + projectID: Project.ID, + catching: @escaping @Sendable () async throws -> Void = {} + ) async -> AnySendableHTML { + + @Dependency(\.database) var database + @Dependency(\.manualD) var manualD + + return await request.view { + await ResultView { + let equipment = try await database.equipment.fetch(projectID) + let componentLosses = try await database.componentLoss.fetch(projectID) + let lengths = try await database.effectiveLength.fetchMax(projectID) + + return ( + try await database.projects.getCompletedSteps(projectID), + componentLosses, + lengths, + try await manualD.frictionRate( + equipmentInfo: equipment, + componentLosses: componentLosses, + effectiveLength: lengths + ) + ) + } onSuccess: { (steps, losses, lengths, frictionRate) in + ProjectView(projectID: projectID, activeTab: .frictionRate, completedSteps: steps) { + FrictionRateView( + componentLosses: losses, + equivalentLengths: lengths, + frictionRateResponse: frictionRate + ) + } + + } + } + } + } extension SiteRoute.View.ProjectRoute.ComponentLossRoute { @@ -313,26 +414,61 @@ extension SiteRoute.View.ProjectRoute.ComponentLossRoute { case .index: return EmptyHTML() case .delete(let id): - return await ResultView { + return await view(on: request, projectID: projectID) { _ = try await database.componentLoss.delete(id) - } onSuccess: { - EmptyHTML() } - // return EmptyHTML() case .submit(let form): - return await ResultView { + return await view(on: request, projectID: projectID) { _ = try await database.componentLoss.create(form) - } onSuccess: { - ProjectView(projectID: projectID, activeTab: .frictionRate) } + case .update(let id, let form): - return await ResultView { + return await view(on: request, projectID: projectID) { _ = try await database.componentLoss.update(id, form) - } onSuccess: { - ProjectView(projectID: projectID, activeTab: .frictionRate) } } } + + func view( + on request: ViewController.Request, + projectID: Project.ID, + catching: @escaping @Sendable () async throws -> Void = {} + ) async -> AnySendableHTML { + + @Dependency(\.database) var database + @Dependency(\.manualD) var manualD + + return await request.view { + await ResultView { + try await catching() + + let equipment = try await database.equipment.fetch(projectID) + let componentLosses = try await database.componentLoss.fetch(projectID) + let lengths = try await database.effectiveLength.fetchMax(projectID) + + return ( + try await database.projects.getCompletedSteps(projectID), + componentLosses, + lengths, + try await manualD.frictionRate( + equipmentInfo: equipment, + componentLosses: componentLosses, + effectiveLength: lengths + ) + ) + } onSuccess: { (steps, losses, lengths, frictionRate) in + ProjectView(projectID: projectID, activeTab: .frictionRate, completedSteps: steps) { + FrictionRateView( + componentLosses: losses, + equivalentLengths: lengths, + frictionRateResponse: frictionRate + ) + } + + } + } + } + } extension SiteRoute.View.ProjectRoute.FrictionRateRoute.FormType { @@ -362,9 +498,8 @@ extension SiteRoute.View.ProjectRoute.EquivalentLengthRoute { } case .index: - return request.view { - ProjectView(projectID: projectID, activeTab: .equivalentLength) - } + return await self.view(on: request, projectID: projectID) + case .form(let dismiss): return EffectiveLengthForm(projectID: projectID, dismiss: dismiss) @@ -378,10 +513,8 @@ extension SiteRoute.View.ProjectRoute.EquivalentLengthRoute { } case .update(let id, let form): - return await ResultView { + return await view(on: request, projectID: projectID) { _ = try await database.effectiveLength.update(id, .init(form: form, projectID: projectID)) - } onSuccess: { - ProjectView(projectID: projectID, activeTab: .equivalentLength) } case .submit(let step): @@ -414,19 +547,38 @@ extension SiteRoute.View.ProjectRoute.EquivalentLengthRoute { ) } case .three(let stepThree): - return await ResultView { - request.logger.debug("ViewController: Got step three: \(stepThree)") - try stepThree.validate() + return await view(on: request, projectID: projectID) { _ = try await database.effectiveLength.create( - .init(form: stepThree, projectID: projectID)) - } onSuccess: { - ProjectView(projectID: projectID, activeTab: .equivalentLength) + .init(form: stepThree, projectID: projectID) + ) } } } } + + func view( + on request: ViewController.Request, + projectID: Project.ID, + catching: @escaping @Sendable () async throws -> Void = {} + ) async -> AnySendableHTML { + @Dependency(\.database) var database + return await request.view { + await ResultView { + try await catching() + return ( + try await database.projects.getCompletedSteps(projectID), + try await database.effectiveLength.fetch(projectID) + ) + } onSuccess: { (steps, equivalentLengths) in + ProjectView(projectID: projectID, activeTab: .equivalentLength, completedSteps: steps) { + EffectiveLengthsView(effectiveLengths: equivalentLengths) + .environment(ProjectViewValue.$projectID, projectID) + } + } + } + } } extension SiteRoute.View.ProjectRoute.DuctSizingRoute { @@ -440,9 +592,7 @@ extension SiteRoute.View.ProjectRoute.DuctSizingRoute { switch self { case .index: - return request.view { - ProjectView(projectID: projectID, activeTab: .ductSizing, logger: request.logger) - } + return await view(on: request, projectID: projectID) case .deleteRectangularSize(let roomID, let rectangularSizeID): return await ResultView { @@ -472,6 +622,26 @@ extension SiteRoute.View.ProjectRoute.DuctSizingRoute { } } } + + func view( + on request: ViewController.Request, + projectID: Project.ID, + catching: @escaping @Sendable () async throws -> Void = {} + ) async -> AnySendableHTML { + @Dependency(\.database) var database + + return await ResultView { + try await catching() + return ( + try await database.projects.getCompletedSteps(projectID), + try await database.calculateDuctSizes(projectID: projectID) + ) + } onSuccess: { (steps, rooms) in + ProjectView(projectID: projectID, activeTab: .ductSizing, completedSteps: steps) { + DuctSizingView(rooms: rooms) + } + } + } } private func _render( diff --git a/Sources/ViewController/Views/DuctSizing/DuctSizingView.swift b/Sources/ViewController/Views/DuctSizing/DuctSizingView.swift index 078094f..0789013 100644 --- a/Sources/ViewController/Views/DuctSizing/DuctSizingView.swift +++ b/Sources/ViewController/Views/DuctSizing/DuctSizingView.swift @@ -80,15 +80,14 @@ struct DuctSizingView: HTML, Sendable { td(.class("hidden 2xl:table-cell")) { Number(room.heatingCFM, digits: 0) } td(.class("hidden 2xl:table-cell")) { Number(room.coolingCFM, digits: 0) } td { - Number(room.designCFM.value, digits: 0) - .attributes( - .class("badge badge-outline badge-\(room.designCFM.color) text-xl font-bold")) + Badge(number: room.designCFM.value, digits: 0) + .attributes(.class("badge-\(room.designCFM.color)")) } - td(.class("hidden 2xl:table-cell")) { Number(room.roundSize, digits: 0) } + td(.class("hidden 2xl:table-cell")) { Number(room.roundSize, digits: 1) } td { Number(room.velocity) } td { - Number(room.finalSize) - .attributes(.class("badge badge-outline badge-secondary text-xl font-bold")) + Badge(number: room.finalSize) + .attributes(.class("badge-secondary")) } td { Number(room.flexSize) diff --git a/Sources/ViewController/Views/FrictionRate/FrictionRateView.swift b/Sources/ViewController/Views/FrictionRate/FrictionRateView.swift index 34c007d..d047315 100644 --- a/Sources/ViewController/Views/FrictionRate/FrictionRateView.swift +++ b/Sources/ViewController/Views/FrictionRate/FrictionRateView.swift @@ -9,10 +9,8 @@ struct FrictionRateView: HTML, Sendable { @Environment(ProjectViewValue.$projectID) var projectID - let equipmentInfo: EquipmentInfo? let componentLosses: [ComponentPressureLoss] let equivalentLengths: EffectiveLength.MaxContainer - // let projectID: Project.ID let frictionRateResponse: ManualDClient.FrictionRateResponse? var availableStaticPressure: Double? { @@ -98,12 +96,9 @@ struct FrictionRateView: HTML, Sendable { div(.class("divider")) {} - // div(.class("grid grid-cols-1 lg:grid-cols-2 gap-4")) { - // EquipmentInfoView(equipmentInfo: equipmentInfo, projectID: projectID) ComponentPressureLossesView( componentPressureLosses: componentLosses, projectID: projectID ) - // } } } } diff --git a/Sources/ViewController/Views/Project/ProjectView.swift b/Sources/ViewController/Views/Project/ProjectView.swift index 514650d..fb1d431 100644 --- a/Sources/ViewController/Views/Project/ProjectView.swift +++ b/Sources/ViewController/Views/Project/ProjectView.swift @@ -11,22 +11,23 @@ enum ProjectViewValue { @TaskLocal static var projectID = Project.ID(0) } -struct ProjectView: HTML, Sendable { - @Dependency(\.database) var database - @Dependency(\.manualD) var manualD +struct ProjectView: HTML, Sendable where Inner: Sendable { let projectID: Project.ID let activeTab: SiteRoute.View.ProjectRoute.DetailRoute.Tab - let logger: Logger? + let inner: Inner + let completedSteps: Project.CompletedSteps init( projectID: Project.ID, activeTab: SiteRoute.View.ProjectRoute.DetailRoute.Tab, - logger: Logger? = nil + completedSteps: Project.CompletedSteps, + @HTMLBuilder content: () -> Inner ) { self.projectID = projectID self.activeTab = activeTab - self.logger = logger + self.inner = content() + self.completedSteps = completedSteps } var body: some HTML { @@ -38,97 +39,118 @@ struct ProjectView: HTML, Sendable { div(.class("drawer-content")) { Navbar(sidebarToggle: true) div(.class("p-4")) { - switch self.activeTab { - case .project: - await resultView(projectID) { - guard let project = try await database.projects.get(projectID) else { - throw NotFoundError() - } - return project - } onSuccess: { project in - ProjectDetail(project: project) - } - case .equipment: - await resultView(projectID) { - try await database.equipment.fetch(projectID) - } onSuccess: { equipment in - EquipmentInfoView(equipmentInfo: equipment, projectID: projectID) - } - // FIX: - // div { "Fix Me" } - case .rooms: - await resultView(projectID) { - try await ( - database.rooms.fetch(projectID), - database.projects.getSensibleHeatRatio(projectID) - ) - } onSuccess: { (rooms, shr) in - RoomsView(rooms: rooms, sensibleHeatRatio: shr) - } - - case .equivalentLength: - await resultView(projectID) { - try await database.effectiveLength.fetch(projectID) - } onSuccess: { - EffectiveLengthsView(effectiveLengths: $0) - } - case .frictionRate: - - await resultView(projectID) { - - let equipmentInfo = try await database.equipment.fetch(projectID) - let componentLosses = try await database.componentLoss.fetch(projectID) - let equivalentLengths = try await database.effectiveLength.fetchMax(projectID) - let frictionRateResponse = try await manualD.frictionRate( - equipmentInfo: equipmentInfo, - componentLosses: componentLosses, - effectiveLength: equivalentLengths - ) - return ( - equipmentInfo, componentLosses, equivalentLengths, frictionRateResponse - ) - } onSuccess: { - FrictionRateView( - equipmentInfo: $0.0, - componentLosses: $0.1, - equivalentLengths: $0.2, - frictionRateResponse: $0.3 - ) - } - case .ductSizing: - await resultView(projectID) { - try await database.calculateDuctSizes(projectID: projectID) - } onSuccess: { - DuctSizingView(rooms: $0) - } - } + inner + .environment(ProjectViewValue.$projectID, projectID) + // switch self.activeTab { + // case .project: + // await resultView(projectID) { + // guard let project = try await database.projects.get(projectID) else { + // throw NotFoundError() + // } + // return project + // } onSuccess: { project in + // ProjectDetail(project: project) + // } + // case .equipment: + // await resultView(projectID) { + // try await database.equipment.fetch(projectID) + // } onSuccess: { equipment in + // EquipmentInfoView(equipmentInfo: equipment, projectID: projectID) + // } + // // FIX: + // // div { "Fix Me" } + // case .rooms: + // await resultView(projectID) { + // try await ( + // database.rooms.fetch(projectID), + // database.projects.getSensibleHeatRatio(projectID) + // ) + // } onSuccess: { (rooms, shr) in + // RoomsView(rooms: rooms, sensibleHeatRatio: shr) + // } + // + // case .equivalentLength: + // await resultView(projectID) { + // try await database.effectiveLength.fetch(projectID) + // } onSuccess: { + // EffectiveLengthsView(effectiveLengths: $0) + // } + // case .frictionRate: + // + // await resultView(projectID) { + // + // let equipmentInfo = try await database.equipment.fetch(projectID) + // let componentLosses = try await database.componentLoss.fetch(projectID) + // let equivalentLengths = try await database.effectiveLength.fetchMax(projectID) + // let frictionRateResponse = try await manualD.frictionRate( + // equipmentInfo: equipmentInfo, + // componentLosses: componentLosses, + // effectiveLength: equivalentLengths + // ) + // return ( + // equipmentInfo, componentLosses, equivalentLengths, frictionRateResponse + // ) + // } onSuccess: { + // FrictionRateView( + // equipmentInfo: $0.0, + // componentLosses: $0.1, + // equivalentLengths: $0.2, + // frictionRateResponse: $0.3 + // ) + // } + // case .ductSizing: + // await resultView(projectID) { + // try await database.calculateDuctSizes(projectID: projectID) + // } onSuccess: { + // DuctSizingView(rooms: $0) + // } + // } } } - try await Sidebar( + Sidebar( active: activeTab, projectID: projectID, - completedSteps: database.projects.getCompletedSteps(projectID) + completedSteps: completedSteps ) } } } - func resultView( - _ projectID: Project.ID, - catching: @escaping @Sendable () async throws(E) -> V, - onSuccess: @escaping @Sendable (V) -> ValueView - ) async -> ResultView, ErrorView> - where - ValueView: Sendable, E: Sendable - { - await .init( - result: .init(catching: catching), - onSuccess: { result in - onSuccess(result) - .environment(ProjectViewValue.$projectID, projectID) - } + // func resultView( + // _ projectID: Project.ID, + // catching: @escaping @Sendable () async throws(E) -> V, + // onSuccess: @escaping @Sendable (V) -> ValueView + // ) async -> ResultView, ErrorView> + // where + // ValueView: Sendable, E: Sendable + // { + // await .init( + // result: .init(catching: catching), + // onSuccess: { result in + // onSuccess(result) + // .environment(ProjectViewValue.$projectID, projectID) + // } + // ) + // } +} + +// TODO: Remove +extension ProjectView where Inner == EmptyHTML { + init( + projectID: Project.ID, + activeTab: SiteRoute.View.ProjectRoute.DetailRoute.Tab, + completedSteps: Project.CompletedSteps = .init( + equipmentInfo: false, + rooms: false, + equivalentLength: false, + frictionRate: false ) + ) { + self.projectID = projectID + self.activeTab = activeTab + self.inner = EmptyHTML() + self.completedSteps = completedSteps } }