feat: Initial view controller dependency and snapshot tests.
This commit is contained in:
343
Tests/ViewControllerTests/ViewControllerTests.swift
Normal file
343
Tests/ViewControllerTests/ViewControllerTests.swift
Normal file
@@ -0,0 +1,343 @@
|
||||
import DatabaseClient
|
||||
import Dependencies
|
||||
import Foundation
|
||||
import HtmlSnapshotTesting
|
||||
import SharedModels
|
||||
import SnapshotTesting
|
||||
import Testing
|
||||
import ViewControllerLive
|
||||
|
||||
// NOTE: Passing routes as arguments doesn't work bc they are sometimes not in the same order.
|
||||
@Suite("ViewControllerTests")
|
||||
struct ViewControllerTests {
|
||||
|
||||
let record: SnapshotTestingConfiguration.Record = .missing
|
||||
|
||||
@Test
|
||||
func sanity() {
|
||||
#expect(Bool(true))
|
||||
}
|
||||
|
||||
@Test
|
||||
func employeeViews() async throws {
|
||||
try await withSnapshotTesting(record: record) {
|
||||
try await withDependencies {
|
||||
$0.viewController = .liveValue
|
||||
$0.database.employees = .mock
|
||||
} operation: {
|
||||
@Dependency(\.viewController) var viewController
|
||||
@Dependency(\.database) var database
|
||||
|
||||
var htmlString = try await viewController.render(.employee(.index))
|
||||
assertSnapshot(of: htmlString, as: .html)
|
||||
|
||||
htmlString = try await viewController.render(.employee(.form))
|
||||
assertSnapshot(of: htmlString, as: .html)
|
||||
|
||||
htmlString = try await viewController.render(.employee(.get(id: UUID(0))))
|
||||
assertSnapshot(of: htmlString, as: .html)
|
||||
|
||||
htmlString = try await viewController.render(.employee(.select(context: .purchaseOrderForm)))
|
||||
assertSnapshot(of: htmlString, as: .html)
|
||||
|
||||
htmlString = try await viewController.render(.employee(.select(context: .purchaseOrderSearch)))
|
||||
assertSnapshot(of: htmlString, as: .html)
|
||||
|
||||
htmlString = try await viewController.render(.employee(.update(id: UUID(0), updates: .mock)))
|
||||
assertSnapshot(of: htmlString, as: .html)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func loginViews() async throws {
|
||||
try await withSnapshotTesting(record: record) {
|
||||
try await withDependencies {
|
||||
$0.viewController = .liveValue
|
||||
} operation: {
|
||||
@Dependency(\.viewController) var viewController
|
||||
|
||||
let htmlString = try await viewController.render(.login(.index(next: "/purchase-orders")))
|
||||
assertSnapshot(of: htmlString, as: .html)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @Test
|
||||
// func purchaseOrderViews() async throws {
|
||||
// try await withSnapshotTesting(record: record) {
|
||||
// try await withDependencies {
|
||||
// $0.database.purchaseOrders = .mock
|
||||
// $0.viewController = .liveValue
|
||||
// } operation: {
|
||||
// @Dependency(\.viewController) var viewController
|
||||
//
|
||||
// let htmlString = try await viewController.render(.login(.index(next: "/purchase-orders")))
|
||||
// assertSnapshot(of: htmlString, as: .html)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
@Test
|
||||
func userViews() async throws {
|
||||
try await withDependencies {
|
||||
$0.dateFormatter = .liveValue
|
||||
$0.database.users = .mock
|
||||
$0.viewController = .liveValue
|
||||
} operation: {
|
||||
@Dependency(\.database) var database
|
||||
@Dependency(\.viewController) var viewController
|
||||
|
||||
var htmlString = try await viewController.render(.user(.index))
|
||||
assertSnapshot(of: htmlString, as: .html)
|
||||
|
||||
htmlString = try await viewController.render(.user(.form))
|
||||
assertSnapshot(of: htmlString, as: .html)
|
||||
|
||||
htmlString = try await viewController.render(.user(.create(.mock)))
|
||||
assertSnapshot(of: htmlString, as: .html)
|
||||
|
||||
htmlString = try await viewController.render(.user(.get(id: UUID(0))))
|
||||
assertSnapshot(of: htmlString, as: .html)
|
||||
|
||||
htmlString = try await viewController.render(.user(.update(id: UUID(0), updates: .mock)))
|
||||
assertSnapshot(of: htmlString, as: .html)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func vendorViews() async throws {
|
||||
try await withDependencies {
|
||||
$0.dateFormatter = .liveValue
|
||||
$0.database.vendors = .mock
|
||||
$0.viewController = .liveValue
|
||||
} operation: {
|
||||
@Dependency(\.database) var database
|
||||
@Dependency(\.viewController) var viewController
|
||||
|
||||
var htmlString = try await viewController.render(.vendor(.index))
|
||||
assertSnapshot(of: htmlString, as: .html)
|
||||
|
||||
htmlString = try await viewController.render(.vendor(.form))
|
||||
assertSnapshot(of: htmlString, as: .html)
|
||||
|
||||
htmlString = try await viewController.render(.vendor(.create(.mock)))
|
||||
assertSnapshot(of: htmlString, as: .html)
|
||||
|
||||
htmlString = try await viewController.render(.vendor(.get(id: UUID(0))))
|
||||
assertSnapshot(of: htmlString, as: .html)
|
||||
|
||||
htmlString = try await viewController.render(.vendor(.update(id: UUID(0), updates: .mock)))
|
||||
assertSnapshot(of: htmlString, as: .html)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func vendorBranchViews() async throws {
|
||||
try await withDependencies {
|
||||
$0.dateFormatter = .liveValue
|
||||
$0.database.vendors = .mock
|
||||
$0.database.vendorBranches = .mock
|
||||
$0.viewController = .liveValue
|
||||
} operation: {
|
||||
@Dependency(\.database) var database
|
||||
@Dependency(\.viewController) var viewController
|
||||
|
||||
var htmlString = try await viewController.render(.vendorBranch(.index(for: UUID(0))))
|
||||
assertSnapshot(of: htmlString, as: .html)
|
||||
|
||||
htmlString = try await viewController.render(.vendorBranch(.select(context: .purchaseOrderSearch)))
|
||||
assertSnapshot(of: htmlString, as: .html)
|
||||
|
||||
htmlString = try await viewController.render(.vendorBranch(.select(context: .purchaseOrderForm)))
|
||||
assertSnapshot(of: htmlString, as: .html)
|
||||
|
||||
htmlString = try await viewController.render(.vendorBranch(.create(.mock)))
|
||||
assertSnapshot(of: htmlString, as: .html)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ViewController {
|
||||
|
||||
func render(_ route: ViewRoute) async throws -> String {
|
||||
guard let html = try await view(for: route, isHtmxRequest: true, authenticate: { _ in }) else {
|
||||
throw TestError()
|
||||
}
|
||||
return html.renderFormatted()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct TestError: Error {}
|
||||
|
||||
extension DatabaseClient.Employees {
|
||||
static var mock: Self {
|
||||
.init(
|
||||
create: { _ in .mock },
|
||||
delete: { _ in },
|
||||
fetchAll: { _ in [Employee.mock] },
|
||||
get: { _ in Employee.mock },
|
||||
update: { _, _ in Employee.mock }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension DatabaseClient.Users {
|
||||
static var mock: Self {
|
||||
.init(
|
||||
count: { 1 },
|
||||
create: { _ in User.mock },
|
||||
delete: { _ in },
|
||||
fetchAll: { [User.mock] },
|
||||
get: { _ in User.mock },
|
||||
login: { _ in User.Token.mock },
|
||||
logout: { _ in },
|
||||
token: { _ in User.Token.mock },
|
||||
update: { _, _ in User.mock }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension DatabaseClient.Vendors {
|
||||
static var mock: Self {
|
||||
.init(
|
||||
create: { _ in Vendor.mock },
|
||||
delete: { _ in },
|
||||
fetchAll: { _ in [Vendor.mock] },
|
||||
get: { _, _ in Vendor.mock },
|
||||
update: { _, _, _ in Vendor.mock }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension DatabaseClient.VendorBranches {
|
||||
static var mock: Self {
|
||||
.init(
|
||||
create: { _ in VendorBranch.mock },
|
||||
delete: { _ in },
|
||||
fetchAll: { _ in [VendorBranch.mock] },
|
||||
fetchAllWithDetail: { [VendorBranch.Detail.mock] },
|
||||
get: { _ in VendorBranch.mock },
|
||||
update: { _, _ in VendorBranch.mock }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension Date {
|
||||
static var mock: Self {
|
||||
Date(timeIntervalSince1970: 1_234_567_890)
|
||||
}
|
||||
}
|
||||
|
||||
extension Employee {
|
||||
static var mock: Self {
|
||||
Employee(
|
||||
id: UUID(0),
|
||||
createdAt: Date(timeIntervalSince1970: 1_234_567_890),
|
||||
firstName: "Testy",
|
||||
lastName: "McTestface",
|
||||
updatedAt: Date(timeIntervalSince1970: 1_234_567_890)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension Employee.Create {
|
||||
static var mock: Self {
|
||||
.init(firstName: "Testy", lastName: "McTestface")
|
||||
}
|
||||
|
||||
func employeeMock() -> Employee {
|
||||
@Dependency(\.date.now) var now
|
||||
return .init(
|
||||
id: UUID(0),
|
||||
createdAt: Date(timeIntervalSince1970: 1_234_567_890),
|
||||
firstName: firstName,
|
||||
lastName: lastName,
|
||||
updatedAt: Date(timeIntervalSince1970: 1_234_567_890)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension Employee.Update {
|
||||
static var mock: Self {
|
||||
.init(firstName: "Testy", lastName: "McTestface", active: false)
|
||||
}
|
||||
}
|
||||
|
||||
extension User {
|
||||
static var mock: Self {
|
||||
.init(id: UUID(0), email: "test@example.com", username: "test")
|
||||
}
|
||||
}
|
||||
|
||||
extension User.Create {
|
||||
static var mock: Self {
|
||||
.init(username: "test", email: "test@example.com", password: "super-secret", confirmPassword: "super-secret")
|
||||
}
|
||||
}
|
||||
|
||||
extension User.Token {
|
||||
static var mock: Self {
|
||||
.init(id: UUID(1), userID: UUID(0), value: "test-token")
|
||||
}
|
||||
}
|
||||
|
||||
extension User.Update {
|
||||
static var mock: Self {
|
||||
User.Update(username: "test", email: "test@test.com")
|
||||
}
|
||||
}
|
||||
|
||||
extension Vendor {
|
||||
static var mock: Self {
|
||||
.init(id: UUID(0), name: "Test", branches: nil, createdAt: .mock, updatedAt: .mock)
|
||||
}
|
||||
}
|
||||
|
||||
extension Vendor.Create {
|
||||
static var mock: Self {
|
||||
.init(name: "Test")
|
||||
}
|
||||
}
|
||||
|
||||
extension Vendor.Update {
|
||||
static var mock: Self {
|
||||
.init(name: "Test")
|
||||
}
|
||||
}
|
||||
|
||||
extension VendorBranch {
|
||||
static var mock: Self {
|
||||
.init(id: UUID(1), name: "Mock", vendorID: UUID(0), createdAt: .mock, updatedAt: .mock)
|
||||
}
|
||||
}
|
||||
|
||||
extension VendorBranch.Create {
|
||||
static var mock: Self {
|
||||
.init(name: "Mock", vendorID: UUID(0))
|
||||
}
|
||||
}
|
||||
|
||||
extension VendorBranch.Detail {
|
||||
static var mock: Self {
|
||||
.init(id: UUID(1), name: "Mock", vendor: .mock, createdAt: .mock, updatedAt: .mock)
|
||||
}
|
||||
}
|
||||
|
||||
extension PurchaseOrder {
|
||||
static var mock: Self {
|
||||
.init(
|
||||
id: 1,
|
||||
workOrder: 12245,
|
||||
materials: "foo",
|
||||
customer: "Testy McTestface",
|
||||
truckStock: true,
|
||||
createdBy: .mock,
|
||||
createdFor: .mock,
|
||||
vendorBranch: .mock,
|
||||
createdAt: .mock,
|
||||
updatedAt: .mock
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Purchase Orders</title>
|
||||
<meta charset="UTF-8">
|
||||
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
|
||||
<script src="/js/main.js"></script>
|
||||
<link rel="stylesheet" href="/css/main.css">
|
||||
<link rel="icon" href="/images/favicon.ico" type="image/x-icon">
|
||||
</head>
|
||||
<body>
|
||||
<header class="header">
|
||||
<div id="logo">HHE - Purchase Orders</div>
|
||||
<div class="sidepanel" id="sidepanel">
|
||||
<a href="javascript:void(0)" class="closebtn" onclick="closeSidepanel()">x</a><a hx-get="/purchase-orders?page=1&limit=50" hx-target="body" hx-push-url="true">Purchase Orders</a><a hx-get="/users" hx-target="body" hx-push-url="true">Users</a><a hx-get="/employees" hx-target="body" hx-push-url="true">Employees</a><a hx-get="/vendors" hx-target="body" hx-push-url="true">Vendors</a>
|
||||
<div style="border-bottom: 1px solid grey; margin-bottom: 5px;"></div>
|
||||
Logout<a hx-post="/logout" hx-target="#content" hx-swap="outerHTML" hx-trigger="click"></a>
|
||||
</div>
|
||||
<button class="openbtn" onclick="openSidepanel()">
|
||||
<img src="/images/menu.svg" style="width: 30px;, height: 30px;">
|
||||
</button>
|
||||
</header>
|
||||
<div class="container" style="padding: 20px 20px;">
|
||||
<h1>Employees</h1>
|
||||
<br>
|
||||
<p class="secondary"><i>Employees are who purchase orders can be issued to.</i></p>
|
||||
<br>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div id="float" class="" style="display: hidden;"></div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th style="width: 100px;">
|
||||
<button class="btn btn-add" style="padding: 0px 10px;" hx-get="/employees/create" hx-target="#float" hx-swap="outerHTML transition:true swap:0.5s">+</button>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="employee-table">
|
||||
<tr id="employee-00000000-0000-0000-0000-000000000000">
|
||||
<td>Testy Mctestface</td>
|
||||
<td>
|
||||
<button class="btn-detail" style="padding-left: 15px;" hx-get="/employees/00000000-0000-0000-0000-000000000000" hx-target="#float" hx-push-url="true" hx-swap="outerHTML transition:true swap:0.5s">〉</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,15 @@
|
||||
<div id="float" class="float" style="display: block;">
|
||||
<div class="btn-row">
|
||||
<button class="btn-close" onclick="toggleContent('float'); window.location.href='/employees';">x</button>
|
||||
</div>
|
||||
<form hx-post="/employees" hx-target="#employee-table" hx-swap="beforeend transition:true swap:0.5s" hx-on::after-request="if(event.detail.successful) toggleContent('float'); window.location.href='/employees';">
|
||||
<div class="row">
|
||||
<input type="text" class="col-5" name="firstName" value="" placeholder="First Name" required>
|
||||
<div class="col-2"></div>
|
||||
<input type="text" class="col-5" name="lastName" value="" placeholder="Last Name" required>
|
||||
</div>
|
||||
<div class="btn-row">
|
||||
<button type="submit" class="btn-primary">Create</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,16 @@
|
||||
<div id="float" class="float" style="display: block;">
|
||||
<div class="btn-row">
|
||||
<button class="btn-close" onclick="toggleContent('float'); window.location.href='/employees';">x</button>
|
||||
</div>
|
||||
<form hx-put="/employees/00000000-0000-0000-0000-000000000000" hx-target="#employee-00000000-0000-0000-0000-000000000000" hx-swap="outerHTML transition:true swap:0.5s" hx-on::after-request="if(event.detail.successful) toggleContent('float'); window.location.href='/employees';">
|
||||
<div class="row">
|
||||
<input type="text" class="col-5" name="firstName" value="Testy" placeholder="First Name" required>
|
||||
<div class="col-2"></div>
|
||||
<input type="text" class="col-5" name="lastName" value="McTestface" placeholder="Last Name" required>
|
||||
</div>
|
||||
<div class="btn-row">
|
||||
<button type="submit" class="btn-primary">Update</button>
|
||||
<button class="danger" hx-confirm="Are you sure you want to delete this employee?" hx-delete="/employees/00000000-0000-0000-0000-000000000000" hx-target="#employee-00000000-0000-0000-0000-000000000000" hx-swap="outerHTML transition:true swap:1s">Delete</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
<select name="createdForID" class="col-3">
|
||||
<option value="00000000-0000-0000-0000-000000000000">Testy Mctestface</option>
|
||||
</select>
|
||||
@@ -0,0 +1,3 @@
|
||||
<select name="createdForID" class="col-6" style="margin-left: 15px;">
|
||||
<option value="00000000-0000-0000-0000-000000000000">Testy Mctestface</option>
|
||||
</select>
|
||||
@@ -0,0 +1,6 @@
|
||||
<tr id="employee-00000000-0000-0000-0000-000000000000">
|
||||
<td>Testy Mctestface</td>
|
||||
<td>
|
||||
<button class="btn-detail" style="padding-left: 15px;" hx-get="/employees/00000000-0000-0000-0000-000000000000" hx-target="#float" hx-push-url="true" hx-swap="outerHTML transition:true swap:0.5s">〉</button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Purchase Orders</title>
|
||||
<meta charset="UTF-8">
|
||||
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
|
||||
<script src="/js/main.js"></script>
|
||||
<link rel="stylesheet" href="/css/main.css">
|
||||
<link rel="icon" href="/images/favicon.ico" type="image/x-icon">
|
||||
</head>
|
||||
<body>
|
||||
<header class="header">
|
||||
<div id="logo">HHE - Purchase Orders</div>
|
||||
</header>
|
||||
<div class="container" style="padding: 20px 20px;">
|
||||
<h1>Login</h1>
|
||||
<br>
|
||||
<p class="secondary"><i></i></p>
|
||||
<br>
|
||||
</div>
|
||||
<form id="user-form" class="user-form" hx-post="/login" hx-push-url="true" hx-target="body" hx-swap="outerHTML" hx-on::after-request="if(event.detail.successful) this.reset(); toggleContent('float');">
|
||||
<input type="hidden" name="next" value="/purchase-orders">
|
||||
<div class="row">
|
||||
<input type="text" id="username" name="username" placeholder="Username" autofocus required>
|
||||
</div>
|
||||
<div class="row">
|
||||
<input type="password" id="password" name="password" placeholder="Password" required>
|
||||
</div>
|
||||
<div class="row">
|
||||
<button type="submit" class="btn-primary">Login</button>
|
||||
</div>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Purchase Orders</title>
|
||||
<meta charset="UTF-8">
|
||||
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
|
||||
<script src="/js/main.js"></script>
|
||||
<link rel="stylesheet" href="/css/main.css">
|
||||
<link rel="icon" href="/images/favicon.ico" type="image/x-icon">
|
||||
</head>
|
||||
<body>
|
||||
<header class="header">
|
||||
<div id="logo">HHE - Purchase Orders</div>
|
||||
<div class="sidepanel" id="sidepanel">
|
||||
<a href="javascript:void(0)" class="closebtn" onclick="closeSidepanel()">x</a><a hx-get="/purchase-orders?page=1&limit=50" hx-target="body" hx-push-url="true">Purchase Orders</a><a hx-get="/users" hx-target="body" hx-push-url="true">Users</a><a hx-get="/employees" hx-target="body" hx-push-url="true">Employees</a><a hx-get="/vendors" hx-target="body" hx-push-url="true">Vendors</a>
|
||||
<div style="border-bottom: 1px solid grey; margin-bottom: 5px;"></div>
|
||||
Logout<a hx-post="/logout" hx-target="#content" hx-swap="outerHTML" hx-trigger="click"></a>
|
||||
</div>
|
||||
<button class="openbtn" onclick="openSidepanel()">
|
||||
<img src="/images/menu.svg" style="width: 30px;, height: 30px;">
|
||||
</button>
|
||||
</header>
|
||||
<div class="container" style="padding: 20px 20px;">
|
||||
<h1>Users</h1>
|
||||
<br>
|
||||
<p class="secondary"><i>Users are who can login and issue purchase orders for employees.</i></p>
|
||||
<br>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div id="float" class="" style="display: hidden;"></div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Username</th>
|
||||
<th>Email</th>
|
||||
<th style="width: 50px;">
|
||||
<button class="btn btn-add" hx-get="/users/create" hx-target="#float" hx-swap="outerHTML">+</button>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="user-table">
|
||||
<tr id="user-00000000-0000-0000-0000-000000000000">
|
||||
<td>test</td>
|
||||
<td>test@example.com</td>
|
||||
<td>
|
||||
<button class="btn-detail" hx-get="/users/00000000-0000-0000-0000-000000000000" hx-target="#float" hx-swap="outerHTML" hx-push-url="true">〉</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,22 @@
|
||||
<div id="float" class="float" style="display: block;">
|
||||
<div class="btn-row">
|
||||
<button class="btn-close" onclick="toggleContent('float');">x</button>
|
||||
</div>
|
||||
<form id="user-form" class="user-form" hx-post="/users" hx-push-url="false" hx-target="#user-table" hx-swap="afterbegin transition:true swap:0.5s" hx-on::after-request="if(event.detail.successful) this.reset(); toggleContent('float');">
|
||||
<div class="row">
|
||||
<input type="text" id="username" name="username" placeholder="Username" autofocus required>
|
||||
</div>
|
||||
<div class="row">
|
||||
<input type="email" id="email" name="email" placeholder="Email" required>
|
||||
</div>
|
||||
<div class="row">
|
||||
<input type="password" id="password" name="password" placeholder="Password" required>
|
||||
</div>
|
||||
<div class="row">
|
||||
<input type="password" id="confirmPassword" name="confirmPassword" placeholder="Confirm Password" required>
|
||||
</div>
|
||||
<div class="row">
|
||||
<button type="submit" class="btn-primary">Create</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,7 @@
|
||||
<tr id="user-00000000-0000-0000-0000-000000000000">
|
||||
<td>test</td>
|
||||
<td>test@example.com</td>
|
||||
<td>
|
||||
<button class="btn-detail" hx-get="/users/00000000-0000-0000-0000-000000000000" hx-target="#float" hx-swap="outerHTML" hx-push-url="true">〉</button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -0,0 +1,18 @@
|
||||
<div id="float" class="float" style="display: block;">
|
||||
<div class="btn-row">
|
||||
<button class="btn-close" onclick="toggleContent('float'); window.location.href='/users';">x</button>
|
||||
</div>
|
||||
<form hx-post="/users/00000000-0000-0000-0000-000000000000" hx-swap="outerHTML" hx-target="#user-00000000-0000-0000-0000-000000000000" hx-on::after-request="toggleContent('float');">
|
||||
<div class="row">
|
||||
<label for="username" class="col-2"><span class="label">Username:</span></label>
|
||||
<input class="col-4" type="text" id="username" name="username" value="test" required>
|
||||
Email:<label for="email" class="col-2"><span class="label"></span></label>
|
||||
<input class="col-4" type="email" id="email" name="email" value="test@example.com" required>
|
||||
</div>
|
||||
<div class="row"><span class="label col-2">Created:</span><span class="date col-4"></span><span class="label col-2">Updated:</span><span class="date col-4"></span></div>
|
||||
<div class="btn-row user-buttons">
|
||||
<button type="submit" class="btn-secondary">Update</button>
|
||||
<button class="danger" hx-delete="/users/00000000-0000-0000-0000-000000000000" hx-trigger="click" hx-swap="outerHTML" hx-target="#user-00000000-0000-0000-0000-000000000000" hx-confirm="Are you sure you want to delete this user?" hx-on::after-request="toggleContent('float'); window.location.href='/users';">Delete</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,7 @@
|
||||
<tr id="user-00000000-0000-0000-0000-000000000000">
|
||||
<td>test</td>
|
||||
<td>test@example.com</td>
|
||||
<td>
|
||||
<button class="btn-detail" hx-get="/users/00000000-0000-0000-0000-000000000000" hx-target="#float" hx-swap="outerHTML" hx-push-url="true">〉</button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -0,0 +1,8 @@
|
||||
<ul id="branch-list">
|
||||
<li id="branch-00000000-0000-0000-0000-000000000001" class="branch-row">
|
||||
<span class="label">Mock</span>
|
||||
<button class="btn" hx-delete="/vendors/branches/00000000-0000-0000-0000-000000000001" hx-target="#branch-00000000-0000-0000-0000-000000000001" hx-swap="outerHTML transition:true swap:0.5s">
|
||||
<img src="/images/trash-can.svg" width="30" height="30" style="margin-top: 5px;">
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -0,0 +1,3 @@
|
||||
<select name="vendorBranchID" class="col-4" style="margin-left: 15px;">
|
||||
<option value="00000000-0000-0000-0000-000000000001">Test - Mock</option>
|
||||
</select>
|
||||
@@ -0,0 +1,3 @@
|
||||
<select name="vendorBranchID" class="col-4">
|
||||
<option value="00000000-0000-0000-0000-000000000001">Test - Mock</option>
|
||||
</select>
|
||||
@@ -0,0 +1,6 @@
|
||||
<li id="branch-00000000-0000-0000-0000-000000000001" class="branch-row">
|
||||
<span class="label">Mock</span>
|
||||
<button class="btn" hx-delete="/vendors/branches/00000000-0000-0000-0000-000000000001" hx-target="#branch-00000000-0000-0000-0000-000000000001" hx-swap="outerHTML transition:true swap:0.5s">
|
||||
<img src="/images/trash-can.svg" width="30" height="30" style="margin-top: 5px;">
|
||||
</button>
|
||||
</li>
|
||||
@@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Purchase Orders</title>
|
||||
<meta charset="UTF-8">
|
||||
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
|
||||
<script src="/js/main.js"></script>
|
||||
<link rel="stylesheet" href="/css/main.css">
|
||||
<link rel="icon" href="/images/favicon.ico" type="image/x-icon">
|
||||
</head>
|
||||
<body>
|
||||
<header class="header">
|
||||
<div id="logo">HHE - Purchase Orders</div>
|
||||
<div class="sidepanel" id="sidepanel">
|
||||
<a href="javascript:void(0)" class="closebtn" onclick="closeSidepanel()">x</a><a hx-get="/purchase-orders?page=1&limit=50" hx-target="body" hx-push-url="true">Purchase Orders</a><a hx-get="/users" hx-target="body" hx-push-url="true">Users</a><a hx-get="/employees" hx-target="body" hx-push-url="true">Employees</a><a hx-get="/vendors" hx-target="body" hx-push-url="true">Vendors</a>
|
||||
<div style="border-bottom: 1px solid grey; margin-bottom: 5px;"></div>
|
||||
Logout<a hx-post="/logout" hx-target="#content" hx-swap="outerHTML" hx-trigger="click"></a>
|
||||
</div>
|
||||
<button class="openbtn" onclick="openSidepanel()">
|
||||
<img src="/images/menu.svg" style="width: 30px;, height: 30px;">
|
||||
</button>
|
||||
</header>
|
||||
<div class="container" style="padding: 20px 20px;">
|
||||
<h1>Vendors</h1>
|
||||
<br>
|
||||
<p class="secondary"><i>Vendors are where purchase orders can be issued to.</i></p>
|
||||
<br>
|
||||
</div>
|
||||
<div class="container" id="content">
|
||||
<div id="float" class="" style="display: hidden;"></div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Branches</th>
|
||||
<th style="width: 100px;">
|
||||
<button class="btn btn-add" style="padding: 0px 10px;" hx-get="/vendors/create" hx-target="#float" hx-swap="outerHTML">+</button>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="vendor-table">
|
||||
<tr id="vendor-00000000-0000-0000-0000-000000000000">
|
||||
<td>Test</td>
|
||||
<td>(0) Branches</td>
|
||||
<td>
|
||||
<button class="btn-detail" style="padding-left: 15px;" hx-get="/vendors/00000000-0000-0000-0000-000000000000" hx-target="#float" hx-push-url="true" hx-swap="outerHTML">〉</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,11 @@
|
||||
<div id="float" class="float" style="display: block;">
|
||||
<div class="btn-row">
|
||||
<button class="btn-close" onclick="toggleContent('float');">x</button>
|
||||
</div>
|
||||
<form id="vendor-form" hx-post="/vendors" hx-target="#content" hx-swap="outerHTML">
|
||||
<div class="row">
|
||||
<input type="text" class="col-9" id="vendor-name" name="name" value="" placeholder="Vendor Name" hx-post="/vendors" hx-trigger="keyup changed delay:500ms" required>
|
||||
<button type="submit" class="btn-primary" style="float: right">Create</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,43 @@
|
||||
<div class="container" id="content">
|
||||
<div id="float" class="float" style="display: block;">
|
||||
<div class="btn-row">
|
||||
<button class="btn-close" onclick="toggleContent('float');" hx-get="/vendors" hx-push-url="true" hx-target="body" hx-swap="outerHTML">x</button>
|
||||
</div>
|
||||
<form id="vendor-form" hx-put="/vendors/00000000-0000-0000-0000-000000000000" hx-target="#content" hx-swap="outerHTML">
|
||||
<div class="row">
|
||||
<input type="text" class="col-9" id="vendor-name" name="name" value="Test" placeholder="Vendor Name" hx-put="/vendors/00000000-0000-0000-0000-000000000000" hx-trigger="keyup changed delay:500ms" required>
|
||||
<button class="danger" style="font-size: 1.25em; padding: 10px 20px; border-radius: 10px;" hx-delete="/vendors/00000000-0000-0000-0000-000000000000" hx-confirm="Are you sure you want to delete this vendor?" hx-target="#vendor-00000000-0000-0000-0000-000000000000" hx-swap="outerHTML transition:true swap:1s" hx-on::after-request="if(event.detail.successful) toggleContent('float'); window.location.href='/vendors';">Delete</button>
|
||||
<button type="submit" class="btn-primary" style="float: right">Update</button>
|
||||
</div>
|
||||
</form>
|
||||
<h2 style="margin-left: 20px; font-size: 1.5em;" class="label">Branches</h2>
|
||||
<form id="branch-form" hx-post="/vendors/branches" hx-target="#branch-list" hx-swap="beforeend" hx-on::after-request="if(event.detail.successful) this.reset();">
|
||||
<input type="hidden" name="vendorID" value="00000000-0000-0000-0000-000000000000">
|
||||
<input type="text" class="col-9" name="name" placeholder="Add branch..." required hx-trigger="keyup changed delay:800ms">
|
||||
<button type="submit" class="btn-secondary" style="float: right; padding: 10px 50px;" hx-target="#branch-list" hx-swap="beforeend">+</button>
|
||||
</form>
|
||||
<div hx-get="/vendors/branches?vendorID=00000000-0000-0000-0000-000000000000" hx-target="this" hx-indicator=".hx-indicator" hx-trigger="revealed">
|
||||
<img src="/images/spinner.svg" width="30" height="30" class="hx-indicator">
|
||||
</div>
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Branches</th>
|
||||
<th style="width: 100px;">
|
||||
<button class="btn btn-add" style="padding: 0px 10px;" hx-get="/vendors/create" hx-target="#float" hx-swap="outerHTML">+</button>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="vendor-table">
|
||||
<tr id="vendor-00000000-0000-0000-0000-000000000000">
|
||||
<td>Test</td>
|
||||
<td>(0) Branches</td>
|
||||
<td>
|
||||
<button class="btn-detail" style="padding-left: 15px;" hx-get="/vendors/00000000-0000-0000-0000-000000000000" hx-target="#float" hx-push-url="true" hx-swap="outerHTML">〉</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -0,0 +1,21 @@
|
||||
<div id="float" class="float" style="display: block;">
|
||||
<div class="btn-row">
|
||||
<button class="btn-close" onclick="toggleContent('float');" hx-get="/vendors" hx-push-url="true" hx-target="body" hx-swap="outerHTML">x</button>
|
||||
</div>
|
||||
<form id="vendor-form" hx-put="/vendors/00000000-0000-0000-0000-000000000000" hx-target="#content" hx-swap="outerHTML">
|
||||
<div class="row">
|
||||
<input type="text" class="col-9" id="vendor-name" name="name" value="Test" placeholder="Vendor Name" hx-put="/vendors/00000000-0000-0000-0000-000000000000" hx-trigger="keyup changed delay:500ms" required>
|
||||
<button class="danger" style="font-size: 1.25em; padding: 10px 20px; border-radius: 10px;" hx-delete="/vendors/00000000-0000-0000-0000-000000000000" hx-confirm="Are you sure you want to delete this vendor?" hx-target="#vendor-00000000-0000-0000-0000-000000000000" hx-swap="outerHTML transition:true swap:1s" hx-on::after-request="if(event.detail.successful) toggleContent('float'); window.location.href='/vendors';">Delete</button>
|
||||
<button type="submit" class="btn-primary" style="float: right">Update</button>
|
||||
</div>
|
||||
</form>
|
||||
<h2 style="margin-left: 20px; font-size: 1.5em;" class="label">Branches</h2>
|
||||
<form id="branch-form" hx-post="/vendors/branches" hx-target="#branch-list" hx-swap="beforeend" hx-on::after-request="if(event.detail.successful) this.reset();">
|
||||
<input type="hidden" name="vendorID" value="00000000-0000-0000-0000-000000000000">
|
||||
<input type="text" class="col-9" name="name" placeholder="Add branch..." required hx-trigger="keyup changed delay:800ms">
|
||||
<button type="submit" class="btn-secondary" style="float: right; padding: 10px 50px;" hx-target="#branch-list" hx-swap="beforeend">+</button>
|
||||
</form>
|
||||
<div hx-get="/vendors/branches?vendorID=00000000-0000-0000-0000-000000000000" hx-target="this" hx-indicator=".hx-indicator" hx-trigger="revealed">
|
||||
<img src="/images/spinner.svg" width="30" height="30" class="hx-indicator">
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,21 @@
|
||||
<div id="float" class="float" style="display: block;">
|
||||
<div class="btn-row">
|
||||
<button class="btn-close" onclick="toggleContent('float');" hx-get="/vendors" hx-push-url="true" hx-target="body" hx-swap="outerHTML">x</button>
|
||||
</div>
|
||||
<form id="vendor-form" hx-put="/vendors/00000000-0000-0000-0000-000000000000" hx-target="#content" hx-swap="outerHTML">
|
||||
<div class="row">
|
||||
<input type="text" class="col-9" id="vendor-name" name="name" value="Test" placeholder="Vendor Name" hx-put="/vendors/00000000-0000-0000-0000-000000000000" hx-trigger="keyup changed delay:500ms" required>
|
||||
<button class="danger" style="font-size: 1.25em; padding: 10px 20px; border-radius: 10px;" hx-delete="/vendors/00000000-0000-0000-0000-000000000000" hx-confirm="Are you sure you want to delete this vendor?" hx-target="#vendor-00000000-0000-0000-0000-000000000000" hx-swap="outerHTML transition:true swap:1s" hx-on::after-request="if(event.detail.successful) toggleContent('float'); window.location.href='/vendors';">Delete</button>
|
||||
<button type="submit" class="btn-primary" style="float: right">Update</button>
|
||||
</div>
|
||||
</form>
|
||||
<h2 style="margin-left: 20px; font-size: 1.5em;" class="label">Branches</h2>
|
||||
<form id="branch-form" hx-post="/vendors/branches" hx-target="#branch-list" hx-swap="beforeend" hx-on::after-request="if(event.detail.successful) this.reset();">
|
||||
<input type="hidden" name="vendorID" value="00000000-0000-0000-0000-000000000000">
|
||||
<input type="text" class="col-9" name="name" placeholder="Add branch..." required hx-trigger="keyup changed delay:800ms">
|
||||
<button type="submit" class="btn-secondary" style="float: right; padding: 10px 50px;" hx-target="#branch-list" hx-swap="beforeend">+</button>
|
||||
</form>
|
||||
<div hx-get="/vendors/branches?vendorID=00000000-0000-0000-0000-000000000000" hx-target="this" hx-indicator=".hx-indicator" hx-trigger="revealed">
|
||||
<img src="/images/spinner.svg" width="30" height="30" class="hx-indicator">
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user