feat: Begins po detail.

This commit is contained in:
2025-01-10 17:13:43 -05:00
parent 455287fe1c
commit 59b6d46606
8 changed files with 156 additions and 7 deletions

View File

@@ -301,3 +301,38 @@ tr.htmx-swapping td {
margin-right: 15px; margin-right: 15px;
font-size: 1.5em; font-size: 1.5em;
} }
.btn-row button {
border: none;
text-decoration: none;
color: grey;
background-color: inherit;
font-size: 1.3em;
padding-bottom: 10px;
}
.btn-row button:hover {
color: blue;
}
.btn-detail {
border: none;
text-decoration: none;
color: grey;
background-color: inherit;
font-size: 1.3em;
}
.po-detail table {
border-collapse: collapse;
border: none;
max-width: 300px;
}
.po-detail td {
border: none;
}
.label {
color: #00ffcc;
}

View File

@@ -1,6 +1,6 @@
<div class="sidepanel" id="sidepanel"> <div class="sidepanel" id="sidepanel">
<a href="javscript:void(0)" class="closebtn" onclick="closeSidepanel()">&times;</a> <a href="javscript:void(0)" class="closebtn" onclick="closeSidepanel()">&times;</a>
<a hx-get="/purchase-orders" <a hx-get="/purchase-orders?page=1&limit=50"
hx-target="body" hx-target="body"
hx-push-url="true" hx-push-url="true"
> >

View File

@@ -0,0 +1,46 @@
<div id="po-detail" class="container">
<table>
<tbody>
<tr>
<td class="label"><h3>Purchase Order:</h3></td>
<td><h3>#(purchaseOrder.id)</h3></td>
</tr>
<tr>
<td class="label"><h3>Work Order:</h3></td>
<td><h3>#(purchaseOrder.workOrder)</h3></td>
</tr>
<tr>
<td class="label"><h3>Customer:</h3></td>
<td><h3>#(purchaseOrder.customer)</h3></td>
</tr>
<tr>
<td class="label"><h3>Vendor:</h3></td>
<td><h3>#capitalized(purchaseOrder.vendorBranch.vendor.name) - #capitalized(purchaseOrder.vendorBranch.name)</h3></td>
</tr>
<tr>
<td class="label"><h3>Materials:</h3></td>
<td><h3>#(purchaseOrder.materials)<h3></td>
</tr>
<tr>
<td class="label"><h3>Created For:</h3></td>
<td><h3>#capitalized(purchaseOrder.createdFor.firstName) #capitalized(purchaseOrder.createdFor.lastName)</h3></td>
</tr>
<tr>
<td class="label"><h3>Truck Stock:</h3></td>
<td><h3>#capitalized(purchaseOrder.truckStock)</h3></td>
</tr>
<tr>
<td class="label"><h3>Created By:</h3></td>
<td><h3>#(purchaseOrder.createdBy.username)</h3></td>
</tr>
<tr>
<td class="label"><h3>Date:</h3></td>
<td><h3>#date(purchaseOrder.createdAt)<h3></td>
</tr>
<tr>
<td class="label"><h3>Updated:</h3></td>
<td><h3>#date(purchaseOrder.updatedAt)<h3></td>
</tr>
</tbody>
</table>
</div>

View File

@@ -8,6 +8,28 @@
#extend("form-container"): #export("formContent"): #extend("form-container"): #export("formContent"):
#extend("purchaseOrders/form", form) #extend("purchaseOrders/form", form)
#endexport #endextend #endexport #endextend
<div class="btn-row">
#if(hasPrevious):
<button hx-get="/purchase-orders?page=#(page - 1)&limit=#(limit)"
hx-target="body"
hx-swap="outerHTML"
hx-push-url="true"
style="float: left;"
>
&lsaquo; Previous
</button>
#endif
#if(hasNext):
<button hx-get="/purchase-orders?page=#(page + 1)&limit=#(limit)"
hx-target="body"
hx-swap="outerHTML"
hx-push-url="true"
style="float: right;"
>
Next &rsaquo;
</button>
#endif
</div>
#extend("purchaseOrders/table") #extend("purchaseOrders/table")
</div> </div>
#endexport #endexport

View File

@@ -7,7 +7,14 @@
<td>#capitalized(createdFor.firstName) #capitalized(createdFor.lastName)</td> <td>#capitalized(createdFor.firstName) #capitalized(createdFor.lastName)</td>
<td>#(createdBy.username)</td> <td>#(createdBy.username)</td>
<td>#capitalized(truckStock)</td> <td>#capitalized(truckStock)</td>
<td> <td style="text-align: center;">
<!-- TODO: add buttons here --> <!-- TODO: add buttons here -->
<button class="btn btn-detail"
hx-get="/purchase-orders/#(id)"
hx-target="#home-content"
hx-push-url="true"
>
&rsaquo;
</button>
</td> </td>
</tr> </tr>

View File

@@ -12,24 +12,36 @@ struct PurchaseOrderViewController: RouteCollection {
pos.get(use: index(req:)) pos.get(use: index(req:))
pos.post(use: create(req:)) pos.post(use: create(req:))
pos.group(":id") {
$0.get(use: detail(req:))
}
} }
// TODO: Use pageinated version.
@Sendable @Sendable
func index(req: Request) async throws -> View { func index(req: Request) async throws -> View {
let purchaseOrders = try await api2.fetchAll(on: req.db) let params = try? req.query.decode(PurchaseOrderIndex.self)
let purchaseOrdersPage = try await api2.fetchPage(params?.page ?? 1, limit: params?.limit ?? 50, on: req.db)
let branches = try await self.branches.getBranches(req: req) let branches = try await self.branches.getBranches(req: req)
let employees = try await employeesApi.index(req: req) let employees = try await employeesApi.index(req: req)
req.logger.debug("Branches: \(branches)") req.logger.debug("Branches: \(branches)")
return try await req.view.render( return try await req.view.render(
"purchaseOrders/index", "purchaseOrders/index",
PurchaseOrderCTX( PurchaseOrderCTX(
purchaseOrders: purchaseOrders, page: purchaseOrdersPage,
form: .create(branches: branches, employees: employees) form: .create(branches: branches, employees: employees)
) )
) )
} }
@Sendable
func detail(req: Request) async throws -> View {
guard let id = req.parameters.get("id", as: PurchaseOrder.IDValue.self) else {
throw Abort(.badRequest, reason: "Id not supplied.")
}
let purchaseOrder = try await api2.get(id: id, on: req.db)
return try await req.view.render("purchaseOrders/detail", ["purchaseOrder": purchaseOrder])
}
@Sendable @Sendable
func create(req: Request) async throws -> View { func create(req: Request) async throws -> View {
try PurchaseOrder.FormCreate.validate(content: req) try PurchaseOrder.FormCreate.validate(content: req)
@@ -40,9 +52,33 @@ struct PurchaseOrderViewController: RouteCollection {
} }
} }
private struct PurchaseOrderIndex: Content {
let page: Int?
let limit: Int?
}
private struct PurchaseOrderCTX: Content { private struct PurchaseOrderCTX: Content {
let purchaseOrders: [PurchaseOrder.DTO] let purchaseOrders: [PurchaseOrder.DTO]
let page: Int
let limit: Int
let hasNext: Bool
let hasPrevious: Bool
let form: PurchaseOrderFormCTX? let form: PurchaseOrderFormCTX?
init(page: Page<PurchaseOrder.DTO>, form: PurchaseOrderFormCTX?) {
self.purchaseOrders = page.items
self.page = page.metadata.page
self.limit = page.metadata.per
self.hasNext = page.metadata.hasNext
self.hasPrevious = page.metadata.page > 1
self.form = form
}
}
private extension PageMetadata {
var hasNext: Bool {
total > (page * per)
}
} }
private struct PurchaseOrderFormCTX: Content { private struct PurchaseOrderFormCTX: Content {

View File

@@ -49,7 +49,7 @@ struct UserFormCTX: Content {
htmxForm: .init( htmxForm: .init(
formClass: "user-form", formClass: "user-form",
formId: "user-form", formId: "user-form",
htmxTargetUrl: .post("/login\(next != nil ? "?next=\(next!)" : "")"), htmxTargetUrl: .post("/login\((next != nil && next != "/") ? "?next=\(next!)" : "")"),
htmxTarget: "body", htmxTarget: "body",
htmxPushUrl: true, htmxPushUrl: true,
htmxResetAfterRequest: true, htmxResetAfterRequest: true,

View File

@@ -35,7 +35,9 @@ struct ViewController: RouteCollection {
func getLogin(req: Request) async throws -> View { func getLogin(req: Request) async throws -> View {
req.logger.debug("Login Query: \(req.url.query ?? "n/a")") req.logger.debug("Login Query: \(req.url.query ?? "n/a")")
let params = try? req.query.decode(LoginParameter.self) let params = try? req.query.decode(LoginParameter.self)
return try await req.view.render("login", UserFormCTX.signIn(next: params?.next)) return try await req.view.render(
"login", UserFormCTX.signIn(next: params?.next)
)
} }
@Sendable @Sendable
@@ -95,6 +97,7 @@ private struct UserForm: Content {
enum HomeRoute: String, Content { enum HomeRoute: String, Content {
case employees case employees
case purchaseOrders
case users case users
case vendors case vendors
} }