diff --git a/Resources/Views/employees/table-row.leaf b/Resources/Views/employees/table-row.leaf new file mode 100644 index 0000000..e7c8d4d --- /dev/null +++ b/Resources/Views/employees/table-row.leaf @@ -0,0 +1,40 @@ + + #capitalized(firstName) #capitalized(lastName) + + #if(active): + + Active + + #else: + + Active + + #endif + + + + #extend("img/trash-can") + + + #extend("img/pencil") + + + + diff --git a/Resources/Views/employees/table.leaf b/Resources/Views/employees/table.leaf index 8960d98..14e014b 100644 --- a/Resources/Views/employees/table.leaf +++ b/Resources/Views/employees/table.leaf @@ -17,46 +17,7 @@ #for(employee in employees): - - #capitalized(employee.firstName) #capitalized(employee.lastName) - - #if(employee.active): - - Active - - #else: - - Active - - #endif - - - - #extend("img/trash-can") - - - #extend("img/pencil") - - - - + #extend("employees/table-row", employee) #endfor diff --git a/Sources/App/Controllers/DB/EmployeeDB.swift b/Sources/App/Controllers/DB/EmployeeDB.swift index 930038d..1f445b9 100644 --- a/Sources/App/Controllers/DB/EmployeeDB.swift +++ b/Sources/App/Controllers/DB/EmployeeDB.swift @@ -19,10 +19,18 @@ struct EmployeeDB: Sendable { var get: @Sendable (Employee.IDValue) async throws -> Employee.DTO? var update: @Sendable (Employee.IDValue, Employee.Update) async throws -> Employee.DTO var delete: @Sendable (Employee.IDValue) async throws -> Void + var toggleActive: @Sendable (Employee.IDValue) async throws -> Employee.DTO func fetchAll() async throws -> [Employee.DTO] { try await fetchAll(false) } + + func get(_ id: String?) async throws -> Employee.DTO? { + guard let idString = id, let id = UUID(uuidString: idString) else { + throw Abort(.badRequest, reason: "Employee id not valid.") + } + return try await get(id) + } } extension EmployeeDB: TestDependencyKey { @@ -61,54 +69,15 @@ extension EmployeeDB: TestDependencyKey { throw Abort(.badRequest, reason: "Employee id not found.") } try await employee.delete(on: database) + }, + toggleActive: { id in + guard let employee = try await Employee.find(id, on: database) else { + throw Abort(.notFound) + } + employee.active.toggle() + try await employee.save(on: database) + return employee.toDTO() } ) } } - -// An intermediate layer between our api and view controllers that interacts with the -// database model. -// struct EmployeeDB { -// -// func create(_ model: Employee.Create, on db: any Database) async throws -> Employee.DTO { -// let model = model.toModel() -// try await model.save(on: db) -// return model.toDTO() -// } -// -// func fetchAll(active: Bool? = nil, on db: any Database) async throws -> [Employee.DTO] { -// var query = Employee.query(on: db) -// .sort(\.$lastName) -// -// if let active { -// query = query.filter(\.$active == active) -// } -// -// return try await query.all().map { $0.toDTO() } -// } -// -// func get(id: Employee.IDValue, on db: any Database) async throws -> Employee.DTO? { -// try await Employee.find(id, on: db).map { $0.toDTO() } -// } -// -// func update( -// id: Employee.IDValue, -// with updates: Employee.Update, -// on db: any Database -// ) async throws -> Employee.DTO { -// guard let employee = try await Employee.find(id, on: db) else { -// throw Abort(.badRequest, reason: "Employee id not found.") -// } -// employee.applyUpdates(updates) -// try await employee.save(on: db) -// return employee.toDTO() -// } -// -// func delete(id: Employee.IDValue, on db: any Database) async throws { -// guard let employee = try await Employee.find(id, on: db) else { -// throw Abort(.badRequest, reason: "Employee id not found.") -// } -// try await employee.delete(on: db) -// } -// -// } diff --git a/Sources/App/Controllers/View/EmployeeViewController.swift b/Sources/App/Controllers/View/EmployeeViewController.swift index 9b7b3fe..492cb1b 100644 --- a/Sources/App/Controllers/View/EmployeeViewController.swift +++ b/Sources/App/Controllers/View/EmployeeViewController.swift @@ -6,7 +6,6 @@ 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") @@ -17,7 +16,7 @@ struct EmployeeViewController: RouteCollection { $0.get(use: edit(req:)) $0.delete(use: delete(req:)) $0.put(use: update(req:)) - $0.post("toggle-active", use: toggleActive(req:)) + $0.patch("toggle-active", use: toggleActive(req:)) } } @@ -36,33 +35,35 @@ struct EmployeeViewController: RouteCollection { @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) + guard let id = req.parameters.get("employeeID", as: Employee.IDValue.self) else { + throw Abort(.badRequest, reason: "Employee id not supplied.") } - 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]) + let employee = try await employees.toggleActive(id) + return try await req.view.render("employees/table-row", employee) } @Sendable func delete(req: Request) async throws -> View { - _ = try await api.delete(req: req) + let id = try req.requireEmployeeID() + _ = try await employees.delete(id) 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 { + guard let employee = try await employees.get(req.parameters.get("employeeID")) else { throw Abort(.notFound) } - return try await req.view.render("employees/form", EmployeeFormCTX(employee: employee.toDTO())) + return try await req.view.render("employees/form", EmployeeFormCTX(employee: employee)) } @Sendable func update(req: Request) async throws -> View { - _ = try await api.update(req: req) + let id = try req.requireEmployeeID() + try Employee.Update.validate(content: req) + let updates = try req.content.decode(Employee.Update.self) + _ = try await employees.update(id, updates) return try await req.view.render("employees/index", EmployeesCTX(oob: true, db: employees)) } @@ -73,6 +74,15 @@ struct EmployeeViewController: RouteCollection { } +private extension Request { + func requireEmployeeID() throws -> Employee.IDValue { + guard let id = parameters.get("employeeID", as: Employee.IDValue.self) else { + throw Abort(.badRequest, reason: "Employee id not supplied") + } + return id + } +} + private struct EmployeesCTX: Content { let oob: Bool let employees: [Employee.DTO]