Compare commits

...

3 Commits

Author SHA1 Message Date
Wes Holland cb5616c81f Cleanup audit page somewhat
11 months ago
Wes Holland d219c136c0 Prettier
11 months ago
Wes Holland d3364af395 Changing CSS framework from Bootstrap to Tailwindcss
11 months ago

4
.gitignore vendored

@ -1,5 +1,9 @@
/target
/resources
/.env
/*.db
/*.db-shm
/*.db-wal
/node_modules
/*.lockb

@ -13,5 +13,8 @@
</option>
<option name="myCustomValuesEnabled" value="true" />
</inspection_tool>
<inspection_tool class="RsUnusedImport" enabled="true" level="WARNING" enabled_by_default="true">
<option name="enableOnlyIfProcMacrosEnabled" value="false" />
</inspection_tool>
</profile>
</component>

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<file url="file://$PROJECT_DIR$" libraries="{alpinejs}" />
</component>
</project>

@ -0,0 +1,3 @@
{
"plugins": ["prettier-plugin-tailwindcss"]
}

@ -0,0 +1,21 @@
# inventory-app
## Prerequisites
1. Rust & Cargo
1. [bun.sh](https://bun.sh/)
## Setup
To install dependencies:
```bash
bun install
```
To run:
```bash
bun run index.ts
```

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

@ -0,0 +1,42 @@
use std::process::Command;
fn main() {
println!("cargo:rerun-if-changed=templates");
println!("cargo:rerun-if-changed=assets");
println!("cargo:rerun-if-changed=tailwind.config.js");
std::fs::remove_dir_all("resources").unwrap_or_default();
Command::new("bun")
.args([
"run",
"tailwindcss",
"-c",
"tailwind.config.js",
"-i",
"assets/public/css/tailwind.css",
"-o",
"resources/main.css",
"--minify",
])
.status()
.expect("failed to run tailwindcss");
copy_files("assets/static");
}
fn copy_files(dir: &str) {
for entry in std::fs::read_dir(dir).expect("failed to read dir `public`") {
let entry = entry.expect("failed to read entry");
if entry.file_type().unwrap().is_dir() {
copy_files(entry.path().to_str().unwrap());
} else {
let path = entry.path();
let filename = path.file_name().unwrap().to_str().unwrap();
let dest = format!("resources/{}", filename);
std::fs::copy(path, dest).expect("failed to copy file");
}
}
}

@ -0,0 +1,11 @@
{
"name": "inventory-app",
"dependencies": {},
"devDependencies": {
"@types/bun": "latest",
"prettier": "^3.4.2",
"prettier-plugin-tailwindcss": "^0.6.11",
"tailwindcss": "^3.4.17"
},
"type": "module"
}

@ -4,8 +4,7 @@ use crate::app::routes::AppState;
pub fn routes() -> Router<AppState> {
Router::new()
.nest_service("/css/pico.min.css", ServeFile::new("static/css/pico.min.css"))
.nest_service("/css/custom.css", ServeFile::new("static/css/custom.css"))
.nest_service("/js/htmx.min.js", ServeFile::new("static/js/htmx.min.js"))
.nest_service("/css/main.css", ServeFile::new("resources/main.css"))
.nest_service("/js/htmx.min.js", ServeFile::new("resources/htmx.min.js"))
.nest_service("/favicon.ico", ServeFile::new("static/favicon.ico"))
}

File diff suppressed because one or more lines are too long

@ -0,0 +1,19 @@
export const content = ["./templates/**/*.html"]
export const theme = {
fontFamily: {
sans: ['Graphik', 'sans-serif'],
serif: ['Merriweather', 'serif'],
},
extend: {
colors: {
'space-cadet':'#1f2041ff',
'english-violet':'#4b3f72ff',
'sunglow':'#ffc857ff',
'dark-cyan':'#119da4ff',
'paynes-gray':'#19647eff',
'cerise': '#da4167ff',
'orchid-pink': '#f0bcd4ff'
}
},
plugins: [],
}

@ -1,10 +1,6 @@
{% extends "problem.html" %}
{% block content %}
{% extends "problem.html" %} {% block content %}
<h1>Error</h1>
<p>
Oops, something went wrong. Press the back button to try again.
</p>
<p>Oops, something went wrong. Press the back button to try again.</p>
{% endblock %}
{% endblock %}

@ -1,27 +1,50 @@
{% extends "main.html" %}
{% extends "main.html" %} {% block title %} Items {% endblock %} {% block
content %}
{% block title %} Items {% endblock %}
{% block content %}
<div class="container">
<p class="container d-flex justify-content-center">
<input id="search" class="form-control"
type="search" name="q"
placeholder="Filter"
aria-label="Search"
value='{{ query.search.as_deref().unwrap_or("") }}'
hx-get="/items"
hx-trigger="search, keyup delay:500ms changed"
hx-target="#items"
hx-push-url="true"
<div class="mx-auto mb-4 px-4">
<label
for="item-filter"
class="sr-only mb-2 text-sm font-medium text-gray-900 dark:text-white"
>Search</label
>
<div class="relative mb-4 max-w-56 content-center">
<div
class="pointer-events-none absolute inset-y-0 start-0 flex items-center ps-3"
>
<svg
class="h-4 w-4 text-gray-500 dark:text-gray-400"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 20 20"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"
/>
</p>
<div id="items" class="container">
{% include "catalog_item_fragment.html" %}
</svg>
</div>
<input
id="item-filter"
class="block w-full rounded-lg border border-gray-300 bg-slate-100 p-4 ps-10 text-sm text-neutral-900 focus:border-paynes-gray focus:ring-paynes-gray dark:bg-neutral-900 dark:text-slate-100"
type="search"
name="q"
placeholder="Filter"
aria-label="Filter"
value='{{ query.search.as_deref().unwrap_or("") }}'
hx-get="/items"
hx-trigger="search, keyup delay:500ms changed"
hx-target="#items"
hx-push-url="true"
/>
</div>
<div id="items" class="container">
{% include "catalog_item_fragment.html" %}
</div>
</div>
{% endblock %}
{% endblock %}

@ -1,11 +1,17 @@
<div class="card">
<ul class="list-group list-group-flush">
<div class="mx-auto">
<div class="flex flex-col">
{% for item in items %}
<li id="item-{{item.id}}-entry" class="list-group-item">
<div class="row">
<div class="col-6"><a href="/item/{{item.id}}/" hx-push-url="true">{{ item.name }}</a></div>
<div class="mx-1 mb-4 flex flex-row">
<div class="basis-1/2">
<a
class="font-medium text-paynes-gray hover:underline dark:text-paynes-gray"
href="/item/{{item.id}}/"
hx-push-url="true"
>
{{ item.name }}
</a>
</div>
</li>
</div>
{% endfor %}
</ul>
</div>
</div>

@ -1,11 +1,10 @@
{% extends "problem.html" %}
{% block content %}
{% extends "problem.html" %} {% block content %}
<h1>Forbidden</h1>
<p>
You are forbidden from accessing this resource. Please contact your supervisor or <a href="/auth/logout">logout</a>
and log back in with a different user.
You are forbidden from accessing this resource. Please contact your supervisor
or <a href="/auth/logout">logout</a>
and log back in with a different user.
</p>
{% endblock %}
{% endblock %}

@ -1,38 +1,64 @@
{% extends "main.html" %}
{% extends "main.html" %} {% block title %} Audit Log {% endblock %} {% block
content %}
{% block title %} Audit Log {% endblock %}
<h1 class="mb-4 text-4xl font-extrabold uppercase">Audit Log (Coming soon)</h1>
{% block content %}
<h1>Audit Log (Coming soon)</h1>
<section class="container mb-4">
<form action="/history" hx-get="/history" hx-trigger="change" hx-target="#items">
<div class="row">
<div class="col-sm-2">
<label for="start-date" class="form-label">Start Date</label>
<input type="date" id="start-date" name="start-date" value="{{ start_date }}" class="form-control" />
</div>
<div class="col-sm-2">
<label for="start-time" class="form-label">Start Time</label>
<input type="time" id="start-time" name="start-time" value="{{ start_time }}" class="form-control"/>
<small class="form-text">Timezone {{ time_zone }}</small>
</div>
<div class="col-sm-2">
<label for="end-date" class="form-label">End Date</label>
<input type="date" id="end-date" name="end-date" value="{{ end_date }}" class="form-control"/>
</div>
<div class="col-sm-2">
<label for="end-time" class="form-label">End Time</label>
<input type="time" id="end-time" name="end-time" value="{{ end_time }}" class="form-control"/>
<small class="form-text">Timezone {{ time_zone }}</small>
</div>
</div>
</form>
<section class="mb-4">
<form
action="/history"
hx-get="/history"
hx-trigger="change"
hx-target="#items"
>
<div class="flex between">
<div class="px-2">
<label for="start-date" class="block">Start Date</label>
<input
type="date"
id="start-date"
name="start-date"
value="{{ start_date }}"
class="block"
/>
</div>
<div class="px-2">
<label for="start-time" class="block">Start Time</label>
<input
type="time"
id="start-time"
name="start-time"
value="{{ start_time }}"
class="block"
/>
<small class="block text-sm">Timezone {{ time_zone }}</small>
</div>
<div class="px-2">
<label for="end-date" class="block">End Date</label>
<input
type="date"
id="end-date"
name="end-date"
value="{{ end_date }}"
class="block"
/>
</div>
<div class="px-2">
<label for="end-time" class="form-label">End Time</label>
<input
type="time"
id="end-time"
name="end-time"
value="{{ end_time }}"
class="block"
/>
<small class="text-sm block">Timezone {{ time_zone }}</small>
</div>
</div>
</form>
</section>
<section id="items" class="container">
{% include "history_item_fragment.html" %}
<section id="items" class="mb-4">
{% include "history_item_fragment.html" %}
</section>
{% endblock %}
{% endblock %}

@ -1,22 +1,29 @@
<div class="card">
<ul class="list-group list-group-flush">
{% for item in items %}
<li class="list-group-item">
<div class="row">
<p class="col">{{ item.create_date }}</p>
<p class="col">{{ item.item_name }}</p>
<p class="col">{{ item.user_name }}</p>
<p class="col">
{% match item.item_type %}
{% when HistoryItemEntry::PositiveAdjustment with (entry) %}
{{ entry.amount }} @ {{ entry.unit_value }}
{% when HistoryItemEntry::NegativeAdjustment with (entry) %}
{{ entry.amount }} {{ entry.reason }}
{% endmatch %}
</p>
</div>
</li>
<table class="table-auto">
<thead>
<tr class="mb-2">
<th>Date</th>
<th>Item</th>
<th>User</th>
<th>Desc</th>
</tr>
</thead>
<tbody>
{% for item in items %}
<tr class="mb-2">
<td class="px-2">{{ item.create_date }}</td>
<td class="px-2">{{ item.item_name }}</td>
<td class="px-2">{{ item.user_name }}</td>
<td class="px-2">
{% match item.item_type %}
{% when HistoryItemEntry::PositiveAdjustment with (entry) %}
{{ entry.amount }} @ {{ entry.unit_value }}
{% when HistoryItemEntry::NegativeAdjustment with (entry) %}
{{ entry.amount }} {{ entry.reason }}
{% endmatch %}
</td>
</tr>
{% endfor %}
</ul>
</div>
</tbody>
</table>

@ -1,27 +1,50 @@
{% extends "main.html" %}
{% extends "main.html" %} {% block title %} Home {% endblock %} {% block content
%}
{% block title %} Home {% endblock %}
{% block content %}
<div class="container">
<p class="container d-flex justify-content-center">
<input id="search"
class="form-control"
type="search" name="q"
placeholder="Search"
aria-label="Search"
value='{{ query.search.as_deref().unwrap_or("") }}'
hx-get="/home/search"
hx-trigger="search, keyup delay:500ms changed"
hx-target="#items"
hx-push-url="true"
<div class="mx-auto mb-4 px-4">
<label
for="default-search"
class="sr-only mb-2 text-sm font-medium text-gray-900 dark:text-white"
>Search</label
>
<div class="relative mb-4 max-w-56 content-center">
<div
class="pointer-events-none absolute inset-y-0 start-0 flex items-center ps-3"
>
<svg
class="h-4 w-4 text-gray-500 dark:text-gray-400"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 20 20"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"
/>
</p>
<div id="items" class="container">
{% include "home_search_item_fragment.html" %}
</svg>
</div>
<input
id="default-search"
class="block w-full rounded-lg border border-gray-300 bg-slate-100 p-4 ps-10 text-sm text-neutral-900 focus:border-paynes-gray focus:ring-paynes-gray dark:bg-neutral-900 dark:text-slate-100"
type="search"
name="q"
placeholder="Search"
aria-label="Search"
value='{{ query.search.as_deref().unwrap_or("") }}'
hx-get="/home/search"
hx-trigger="search, keyup delay:500ms changed"
hx-target="#items"
hx-push-url="true"
/>
</div>
<div id="items" class="container">
{% include "home_search_item_fragment.html" %}
</div>
</div>
{% endblock %}
{% endblock %}

@ -1,16 +1,29 @@
{% if items.len() > 0 %}
<div class="card">
<ul class="list-group list-group-flush">
{% for item in items %}
<li id="item-{{item.id}}-entry" class="list-group-item">
<div class="row">
<div class="col-6"><a href="/item/{{item.id}}/" hx-push-url="true">{{ item.name }}</a></div>
<div class="col">Count: <span id="item-{{item.id}}-count" hx-get="/item/{{item.id}}/count" hx-trigger="load">0</span></div>
<div class="col">Reorder Point: {{ item.reorder_point }}</div>
<div class="mx-auto">
<div class="flex flex-col">
{% for item in items %}
<div class="mx-1 mb-4 flex flex-row">
<div class="basis-1/2">
<a
class="font-medium text-paynes-gray hover:underline dark:text-paynes-gray"
href="/item/{{item.id}}/"
hx-push-url="true"
>
{{ item.name }}
</a>
</div>
<div class="basis-1/4">
Count:
<span
id="item-{{item.id}}-count"
hx-get="/item/{{item.id}}/count"
hx-trigger="load"
>0</span
>
</div>
<div class="basis-1/4">Reorder Point: {{ item.reorder_point }}</div>
</div>
</li>
{% endfor %}
</ul>
{% endfor %}
</div>
</div>
{% endif %}

@ -1,25 +1,31 @@
<table class="table" hx-get="/item/{{item_id}}/adjustments" hx-trigger="new-adjustment from:body" hx-swap="outerHTML">
<thead>
<table
class="w-full text-left text-sm rtl:text-right"
hx-get="/item/{{item_id}}/adjustments"
hx-trigger="new-adjustment from:body"
hx-swap="outerHTML"
>
<thead
class="bg-gray-50 text-xs uppercase text-gray-700 dark:bg-gray-700 dark:text-gray-400"
>
<tr>
<th scope="col">Date</th>
<th scope="col">Amount</th>
<th scope="col">Reason</th>
<th scope="col">Unit Value</th>
<th scope="col">Running Total</th>
<th scope="col">Running Value</th>
<th class="px-6 py-3" scope="col">Date</th>
<th class="px-6 py-3" scope="col">Amount</th>
<th class="px-6 py-3" scope="col">Reason</th>
<th class="px-6 py-3" scope="col">Unit Value</th>
<th class="px-6 py-3" scope="col">Running Total</th>
<th class="px-6 py-3" scope="col">Running Value</th>
</tr>
</thead>
<tbody>
</thead>
<tbody>
{% for item in adjustments %}
<tr>
<td>{{ item.date }}</td>
<td>{{ item.amount }}</td>
<td>{{ item.reason }}</td>
<td>{{ item.unit_value }}</td>
<td>{{ item.tally }}</td>
<td>{{ item.tally_value }}</td>
<td class="px-6 py-4">{{ item.date }}</td>
<td class="px-6 py-4">{{ item.amount }}</td>
<td class="px-6 py-4">{{ item.reason }}</td>
<td class="px-6 py-4">{{ item.unit_value }}</td>
<td class="px-6 py-4">{{ item.tally }}</td>
<td class="px-6 py-4">{{ item.tally_value }}</td>
</tr>
{% endfor %}
</tbody>
</tbody>
</table>

@ -1,65 +1,119 @@
<form hx-post="/item/{{item_id}}/adjustment/negative"
hx-target="this" hx-swap="outerHTML"
x-ref="formNegativeAdjustment"
x-data="{ reason: 'Sale' }">
<input id="reason" name="reason" type="hidden" x-model="reason"/>
<div class="row">
<div class="col">
<!--<label for="amount" class="form-label">Amount</label>-->
<input type="number"
id="amount"
name="amount"
step="0.01"
class="form-control"
placeholder="Amount"
aria-label="amount"
required
{% if !amount_error.is_empty() -%}
aria-invalid="true"
aria-describedby="invalid-amount"
{% endif -%}
/>
{% if !amount_error.is_empty() -%}
<small id="invalid-amount" class="invalid-feedback">{{ amount_error }}</small>
{% endif -%}
</div>
<div class="col">
<button class="btn btn-primary" x-text="reason">Sale</button>
</div>
<div class="col">
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown"
aria-expanded="false">
<span class="visually-hidden">Other</span>
</button>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item"
@click="reason = 'Sale'">
Sale
</a>
</li>
<li>
<a class="dropdown-item"
@click="reason = 'Destruction'">
Destruction
</a>
</li>
<li>
<a class="dropdown-item"
@click="reason = 'Expiration'">
Expiration
</a>
</li>
<li>
<a class="dropdown-item"
@click="reason = 'Theft'">
Theft
</a>
</li>
</ul>
</div>
</div>
<form
hx-post="/item/{{item_id}}/adjustment/negative"
hx-target="this"
hx-swap="outerHTML"
x-ref="formNegativeAdjustment"
x-data="{ reason: 'Sale', reason_dropdown_show: false }"
>
<div class="mb-5">
<label for="amount" class="mb-2 block text-sm font-medium">Amount</label>
<input
type="number"
id="amount"
name="amount"
step="0.01"
class="block max-w-56 rounded-lg border border-neutral-900 p-2.5 text-sm text-neutral-900 focus:border-paynes-gray focus:ring-paynes-gray dark:text-slate-100"
placeholder="Amount"
aria-label="amount"
required
{%
if
!amount_error.is_empty()
-%}
aria-invalid="true"
aria-describedby="invalid-amount"
{%
endif
-%}
/>
{% if !amount_error.is_empty() -%}
<small id="invalid-amount" class="mt-2 text-sm text-cerise">
{{ amount_error }}
</small>
{% endif -%}
</div>
<div class="mb-5 flex justify-start">
<button
class="rounded-l-lg bg-english-violet px-5 py-2.5 text-sm font-medium text-slate-100 hover:bg-dark-cyan focus:outline-none focus:ring-4 focus:ring-dark-cyan"
x-text="reason"
>
Sale
</button>
<div
x-data="{ isOpen: false, openedWithKeyboard: false }"
class="relative w-fit"
x-on:keydown.esc.window="isOpen = false; openedWithKeyboard = false"
>
<button
type="button"
x-on:click="isOpen = ! isOpen"
class="rounded-radius inline-flex items-center whitespace-nowrap rounded-r-lg border-l-2 border-slate-100 bg-english-violet px-3.5 py-3.5 text-sm font-medium tracking-wide text-slate-100 transition hover:bg-dark-cyan focus:outline-none focus:ring-4 focus:ring-dark-cyan dark:border-neutral-900"
aria-haspopup="true"
x-on:keydown.space.prevent="openedWithKeyboard = true"
x-on:keydown.enter.prevent="openedWithKeyboard = true"
x-on:keydown.down.prevent="openedWithKeyboard = true"
x-bind:class="isOpen || openedWithKeyboard ? 'text-on-surface-strong dark:text-on-surface-dark-strong' : 'text-on-surface dark:text-on-surface-dark'"
x-bind:aria-expanded="isOpen || openedWithKeyboard"
>
<span class="sr-only">Reason Options</span>
<svg
aria-hidden="true"
fill="none"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
class="size-4 rotate-0"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M19.5 8.25l-7.5 7.5-7.5-7.5"
/>
</svg>
</button>
<!-- Dropdown Menu -->
<div
x-cloak
x-show="isOpen || openedWithKeyboard"
x-transition
x-trap="openedWithKeyboard"
x-on:click.outside="isOpen = false, openedWithKeyboard = false"
x-on:keydown.down.prevent="$focus.wrap().next()"
x-on:keydown.up.prevent="$focus.wrap().previous()"
class="rounded-radius border-outline absolute left-0 top-11 flex w-fit min-w-48 flex-col overflow-hidden border border-neutral-900 bg-slate-100 dark:border-slate-100 dark:bg-neutral-900"
role="menu"
>
<a
class="focus-visible:outline-hidden px-4 py-2 text-sm hover:bg-dark-cyan hover:font-semibold focus-visible:bg-dark-cyan focus-visible:font-semibold"
role="menuitem"
@click="reason = 'Sale'; isOpen = false"
>
Sale
</a>
<a
class="focus-visible:outline-hidden px-4 py-2 text-sm hover:bg-dark-cyan hover:font-semibold focus-visible:bg-dark-cyan focus-visible:font-semibold"
role="menuitem"
@click="reason = 'Destruction'; isOpen = false"
>
Destruction
</a>
<a
class="focus-visible:outline-hidden px-4 py-2 text-sm hover:bg-dark-cyan hover:font-semibold focus-visible:bg-dark-cyan focus-visible:font-semibold"
role="menuitem"
@click="reason = 'Expiration'; isOpen = false"
>
Expiration
</a>
<a
class="focus-visible:outline-hidden px-4 py-2 text-sm hover:bg-dark-cyan hover:font-semibold focus-visible:bg-dark-cyan focus-visible:font-semibold"
role="menuitem"
@click="reason = 'Theft'; isOpen = false"
>
Theft
</a>
</div>
</div>
</div>
<input id="reason" name="reason" type="hidden" x-model="reason" />
</form>

@ -1,43 +1,66 @@
<form hx-post="/item/{{item_id}}/adjustment/positive" hx-target="this" hx-swap="outerHTML" >
<div class="row">
<div class="col">
<input type="number"
id="amount"
name="amount"
step="0.01"
class="form-control"
placeholder="Amount"
aria-label="amount"
required
{% if !amount_error.is_empty() -%}
aria-invalid="true"
aria-describedby="invalid-amount"
{% endif -%}
/>
{% if !amount_error.is_empty() -%}
<small id="invalid-amount" class="invalid-feedback">{{ amount_error }}</small>
{% endif -%}
</div>
<div class="col">
<input type="text"
id="price"
name="price"
placeholder="Price"
class="form-control"
aria-label="price"
required
{% if !price_error.is_empty() -%}
aria-invalid="true"
aria-describedby="invalid-price"
{% endif -%}
/>
{% if !price_error.is_empty() -%}
<small id="invalid-price" class="invalid-feedback">{{ price_error }}</small>
{% endif -%}
</div>
<div class="col">
<button class="btn btn-primary">Add</button>
</div>
</div>
<form
hx-post="/item/{{item_id}}/adjustment/positive"
hx-target="this"
hx-swap="outerHTML"
>
<div class="mb-5">
<label for="amount" class="mb-2 block text-sm font-medium">Amount</label>
<input
type="number"
id="amount"
name="amount"
class="block max-w-56 rounded-lg border border-neutral-900 p-2.5 text-sm text-neutral-900 focus:border-paynes-gray focus:ring-paynes-gray dark:text-slate-100"
step="0.01"
placeholder="Amount"
aria-label="amount"
required
{%
if
!amount_error.is_empty()
-%}
aria-invalid="true"
aria-describedby="invalid-amount"
{%
endif
-%}
/>
{% if !amount_error.is_empty() -%}
<small id="invalid-amount" class="mt-2 text-sm text-cerise">
{{ amount_error }}
</small>
{% endif -%}
</div>
<div class="mb-5">
<label for="price" class="mb-2 block text-sm font-medium">Price</label>
<input
type="text"
id="price"
name="price"
class="block max-w-56 rounded-lg border border-neutral-900 p-2.5 text-sm text-neutral-900 focus:border-paynes-gray focus:ring-paynes-gray dark:text-slate-100"
placeholder="Price"
aria-label="price"
required
{%
if
!price_error.is_empty()
-%}
aria-invalid="true"
aria-describedby="invalid-price"
{%
endif
-%}
/>
{% if !price_error.is_empty() -%}
<small id="invalid-price" class="mt-2 text-sm text-cerise"
>{{ price_error }}</small
>
{% endif -%}
</div>
<div class="mb-5">
<button
class="mb-2 me-2 rounded-lg bg-english-violet px-5 py-2.5 text-sm font-medium text-slate-100 hover:bg-dark-cyan focus:outline-none focus:ring-4 focus:ring-dark-cyan"
>
Add
</button>
</div>
</form>

@ -1,53 +1,66 @@
{% extends "main.html" %}
{% extends "main.html" %} {% block title %} Items {% endblock %} {% block
content %}
{% block title %} Items {% endblock %}
<h2 class="mb-4 flex items-center text-4xl font-extrabold">
{{item.name}} {% if !item.active %}
<span
class="me-2 ms-2 rounded border border-cerise bg-orchid-pink px-2.5 py-0.5 text-2xl font-semibold text-cerise"
>
Inactive
</span>
{% endif %}
</h2>
{% block content %}
<div class="container mb-5">
<h2>{{item.name}} {% if !item.active %}<span class="badge text-bg-danger">Inactive</span> {% endif %}</h2>
</div>
<section class="container">
<div hx-get="/item/{{item_id}}/stats" hx-trigger="load" hx-swap="outerHTML">
</div>
</section>
<section
hx-get="/item/{{item_id}}/stats"
hx-trigger="load"
hx-swap="outerHTML"
></section>
{% if item.active %}
<section class="container-fluid">
<section
class="mx-auto mb-5"
x-data="{ negative_form_open: false, positive_form_open: false }"
>
<div class="flex justify-evenly">
<button
class="mb-2 me-2 rounded-lg bg-paynes-gray px-5 py-2.5 text-sm font-medium text-slate-100 hover:bg-dark-cyan focus:outline-none focus:ring-4 focus:ring-dark-cyan"
@click="negative_form_open = ! negative_form_open"
>
Minus
</button>
<button
class="mb-2 me-2 rounded-lg bg-paynes-gray px-5 py-2.5 text-sm font-medium text-slate-100 hover:bg-dark-cyan focus:outline-none focus:ring-4 focus:ring-dark-cyan"
@click="positive_form_open = ! positive_form_open"
>
Plus
</button>
</div>
<div x-data="{ open: false }" class="mb-3">
<div class="d-flex justify-content-start">
<div class="me-3">
<button class="btn btn-primary display-6" @click="open = ! open">
Minus
</button>
</div>
<div x-show="open" @click.outside="open = false">
<div hx-get="/item/{{item_id}}/adjustment/negative" hx-trigger="load" hx-swap="outerHTML"></div>
</div>
</div>
</div>
<div x-show="negative_form_open" @click.outside="negative_form_open = false">
<div
hx-get="/item/{{item_id}}/adjustment/negative"
hx-trigger="load"
hx-swap="outerHTML"
></div>
</div>
<div x-data="{ open: false }" class="mb-3">
<div class="d-flex justify-content-start">
<div class="me-3">
<button class="btn btn-primary" @click="open = ! open">
Plus
</button>
</div>
<div x-show="open" @click.outside="open = false">
<div hx-get="/item/{{item_id}}/adjustment/positive" hx-trigger="load" hx-swap="outerHTML">
</div>
</div>
</div>
</div>
<div x-show="positive_form_open" @click.outside="positive_form_open = false">
<div
hx-get="/item/{{item_id}}/adjustment/positive"
hx-trigger="load"
hx-swap="outerHTML"
></div>
</div>
</section>
{% endif %}
<section class="container">
<div hx-get="/item/{{item_id}}/adjustments" hx-trigger="load" hx-swap="outerHTML">
</div>
<section class="mx-auto">
<div
hx-get="/item/{{item_id}}/adjustments"
hx-trigger="load"
hx-swap="outerHTML"
></div>
</section>
{% endblock %}
{% endblock %}

@ -1,36 +1,25 @@
<section hx-get="/item/{{item_id}}/stats" hx-trigger="new-adjustment from:body" hx-swap="outerHTML" class="mb-4">
<div class="row">
<div class="col-md-3">
<div class="card text-center mb-1">
<div class="card-header">
<p class="text-bolder text-uppercase">Stock</p>
</div>
<div class="card-body">
<p class="display-6">{{item.amount}} {{item.display_unit_short}}</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center mb-1">
<div class="card-header">
<p class="text-bolder text-uppercase">Reorder Point</p>
</div>
<div class="card-body">
<p class="display-6">{{item.reorder_point}} {{item.display_unit_short}}</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center mb-1">
<div class="card-header">
<p class="text-bolder text-uppercase">Value</p>
</div>
<div class="card-body">
<p class="display-6">{{value}}</p>
</div>
</div>
</div>
<section
hx-get="/item/{{item_id}}/stats"
hx-trigger="new-adjustment from:body"
hx-swap="outerHTML"
class="mb-4"
>
<div class="flex justify-start">
<div class="me-4 rounded border border-space-cadet px-4 py-4">
<p class="text-center text-xl uppercase">Stock</p>
<p class="text-center text-xl">
{{item.amount}} {{item.display_unit_short}}
</p>
</div>
<div class="me-4 rounded border border-space-cadet px-4 py-4">
<p class="text-center text-xl uppercase">Reorder</p>
<p class="text-center text-xl">
{{item.reorder_point}} {{item.display_unit_short}}
</p>
</div>
<div class="me-4 rounded border border-space-cadet px-4 py-4">
<p class="text-center text-xl uppercase">Value</p>
<p class="text-center text-xl">{{value}}</p>
</div>
</div>
</section>

@ -1,9 +1,7 @@
{% extends "main.html" %}
{% block content %}
{% extends "main.html" %} {% block content %}
<h1>Logged out</h1>
<p>You have been logged out</p>
<p><a href="/auth/login">Log In</a></p>
{% endblock %}
{% endblock %}

@ -1,53 +1,83 @@
<!DOCTYPE html>
<html lang="en" data-bs-theme="dark">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<!-- <link rel="stylesheet" href="/css/pico.min.css" > -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!--TODO Vendor css-->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="color-scheme" content="light dark" />
<link rel="stylesheet" href="/css/main.css" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="/js/htmx.min.js"></script>
<!--TODO Vendor this script -->
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<script
defer
src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"
></script>
<title>{% block title %}Title{% endblock %}</title>
</head>
<body>
<header class="container mb-4">
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<a class="navbar-brand" href="/home">Inventory App</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-end" id="navbarNav">
<ul class="navbar-nav ">
<li class="nav-item"><h1></h1></li>
<li class="nav-item"><a class="nav-link" href="/overview">Overview</a></li>
<li class="nav-item"><a class="nav-link" href="/catalog">Catalog</a></li>
<li class="nav-item"><a class="nav-link" href="/upload">Upload</a></li>
<li class="nav-item"><a class="nav-link" href="/reports">Reports</a></li>
<li class="nav-item"><a class="nav-link" href="/history">History</a></li>
</ul>
<div class="d-flex">
<a class="btn btn-primary" href="/auth/logout">Logout</a>
</div>
</div>
</head>
<body
class="min-h-screen bg-slate-100 text-neutral-900 dark:bg-neutral-900 dark:text-slate-100"
>
<header class="top-0 w-full mb-4 bg-space-cadet font-sans">
<nav
class="mx-auto flex max-w-7xl justify-between gap-2 px-2 py-4 sm:px-6 lg:px-8"
>
<div>
<a
href="/home"
class="rounded-md bg-english-violet px-3 py-2 text-sm font-medium text-white"
>
Inventory App
</a>
</div>
</nav>
</header>
<main class="container">
{% block content %}<p>Content Missing</p>{% endblock %}
</main>
<footer class="container-fluid">
<div>
<a
href="/overview"
class="rounded-md px-3 py-2 text-sm font-medium text-gray-300 hover:bg-gray-700 hover:text-white"
>
Overview
</a>
<a
href="/catalog"
class="rounded-md px-3 py-2 text-sm font-medium text-gray-300 hover:bg-gray-700 hover:text-white"
>
Catalog
</a>
<a
href="/upload"
class="rounded-md px-3 py-2 text-sm font-medium text-gray-300 hover:bg-gray-700 hover:text-white"
>
Upload
</a>
<a
href="/reports"
class="rounded-md px-3 py-2 text-sm font-medium text-gray-300 hover:bg-gray-700 hover:text-white"
>
Reports
</a>
<a
href="/history"
class="rounded-md px-3 py-2 text-sm font-medium text-gray-300 hover:bg-gray-700 hover:text-white"
>
History
</a>
</div>
</nav>
</header>
<main
class="relative mx-auto max-w-[68.75rem] w-[95vw] bg-slate-100 px-1 text-neutral-900 dark:bg-neutral-900 dark:text-slate-100"
>
{% block content %}
<p>Content Missing</p>
{% endblock %}
</main>
<footer>
<!--
<script>
// Needed for nice bootstrap dropdowns
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="dropdown"]');
const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl))
</script>
</footer>
</body>
</html>
--></footer>
</body>
</html>

@ -1,11 +1,10 @@
{% extends "problem.html" %}
{% block content %}
{% extends "problem.html" %} {% block content %}
<h1>Not Found</h1>
<p>
Sorry, we can't seem to find the page you're looking for. Please press back button or return
<a href="/">home</a>.
Sorry, we can't seem to find the page you're looking for. Please press back
button or return
<a href="/">home</a>.
</p>
{% endblock %}
{% endblock %}

@ -1,9 +1,6 @@
{% extends "main.html" %}
{% block title %} Overview {% endblock %}
{% block content %}
{% extends "main.html" %} {% block title %} Overview {% endblock %} {% block
content %}
<h1>Overview (Coming soon)</h1>
{% endblock %}
{% endblock %}

@ -1,16 +1,18 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<link rel="stylesheet" href="/css/pico.min.css">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="color-scheme" content="light dark" />
<link rel="stylesheet" href="/css/pico.min.css" />
<script src="/js/htmx.min.js"></script>
<title>Problem</title>
</head>
<body>
<main class="container">
{% block content %}<p>Something went wrong</p>{% endblock %}
</main>
</body>
</html>
</head>
<body>
<main class="container">
{% block content %}
<p>Something went wrong</p>
{% endblock %}
</main>
</body>
</html>

@ -1,9 +1,6 @@
{% extends "main.html" %}
{% block title %} Reports {% endblock %}
{% block content %}
{% extends "main.html" %} {% block title %} Reports {% endblock %} {% block
content %}
<h1>Reports (Coming soon)</h1>
{% endblock %}
{% endblock %}

@ -1,19 +1,27 @@
{% extends "main.html" %}
{% extends "main.html" %} {% block title %} Upload {% endblock %} {% block
content %}
{% block title %} Upload {% endblock %}
{% block content %}
<form action="/upload/catalog" method="post" enctype="multipart/form-data" x-data="{ file: '' }">
<fieldset class="grid">
<h3>Catalog Import</h3>
<label role="button" class="secondary" x-show="!file">
Choose File
<input type="file" name="file" x-model="file" style="display: none" required />
</label>
<input type="submit" value="Upload" x-show="file">
<input type="reset" value="Cancel" x-show="file">
</fieldset>
<form
action="/upload/catalog"
method="post"
enctype="multipart/form-data"
x-data="{ file: '' }"
>
<fieldset class="grid">
<h3>Catalog Import</h3>
<label role="button" class="secondary" x-show="!file">
Choose File
<input
type="file"
name="file"
x-model="file"
style="display: none"
required
/>
</label>
<input type="submit" value="Upload" x-show="file" />
<input type="reset" value="Cancel" x-show="file" />
</fieldset>
</form>
{% endblock %}
{% endblock %}

Loading…
Cancel
Save

Powered by TurnKey Linux.