diff --git a/Public/css/main.css b/Public/css/main.css index b12bfbe..10c1916 100644 --- a/Public/css/main.css +++ b/Public/css/main.css @@ -177,7 +177,7 @@ a.toggle, a img.toggle { height: 275px; width: 0; position: fixed; - z-index: 1; + z-index: 2; top: 0; right: 0; background-color: #111; diff --git a/Public/js/main.js b/Public/js/main.js index de25716..401445c 100644 --- a/Public/js/main.js +++ b/Public/js/main.js @@ -26,10 +26,16 @@ function updateDropDownSelection(id, contentId) { } function openSidepanel() { + if (searchBtn = document.getElementById("btn-search")) { + searchBtn.style.display = "none"; + } document.getElementById("sidepanel").style.width = "250px"; } function closeSidepanel() { + if (searchBtn = document.getElementById("btn-search")) { + searchBtn.style.display = "inline"; + } document.getElementById("sidepanel").style.width = "0"; } diff --git a/Sources/App/Controllers/View/PurchaseOrderSearchViewController.swift b/Sources/App/Controllers/View/PurchaseOrderSearchViewController.swift index e1a0475..22b7377 100644 --- a/Sources/App/Controllers/View/PurchaseOrderSearchViewController.swift +++ b/Sources/App/Controllers/View/PurchaseOrderSearchViewController.swift @@ -13,7 +13,6 @@ struct PurchaseOrderSearchViewController: RouteCollection { func boot(routes: any RoutesBuilder) throws { let route = routes.protected.grouped("purchase-orders", "search") route.get(use: index) - // route.get("form", use: form) route.post(use: post) } @@ -21,7 +20,7 @@ struct PurchaseOrderSearchViewController: RouteCollection { func index(req: Request) async throws -> HTMLResponse { let query = try? req.query.decode(FormQuery.self) let html = PurchaseOrderSearch(context: query?.context) - guard req.isHtmxRequest else { + if query?.table == true || !req.isHtmxRequest { return await req.render { mainPage(search: html) } } return await req.render { html } @@ -34,17 +33,6 @@ struct PurchaseOrderSearchViewController: RouteCollection { return await req.render { PurchaseOrderTable(page: results, context: .search, searchContext: nil) } } - // - // @Sendable - // func form(req: Request) async throws -> HTMLResponse { - // let query = try req.query.decode(FormQuery.self) - // let html = PurchaseOrderSearch(context: query.context) - // guard req.isHtmxRequest else { - // return await req.render { mainPage(search: html) } - // } - // return await req.render { PurchaseOrderSearch(context: query.context) } - // } - func mainPage(search: PurchaseOrderSearch = .init()) -> some SendableHTMLDocument { MainPage(displayNav: true, route: .purchaseOrders) { div(.class("container"), .id("purchase-order-content")) { @@ -81,4 +69,5 @@ extension PurchaseOrderSearchContent { private struct FormQuery: Content { let context: PurchaseOrderSearchContext + let table: Bool? } diff --git a/Sources/App/Controllers/ViewController.swift b/Sources/App/Controllers/ViewController.swift deleted file mode 100644 index 8a75fc0..0000000 --- a/Sources/App/Controllers/ViewController.swift +++ /dev/null @@ -1,111 +0,0 @@ -// import Fluent -// import Leaf -// import Vapor -// -// struct ViewController: RouteCollection { -// -// private let api = ApiController() -// private let employees = EmployeeViewController() -// private let purchaseOrders = PurchaseOrderViewController() -// private let users = UserViewController() -// private let vendors = VendorViewController() -// -// func boot(routes: any RoutesBuilder) throws { -// let protected = routes.protected -// -// // MARK: - Non-protected routes. -// -// // routes.get(use: index(req:)) -// routes.get("login", use: getLogin(req:)) -// routes.post("login", use: postLogin(req:)) -// -// // MARK: Protected routes. -// -// protected.get(use: home(req:)) -// protected.get("**", use: catchAll(req:)) -// protected.post("logout", use: logout(req:)) -// // protected.get("users", use: users(req:)) -// try routes.register(collection: employees) -// try routes.register(collection: purchaseOrders) -// try routes.register(collection: users) -// try routes.register(collection: vendors) -// } -// -// @Sendable -// func getLogin(req: Request) async throws -> View { -// req.logger.debug("Login Query: \(req.url.query ?? "n/a")") -// let params = try? req.query.decode(LoginParameter.self) -// return try await req.view.render( -// "login", UserFormCTX.signIn(next: params?.next) -// ) -// } -// -// @Sendable -// func postLogin(req: Request) async throws -> Response { -// let content = try req.content.decode(UserForm.self) -// guard let user = try await User.query(on: req.db) -// .filter(\.$username == content.username) -// .first() -// else { -// throw Abort(.badRequest, reason: "User not found.") -// } -// -// guard try user.verify(password: content.password) else { -// throw Abort(.unauthorized, reason: "Invalid password.") -// } -// req.auth.login(user) -// -// req.logger.debug("User logged in: \(user.toDTO())") -// return try await home(req: req) -// } -// -// @Sendable -// func logout(req: Request) async throws -> View { -// req.auth.logout(User.self) -// return try await req.view.render("login") -// } -// -// @Sendable -// func home(req: Request) async throws -> Response { -// if let loginParams = try? req.query.decode(LoginParameter.self) { -// return req.redirect(to: loginParams.next) -// } -// return try await req.view.render("home").encodeResponse(for: req) -// } -// -// @Sendable -// func catchAll(req: Request) async throws -> View { -// var route: HomeRoute? -// -// if let loginParams = try? req.query.decode(LoginParameter.self), -// let next = loginParams.next.split(separator: "/").last -// { -// route = HomeRoute(rawValue: String(next)) -// } else if let routeString = req.parameters.getCatchall().last { -// route = HomeRoute(rawValue: routeString) -// } -// -// return try await req.view.render("home", HomeCTX(route: route)) -// } -// -// } -// -// private struct UserForm: Content { -// let username: String -// let password: String -// } -// -// enum HomeRoute: String, Content { -// case employees -// case purchaseOrders -// case users -// case vendors -// } -// -// struct HomeCTX: Content { -// let route: HomeRoute? -// } -// -// struct LoginParameter: Content { -// let next: String -// } diff --git a/Sources/App/Views/PurchaseOrders/PurchaseOrderForm.swift b/Sources/App/Views/PurchaseOrders/PurchaseOrderForm.swift index 61d7d20..d0fcab7 100644 --- a/Sources/App/Views/PurchaseOrders/PurchaseOrderForm.swift +++ b/Sources/App/Views/PurchaseOrders/PurchaseOrderForm.swift @@ -1,9 +1,12 @@ +import Dependencies import Elementary import ElementaryHTMX import SharedModels struct PurchaseOrderForm: HTML { + @Dependency(\.dateFormatter) var dateFormatter + let purchaseOrder: PurchaseOrder? let shouldShow: Bool @@ -98,6 +101,17 @@ struct PurchaseOrderForm: HTML { ) } } + if let purchaseOrder, let createdAt = purchaseOrder.createdAt { + div(.class("row")) { + label(.class("label col-2")) { "Created:" } + h3(.class("col-2")) { dateFormatter.string(from: createdAt) } + if let updatedAt = purchaseOrder.updatedAt { + div(.class("col-1")) {} + label(.class("label col-2")) { "Updated:" } + h3(.class("col-2")) { dateFormatter.string(from: updatedAt) } + } + } + } div(.class("btn-row")) { button(.class("btn-primary"), .type(.submit)) { buttonLabel } if purchaseOrder != nil { diff --git a/Sources/App/Views/PurchaseOrders/PurchaseOrderTable.swift b/Sources/App/Views/PurchaseOrders/PurchaseOrderTable.swift index c8b59ba..91ad083 100644 --- a/Sources/App/Views/PurchaseOrders/PurchaseOrderTable.swift +++ b/Sources/App/Views/PurchaseOrders/PurchaseOrderTable.swift @@ -59,8 +59,9 @@ struct PurchaseOrderTable: HTML { div(.class("btn-row")) { if context != .search { button( + .id("btn-search"), .class("btn-primary"), .style("position: absolute; top: 80px; right: 20px;"), - .hx.get(route: .purchaseOrders(.search(.context(.employee)))), + .hx.get(route: .purchaseOrders(.search(.context(.employee, table: true)))), .hx.target(.body), .hx.swap(.outerHTML.transition(true).swap("0.5s")), .hx.pushURL(true) diff --git a/Sources/App/Views/ViewRoute.swift b/Sources/App/Views/ViewRoute.swift index 0a45c79..9425700 100644 --- a/Sources/App/Views/ViewRoute.swift +++ b/Sources/App/Views/ViewRoute.swift @@ -3,7 +3,38 @@ import ElementaryHTMX import Fluent import SharedModels -enum RouteContainer { +extension HTMLAttribute.hx { + static func get(route: RouteKey) -> HTMLAttribute { + get(route.url) + } + + static func post(route: RouteKey) -> HTMLAttribute { + post(route.url) + } + + static func put(route: RouteKey) -> HTMLAttribute { + put(route.url) + } + + static func delete(route: RouteKey) -> HTMLAttribute { + delete(route.url) + } +} + +extension HTMLAttribute.hx { + + static func target(_ target: HXTarget) -> HTMLAttribute { + Self.target(target.selector) + } +} + +extension HTMLAttribute where Tag: HTMLTrait.Attributes.Global { + static func id(_ target: HXTarget) -> Self { + id(target.id) + } +} + +enum RouteKey { case employees(EmployeeRoute? = nil) case purchaseOrders(PurchaseOrderRoute? = nil) case users(UserRoute? = nil) @@ -59,12 +90,14 @@ enum RouteContainer { } enum SearchQuery { - case context(PurchaseOrderSearchContext) + case context(PurchaseOrderSearchContext, table: Bool? = nil) var query: String { switch self { - case let .context(context): - return "context=\(context.rawValue)" + case let .context(context, table): + let query = "context=\(context.rawValue)" + guard let table else { return query } + return "\(query)&table=\(table)" } } } @@ -84,29 +117,6 @@ enum RouteContainer { } -extension HTMLAttribute.hx { - static func get(route: RouteContainer) -> HTMLAttribute { - get(route.url) - } - - static func post(route: RouteContainer) -> HTMLAttribute { - post(route.url) - } - - static func put(route: RouteContainer) -> HTMLAttribute { - put(route.url) - } - - static func delete(route: RouteContainer) -> HTMLAttribute { - delete(route.url) - } - -} - -enum RouteKey: String { - case purchaseOrders = "purchase-orders" -} - enum HXTarget { case body case employee(EmployeeKey) @@ -185,19 +195,6 @@ enum HXTarget { } } -extension HTMLAttribute.hx { - - static func target(_ target: HXTarget) -> HTMLAttribute { - Self.target(target.selector) - } -} - -extension HTMLAttribute where Tag: HTMLTrait.Attributes.Global { - static func id(_ target: HXTarget) -> Self { - id(target.id) - } -} - enum ViewRoute: String { case employees