import Elementary import ElementaryHTMX import Fluent import SharedModels import Vapor struct PurchaseOrderTable: HTML { typealias SearchContext = SharedModels.ViewRoute.PurchaseOrderRoute.Search.Context let page: Page let context: Context let searchContext: SearchContext? init( page: Page, context: Context = .default, searchContext: SearchContext? = nil ) { self.page = page self.context = context self.searchContext = searchContext } var content: some HTML { table(.id(.purchaseOrder())) { thead { buttonRow tableHeader } tbody(.id(.purchaseOrder(.table))) { Rows(page: page) } } } private var tableHeader: some HTML { tr { th { "PO" } th { "Work Order" } th { "Customer" } th { "Vendor" } th { "Materials" } th { "Created For" } th { if context != .search { Button.add() .attributes( .hx.get(route: .purchaseOrder(.form)), .hx.target(.id(.float)), .hx.swap(.outerHTML), .hx.pushURL(true) ) } } } } private var buttonRow: some HTML { tr { div(.class("btn-row")) { if context != .search { button( .id("btn-search"), .class("btn-primary"), .style("position: absolute; top: 80px; right: 20px;"), .hx.get(route: .purchaseOrder(.search(.index(context: .employee, table: true)))), .hx.target(.body), .hx.swap(.outerHTML.transition(true).swap("0.5s")), .hx.pushURL(true) ) { Img.search() } } } } } // Produces only the rows for the given page struct Rows: HTML { let page: Page var content: some HTML { for purchaseOrder in page.items { Row(purchaseOrder: purchaseOrder) } // We set page to 0 when we're on search, but have not completed the search // form yet, so don't add the infinite scroll row / trigger otherwise it will // load the first page, which is not what we want, but we need the empty table // to be available once the search form is completed. if page.metadata.page > 0, page.metadata.pageCount > page.metadata.page { tr( .hx.get(route: .purchaseOrder(.page(page: page.metadata.page + 1, limit: page.metadata.per))), .hx.trigger(.event(.revealed)), .hx.swap(.outerHTML.transition(true).swap("1s")), .hx.target(.this), .hx.indicator("next .htmx-indicator") ) { img(.src("/images/spinner.svg"), .class("htmx-indicator"), .width(60), .height(60)) } } } } // A single row. struct Row: HTML { let purchaseOrder: PurchaseOrder var content: some HTML { tr( .id(.purchaseOrder(.row(id: purchaseOrder.id))) ) { td { "\(purchaseOrder.id)" } td { purchaseOrder.workOrder != nil ? String(purchaseOrder.workOrder!) : "" } td { purchaseOrder.customer } td { purchaseOrder.vendorBranch.displayName } td { purchaseOrder.materials } td { purchaseOrder.createdFor.fullName } td { Button.detail() .attributes( .hx.get(route: .purchaseOrder(.get(id: purchaseOrder.id))), .hx.target(.id(.float)), .hx.swap(.outerHTML.transition(true).swap("0.5s")), .hx.pushURL(true) ) } } } } enum Context: String { case `default` case search } } private extension VendorBranch.Detail { var displayName: String { "\(vendor.name.capitalized) - \(name.capitalized)" } }