feat: Working on layout / css.
This commit is contained in:
@@ -10,11 +10,29 @@ struct ToggleFormButton: HTML {
|
||||
|
||||
enum Button {
|
||||
|
||||
static func add() -> some HTML<HTMLTag.button> {
|
||||
button(.class("btn-add")) { "+" }
|
||||
}
|
||||
|
||||
static func danger<C: HTML>(@HTMLBuilder body: () -> C) -> some HTML<HTMLTag.button> {
|
||||
button(.class("danger")) { body() }
|
||||
}
|
||||
|
||||
static func close(id: String) -> some HTML<HTMLTag.button> {
|
||||
button(.class("btn-add"), .on(.click, "toggleContent('\(id)')")) { "x" }
|
||||
static func close(id: String, resetURL: String? = nil) -> some HTML<HTMLTag.button> {
|
||||
button(.class("btn-close"), .on(.click, makeOnClick(id, resetURL))) {
|
||||
"x"
|
||||
}
|
||||
}
|
||||
|
||||
static func update() -> some HTML<HTMLTag.button> {
|
||||
button(.class("btn-update")) { "Update" }
|
||||
}
|
||||
|
||||
private static func makeOnClick(_ id: String, _ resetURL: String?) -> String {
|
||||
var output = "toggleContent('\(id)');"
|
||||
if let resetURL {
|
||||
return "\(output) window.location.href='\(resetURL)';"
|
||||
}
|
||||
return output
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,21 +3,43 @@ import Elementary
|
||||
struct Float<C: HTML>: HTML {
|
||||
|
||||
let id: String
|
||||
let shouldDisplay: Bool
|
||||
let body: C?
|
||||
let resetURL: String?
|
||||
|
||||
init(id: String = "float") {
|
||||
self.id = id
|
||||
self.shouldDisplay = false
|
||||
self.resetURL = nil
|
||||
self.body = nil
|
||||
}
|
||||
|
||||
init(id: String = "float", @HTMLBuilder body: () -> C) {
|
||||
init(
|
||||
id: String = "float",
|
||||
shouldDisplay: Bool,
|
||||
resetURL: String? = nil,
|
||||
@HTMLBuilder body: () -> C
|
||||
) {
|
||||
self.id = id
|
||||
self.shouldDisplay = shouldDisplay
|
||||
self.resetURL = resetURL
|
||||
self.body = body()
|
||||
}
|
||||
|
||||
var content: some HTML {
|
||||
div(.id(id), .class("float")) {
|
||||
if let body {
|
||||
private var classString: String {
|
||||
shouldDisplay ? "float" : ""
|
||||
}
|
||||
|
||||
private var display: String {
|
||||
shouldDisplay ? "block" : "hidden"
|
||||
}
|
||||
|
||||
var content: some HTML<HTMLTag.div> {
|
||||
div(.id(id), .class(classString), .style("display: \(display);")) {
|
||||
if let body, shouldDisplay {
|
||||
div(.class("btn-row")) {
|
||||
Button.close(id: id, resetURL: resetURL)
|
||||
}
|
||||
body
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Elementary
|
||||
import ElementaryHTMX
|
||||
|
||||
struct MainPage<Inner: HTML>: HTMLDocument {
|
||||
struct MainPage<Inner: HTML>: SendableHTMLDocument where Inner: Sendable {
|
||||
|
||||
var title: String { "Purchase Orders" }
|
||||
|
||||
@@ -38,7 +38,29 @@ struct MainPage<Inner: HTML>: HTMLDocument {
|
||||
}
|
||||
}
|
||||
|
||||
extension MainPage: Sendable where Inner: Sendable {}
|
||||
struct RouteHeaderView: HTML {
|
||||
|
||||
let title: String
|
||||
let description: String
|
||||
|
||||
init(title: String, description: String) {
|
||||
self.title = title
|
||||
self.description = description
|
||||
}
|
||||
|
||||
init(route: ViewRoute) {
|
||||
self.init(title: route.title, description: route.description)
|
||||
}
|
||||
|
||||
var content: some HTML {
|
||||
div(.class("container"), .style("padding: 20px 20px;")) {
|
||||
h1 { title }
|
||||
br()
|
||||
p { description }
|
||||
br()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Logo: HTML, Sendable {
|
||||
|
||||
@@ -48,3 +70,5 @@ struct Logo: HTML, Sendable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protocol SendableHTMLDocument: HTMLDocument, Sendable {}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Elementary
|
||||
import ElementaryHTMX
|
||||
|
||||
struct Navbar: HTML {
|
||||
struct Navbar: HTML, Sendable {
|
||||
var content: some HTML {
|
||||
div(.class("sidepanel"), .id("sidepanel")) {
|
||||
a(.href("javascript:void(0)"), .class("closebtn"), .on(.click, "closeSidepanel()")) {
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import Elementary
|
||||
import ElementaryHTMX
|
||||
|
||||
struct RouteHeaderView: HTML {
|
||||
|
||||
let title: String
|
||||
let description: String
|
||||
|
||||
init(title: String, description: String) {
|
||||
self.title = title
|
||||
self.description = description
|
||||
}
|
||||
|
||||
init(route: ViewRoute) {
|
||||
self.init(title: route.title, description: route.description)
|
||||
}
|
||||
|
||||
var content: some HTML {
|
||||
div(.class("container"), .style("padding: 20px 20px;")) {
|
||||
h1 { title }
|
||||
br()
|
||||
p { description }
|
||||
br()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,42 +8,41 @@ struct UserDetail: HTML, Sendable {
|
||||
|
||||
let user: User?
|
||||
|
||||
var classString: String {
|
||||
user != nil ? "float" : ""
|
||||
}
|
||||
|
||||
var display: String {
|
||||
user != nil ? "block" : "hidden"
|
||||
}
|
||||
|
||||
var content: some HTML {
|
||||
div(
|
||||
.id("float"),
|
||||
.class(classString),
|
||||
.style("display: \(display);")
|
||||
) {
|
||||
Float(shouldDisplay: user != nil, resetURL: "/users") {
|
||||
if let user {
|
||||
Button.close(id: "float")
|
||||
form {
|
||||
form(
|
||||
.hx.post("/users/\(user.id)"),
|
||||
.hx.swap(.outerHTML),
|
||||
.hx.target("#user_\(user.id)"),
|
||||
.custom(name: "hx-on::after-request", value: "toggleContent('float'); window.location.href='/users';")
|
||||
) {
|
||||
div(.class("row")) {
|
||||
makeLabel(for: "username", value: "Username:")
|
||||
input(.type(.text), .name("username"), .value(user.username))
|
||||
input(.class("col-5"), .type(.text), .id("username"), .name("username"), .value(user.username), .required)
|
||||
makeLabel(for: "email", value: "Email:")
|
||||
input(.type(.email), .name("email"), .value(user.username))
|
||||
input(.class("col-5"), .type(.email), .id("email"), .name("email"), .value(user.email), .required)
|
||||
}
|
||||
div(.class("row")) {
|
||||
div(.style("display: inline-block;")) {
|
||||
h3(.class("label")) { "Created:" }
|
||||
h3 { dateFormatter.formattedDate(user.createdAt) }
|
||||
}
|
||||
div(.style("display: inline-block;")) {
|
||||
h3(.class("label")) { "Updated:" }
|
||||
h3 { dateFormatter.formattedDate(user.updatedAt) }
|
||||
}
|
||||
span(.class("label col-1")) { "Created:" }
|
||||
span(.class("date col-4")) { dateFormatter.formattedDate(user.createdAt) }
|
||||
span(.class("label col-1")) { "Updated:" }
|
||||
span(.class("date col-4")) { dateFormatter.formattedDate(user.updatedAt) }
|
||||
}
|
||||
div(.class("btn-row user-buttons")) {
|
||||
button(
|
||||
.type(.submit),
|
||||
.style("background-color: blue; color: white;")
|
||||
) { "Update" }
|
||||
Button.danger { "Delete" }
|
||||
.attributes(
|
||||
.hx.delete("/users/\(user.id)"),
|
||||
.hx.trigger(.event(.click)),
|
||||
.hx.swap(.outerHTML),
|
||||
.hx.target("#user_\(user.id)"),
|
||||
.custom(name: "hx-on::after-request", value: "toggleContent('float'); window.location.href='/users';")
|
||||
)
|
||||
}
|
||||
}
|
||||
div(.class("btn-row user-buttons")) {
|
||||
Button.danger { "Delete" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,7 +52,7 @@ struct UserDetail: HTML, Sendable {
|
||||
for name: String,
|
||||
value: String
|
||||
) -> some HTML {
|
||||
label(.for(name)) { h3 { value } }
|
||||
label(.for(name), .class("col-1")) { span(.class("label")) { value } }
|
||||
}
|
||||
|
||||
func row(_ label: String, _ value: String) -> some HTML<HTMLTag.tr> {
|
||||
|
||||
@@ -5,12 +5,27 @@ struct UserForm: HTML, Sendable {
|
||||
let context: Context
|
||||
|
||||
var content: some HTML {
|
||||
if context == .create {
|
||||
Float(shouldDisplay: true) {
|
||||
makeForm()
|
||||
}
|
||||
} else {
|
||||
makeForm()
|
||||
}
|
||||
}
|
||||
|
||||
private func makeForm() -> some HTML {
|
||||
form(
|
||||
.id("user-form"),
|
||||
.class("user-form"),
|
||||
.hx.post(context.targetURL),
|
||||
.hx.pushURL(context.pushURL),
|
||||
.custom(name: "hx-on::after-request", value: "if(event.detail.successful) this.reset(); toggleContent('form');")
|
||||
.hx.target(context.target),
|
||||
.hx.swap(.outerHTML),
|
||||
.custom(
|
||||
name: "hx-on::after-request",
|
||||
value: "if(event.detail.successful) this.reset(); toggleContent('float');"
|
||||
)
|
||||
) {
|
||||
input(.type(.text), .id("username"), .name("username"), .placeholder("Username"), .autofocus, .required)
|
||||
br()
|
||||
@@ -21,14 +36,20 @@ struct UserForm: HTML, Sendable {
|
||||
input(.type(.password), .id("password"), .name("password"), .placeholder("Password"), .required)
|
||||
br()
|
||||
if context.showConfirmPassword {
|
||||
input(.type(.password), .id("confirmPassword"), .name("confirmPassword"), .required)
|
||||
input(
|
||||
.type(.password),
|
||||
.id("confirmPassword"),
|
||||
.name("confirmPassword"),
|
||||
.placeholder("Confirm Password"),
|
||||
.required
|
||||
)
|
||||
br()
|
||||
}
|
||||
input(.type(.submit), .value(context.buttonLabel))
|
||||
}
|
||||
}
|
||||
|
||||
enum Context {
|
||||
enum Context: Equatable {
|
||||
case create
|
||||
case login(next: String?)
|
||||
|
||||
@@ -62,6 +83,15 @@ struct UserForm: HTML, Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
var target: String {
|
||||
switch self {
|
||||
case .create:
|
||||
return "next table"
|
||||
case .login:
|
||||
return "body"
|
||||
}
|
||||
}
|
||||
|
||||
var targetURL: String {
|
||||
switch self {
|
||||
case .create:
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import DatabaseClient
|
||||
import Elementary
|
||||
import SharedModels
|
||||
|
||||
struct UserIndex: HTML {
|
||||
let user: User?
|
||||
|
||||
init(user: User? = nil) {
|
||||
self.user = user
|
||||
}
|
||||
|
||||
var content: some HTML {
|
||||
div {
|
||||
// UserDetail(user: user)
|
||||
div(.id("float"), .class("float")) {}
|
||||
UserTable()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import SharedModels
|
||||
|
||||
struct UserTable: HTML {
|
||||
|
||||
@Dependency(\.database.users.fetchAll) var fetchAll
|
||||
let users: [User]
|
||||
|
||||
var content: some HTML {
|
||||
table(.id("user-table")) {
|
||||
@@ -14,11 +14,17 @@ struct UserTable: HTML {
|
||||
tr {
|
||||
th { "Username" }
|
||||
th { "Email" }
|
||||
th(.style("width: 50px;")) { ToggleFormButton() }
|
||||
th(.style("width: 50px;")) {
|
||||
Button.add()
|
||||
.attributes(
|
||||
.hx.get("/users/create"),
|
||||
.hx.target("#float"),
|
||||
.hx.swap(.outerHTML)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
let users = try await fetchAll()
|
||||
tbody(.id("user-table-body")) {
|
||||
for user in users {
|
||||
Row(user: user)
|
||||
}
|
||||
@@ -29,8 +35,12 @@ struct UserTable: HTML {
|
||||
struct Row: HTML {
|
||||
let user: User
|
||||
|
||||
init(user: User) {
|
||||
self.user = user
|
||||
}
|
||||
|
||||
var content: some HTML<HTMLTag.tr> {
|
||||
tr {
|
||||
tr(.id("user_\(user.id)")) {
|
||||
td { user.username }
|
||||
td { user.email }
|
||||
td {
|
||||
@@ -38,6 +48,7 @@ struct UserTable: HTML {
|
||||
.hx.get("/users/\(user.id.uuidString)"),
|
||||
.hx.target("#float"),
|
||||
.hx.swap(.outerHTML),
|
||||
.hx.pushURL(true),
|
||||
.class("btn-detail")
|
||||
) {
|
||||
"〉"
|
||||
|
||||
Reference in New Issue
Block a user