This repository has been archived on 2026-02-12. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
swift-duct-calc/Sources/ManualDCore/Routes/ViewRoute.swift

677 lines
17 KiB
Swift

import CasePathsCore
import FluentKit
import Foundation
@preconcurrency import URLRouting
extension SiteRoute {
/// Represents view routes.
///
/// The routes return html.
public enum View: Equatable, Sendable {
case login(LoginRoute)
case signup(SignupRoute)
case project(ProjectRoute)
public static let router = OneOf {
Route(.case(Self.login)) {
SiteRoute.View.LoginRoute.router
}
Route(.case(Self.signup)) {
SiteRoute.View.SignupRoute.router
}
Route(.case(Self.project)) {
SiteRoute.View.ProjectRoute.router
}
}
}
}
extension SiteRoute.View {
public enum ProjectRoute: Equatable, Sendable {
case create(Project.Create)
case delete(id: Project.ID)
case detail(Project.ID, DetailRoute)
case form(id: Project.ID? = nil, dismiss: Bool = false)
case index
case page(PageRequest)
case update(Project.Update)
public static func page(page: Int, per limit: Int) -> Self {
.page(.init(page: page, per: limit))
}
static let rootPath = "projects"
public static let router = OneOf {
Route(.case(Self.create)) {
Path { rootPath }
Method.post
Body {
FormData {
Field("name", .string)
Field("streetAddress", .string)
Field("city", .string)
Field("state", .string)
Field("zipCode", .string)
Optionally {
Field("sensibleHeatRatio", default: nil) {
Double.parser()
}
}
}
.map(.memberwise(Project.Create.init))
}
}
Route(.case(Self.delete)) {
Path {
rootPath
Project.ID.parser()
}
Method.delete
}
Route(.case(Self.detail)) {
Path {
rootPath
Project.ID.parser()
}
DetailRoute.router
}
Route(.case(Self.form)) {
Path {
rootPath
"create"
}
Method.get
Query {
Optionally {
Field("id", default: nil) { Project.ID.parser() }
}
Field("dismiss", default: false) { Bool.parser() }
}
}
Route(.case(Self.index)) {
Path { rootPath }
Method.get
}
Route(.case(Self.page)) {
Path {
rootPath
"page"
}
Method.get
Query {
Field("page", default: 1) { Int.parser() }
Field("per", default: 25) { Int.parser() }
}
.map(.memberwise(PageRequest.init))
}
Route(.case(Self.update)) {
Path { rootPath }
Method.patch
Body {
FormData {
Field("id") { Project.ID.parser() }
Optionally {
Field("name", .string)
}
Optionally {
Field("streetAddress", .string)
}
Optionally {
Field("city", .string)
}
Optionally {
Field("state", .string)
}
Optionally {
Field("zipCode", .string)
}
Optionally {
Field("sensibleHeatRatio", default: nil) {
Double.parser()
}
}
}
.map(.memberwise(Project.Update.init))
}
}
}
}
}
extension SiteRoute.View.ProjectRoute {
public enum DetailRoute: Equatable, Sendable {
case index(tab: Tab = .default)
case equipment(EquipmentInfoRoute)
case equivalentLength(EquivalentLengthRoute)
case frictionRate(FrictionRateRoute)
case rooms(RoomRoute)
static let router = OneOf {
Route(.case(Self.index)) {
Method.get
Query {
Field("tab", default: Tab.default) {
Tab.parser()
}
}
}
Route(.case(Self.equipment)) {
EquipmentInfoRoute.router
}
Route(.case(Self.equivalentLength)) {
EquivalentLengthRoute.router
}
Route(.case(Self.frictionRate)) {
FrictionRateRoute.router
}
Route(.case(Self.rooms)) {
RoomRoute.router
}
}
public enum Tab: String, CaseIterable, Equatable, Sendable {
case project
case rooms
case equivalentLength
case frictionRate
case ductSizing
public static var `default`: Self { .rooms }
}
}
public enum RoomRoute: Equatable, Sendable {
case delete(id: Room.ID)
case form(id: Room.ID? = nil, dismiss: Bool = false)
case index
case submit(Room.Create)
case update(Room.Update)
case updateSensibleHeatRatio(SHRUpdate)
static let rootPath = "rooms"
public static let router = OneOf {
Route(.case(Self.delete)) {
Path {
rootPath
Room.ID.parser()
}
Method.delete
}
Route(.case(Self.form)) {
Path {
rootPath
"create"
}
Method.get
Query {
Optionally {
Field("id", default: nil) { Room.ID.parser() }
}
Field("dismiss", default: false) { Bool.parser() }
}
}
Route(.case(Self.index)) {
Path {
rootPath
}
Method.get
}
Route(.case(Self.submit)) {
Path { rootPath }
Method.post
Body {
FormData {
Field("projectID") { Project.ID.parser() }
Field("name", .string)
Field("heatingLoad") { Double.parser() }
Field("coolingTotal") { Double.parser() }
Optionally {
Field("coolingSensible", default: nil) { Double.parser() }
}
Field("registerCount") { Digits() }
}
.map(.memberwise(Room.Create.init))
}
}
Route(.case(Self.update)) {
Path { rootPath }
Method.patch
Body {
FormData {
Field("id") { Room.ID.parser() }
Optionally {
Field("name", .string)
}
Optionally {
Field("heatingLoad") { Double.parser() }
}
Optionally {
Field("coolingTotal") { Double.parser() }
}
Optionally {
Field("coolingSensible") { Double.parser() }
}
Optionally {
Field("registerCount") { Digits() }
}
}
.map(.memberwise(Room.Update.init))
}
}
Route(.case(Self.updateSensibleHeatRatio)) {
Path {
rootPath
"update-shr"
}
Method.patch
Body {
FormData {
Field("projectID") { Project.ID.parser() }
Optionally {
Field("sensibleHeatRatio") { Double.parser() }
}
}
.map(.memberwise(SHRUpdate.init))
}
}
}
public struct SHRUpdate: Codable, Equatable, Sendable {
public let projectID: Project.ID
public let sensibleHeatRatio: Double?
}
}
public enum FrictionRateRoute: Equatable, Sendable {
case index
// TODO: Remove form or move equipment / component losses routes here.
case form(FormType, dismiss: Bool = false)
static let rootPath = "friction-rate"
public static let router = OneOf {
Route(.case(Self.index)) {
Path { rootPath }
Method.get
}
Route(.case(Self.form)) {
Path {
rootPath
"create"
}
Method.get
Query {
Field("type") { FormType.parser() }
Field("dismiss", default: false) { Bool.parser() }
}
}
}
public enum FormType: String, CaseIterable, Codable, Equatable, Sendable {
case equipmentInfo
case componentPressureLoss
}
}
public enum EquipmentInfoRoute: Equatable, Sendable {
case index
case form(dismiss: Bool)
case submit(EquipmentInfo.Create)
case update(EquipmentInfo.Update)
static let rootPath = "equipment"
public static let router = OneOf {
Route(.case(Self.index)) {
Path { rootPath }
Method.get
}
Route(.case(Self.form)) {
Path {
rootPath
"create"
}
Method.get
Query {
Field("dismiss", default: true) { Bool.parser() }
}
}
Route(.case(Self.submit)) {
Path { rootPath }
Method.post
Body {
FormData {
Field("projectID") { Project.ID.parser() }
Field("staticPressure") { Double.parser() }
Field("heatingCFM") { Int.parser() }
Field("coolingCFM") { Int.parser() }
}
.map(.memberwise(EquipmentInfo.Create.init))
}
}
Route(.case(Self.update)) {
Path { rootPath }
Method.patch
Body {
FormData {
Field("id") { EquipmentInfo.ID.parser() }
Optionally {
Field("staticPressure", default: nil) { Double.parser() }
}
Optionally {
Field("heatingCFM", default: nil) { Int.parser() }
}
Optionally {
Field("coolingCFM", default: nil) { Int.parser() }
}
}
.map(.memberwise(EquipmentInfo.Update.init))
}
}
}
}
public enum EquivalentLengthRoute: Equatable, Sendable {
case delete(id: EffectiveLength.ID)
case field(FieldType, style: EffectiveLength.EffectiveLengthType? = nil)
case form(dismiss: Bool = false)
case index
case submit(FormStep)
case update(StepThree)
static let rootPath = "effective-lengths"
public static let router = OneOf {
Route(.case(Self.delete(id:))) {
Path {
rootPath
EffectiveLength.ID.parser()
}
Method.delete
}
Route(.case(Self.index)) {
Path { rootPath }
Method.get
}
Route(.case(Self.form(dismiss:))) {
Path {
rootPath
"create"
}
Method.get
Query {
Field("dismiss", default: false) { Bool.parser() }
}
}
Route(.case(Self.field)) {
Path {
rootPath
"field"
}
Method.get
Query {
Field("type") { FieldType.parser() }
Optionally {
Field("style", default: nil) {
EffectiveLength.EffectiveLengthType.parser()
}
}
}
}
Route(.case(Self.submit)) {
Path { rootPath }
Method.post
FormStep.router
}
Route(.case(Self.update)) {
Path { rootPath }
Method.patch
Body {
FormData {
Optionally {
Field("id", default: nil) { EffectiveLength.ID.parser() }
}
Field("name", .string)
Field("type") { EffectiveLength.EffectiveLengthType.parser() }
Many {
Field("straightLengths") {
Int.parser()
}
}
Many {
Field("group[group]") {
Int.parser()
}
}
Many {
Field("group[letter]", .string)
}
Many {
Field("group[length]") {
Int.parser()
}
}
Many {
Field("group[quantity]") {
Int.parser()
}
}
}
.map(.memberwise(StepThree.init))
}
}
}
public enum FormStep: Equatable, Sendable {
case one(StepOne)
case two(StepTwo)
case three(StepThree)
static let router = OneOf {
Route(.case(Self.one)) {
Path {
Key.stepOne.rawValue
}
Body {
FormData {
Optionally {
Field("id", default: nil) { EffectiveLength.ID.parser() }
}
Field("name", .string)
Field("type") { EffectiveLength.EffectiveLengthType.parser() }
}
.map(.memberwise(StepOne.init))
}
}
Route(.case(Self.two)) {
Path {
Key.stepTwo.rawValue
}
Body {
FormData {
Optionally {
Field("id", default: nil) { EffectiveLength.ID.parser() }
}
Field("name", .string)
Field("type") { EffectiveLength.EffectiveLengthType.parser() }
Many {
Field("straightLengths") {
Int.parser()
}
}
}
.map(.memberwise(StepTwo.init))
}
}
Route(.case(Self.three)) {
Path {
Key.stepThree.rawValue
}
Body {
FormData {
Optionally {
Field("id", default: nil) { EffectiveLength.ID.parser() }
}
Field("name", .string)
Field("type") { EffectiveLength.EffectiveLengthType.parser() }
Many {
Field("straightLengths") {
Int.parser()
}
}
Many {
Field("group[group]") {
Int.parser()
}
}
Many {
Field("group[letter]", .string)
}
Many {
Field("group[length]") {
Int.parser()
}
}
Many {
Field("group[quantity]") {
Int.parser()
}
}
}
.map(.memberwise(StepThree.init))
}
}
}
public enum Key: String, CaseIterable, Codable, Equatable, Sendable {
case stepOne
case stepTwo
case stepThree
}
}
public struct StepOne: Codable, Equatable, Sendable {
public let id: EffectiveLength.ID?
public let name: String
public let type: EffectiveLength.EffectiveLengthType
}
public struct StepTwo: Codable, Equatable, Sendable {
public let id: EffectiveLength.ID?
public let name: String
public let type: EffectiveLength.EffectiveLengthType
public let straightLengths: [Int]
public init(
id: EffectiveLength.ID? = nil,
name: String,
type: EffectiveLength.EffectiveLengthType,
straightLengths: [Int]
) {
self.id = id
self.name = name
self.type = type
self.straightLengths = straightLengths
}
}
public struct StepThree: Codable, Equatable, Sendable {
public let id: EffectiveLength.ID?
public let name: String
public let type: EffectiveLength.EffectiveLengthType
public let straightLengths: [Int]
public let groupGroups: [Int]
public let groupLetters: [String]
public let groupLengths: [Int]
public let groupQuantities: [Int]
}
public enum FieldType: String, CaseIterable, Equatable, Sendable {
case straightLength
case group
}
}
}
extension SiteRoute.View {
public enum LoginRoute: Equatable, Sendable {
case index(next: String? = nil)
case submit(User.Login)
static let rootPath = "login"
public static let router = OneOf {
Route(.case(Self.index)) {
Path { rootPath }
Method.get
Query {
Optionally {
Field("next", .string, default: nil)
// {
// CharacterSet.map(.string)
// }
}
}
}
Route(.case(Self.submit)) {
Path { rootPath }
Method.post
Body {
FormData {
Field("email", .string)
Field("password", .string)
Optionally {
Field("next", .string, default: nil)
}
}
.map(.memberwise(User.Login.init))
}
}
}
}
}
extension SiteRoute.View {
public enum SignupRoute: Equatable, Sendable {
case index
case submit(User.Create)
static let rootPath = "signup"
public static let router = OneOf {
Route(.case(Self.index)) {
Path { rootPath }
Method.get
}
Route(.case(Self.submit)) {
Path { rootPath }
Method.post
Body {
FormData {
Field("username", .string)
Field("email", .string)
Field("password", .string)
Field("confirmPassword", .string)
}
.map(.memberwise(User.Create.init))
}
}
}
}
}
extension PageRequest: @retroactive Equatable {
public static func == (lhs: FluentKit.PageRequest, rhs: FluentKit.PageRequest) -> Bool {
lhs.page == rhs.page && lhs.per == rhs.per
}
}