fix: Fixes database going out of scope when rendering project view.
This commit is contained in:
@@ -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] {
|
||||
|
||||
@@ -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<C: HTML>(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Inner: HTML>: 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<V: Sendable, E: Error, ValueView: HTML>(
|
||||
_ projectID: Project.ID,
|
||||
catching: @escaping @Sendable () async throws(E) -> V,
|
||||
onSuccess: @escaping @Sendable (V) -> ValueView
|
||||
) async -> ResultView<V, E, _ModifiedTaskLocal<Project.ID, ValueView>, ErrorView<E>>
|
||||
where
|
||||
ValueView: Sendable, E: Sendable
|
||||
{
|
||||
await .init(
|
||||
result: .init(catching: catching),
|
||||
onSuccess: { result in
|
||||
onSuccess(result)
|
||||
.environment(ProjectViewValue.$projectID, projectID)
|
||||
}
|
||||
// func resultView<V: Sendable, E: Error, ValueView: HTML>(
|
||||
// _ projectID: Project.ID,
|
||||
// catching: @escaping @Sendable () async throws(E) -> V,
|
||||
// onSuccess: @escaping @Sendable (V) -> ValueView
|
||||
// ) async -> ResultView<V, E, _ModifiedTaskLocal<Project.ID, ValueView>, ErrorView<E>>
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user