feat: Moves vendor branch views to their own files, starts to implement snapshot testing for html
This commit is contained in:
@@ -16,6 +16,7 @@ extension SharedModels.ViewRoute {
|
||||
|
||||
var middleware: [any Middleware]? {
|
||||
switch self {
|
||||
case .index: return viewProtectedMiddleware
|
||||
case let .employee(route): return route.middleware
|
||||
case .login: return nil
|
||||
case let .purchaseOrder(route): return route.middleware
|
||||
@@ -28,6 +29,9 @@ extension SharedModels.ViewRoute {
|
||||
func handle(request: Request) async throws -> any AsyncResponseEncodable {
|
||||
@Dependency(\.database.users) var users
|
||||
switch self {
|
||||
case .index:
|
||||
return request.redirect(to: Self.router.path(for: .purchaseOrder(.index)))
|
||||
|
||||
case let .employee(route):
|
||||
return try await route.handle(request: request)
|
||||
|
||||
@@ -348,6 +352,17 @@ extension SharedModels.ViewRoute.VendorBranchRoute {
|
||||
@Dependency(\.database) var database
|
||||
|
||||
switch self {
|
||||
case let .index(for: vendorID):
|
||||
guard let vendorID else {
|
||||
throw Abort(.badRequest, reason: "Vendor id not supplied")
|
||||
}
|
||||
return try await request.render {
|
||||
try await VendorBranchList(
|
||||
vendorID: vendorID,
|
||||
branches: database.vendorBranches.fetchAll(.for(vendorID: vendorID))
|
||||
)
|
||||
}
|
||||
|
||||
case let .select(context: context):
|
||||
return try await request.render {
|
||||
try await context.toHTML(branches: database.vendorBranches.fetchAllWithDetail())
|
||||
@@ -355,7 +370,7 @@ extension SharedModels.ViewRoute.VendorBranchRoute {
|
||||
|
||||
case let .create(branch):
|
||||
return try await request.render {
|
||||
try await VendorDetail.BranchRow(branch: database.vendorBranches.create(branch))
|
||||
try await VendorBranchList.Row(branch: database.vendorBranches.create(branch))
|
||||
}
|
||||
|
||||
case let .delete(id: id):
|
||||
|
||||
@@ -117,11 +117,13 @@ enum IDKey: CustomStringConvertible {
|
||||
}
|
||||
|
||||
enum Branch: CustomStringConvertible {
|
||||
case list
|
||||
case form
|
||||
case row(id: VendorBranch.ID)
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .list: return "list"
|
||||
case .form: return "form"
|
||||
case let .row(id): return id.uuidString
|
||||
}
|
||||
@@ -148,7 +150,7 @@ enum IDKey: CustomStringConvertible {
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case let .content: return "content"
|
||||
case .content: return "content"
|
||||
case let .row(id): return "\(id)"
|
||||
case .search: return "search"
|
||||
case .table: return "table"
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import Elementary
|
||||
import SharedModels
|
||||
import URLRouting
|
||||
|
||||
struct ToggleFormButton: HTML {
|
||||
var content: some HTML<HTMLTag.a> {
|
||||
@@ -24,6 +26,13 @@ enum Button {
|
||||
}
|
||||
}
|
||||
|
||||
static func close(id: IDKey, resetURL route: ViewRoute? = nil) -> some HTML<HTMLTag.button> {
|
||||
close(
|
||||
id: id.description,
|
||||
resetURL: route != nil ? ViewRoute.router.path(for: route!) : nil
|
||||
)
|
||||
}
|
||||
|
||||
static func update() -> some HTML<HTMLTag.button> {
|
||||
button(.class("btn-update")) { "Update" }
|
||||
}
|
||||
|
||||
@@ -10,4 +10,9 @@ enum Img {
|
||||
static func search(width: Int = 30, height: Int = 30) -> some HTML<HTMLTag.img> {
|
||||
img(.src("/images/search.svg"), .width(width), .height(height))
|
||||
}
|
||||
|
||||
@Sendable
|
||||
static func trashCan(width: Int = 30, height: Int = 30) -> some HTML<HTMLTag.img> {
|
||||
img(.src("/images/trash-can.svg"), .width(width), .height(height))
|
||||
}
|
||||
}
|
||||
|
||||
34
Sources/App/Views/VendorBranches/VendorBranchForm.swift
Normal file
34
Sources/App/Views/VendorBranches/VendorBranchForm.swift
Normal file
@@ -0,0 +1,34 @@
|
||||
import Elementary
|
||||
import ElementaryHTMX
|
||||
import SharedModels
|
||||
|
||||
struct VendorBranchForm: HTML {
|
||||
let vendorID: Vendor.ID
|
||||
|
||||
var content: some HTML {
|
||||
form(
|
||||
.id(.branch(.form)),
|
||||
.hx.post(route: .vendorBranch(.index())),
|
||||
.hx.target(.id(.branch(.list))),
|
||||
.hx.swap(.beforeEnd),
|
||||
.hx.on(.afterRequest, .ifSuccessful(.resetForm))
|
||||
) {
|
||||
input(.type(.hidden), .name("vendorID"), .value(vendorID.uuidString))
|
||||
input(
|
||||
.type(.text), .class("col-9"), .name("name"), .placeholder("Add branch..."), .required,
|
||||
// .hx.post(route: .vendorBranch(.index())),
|
||||
.hx.trigger(.event(.keyup).changed().delay("800ms")) // ,
|
||||
// .hx.target(.id(.branch(.list))),
|
||||
// .hx.swap(.beforeEnd),
|
||||
// .custom(name: "hx-on::after-request", value: "if(event.detail.successful) this.reset();")
|
||||
)
|
||||
button(
|
||||
.type(.submit),
|
||||
.class("btn-secondary"),
|
||||
.style("float: right; padding: 10px 50px;"),
|
||||
.hx.target(.id(.branch(.list))),
|
||||
.hx.swap(.beforeEnd)
|
||||
) { "+" }
|
||||
}
|
||||
}
|
||||
}
|
||||
46
Sources/App/Views/VendorBranches/VendorBranchList.swift
Normal file
46
Sources/App/Views/VendorBranches/VendorBranchList.swift
Normal file
@@ -0,0 +1,46 @@
|
||||
import Elementary
|
||||
import ElementaryHTMX
|
||||
import SharedModels
|
||||
|
||||
struct VendorBranchList: HTML {
|
||||
let vendorID: Vendor.ID
|
||||
let branches: [VendorBranch]?
|
||||
|
||||
var content: some HTML {
|
||||
if let branches {
|
||||
ul(.id(.branch(.list))) {
|
||||
for branch in branches {
|
||||
Row(branch: branch)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
div(
|
||||
.hx.get(route: .vendorBranch(.index(for: vendorID))),
|
||||
.hx.target(.this),
|
||||
.hx.indicator(".hx-indicator"),
|
||||
.hx.trigger(.event(.revealed))
|
||||
) {
|
||||
Img.spinner().attributes(.class("hx-indicator"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Row: HTML {
|
||||
let branch: VendorBranch
|
||||
|
||||
var content: some HTML<HTMLTag.li> {
|
||||
li(.id(.branch(.row(id: branch.id))), .class("branch-row")) {
|
||||
span(.class("label")) { branch.name.capitalized }
|
||||
button(
|
||||
.class("btn"),
|
||||
.hx.delete(route: .vendorBranch(.delete(id: branch.id))),
|
||||
.hx.target(.id(.branch(.row(id: branch.id)))),
|
||||
.hx.swap(.outerHTML.transition(true).swap("0.5s"))
|
||||
) {
|
||||
Img.trashCan().attributes(.style("margin-top: 5px;"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,7 +2,6 @@ import Elementary
|
||||
import ElementaryHTMX
|
||||
import SharedModels
|
||||
|
||||
// TODO: Lazy Load branches when view appears.
|
||||
struct VendorDetail: HTML {
|
||||
|
||||
let vendor: Vendor
|
||||
@@ -11,8 +10,8 @@ struct VendorDetail: HTML {
|
||||
Float(shouldDisplay: true) {
|
||||
VendorForm(.formOnly(vendor))
|
||||
h2(.style("margin-left: 20px; font-size: 1.5em;"), .class("label")) { "Branches" }
|
||||
branchForm
|
||||
branches
|
||||
VendorBranchForm(vendorID: vendor.id)
|
||||
VendorBranchList(vendorID: vendor.id, branches: nil)
|
||||
} closeButton: {
|
||||
Button.close(id: "float")
|
||||
.attributes(
|
||||
@@ -23,61 +22,4 @@ struct VendorDetail: HTML {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: What route for here??
|
||||
var branchForm: some HTML {
|
||||
form(
|
||||
.id(.branch(.form)),
|
||||
.hx.post("/vendors/branches"),
|
||||
.hx.target("#branches"),
|
||||
.hx.swap(.beforeEnd),
|
||||
.hx.on(.afterRequest, .ifSuccessful(.resetForm))
|
||||
// .custom(name: "hx-on::after-request", value: "if(event.detail.successful) this.reset();")
|
||||
) {
|
||||
input(.type(.hidden), .name("vendorID"), .value(vendor.id.uuidString))
|
||||
input(
|
||||
.type(.text), .class("col-9"), .name("name"), .placeholder("Add branch..."), .required,
|
||||
// FIX: route
|
||||
// .hx.post(route: .vendorBranch(.index(for: vendor.id))),
|
||||
.hx.trigger(.event(.keyup).changed().delay("800ms")),
|
||||
.hx.target("#branches"),
|
||||
.hx.swap(.beforeEnd) // ,
|
||||
// .custom(name: "hx-on::after-request", value: "if(event.detail.successful) this.reset();")
|
||||
)
|
||||
button(
|
||||
.type(.submit),
|
||||
.class("btn-secondary"),
|
||||
.style("float: right; padding: 10px 50px;"),
|
||||
.hx.target("#branch-table"),
|
||||
.hx.swap(.beforeEnd)
|
||||
) { "+" }
|
||||
}
|
||||
}
|
||||
|
||||
var branches: some HTML {
|
||||
ul(.id("branches")) {
|
||||
for branch in vendor.branches ?? [] {
|
||||
BranchRow(branch: branch)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct BranchRow: HTML {
|
||||
let branch: VendorBranch
|
||||
|
||||
var content: some HTML<HTMLTag.li> {
|
||||
li(.id(.branch(.row(id: branch.id))), .class("branch-row")) {
|
||||
span(.class("label")) { branch.name.capitalized }
|
||||
button(
|
||||
.class("btn"),
|
||||
.hx.delete(route: .vendorBranch(.delete(id: branch.id))),
|
||||
.hx.target(.id(.branch(.row(id: branch.id)))),
|
||||
.hx.swap(.outerHTML.transition(true).swap("0.5s"))
|
||||
) {
|
||||
img(.src("/images/trash-can.svg"), .width(30), .height(30), .style("margin-top: 5px;"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,7 +40,13 @@ public func configure(_ app: Application) async throws {
|
||||
|
||||
app.mount(
|
||||
SiteRoute.router,
|
||||
middleware: { $0.middleware() },
|
||||
middleware: {
|
||||
if app.environment == .testing {
|
||||
return nil
|
||||
} else {
|
||||
return $0.middleware()
|
||||
}
|
||||
},
|
||||
use: siteHandler
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user