import Dependencies import Fluent import Leaf import Vapor struct EmployeeViewController: RouteCollection { @Dependency(\.employees) var employees private let api = EmployeeApiController() func boot(routes: any RoutesBuilder) throws { let protected = routes.protected.grouped("employees") protected.get(use: index(req:)) protected.get("form", use: employeeForm(req:)) protected.post(use: create(req:)) protected.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(db: employees)) } @Sendable func create(req: Request) async throws -> View { try Employee.Create.validate(content: req) let model = try req.content.decode(Employee.Create.self) _ = try await employees.create(model) return try await req.view.render("employees/index", EmployeesCTX(oob: true, db: employees)) } @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 employees.fetchAll() return try await req.view.render("employees/table", ["employees": employees]) } @Sendable func delete(req: Request) async throws -> View { _ = try await api.delete(req: req) let employees = try await employees.fetchAll() 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.update(req: req) return try await req.view.render("employees/index", EmployeesCTX(oob: true, db: employees)) } @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, db: EmployeeDB ) async throws { self.oob = oob self.employees = try await db.fetchAll() 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? } }