feat: Initial purchase order views, login seems to be broken though.
This commit is contained in:
@@ -26,9 +26,11 @@ h1 { font-size: 2.5em; }
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
.header {
|
||||||
|
position: sticky;
|
||||||
background-color: var(--dark-bg);
|
background-color: var(--dark-bg);
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
|
top: 0;
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
border-bottom: 1px solid grey;
|
border-bottom: 1px solid grey;
|
||||||
@@ -97,6 +99,10 @@ form {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
form .label {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
#user-form input {
|
#user-form input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 20px;
|
margin: 20px;
|
||||||
@@ -142,6 +148,7 @@ select {
|
|||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
font-size: 1.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
option {
|
option {
|
||||||
@@ -291,11 +298,12 @@ a.toggle, a img.toggle {
|
|||||||
.float {
|
.float {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 60px;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: #14141f;
|
background-color: #14141f;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.float .closebtn {
|
.float .closebtn {
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ struct PurchaseOrderApiController: RouteCollection {
|
|||||||
@Sendable
|
@Sendable
|
||||||
func create(req: Request) async throws -> PurchaseOrder {
|
func create(req: Request) async throws -> PurchaseOrder {
|
||||||
try await purchaseOrders.create(
|
try await purchaseOrders.create(
|
||||||
req.content.decode(PurchaseOrder.Create.self),
|
req.content.decode(PurchaseOrder.CreateIntermediate.self)
|
||||||
req.auth.require(User.self).id
|
.toCreate(createdByID: req.auth.require(User.self).id)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,196 +1,103 @@
|
|||||||
// import Dependencies
|
import Dependencies
|
||||||
// import Fluent
|
import Elementary
|
||||||
// import Vapor
|
import SharedModels
|
||||||
//
|
import Vapor
|
||||||
// struct PurchaseOrderViewController: RouteCollection {
|
import VaporElementary
|
||||||
// @Dependency(\.employees) var employees
|
|
||||||
// @Dependency(\.purchaseOrders) var purchaseOrders
|
struct PurchaseOrderViewController: RouteCollection {
|
||||||
// @Dependency(\.vendorBranches) var vendorBranches
|
@Dependency(\.database.employees) var employees
|
||||||
//
|
@Dependency(\.database.vendorBranches) var vendorBranches
|
||||||
// func boot(routes: any RoutesBuilder) throws {
|
@Dependency(\.database.purchaseOrders) var purchaseOrders
|
||||||
// let pos = routes.protected.grouped("purchase-orders")
|
|
||||||
//
|
func boot(routes: any RoutesBuilder) throws {
|
||||||
// pos.get(use: index(req:))
|
let route = routes.protected.grouped("purchase-orders")
|
||||||
// pos.group("details", "close") {
|
route.get(use: index)
|
||||||
// $0.get(use: detailClose(req:))
|
route.get("next", use: nextPage)
|
||||||
// }
|
route.post(use: create(req:))
|
||||||
// pos.post(use: create(req:))
|
route.group("create") {
|
||||||
// pos.group(":id") {
|
$0.get(use: form)
|
||||||
// $0.get(use: detail(req:))
|
$0.get("vendor-branch-select", use: vendorBranchSelect(req:))
|
||||||
// }
|
$0.get("employee-select", use: employeeSelect(req:))
|
||||||
// }
|
}
|
||||||
//
|
route.group(":id") {
|
||||||
// @Sendable
|
$0.get(use: get)
|
||||||
// func index(req: Request) async throws -> View {
|
}
|
||||||
// let params = try? req.query.decode(PurchaseOrderIndex.self)
|
}
|
||||||
// let purchaseOrdersPage = try await purchaseOrders.fetchPage(
|
|
||||||
// .init(page: params?.page ?? 1, per: params?.limit ?? 50)
|
@Sendable
|
||||||
// )
|
func index(req: Request) async throws -> HTMLResponse {
|
||||||
// let branches = try await vendorBranches.getBranches(req: req)
|
let page = try? req.query.decode(IndexQuery.self)
|
||||||
// let employees = try await employees.fetchAll()
|
return try await req.render { try await mainPage(PurchaseOrderForm(), page: page ?? .default) }
|
||||||
// req.logger.debug("Branches: \(branches)")
|
}
|
||||||
// return try await req.view.render(
|
|
||||||
// "purchaseOrders/index",
|
@Sendable
|
||||||
// PurchaseOrderCTX(
|
func nextPage(req: Request) async throws -> HTMLResponse {
|
||||||
// page: purchaseOrdersPage,
|
let query = try req.query.decode(IndexQuery.self)
|
||||||
// form: .create(branches: branches, employees: employees)
|
let page = try await purchaseOrders.fetchPage(.init(page: query.page, per: query.limit))
|
||||||
// )
|
return await req.render { PurchaseOrderTable.Rows(page: page) }
|
||||||
// )
|
}
|
||||||
// }
|
|
||||||
//
|
@Sendable
|
||||||
// @Sendable
|
func form(req: Request) async throws -> HTMLResponse {
|
||||||
// func detail(req: Request) async throws -> View {
|
// guard req.isHtmxRequest else {
|
||||||
// guard let id = req.parameters.get("id", as: PurchaseOrder.IDValue.self) else {
|
// return try await req.render {
|
||||||
// throw Abort(.badRequest, reason: "Id not supplied.")
|
// try await mainPage(PurchaseOrderForm(shouldShow: true), page: .default)
|
||||||
// }
|
// }
|
||||||
// let purchaseOrder = try await purchaseOrders.get(id)
|
// }
|
||||||
// return try await req.view.render("purchaseOrders/detail", ["purchaseOrderDetail": purchaseOrder])
|
return await req.render { PurchaseOrderForm(shouldShow: true) }
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// @Sendable
|
@Sendable
|
||||||
// func detailClose(req: Request) async throws -> View {
|
func vendorBranchSelect(req: Request) async throws -> HTMLResponse {
|
||||||
// return try await req.view.render("purchaseOrders/detail")
|
let branches = try await vendorBranches.fetchAllWithDetail()
|
||||||
// }
|
return await req.render { PurchaseOrderForm.VendorSelect(vendorBranches: branches) }
|
||||||
//
|
}
|
||||||
// @Sendable
|
|
||||||
// func create(req: Request) async throws -> View {
|
@Sendable
|
||||||
// try PurchaseOrder.FormCreate.validate(content: req)
|
func employeeSelect(req: Request) async throws -> HTMLResponse {
|
||||||
// let createdById = try req.auth.require(User.self).requireID()
|
let employees = try await self.employees.fetchAll()
|
||||||
// let create = try req.content.decode(PurchaseOrder.FormCreate.self).toCreate()
|
return await req.render { PurchaseOrderForm.EmployeeSelect(employees: employees) }
|
||||||
// let purchaseOrder = try await purchaseOrders.create(create, createdById)
|
}
|
||||||
// return try await req.view.render("purchaseOrders/table-row", purchaseOrder)
|
|
||||||
// }
|
@Sendable
|
||||||
// }
|
func get(req: Request) async throws -> HTMLResponse {
|
||||||
//
|
let purchaseOrder = try await purchaseOrders.get(req.ensureIDPathComponent(as: Int.self))
|
||||||
// private struct PurchaseOrderIndex: Content {
|
let form = PurchaseOrderForm(purchaseOrder: purchaseOrder, shouldShow: true)
|
||||||
// let page: Int?
|
guard req.isHtmxRequest else {
|
||||||
// let limit: Int?
|
return try await req.render {
|
||||||
// }
|
try await mainPage(form, page: .default)
|
||||||
//
|
}
|
||||||
// private struct PurchaseOrderCTX: Content {
|
}
|
||||||
// let purchaseOrderDetail: PurchaseOrder.DTO?
|
return await req.render { form }
|
||||||
// let purchaseOrders: [PurchaseOrder.DTO]
|
}
|
||||||
// let page: Int
|
|
||||||
// let limit: Int
|
@Sendable
|
||||||
// let hasNext: Bool
|
func create(req: Request) async throws -> HTMLResponse {
|
||||||
// let hasPrevious: Bool
|
let create = try req.content.decode(PurchaseOrder.CreateIntermediate.self)
|
||||||
// let form: PurchaseOrderFormCTX?
|
let user = try req.auth.require(User.self)
|
||||||
//
|
let purchaseOrder = try await purchaseOrders.create(create.toCreate(createdByID: user.id))
|
||||||
// init(
|
return await req.render { PurchaseOrderTable.Row(purchaseOrder: purchaseOrder) }
|
||||||
// detail: PurchaseOrder.DTO? = nil,
|
}
|
||||||
// page: Page<PurchaseOrder.DTO>,
|
|
||||||
// form: PurchaseOrderFormCTX?
|
private func mainPage<C: HTML>(
|
||||||
// ) {
|
_ html: C,
|
||||||
// self.purchaseOrderDetail = detail
|
page: IndexQuery
|
||||||
// self.purchaseOrders = page.items
|
) async throws -> some SendableHTMLDocument where C: Sendable {
|
||||||
// self.page = page.metadata.page
|
let page = try await purchaseOrders.fetchPage(.init(page: page.page, per: page.limit))
|
||||||
// self.limit = page.metadata.per
|
return MainPage(displayNav: true, route: .purchaseOrders) {
|
||||||
// self.hasNext = page.metadata.hasNext
|
div(.class("container")) {
|
||||||
// self.hasPrevious = page.metadata.page > 1
|
html
|
||||||
// self.form = form
|
PurchaseOrderTable(page: page)
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
//
|
}
|
||||||
// private extension PageMetadata {
|
}
|
||||||
// var hasNext: Bool {
|
|
||||||
// total > (page * per)
|
struct IndexQuery: Content {
|
||||||
// }
|
let page: Int
|
||||||
// }
|
let limit: Int
|
||||||
//
|
|
||||||
// private struct PurchaseOrderFormCTX: Content {
|
static var `default`: Self {
|
||||||
//
|
.init(page: 1, limit: 25)
|
||||||
// let htmxForm: HtmxFormCTX<Context>
|
}
|
||||||
//
|
}
|
||||||
// struct Context: Content {
|
|
||||||
// let branches: [VendorBranch.FormDTO]
|
|
||||||
// let employees: [Employee.DTO]
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// static func create(branches: [VendorBranch.FormDTO], employees: [Employee.DTO]) -> Self {
|
|
||||||
// .init(htmxForm: .init(
|
|
||||||
// formClass: "po-form",
|
|
||||||
// formId: "po-form",
|
|
||||||
// htmxTargetUrl: .post("/purchase-orders"),
|
|
||||||
// htmxTarget: "#po-table-body",
|
|
||||||
// htmxPushUrl: false,
|
|
||||||
// htmxResetAfterRequest: true,
|
|
||||||
// htmxSwapOob: nil,
|
|
||||||
// htmxSwap: .afterbegin,
|
|
||||||
// context: .init(branches: branches, employees: employees)
|
|
||||||
// ))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// extension VendorBranch {
|
|
||||||
// struct FormDTO: Content {
|
|
||||||
// let id: UUID
|
|
||||||
// let name: String
|
|
||||||
// let vendor: Vendor.DTO
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func toFormDTO() throws -> VendorBranch.FormDTO {
|
|
||||||
// try .init(
|
|
||||||
// id: requireID(),
|
|
||||||
// name: name,
|
|
||||||
// vendor: vendor.toDTO()
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private extension PurchaseOrder {
|
|
||||||
// struct FormCreate: Content {
|
|
||||||
// let id: Int?
|
|
||||||
// let workOrder: String?
|
|
||||||
// let materials: String
|
|
||||||
// let customer: String
|
|
||||||
// let truckStock: Bool?
|
|
||||||
// let createdForID: Employee.IDValue
|
|
||||||
// let vendorBranchID: VendorBranch.IDValue
|
|
||||||
//
|
|
||||||
// // TODO: Remove.
|
|
||||||
// func toModel(createdByID: User.IDValue) -> PurchaseOrder {
|
|
||||||
// .init(
|
|
||||||
// id: id,
|
|
||||||
// workOrder: workOrder != nil ? (workOrder == "" ? nil : Int(workOrder!)) : nil,
|
|
||||||
// materials: materials,
|
|
||||||
// customer: customer,
|
|
||||||
// truckStock: truckStock ?? false,
|
|
||||||
// createdByID: createdByID,
|
|
||||||
// createdForID: createdForID,
|
|
||||||
// vendorBranchID: vendorBranchID,
|
|
||||||
// createdAt: nil,
|
|
||||||
// updatedAt: nil
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func toCreate() -> PurchaseOrder.Create {
|
|
||||||
// .init(
|
|
||||||
// id: id,
|
|
||||||
// workOrder: workOrder != nil ? (workOrder == "" ? nil : Int(workOrder!)) : nil,
|
|
||||||
// materials: materials,
|
|
||||||
// customer: customer,
|
|
||||||
// truckStock: truckStock,
|
|
||||||
// createdForID: createdForID,
|
|
||||||
// vendorBranchID: vendorBranchID
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private extension VendorBranchDB {
|
|
||||||
//
|
|
||||||
// func getBranches(req: Request) async throws -> [VendorBranch.FormDTO] {
|
|
||||||
// try await VendorBranch.query(on: req.db)
|
|
||||||
// .with(\.$vendor)
|
|
||||||
// .all()
|
|
||||||
// .map { try $0.toFormDTO() }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// extension PurchaseOrder.FormCreate: Validatable {
|
|
||||||
//
|
|
||||||
// static func validations(_ validations: inout Validations) {
|
|
||||||
// validations.add("materials", as: String.self, is: !.empty)
|
|
||||||
// validations.add("customer", as: String.self, is: !.empty)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -9,15 +9,14 @@ extension RoutesBuilder {
|
|||||||
// Used to ensure views are protected, redirects users to the login page if they're
|
// Used to ensure views are protected, redirects users to the login page if they're
|
||||||
// not authenticated.
|
// not authenticated.
|
||||||
var protected: any RoutesBuilder {
|
var protected: any RoutesBuilder {
|
||||||
return self
|
// return self
|
||||||
// return grouped(
|
return grouped(
|
||||||
// UserPasswordAuthenticator(),
|
UserPasswordAuthenticator(),
|
||||||
// UserTokenAuthenticator(),
|
UserSessionAuthenticator(),
|
||||||
// UserSessionAuthenticator(),
|
User.redirectMiddleware { req in
|
||||||
// User.redirectMiddleware { req in
|
"login?next=\(req.url)"
|
||||||
// "login?next=\(req.url)"
|
}
|
||||||
// }
|
)
|
||||||
// )
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiUnprotected(route: PathComponent) -> any RoutesBuilder {
|
func apiUnprotected(route: PathComponent) -> any RoutesBuilder {
|
||||||
|
|||||||
@@ -21,6 +21,15 @@
|
|||||||
var vendors: [Vendor] = []
|
var vendors: [Vendor] = []
|
||||||
var vendorBranches: [VendorBranch] = []
|
var vendorBranches: [VendorBranch] = []
|
||||||
|
|
||||||
|
let adminUser = User.Create(
|
||||||
|
username: Environment.get("ADMIN_USERNAME") ?? "admin",
|
||||||
|
email: Environment.get("ADMIN_EMAIL") ?? "admin@development.com",
|
||||||
|
password: Environment.get("ADMIN_PASSWORD") ?? "super-secret",
|
||||||
|
confirmPassword: Environment.get("ADMIN_PASSWORD") ?? "super-secret"
|
||||||
|
)
|
||||||
|
|
||||||
|
_ = try await database.users.create(adminUser)
|
||||||
|
|
||||||
for user in User.Create.generateMocks() {
|
for user in User.Create.generateMocks() {
|
||||||
let created = try await database.users.create(user)
|
let created = try await database.users.create(user)
|
||||||
users.append(created)
|
users.append(created)
|
||||||
@@ -41,8 +50,13 @@
|
|||||||
vendorBranches.append(created)
|
vendorBranches.append(created)
|
||||||
}
|
}
|
||||||
|
|
||||||
for purchaseOrder in PurchaseOrder.Create.generateMocks(employees: employees, vendorBranches: vendorBranches) {
|
for purchaseOrder in PurchaseOrder.CreateIntermediate.generateMocks(
|
||||||
_ = try await database.purchaseOrders.create(purchaseOrder, users.randomElement()!.id)
|
employees: employees,
|
||||||
|
vendorBranches: vendorBranches
|
||||||
|
) {
|
||||||
|
_ = try await database.purchaseOrders.create(
|
||||||
|
purchaseOrder.toCreate(createdByID: users.randomElement()!.id)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ struct MainPage<Inner: HTML>: SendableHTMLDocument where Inner: Sendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some HTML {
|
var body: some HTML {
|
||||||
header {
|
header(.class("header")) {
|
||||||
Logo()
|
Logo()
|
||||||
if displayNav {
|
if displayNav {
|
||||||
Navbar()
|
Navbar()
|
||||||
|
|||||||
161
Sources/App/Views/PurchaseOrders/PurchaseOrderForm.swift
Normal file
161
Sources/App/Views/PurchaseOrders/PurchaseOrderForm.swift
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
import Elementary
|
||||||
|
import ElementaryHTMX
|
||||||
|
import SharedModels
|
||||||
|
|
||||||
|
struct PurchaseOrderForm: HTML {
|
||||||
|
|
||||||
|
let purchaseOrder: PurchaseOrder?
|
||||||
|
let shouldShow: Bool
|
||||||
|
|
||||||
|
init(purchaseOrder: PurchaseOrder? = nil, shouldShow: Bool = false) {
|
||||||
|
self.purchaseOrder = purchaseOrder
|
||||||
|
self.shouldShow = shouldShow
|
||||||
|
}
|
||||||
|
|
||||||
|
var content: some HTML {
|
||||||
|
Float(shouldDisplay: shouldShow, resetURL: "/purchase-orders") {
|
||||||
|
if shouldShow {
|
||||||
|
if purchaseOrder != nil {
|
||||||
|
p {
|
||||||
|
span(.class("label"), .style("margin-right: 15px;")) { "Note:" }
|
||||||
|
span { i(.style("font-size: 1em;")) {
|
||||||
|
"Vendor and Employee can not be changed once a purchase order has been created."
|
||||||
|
} }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
form(
|
||||||
|
.hx.post("/purchase-orders"),
|
||||||
|
.hx.target("purchase-order-table"),
|
||||||
|
.hx.swap(.afterBegin.transition(true).swap("1s")),
|
||||||
|
.custom(
|
||||||
|
name: "hx-on::after-request",
|
||||||
|
value: "if (event.detail.successful) toggleContent('float'); window.location.href='/purchase-orders';"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
div(.class("row")) {
|
||||||
|
label(
|
||||||
|
.for("customer"), .class("label col-2"), .style("margin-right: 15px; margin-bottom: 5px;")
|
||||||
|
) { "Customer:" }
|
||||||
|
input(
|
||||||
|
.type(.text), .class("col-3"),
|
||||||
|
.name("customer"), .placeholder("Customer"),
|
||||||
|
.value(purchaseOrder?.customer ?? ""),
|
||||||
|
.required, .autofocus
|
||||||
|
)
|
||||||
|
label(
|
||||||
|
.for("workOrder"), .class("label col-2"), .style("margin-right: 15px; margin-bottom: 5px;")
|
||||||
|
) { "Work Order:" }
|
||||||
|
input(
|
||||||
|
.type(.text), .class("col-4"),
|
||||||
|
.name("workOrder"), .placeholder("Work Order: (12345)"),
|
||||||
|
.value("\(purchaseOrder?.workOrder != nil ? String(purchaseOrder!.workOrder!) : "")")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
div(.class("row")) {
|
||||||
|
label(
|
||||||
|
.for("materials"), .class("label col-2"), .style("margin-right: 15px; margin-bottom: 5px;")
|
||||||
|
) { "Materials:" }
|
||||||
|
input(
|
||||||
|
.type(.text), .class("col-3"),
|
||||||
|
.name("materials"), .placeholder("Materials"),
|
||||||
|
.value(purchaseOrder?.materials ?? ""),
|
||||||
|
.required
|
||||||
|
)
|
||||||
|
label(
|
||||||
|
.for("vendorBranchID"), .class("label col-2"), .style("margin-right: 15px; margin-bottom: 5px;")
|
||||||
|
) { "Vendor:" }
|
||||||
|
if purchaseOrder == nil {
|
||||||
|
div(
|
||||||
|
.class("col-4"),
|
||||||
|
.hx.get("/purchase-orders/create/vendor-branch-select"),
|
||||||
|
.hx.target("this"),
|
||||||
|
.hx.swap(.outerHTML.transition(true).swap("0.5s")),
|
||||||
|
.hx.trigger(.event(.revealed)),
|
||||||
|
.hx.indicator(".hx-indicator")
|
||||||
|
) {
|
||||||
|
Img.spinner().attributes(.class("hx-indicator"), .style("float: left;"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
input(
|
||||||
|
.type(.text), .class("col-4"),
|
||||||
|
.name("vendorBranchID"),
|
||||||
|
.value("\(purchaseOrder!.vendorBranch.vendor.name) - \(purchaseOrder!.vendorBranch.name)"),
|
||||||
|
.disabled
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div(.class("row")) {
|
||||||
|
label(
|
||||||
|
.for("createdForID"), .class("label col-2"), .style("margin-right: 15px; margin-bottom: 5px;")
|
||||||
|
) { "Employee:" }
|
||||||
|
if purchaseOrder == nil {
|
||||||
|
div(
|
||||||
|
.class("col-3"),
|
||||||
|
.hx.get("/purchase-orders/create/employee-select"),
|
||||||
|
.hx.target("this"),
|
||||||
|
.hx.swap(.outerHTML.transition(true).swap("0.5s")),
|
||||||
|
.hx.trigger(.event(.revealed)),
|
||||||
|
.hx.indicator(".hx-indicator")
|
||||||
|
) {
|
||||||
|
Img.spinner().attributes(.class("hx-indicator"), .style("float: left;"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
input(
|
||||||
|
.type(.text), .class("col-3"),
|
||||||
|
.value(purchaseOrder!.createdFor.fullName),
|
||||||
|
.disabled
|
||||||
|
)
|
||||||
|
}
|
||||||
|
label(
|
||||||
|
.for("truckStock"), .class("label col-2"), .style("margin-right: 15px; margin-bottom: 5px;")
|
||||||
|
) { "Truck Stock:" }
|
||||||
|
if purchaseOrder?.truckStock == true {
|
||||||
|
input(
|
||||||
|
.type(.checkbox), .class("col-2"), .name("truckStock"), .style("margin-top: 20px;"), .checked
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
input(
|
||||||
|
.type(.checkbox), .class("col-2"), .name("truckStock"), .style("margin-top: 20px;")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div(.class("btn-row")) {
|
||||||
|
button(.class("btn-primary"), .type(.submit)) { buttonLabel }
|
||||||
|
if purchaseOrder != nil {
|
||||||
|
Button.danger { "Delete" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var buttonLabel: String {
|
||||||
|
guard purchaseOrder != nil else { return "Create" }
|
||||||
|
return "Update"
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VendorSelect: HTML {
|
||||||
|
let vendorBranches: [VendorBranch.Detail]
|
||||||
|
|
||||||
|
var content: some HTML<HTMLTag.select> {
|
||||||
|
select(.name("vendorBranchID"), .class("col-3")) {
|
||||||
|
for branch in vendorBranches {
|
||||||
|
option(.value(branch.id.uuidString)) { "\(branch.vendor.name) - \(branch.name)" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EmployeeSelect: HTML {
|
||||||
|
let employees: [Employee]
|
||||||
|
|
||||||
|
var content: some HTML<HTMLTag.select> {
|
||||||
|
select(.name("createdForID"), .class("col-3")) {
|
||||||
|
for employee in employees {
|
||||||
|
option(.value(employee.id.uuidString)) { employee.fullName }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
92
Sources/App/Views/PurchaseOrders/PurchaseOrderTable.swift
Normal file
92
Sources/App/Views/PurchaseOrders/PurchaseOrderTable.swift
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import Elementary
|
||||||
|
import ElementaryHTMX
|
||||||
|
import Fluent
|
||||||
|
import SharedModels
|
||||||
|
import Vapor
|
||||||
|
|
||||||
|
struct PurchaseOrderTable: HTML {
|
||||||
|
|
||||||
|
let page: Page<PurchaseOrder>
|
||||||
|
|
||||||
|
var content: some HTML {
|
||||||
|
table {
|
||||||
|
thead {
|
||||||
|
tr {
|
||||||
|
th { "PO" }
|
||||||
|
th { "Work Order" }
|
||||||
|
th { "Customer" }
|
||||||
|
th { "Vendor" }
|
||||||
|
th { "Materials" }
|
||||||
|
th { "Created For" }
|
||||||
|
th {
|
||||||
|
Button.add()
|
||||||
|
.attributes(
|
||||||
|
.hx.get("/purchase-orders/create"),
|
||||||
|
.hx.target("#float"),
|
||||||
|
.hx.swap(.outerHTML.transition(true).swap("1s")),
|
||||||
|
.hx.pushURL(true)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tbody(.id("purchase-order-table")) {
|
||||||
|
Rows(page: page)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Produces only the rows for the given page
|
||||||
|
struct Rows: HTML {
|
||||||
|
let page: Page<PurchaseOrder>
|
||||||
|
|
||||||
|
var content: some HTML {
|
||||||
|
for purchaseOrder in page.items {
|
||||||
|
Row(purchaseOrder: purchaseOrder)
|
||||||
|
}
|
||||||
|
if page.metadata.pageCount > page.metadata.page {
|
||||||
|
tr(
|
||||||
|
.hx.get("/purchase-orders/next?page=\(page.metadata.page + 1)&limit=\(page.metadata.per)"),
|
||||||
|
.hx.trigger(.event(.revealed)),
|
||||||
|
.hx.swap(.outerHTML.transition(true).swap("1s")),
|
||||||
|
.hx.target("this"),
|
||||||
|
.hx.indicator(".htmx-indicator")
|
||||||
|
) {
|
||||||
|
img(.src("/images/spinner.svg"), .class("htmx-indicator"), .width(60), .height(60))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A single row.
|
||||||
|
struct Row: HTML {
|
||||||
|
let purchaseOrder: PurchaseOrder
|
||||||
|
|
||||||
|
var content: some HTML<HTMLTag.tr> {
|
||||||
|
tr(
|
||||||
|
.id("purchase_order_\(purchaseOrder.id)")
|
||||||
|
) {
|
||||||
|
td { "\(purchaseOrder.id)" }
|
||||||
|
td { purchaseOrder.workOrder != nil ? String(purchaseOrder.workOrder!) : "" }
|
||||||
|
td { purchaseOrder.customer }
|
||||||
|
td { purchaseOrder.vendorBranch.displayName }
|
||||||
|
td { purchaseOrder.materials }
|
||||||
|
td { purchaseOrder.createdFor.fullName }
|
||||||
|
td {
|
||||||
|
Button.detail()
|
||||||
|
.attributes(
|
||||||
|
.hx.get("/purchase-orders/\(purchaseOrder.id)"),
|
||||||
|
.hx.target("#float"),
|
||||||
|
.hx.swap(.outerHTML.transition(true).swap("0.5s")),
|
||||||
|
.hx.pushURL(true)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension VendorBranch.Detail {
|
||||||
|
var displayName: String {
|
||||||
|
"\(vendor.name.capitalized) - \(name.capitalized)"
|
||||||
|
}
|
||||||
|
}
|
||||||
7
Sources/App/Views/Utils/Img.swift
Normal file
7
Sources/App/Views/Utils/Img.swift
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import Elementary
|
||||||
|
|
||||||
|
enum Img {
|
||||||
|
static func spinner(width: Int = 30, height: Int = 30) -> some HTML<HTMLTag.img> {
|
||||||
|
img(.src("/images/spinner.svg"), .width(width), .height(height))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import DatabaseClientLive
|
|||||||
import Dependencies
|
import Dependencies
|
||||||
import Elementary
|
import Elementary
|
||||||
import Fluent
|
import Fluent
|
||||||
|
import SharedModels
|
||||||
import Vapor
|
import Vapor
|
||||||
import VaporElementary
|
import VaporElementary
|
||||||
|
|
||||||
@@ -10,7 +11,7 @@ func routes(_ app: Application) throws {
|
|||||||
try app.register(collection: UserViewController())
|
try app.register(collection: UserViewController())
|
||||||
try app.register(collection: VendorViewController())
|
try app.register(collection: VendorViewController())
|
||||||
try app.register(collection: EmployeeViewController())
|
try app.register(collection: EmployeeViewController())
|
||||||
// try app.register(collection: ViewController())
|
try app.register(collection: PurchaseOrderViewController())
|
||||||
|
|
||||||
app.get { _ in
|
app.get { _ in
|
||||||
HTMLResponse {
|
HTMLResponse {
|
||||||
@@ -22,20 +23,37 @@ func routes(_ app: Application) throws {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
app.get("login") { _ in
|
app.get("login") { req in
|
||||||
HTMLResponse {
|
let context = try req.query.decode(LoginContext.self)
|
||||||
|
return await req.render {
|
||||||
MainPage(displayNav: false, route: .login) {
|
MainPage(displayNav: false, route: .login) {
|
||||||
UserForm(context: .login(next: nil))
|
UserForm(context: .login(next: context.next))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// app.get("users") { _ in
|
app.post("login") { req in
|
||||||
// HTMLResponse {
|
@Dependency(\.database.users) var users
|
||||||
// // UserIndex()
|
let loginForm = try req.content.decode(User.Login.self)
|
||||||
// MainPage(displayNav: false, route: .users) {
|
let token = try await users.login(loginForm)
|
||||||
// UserTable()
|
let user = try await users.get(token.userID)!
|
||||||
// }
|
req.session.authenticate(user)
|
||||||
// }
|
return try await PurchaseOrderViewController().index(req: req)
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
let protected = app.grouped(UserPasswordAuthenticator(), UserSessionAuthenticator())
|
||||||
|
|
||||||
|
protected.get("me") { req in
|
||||||
|
let user = try req.auth.require(User.self)
|
||||||
|
|
||||||
|
return HTMLResponse {
|
||||||
|
MainPage(displayNav: false, route: .purchaseOrders) {
|
||||||
|
h1 { "You are logged in as: \(user.username)" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct LoginContext: Content {
|
||||||
|
let next: String?
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import Vapor
|
|||||||
public extension DatabaseClient {
|
public extension DatabaseClient {
|
||||||
@DependencyClient
|
@DependencyClient
|
||||||
struct PurchaseOrders: Sendable {
|
struct PurchaseOrders: Sendable {
|
||||||
public var create: @Sendable (PurchaseOrder.Create, User.ID) async throws -> PurchaseOrder
|
public var create: @Sendable (PurchaseOrder.Create) async throws -> PurchaseOrder
|
||||||
public var fetchAll: @Sendable () async throws -> [PurchaseOrder]
|
public var fetchAll: @Sendable () async throws -> [PurchaseOrder]
|
||||||
public var fetchPage: @Sendable (PageRequest) async throws -> Page<PurchaseOrder>
|
public var fetchPage: @Sendable (PageRequest) async throws -> Page<PurchaseOrder>
|
||||||
public var get: @Sendable (PurchaseOrder.ID) async throws -> PurchaseOrder?
|
public var get: @Sendable (PurchaseOrder.ID) async throws -> PurchaseOrder?
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ public extension DatabaseClient {
|
|||||||
public var create: @Sendable (VendorBranch.Create) async throws -> VendorBranch
|
public var create: @Sendable (VendorBranch.Create) async throws -> VendorBranch
|
||||||
public var delete: @Sendable (VendorBranch.ID) async throws -> Void
|
public var delete: @Sendable (VendorBranch.ID) async throws -> Void
|
||||||
public var fetchAll: @Sendable (FetchRequest) async throws -> [VendorBranch]
|
public var fetchAll: @Sendable (FetchRequest) async throws -> [VendorBranch]
|
||||||
|
public var fetchAllWithDetail: @Sendable () async throws -> [VendorBranch.Detail]
|
||||||
public var get: @Sendable (VendorBranch.ID) async throws -> VendorBranch?
|
public var get: @Sendable (VendorBranch.ID) async throws -> VendorBranch?
|
||||||
public var update: @Sendable (VendorBranch.ID, VendorBranch.Update) async throws -> VendorBranch
|
public var update: @Sendable (VendorBranch.ID, VendorBranch.Update) async throws -> VendorBranch
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import SharedModels
|
|||||||
public extension DatabaseClient.PurchaseOrders {
|
public extension DatabaseClient.PurchaseOrders {
|
||||||
|
|
||||||
static func live(database: any Database) -> Self {
|
static func live(database: any Database) -> Self {
|
||||||
.init { create, createdById in
|
.init { create in
|
||||||
let model = try create.toModel(createdByID: createdById)
|
let model = try create.toModel()
|
||||||
try await model.save(on: database)
|
try await model.save(on: database)
|
||||||
let fetched = try await PurchaseOrderModel.allQuery(on: database).filter(\.$id == model.id!).first()!
|
let fetched = try await PurchaseOrderModel.allQuery(on: database).filter(\.$id == model.id!).first()!
|
||||||
return try fetched.toDTO()
|
return try fetched.toDTO()
|
||||||
@@ -62,7 +62,7 @@ extension PurchaseOrder {
|
|||||||
|
|
||||||
extension PurchaseOrder.Create {
|
extension PurchaseOrder.Create {
|
||||||
|
|
||||||
func toModel(createdByID: User.ID) throws -> PurchaseOrderModel {
|
func toModel() throws -> PurchaseOrderModel {
|
||||||
try validate()
|
try validate()
|
||||||
return .init(
|
return .init(
|
||||||
materials: materials,
|
materials: materials,
|
||||||
@@ -157,7 +157,7 @@ final class PurchaseOrderModel: Model, Codable, @unchecked Sendable {
|
|||||||
truckStock: truckStock,
|
truckStock: truckStock,
|
||||||
createdBy: createdBy.toDTO(),
|
createdBy: createdBy.toDTO(),
|
||||||
createdFor: createdFor.toDTO(),
|
createdFor: createdFor.toDTO(),
|
||||||
vendorBranch: vendorBranch.toDTO(),
|
vendorBranch: vendorBranch.toDetail(),
|
||||||
createdAt: createdAt,
|
createdAt: createdAt,
|
||||||
updatedAt: updatedAt
|
updatedAt: updatedAt
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -34,6 +34,11 @@ public extension DatabaseClient.VendorBranches {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return try await query.all().map { try $0.toDTO() }
|
return try await query.all().map { try $0.toDTO() }
|
||||||
|
} fetchAllWithDetail: {
|
||||||
|
try await VendorBranchModel.query(on: db)
|
||||||
|
.with(\.$vendor)
|
||||||
|
.all()
|
||||||
|
.map { try $0.toDetail() }
|
||||||
} get: { id in
|
} get: { id in
|
||||||
try await VendorBranchModel.find(id, on: db).map { try $0.toDTO() }
|
try await VendorBranchModel.find(id, on: db).map { try $0.toDTO() }
|
||||||
} update: { id, updates in
|
} update: { id, updates in
|
||||||
@@ -147,6 +152,16 @@ final class VendorBranchModel: Model, @unchecked Sendable {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toDetail() throws -> VendorBranch.Detail {
|
||||||
|
try .init(
|
||||||
|
id: requireID(),
|
||||||
|
name: name,
|
||||||
|
vendor: vendor.toDTO(),
|
||||||
|
createdAt: createdAt,
|
||||||
|
updatedAt: updatedAt
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func applyUpdates(_ updates: VendorBranch.Update) throws {
|
func applyUpdates(_ updates: VendorBranch.Update) throws {
|
||||||
try updates.validate()
|
try updates.validate()
|
||||||
if let name = updates.name {
|
if let name = updates.name {
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ public struct Employee: Codable, Equatable, Identifiable, Sendable {
|
|||||||
self.lastName = lastName
|
self.lastName = lastName
|
||||||
self.updatedAt = updatedAt
|
self.updatedAt = updatedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var fullName: String {
|
||||||
|
"\(firstName.capitalized) \(lastName.capitalized)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension Employee {
|
public extension Employee {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public struct PurchaseOrder: Codable, Equatable, Identifiable, Sendable {
|
|||||||
public var truckStock: Bool
|
public var truckStock: Bool
|
||||||
public var createdBy: User
|
public var createdBy: User
|
||||||
public var createdFor: Employee
|
public var createdFor: Employee
|
||||||
public var vendorBranch: VendorBranch
|
public var vendorBranch: VendorBranch.Detail
|
||||||
public var createdAt: Date?
|
public var createdAt: Date?
|
||||||
public var updatedAt: Date?
|
public var updatedAt: Date?
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ public struct PurchaseOrder: Codable, Equatable, Identifiable, Sendable {
|
|||||||
truckStock: Bool,
|
truckStock: Bool,
|
||||||
createdBy: User,
|
createdBy: User,
|
||||||
createdFor: Employee,
|
createdFor: Employee,
|
||||||
vendorBranch: VendorBranch,
|
vendorBranch: VendorBranch.Detail,
|
||||||
createdAt: Date?,
|
createdAt: Date?,
|
||||||
updatedAt: Date?
|
updatedAt: Date?
|
||||||
) {
|
) {
|
||||||
@@ -41,9 +41,40 @@ public struct PurchaseOrder: Codable, Equatable, Identifiable, Sendable {
|
|||||||
|
|
||||||
public extension PurchaseOrder {
|
public extension PurchaseOrder {
|
||||||
|
|
||||||
// TODO: Add created by id.
|
|
||||||
struct Create: Codable, Sendable {
|
struct Create: Codable, Sendable {
|
||||||
|
|
||||||
|
public let id: Int?
|
||||||
|
public let workOrder: Int?
|
||||||
|
public let materials: String
|
||||||
|
public let customer: String
|
||||||
|
public let truckStock: Bool?
|
||||||
|
public let createdByID: User.ID
|
||||||
|
public let createdForID: Employee.ID
|
||||||
|
public let vendorBranchID: VendorBranch.ID
|
||||||
|
|
||||||
|
public init(
|
||||||
|
id: Int? = nil,
|
||||||
|
workOrder: Int? = nil,
|
||||||
|
materials: String,
|
||||||
|
customer: String,
|
||||||
|
truckStock: Bool? = nil,
|
||||||
|
createdByID: User.ID,
|
||||||
|
createdForID: Employee.ID,
|
||||||
|
vendorBranchID: VendorBranch.ID
|
||||||
|
) {
|
||||||
|
self.id = id
|
||||||
|
self.workOrder = workOrder
|
||||||
|
self.materials = materials
|
||||||
|
self.customer = customer
|
||||||
|
self.truckStock = truckStock
|
||||||
|
self.createdByID = createdByID
|
||||||
|
self.createdForID = createdForID
|
||||||
|
self.vendorBranchID = vendorBranchID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CreateIntermediate: Codable, Sendable {
|
||||||
|
|
||||||
public let id: Int?
|
public let id: Int?
|
||||||
public let workOrder: Int?
|
public let workOrder: Int?
|
||||||
public let materials: String
|
public let materials: String
|
||||||
@@ -69,44 +100,26 @@ public extension PurchaseOrder {
|
|||||||
self.createdForID = createdForID
|
self.createdForID = createdForID
|
||||||
self.vendorBranchID = vendorBranchID
|
self.vendorBranchID = vendorBranchID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func toCreate(createdByID userID: User.ID) -> PurchaseOrder.Create {
|
||||||
|
.init(
|
||||||
|
id: id,
|
||||||
|
workOrder: workOrder,
|
||||||
|
materials: materials,
|
||||||
|
customer: customer,
|
||||||
|
truckStock: truckStock,
|
||||||
|
createdByID: userID,
|
||||||
|
createdForID: createdForID,
|
||||||
|
vendorBranchID: vendorBranchID
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
// public extension PurchaseOrder {
|
|
||||||
|
|
||||||
// static func generateMocks(
|
public extension PurchaseOrder.CreateIntermediate {
|
||||||
// count: Int = 50,
|
|
||||||
// employees: [Employee],
|
|
||||||
// users: [User],
|
|
||||||
// vendorBranches: [VendorBranch]
|
|
||||||
// ) -> [Self] {
|
|
||||||
// @Dependency(\.date.now) var now
|
|
||||||
// @Dependency(\.uuid) var uuid
|
|
||||||
//
|
|
||||||
// var output = [Self]()
|
|
||||||
//
|
|
||||||
// for id in 0 ... count {
|
|
||||||
// output.append(.init(
|
|
||||||
// id: id,
|
|
||||||
// workOrder: Int.random(in: 0 ... 100),
|
|
||||||
// materials: "Some thing",
|
|
||||||
// customer: "\(RandomNames.firstNames.randomElement()!) \(RandomNames.lastNames.randomElement()!)",
|
|
||||||
// truckStock: Bool.random(),
|
|
||||||
// createdBy: users.randomElement()!,
|
|
||||||
// createdFor: employees.randomElement()!,
|
|
||||||
// vendorBranch: vendorBranches.randomElement()!,
|
|
||||||
// createdAt: now,
|
|
||||||
// updatedAt: now
|
|
||||||
// ))
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return output
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
public extension PurchaseOrder.Create {
|
|
||||||
static func generateMocks(
|
static func generateMocks(
|
||||||
count: Int = 50,
|
count: Int = 50,
|
||||||
employees: [Employee],
|
employees: [Employee],
|
||||||
|
|||||||
@@ -34,6 +34,28 @@ public extension VendorBranch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Detail: Codable, Equatable, Identifiable, Sendable {
|
||||||
|
public var id: UUID
|
||||||
|
public var name: String
|
||||||
|
public var vendor: Vendor
|
||||||
|
public var createdAt: Date?
|
||||||
|
public var updatedAt: Date?
|
||||||
|
|
||||||
|
public init(
|
||||||
|
id: UUID,
|
||||||
|
name: String,
|
||||||
|
vendor: Vendor,
|
||||||
|
createdAt: Date? = nil,
|
||||||
|
updatedAt: Date? = nil
|
||||||
|
) {
|
||||||
|
self.id = id
|
||||||
|
self.name = name
|
||||||
|
self.vendor = vendor
|
||||||
|
self.createdAt = createdAt
|
||||||
|
self.updatedAt = updatedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct Update: Codable, Sendable {
|
struct Update: Codable, Sendable {
|
||||||
public let name: String?
|
public let name: String?
|
||||||
|
|
||||||
@@ -44,30 +66,6 @@ public extension VendorBranch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
// public extension VendorBranch {
|
|
||||||
//
|
|
||||||
// static func generateMocks(countPerVendor: Int = 3, vendors: [Vendor]) -> [Self] {
|
|
||||||
// @Dependency(\.date.now) var now
|
|
||||||
// @Dependency(\.uuid) var uuid
|
|
||||||
//
|
|
||||||
// var output = [Self]()
|
|
||||||
//
|
|
||||||
// for vendor in vendors {
|
|
||||||
// for _ in 0 ... countPerVendor {
|
|
||||||
// output.append(.init(
|
|
||||||
// id: uuid(),
|
|
||||||
// name: RandomNames.cityNames.randomElement()!,
|
|
||||||
// vendorID: vendor.id,
|
|
||||||
// createdAt: now,
|
|
||||||
// updatedAt: now
|
|
||||||
// ))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return output
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
public extension VendorBranch.Create {
|
public extension VendorBranch.Create {
|
||||||
|
|
||||||
static func generateMocks(countPerVendor: Int = 3, vendors: [Vendor]) -> [Self] {
|
static func generateMocks(countPerVendor: Int = 3, vendors: [Vendor]) -> [Self] {
|
||||||
|
|||||||
Reference in New Issue
Block a user