import Fluent import Leaf import Vapor struct EmployeeViewController: RouteCollection { private let api = ApiController() func boot(routes: any RoutesBuilder) throws { let employees = routes.protected.grouped("employees") employees.get(use: index(req:)) employees.get("form", use: employeeForm(req:)) employees.post(use: create(req:)) employees.group(":employeeID") { $0.get(use: edit(req:)) $0.delete(use: delete(req:)) $0.put(use: update(req:)) $0.post("toggle-active", use: toggleActive(req:)) } } @Sendable func index(req: Request) async throws -> View { return try await req.view.render("employees/index", EmployeesCTX(api: api, req: req)) } @Sendable func create(req: Request) async throws -> View { _ = try await api.createEmployee(req: req) return try await req.view.render("employees/index", EmployeesCTX(oob: true, api: api, req: req)) } @Sendable func toggleActive(req: Request) async throws -> View { guard let employee = try await Employee.find(req.parameters.get("employeeID"), on: req.db) else { throw Abort(.notFound) } employee.active.toggle() try await employee.save(on: req.db) let employees = try await api.getSortedEmployees(req: req) return try await req.view.render("employees/table", ["employees": employees]) } @Sendable func delete(req: Request) async throws -> View { _ = try await api.deleteEmployee(req: req) let employees = try await api.getSortedEmployees(req: req) return try await req.view.render("employees/table", ["employees": employees]) } @Sendable func edit(req: Request) async throws -> View { guard let employee = try await Employee.find(req.parameters.get("employeeID"), on: req.db) else { throw Abort(.notFound) } return try await req.view.render("employees/form", EmployeeFormCTX(employee: employee.toDTO())) } @Sendable func update(req: Request) async throws -> View { _ = try await api.updateEmployee(req: req) return try await req.view.render("employees/index", EmployeesCTX(oob: true, api: api, req: req)) } @Sendable func employeeForm(req: Request) async throws -> View { try await req.view.render("employees/form", EmployeeFormCTX()) } } private struct EmployeesCTX: Content { let oob: Bool let employees: [Employee.DTO] let form: EmployeeFormCTX init( oob: Bool = false, employee: Employee? = nil, api: ApiController, req: Request ) async throws { self.oob = oob self.employees = try await api.getSortedEmployees(req: req) self.form = .init(employee: employee.map { $0.toDTO() }) } } private struct EmployeeFormCTX: Content { let htmxForm: HtmxFormCTX init(employee: Employee.DTO? = nil) { self.htmxForm = .init( formClass: "employee-form", formId: "employee-form", htmxTargetUrl: employee?.id == nil ? .post("/employees") : .put("/employees/\(employee!.id!)"), htmxTarget: "#employee-table", htmxPushUrl: false, htmxResetAfterRequest: true, htmxSwapOob: nil, htmxSwap: employee == nil ? .outerHTML : nil, context: .init(employee: employee) ) } struct Context: Content { let employee: Employee.DTO? } } private extension ApiController { func getSortedEmployees(req: Request) async throws -> [Employee.DTO] { var employees = try await employeesIndex(req: req) employees.sort { ($0.active ?? false) && !($1.active ?? false) } employees.sort { ($0.lastName ?? "") < ($1.lastName ?? "") } return employees } }