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 /target
/resources
/.env /.env
/*.db /*.db
/*.db-shm /*.db-shm
/*.db-wal /*.db-wal
/node_modules
/*.lockb

@ -13,5 +13,8 @@
</option> </option>
<option name="myCustomValuesEnabled" value="true" /> <option name="myCustomValuesEnabled" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="RsUnusedImport" enabled="true" level="WARNING" enabled_by_default="true">
<option name="enableOnlyIfProcMacrosEnabled" value="false" />
</inspection_tool>
</profile> </profile>
</component> </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> { pub fn routes() -> Router<AppState> {
Router::new() Router::new()
.nest_service("/css/pico.min.css", ServeFile::new("static/css/pico.min.css")) .nest_service("/css/main.css", ServeFile::new("resources/main.css"))
.nest_service("/css/custom.css", ServeFile::new("static/css/custom.css")) .nest_service("/js/htmx.min.js", ServeFile::new("resources/htmx.min.js"))
.nest_service("/js/htmx.min.js", ServeFile::new("static/js/htmx.min.js"))
.nest_service("/favicon.ico", ServeFile::new("static/favicon.ico")) .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" %} {% extends "problem.html" %} {% block content %}
{% block content %}
<h1>Error</h1> <h1>Error</h1>
<p> <p>Oops, something went wrong. Press the back button to try again.</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 %} <div class="mx-auto mb-4 px-4">
<label
{% block content %} for="item-filter"
class="sr-only mb-2 text-sm font-medium text-gray-900 dark:text-white"
<div class="container"> >Search</label
<p class="container d-flex justify-content-center"> >
<input id="search" class="form-control" <div class="relative mb-4 max-w-56 content-center">
type="search" name="q" <div
placeholder="Filter" class="pointer-events-none absolute inset-y-0 start-0 flex items-center ps-3"
aria-label="Search" >
value='{{ query.search.as_deref().unwrap_or("") }}' <svg
hx-get="/items" class="h-4 w-4 text-gray-500 dark:text-gray-400"
hx-trigger="search, keyup delay:500ms changed" aria-hidden="true"
hx-target="#items" xmlns="http://www.w3.org/2000/svg"
hx-push-url="true" 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> </svg>
<div id="items" class="container">
{% include "catalog_item_fragment.html" %}
</div> </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> </div>
{% endblock %} {% endblock %}

@ -1,11 +1,17 @@
<div class="card"> <div class="mx-auto">
<ul class="list-group list-group-flush"> <div class="flex flex-col">
{% for item in items %} {% for item in items %}
<li id="item-{{item.id}}-entry" class="list-group-item"> <div class="mx-1 mb-4 flex flex-row">
<div class="row"> <div class="basis-1/2">
<div class="col-6"><a href="/item/{{item.id}}/" hx-push-url="true">{{ item.name }}</a></div> <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>
</li> </div>
{% endfor %} {% endfor %}
</ul> </div>
</div> </div>

@ -1,11 +1,10 @@
{% extends "problem.html" %} {% extends "problem.html" %} {% block content %}
{% block content %}
<h1>Forbidden</h1> <h1>Forbidden</h1>
<p> <p>
You are forbidden from accessing this resource. Please contact your supervisor or <a href="/auth/logout">logout</a> You are forbidden from accessing this resource. Please contact your supervisor
and log back in with a different user. or <a href="/auth/logout">logout</a>
and log back in with a different user.
</p> </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 %} <section class="mb-4">
<form
<h1>Audit Log (Coming soon)</h1> action="/history"
hx-get="/history"
<section class="container mb-4"> hx-trigger="change"
<form action="/history" hx-get="/history" hx-trigger="change" hx-target="#items"> hx-target="#items"
<div class="row"> >
<div class="col-sm-2"> <div class="flex between">
<label for="start-date" class="form-label">Start Date</label> <div class="px-2">
<input type="date" id="start-date" name="start-date" value="{{ start_date }}" class="form-control" /> <label for="start-date" class="block">Start Date</label>
</div> <input
<div class="col-sm-2"> type="date"
<label for="start-time" class="form-label">Start Time</label> id="start-date"
<input type="time" id="start-time" name="start-time" value="{{ start_time }}" class="form-control"/> name="start-date"
<small class="form-text">Timezone {{ time_zone }}</small> value="{{ start_date }}"
</div> class="block"
<div class="col-sm-2"> />
<label for="end-date" class="form-label">End Date</label> </div>
<input type="date" id="end-date" name="end-date" value="{{ end_date }}" class="form-control"/> <div class="px-2">
</div> <label for="start-time" class="block">Start Time</label>
<div class="col-sm-2"> <input
<label for="end-time" class="form-label">End Time</label> type="time"
<input type="time" id="end-time" name="end-time" value="{{ end_time }}" class="form-control"/> id="start-time"
<small class="form-text">Timezone {{ time_zone }}</small> name="start-time"
</div> value="{{ start_time }}"
</div> class="block"
</form> />
<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>
<section id="items" class="container"> <section id="items" class="mb-4">
{% include "history_item_fragment.html" %} {% include "history_item_fragment.html" %}
</section> </section>
{% endblock %} {% endblock %}

@ -1,22 +1,29 @@
<table class="table-auto">
<div class="card"> <thead>
<ul class="list-group list-group-flush"> <tr class="mb-2">
{% for item in items %} <th>Date</th>
<li class="list-group-item"> <th>Item</th>
<div class="row"> <th>User</th>
<p class="col">{{ item.create_date }}</p> <th>Desc</th>
<p class="col">{{ item.item_name }}</p> </tr>
<p class="col">{{ item.user_name }}</p> </thead>
<p class="col"> <tbody>
{% match item.item_type %} {% for item in items %}
{% when HistoryItemEntry::PositiveAdjustment with (entry) %} <tr class="mb-2">
{{ entry.amount }} @ {{ entry.unit_value }} <td class="px-2">{{ item.create_date }}</td>
{% when HistoryItemEntry::NegativeAdjustment with (entry) %} <td class="px-2">{{ item.item_name }}</td>
{{ entry.amount }} {{ entry.reason }} <td class="px-2">{{ item.user_name }}</td>
{% endmatch %} <td class="px-2">
</p> {% match item.item_type %}
</div> {% when HistoryItemEntry::PositiveAdjustment with (entry) %}
</li> {{ entry.amount }} @ {{ entry.unit_value }}
{% when HistoryItemEntry::NegativeAdjustment with (entry) %}
{{ entry.amount }} {{ entry.reason }}
{% endmatch %}
</td>
</tr>
{% endfor %} {% 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 %} <div class="mx-auto mb-4 px-4">
<label
{% block content %} for="default-search"
class="sr-only mb-2 text-sm font-medium text-gray-900 dark:text-white"
<div class="container"> >Search</label
<p class="container d-flex justify-content-center"> >
<input id="search" <div class="relative mb-4 max-w-56 content-center">
class="form-control" <div
type="search" name="q" class="pointer-events-none absolute inset-y-0 start-0 flex items-center ps-3"
placeholder="Search" >
aria-label="Search" <svg
value='{{ query.search.as_deref().unwrap_or("") }}' class="h-4 w-4 text-gray-500 dark:text-gray-400"
hx-get="/home/search" aria-hidden="true"
hx-trigger="search, keyup delay:500ms changed" xmlns="http://www.w3.org/2000/svg"
hx-target="#items" fill="none"
hx-push-url="true" 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> </svg>
<div id="items" class="container">
{% include "home_search_item_fragment.html" %}
</div> </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> </div>
{% endblock %} {% endblock %}

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

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

@ -1,65 +1,119 @@
<form hx-post="/item/{{item_id}}/adjustment/negative" <form
hx-target="this" hx-swap="outerHTML" hx-post="/item/{{item_id}}/adjustment/negative"
x-ref="formNegativeAdjustment" hx-target="this"
x-data="{ reason: 'Sale' }"> hx-swap="outerHTML"
<input id="reason" name="reason" type="hidden" x-model="reason"/> x-ref="formNegativeAdjustment"
<div class="row"> x-data="{ reason: 'Sale', reason_dropdown_show: false }"
<div class="col"> >
<!--<label for="amount" class="form-label">Amount</label>--> <div class="mb-5">
<input type="number" <label for="amount" class="mb-2 block text-sm font-medium">Amount</label>
id="amount" <input
name="amount" type="number"
step="0.01" id="amount"
class="form-control" name="amount"
placeholder="Amount" step="0.01"
aria-label="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"
required placeholder="Amount"
{% if !amount_error.is_empty() -%} aria-label="amount"
aria-invalid="true" required
aria-describedby="invalid-amount" {%
{% endif -%} if
/> !amount_error.is_empty()
{% if !amount_error.is_empty() -%} -%}
<small id="invalid-amount" class="invalid-feedback">{{ amount_error }}</small> aria-invalid="true"
{% endif -%} aria-describedby="invalid-amount"
</div> {%
<div class="col"> endif
<button class="btn btn-primary" x-text="reason">Sale</button> -%}
/>
</div> {% if !amount_error.is_empty() -%}
<div class="col"> <small id="invalid-amount" class="mt-2 text-sm text-cerise">
<div class="dropdown"> {{ amount_error }}
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" </small>
aria-expanded="false"> {% endif -%}
<span class="visually-hidden">Other</span> </div>
</button> <div class="mb-5 flex justify-start">
<ul class="dropdown-menu"> <button
<li> 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"
<a class="dropdown-item" x-text="reason"
@click="reason = 'Sale'"> >
Sale Sale
</a> </button>
</li> <div
<li> x-data="{ isOpen: false, openedWithKeyboard: false }"
<a class="dropdown-item" class="relative w-fit"
@click="reason = 'Destruction'"> x-on:keydown.esc.window="isOpen = false; openedWithKeyboard = false"
Destruction >
</a> <button
</li> type="button"
<li> x-on:click="isOpen = ! isOpen"
<a class="dropdown-item" 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"
@click="reason = 'Expiration'"> aria-haspopup="true"
Expiration x-on:keydown.space.prevent="openedWithKeyboard = true"
</a> x-on:keydown.enter.prevent="openedWithKeyboard = true"
</li> x-on:keydown.down.prevent="openedWithKeyboard = true"
<li> x-bind:class="isOpen || openedWithKeyboard ? 'text-on-surface-strong dark:text-on-surface-dark-strong' : 'text-on-surface dark:text-on-surface-dark'"
<a class="dropdown-item" x-bind:aria-expanded="isOpen || openedWithKeyboard"
@click="reason = 'Theft'"> >
Theft <span class="sr-only">Reason Options</span>
</a> <svg
</li> aria-hidden="true"
</ul> fill="none"
</div> xmlns="http://www.w3.org/2000/svg"
</div> 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>
</div>
<input id="reason" name="reason" type="hidden" x-model="reason" />
</form> </form>

@ -1,43 +1,66 @@
<form
<form hx-post="/item/{{item_id}}/adjustment/positive" hx-target="this" hx-swap="outerHTML" > hx-post="/item/{{item_id}}/adjustment/positive"
<div class="row"> hx-target="this"
<div class="col"> hx-swap="outerHTML"
<input type="number" >
id="amount" <div class="mb-5">
name="amount" <label for="amount" class="mb-2 block text-sm font-medium">Amount</label>
step="0.01" <input
class="form-control" type="number"
placeholder="Amount" id="amount"
aria-label="amount" name="amount"
required 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"
{% if !amount_error.is_empty() -%} step="0.01"
aria-invalid="true" placeholder="Amount"
aria-describedby="invalid-amount" aria-label="amount"
{% endif -%} required
/> {%
{% if !amount_error.is_empty() -%} if
<small id="invalid-amount" class="invalid-feedback">{{ amount_error }}</small> !amount_error.is_empty()
{% endif -%} -%}
</div> aria-invalid="true"
<div class="col"> aria-describedby="invalid-amount"
<input type="text" {%
id="price" endif
name="price" -%}
placeholder="Price" />
class="form-control" {% if !amount_error.is_empty() -%}
aria-label="price" <small id="invalid-amount" class="mt-2 text-sm text-cerise">
required {{ amount_error }}
{% if !price_error.is_empty() -%} </small>
aria-invalid="true" {% endif -%}
aria-describedby="invalid-price" </div>
{% endif -%} <div class="mb-5">
/> <label for="price" class="mb-2 block text-sm font-medium">Price</label>
{% if !price_error.is_empty() -%} <input
<small id="invalid-price" class="invalid-feedback">{{ price_error }}</small> type="text"
{% endif -%} id="price"
</div> name="price"
<div class="col"> 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"
<button class="btn btn-primary">Add</button> placeholder="Price"
</div> aria-label="price"
</div> 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> </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 %} <section
hx-get="/item/{{item_id}}/stats"
<div class="container mb-5"> hx-trigger="load"
<h2>{{item.name}} {% if !item.active %}<span class="badge text-bg-danger">Inactive</span> {% endif %}</h2> hx-swap="outerHTML"
</div> ></section>
<section class="container">
<div hx-get="/item/{{item_id}}/stats" hx-trigger="load" hx-swap="outerHTML">
</div>
</section>
{% if item.active %} {% 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 x-show="negative_form_open" @click.outside="negative_form_open = false">
<div class="d-flex justify-content-start"> <div
<div class="me-3"> hx-get="/item/{{item_id}}/adjustment/negative"
<button class="btn btn-primary display-6" @click="open = ! open"> hx-trigger="load"
Minus hx-swap="outerHTML"
</button> ></div>
</div> </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-data="{ open: false }" class="mb-3"> <div x-show="positive_form_open" @click.outside="positive_form_open = false">
<div class="d-flex justify-content-start"> <div
<div class="me-3"> hx-get="/item/{{item_id}}/adjustment/positive"
<button class="btn btn-primary" @click="open = ! open"> hx-trigger="load"
Plus hx-swap="outerHTML"
</button> ></div>
</div> </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>
</section> </section>
{% endif %} {% endif %}
<section class="container"> <section class="mx-auto">
<div hx-get="/item/{{item_id}}/adjustments" hx-trigger="load" hx-swap="outerHTML"> <div
</div> hx-get="/item/{{item_id}}/adjustments"
hx-trigger="load"
hx-swap="outerHTML"
></div>
</section> </section>
{% endblock %} {% endblock %}

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

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

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

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

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

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

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

Loading…
Cancel
Save

Powered by TurnKey Linux.