WIP: Working on friction rate worksheet views.
This commit is contained in:
@@ -59,4 +59,51 @@ public typealias ComponentPressureLosses = [String: Double]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
extension ComponentPressureLoss {
|
||||
public static var mock: [Self] {
|
||||
[
|
||||
.init(
|
||||
id: UUID(0),
|
||||
projectID: UUID(0),
|
||||
name: "evaporator-coil",
|
||||
value: 0.2,
|
||||
createdAt: Date(),
|
||||
updatedAt: Date()
|
||||
),
|
||||
.init(
|
||||
id: UUID(1),
|
||||
projectID: UUID(0),
|
||||
name: "filter",
|
||||
value: 0.1,
|
||||
createdAt: Date(),
|
||||
updatedAt: Date()
|
||||
),
|
||||
.init(
|
||||
id: UUID(2),
|
||||
projectID: UUID(0),
|
||||
name: "supply-outlet",
|
||||
value: 0.03,
|
||||
createdAt: Date(),
|
||||
updatedAt: Date()
|
||||
),
|
||||
.init(
|
||||
id: UUID(3),
|
||||
projectID: UUID(0),
|
||||
name: "return-grille",
|
||||
value: 0.03,
|
||||
createdAt: Date(),
|
||||
updatedAt: Date()
|
||||
),
|
||||
.init(
|
||||
id: UUID(4),
|
||||
projectID: UUID(0),
|
||||
name: "balance-damper",
|
||||
value: 0.03,
|
||||
createdAt: Date(),
|
||||
updatedAt: Date()
|
||||
),
|
||||
]
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import Dependencies
|
||||
import Foundation
|
||||
|
||||
public struct EquipmentInfo: Codable, Equatable, Identifiable, Sendable {
|
||||
@@ -50,3 +51,16 @@ extension EquipmentInfo {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
extension EquipmentInfo {
|
||||
public static let mock = Self(
|
||||
id: UUID(0),
|
||||
projectID: UUID(0),
|
||||
heatingCFM: 1000,
|
||||
coolingCFM: 1000,
|
||||
createdAt: Date(),
|
||||
updatedAt: Date()
|
||||
)
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -89,7 +89,7 @@ extension SiteRoute.View {
|
||||
extension SiteRoute.View {
|
||||
public enum FrictionRateRoute: Equatable, Sendable {
|
||||
case index
|
||||
case form
|
||||
case form(FormType, dismiss: Bool = false)
|
||||
|
||||
static let rootPath = "friction-rate"
|
||||
|
||||
@@ -104,7 +104,18 @@ extension SiteRoute.View {
|
||||
"create"
|
||||
}
|
||||
Method.get
|
||||
Query {
|
||||
Field("type") { FormType.parser() }
|
||||
Field("dismiss", default: false) { Bool.parser() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SiteRoute.View.FrictionRateRoute {
|
||||
public enum FormType: String, CaseIterable, Codable, Equatable, Sendable {
|
||||
case equipmentInfo
|
||||
case componentPressureLoss
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,3 +51,32 @@ public struct CancelButton: HTML, Sendable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct EditButton: HTML, Sendable {
|
||||
let title: String
|
||||
let type: HTMLAttribute<HTMLTag.button>.ButtonType
|
||||
|
||||
public init(
|
||||
title: String = "Edit",
|
||||
type: HTMLAttribute<HTMLTag.button>.ButtonType = .button
|
||||
) {
|
||||
self.title = title
|
||||
self.type = type
|
||||
}
|
||||
|
||||
public var body: some HTML<HTMLTag.button> {
|
||||
button(
|
||||
.class(
|
||||
"""
|
||||
text-white font-bold text-xl bg-blue-500 hover:bg-blue-600 px-4 py-2 rounded-lg shadow-lg
|
||||
"""
|
||||
),
|
||||
.type(type)
|
||||
) {
|
||||
div(.class("flex")) {
|
||||
span(.class("pe-2")) { title }
|
||||
SVG(.squarePen)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
20
Sources/Styleguide/Label.swift
Normal file
20
Sources/Styleguide/Label.swift
Normal file
@@ -0,0 +1,20 @@
|
||||
import Elementary
|
||||
|
||||
public struct Label: HTML, Sendable {
|
||||
|
||||
let title: String
|
||||
|
||||
public init(_ title: String) {
|
||||
self.title = title
|
||||
}
|
||||
|
||||
public init(_ title: @escaping () -> String) {
|
||||
self.title = title()
|
||||
}
|
||||
|
||||
public var body: some HTML<HTMLTag.span> {
|
||||
span(.class("text-xl text-gray-400 font-bold")) {
|
||||
title
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ public struct SVG: HTML, Sendable {
|
||||
extension SVG {
|
||||
public enum Key: Sendable {
|
||||
case close
|
||||
case squarePen
|
||||
|
||||
var svg: String {
|
||||
switch self {
|
||||
@@ -23,6 +24,10 @@ extension SVG {
|
||||
return """
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x-icon lucide-x"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
||||
"""
|
||||
case .squarePen:
|
||||
return """
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-pen-icon lucide-square-pen"><path d="M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z"/></svg>
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,11 +58,28 @@ extension SiteRoute.View.FrictionRateRoute {
|
||||
return MainPage {
|
||||
FrictionRateView()
|
||||
}
|
||||
// FIX:
|
||||
default:
|
||||
return MainPage {
|
||||
FrictionRateView()
|
||||
case .form(let type, let dismiss):
|
||||
guard !dismiss else {
|
||||
return div(.id(type.id)) {}
|
||||
}
|
||||
// FIX: Forms need to reference existing items.
|
||||
switch type {
|
||||
case .equipmentInfo:
|
||||
return EquipmentForm()
|
||||
case .componentPressureLoss:
|
||||
return ComponentLossForm()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SiteRoute.View.FrictionRateRoute.FormType {
|
||||
var id: String {
|
||||
switch self {
|
||||
case .equipmentInfo:
|
||||
return "equipmentForm"
|
||||
case .componentPressureLoss:
|
||||
return "componentLossForm"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import Elementary
|
||||
import ElementaryHTMX
|
||||
import ManualDCore
|
||||
import Styleguide
|
||||
|
||||
struct ComponentLossForm: HTML, Sendable {
|
||||
var body: some HTML {
|
||||
div(
|
||||
.id("componentLossForm"),
|
||||
.class(
|
||||
"""
|
||||
fixed top-40 left-[25vw] w-1/2 z-50 text-gray-800
|
||||
bg-gray-200 border border-gray-400
|
||||
rounded-lg shadow-lg mx-10
|
||||
"""
|
||||
)
|
||||
) {
|
||||
h1(.class("text-2xl font-bold")) { "Component Loss" }
|
||||
form(.class("space-y-4 p-4")) {
|
||||
div {
|
||||
label(.for("name")) { "Name" }
|
||||
Input(id: "name", placeholder: "Name")
|
||||
.attributes(.type(.text), .required, .autofocus)
|
||||
}
|
||||
div {
|
||||
label(.for("value")) { "Value" }
|
||||
Input(id: "name", placeholder: "Pressure loss")
|
||||
.attributes(.type(.number), .min("0"), .max("1"), .step("0.1"), .required)
|
||||
}
|
||||
Row {
|
||||
div {}
|
||||
div {
|
||||
CancelButton()
|
||||
.attributes(
|
||||
.hx.get(route: .frictionRate(.form(.componentPressureLoss, dismiss: true))),
|
||||
.hx.target("#componentLossForm"),
|
||||
.hx.swap(.outerHTML)
|
||||
)
|
||||
SubmitButton()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import Elementary
|
||||
import ElementaryHTMX
|
||||
import ManualDCore
|
||||
import Styleguide
|
||||
|
||||
struct ComponentPressureLossTable: HTML, Sendable {
|
||||
|
||||
let componentPressureLosses: [ComponentPressureLoss]
|
||||
|
||||
var body: some HTML {
|
||||
div(.id("cplTable")) {
|
||||
h1(.class("text-2xl font-bold pb-4")) { "Component Pressure Losses" }
|
||||
table(
|
||||
.class(
|
||||
"w-full border-collapse border border-gray-200 table-fixed"
|
||||
)
|
||||
) {
|
||||
thead { tableHeader }
|
||||
tbody(.id("cplTableBody")) {
|
||||
Rows(componentPressureLosses: componentPressureLosses)
|
||||
}
|
||||
}
|
||||
}
|
||||
div(.id("componentLossForm")) {}
|
||||
}
|
||||
|
||||
private var tableHeader: some HTML<HTMLTag.tr> {
|
||||
tr {
|
||||
th(.class("border border-gray-200 text-xl font-bold")) { "Name" }
|
||||
th(.class("border border-gray-200 text-xl font-bold")) { "Pressure Loss" }
|
||||
th(.class("border border-gray-200 text-xl font-bold")) {
|
||||
Row {
|
||||
div {}
|
||||
button(
|
||||
.class("px-2"),
|
||||
.hx.get(route: .frictionRate(.form(.componentPressureLoss))),
|
||||
.hx.target("#componentLossForm"),
|
||||
.hx.swap(.outerHTML)
|
||||
) {
|
||||
Icon(.circlePlus)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct Rows: HTML, Sendable {
|
||||
let componentPressureLosses: [ComponentPressureLoss]
|
||||
|
||||
var body: some HTML {
|
||||
for cpl in componentPressureLosses {
|
||||
tr {
|
||||
td(.class("border border-gray-200 p-2")) { cpl.name }
|
||||
td(.class("border border-gray-200 p-2")) { "\(cpl.value)" }
|
||||
td(.class("border border-gray-200 p-2")) {
|
||||
// FIX: Add edit button
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,16 +2,28 @@ import Elementary
|
||||
import ManualDCore
|
||||
import Styleguide
|
||||
|
||||
// TODO: Have form hold onto equipment info model to edit.
|
||||
struct EquipmentForm: HTML, Sendable {
|
||||
|
||||
var body: some HTML {
|
||||
div(.id("equipmentForm")) {
|
||||
h1(.class("text-3xl font-bold pb-6")) { "Equipment Info" }
|
||||
form {
|
||||
div(
|
||||
.id("equipmentForm"),
|
||||
.class(
|
||||
"""
|
||||
fixed top-40 left-[25vw] w-1/2 z-50 text-gray-800
|
||||
bg-gray-200 border border-gray-400
|
||||
rounded-lg shadow-lg mx-10
|
||||
"""
|
||||
)
|
||||
) {
|
||||
h1(.class("text-3xl font-bold pb-6 ps-2")) { "Equipment Info" }
|
||||
form(.class("space-y-4 p-4")) {
|
||||
div {
|
||||
label(.for("staticPressure")) { "Static Pressure" }
|
||||
Input(id: "staticPressure", placeholder: "Static pressure")
|
||||
.attributes(.type(.number), .value("0.5"), .min("0"), .max("1.0"), .step("0.1"))
|
||||
.attributes(
|
||||
.type(.number), .value("0.5"), .min("0"), .max("1.0"), .step("0.1")
|
||||
)
|
||||
}
|
||||
div {
|
||||
label(.for("heatingCFM")) { "Heating CFM" }
|
||||
@@ -26,6 +38,12 @@ struct EquipmentForm: HTML, Sendable {
|
||||
Row {
|
||||
div {}
|
||||
div(.class("space-x-4")) {
|
||||
CancelButton()
|
||||
.attributes(
|
||||
.hx.get(route: .frictionRate(.form(.equipmentInfo, dismiss: true))),
|
||||
.hx.target("#equipmentForm"),
|
||||
.hx.swap(.outerHTML)
|
||||
)
|
||||
SubmitButton(title: "Save")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import Elementary
|
||||
import ManualDCore
|
||||
import Styleguide
|
||||
|
||||
struct EquipmentInfoView: HTML, Sendable {
|
||||
let equipmentInfo: EquipmentInfo
|
||||
|
||||
var body: some HTML {
|
||||
div(.class("space-y-4 border border-gray-200 rounded-lg shadow-lg p-4")) {
|
||||
Row {
|
||||
h1(.class("text-2xl font-bold")) { "Equipment Info" }
|
||||
}
|
||||
|
||||
Row {
|
||||
Label { "Static Pressure" }
|
||||
span { "\(equipmentInfo.staticPressure)" }
|
||||
}
|
||||
.attributes(.class("border-b border-gray-200"))
|
||||
|
||||
Row {
|
||||
Label { "Heating CFM" }
|
||||
span { "\(equipmentInfo.heatingCFM)" }
|
||||
}
|
||||
.attributes(.class("border-b border-gray-200"))
|
||||
|
||||
Row {
|
||||
Label { "Cooling CFM" }
|
||||
span { "\(equipmentInfo.coolingCFM)" }
|
||||
}
|
||||
.attributes(.class("border-b border-gray-200"))
|
||||
|
||||
Row {
|
||||
div {}
|
||||
EditButton()
|
||||
.attributes(
|
||||
.hx.get(route: .frictionRate(.form(.equipmentInfo))),
|
||||
.hx.target("#equipmentForm"),
|
||||
.hx.swap(.outerHTML)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
div(.id("equipmentForm")) {}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,10 @@ import Styleguide
|
||||
struct FrictionRateView: HTML, Sendable {
|
||||
|
||||
var body: some HTML {
|
||||
div {
|
||||
EquipmentForm()
|
||||
div(.class("w-full flex-1 p-4 space-y-6")) {
|
||||
h1(.class("text-4xl font-bold pb-6")) { "Friction Rate" }
|
||||
EquipmentInfoView(equipmentInfo: EquipmentInfo.mock)
|
||||
ComponentPressureLossTable(componentPressureLosses: ComponentPressureLoss.mock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user