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 %}

@ -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"
<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"
/>
</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="Search"
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"
/>
</p>
</div>
<div id="items" class="container">
{% include "catalog_item_fragment.html" %}
</div>
</div>
{% 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>
</div>
</li>
{% endfor %}
</ul>
</div>
</div>

@ -1,10 +1,9 @@
{% 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>
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>

@ -1,37 +1,63 @@
{% 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" />
<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="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 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="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 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="col-sm-2">
<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="form-control"/>
<small class="form-text">Timezone {{ time_zone }}</small>
<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">
<section id="items" class="mb-4">
{% include "history_item_fragment.html" %}
</section>

@ -1,22 +1,29 @@
<div class="card">
<ul class="list-group list-group-flush">
<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 %}
<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">
<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 %}
</p>
</div>
</li>
</td>
</tr>
{% endfor %}
</ul>
</div>
</tbody>
</table>

@ -1,14 +1,37 @@
{% 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"
<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"
/>
</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("") }}'
@ -17,7 +40,7 @@
hx-target="#items"
hx-push-url="true"
/>
</p>
</div>
<div id="items" class="container">
{% include "home_search_item_fragment.html" %}

@ -1,16 +1,29 @@
{% if items.len() > 0 %}
<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="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-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>
</div>
</div>
{% endif %}

@ -1,24 +1,30 @@
<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>
{% 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>

@ -1,65 +1,119 @@
<form hx-post="/item/{{item_id}}/adjustment/negative"
hx-target="this" hx-swap="outerHTML"
<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"
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="form-control"
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() -%}
{%
if
!amount_error.is_empty()
-%}
aria-invalid="true"
aria-describedby="invalid-amount"
{% endif -%}
{%
endif
-%}
/>
{% if !amount_error.is_empty() -%}
<small id="invalid-amount" class="invalid-feedback">{{ amount_error }}</small>
<small id="invalid-amount" class="mt-2 text-sm text-cerise">
{{ 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>
<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>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item"
@click="reason = 'Sale'">
<!-- 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>
</li>
<li>
<a class="dropdown-item"
@click="reason = 'Destruction'">
<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>
</li>
<li>
<a class="dropdown-item"
@click="reason = 'Expiration'">
<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>
</li>
<li>
<a class="dropdown-item"
@click="reason = 'Theft'">
<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>
</li>
</ul>
</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"
<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"
class="form-control"
placeholder="Amount"
aria-label="amount"
required
{% if !amount_error.is_empty() -%}
{%
if
!amount_error.is_empty()
-%}
aria-invalid="true"
aria-describedby="invalid-amount"
{% endif -%}
{%
endif
-%}
/>
{% if !amount_error.is_empty() -%}
<small id="invalid-amount" class="invalid-feedback">{{ amount_error }}</small>
<small id="invalid-amount" class="mt-2 text-sm text-cerise">
{{ amount_error }}
</small>
{% endif -%}
</div>
<div class="col">
<input type="text"
<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"
class="form-control"
aria-label="price"
required
{% if !price_error.is_empty() -%}
{%
if
!price_error.is_empty()
-%}
aria-invalid="true"
aria-describedby="invalid-price"
{% endif -%}
{%
endif
-%}
/>
{% if !price_error.is_empty() -%}
<small id="invalid-price" class="invalid-feedback">{{ price_error }}</small>
<small id="invalid-price" class="mt-2 text-sm text-cerise"
>{{ price_error }}</small
>
{% endif -%}
</div>
<div class="col">
<button class="btn btn-primary">Add</button>
</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 %}
{% block content %}
<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>
<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">
<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">
<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>
</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 class="d-flex justify-content-start">
<div class="me-3">
<button class="btn btn-primary" @click="open = ! open">
<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-show="open" @click.outside="open = false">
<div hx-get="/item/{{item_id}}/adjustment/positive" hx-trigger="load" hx-swap="outerHTML">
</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-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 %}

@ -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>
<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,6 +1,4 @@
{% extends "main.html" %}
{% block content %}
{% extends "main.html" %} {% block content %}
<h1>Logged out</h1>
<p>You have been logged out</p>

@ -1,53 +1,83 @@
<!DOCTYPE html>
<html lang="en" data-bs-theme="dark">
<!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" > -->
<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">
<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>
<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>
<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="container">
{% block content %}<p>Content Missing</p>{% endblock %}
<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 class="container-fluid">
<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>
--></footer>
</body>
</html>

@ -1,10 +1,9 @@
{% 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
Sorry, we can't seem to find the page you're looking for. Please press back
button or return
<a href="/">home</a>.
</p>

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

@ -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">
<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 %}
{% block content %}
<p>Something went wrong</p>
{% endblock %}
</main>
</body>
</html>

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

@ -1,18 +1,26 @@
{% 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: '' }">
<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 />
<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">
<input type="submit" value="Upload" x-show="file" />
<input type="reset" value="Cancel" x-show="file" />
</fieldset>
</form>

Loading…
Cancel
Save

Powered by TurnKey Linux.