feat: Begins migrating views from leaf to elementary
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"originHash" : "5e46aec5c52d22d116c033b012d49986e5f900c102b88ac80e054d2e6e64d458",
|
"originHash" : "b5426b686e9548f4fc69224d4e0f13d10ce55816d5b71622f5f446b12d66140a",
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
{
|
||||||
"identity" : "async-http-client",
|
"identity" : "async-http-client",
|
||||||
@@ -37,6 +37,24 @@
|
|||||||
"version" : "4.15.1"
|
"version" : "4.15.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"identity" : "elementary",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/sliemeobn/elementary.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "9830c5e1b6740d367b28d416797ff4e6b007e541",
|
||||||
|
"version" : "0.4.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "elementary-htmx",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/sliemeobn/elementary-htmx.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "6e8430d24a6dc2de9d16270a6af985eb37a190ad",
|
||||||
|
"version" : "0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"identity" : "fluent",
|
"identity" : "fluent",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
@@ -325,6 +343,15 @@
|
|||||||
"version" : "4.111.0"
|
"version" : "4.111.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"identity" : "vapor-elementary",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/vapor-community/vapor-elementary.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "a14bc5d995f06fa2c398fa7a6bb6f98f1fd54446",
|
||||||
|
"version" : "0.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"identity" : "websocket-kit",
|
"identity" : "websocket-kit",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
|||||||
@@ -23,7 +23,10 @@ let package = Package(
|
|||||||
.package(url: "https://github.com/vapor/leaf.git", from: "4.3.0"),
|
.package(url: "https://github.com/vapor/leaf.git", from: "4.3.0"),
|
||||||
// 🔵 Non-blocking, event-driven networking for Swift. Used for custom executors
|
// 🔵 Non-blocking, event-driven networking for Swift. Used for custom executors
|
||||||
.package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"),
|
.package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"),
|
||||||
.package(url: "https://github.com/pointfreeco/swift-dependencies.git", from: "1.6.3")
|
.package(url: "https://github.com/pointfreeco/swift-dependencies.git", from: "1.6.3"),
|
||||||
|
.package(url: "https://github.com/sliemeobn/elementary.git", from: "0.3.2"),
|
||||||
|
.package(url: "https://github.com/sliemeobn/elementary-htmx.git", from: "0.4.0"),
|
||||||
|
.package(url: "https://github.com/vapor-community/vapor-elementary.git", from: "0.1.0")
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.executableTarget(
|
.executableTarget(
|
||||||
@@ -37,7 +40,10 @@ let package = Package(
|
|||||||
.product(name: "NIOCore", package: "swift-nio"),
|
.product(name: "NIOCore", package: "swift-nio"),
|
||||||
.product(name: "NIOPosix", package: "swift-nio"),
|
.product(name: "NIOPosix", package: "swift-nio"),
|
||||||
.product(name: "Dependencies", package: "swift-dependencies"),
|
.product(name: "Dependencies", package: "swift-dependencies"),
|
||||||
.product(name: "DependenciesMacros", package: "swift-dependencies")
|
.product(name: "DependenciesMacros", package: "swift-dependencies"),
|
||||||
|
.product(name: "Elementary", package: "elementary"),
|
||||||
|
.product(name: "VaporElementary", package: "vapor-elementary"),
|
||||||
|
.product(name: "ElementaryHTMX", package: "elementary-htmx")
|
||||||
|
|
||||||
],
|
],
|
||||||
swiftSettings: swiftSettings
|
swiftSettings: swiftSettings
|
||||||
|
|||||||
1
Public/images/menu.svg
Normal file
1
Public/images/menu.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <g id="Menu / Hamburger_MD"> <path id="Vector" d="M5 17H19M5 12H19M5 7H19" stroke="#929292" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path> </g> </g></svg>
|
||||||
|
After Width: | Height: | Size: 409 B |
@@ -1,88 +0,0 @@
|
|||||||
// import Dependencies
|
|
||||||
// import DependenciesMacros
|
|
||||||
// import Fluent
|
|
||||||
// import Vapor
|
|
||||||
//
|
|
||||||
// extension DependencyValues {
|
|
||||||
// // An intermediate layer between our api and view controllers that interacts with the
|
|
||||||
// // database model.
|
|
||||||
// var employees: EmployeeDB {
|
|
||||||
// get { self[EmployeeDB.self] }
|
|
||||||
// set { self[EmployeeDB.self] = newValue }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @DependencyClient
|
|
||||||
// struct EmployeeDB: Sendable {
|
|
||||||
// var create: @Sendable (Employee.Create) async throws -> Employee.DTO
|
|
||||||
// var fetchAll: @Sendable (FetchRequest) async throws -> [Employee.DTO]
|
|
||||||
// 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
|
|
||||||
//
|
|
||||||
// enum FetchRequest {
|
|
||||||
// case active
|
|
||||||
// case `default`
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func fetchAll() async throws -> [Employee.DTO] {
|
|
||||||
// try await fetchAll(.default)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// 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 {
|
|
||||||
// static let testValue: EmployeeDB = Self()
|
|
||||||
//
|
|
||||||
// static func live(database: any Database) -> Self {
|
|
||||||
// .init(
|
|
||||||
// create: { model in
|
|
||||||
// let model = model.toModel()
|
|
||||||
// try await model.save(on: database)
|
|
||||||
// return model.toDTO()
|
|
||||||
// },
|
|
||||||
// fetchAll: { request in
|
|
||||||
// var query = Employee.query(on: database)
|
|
||||||
// .sort(\.$lastName)
|
|
||||||
//
|
|
||||||
// if request == .active {
|
|
||||||
// query = query.filter(\.$active == true)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return try await query.all().map { $0.toDTO() }
|
|
||||||
// },
|
|
||||||
// get: { id in
|
|
||||||
// try await Employee.find(id, on: database).map { $0.toDTO() }
|
|
||||||
// },
|
|
||||||
// update: { id, updates in
|
|
||||||
// guard let employee = try await Employee.find(id, on: database) else {
|
|
||||||
// throw Abort(.badRequest, reason: "Employee id not found.")
|
|
||||||
// }
|
|
||||||
// employee.applyUpdates(updates)
|
|
||||||
// try await employee.save(on: database)
|
|
||||||
// return employee.toDTO()
|
|
||||||
// },
|
|
||||||
// delete: { id in
|
|
||||||
// guard let employee = try await Employee.find(id, on: database) else {
|
|
||||||
// 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()
|
|
||||||
// }
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
// import Dependencies
|
|
||||||
// import DependenciesMacros
|
|
||||||
// import Fluent
|
|
||||||
// import Vapor
|
|
||||||
//
|
|
||||||
// extension DependencyValues {
|
|
||||||
// // An intermediate between our api and view controllers that interacts with the
|
|
||||||
// // database.
|
|
||||||
// var purchaseOrders: PurchaseOrdersDB {
|
|
||||||
// get { self[PurchaseOrdersDB.self] }
|
|
||||||
// set { self[PurchaseOrdersDB.self] = newValue }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @DependencyClient
|
|
||||||
// struct PurchaseOrdersDB: Sendable {
|
|
||||||
// var create: @Sendable (PurchaseOrder.Create, User.IDValue) async throws -> PurchaseOrder.DTO
|
|
||||||
// var fetchAll: @Sendable () async throws -> [PurchaseOrder.DTO]
|
|
||||||
// var fetchPage: @Sendable (PageRequest) async throws -> Page<PurchaseOrder.DTO>
|
|
||||||
// var get: @Sendable (PurchaseOrder.IDValue) async throws -> PurchaseOrder.DTO?
|
|
||||||
// // var update: @Sendable (PurchaseOrder.IDValue, PurchaseOrder.Update) async throws -> PurchaseOrder.DTO
|
|
||||||
// var delete: @Sendable (PurchaseOrder.IDValue) async throws -> Void
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// extension PurchaseOrdersDB: TestDependencyKey {
|
|
||||||
// static let testValue: PurchaseOrdersDB = Self()
|
|
||||||
//
|
|
||||||
// static func live(database db: any Database) -> Self {
|
|
||||||
// .init(
|
|
||||||
// create: { model, createdById in
|
|
||||||
// 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 PurchaseOrder.get(purchaseOrder.requireID(), on: db) else {
|
|
||||||
// return purchaseOrder.toDTO()
|
|
||||||
// }
|
|
||||||
// return loaded
|
|
||||||
//
|
|
||||||
// },
|
|
||||||
// fetchAll: {
|
|
||||||
// try await PurchaseOrder.allQuery(on: db)
|
|
||||||
// .sort(\.$id, .descending)
|
|
||||||
// .all().map { $0.toDTO() }
|
|
||||||
// },
|
|
||||||
// fetchPage: { request in
|
|
||||||
// try await PurchaseOrder.allQuery(on: db)
|
|
||||||
// .sort(\.$id, .descending)
|
|
||||||
// .paginate(request)
|
|
||||||
// .map { $0.toDTO() }
|
|
||||||
// },
|
|
||||||
// get: { id in
|
|
||||||
// try await PurchaseOrder.get(id, on: db)
|
|
||||||
// },
|
|
||||||
// delete: { id in
|
|
||||||
// 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)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// static func get(_ id: PurchaseOrder.IDValue, on db: any Database) async throws -> PurchaseOrder.DTO? {
|
|
||||||
// try await PurchaseOrder.allQuery(on: db)
|
|
||||||
// .filter(\.$id == id)
|
|
||||||
// .first()?.toDTO()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
// import Dependencies
|
|
||||||
// import DependenciesMacros
|
|
||||||
// import Fluent
|
|
||||||
// import Vapor
|
|
||||||
//
|
|
||||||
// extension DependencyValues {
|
|
||||||
// var users: UserDB {
|
|
||||||
// get { self[UserDB.self] }
|
|
||||||
// set { self[UserDB.self] = newValue }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @DependencyClient
|
|
||||||
// struct UserDB: Sendable {
|
|
||||||
// var create: @Sendable (User.Create) async throws -> User.DTO
|
|
||||||
// var delete: @Sendable (User.IDValue) async throws -> Void
|
|
||||||
// var fetchAll: @Sendable () async throws -> [User.DTO]
|
|
||||||
// var get: @Sendable (User.IDValue) async throws -> User.DTO?
|
|
||||||
// var login: @Sendable (User) async throws -> UserToken
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// extension UserDB: TestDependencyKey {
|
|
||||||
// static let testValue: UserDB = Self()
|
|
||||||
//
|
|
||||||
// static func live(database db: any Database) -> Self {
|
|
||||||
// self.init(
|
|
||||||
// create: { model in
|
|
||||||
// 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()
|
|
||||||
//
|
|
||||||
// },
|
|
||||||
// delete: { id in
|
|
||||||
// guard let user = try await User.find(id, on: db) else {
|
|
||||||
// throw Abort(.notFound)
|
|
||||||
// }
|
|
||||||
// try await user.delete(on: db)
|
|
||||||
//
|
|
||||||
// },
|
|
||||||
// fetchAll: {
|
|
||||||
// try await User.query(on: db).all().map { $0.toDTO() }
|
|
||||||
// },
|
|
||||||
// get: { id in
|
|
||||||
// try await User.find(id, on: db).map { $0.toDTO() }
|
|
||||||
// },
|
|
||||||
// login: { user in
|
|
||||||
// let token = try user.generateToken()
|
|
||||||
// try await token.save(on: db)
|
|
||||||
// return token
|
|
||||||
// }
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
// import Dependencies
|
|
||||||
// import DependenciesMacros
|
|
||||||
// import Fluent
|
|
||||||
// import Vapor
|
|
||||||
//
|
|
||||||
// public extension DependencyValues {
|
|
||||||
// var vendorBranches: VendorBranchDB {
|
|
||||||
// get { self[VendorBranchDB.self] }
|
|
||||||
// set { self[VendorBranchDB.self] = newValue }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @DependencyClient
|
|
||||||
// public struct VendorBranchDB: Sendable {
|
|
||||||
// var create: @Sendable (VendorBranch.Create, Vendor.IDValue) async throws -> VendorBranch.DTO
|
|
||||||
// var delete: @Sendable (VendorBranch.IDValue) async throws -> Void
|
|
||||||
// var fetchAll: @Sendable (FetchRequest) async throws -> [VendorBranch.DTO]
|
|
||||||
// var get: @Sendable (VendorBranch.IDValue) async throws -> VendorBranch.DTO?
|
|
||||||
// var update: @Sendable (VendorBranch.IDValue, VendorBranch.Update) async throws -> VendorBranch.DTO
|
|
||||||
//
|
|
||||||
// enum FetchRequest: Equatable {
|
|
||||||
// case `default`
|
|
||||||
// case `for`(vendorID: Vendor.IDValue)
|
|
||||||
// case withVendor
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func fetchAll() async throws -> [VendorBranch.DTO] {
|
|
||||||
// try await fetchAll(.default)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// extension VendorBranchDB: TestDependencyKey {
|
|
||||||
// public static let testValue: VendorBranchDB = Self()
|
|
||||||
//
|
|
||||||
// static func live(database db: any Database) -> Self {
|
|
||||||
// .init(
|
|
||||||
// create: { model, vendorID in
|
|
||||||
// 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()
|
|
||||||
// },
|
|
||||||
// delete: { id in
|
|
||||||
// guard let branch = try await VendorBranch.find(id, on: db) else {
|
|
||||||
// throw Abort(.notFound)
|
|
||||||
// }
|
|
||||||
// try await branch.delete(on: db)
|
|
||||||
// },
|
|
||||||
// fetchAll: { request in
|
|
||||||
// var query = VendorBranch.query(on: db)
|
|
||||||
// switch request {
|
|
||||||
// case .withVendor:
|
|
||||||
// query = query.with(\.$vendor)
|
|
||||||
//
|
|
||||||
// case let .for(vendorID: vendorID):
|
|
||||||
// let branches = try await Vendor.query(on: db)
|
|
||||||
// .filter(\.$id == vendorID)
|
|
||||||
// .with(\.$branches)
|
|
||||||
// .first()?
|
|
||||||
// .branches
|
|
||||||
// .map { $0.toDTO() }
|
|
||||||
//
|
|
||||||
// guard let branches else { throw Abort(.badGateway, reason: "Vendor id not found.") }
|
|
||||||
// return branches
|
|
||||||
//
|
|
||||||
// case .default:
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// return try await query.all().map { $0.toDTO() }
|
|
||||||
//
|
|
||||||
// },
|
|
||||||
// get: { id in
|
|
||||||
// try await VendorBranch.find(id, on: db).map { $0.toDTO() }
|
|
||||||
// },
|
|
||||||
// update: { id, updates in
|
|
||||||
// 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()
|
|
||||||
// }
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
// import Dependencies
|
|
||||||
// import DependenciesMacros
|
|
||||||
// import Fluent
|
|
||||||
// import Vapor
|
|
||||||
//
|
|
||||||
// public extension DependencyValues {
|
|
||||||
// var vendors: VendorDB {
|
|
||||||
// get { self[VendorDB.self] }
|
|
||||||
// set { self[VendorDB.self] = newValue }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @DependencyClient
|
|
||||||
// public struct VendorDB: Sendable {
|
|
||||||
// var create: @Sendable (Vendor.Create) async throws -> Vendor.DTO
|
|
||||||
// var delete: @Sendable (Vendor.IDValue) async throws -> Void
|
|
||||||
// var fetchAll: @Sendable (FetchRequest) async throws -> [Vendor.DTO]
|
|
||||||
// var get: @Sendable (Vendor.IDValue, GetRequest) async throws -> Vendor.DTO?
|
|
||||||
// var update: @Sendable (Vendor.IDValue, Vendor.Update) async throws -> Vendor.DTO
|
|
||||||
//
|
|
||||||
// enum FetchRequest {
|
|
||||||
// case `default`
|
|
||||||
// case withBranches
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// enum GetRequest {
|
|
||||||
// case `default`
|
|
||||||
// case withBranches
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func fetchAll() async throws -> [Vendor.DTO] {
|
|
||||||
// try await fetchAll(.default)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func get(_ id: Vendor.IDValue) async throws -> Vendor.DTO? {
|
|
||||||
// try await get(id, .default)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// extension VendorDB: TestDependencyKey {
|
|
||||||
// public static let testValue: VendorDB = Self()
|
|
||||||
//
|
|
||||||
// static func live(database db: any Database) -> Self {
|
|
||||||
// .init(
|
|
||||||
// create: { model in
|
|
||||||
// let model = model.toModel()
|
|
||||||
// try await model.save(on: db)
|
|
||||||
// return model.toDTO()
|
|
||||||
//
|
|
||||||
// },
|
|
||||||
// delete: { id in
|
|
||||||
// guard let vendor = try await Vendor.find(id, on: db) else {
|
|
||||||
// throw Abort(.notFound)
|
|
||||||
// }
|
|
||||||
// try await vendor.delete(on: db)
|
|
||||||
//
|
|
||||||
// },
|
|
||||||
// fetchAll: { request in
|
|
||||||
// var query = Vendor.query(on: db).sort(\.$name, .ascending)
|
|
||||||
// let withBranches = request == .withBranches
|
|
||||||
// if withBranches {
|
|
||||||
// query = query.with(\.$branches)
|
|
||||||
// }
|
|
||||||
// return try await query.all().map { $0.toDTO(includeBranches: withBranches) }
|
|
||||||
//
|
|
||||||
// },
|
|
||||||
// get: { id, request in
|
|
||||||
// var query = Vendor.query(on: db).filter(\.$id == id)
|
|
||||||
// let withBranches = request == .withBranches
|
|
||||||
// if withBranches {
|
|
||||||
// query = query.with(\.$branches)
|
|
||||||
// }
|
|
||||||
// return try await query.first().map { $0.toDTO(includeBranches: withBranches) }
|
|
||||||
//
|
|
||||||
// },
|
|
||||||
// update: { id, updates in
|
|
||||||
// guard let vendor = try await Vendor.find(id, on: db) else {
|
|
||||||
// throw Abort(.notFound)
|
|
||||||
// }
|
|
||||||
// vendor.applyUpdates(updates)
|
|
||||||
// return vendor.toDTO()
|
|
||||||
// }
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
23
Sources/App/DependenciesMiddleware.swift
Normal file
23
Sources/App/DependenciesMiddleware.swift
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import DatabaseClientLive
|
||||||
|
import Dependencies
|
||||||
|
import Vapor
|
||||||
|
|
||||||
|
struct DependenciesMiddleware: AsyncMiddleware {
|
||||||
|
|
||||||
|
private let values: DependencyValues.Continuation
|
||||||
|
|
||||||
|
init() {
|
||||||
|
self.values = withEscapedDependencies { $0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
func respond(to request: Request, chainingTo next: any AsyncResponder) async throws -> Response {
|
||||||
|
try await values.yield {
|
||||||
|
try await withDependencies {
|
||||||
|
$0.database = .live(database: request.db)
|
||||||
|
} operation: {
|
||||||
|
try await next.respond(to: request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
// import Fluent
|
|
||||||
// import struct Foundation.UUID
|
|
||||||
// import Vapor
|
|
||||||
//
|
|
||||||
// // TODO: Add soft-delete??
|
|
||||||
//
|
|
||||||
// /// The employee database model.
|
|
||||||
// ///
|
|
||||||
// /// An employee is someone that PO's can be generated for. They can be either a field
|
|
||||||
// /// employee / technician, an office employee, or an administrator.
|
|
||||||
// ///
|
|
||||||
// /// # NOTE: Only `User` types can login and generate po's for employees.
|
|
||||||
// ///
|
|
||||||
// final class Employee: Model, @unchecked Sendable {
|
|
||||||
//
|
|
||||||
// static let schema = "employee"
|
|
||||||
//
|
|
||||||
// @ID(key: .id)
|
|
||||||
// var id: UUID?
|
|
||||||
//
|
|
||||||
// @Field(key: "first_name")
|
|
||||||
// var firstName: String
|
|
||||||
//
|
|
||||||
// @Field(key: "last_name")
|
|
||||||
// var lastName: String
|
|
||||||
//
|
|
||||||
// @Field(key: "is_active")
|
|
||||||
// var active: Bool
|
|
||||||
//
|
|
||||||
// @Timestamp(key: "created_at", on: .create)
|
|
||||||
// var createdAt: Date?
|
|
||||||
//
|
|
||||||
// @Timestamp(key: "updated_at", on: .update)
|
|
||||||
// var updatedAt: Date?
|
|
||||||
//
|
|
||||||
// init() {}
|
|
||||||
//
|
|
||||||
// init(
|
|
||||||
// id: UUID? = nil,
|
|
||||||
// firstName: String,
|
|
||||||
// lastName: String,
|
|
||||||
// active: Bool,
|
|
||||||
// createdAt: Date? = nil,
|
|
||||||
// updatedAt: Date? = nil
|
|
||||||
// ) {
|
|
||||||
// self.id = id
|
|
||||||
// self.firstName = firstName
|
|
||||||
// self.lastName = lastName
|
|
||||||
// self.active = active
|
|
||||||
// self.createdAt = createdAt
|
|
||||||
// self.updatedAt = updatedAt
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func toDTO() -> DTO {
|
|
||||||
// .init(
|
|
||||||
// id: id,
|
|
||||||
// firstName: $firstName.value,
|
|
||||||
// lastName: $lastName.value,
|
|
||||||
// active: $active.value,
|
|
||||||
// createdAt: createdAt,
|
|
||||||
// updatedAt: updatedAt
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func applyUpdates(_ updates: Update) {
|
|
||||||
// if let firstName = updates.firstName {
|
|
||||||
// self.firstName = firstName
|
|
||||||
// }
|
|
||||||
// if let lastName = updates.lastName {
|
|
||||||
// self.lastName = lastName
|
|
||||||
// }
|
|
||||||
// if let active = updates.active {
|
|
||||||
// self.active = active
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // MARK: - Helpers
|
|
||||||
//
|
|
||||||
// extension Employee {
|
|
||||||
//
|
|
||||||
// struct Create: Content {
|
|
||||||
// let firstName: String
|
|
||||||
// let lastName: String
|
|
||||||
// let active: Bool?
|
|
||||||
//
|
|
||||||
// func toModel() -> Employee {
|
|
||||||
// .init(
|
|
||||||
// firstName: firstName,
|
|
||||||
// lastName: lastName,
|
|
||||||
// active: active ?? true
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// struct DTO: Content {
|
|
||||||
//
|
|
||||||
// var id: UUID?
|
|
||||||
// var firstName: String?
|
|
||||||
// var lastName: String?
|
|
||||||
// var active: Bool?
|
|
||||||
// var createdAt: Date?
|
|
||||||
// var updatedAt: Date?
|
|
||||||
//
|
|
||||||
// func toModel() -> Employee {
|
|
||||||
// let model = Employee()
|
|
||||||
//
|
|
||||||
// model.id = id
|
|
||||||
// if let firstName {
|
|
||||||
// model.firstName = firstName
|
|
||||||
// }
|
|
||||||
// if let lastName {
|
|
||||||
// model.lastName = lastName
|
|
||||||
// }
|
|
||||||
// if let active {
|
|
||||||
// model.active = active
|
|
||||||
// }
|
|
||||||
// return model
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// struct Migrate: AsyncMigration {
|
|
||||||
//
|
|
||||||
// let name = "CreateEmployee"
|
|
||||||
//
|
|
||||||
// func prepare(on database: any Database) async throws {
|
|
||||||
// try await database.schema(Employee.schema)
|
|
||||||
// .id()
|
|
||||||
// .field("first_name", .string, .required)
|
|
||||||
// .field("last_name", .string, .required)
|
|
||||||
// .field("is_active", .bool, .required, .sql(.default(true)))
|
|
||||||
// .field("created_at", .datetime)
|
|
||||||
// .field("updated_at", .datetime)
|
|
||||||
// .unique(on: "first_name", "last_name")
|
|
||||||
// .create()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func revert(on database: any Database) async throws {
|
|
||||||
// try await database.schema(Employee.schema).delete()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// struct Update: Content {
|
|
||||||
// var firstName: String?
|
|
||||||
// var lastName: String?
|
|
||||||
// var active: Bool?
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // MARK: - Validations
|
|
||||||
//
|
|
||||||
// extension Employee.Create: Validatable {
|
|
||||||
// static func validations(_ validations: inout Validations) {
|
|
||||||
// validations.add("firstName", as: String.self, is: !.empty)
|
|
||||||
// validations.add("lastName", as: String.self, is: !.empty)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// extension Employee.Update: Validatable {
|
|
||||||
// static func validations(_ validations: inout Validations) {
|
|
||||||
// validations.add("firstName", as: String?.self, is: .nil || !.empty, required: false)
|
|
||||||
// validations.add("lastName", as: String?.self, is: .nil || !.empty, required: false)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
// import Fluent
|
|
||||||
// import Vapor
|
|
||||||
//
|
|
||||||
// /// The purchase order database model.
|
|
||||||
// ///
|
|
||||||
// /// # NOTE: An initial purchase order should be created with an `id` higher than our current PO
|
|
||||||
// /// so that subsequent PO's are generated with higher values than our current system produces.
|
|
||||||
// /// once the first one is set, the rest will auto-increment from there.
|
|
||||||
// final class PurchaseOrder: Model, Content, @unchecked Sendable {
|
|
||||||
// static let schema = "purchase_order"
|
|
||||||
//
|
|
||||||
// @ID(custom: "id", generatedBy: .database)
|
|
||||||
// var id: Int?
|
|
||||||
//
|
|
||||||
// @Field(key: "work_order")
|
|
||||||
// var workOrder: Int?
|
|
||||||
//
|
|
||||||
// @Field(key: "materials")
|
|
||||||
// var materials: String
|
|
||||||
//
|
|
||||||
// @Field(key: "customer")
|
|
||||||
// var customer: String
|
|
||||||
//
|
|
||||||
// @Field(key: "truck_stock")
|
|
||||||
// var truckStock: Bool
|
|
||||||
//
|
|
||||||
// @Parent(key: "created_by_id")
|
|
||||||
// var createdBy: User
|
|
||||||
//
|
|
||||||
// @Parent(key: "created_for_id")
|
|
||||||
// var createdFor: Employee
|
|
||||||
//
|
|
||||||
// @Parent(key: "vendor_branch_id")
|
|
||||||
// var vendorBranch: VendorBranch
|
|
||||||
//
|
|
||||||
// @Timestamp(key: "created_at", on: .create)
|
|
||||||
// var createdAt: Date?
|
|
||||||
//
|
|
||||||
// @Timestamp(key: "updated_at", on: .update)
|
|
||||||
// var updatedAt: Date?
|
|
||||||
//
|
|
||||||
// init() {}
|
|
||||||
//
|
|
||||||
// init(
|
|
||||||
// id: Int? = nil,
|
|
||||||
// workOrder: Int? = nil,
|
|
||||||
// materials: String,
|
|
||||||
// customer: String,
|
|
||||||
// truckStock: Bool,
|
|
||||||
// createdByID: User.IDValue,
|
|
||||||
// createdForID: Employee.IDValue,
|
|
||||||
// vendorBranchID: VendorBranch.IDValue,
|
|
||||||
// createdAt: Date? = nil,
|
|
||||||
// updatedAt: Date? = nil
|
|
||||||
// ) {
|
|
||||||
// self.id = id
|
|
||||||
// self.workOrder = workOrder
|
|
||||||
// self.materials = materials
|
|
||||||
// self.customer = customer
|
|
||||||
// self.truckStock = truckStock
|
|
||||||
// $createdBy.id = createdByID
|
|
||||||
// $createdFor.id = createdForID
|
|
||||||
// $vendorBranch.id = vendorBranchID
|
|
||||||
// self.createdAt = createdAt
|
|
||||||
// self.updatedAt = updatedAt
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func toDTO() -> DTO {
|
|
||||||
// .init(
|
|
||||||
// id: id,
|
|
||||||
// workOrder: workOrder,
|
|
||||||
// materials: materials,
|
|
||||||
// customer: customer,
|
|
||||||
// truckStock: truckStock,
|
|
||||||
// createdBy: $createdBy.value?.toDTO(),
|
|
||||||
// createdFor: $createdFor.value?.toDTO(),
|
|
||||||
// vendorBranch: $vendorBranch.value,
|
|
||||||
// createdAt: createdAt,
|
|
||||||
// updatedAt: updatedAt
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// extension PurchaseOrder {
|
|
||||||
//
|
|
||||||
// struct Create: Content {
|
|
||||||
// let id: Int?
|
|
||||||
// let workOrder: Int?
|
|
||||||
// let materials: String
|
|
||||||
// let customer: String
|
|
||||||
// let truckStock: Bool?
|
|
||||||
// let createdForID: Employee.IDValue
|
|
||||||
// let vendorBranchID: VendorBranch.IDValue
|
|
||||||
//
|
|
||||||
// func toModel(createdByID: User.IDValue) -> PurchaseOrder {
|
|
||||||
// .init(
|
|
||||||
// id: id,
|
|
||||||
// workOrder: workOrder,
|
|
||||||
// materials: materials,
|
|
||||||
// customer: customer,
|
|
||||||
// truckStock: truckStock ?? false,
|
|
||||||
// createdByID: createdByID,
|
|
||||||
// createdForID: createdForID,
|
|
||||||
// vendorBranchID: vendorBranchID,
|
|
||||||
// createdAt: nil,
|
|
||||||
// updatedAt: nil
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// struct DTO: Content {
|
|
||||||
// let id: Int?
|
|
||||||
// let workOrder: Int?
|
|
||||||
// let materials: String
|
|
||||||
// let customer: String
|
|
||||||
// let truckStock: Bool
|
|
||||||
// let createdBy: User.DTO?
|
|
||||||
// let createdFor: Employee.DTO?
|
|
||||||
// let vendorBranch: VendorBranch?
|
|
||||||
// let createdAt: Date?
|
|
||||||
// let updatedAt: Date?
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// struct Migrate: AsyncMigration {
|
|
||||||
//
|
|
||||||
// let name = "CreatePurchaseOrder"
|
|
||||||
//
|
|
||||||
// func prepare(on database: any Database) async throws {
|
|
||||||
// try await database.schema(PurchaseOrder.schema)
|
|
||||||
// .field("id", .int, .identifier(auto: true))
|
|
||||||
// .field("work_order", .int)
|
|
||||||
// .field("customer", .string, .required)
|
|
||||||
// .field("materials", .string, .required)
|
|
||||||
// .field("truck_stock", .bool, .required)
|
|
||||||
// .field("created_by_id", .uuid, .required, .references(User.schema, "id"))
|
|
||||||
// .field("created_for_id", .uuid, .required, .references(Employee.schema, "id"))
|
|
||||||
// .field("vendor_branch_id", .uuid, .required, .references(VendorBranch.schema, "id"))
|
|
||||||
// .field("created_at", .datetime)
|
|
||||||
// .field("updated_at", .datetime)
|
|
||||||
// .create()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func revert(on database: any Database) async throws {
|
|
||||||
// try await database.schema(PurchaseOrder.schema).delete()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// extension PurchaseOrder.Create: Validatable {
|
|
||||||
//
|
|
||||||
// static func validations(_ validations: inout Validations) {
|
|
||||||
// validations.add("materials", as: String.self, is: !.empty)
|
|
||||||
// validations.add("customer", as: String.self, is: !.empty)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
// import Fluent
|
|
||||||
// import Vapor
|
|
||||||
//
|
|
||||||
// /// The user database model.
|
|
||||||
// ///
|
|
||||||
// /// A user is someone who is able to login and generate PO's for employees. Generally a user should also
|
|
||||||
// /// have an employee profile, but not all employees are users. Users are generally restricted to office workers
|
|
||||||
// /// and administrators.
|
|
||||||
// ///
|
|
||||||
// ///
|
|
||||||
// final class User: Model, @unchecked Sendable {
|
|
||||||
// static let schema = "user"
|
|
||||||
//
|
|
||||||
// @ID(key: .id)
|
|
||||||
// var id: UUID?
|
|
||||||
//
|
|
||||||
// @Field(key: "username")
|
|
||||||
// var username: String
|
|
||||||
//
|
|
||||||
// @Field(key: "email")
|
|
||||||
// var email: String
|
|
||||||
//
|
|
||||||
// @Field(key: "password_hash")
|
|
||||||
// var passwordHash: String
|
|
||||||
//
|
|
||||||
// @Timestamp(key: "created_at", on: .create)
|
|
||||||
// var createdAt: Date?
|
|
||||||
//
|
|
||||||
// @Timestamp(key: "updated_at", on: .update)
|
|
||||||
// var updatedAt: Date?
|
|
||||||
//
|
|
||||||
// init() {}
|
|
||||||
//
|
|
||||||
// init(
|
|
||||||
// id: UUID? = nil,
|
|
||||||
// username: String,
|
|
||||||
// email: String,
|
|
||||||
// passwordHash: String
|
|
||||||
// ) {
|
|
||||||
// self.id = id
|
|
||||||
// self.username = username
|
|
||||||
// self.email = email
|
|
||||||
// self.passwordHash = passwordHash
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func toDTO() -> DTO {
|
|
||||||
// .init(
|
|
||||||
// id: id,
|
|
||||||
// username: $username.value,
|
|
||||||
// email: $email.value,
|
|
||||||
// createdAt: createdAt,
|
|
||||||
// updatedAt: updatedAt
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func generateToken() throws -> UserToken {
|
|
||||||
// try .init(
|
|
||||||
// value: [UInt8].random(count: 16).base64,
|
|
||||||
// userID: requireID()
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// extension User {
|
|
||||||
//
|
|
||||||
// struct Create: Content {
|
|
||||||
// var username: String
|
|
||||||
// var email: String
|
|
||||||
// var password: String
|
|
||||||
// var confirmPassword: String
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// struct DTO: Content {
|
|
||||||
// let id: UUID?
|
|
||||||
// let username: String?
|
|
||||||
// let email: String?
|
|
||||||
// let createdAt: Date?
|
|
||||||
// let updatedAt: Date?
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// struct Migrate: AsyncMigration {
|
|
||||||
// let name = "CreateUser"
|
|
||||||
//
|
|
||||||
// func prepare(on database: any Database) async throws {
|
|
||||||
// try await database.schema(User.schema)
|
|
||||||
// .id()
|
|
||||||
// .field("username", .string, .required)
|
|
||||||
// .field("email", .string, .required)
|
|
||||||
// .field("password_hash", .string, .required)
|
|
||||||
// .field("created_at", .datetime)
|
|
||||||
// .field("updated_at", .datetime)
|
|
||||||
// .unique(on: "email", "username")
|
|
||||||
// .create()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func revert(on database: any Database) async throws {
|
|
||||||
// try await database.schema(User.schema).delete()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// extension User: ModelAuthenticatable {
|
|
||||||
// static let usernameKey = \User.$username
|
|
||||||
// static let passwordHashKey = \User.$passwordHash
|
|
||||||
//
|
|
||||||
// func verify(password: String) throws -> Bool {
|
|
||||||
// try Bcrypt.verify(password, created: passwordHash)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// extension User: ModelSessionAuthenticatable {}
|
|
||||||
// extension User: ModelCredentialsAuthenticatable {}
|
|
||||||
//
|
|
||||||
// extension User.Create: Validatable {
|
|
||||||
// static func validations(_ validations: inout Validations) {
|
|
||||||
// validations.add("username", as: String.self, is: !.empty)
|
|
||||||
// validations.add("email", as: String.self, is: .email)
|
|
||||||
// validations.add("password", as: String.self, is: .count(8...))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
// import Fluent
|
|
||||||
// import Vapor
|
|
||||||
//
|
|
||||||
// final class UserToken: Model, Content, @unchecked Sendable {
|
|
||||||
//
|
|
||||||
// static let schema = "user_token"
|
|
||||||
//
|
|
||||||
// @ID(key: .id)
|
|
||||||
// var id: UUID?
|
|
||||||
//
|
|
||||||
// @Field(key: "value")
|
|
||||||
// var value: String
|
|
||||||
//
|
|
||||||
// @Parent(key: "user_id")
|
|
||||||
// var user: User
|
|
||||||
//
|
|
||||||
// init() {}
|
|
||||||
//
|
|
||||||
// init(id: UUID? = nil, value: String, userID: User.IDValue) {
|
|
||||||
// self.id = id
|
|
||||||
// self.value = value
|
|
||||||
// $user.id = userID
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// extension UserToken {
|
|
||||||
//
|
|
||||||
// struct Migrate: AsyncMigration {
|
|
||||||
// let name = "CreateUserToken"
|
|
||||||
//
|
|
||||||
// func prepare(on database: any Database) async throws {
|
|
||||||
// try await database.schema(UserToken.schema)
|
|
||||||
// .id()
|
|
||||||
// .field("value", .string, .required)
|
|
||||||
// .field("user_id", .uuid, .required, .references(User.schema, "id"))
|
|
||||||
// .unique(on: "value")
|
|
||||||
// .create()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func revert(on database: any Database) async throws {
|
|
||||||
// try await database.schema(UserToken.schema).delete()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// extension UserToken: ModelTokenAuthenticatable {
|
|
||||||
// static let valueKey = \UserToken.$value
|
|
||||||
// static let userKey = \UserToken.$user
|
|
||||||
//
|
|
||||||
// var isValid: Bool { true }
|
|
||||||
// }
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
// import Fluent
|
|
||||||
// import struct Foundation.UUID
|
|
||||||
// import Vapor
|
|
||||||
//
|
|
||||||
// // The primary database model.
|
|
||||||
// final class Vendor: Model, @unchecked Sendable {
|
|
||||||
//
|
|
||||||
// static let schema = "vendor"
|
|
||||||
//
|
|
||||||
// @ID(key: .id)
|
|
||||||
// var id: UUID?
|
|
||||||
//
|
|
||||||
// @Field(key: "name")
|
|
||||||
// var name: String
|
|
||||||
//
|
|
||||||
// @Timestamp(key: "created_at", on: .create)
|
|
||||||
// var createdAt: Date?
|
|
||||||
//
|
|
||||||
// @Timestamp(key: "updated_at", on: .update)
|
|
||||||
// var updatedAt: Date?
|
|
||||||
//
|
|
||||||
// @Children(for: \.$vendor)
|
|
||||||
// var branches: [VendorBranch]
|
|
||||||
//
|
|
||||||
// init() {}
|
|
||||||
//
|
|
||||||
// init(id: UUID? = nil, name: String) {
|
|
||||||
// self.id = id
|
|
||||||
// self.name = name
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func toDTO(includeBranches: Bool? = nil) -> DTO {
|
|
||||||
// .init(
|
|
||||||
// id: id,
|
|
||||||
// name: $name.value,
|
|
||||||
// branches: ($branches.value != nil && $branches.value!.count > 0)
|
|
||||||
// ? $branches.value!.map { $0.toDTO() }
|
|
||||||
// : (includeBranches == true) ? [] : nil,
|
|
||||||
// createdAt: createdAt,
|
|
||||||
// updatedAt: updatedAt
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func applyUpdates(_ updates: Update) {
|
|
||||||
// name = updates.name
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // MARK: - Helpers.
|
|
||||||
//
|
|
||||||
// extension Vendor {
|
|
||||||
// struct Create: Content {
|
|
||||||
// var name: String
|
|
||||||
//
|
|
||||||
// func toModel() -> Vendor {
|
|
||||||
// .init(name: name)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// struct DTO: Content {
|
|
||||||
//
|
|
||||||
// var id: UUID?
|
|
||||||
// var name: String?
|
|
||||||
// var branches: [VendorBranch.DTO]?
|
|
||||||
// let createdAt: Date?
|
|
||||||
// let updatedAt: Date?
|
|
||||||
//
|
|
||||||
// func toModel() -> Vendor {
|
|
||||||
// let model = Vendor()
|
|
||||||
// model.id = id
|
|
||||||
// if let name {
|
|
||||||
// model.name = name
|
|
||||||
// }
|
|
||||||
// return model
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// struct Migrate: AsyncMigration {
|
|
||||||
// let name = "CreateVendor"
|
|
||||||
//
|
|
||||||
// func prepare(on database: any Database) async throws {
|
|
||||||
// try await database.schema(Vendor.schema)
|
|
||||||
// .id()
|
|
||||||
// .field("name", .string, .required)
|
|
||||||
// .field("created_at", .datetime)
|
|
||||||
// .field("updated_at", .datetime)
|
|
||||||
// .unique(on: "name")
|
|
||||||
// .create()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func revert(on database: any Database) async throws {
|
|
||||||
// try await database.schema(Vendor.schema).delete()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// struct Update: Content {
|
|
||||||
// var name: String
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // MARK: - Validations
|
|
||||||
//
|
|
||||||
// extension Vendor.Create: Validatable {
|
|
||||||
// static func validations(_ validations: inout Validations) {
|
|
||||||
// validations.add("name", as: String.self, is: !.empty)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// extension Vendor.Update: Validatable {
|
|
||||||
// static func validations(_ validations: inout Validations) {
|
|
||||||
// validations.add("name", as: String.self, is: !.empty)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
// import Fluent
|
|
||||||
// import struct Foundation.UUID
|
|
||||||
// import Vapor
|
|
||||||
//
|
|
||||||
// final class VendorBranch: Model, @unchecked Sendable {
|
|
||||||
//
|
|
||||||
// static let schema = "vendor_branch"
|
|
||||||
//
|
|
||||||
// @ID(key: .id)
|
|
||||||
// var id: UUID?
|
|
||||||
//
|
|
||||||
// @Field(key: "name")
|
|
||||||
// var name: String
|
|
||||||
//
|
|
||||||
// @Timestamp(key: "created_at", on: .create)
|
|
||||||
// var createdAt: Date?
|
|
||||||
//
|
|
||||||
// @Timestamp(key: "updated_at", on: .update)
|
|
||||||
// var updatedAt: Date?
|
|
||||||
//
|
|
||||||
// @Parent(key: "vendor_id")
|
|
||||||
// var vendor: Vendor
|
|
||||||
//
|
|
||||||
// init() {}
|
|
||||||
//
|
|
||||||
// init(id: UUID? = nil, name: String, vendorId: Vendor.IDValue) {
|
|
||||||
// self.id = id
|
|
||||||
// self.name = name
|
|
||||||
// $vendor.id = vendorId
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func toDTO() -> DTO {
|
|
||||||
// .init(
|
|
||||||
// id: id,
|
|
||||||
// name: $name.value,
|
|
||||||
// vendorId: $vendor.id,
|
|
||||||
// createdAt: createdAt,
|
|
||||||
// updatedAt: updatedAt
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func applyUpdates(_ updates: Update) {
|
|
||||||
// name = updates.name
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // MARK: - Helpers
|
|
||||||
//
|
|
||||||
// extension VendorBranch {
|
|
||||||
// struct Create: Content {
|
|
||||||
// var name: String
|
|
||||||
//
|
|
||||||
// func toModel() -> VendorBranch {
|
|
||||||
// let model = VendorBranch()
|
|
||||||
// model.name = name
|
|
||||||
// return model
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// struct DTO: Content {
|
|
||||||
// var id: UUID?
|
|
||||||
// var name: String?
|
|
||||||
// var vendorId: Vendor.IDValue?
|
|
||||||
// let createdAt: Date?
|
|
||||||
// let updatedAt: Date?
|
|
||||||
//
|
|
||||||
// func toModel() -> VendorBranch {
|
|
||||||
// let model = VendorBranch()
|
|
||||||
//
|
|
||||||
// model.id = id
|
|
||||||
// if let name {
|
|
||||||
// model.name = name
|
|
||||||
// }
|
|
||||||
// if let vendorId {
|
|
||||||
// model.$vendor.id = vendorId
|
|
||||||
// }
|
|
||||||
// return model
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// struct Migrate: AsyncMigration {
|
|
||||||
// let name = "CreateVendorBranch"
|
|
||||||
//
|
|
||||||
// func prepare(on database: any Database) async throws {
|
|
||||||
// try await database.schema(VendorBranch.schema)
|
|
||||||
// .id()
|
|
||||||
// .field("name", .string, .required)
|
|
||||||
// .field("vendor_id", .uuid, .required)
|
|
||||||
// .field("created_at", .datetime)
|
|
||||||
// .field("updated_at", .datetime)
|
|
||||||
// .foreignKey("vendor_id", references: Vendor.schema, "id", onDelete: .cascade)
|
|
||||||
// .create()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func revert(on database: any Database) async throws {
|
|
||||||
// try await database.schema(VendorBranch.schema).delete()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// struct Update: Content {
|
|
||||||
// var name: String
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // MARK: - Validations
|
|
||||||
//
|
|
||||||
// extension VendorBranch.Create: Validatable {
|
|
||||||
// static func validations(_ validations: inout Validations) {
|
|
||||||
// validations.add("name", as: String.self, is: !.empty)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// extension VendorBranch.Update: Validatable {
|
|
||||||
// static func validations(_ validations: inout Validations) {
|
|
||||||
// validations.add("name", as: String.self, is: !.empty)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
9
Sources/App/Views/Buttons.swift
Normal file
9
Sources/App/Views/Buttons.swift
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import Elementary
|
||||||
|
|
||||||
|
struct ToggleFormButton: HTML {
|
||||||
|
var content: some HTML<HTMLTag.a> {
|
||||||
|
a(.href("javascript:void(0)"), .on(.click, "toggleContent('form')"), .class("btn-add")) {
|
||||||
|
"+"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
43
Sources/App/Views/Main.swift
Normal file
43
Sources/App/Views/Main.swift
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import Elementary
|
||||||
|
import ElementaryHTMX
|
||||||
|
|
||||||
|
struct MainPage<Inner: HTML>: HTMLDocument {
|
||||||
|
|
||||||
|
var title: String { "Purchase Orders" }
|
||||||
|
|
||||||
|
let inner: Inner
|
||||||
|
let displayNav: Bool
|
||||||
|
|
||||||
|
init(displayNav: Bool = false, _ inner: () -> Inner) {
|
||||||
|
self.displayNav = displayNav
|
||||||
|
self.inner = inner()
|
||||||
|
}
|
||||||
|
|
||||||
|
var head: some HTML {
|
||||||
|
meta(.charset(.utf8))
|
||||||
|
script(.src("https://unpkg.com/htmx.org@2.0.4")) {}
|
||||||
|
script(.src("/js/main.js")) {}
|
||||||
|
link(.rel(.stylesheet), .href("/css/main.css"))
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some HTML {
|
||||||
|
header {
|
||||||
|
Logo()
|
||||||
|
if displayNav {
|
||||||
|
Navbar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension MainPage: Sendable where Inner: Sendable {}
|
||||||
|
|
||||||
|
struct Logo: HTML, Sendable {
|
||||||
|
|
||||||
|
var content: some HTML {
|
||||||
|
div(.id("logo")) {
|
||||||
|
"HHE - Purchase Orders"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
Sources/App/Views/Navbar.swift
Normal file
31
Sources/App/Views/Navbar.swift
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import Elementary
|
||||||
|
import ElementaryHTMX
|
||||||
|
|
||||||
|
struct Navbar: HTML {
|
||||||
|
var content: some HTML {
|
||||||
|
div(.class("sidepanel"), .id("sidepanel")) {
|
||||||
|
a(.href("javascript:void(0)"), .class("closebtn"), .on(.click, "closeSidepanel()")) {
|
||||||
|
"x"
|
||||||
|
}
|
||||||
|
a(.hx.get("/purchase-orders?page=1&limit=50"), .hx.target("body"), .hx.pushURL(true)) {
|
||||||
|
"Purchae Orders"
|
||||||
|
}
|
||||||
|
a(.hx.get("/users"), .hx.target("body"), .hx.pushURL(true)) {
|
||||||
|
"Users"
|
||||||
|
}
|
||||||
|
a(.hx.get("/employees"), .hx.target("body"), .hx.pushURL(true)) {
|
||||||
|
"Employees"
|
||||||
|
}
|
||||||
|
a(.hx.get("/vendors"), .hx.target("body"), .hx.pushURL(true)) {
|
||||||
|
"Vendors"
|
||||||
|
}
|
||||||
|
div(.style("border-bottom: 1px solid grey; margin-bottom: 5px;")) {}
|
||||||
|
a(.hx.post("/logout"), .hx.target("#content"), .hx.swap(.outerHTML), .hx.trigger(.event(.click))) {
|
||||||
|
"Logout"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button(.class("openbtn"), .on(.click, "openSidepanel()")) {
|
||||||
|
img(.src("/images/menu.svg"), .style("width: 30px;, height: 30px;"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
78
Sources/App/Views/Users/UserForm.swift
Normal file
78
Sources/App/Views/Users/UserForm.swift
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import Elementary
|
||||||
|
import ElementaryHTMX
|
||||||
|
|
||||||
|
struct UserForm: HTML, Sendable {
|
||||||
|
let context: Context
|
||||||
|
|
||||||
|
var content: some HTML {
|
||||||
|
form(
|
||||||
|
.id("user-form"),
|
||||||
|
.class("user-form"),
|
||||||
|
.hx.post(context.targetURL),
|
||||||
|
.hx.pushURL(context.pushURL),
|
||||||
|
.custom(name: "hx-on::after-request", value: "if(event.detail.successful) this.reset(); toggleContent('form');")
|
||||||
|
) {
|
||||||
|
input(.type(.text), .id("username"), .name("username"), .placeholder("Username"), .autofocus, .required)
|
||||||
|
br()
|
||||||
|
if context.showEmailInput {
|
||||||
|
input(.type(.email), .id("email"), .name("email"), .placeholder("Email"), .required)
|
||||||
|
br()
|
||||||
|
}
|
||||||
|
input(.type(.password), .id("password"), .name("password"), .placeholder("Password"), .required)
|
||||||
|
br()
|
||||||
|
if context.showConfirmPassword {
|
||||||
|
input(.type(.password), .id("confirmPassword"), .name("confirmPassword"), .required)
|
||||||
|
br()
|
||||||
|
}
|
||||||
|
input(.type(.submit), .value(context.buttonLabel))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Context {
|
||||||
|
case create
|
||||||
|
case login(next: String?)
|
||||||
|
|
||||||
|
var showConfirmPassword: Bool {
|
||||||
|
switch self {
|
||||||
|
case .create: return true
|
||||||
|
case .login: return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var showEmailInput: Bool {
|
||||||
|
switch self {
|
||||||
|
case .create: return true
|
||||||
|
case .login: return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pushURL: Bool {
|
||||||
|
switch self {
|
||||||
|
case .create: return false
|
||||||
|
case .login: return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var buttonLabel: String {
|
||||||
|
switch self {
|
||||||
|
case .create:
|
||||||
|
return "Create"
|
||||||
|
case .login:
|
||||||
|
return "Login"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetURL: String {
|
||||||
|
switch self {
|
||||||
|
case .create:
|
||||||
|
return "/users"
|
||||||
|
case let .login(next: next):
|
||||||
|
let path = "/login"
|
||||||
|
if let next {
|
||||||
|
return "\(path)?next=\(next)"
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
40
Sources/App/Views/Users/UserTable.swift
Normal file
40
Sources/App/Views/Users/UserTable.swift
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import DatabaseClient
|
||||||
|
import Dependencies
|
||||||
|
import Elementary
|
||||||
|
import ElementaryHTMX
|
||||||
|
import SharedModels
|
||||||
|
|
||||||
|
struct UserTable: HTML {
|
||||||
|
|
||||||
|
@Dependency(\.database.users.fetchAll) var fetchAll
|
||||||
|
|
||||||
|
var content: some HTML {
|
||||||
|
table(.id("user-table")) {
|
||||||
|
thead {
|
||||||
|
tr {
|
||||||
|
th { "Username" }
|
||||||
|
th { "Email" }
|
||||||
|
th { ToggleFormButton() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tbody {
|
||||||
|
let users = try await fetchAll()
|
||||||
|
for user in users {
|
||||||
|
Row(user: user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Row: HTML {
|
||||||
|
let user: User
|
||||||
|
|
||||||
|
var content: some HTML<HTMLTag.tr> {
|
||||||
|
tr {
|
||||||
|
td { user.username }
|
||||||
|
td { user.email }
|
||||||
|
td { "Fix me." }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ public func configure(_ app: Application) async throws {
|
|||||||
// uncomment to serve files from /Public folder
|
// uncomment to serve files from /Public folder
|
||||||
app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
|
app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
|
||||||
app.middleware.use(app.sessions.middleware)
|
app.middleware.use(app.sessions.middleware)
|
||||||
|
app.middleware.use(DependenciesMiddleware())
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
app.lifecycle.use(BrowserSyncHandler())
|
app.lifecycle.use(BrowserSyncHandler())
|
||||||
@@ -42,7 +43,6 @@ public func configure(_ app: Application) async throws {
|
|||||||
try withDependencies {
|
try withDependencies {
|
||||||
$0.database = databaseClient
|
$0.database = databaseClient
|
||||||
} operation: {
|
} operation: {
|
||||||
// register routes
|
|
||||||
try routes(app)
|
try routes(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import DatabaseClientLive
|
||||||
|
import Dependencies
|
||||||
import Logging
|
import Logging
|
||||||
import NIOCore
|
import NIOCore
|
||||||
import NIOPosix
|
import NIOPosix
|
||||||
|
|||||||
@@ -1,7 +1,37 @@
|
|||||||
|
import DatabaseClientLive
|
||||||
|
import Dependencies
|
||||||
|
import Elementary
|
||||||
import Fluent
|
import Fluent
|
||||||
import Vapor
|
import Vapor
|
||||||
|
import VaporElementary
|
||||||
|
|
||||||
func routes(_ app: Application) throws {
|
func routes(_ app: Application) throws {
|
||||||
try app.register(collection: ApiController())
|
try app.register(collection: ApiController())
|
||||||
// try app.register(collection: ViewController())
|
// try app.register(collection: ViewController())
|
||||||
|
|
||||||
|
app.get("test") { _ in
|
||||||
|
HTMLResponse {
|
||||||
|
MainPage(displayNav: false) {
|
||||||
|
div(.class("container")) {
|
||||||
|
h1 { "iT WORKS" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.get("login") { _ in
|
||||||
|
HTMLResponse {
|
||||||
|
MainPage(displayNav: false) {
|
||||||
|
UserForm(context: .login(next: nil))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.get("users") { _ in
|
||||||
|
HTMLResponse {
|
||||||
|
MainPage(displayNav: false) {
|
||||||
|
UserTable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user