feat: Begins migrating views from leaf to elementary
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"originHash" : "5e46aec5c52d22d116c033b012d49986e5f900c102b88ac80e054d2e6e64d458",
|
||||
"originHash" : "b5426b686e9548f4fc69224d4e0f13d10ce55816d5b71622f5f446b12d66140a",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "async-http-client",
|
||||
@@ -37,6 +37,24 @@
|
||||
"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",
|
||||
"kind" : "remoteSourceControl",
|
||||
@@ -325,6 +343,15 @@
|
||||
"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",
|
||||
"kind" : "remoteSourceControl",
|
||||
|
||||
@@ -23,7 +23,10 @@ let package = Package(
|
||||
.package(url: "https://github.com/vapor/leaf.git", from: "4.3.0"),
|
||||
// 🔵 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/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: [
|
||||
.executableTarget(
|
||||
@@ -37,7 +40,10 @@ let package = Package(
|
||||
.product(name: "NIOCore", package: "swift-nio"),
|
||||
.product(name: "NIOPosix", package: "swift-nio"),
|
||||
.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
|
||||
|
||||
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
|
||||
app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
|
||||
app.middleware.use(app.sessions.middleware)
|
||||
app.middleware.use(DependenciesMiddleware())
|
||||
|
||||
#if DEBUG
|
||||
app.lifecycle.use(BrowserSyncHandler())
|
||||
@@ -42,7 +43,6 @@ public func configure(_ app: Application) async throws {
|
||||
try withDependencies {
|
||||
$0.database = databaseClient
|
||||
} operation: {
|
||||
// register routes
|
||||
try routes(app)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import DatabaseClientLive
|
||||
import Dependencies
|
||||
import Logging
|
||||
import NIOCore
|
||||
import NIOPosix
|
||||
|
||||
@@ -1,7 +1,37 @@
|
||||
import DatabaseClientLive
|
||||
import Dependencies
|
||||
import Elementary
|
||||
import Fluent
|
||||
import Vapor
|
||||
import VaporElementary
|
||||
|
||||
func routes(_ app: Application) throws {
|
||||
try app.register(collection: ApiController())
|
||||
// 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