feat: Reorganizes files.
This commit is contained in:
49
Sources/App/Controllers/DB/EmployeeDB.swift
Normal file
49
Sources/App/Controllers/DB/EmployeeDB.swift
Normal file
@@ -0,0 +1,49 @@
|
||||
import Fluent
|
||||
import Vapor
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
}
|
||||
65
Sources/App/Controllers/DB/PurchaseOrderDB.swift
Normal file
65
Sources/App/Controllers/DB/PurchaseOrderDB.swift
Normal file
@@ -0,0 +1,65 @@
|
||||
import Fluent
|
||||
import Vapor
|
||||
|
||||
// An intermediate between our api and view controllers that interacts with the
|
||||
// database.
|
||||
struct PurchaseOrderDB {
|
||||
|
||||
func create(
|
||||
_ model: PurchaseOrder.Create,
|
||||
createdById: User.IDValue,
|
||||
on db: any Database
|
||||
) async throws -> PurchaseOrder.DTO {
|
||||
guard let employee = try await Employee.find(model.createdForID, on: db) else {
|
||||
throw Abort(.notFound, reason: "Employee not found.")
|
||||
}
|
||||
|
||||
guard employee.active else {
|
||||
throw Abort(.badRequest, reason: "Employee is not active, unable to generate a PO for in-active employees")
|
||||
}
|
||||
|
||||
let purchaseOrder = model.toModel(createdByID: createdById)
|
||||
try await purchaseOrder.save(on: db)
|
||||
guard let loaded = try await get(id: purchaseOrder.requireID(), on: db) else {
|
||||
return purchaseOrder.toDTO()
|
||||
}
|
||||
return loaded
|
||||
}
|
||||
|
||||
func fetchAll(on db: any Database) async throws -> [PurchaseOrder.DTO] {
|
||||
try await PurchaseOrder.allQuery(on: db)
|
||||
.sort(\.$id, .descending)
|
||||
.all().map { $0.toDTO() }
|
||||
}
|
||||
|
||||
func fetchPage(_ page: Int, limit: Int, on db: any Database) async throws -> Page<PurchaseOrder.DTO> {
|
||||
try await PurchaseOrder.allQuery(on: db)
|
||||
.sort(\.$id, .descending)
|
||||
.paginate(PageRequest(page: page, per: limit))
|
||||
.map { $0.toDTO() }
|
||||
}
|
||||
|
||||
func get(id: PurchaseOrder.IDValue, on db: any Database) async throws -> PurchaseOrder.DTO? {
|
||||
try await PurchaseOrder.allQuery(on: db)
|
||||
.filter(\.$id == id)
|
||||
.first()?.toDTO()
|
||||
}
|
||||
|
||||
func delete(id: PurchaseOrder.IDValue, on db: any Database) async throws {
|
||||
guard let purchaseOrder = try await PurchaseOrder.find(id, on: db) else {
|
||||
throw Abort(.notFound)
|
||||
}
|
||||
try await purchaseOrder.delete(on: db)
|
||||
}
|
||||
}
|
||||
|
||||
private extension PurchaseOrder {
|
||||
static func allQuery(on db: any Database) -> QueryBuilder<PurchaseOrder> {
|
||||
PurchaseOrder.query(on: db)
|
||||
.with(\.$createdBy)
|
||||
.with(\.$createdFor)
|
||||
.with(\.$vendorBranch) { branch in
|
||||
branch.with(\.$vendor)
|
||||
}
|
||||
}
|
||||
}
|
||||
36
Sources/App/Controllers/DB/UserDB.swift
Normal file
36
Sources/App/Controllers/DB/UserDB.swift
Normal file
@@ -0,0 +1,36 @@
|
||||
import Fluent
|
||||
import Vapor
|
||||
|
||||
struct UserDB {
|
||||
|
||||
func create(_ model: User.Create, on db: any Database) async throws -> User.DTO {
|
||||
guard model.password == model.confirmPassword else {
|
||||
throw Abort(.badRequest, reason: "Passwords did not match.")
|
||||
}
|
||||
let user = try User(
|
||||
username: model.username,
|
||||
email: model.email,
|
||||
passwordHash: Bcrypt.hash(model.password)
|
||||
)
|
||||
try await user.save(on: db)
|
||||
return user.toDTO()
|
||||
}
|
||||
|
||||
func login(user: User, on db: any Database) async throws -> UserToken {
|
||||
let token = try user.generateToken()
|
||||
try await token.save(on: db)
|
||||
return token
|
||||
}
|
||||
|
||||
func fetchAll(on db: any Database) async throws -> [User.DTO] {
|
||||
try await User.query(on: db).all().map { $0.toDTO() }
|
||||
}
|
||||
|
||||
func delete(id: User.IDValue, on db: any Database) async throws {
|
||||
guard let user = try await User.find(id, on: db) else {
|
||||
throw Abort(.notFound)
|
||||
}
|
||||
try await user.delete(on: db)
|
||||
}
|
||||
|
||||
}
|
||||
62
Sources/App/Controllers/DB/VendorBranchDB.swift
Normal file
62
Sources/App/Controllers/DB/VendorBranchDB.swift
Normal file
@@ -0,0 +1,62 @@
|
||||
import Fluent
|
||||
import Vapor
|
||||
|
||||
struct VendorBranchDB {
|
||||
|
||||
func create(
|
||||
_ model: VendorBranch.Create,
|
||||
for vendorID: Vendor.IDValue,
|
||||
on db: any Database
|
||||
) async throws -> VendorBranch.DTO {
|
||||
let branch = model.toModel()
|
||||
guard let vendor = try await Vendor.find(vendorID, on: db) else {
|
||||
throw Abort(.badRequest, reason: "Vendor does not exist.")
|
||||
}
|
||||
try await vendor.$branches.create(branch, on: db)
|
||||
return branch.toDTO()
|
||||
}
|
||||
|
||||
func fetchAll(withVendor: Bool? = nil, on db: any Database) async throws -> [VendorBranch.DTO] {
|
||||
var query = VendorBranch.query(on: db)
|
||||
if withVendor == true {
|
||||
query = query.with(\.$vendor)
|
||||
}
|
||||
return try await query.all().map { $0.toDTO() }
|
||||
}
|
||||
|
||||
func fetch(for vendorID: Vendor.IDValue, on db: any Database) async throws -> [VendorBranch.DTO] {
|
||||
guard let vendor = try await Vendor.query(on: db)
|
||||
.filter(\.$id == vendorID)
|
||||
.with(\.$branches)
|
||||
.first()
|
||||
else {
|
||||
throw Abort(.notFound)
|
||||
}
|
||||
|
||||
return vendor.branches.map { $0.toDTO() }
|
||||
}
|
||||
|
||||
func get(id: VendorBranch.IDValue, on db: any Database) async throws -> VendorBranch.DTO? {
|
||||
try await VendorBranch.find(id, on: db).map { $0.toDTO() }
|
||||
}
|
||||
|
||||
func update(
|
||||
id: VendorBranch.IDValue,
|
||||
with updates: VendorBranch.Update,
|
||||
on db: any Database
|
||||
) async throws -> VendorBranch.DTO {
|
||||
guard let branch = try await VendorBranch.find(id, on: db) else {
|
||||
throw Abort(.notFound)
|
||||
}
|
||||
branch.applyUpdates(updates)
|
||||
try await branch.save(on: db)
|
||||
return branch.toDTO()
|
||||
}
|
||||
|
||||
func delete(id: VendorBranch.IDValue, on db: any Database) async throws {
|
||||
guard let branch = try await VendorBranch.find(id, on: db) else {
|
||||
throw Abort(.notFound)
|
||||
}
|
||||
try await branch.delete(on: db)
|
||||
}
|
||||
}
|
||||
47
Sources/App/Controllers/DB/VendorDB.swift
Normal file
47
Sources/App/Controllers/DB/VendorDB.swift
Normal file
@@ -0,0 +1,47 @@
|
||||
import Fluent
|
||||
import Vapor
|
||||
|
||||
struct VendorDB {
|
||||
func create(_ model: Vendor.Create, on db: any Database) async throws -> Vendor.DTO {
|
||||
let model = model.toModel()
|
||||
try await model.save(on: db)
|
||||
return model.toDTO()
|
||||
}
|
||||
|
||||
func fetchAll(withBranches: Bool? = nil, on db: any Database) async throws -> [Vendor.DTO] {
|
||||
var query = Vendor.query(on: db).sort(\.$name, .ascending)
|
||||
if withBranches == true {
|
||||
query = query.with(\.$branches)
|
||||
}
|
||||
return try await query.all().map { $0.toDTO(includeBranches: withBranches) }
|
||||
}
|
||||
|
||||
func get(id: Vendor.IDValue, withBranches: Bool? = nil, on db: any Database) async throws -> Vendor.DTO? {
|
||||
var query = Vendor.query(on: db).filter(\.$id == id)
|
||||
|
||||
if withBranches == true {
|
||||
query = query.with(\.$branches)
|
||||
}
|
||||
return try await query.first().map { $0.toDTO(includeBranches: withBranches) }
|
||||
}
|
||||
|
||||
func update(
|
||||
id: Vendor.IDValue,
|
||||
with updates: Vendor.Update,
|
||||
on db: any Database
|
||||
) async throws -> Vendor.DTO {
|
||||
guard let vendor = try await Vendor.find(id, on: db) else {
|
||||
throw Abort(.notFound)
|
||||
}
|
||||
vendor.applyUpdates(updates)
|
||||
return vendor.toDTO()
|
||||
}
|
||||
|
||||
func delete(id: Vendor.IDValue, on db: any Database) async throws {
|
||||
guard let vendor = try await Vendor.find(id, on: db) else {
|
||||
throw Abort(.notFound)
|
||||
}
|
||||
try await vendor.delete(on: db)
|
||||
}
|
||||
|
||||
}
|
||||
97
Sources/App/Controllers/View/Contexts/HtmxFormCTX.swift
Normal file
97
Sources/App/Controllers/View/Contexts/HtmxFormCTX.swift
Normal file
@@ -0,0 +1,97 @@
|
||||
import Vapor
|
||||
|
||||
/// Represents a generic form context that is used to generate form templates
|
||||
/// that are handled by htmx.
|
||||
struct HtmxFormCTX<Context: Content>: Content {
|
||||
let formClass: String?
|
||||
let formId: String
|
||||
let htmxPostTargetUrl: String?
|
||||
let htmxPutTargetUrl: String?
|
||||
let htmxTarget: String
|
||||
let htmxPushUrl: Bool
|
||||
let htmxResetAfterRequest: Bool
|
||||
let htmxSwapOob: String?
|
||||
let htmxSwap: String?
|
||||
let context: Context
|
||||
|
||||
init(
|
||||
formClass: String? = nil,
|
||||
formId: String,
|
||||
htmxTargetUrl: TargetUrl,
|
||||
htmxTarget: String,
|
||||
htmxPushUrl: Bool,
|
||||
htmxResetAfterRequest: Bool = true,
|
||||
htmxSwapOob: HtmxSwap? = nil,
|
||||
htmxSwap: HtmxSwap? = nil,
|
||||
context: Context
|
||||
) {
|
||||
self.formClass = formClass
|
||||
self.formId = formId
|
||||
self.htmxPostTargetUrl = htmxTargetUrl.postUrl
|
||||
self.htmxPutTargetUrl = htmxTargetUrl.putUrl
|
||||
self.htmxTarget = htmxTarget
|
||||
self.htmxPushUrl = htmxPushUrl
|
||||
self.htmxResetAfterRequest = htmxResetAfterRequest
|
||||
self.htmxSwapOob = htmxSwapOob?.rawValue
|
||||
self.htmxSwap = htmxSwap?.rawValue
|
||||
self.context = context
|
||||
}
|
||||
|
||||
enum HtmxSwap: String {
|
||||
case innerHTML
|
||||
case outerHTML
|
||||
case afterbegin
|
||||
case beforebegin
|
||||
case afterend
|
||||
case beforeend
|
||||
case delete
|
||||
case none
|
||||
}
|
||||
|
||||
enum TargetUrl {
|
||||
case put(String)
|
||||
case post(String)
|
||||
|
||||
var putUrl: String? {
|
||||
guard case let .put(url) = self else { return nil }
|
||||
return url
|
||||
}
|
||||
|
||||
var postUrl: String? {
|
||||
guard case let .post(url) = self else { return nil }
|
||||
return url
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct EmptyContent: Content {}
|
||||
|
||||
struct ButtonLabelContext: Content {
|
||||
let buttonLabel: String
|
||||
}
|
||||
|
||||
extension HtmxFormCTX where Context == ButtonLabelContext {
|
||||
init(
|
||||
formClass: String? = nil,
|
||||
formId: String,
|
||||
htmxTargetUrl: TargetUrl,
|
||||
htmxTarget: String,
|
||||
htmxPushUrl: Bool,
|
||||
htmxResetAfterRequest: Bool = true,
|
||||
htmxSwapOob: HtmxSwap? = nil,
|
||||
htmxSwap: HtmxSwap? = nil,
|
||||
buttonLabel: String
|
||||
) {
|
||||
self.init(
|
||||
formClass: formClass,
|
||||
formId: formId,
|
||||
htmxTargetUrl: htmxTargetUrl,
|
||||
htmxTarget: htmxTarget,
|
||||
htmxPushUrl: htmxPushUrl,
|
||||
htmxResetAfterRequest: htmxResetAfterRequest,
|
||||
htmxSwapOob: htmxSwapOob,
|
||||
htmxSwap: htmxSwapOob,
|
||||
context: .init(buttonLabel: buttonLabel)
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user