Compare commits
2 Commits
57766c990e
...
f2c79ad56f
| Author | SHA1 | Date | |
|---|---|---|---|
|
f2c79ad56f
|
|||
|
728a6c3000
|
@@ -30,6 +30,10 @@ enum RoomRowType {
|
|||||||
|
|
||||||
struct RoomCreateParser: ParserPrinter {
|
struct RoomCreateParser: ParserPrinter {
|
||||||
|
|
||||||
|
// FIX: The delegated to field won't work here, as we potentially have not created
|
||||||
|
// the room yet, so we will need an intermediate representation for the csv data
|
||||||
|
// that uses a room's name or disregard and require user to delegate airflow in
|
||||||
|
// the ui.
|
||||||
var body: some ParserPrinter<Substring.UTF8View, Room.Create> {
|
var body: some ParserPrinter<Substring.UTF8View, Room.Create> {
|
||||||
ParsePrint {
|
ParsePrint {
|
||||||
Prefix { $0 != UInt8(ascii: ",") }.map(.string)
|
Prefix { $0 != UInt8(ascii: ",") }.map(.string)
|
||||||
@@ -45,6 +49,10 @@ struct RoomCreateParser: ParserPrinter {
|
|||||||
}
|
}
|
||||||
",".utf8
|
",".utf8
|
||||||
Int.parser()
|
Int.parser()
|
||||||
|
",".utf8
|
||||||
|
Optionally {
|
||||||
|
Room.ID.parser()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.map(.memberwise(Room.Create.init))
|
.map(.memberwise(Room.Create.init))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ extension Room.Create {
|
|||||||
heatingLoad: heatingLoad,
|
heatingLoad: heatingLoad,
|
||||||
coolingLoad: coolingLoad,
|
coolingLoad: coolingLoad,
|
||||||
registerCount: registerCount,
|
registerCount: registerCount,
|
||||||
|
delegetedToID: delegatedTo,
|
||||||
projectID: projectID
|
projectID: projectID
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -105,6 +106,7 @@ extension Room {
|
|||||||
.field("heatingLoad", .double, .required)
|
.field("heatingLoad", .double, .required)
|
||||||
.field("coolingLoad", .dictionary, .required)
|
.field("coolingLoad", .dictionary, .required)
|
||||||
.field("registerCount", .int8, .required)
|
.field("registerCount", .int8, .required)
|
||||||
|
.field("delegatedToID", .uuid, .references(RoomModel.schema, "id"))
|
||||||
.field("rectangularSizes", .array)
|
.field("rectangularSizes", .array)
|
||||||
.field("createdAt", .datetime)
|
.field("createdAt", .datetime)
|
||||||
.field("updatedAt", .datetime)
|
.field("updatedAt", .datetime)
|
||||||
@@ -140,6 +142,9 @@ final class RoomModel: Model, @unchecked Sendable, Validatable {
|
|||||||
@Field(key: "registerCount")
|
@Field(key: "registerCount")
|
||||||
var registerCount: Int
|
var registerCount: Int
|
||||||
|
|
||||||
|
@OptionalParent(key: "delegatedToID")
|
||||||
|
var room: RoomModel?
|
||||||
|
|
||||||
@Field(key: "rectangularSizes")
|
@Field(key: "rectangularSizes")
|
||||||
var rectangularSizes: [Room.RectangularSize]?
|
var rectangularSizes: [Room.RectangularSize]?
|
||||||
|
|
||||||
@@ -160,6 +165,7 @@ final class RoomModel: Model, @unchecked Sendable, Validatable {
|
|||||||
heatingLoad: Double,
|
heatingLoad: Double,
|
||||||
coolingLoad: Room.CoolingLoad,
|
coolingLoad: Room.CoolingLoad,
|
||||||
registerCount: Int,
|
registerCount: Int,
|
||||||
|
delegetedToID: UUID? = nil,
|
||||||
rectangularSizes: [Room.RectangularSize]? = nil,
|
rectangularSizes: [Room.RectangularSize]? = nil,
|
||||||
createdAt: Date? = nil,
|
createdAt: Date? = nil,
|
||||||
updatedAt: Date? = nil,
|
updatedAt: Date? = nil,
|
||||||
@@ -170,6 +176,7 @@ final class RoomModel: Model, @unchecked Sendable, Validatable {
|
|||||||
self.heatingLoad = heatingLoad
|
self.heatingLoad = heatingLoad
|
||||||
self.coolingLoad = coolingLoad
|
self.coolingLoad = coolingLoad
|
||||||
self.registerCount = registerCount
|
self.registerCount = registerCount
|
||||||
|
$room.id = delegetedToID
|
||||||
self.rectangularSizes = rectangularSizes
|
self.rectangularSizes = rectangularSizes
|
||||||
self.createdAt = createdAt
|
self.createdAt = createdAt
|
||||||
self.updatedAt = updatedAt
|
self.updatedAt = updatedAt
|
||||||
@@ -184,6 +191,7 @@ final class RoomModel: Model, @unchecked Sendable, Validatable {
|
|||||||
heatingLoad: heatingLoad,
|
heatingLoad: heatingLoad,
|
||||||
coolingLoad: coolingLoad,
|
coolingLoad: coolingLoad,
|
||||||
registerCount: registerCount,
|
registerCount: registerCount,
|
||||||
|
delegatedTo: $room.id,
|
||||||
rectangularSizes: rectangularSizes,
|
rectangularSizes: rectangularSizes,
|
||||||
createdAt: createdAt!,
|
createdAt: createdAt!,
|
||||||
updatedAt: updatedAt!
|
updatedAt: updatedAt!
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import Foundation
|
|||||||
/// room, the number of registers in the room, and any rectangular
|
/// room, the number of registers in the room, and any rectangular
|
||||||
/// duct size calculations stored for the room.
|
/// duct size calculations stored for the room.
|
||||||
public struct Room: Codable, Equatable, Identifiable, Sendable {
|
public struct Room: Codable, Equatable, Identifiable, Sendable {
|
||||||
|
|
||||||
/// The unique id of the room.
|
/// The unique id of the room.
|
||||||
public let id: UUID
|
public let id: UUID
|
||||||
|
|
||||||
@@ -24,6 +25,10 @@ public struct Room: Codable, Equatable, Identifiable, Sendable {
|
|||||||
|
|
||||||
/// The number of registers for the room.
|
/// The number of registers for the room.
|
||||||
public let registerCount: Int
|
public let registerCount: Int
|
||||||
|
|
||||||
|
/// An optional room that the airflow is delegated to.
|
||||||
|
public let delegatedTo: Room.ID?
|
||||||
|
|
||||||
/// The rectangular duct size calculations for a room.
|
/// The rectangular duct size calculations for a room.
|
||||||
///
|
///
|
||||||
/// **NOTE:** These are optionally set after the round sizes have been calculate
|
/// **NOTE:** These are optionally set after the round sizes have been calculate
|
||||||
@@ -43,6 +48,7 @@ public struct Room: Codable, Equatable, Identifiable, Sendable {
|
|||||||
heatingLoad: Double,
|
heatingLoad: Double,
|
||||||
coolingLoad: CoolingLoad,
|
coolingLoad: CoolingLoad,
|
||||||
registerCount: Int = 1,
|
registerCount: Int = 1,
|
||||||
|
delegatedTo: Room.ID? = nil,
|
||||||
rectangularSizes: [RectangularSize]? = nil,
|
rectangularSizes: [RectangularSize]? = nil,
|
||||||
createdAt: Date,
|
createdAt: Date,
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
@@ -53,6 +59,7 @@ public struct Room: Codable, Equatable, Identifiable, Sendable {
|
|||||||
self.heatingLoad = heatingLoad
|
self.heatingLoad = heatingLoad
|
||||||
self.coolingLoad = coolingLoad
|
self.coolingLoad = coolingLoad
|
||||||
self.registerCount = registerCount
|
self.registerCount = registerCount
|
||||||
|
self.delegatedTo = delegatedTo
|
||||||
self.rectangularSizes = rectangularSizes
|
self.rectangularSizes = rectangularSizes
|
||||||
self.createdAt = createdAt
|
self.createdAt = createdAt
|
||||||
self.updatedAt = updatedAt
|
self.updatedAt = updatedAt
|
||||||
@@ -98,15 +105,22 @@ extension Room {
|
|||||||
public struct Create: Codable, Equatable, Sendable {
|
public struct Create: Codable, Equatable, Sendable {
|
||||||
/// A unique name for the room in the project.
|
/// A unique name for the room in the project.
|
||||||
public let name: String
|
public let name: String
|
||||||
|
|
||||||
/// The heating load required for the room (from Manual-J).
|
/// The heating load required for the room (from Manual-J).
|
||||||
public let heatingLoad: Double
|
public let heatingLoad: Double
|
||||||
|
|
||||||
/// The total cooling load required for the room (from Manual-J).
|
/// The total cooling load required for the room (from Manual-J).
|
||||||
public let coolingTotal: Double?
|
public let coolingTotal: Double?
|
||||||
|
|
||||||
/// An optional sensible cooling load for the room.
|
/// An optional sensible cooling load for the room.
|
||||||
public let coolingSensible: Double?
|
public let coolingSensible: Double?
|
||||||
|
|
||||||
/// The number of registers for the room.
|
/// The number of registers for the room.
|
||||||
public let registerCount: Int
|
public let registerCount: Int
|
||||||
|
|
||||||
|
/// An optional room that this room delegates it's airflow to.
|
||||||
|
public let delegatedTo: Room.ID?
|
||||||
|
|
||||||
public var coolingLoad: Room.CoolingLoad {
|
public var coolingLoad: Room.CoolingLoad {
|
||||||
.init(total: coolingTotal, sensible: coolingSensible)
|
.init(total: coolingTotal, sensible: coolingSensible)
|
||||||
}
|
}
|
||||||
@@ -116,13 +130,15 @@ extension Room {
|
|||||||
heatingLoad: Double,
|
heatingLoad: Double,
|
||||||
coolingTotal: Double? = nil,
|
coolingTotal: Double? = nil,
|
||||||
coolingSensible: Double? = nil,
|
coolingSensible: Double? = nil,
|
||||||
registerCount: Int = 1
|
registerCount: Int = 1,
|
||||||
|
delegatedTo: Room.ID? = nil
|
||||||
) {
|
) {
|
||||||
self.name = name
|
self.name = name
|
||||||
self.heatingLoad = heatingLoad
|
self.heatingLoad = heatingLoad
|
||||||
self.coolingTotal = coolingTotal
|
self.coolingTotal = coolingTotal
|
||||||
self.coolingSensible = coolingSensible
|
self.coolingSensible = coolingSensible
|
||||||
self.registerCount = registerCount
|
self.registerCount = registerCount
|
||||||
|
self.delegatedTo = delegatedTo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -233,7 +233,6 @@ extension SiteRoute.View.ProjectRoute {
|
|||||||
Method.post
|
Method.post
|
||||||
Body {
|
Body {
|
||||||
FormData {
|
FormData {
|
||||||
// Field("projectID") { Project.ID.parser() }
|
|
||||||
Field("name", .string)
|
Field("name", .string)
|
||||||
Field("heatingLoad") { Double.parser() }
|
Field("heatingLoad") { Double.parser() }
|
||||||
Optionally {
|
Optionally {
|
||||||
@@ -243,6 +242,9 @@ extension SiteRoute.View.ProjectRoute {
|
|||||||
Field("coolingSensible") { Double.parser() }
|
Field("coolingSensible") { Double.parser() }
|
||||||
}
|
}
|
||||||
Field("registerCount") { Digits() }
|
Field("registerCount") { Digits() }
|
||||||
|
Optionally {
|
||||||
|
Field("delegatedTo") { Room.ID.parser() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.map(.memberwise(Room.Create.init))
|
.map(.memberwise(Room.Create.init))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ extension SVG {
|
|||||||
case doorClosed
|
case doorClosed
|
||||||
case email
|
case email
|
||||||
case fan
|
case fan
|
||||||
|
case filePlusCorner
|
||||||
case key
|
case key
|
||||||
case mapPin
|
case mapPin
|
||||||
case rulerDimensionLine
|
case rulerDimensionLine
|
||||||
@@ -94,6 +95,10 @@ extension SVG {
|
|||||||
return """
|
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-fan-icon lucide-fan"><path d="M10.827 16.379a6.082 6.082 0 0 1-8.618-7.002l5.412 1.45a6.082 6.082 0 0 1 7.002-8.618l-1.45 5.412a6.082 6.082 0 0 1 8.618 7.002l-5.412-1.45a6.082 6.082 0 0 1-7.002 8.618l1.45-5.412Z"/><path d="M12 12v.01"/></svg>
|
<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-fan-icon lucide-fan"><path d="M10.827 16.379a6.082 6.082 0 0 1-8.618-7.002l5.412 1.45a6.082 6.082 0 0 1 7.002-8.618l-1.45 5.412a6.082 6.082 0 0 1 8.618 7.002l-5.412-1.45a6.082 6.082 0 0 1-7.002 8.618l1.45-5.412Z"/><path d="M12 12v.01"/></svg>
|
||||||
"""
|
"""
|
||||||
|
case .filePlusCorner:
|
||||||
|
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-file-plus-corner-icon lucide-file-plus-corner"><path d="M11.35 22H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v5.35"/><path d="M14 2v5a1 1 0 0 0 1 1h5"/><path d="M14 19h6"/><path d="M17 16v6"/></svg>
|
||||||
|
"""
|
||||||
case .key:
|
case .key:
|
||||||
return """
|
return """
|
||||||
<svg class="h-[1em] opacity-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
<svg class="h-[1em] opacity-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
|||||||
61
Sources/Styleguide/Select.swift
Normal file
61
Sources/Styleguide/Select.swift
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import Elementary
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// NOTE: This does not have the 'select' class added to it, because it's generally
|
||||||
|
/// added to the label of the field.
|
||||||
|
public struct Select<Label, Element>: HTML where Label: HTML {
|
||||||
|
|
||||||
|
let label: @Sendable (Element) -> Label
|
||||||
|
let value: @Sendable (Element) -> String
|
||||||
|
let selected: @Sendable (Element) -> Bool
|
||||||
|
let items: [Element]
|
||||||
|
let placeholder: String?
|
||||||
|
|
||||||
|
public init(
|
||||||
|
_ items: [Element],
|
||||||
|
placeholder: String? = nil,
|
||||||
|
value: @escaping @Sendable (Element) -> String,
|
||||||
|
selected: @escaping @Sendable (Element) -> Bool = { _ in false },
|
||||||
|
@HTMLBuilder label: @escaping @Sendable (Element) -> Label
|
||||||
|
) {
|
||||||
|
self.label = label
|
||||||
|
self.items = items
|
||||||
|
self.placeholder = placeholder
|
||||||
|
self.selected = selected
|
||||||
|
self.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
public var body: some HTML<HTMLTag.select> {
|
||||||
|
select {
|
||||||
|
if let placeholder {
|
||||||
|
option(.selected, .disabled) { placeholder }
|
||||||
|
}
|
||||||
|
for item in items {
|
||||||
|
option(.value(value(item))) { label(item) }
|
||||||
|
.attributes(.selected, when: selected(item))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Select: Sendable where Element: Sendable, Label: Sendable {}
|
||||||
|
|
||||||
|
extension Select where Element: Identifiable, Element.ID == UUID, Element: Sendable {
|
||||||
|
|
||||||
|
public init(
|
||||||
|
_ items: [Element],
|
||||||
|
placeholder: String? = nil,
|
||||||
|
selected: @escaping @Sendable (Element) -> Bool = { _ in false },
|
||||||
|
@HTMLBuilder label: @escaping @Sendable (Element) -> Label
|
||||||
|
) {
|
||||||
|
self.init(
|
||||||
|
items,
|
||||||
|
placeholder: placeholder,
|
||||||
|
value: { $0.id.uuidString },
|
||||||
|
selected: selected,
|
||||||
|
label: label
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -108,6 +108,7 @@ public struct MainPage<Inner: HTML>: SendableHTMLDocument where Inner: Sendable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.attributes(.data("theme", value: theme?.rawValue ?? "default"), when: theme != nil)
|
.attributes(.data("theme", value: theme?.rawValue ?? "default"), when: theme != nil)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import Foundation
|
|||||||
import ManualDCore
|
import ManualDCore
|
||||||
import Styleguide
|
import Styleguide
|
||||||
|
|
||||||
// TODO: Need to hold the project ID in hidden input field.
|
|
||||||
struct RoomForm: HTML, Sendable {
|
struct RoomForm: HTML, Sendable {
|
||||||
|
|
||||||
static func id(_ room: Room? = nil) -> String {
|
static func id(_ room: Room? = nil) -> String {
|
||||||
@@ -17,14 +16,17 @@ struct RoomForm: HTML, Sendable {
|
|||||||
let dismiss: Bool
|
let dismiss: Bool
|
||||||
let projectID: Project.ID
|
let projectID: Project.ID
|
||||||
let room: Room?
|
let room: Room?
|
||||||
|
let rooms: [Room]
|
||||||
|
|
||||||
init(
|
init(
|
||||||
dismiss: Bool,
|
dismiss: Bool,
|
||||||
projectID: Project.ID,
|
projectID: Project.ID,
|
||||||
|
rooms: [Room],
|
||||||
room: Room? = nil
|
room: Room? = nil
|
||||||
) {
|
) {
|
||||||
self.dismiss = dismiss
|
self.dismiss = dismiss
|
||||||
self.projectID = projectID
|
self.projectID = projectID
|
||||||
|
self.rooms = rooms
|
||||||
self.room = room
|
self.room = room
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,6 +40,7 @@ struct RoomForm: HTML, Sendable {
|
|||||||
var body: some HTML {
|
var body: some HTML {
|
||||||
ModalForm(id: Self.id(room), dismiss: dismiss) {
|
ModalForm(id: Self.id(room), dismiss: dismiss) {
|
||||||
h1(.class("text-3xl font-bold pb-6")) { "Room" }
|
h1(.class("text-3xl font-bold pb-6")) { "Room" }
|
||||||
|
|
||||||
form(
|
form(
|
||||||
.class("grid grid-cols-1 gap-4"),
|
.class("grid grid-cols-1 gap-4"),
|
||||||
room == nil
|
room == nil
|
||||||
@@ -97,12 +100,32 @@ struct RoomForm: HTML, Sendable {
|
|||||||
.type(.number),
|
.type(.number),
|
||||||
.min("1"),
|
.min("1"),
|
||||||
.required,
|
.required,
|
||||||
.value(room?.registerCount ?? 1)
|
.value(room?.registerCount ?? 1),
|
||||||
|
.id("registerCount")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
label(.class("select w-full"), .id("delegateToSelect")) {
|
||||||
|
span(.class("label")) { "Room" }
|
||||||
|
Select(rooms, placeholder: "Delegate Airflow") {
|
||||||
|
$0.name
|
||||||
|
}
|
||||||
|
.attributes(.name("delegatedTo"))
|
||||||
|
}
|
||||||
SubmitButton()
|
SubmitButton()
|
||||||
.attributes(.class("btn-block"))
|
.attributes(.class("btn-block"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
script {
|
||||||
|
"""
|
||||||
|
function myClick() {
|
||||||
|
console.log('clicked');
|
||||||
|
const simple = document.getElementById('simple');
|
||||||
|
console.log(simple.style.display);
|
||||||
|
simple.style.display = 'block';
|
||||||
|
console.log(simple.style.display);
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import Styleguide
|
|||||||
|
|
||||||
struct RoomsView: HTML, Sendable {
|
struct RoomsView: HTML, Sendable {
|
||||||
@Environment(ProjectViewValue.$projectID) var projectID
|
@Environment(ProjectViewValue.$projectID) var projectID
|
||||||
// let projectID: Project.ID
|
|
||||||
let rooms: [Room]
|
let rooms: [Room]
|
||||||
let sensibleHeatRatio: Double?
|
let sensibleHeatRatio: Double?
|
||||||
|
|
||||||
@@ -18,6 +17,11 @@ struct RoomsView: HTML, Sendable {
|
|||||||
|
|
||||||
var body: some HTML {
|
var body: some HTML {
|
||||||
div(.class("flex w-full flex-col")) {
|
div(.class("flex w-full flex-col")) {
|
||||||
|
input(.type(.checkbox), .name("delegateToCheckbox"), .on(.click, "showElement('simple');"))
|
||||||
|
div(.style("display: none;"), .id("simple"), .class("hidden")) {
|
||||||
|
"This is hidden"
|
||||||
|
}
|
||||||
|
|
||||||
PageTitleRow {
|
PageTitleRow {
|
||||||
div(.class("flex grid grid-cols-3 w-full gap-y-4")) {
|
div(.class("flex grid grid-cols-3 w-full gap-y-4")) {
|
||||||
|
|
||||||
@@ -25,7 +29,7 @@ struct RoomsView: HTML, Sendable {
|
|||||||
PageTitle { "Room Loads" }
|
PageTitle { "Room Loads" }
|
||||||
}
|
}
|
||||||
|
|
||||||
div(.class("flex justify-end grow")) {
|
div(.class("flex justify-end grow space-x-4")) {
|
||||||
Tooltip("Set sensible heat ratio", position: .left) {
|
Tooltip("Set sensible heat ratio", position: .left) {
|
||||||
button(
|
button(
|
||||||
.class(
|
.class(
|
||||||
@@ -50,17 +54,6 @@ struct RoomsView: HTML, Sendable {
|
|||||||
}
|
}
|
||||||
.attributes(.class("tooltip-open"), when: sensibleHeatRatio == nil)
|
.attributes(.class("tooltip-open"), when: sensibleHeatRatio == nil)
|
||||||
|
|
||||||
Tooltip("Upload csv file", position: .left) {
|
|
||||||
form(
|
|
||||||
.hx.post(csvRoute),
|
|
||||||
.hx.target("body"),
|
|
||||||
.hx.swap(.outerHTML),
|
|
||||||
.custom(name: "enctype", value: "multipart/form-data")
|
|
||||||
) {
|
|
||||||
input(.type(.file), .name("file"), .accept(".csv"))
|
|
||||||
SubmitButton()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div(.class("flex items-end space-x-4 font-bold")) {
|
div(.class("flex items-end space-x-4 font-bold")) {
|
||||||
@@ -115,7 +108,17 @@ struct RoomsView: HTML, Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
th {
|
th {
|
||||||
div(.class("flex justify-end me-2")) {
|
div(.class("flex justify-end me-2 space-x-4")) {
|
||||||
|
|
||||||
|
Tooltip("Upload CSV", position: .left) {
|
||||||
|
button(
|
||||||
|
.class("btn btn-secondary"),
|
||||||
|
.showModal(id: UploadCSVForm.id)
|
||||||
|
) {
|
||||||
|
SVG(.filePlusCorner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Tooltip("Add Room") {
|
Tooltip("Add Room") {
|
||||||
PlusButton()
|
PlusButton()
|
||||||
.attributes(
|
.attributes(
|
||||||
@@ -124,22 +127,25 @@ struct RoomsView: HTML, Sendable {
|
|||||||
)
|
)
|
||||||
.attributes(.class("tooltip-left"))
|
.attributes(.class("tooltip-left"))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tbody {
|
tbody {
|
||||||
for room in rooms {
|
for room in rooms {
|
||||||
RoomRow(room: room, shr: sensibleHeatRatio)
|
RoomRow(room: room, shr: sensibleHeatRatio, rooms: rooms)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RoomForm(dismiss: true, projectID: projectID, room: nil)
|
RoomForm(dismiss: true, projectID: projectID, rooms: rooms, room: nil)
|
||||||
|
UploadCSVForm(dismiss: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct RoomRow: HTML, Sendable {
|
public struct RoomRow: HTML, Sendable {
|
||||||
|
|
||||||
|
let rooms: [Room]
|
||||||
let room: Room
|
let room: Room
|
||||||
let shr: Double
|
let shr: Double
|
||||||
|
|
||||||
@@ -151,9 +157,10 @@ struct RoomsView: HTML, Sendable {
|
|||||||
// return value
|
// return value
|
||||||
}
|
}
|
||||||
|
|
||||||
init(room: Room, shr: Double?) {
|
init(room: Room, shr: Double?, rooms: [Room]) {
|
||||||
self.room = room
|
self.room = room
|
||||||
self.shr = shr ?? 1.0
|
self.shr = shr ?? 1.0
|
||||||
|
self.rooms = rooms
|
||||||
}
|
}
|
||||||
|
|
||||||
public var body: some HTML {
|
public var body: some HTML {
|
||||||
@@ -208,6 +215,7 @@ struct RoomsView: HTML, Sendable {
|
|||||||
RoomForm(
|
RoomForm(
|
||||||
dismiss: true,
|
dismiss: true,
|
||||||
projectID: room.projectID,
|
projectID: room.projectID,
|
||||||
|
rooms: rooms,
|
||||||
room: room
|
room: room
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -257,4 +265,39 @@ struct RoomsView: HTML, Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct UploadCSVForm: HTML {
|
||||||
|
static let id = "uploadCSV"
|
||||||
|
|
||||||
|
@Environment(ProjectViewValue.$projectID) var projectID
|
||||||
|
let dismiss: Bool
|
||||||
|
|
||||||
|
private var route: String {
|
||||||
|
SiteRoute.router.path(for: .view(.project(.detail(projectID, .rooms(.index)))))
|
||||||
|
.appendingPath("csv")
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some HTML {
|
||||||
|
ModalForm(id: Self.id, dismiss: dismiss) {
|
||||||
|
div(.class("pb-6 space-y-3")) {
|
||||||
|
h1(.class("text-3xl font-bold")) { "Upload CSV" }
|
||||||
|
p(.class("text-sm italic")) {
|
||||||
|
"Drag and drop, or click to upload"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
form(
|
||||||
|
.hx.post(route),
|
||||||
|
.hx.target("body"),
|
||||||
|
.hx.swap(.outerHTML),
|
||||||
|
.custom(name: "enctype", value: "multipart/form-data")
|
||||||
|
) {
|
||||||
|
input(.type(.file), .name("file"), .accept(".csv"))
|
||||||
|
|
||||||
|
SubmitButton()
|
||||||
|
.attributes(.class("btn-block mt-6"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user