import Dependencies import Elementary import Fluent import SharedModels import Vapor import VaporElementary struct PurchaseOrderViewController: RouteCollection { @Dependency(\.database.employees) var employees @Dependency(\.database.vendorBranches) var vendorBranches @Dependency(\.database.purchaseOrders) var purchaseOrders func boot(routes: any RoutesBuilder) throws { let route = routes.protected.grouped("purchase-orders") route.get(use: index) route.get("next", use: nextPage) route.post(use: create(req:)) route.group("create") { $0.get(use: form) } route.group(":id") { $0.get(use: get) } } @Sendable func index(req: Request) async throws -> HTMLResponse { let page = try? req.query.decode(IndexQuery.self) return try await req.render { try await mainPage(PurchaseOrderForm(), page: page ?? .default) } } @Sendable func nextPage(req: Request) async throws -> HTMLResponse { let query = try req.query.decode(IndexQuery.self) let page = try await purchaseOrders.fetchPage(.init(page: query.page, per: query.limit)) return await req.render { PurchaseOrderTable.Rows(page: page) } } @Sendable func form(req: Request) async throws -> HTMLResponse { guard req.isHtmxRequest else { return try await req.render { try await mainPage(PurchaseOrderForm(shouldShow: true), page: .default) } } return await req.render { PurchaseOrderForm(shouldShow: true) } } @Sendable func get(req: Request) async throws -> HTMLResponse { let purchaseOrder = try await purchaseOrders.get(req.ensureIDPathComponent(as: Int.self)) let form = PurchaseOrderForm(purchaseOrder: purchaseOrder, shouldShow: true) guard req.isHtmxRequest else { return try await req.render { try await mainPage(form, page: .default) } } return await req.render { form } } @Sendable func create(req: Request) async throws -> HTMLResponse { let create = try req.content.decode(CreateContext.self).toIntermediate() let user = try req.auth.require(User.self) let purchaseOrder = try await purchaseOrders.create(create.toCreate(createdByID: user.id)) return await req.render { PurchaseOrderTable.Row(purchaseOrder: purchaseOrder) } } // @Sendable // func postSearch(req: Request) async throws -> HTMLResponse { // let context = try req.content.decode(PurchaseOrderSearchContent.self) // let results = try await purchaseOrders.search(context.toDatabaseQuery(), .init(page: 1, per: 25)) // return await req.render { PurchaseOrderTable(page: results, context: .search, searchContext: nil) } // } // Show the form to generate a search query. // @Sendable // func getSearch(req: Request) async throws -> HTMLResponse { // // TODO: Need to handle updating the form. // return await req.render { // MainPage(displayNav: true, route: .purchaseOrders) { // div(.class("container"), .id("purchase-order-content")) { // PurchaseOrderSearch() // PurchaseOrderTable(page: .init(items: [], metadata: .init(page: 0, per: 50, total: 0))) // } // } // } // } private func mainPage( _ html: C, page: IndexQuery ) async throws -> some SendableHTMLDocument where C: Sendable { let page = try await purchaseOrders.fetchPage(.init(page: page.page, per: page.limit)) return MainPage(displayNav: true, route: .purchaseOrders) { div(.class("container"), .id("purchase-order-content")) { html PurchaseOrderTable(page: page) } } } } struct IndexQuery: Content { let page: Int let limit: Int static var `default`: Self { .init(page: 1, limit: 25) } } private struct CreateContext: Content { let id: Int? let workOrder: String let materials: String let customer: String let truckStock: Bool? let createdForID: Employee.ID let vendorBranchID: VendorBranch.ID func toIntermediate() -> PurchaseOrder.CreateIntermediate { .init( id: id, workOrder: workOrder.isEmpty ? nil : Int(workOrder), materials: materials, customer: customer, truckStock: truckStock, createdForID: createdForID, vendorBranchID: vendorBranchID ) } }