WIP: Begins work on login / signup, adds user database models, authentication needs implemented.
This commit is contained in:
@@ -5,9 +5,15 @@ public struct MainPage<Inner: HTML>: SendableHTMLDocument where Inner: Sendable
|
||||
public var lang: String { "en" }
|
||||
let inner: Inner
|
||||
let activeTab: Sidebar.ActiveTab
|
||||
let showSidebar: Bool
|
||||
|
||||
init(active activeTab: Sidebar.ActiveTab, _ inner: () -> Inner) {
|
||||
init(
|
||||
active activeTab: Sidebar.ActiveTab,
|
||||
showSidebar: Bool = true,
|
||||
_ inner: () -> Inner
|
||||
) {
|
||||
self.activeTab = activeTab
|
||||
self.showSidebar = showSidebar
|
||||
self.inner = inner()
|
||||
}
|
||||
|
||||
@@ -24,7 +30,9 @@ public struct MainPage<Inner: HTML>: SendableHTMLDocument where Inner: Sendable
|
||||
// div(.class("bg-white dark:bg-gray-800 dark:text-white")) {
|
||||
div {
|
||||
div(.class("flex flex-row")) {
|
||||
Sidebar(active: activeTab)
|
||||
if showSidebar {
|
||||
Sidebar(active: activeTab)
|
||||
}
|
||||
main(.class("flex flex-col h-screen w-full px-6 py-10")) {
|
||||
inner
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ struct RoomsView: HTML, Sendable {
|
||||
let rooms: [Room]
|
||||
|
||||
var body: some HTML {
|
||||
div(.class("m-10")) {
|
||||
div {
|
||||
Row {
|
||||
h1(.class("text-3xl font-bold pb-6")) { "Room Loads" }
|
||||
h1(.class("text-2xl font-bold")) { "Room Loads" }
|
||||
div(
|
||||
.class("tooltip tooltip-left"),
|
||||
.data("tip", value: "Add room")
|
||||
@@ -20,94 +20,67 @@ struct RoomsView: HTML, Sendable {
|
||||
.hx.get(route: .room(.form(dismiss: false))),
|
||||
.hx.target("#roomForm"),
|
||||
.hx.swap(.outerHTML),
|
||||
.class("btn btn-primary w-[40px]")
|
||||
.class("btn btn-primary w-[40px] text-2xl")
|
||||
) {
|
||||
"+"
|
||||
}
|
||||
}
|
||||
}
|
||||
.attributes(.class("pb-6"))
|
||||
|
||||
div(
|
||||
.id("roomTable"),
|
||||
.class(
|
||||
"""
|
||||
border border-gray-200 rounded-lg shadow-lg
|
||||
grid grid-cols-5 p-4
|
||||
"""
|
||||
)
|
||||
) {
|
||||
// Header
|
||||
Label("Name")
|
||||
// Pushes items to right
|
||||
Row {
|
||||
div {}
|
||||
Label("Heating Load")
|
||||
}
|
||||
Row {
|
||||
div {}
|
||||
Label("Cooling Total")
|
||||
}
|
||||
Row {
|
||||
div {}
|
||||
Label("Cooling Sensible")
|
||||
}
|
||||
Row {
|
||||
div {}
|
||||
Label("Register Count")
|
||||
}
|
||||
|
||||
// Divider
|
||||
div(.class("border-b border-gray-200 col-span-5 mb-2")) {}
|
||||
|
||||
// Rows
|
||||
for row in rooms {
|
||||
span { row.name }
|
||||
// Pushes items to right
|
||||
Row {
|
||||
div {}
|
||||
Number(row.heatingLoad)
|
||||
.attributes(.class("text-red-500"))
|
||||
div(.class("overflow-x-auto rounded-box border")) {
|
||||
table(.class("table table-zebra"), .id("roomsTable")) {
|
||||
thead {
|
||||
tr {
|
||||
th { Label("Name") }
|
||||
th { Label("Heating Load") }
|
||||
th { Label("Cooling Total") }
|
||||
th { Label("Cooling Sensible") }
|
||||
th { Label("Register Count") }
|
||||
}
|
||||
}
|
||||
Row {
|
||||
div {}
|
||||
Number(row.coolingLoad.total)
|
||||
.attributes(.class("text-green-400"))
|
||||
tbody {
|
||||
for room in rooms {
|
||||
tr {
|
||||
td { room.name }
|
||||
td {
|
||||
Number(room.heatingLoad)
|
||||
.attributes(.class("text-error"))
|
||||
}
|
||||
td {
|
||||
Number(room.coolingLoad.total)
|
||||
.attributes(.class("text-success"))
|
||||
}
|
||||
td {
|
||||
Number(room.coolingLoad.sensible)
|
||||
.attributes(.class("text-info"))
|
||||
}
|
||||
td {
|
||||
Number(room.registerCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
// TOTALS
|
||||
tr(.class("font-bold text-xl")) {
|
||||
td { Label("Total") }
|
||||
td {
|
||||
Number(rooms.heatingTotal)
|
||||
.attributes(.class("badge badge-outline badge-error badge-xl"))
|
||||
}
|
||||
td {
|
||||
Number(rooms.coolingTotal)
|
||||
.attributes(
|
||||
.class("badge badge-outline badge-success badge-xl"))
|
||||
}
|
||||
td {
|
||||
Number(rooms.coolingSensibleTotal)
|
||||
.attributes(.class("badge badge-outline badge-info badge-xl"))
|
||||
}
|
||||
td {}
|
||||
}
|
||||
}
|
||||
Row {
|
||||
div {}
|
||||
Number(row.coolingLoad.sensible)
|
||||
.attributes(.class("text-blue-400"))
|
||||
}
|
||||
Row {
|
||||
div {}
|
||||
Number(row.registerCount)
|
||||
}
|
||||
|
||||
// Divider
|
||||
div(.class("border-b border-gray-200 col-span-5 mb-2")) {}
|
||||
}
|
||||
|
||||
// Totals
|
||||
Label("Total")
|
||||
Row {
|
||||
div {}
|
||||
Number(rooms.heatingTotal)
|
||||
.attributes(.class("badge badge-outline badge-error badge-xl text-xl font-bold"))
|
||||
}
|
||||
Row {
|
||||
div {}
|
||||
Number(rooms.coolingTotal)
|
||||
.attributes(.class("badge badge-outline badge-success badge-xl text-xl font-bold"))
|
||||
}
|
||||
Row {
|
||||
div {}
|
||||
Number(rooms.coolingSensibleTotal)
|
||||
.attributes(.class("badge badge-outline badge-info badge-xl text-xl font-bold"))
|
||||
}
|
||||
// Empty register count column
|
||||
div {}
|
||||
}
|
||||
|
||||
RoomForm(dismiss: true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import Elementary
|
||||
import ManualDCore
|
||||
import Styleguide
|
||||
|
||||
// TODO: Need to add active to sidebar links.
|
||||
// TODO: Update to use DaisyUI drawer.
|
||||
struct Sidebar: HTML {
|
||||
|
||||
let active: ActiveTab
|
||||
@@ -23,7 +23,7 @@ struct Sidebar: HTML {
|
||||
Label("Theme")
|
||||
input(.type(.checkbox), .class("toggle theme-controller"), .value("light"))
|
||||
}
|
||||
.attributes(.class("py-4"))
|
||||
.attributes(.class("p-4"))
|
||||
|
||||
row(title: "Project", icon: .mapPin, route: .project(.index))
|
||||
.attributes(.data("active", value: active == .projects ? "true" : "false"))
|
||||
|
||||
158
Sources/ViewController/Views/User/LoginForm.swift
Normal file
158
Sources/ViewController/Views/User/LoginForm.swift
Normal file
@@ -0,0 +1,158 @@
|
||||
import Elementary
|
||||
import ElementaryHTMX
|
||||
import Styleguide
|
||||
|
||||
struct LoginForm: HTML, Sendable {
|
||||
|
||||
let style: Style
|
||||
|
||||
init(style: Style = .login) {
|
||||
self.style = style
|
||||
}
|
||||
|
||||
var body: some HTML {
|
||||
form {
|
||||
fieldset(.class("fieldset bg-base-200 border-base-300 rounded-box w-xl border p-4")) {
|
||||
legend(.class("fieldset-legend")) { style.title }
|
||||
|
||||
if style == .signup {
|
||||
label(.class("input validator w-full")) {
|
||||
SVG(.user)
|
||||
input(
|
||||
.type(.text), .required, .placeholder("Username"),
|
||||
.minlength("3"), .pattern(.username)
|
||||
)
|
||||
}
|
||||
div(.class("validator-hint hidden")) {
|
||||
"Enter valid username"
|
||||
br()
|
||||
"Must be at least 3 characters"
|
||||
}
|
||||
}
|
||||
|
||||
label(.class("input validator w-full")) {
|
||||
SVG(.email)
|
||||
input(
|
||||
.type(.email), .placeholder("Email"), .required
|
||||
)
|
||||
}
|
||||
div(.class("validator-hint hidden")) { "Enter valid email address." }
|
||||
|
||||
label(.class("input validator w-full")) {
|
||||
SVG(.key)
|
||||
input(
|
||||
.type(.password), .placeholder("Password"), .required,
|
||||
.pattern(.password), .minlength("8")
|
||||
)
|
||||
}
|
||||
|
||||
if style == .signup {
|
||||
label(.class("input validator w-full")) {
|
||||
SVG(.key)
|
||||
input(
|
||||
.type(.password), .placeholder("Confirm Password"), .required,
|
||||
.pattern(.password), .minlength("8")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
div(.class("validator-hint hidden")) {
|
||||
p {
|
||||
"Must be more than 8 characters, including"
|
||||
br()
|
||||
"At least one number"
|
||||
br()
|
||||
"At least one lowercase letter"
|
||||
br()
|
||||
"At least one uppercase letter"
|
||||
}
|
||||
}
|
||||
|
||||
button(.class("btn btn-neutral mt-4")) { style.title }
|
||||
}
|
||||
}
|
||||
// div(.class("flex items-center justify-center")) {
|
||||
// div(.class("w-full mx-auto")) {
|
||||
// h1(.class("text-2xl font-bold")) { style.title }
|
||||
// form(
|
||||
// .class("w-full h-screen")
|
||||
// ) {
|
||||
// fieldset(.class("fieldset w-full")) {
|
||||
// legend(.class("fieldset-legend")) { "Email" }
|
||||
// label(.class("input validator")) {
|
||||
// SVG(.email)
|
||||
// input(
|
||||
// .type(.email), .placeholder("mail@site.com"), .required,
|
||||
// .autofocus
|
||||
// )
|
||||
// }
|
||||
// div(.class("validator-hint hidden")) { "Enter valid email address." }
|
||||
// }
|
||||
//
|
||||
// if style == .signup {
|
||||
// fieldset(.class("fieldset")) {
|
||||
// legend(.class("fieldset-legend")) { "Name" }
|
||||
// label(.class("input validator")) {
|
||||
// input(
|
||||
// .type(.text), .placeholder("Username"), .required,
|
||||
// .init(name: "pattern", value: "[A-Za-z][A-Za-z0-9\\-]*"),
|
||||
// .init(name: "minlength", value: "3")
|
||||
// )
|
||||
// }
|
||||
// div(.class("validator-hint hidden")) { "Enter valid email address." }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fieldset(.class("fieldset")) {
|
||||
// legend(.class("fieldset-legend")) { "Password" }
|
||||
// label(.class("input validator")) {
|
||||
// SVG(.key)
|
||||
// input(
|
||||
// .type(.password), .placeholder("Password"), .required,
|
||||
// .init(name: "pattern", value: "(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{8,}"),
|
||||
// .init(name: "minlength", value: "8")
|
||||
// )
|
||||
// }
|
||||
// if style == .signup {
|
||||
// label(.class("input validator")) {
|
||||
// SVG(.key)
|
||||
// input(
|
||||
// .type(.password), .placeholder("Confirm Password"), .required,
|
||||
// .init(name: "pattern", value: "(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{8,}"),
|
||||
// .init(name: "minlength", value: "8")
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// div(.class("validator-hint hidden")) {
|
||||
// p {
|
||||
// "Must be more than 8 characters, including"
|
||||
// br()
|
||||
// "At least one number"
|
||||
// br()
|
||||
// "At least one lowercase letter"
|
||||
// br()
|
||||
// "At least one uppercase letter"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// SubmitButton(title: style.title)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
extension LoginForm {
|
||||
enum Style: Equatable, Sendable {
|
||||
case login
|
||||
case signup
|
||||
|
||||
var title: String {
|
||||
switch self {
|
||||
case .login: return "Login"
|
||||
case .signup: return "Sign Up"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
0
Sources/ViewController/Views/User/SignupForm.swift
Normal file
0
Sources/ViewController/Views/User/SignupForm.swift
Normal file
Reference in New Issue
Block a user