feat(a11y): add ARIA attributes, token colors, toast, and tab-switch links
Add aria-required to required inputs, role=alert to error messages, aria-label to icon-only list nav buttons, and progressbar ARIA to bag status bars. Replace hardcoded red/green colors with error/success design tokens across all templates. Add toast notifications for entity creation and tab-switch links for empty add-form states. Increase scan upload body limit to 10MB.
This commit is contained in:
parent
aad927085b
commit
3c273feb18
26 changed files with 213 additions and 61 deletions
|
|
@ -67,8 +67,14 @@ pub(super) fn router() -> axum::Router<AppState> {
|
||||||
.route("/nearby-cafes", get(cafes::nearby_cafes))
|
.route("/nearby-cafes", get(cafes::nearby_cafes))
|
||||||
.route("/extract-roaster", post(roasters::extract_roaster))
|
.route("/extract-roaster", post(roasters::extract_roaster))
|
||||||
.route("/extract-roast", post(roasts::extract_roast_info))
|
.route("/extract-roast", post(roasts::extract_roast_info))
|
||||||
.route("/extract-bag-scan", post(scan::extract_bag_scan))
|
.route(
|
||||||
.route("/scan", post(scan::submit_scan))
|
"/extract-bag-scan",
|
||||||
|
post(scan::extract_bag_scan).layer(DefaultBodyLimit::max(10 * 1024 * 1024)),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/scan",
|
||||||
|
post(scan::submit_scan).layer(DefaultBodyLimit::max(10 * 1024 * 1024)),
|
||||||
|
)
|
||||||
.route("/check-in", post(checkin::submit_checkin))
|
.route("/check-in", post(checkin::submit_checkin))
|
||||||
.route("/cups", get(cups::list_cups).post(cups::create_cup))
|
.route("/cups", get(cups::list_cups).post(cups::create_cup))
|
||||||
.route("/cups/{id}", get(cups::get_cup).delete(cups::delete_cup))
|
.route("/cups/{id}", get(cups::get_cup).delete(cups::delete_cup))
|
||||||
|
|
|
||||||
|
|
@ -48,12 +48,33 @@
|
||||||
type="module"
|
type="module"
|
||||||
src="https://cdn.jsdelivr.net/gh/starfederation/datastar@1.0.0-RC.6/bundles/datastar.js"
|
src="https://cdn.jsdelivr.net/gh/starfederation/datastar@1.0.0-RC.6/bundles/datastar.js"
|
||||||
></script>
|
></script>
|
||||||
|
<script defer src="/static/js/location.js"></script>
|
||||||
<script defer src="/static/js/components/photo-capture.js"></script>
|
<script defer src="/static/js/components/photo-capture.js"></script>
|
||||||
<script defer src="/static/js/components/searchable-select.js"></script>
|
<script defer src="/static/js/components/searchable-select.js"></script>
|
||||||
<script defer src="/static/js/components/chip-scroll.js"></script>
|
<script defer src="/static/js/components/chip-scroll.js"></script>
|
||||||
<script defer src="/static/js/components/world-map.js"></script>
|
<script defer src="/static/js/components/world-map.js"></script>
|
||||||
<script defer src="/static/js/components/donut-chart.js"></script>
|
<script defer src="/static/js/components/donut-chart.js"></script>
|
||||||
{% block head %}{% endblock %}
|
{% block head %}{% endblock %}
|
||||||
|
<script>
|
||||||
|
const showToast = (msg, ms = 3000) => {
|
||||||
|
const el = document.getElementById("toast-message");
|
||||||
|
if (!el) return;
|
||||||
|
el.textContent = msg;
|
||||||
|
el.parentElement.classList.remove("hidden");
|
||||||
|
clearTimeout(showToast._t);
|
||||||
|
showToast._t = setTimeout(
|
||||||
|
() => el.parentElement.classList.add("hidden"),
|
||||||
|
ms,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const t = sessionStorage.getItem("toast");
|
||||||
|
if (t) {
|
||||||
|
sessionStorage.removeItem("toast");
|
||||||
|
showToast(t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
<script>
|
<script>
|
||||||
const toggleRow = (event) => {
|
const toggleRow = (event) => {
|
||||||
if (event.target.closest(".card-detail")) return;
|
if (event.target.closest(".card-detail")) return;
|
||||||
|
|
@ -184,5 +205,18 @@
|
||||||
</p>
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="toast-container"
|
||||||
|
class="fixed bottom-4 left-1/2 -translate-x-1/2 z-50"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="hidden rounded-lg border border-success-border bg-surface px-4 py-3 text-sm font-medium text-success-text shadow-lg"
|
||||||
|
role="status"
|
||||||
|
aria-live="polite"
|
||||||
|
>
|
||||||
|
<span id="toast-message"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,6 @@
|
||||||
{% import "partials/location_search.html" as location %}
|
{% import "partials/location_search.html" as location %}
|
||||||
{% block title %}Brewlog · Add{% endblock %}
|
{% block title %}Brewlog · Add{% endblock %}
|
||||||
|
|
||||||
{% block head %}
|
|
||||||
<script>
|
|
||||||
{
|
|
||||||
{
|
|
||||||
location::location_search_js();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section
|
<section
|
||||||
data-signals:_add-submitting="false"
|
data-signals:_add-submitting="false"
|
||||||
|
|
@ -127,7 +117,8 @@
|
||||||
data-show="$_roasterExtractError"
|
data-show="$_roasterExtractError"
|
||||||
data-text="$_roasterExtractError"
|
data-text="$_roasterExtractError"
|
||||||
style="display:none"
|
style="display:none"
|
||||||
class="mt-2 text-sm text-red-600"
|
class="mt-2 text-sm text-error"
|
||||||
|
role="alert"
|
||||||
></p>
|
></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -137,6 +128,7 @@
|
||||||
method="post"
|
method="post"
|
||||||
action="/api/v1/roasters"
|
action="/api/v1/roasters"
|
||||||
class="flex flex-col gap-4"
|
class="flex flex-col gap-4"
|
||||||
|
onsubmit="sessionStorage.setItem('toast', 'Roaster added')"
|
||||||
>
|
>
|
||||||
<div class="grid gap-4 sm:grid-cols-2">
|
<div class="grid gap-4 sm:grid-cols-2">
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
<label class="flex flex-col gap-1 text-sm">
|
||||||
|
|
@ -148,6 +140,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
name="name"
|
name="name"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
placeholder="Example Coffee Roasters"
|
placeholder="Example Coffee Roasters"
|
||||||
data-bind:_roaster-name
|
data-bind:_roaster-name
|
||||||
|
|
@ -162,6 +155,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
name="country"
|
name="country"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
placeholder="United States"
|
placeholder="United States"
|
||||||
data-bind:_roaster-country
|
data-bind:_roaster-country
|
||||||
|
|
@ -216,7 +210,15 @@
|
||||||
<div class="text-sm text-text-secondary">
|
<div class="text-sm text-text-secondary">
|
||||||
<h3 class="text-lg font-semibold text-text">Add a roaster first</h3>
|
<h3 class="text-lg font-semibold text-text">Add a roaster first</h3>
|
||||||
<p class="mt-2">
|
<p class="mt-2">
|
||||||
Roasts need a roaster. Add a roaster above to enable this form.
|
Roasts need a roaster.
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="font-medium text-accent hover:underline"
|
||||||
|
data-on:click="$_addType = 'roaster'"
|
||||||
|
>
|
||||||
|
Add a roaster
|
||||||
|
</button>
|
||||||
|
to enable this form.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
@ -270,7 +272,8 @@
|
||||||
data-show="$_roastExtractError"
|
data-show="$_roastExtractError"
|
||||||
data-text="$_roastExtractError"
|
data-text="$_roastExtractError"
|
||||||
style="display:none"
|
style="display:none"
|
||||||
class="mt-2 text-sm text-red-600"
|
class="mt-2 text-sm text-error"
|
||||||
|
role="alert"
|
||||||
></p>
|
></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -280,6 +283,7 @@
|
||||||
method="post"
|
method="post"
|
||||||
action="/api/v1/roasts"
|
action="/api/v1/roasts"
|
||||||
class="flex flex-col gap-4"
|
class="flex flex-col gap-4"
|
||||||
|
onsubmit="sessionStorage.setItem('toast', 'Roast added')"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col gap-1 text-sm">
|
<div class="flex flex-col gap-1 text-sm">
|
||||||
<span
|
<span
|
||||||
|
|
@ -314,6 +318,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
name="name"
|
name="name"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
placeholder="Ethiopia Yirgacheffe"
|
placeholder="Ethiopia Yirgacheffe"
|
||||||
data-bind:_roast-name
|
data-bind:_roast-name
|
||||||
|
|
@ -328,6 +333,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
name="origin"
|
name="origin"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
placeholder="Ethiopia"
|
placeholder="Ethiopia"
|
||||||
data-bind:_origin
|
data-bind:_origin
|
||||||
|
|
@ -342,6 +348,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
name="region"
|
name="region"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
placeholder="Guji"
|
placeholder="Guji"
|
||||||
data-bind:_region
|
data-bind:_region
|
||||||
|
|
@ -356,6 +363,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
name="producer"
|
name="producer"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
placeholder="Chelbesa Cooperative"
|
placeholder="Chelbesa Cooperative"
|
||||||
data-bind:_producer
|
data-bind:_producer
|
||||||
|
|
@ -370,6 +378,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
name="process"
|
name="process"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
placeholder="Washed"
|
placeholder="Washed"
|
||||||
data-bind:_process
|
data-bind:_process
|
||||||
|
|
@ -384,6 +393,7 @@
|
||||||
name="tasting_notes"
|
name="tasting_notes"
|
||||||
rows="2"
|
rows="2"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
placeholder="Blueberry, Jasmine"
|
placeholder="Blueberry, Jasmine"
|
||||||
data-bind:_tasting-notes
|
data-bind:_tasting-notes
|
||||||
|
|
@ -413,7 +423,15 @@
|
||||||
<div class="text-sm text-text-secondary">
|
<div class="text-sm text-text-secondary">
|
||||||
<h3 class="text-lg font-semibold text-text">Add a roast first</h3>
|
<h3 class="text-lg font-semibold text-text">Add a roast first</h3>
|
||||||
<p class="mt-2">
|
<p class="mt-2">
|
||||||
Bags need a roast. Add a roaster and roast to enable this form.
|
Bags need a roast.
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="font-medium text-accent hover:underline"
|
||||||
|
data-on:click="$_addType = 'roast'"
|
||||||
|
>
|
||||||
|
Add a roast
|
||||||
|
</button>
|
||||||
|
to enable this form.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
@ -427,6 +445,7 @@
|
||||||
method="post"
|
method="post"
|
||||||
action="/api/v1/bags"
|
action="/api/v1/bags"
|
||||||
class="mt-4 flex flex-col gap-4"
|
class="mt-4 flex flex-col gap-4"
|
||||||
|
onsubmit="sessionStorage.setItem('toast', 'Bag added')"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col gap-1 text-sm">
|
<div class="flex flex-col gap-1 text-sm">
|
||||||
<span
|
<span
|
||||||
|
|
@ -470,6 +489,7 @@
|
||||||
name="amount"
|
name="amount"
|
||||||
step="0.1"
|
step="0.1"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
placeholder="250"
|
placeholder="250"
|
||||||
/>
|
/>
|
||||||
|
|
@ -497,14 +517,30 @@
|
||||||
<div class="text-sm text-text-secondary">
|
<div class="text-sm text-text-secondary">
|
||||||
<h3 class="text-lg font-semibold text-text">Open a bag first</h3>
|
<h3 class="text-lg font-semibold text-text">Open a bag first</h3>
|
||||||
<p class="mt-2">
|
<p class="mt-2">
|
||||||
Brews need an open bag of coffee. Add a bag to enable this form.
|
Brews need an open bag of coffee.
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="font-medium text-accent hover:underline"
|
||||||
|
data-on:click="$_addType = 'bag'"
|
||||||
|
>
|
||||||
|
Add a bag
|
||||||
|
</button>
|
||||||
|
to enable this form.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{% else if grinder_options.is_empty() || brewer_options.is_empty() %}
|
{% else if grinder_options.is_empty() || brewer_options.is_empty() %}
|
||||||
<div class="text-sm text-text-secondary">
|
<div class="text-sm text-text-secondary">
|
||||||
<h3 class="text-lg font-semibold text-text">Add gear first</h3>
|
<h3 class="text-lg font-semibold text-text">Add gear first</h3>
|
||||||
<p class="mt-2">
|
<p class="mt-2">
|
||||||
Brews need a grinder and brewer. Add gear to enable this form.
|
Brews need a grinder and brewer.
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="font-medium text-accent hover:underline"
|
||||||
|
data-on:click="$_addType = 'gear'"
|
||||||
|
>
|
||||||
|
Add gear
|
||||||
|
</button>
|
||||||
|
to enable this form.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
@ -516,6 +552,7 @@
|
||||||
method="post"
|
method="post"
|
||||||
action="/api/v1/brews"
|
action="/api/v1/brews"
|
||||||
class="mt-4 flex flex-col gap-6 overflow-hidden pb-16 md:pb-0"
|
class="mt-4 flex flex-col gap-6 overflow-hidden pb-16 md:pb-0"
|
||||||
|
onsubmit="sessionStorage.setItem('toast', 'Brew added')"
|
||||||
>
|
>
|
||||||
<!-- Coffee -->
|
<!-- Coffee -->
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -568,6 +605,7 @@
|
||||||
step="any"
|
step="any"
|
||||||
min="1"
|
min="1"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field flex-1 text-center"
|
class="input-field flex-1 text-center"
|
||||||
data-bind:_brew-weight
|
data-bind:_brew-weight
|
||||||
/>
|
/>
|
||||||
|
|
@ -616,6 +654,7 @@
|
||||||
<select
|
<select
|
||||||
name="grinder_id"
|
name="grinder_id"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
data-on:change="$_grinderDisplay = evt.target.options[evt.target.selectedIndex].text"
|
data-on:change="$_grinderDisplay = evt.target.options[evt.target.selectedIndex].text"
|
||||||
>
|
>
|
||||||
|
|
@ -648,6 +687,7 @@
|
||||||
step="any"
|
step="any"
|
||||||
min="0"
|
min="0"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field flex-1 text-center"
|
class="input-field flex-1 text-center"
|
||||||
data-bind:_brew-grind
|
data-bind:_brew-grind
|
||||||
/>
|
/>
|
||||||
|
|
@ -707,6 +747,7 @@
|
||||||
<select
|
<select
|
||||||
name="brewer_id"
|
name="brewer_id"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
data-on:change="$_brewerDisplay = evt.target.options[evt.target.selectedIndex].text"
|
data-on:change="$_brewerDisplay = evt.target.options[evt.target.selectedIndex].text"
|
||||||
>
|
>
|
||||||
|
|
@ -768,6 +809,7 @@
|
||||||
step="any"
|
step="any"
|
||||||
min="10"
|
min="10"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field flex-1 text-center"
|
class="input-field flex-1 text-center"
|
||||||
data-bind:_brew-volume
|
data-bind:_brew-volume
|
||||||
/>
|
/>
|
||||||
|
|
@ -800,6 +842,7 @@
|
||||||
min="0"
|
min="0"
|
||||||
max="100"
|
max="100"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field flex-1 text-center"
|
class="input-field flex-1 text-center"
|
||||||
data-bind:_brew-temp
|
data-bind:_brew-temp
|
||||||
/>
|
/>
|
||||||
|
|
@ -926,6 +969,7 @@
|
||||||
method="post"
|
method="post"
|
||||||
action="/api/v1/gear"
|
action="/api/v1/gear"
|
||||||
class="mt-4 flex flex-col gap-4"
|
class="mt-4 flex flex-col gap-4"
|
||||||
|
onsubmit="sessionStorage.setItem('toast', 'Gear added')"
|
||||||
>
|
>
|
||||||
<div class="grid gap-4 sm:grid-cols-3">
|
<div class="grid gap-4 sm:grid-cols-3">
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
<label class="flex flex-col gap-1 text-sm">
|
||||||
|
|
@ -933,7 +977,12 @@
|
||||||
class="text-xs font-semibold text-text-muted uppercase tracking-wide"
|
class="text-xs font-semibold text-text-muted uppercase tracking-wide"
|
||||||
>Category*</span
|
>Category*</span
|
||||||
>
|
>
|
||||||
<select name="category" required class="input-field">
|
<select
|
||||||
|
name="category"
|
||||||
|
required
|
||||||
|
aria-required="true"
|
||||||
|
class="input-field"
|
||||||
|
>
|
||||||
<option value="">Select a category</option>
|
<option value="">Select a category</option>
|
||||||
<option value="grinder">Grinder</option>
|
<option value="grinder">Grinder</option>
|
||||||
<option value="brewer">Brewer</option>
|
<option value="brewer">Brewer</option>
|
||||||
|
|
@ -949,6 +998,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
name="make"
|
name="make"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
placeholder="Baratza"
|
placeholder="Baratza"
|
||||||
/>
|
/>
|
||||||
|
|
@ -962,6 +1012,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
name="model"
|
name="model"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
placeholder="Encore"
|
placeholder="Encore"
|
||||||
/>
|
/>
|
||||||
|
|
@ -998,7 +1049,8 @@
|
||||||
data-show="$_cafeError"
|
data-show="$_cafeError"
|
||||||
data-text="$_cafeError"
|
data-text="$_cafeError"
|
||||||
style="display:none"
|
style="display:none"
|
||||||
class="mt-2 text-sm text-red-600"
|
class="mt-2 text-sm text-error"
|
||||||
|
role="alert"
|
||||||
></p>
|
></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -1036,6 +1088,7 @@
|
||||||
method="post"
|
method="post"
|
||||||
action="/api/v1/cafes"
|
action="/api/v1/cafes"
|
||||||
class="mt-4 flex flex-col gap-4"
|
class="mt-4 flex flex-col gap-4"
|
||||||
|
onsubmit="sessionStorage.setItem('toast', 'Cafe added')"
|
||||||
>
|
>
|
||||||
<div class="grid gap-4 sm:grid-cols-2">
|
<div class="grid gap-4 sm:grid-cols-2">
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
<label class="flex flex-col gap-1 text-sm">
|
||||||
|
|
@ -1047,6 +1100,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
name="name"
|
name="name"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
placeholder="Blue Bottle Coffee"
|
placeholder="Blue Bottle Coffee"
|
||||||
data-bind:_cafe-name
|
data-bind:_cafe-name
|
||||||
|
|
@ -1061,6 +1115,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
name="city"
|
name="city"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
placeholder="San Francisco"
|
placeholder="San Francisco"
|
||||||
data-bind:_cafe-city
|
data-bind:_cafe-city
|
||||||
|
|
@ -1075,6 +1130,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
name="country"
|
name="country"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
placeholder="United States"
|
placeholder="United States"
|
||||||
data-bind:_cafe-country
|
data-bind:_cafe-country
|
||||||
|
|
@ -1103,6 +1159,7 @@
|
||||||
name="latitude"
|
name="latitude"
|
||||||
step="any"
|
step="any"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
placeholder="37.7749"
|
placeholder="37.7749"
|
||||||
data-bind:_cafe-lat
|
data-bind:_cafe-lat
|
||||||
|
|
@ -1118,6 +1175,7 @@
|
||||||
name="longitude"
|
name="longitude"
|
||||||
step="any"
|
step="any"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
placeholder="-122.4194"
|
placeholder="-122.4194"
|
||||||
data-bind:_cafe-lng
|
data-bind:_cafe-lng
|
||||||
|
|
@ -1147,8 +1205,27 @@
|
||||||
Add a roast and cafe first
|
Add a roast and cafe first
|
||||||
</h3>
|
</h3>
|
||||||
<p class="mt-2">
|
<p class="mt-2">
|
||||||
Cups need both a roast and a cafe. Add them above to enable this
|
Cups need both a roast and a cafe.
|
||||||
form.
|
{% if roast_options.is_empty() %}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="font-medium text-accent hover:underline"
|
||||||
|
data-on:click="$_addType = 'roast'"
|
||||||
|
>
|
||||||
|
Add a roast
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if roast_options.is_empty() && cafe_options.is_empty() %}and{% endif %}
|
||||||
|
{% if cafe_options.is_empty() %}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="font-medium text-accent hover:underline"
|
||||||
|
data-on:click="$_addType = 'cafe'"
|
||||||
|
>
|
||||||
|
add a cafe
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
to enable this form.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
@ -1162,6 +1239,7 @@
|
||||||
method="post"
|
method="post"
|
||||||
action="/api/v1/cups"
|
action="/api/v1/cups"
|
||||||
class="mt-4 flex flex-col gap-4"
|
class="mt-4 flex flex-col gap-4"
|
||||||
|
onsubmit="sessionStorage.setItem('toast', 'Cup added')"
|
||||||
>
|
>
|
||||||
<div class="grid gap-4 sm:grid-cols-2">
|
<div class="grid gap-4 sm:grid-cols-2">
|
||||||
<div class="flex flex-col gap-1 text-sm">
|
<div class="flex flex-col gap-1 text-sm">
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,8 @@
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
id="add-passkey-error"
|
id="add-passkey-error"
|
||||||
class="mb-3 hidden rounded-md bg-red-100 border border-red-300 p-2 text-sm text-red-800 dark:bg-red-950/40 dark:border-red-800 dark:text-red-300"
|
class="mb-3 hidden rounded-md bg-error-bg border border-error-border p-2 text-sm text-error-text"
|
||||||
|
role="alert"
|
||||||
></div>
|
></div>
|
||||||
<div class="flex items-end gap-3">
|
<div class="flex items-end gap-3">
|
||||||
<label class="flex-1 flex flex-col gap-1 text-sm">
|
<label class="flex-1 flex flex-col gap-1 text-sm">
|
||||||
|
|
@ -82,6 +83,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
id="passkey-name"
|
id="passkey-name"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
placeholder="e.g. MacBook Touch ID, iPhone"
|
placeholder="e.g. MacBook Touch ID, iPhone"
|
||||||
/>
|
/>
|
||||||
|
|
@ -188,7 +190,8 @@
|
||||||
data-show="$_tokenError"
|
data-show="$_tokenError"
|
||||||
data-text="$_tokenError"
|
data-text="$_tokenError"
|
||||||
style="display: none"
|
style="display: none"
|
||||||
class="mb-3 rounded-md bg-red-100 border border-red-300 p-2 text-sm text-red-800 dark:bg-red-950/40 dark:border-red-800 dark:text-red-300"
|
class="mb-3 rounded-md bg-error-bg border border-error-border p-2 text-sm text-error-text"
|
||||||
|
role="alert"
|
||||||
></p>
|
></p>
|
||||||
<form
|
<form
|
||||||
data-on:submit="$_creatingToken = true; $_tokenError = ''; @post('/api/v1/tokens', {contentType: 'form'})"
|
data-on:submit="$_creatingToken = true; $_tokenError = ''; @post('/api/v1/tokens', {contentType: 'form'})"
|
||||||
|
|
@ -204,6 +207,7 @@
|
||||||
name="name"
|
name="name"
|
||||||
id="token-name"
|
id="token-name"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
placeholder="e.g. laptop-cli, ci-server"
|
placeholder="e.g. laptop-cli, ci-server"
|
||||||
/>
|
/>
|
||||||
|
|
@ -230,29 +234,29 @@
|
||||||
<div
|
<div
|
||||||
data-show="$_tokenCreated"
|
data-show="$_tokenCreated"
|
||||||
style="display: none"
|
style="display: none"
|
||||||
class="relative rounded-md border border-green-300 dark:border-green-800 bg-green-50 dark:bg-green-950/40 p-4"
|
class="relative rounded-md border border-success-border bg-success-bg p-4"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick="window.location.reload()"
|
onclick="window.location.reload()"
|
||||||
class="absolute top-3.5 right-3 inline-flex h-6 w-6 items-center justify-center rounded text-green-800 dark:text-green-300 transition hover:text-green-600 dark:hover:text-green-100"
|
class="absolute top-3.5 right-3 inline-flex h-6 w-6 items-center justify-center rounded text-success-text transition hover:text-accent-text"
|
||||||
aria-label="Dismiss"
|
aria-label="Dismiss"
|
||||||
>
|
>
|
||||||
{{ icons::x_mark("h-4 w-4") }}
|
{{ icons::x_mark("h-4 w-4") }}
|
||||||
</button>
|
</button>
|
||||||
<p class="pr-6 text-sm font-medium text-green-800 dark:text-green-300">
|
<p class="pr-6 text-sm font-medium text-success-text">
|
||||||
Token created! Copy it now — it will not be shown again.
|
Token created! Copy it now — it will not be shown again.
|
||||||
</p>
|
</p>
|
||||||
<div class="mt-3 flex items-center gap-2">
|
<div class="mt-3 flex items-center gap-2">
|
||||||
<code
|
<code
|
||||||
id="token-value"
|
id="token-value"
|
||||||
data-text="$_tokenValue"
|
data-text="$_tokenValue"
|
||||||
class="flex-1 rounded bg-surface px-3 py-2 text-sm font-mono text-text border border-green-200 dark:border-green-800 break-all select-all"
|
class="flex-1 rounded bg-surface px-3 py-2 text-sm font-mono text-text border border-success-border break-all select-all"
|
||||||
></code>
|
></code>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick="copyToken(this)"
|
onclick="copyToken(this)"
|
||||||
class="shrink-0 inline-flex items-center gap-2 rounded-md bg-green-600 px-3 py-1.5 text-sm font-medium text-green-50 transition hover:bg-green-500"
|
class="shrink-0 inline-flex items-center gap-2 rounded-md bg-success px-3 py-1.5 text-sm font-medium text-accent-text transition hover:bg-success"
|
||||||
>
|
>
|
||||||
{{ icons::clipboard("h-4 w-4") }} Copy
|
{{ icons::clipboard("h-4 w-4") }} Copy
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -335,11 +339,12 @@
|
||||||
|
|
||||||
<div
|
<div
|
||||||
id="backup-status"
|
id="backup-status"
|
||||||
class="hidden rounded-md border border-green-300 bg-green-50 p-4 text-sm text-green-800 dark:bg-green-950/40 dark:border-green-800 dark:text-green-300"
|
class="hidden rounded-md border border-success-border bg-success-bg p-4 text-sm text-success-text"
|
||||||
></div>
|
></div>
|
||||||
<div
|
<div
|
||||||
id="backup-error"
|
id="backup-error"
|
||||||
class="hidden rounded-md bg-red-100 border border-red-300 p-3 text-sm text-red-800 dark:bg-red-950/40 dark:border-red-800 dark:text-red-300"
|
class="hidden rounded-md bg-error-bg border border-error-border p-3 text-sm text-error-text"
|
||||||
|
role="alert"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium text-red-600 transition hover:bg-surface-alt"
|
class="inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium text-error transition hover:bg-surface-alt"
|
||||||
data-on:click="confirm('Delete this bag? This cannot be undone.') && @delete('/api/v1/bags/{{ bag.id }}')"
|
data-on:click="confirm('Delete this bag? This cannot be undone.') && @delete('/api/v1/bags/{{ bag.id }}')"
|
||||||
>
|
>
|
||||||
{{ icons::delete("h-4 w-4") }} Delete
|
{{ icons::delete("h-4 w-4") }} Delete
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@
|
||||||
<div class="rounded-lg border bg-surface p-5 flex items-center gap-2">
|
<div class="rounded-lg border bg-surface p-5 flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium text-red-600 transition hover:bg-surface-alt"
|
class="inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium text-error transition hover:bg-surface-alt"
|
||||||
data-on:click="confirm('Delete this brew? This cannot be undone.') && @delete('/api/v1/brews/{{ brew.id }}')"
|
data-on:click="confirm('Delete this brew? This cannot be undone.') && @delete('/api/v1/brews/{{ brew.id }}')"
|
||||||
>
|
>
|
||||||
{{ icons::delete("h-4 w-4") }} Delete
|
{{ icons::delete("h-4 w-4") }} Delete
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@
|
||||||
<div class="rounded-lg border bg-surface p-5 flex items-center gap-2">
|
<div class="rounded-lg border bg-surface p-5 flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium text-red-600 transition hover:bg-surface-alt"
|
class="inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium text-error transition hover:bg-surface-alt"
|
||||||
data-on:click="confirm('Delete this cafe? This cannot be undone.') && @delete('/api/v1/cafes/{{ cafe.id }}')"
|
data-on:click="confirm('Delete this cafe? This cannot be undone.') && @delete('/api/v1/cafes/{{ cafe.id }}')"
|
||||||
>
|
>
|
||||||
{{ icons::delete("h-4 w-4") }} Delete
|
{{ icons::delete("h-4 w-4") }} Delete
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,6 @@
|
||||||
{% block title %}
|
{% block title %}
|
||||||
Brewlog · Check In
|
Brewlog · Check In
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block head %}
|
|
||||||
<script>
|
|
||||||
{
|
|
||||||
{
|
|
||||||
location::location_search_js();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section
|
<section
|
||||||
id="checkin-root"
|
id="checkin-root"
|
||||||
|
|
@ -51,7 +42,11 @@
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Step progress -->
|
<!-- Step progress -->
|
||||||
<div class="relative mx-auto mt-6" style="width: 80%">
|
<nav
|
||||||
|
aria-label="Check-in progress"
|
||||||
|
class="relative mx-auto mt-6"
|
||||||
|
style="width: 80%"
|
||||||
|
>
|
||||||
<!-- Background track (continuous line through dot centers) -->
|
<!-- Background track (continuous line through dot centers) -->
|
||||||
<div
|
<div
|
||||||
class="absolute bg-border"
|
class="absolute bg-border"
|
||||||
|
|
@ -79,6 +74,7 @@
|
||||||
<span
|
<span
|
||||||
class="mt-2 text-xs font-medium transition-colors"
|
class="mt-2 text-xs font-medium transition-colors"
|
||||||
data-class="{'text-accent': $_step === 1, 'text-text': $_step > 1}"
|
data-class="{'text-accent': $_step === 1, 'text-text': $_step > 1}"
|
||||||
|
data-attr:aria-current="$_step === 1 ? 'step' : null"
|
||||||
>Cafe</span
|
>Cafe</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -90,6 +86,7 @@
|
||||||
<span
|
<span
|
||||||
class="mt-2 text-xs font-medium transition-colors"
|
class="mt-2 text-xs font-medium transition-colors"
|
||||||
data-class="{'text-accent': $_step === 2, 'text-text': $_step > 2, 'text-text-muted': $_step < 2}"
|
data-class="{'text-accent': $_step === 2, 'text-text': $_step > 2, 'text-text-muted': $_step < 2}"
|
||||||
|
data-attr:aria-current="$_step === 2 ? 'step' : null"
|
||||||
>Coffee</span
|
>Coffee</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -101,11 +98,12 @@
|
||||||
<span
|
<span
|
||||||
class="mt-2 text-xs font-medium transition-colors"
|
class="mt-2 text-xs font-medium transition-colors"
|
||||||
data-class="{'text-accent': $_step === 3, 'text-text-muted': $_step < 3}"
|
data-class="{'text-accent': $_step === 3, 'text-text-muted': $_step < 3}"
|
||||||
|
data-attr:aria-current="$_step === 3 ? 'step' : null"
|
||||||
>Submit</span
|
>Submit</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</nav>
|
||||||
|
|
||||||
<!-- Selected cafe summary (shown after selection) -->
|
<!-- Selected cafe summary (shown after selection) -->
|
||||||
<div
|
<div
|
||||||
|
|
@ -161,10 +159,11 @@
|
||||||
|
|
||||||
<!-- Error display -->
|
<!-- Error display -->
|
||||||
<p
|
<p
|
||||||
class="mt-3 text-sm text-red-600"
|
class="mt-3 text-sm text-error"
|
||||||
data-show="$_error"
|
data-show="$_error"
|
||||||
style="display: none"
|
style="display: none"
|
||||||
data-text="$_error"
|
data-text="$_error"
|
||||||
|
role="alert"
|
||||||
></p>
|
></p>
|
||||||
|
|
||||||
<!-- Step 1: Cafe selection -->
|
<!-- Step 1: Cafe selection -->
|
||||||
|
|
@ -189,6 +188,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
data-bind:_cafe-name
|
data-bind:_cafe-name
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
@ -283,7 +283,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="mb-4 rounded-md bg-green-50 border border-green-200 px-3 py-2 text-sm text-green-800"
|
class="mb-4 rounded-md bg-success-bg border border-success-border px-3 py-2 text-sm text-success-text"
|
||||||
data-show="$_scanSuccess"
|
data-show="$_scanSuccess"
|
||||||
style="display: none"
|
style="display: none"
|
||||||
>
|
>
|
||||||
|
|
@ -345,7 +345,7 @@
|
||||||
|
|
||||||
<form
|
<form
|
||||||
data-on:submit="$_submitting = true; $_error = ''; @post('/api/v1/check-in', {contentType: 'form'})"
|
data-on:submit="$_submitting = true; $_error = ''; @post('/api/v1/check-in', {contentType: 'form'})"
|
||||||
data-on:datastar-fetch="if (!$_submitting) return; if (evt.detail.type === 'error') { $_submitting = false; $_error = 'Check-in failed. Please try again.' }"
|
data-on:datastar-fetch="if (!$_submitting) return; if (evt.detail.type === 'finished') { sessionStorage.setItem('toast', 'Checked in') } else if (evt.detail.type === 'error') { $_submitting = false; $_error = 'Check-in failed. Please try again.' }"
|
||||||
>
|
>
|
||||||
<input type="hidden" name="cafe_id" data-attr:value="$_cafeId" />
|
<input type="hidden" name="cafe_id" data-attr:value="$_cafeId" />
|
||||||
<input type="hidden" name="cafe_name" data-attr:value="$_cafeName" />
|
<input type="hidden" name="cafe_name" data-attr:value="$_cafeName" />
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,12 @@
|
||||||
</div>
|
</div>
|
||||||
{% else if error.is_some() %}
|
{% else if error.is_some() %}
|
||||||
<div class="flex flex-col items-center text-center gap-3">
|
<div class="flex flex-col items-center text-center gap-3">
|
||||||
{{ icons::x_circle("h-10 w-10 text-red-600 dark:text-red-400") }}
|
{{ icons::x_circle("h-10 w-10 text-error") }}
|
||||||
<h1 class="text-2xl font-semibold text-accent">
|
<h1 class="text-2xl font-semibold text-accent">
|
||||||
Authentication Failed
|
Authentication Failed
|
||||||
</h1>
|
</h1>
|
||||||
<div
|
<div
|
||||||
class="w-full rounded-md bg-red-100 border border-red-300 p-3 text-sm text-red-800 dark:bg-red-950/40 dark:border-red-800 dark:text-red-300"
|
class="w-full rounded-md bg-error-bg border border-error-border p-3 text-sm text-error-text"
|
||||||
>
|
>
|
||||||
{{ error.as_ref().unwrap() }}
|
{{ error.as_ref().unwrap() }}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@
|
||||||
<div class="rounded-lg border bg-surface p-5 flex items-center gap-2">
|
<div class="rounded-lg border bg-surface p-5 flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium text-red-600 transition hover:bg-surface-alt"
|
class="inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium text-error transition hover:bg-surface-alt"
|
||||||
data-on:click="confirm('Delete this cup? This cannot be undone.') && @delete('/api/v1/cups/{{ cup.id }}')"
|
data-on:click="confirm('Delete this cup? This cannot be undone.') && @delete('/api/v1/cups/{{ cup.id }}')"
|
||||||
>
|
>
|
||||||
{{ icons::delete("h-4 w-4") }} Delete
|
{{ icons::delete("h-4 w-4") }} Delete
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@
|
||||||
<div class="rounded-lg border bg-surface p-5 flex items-center gap-2">
|
<div class="rounded-lg border bg-surface p-5 flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium text-red-600 transition hover:bg-surface-alt"
|
class="inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium text-error transition hover:bg-surface-alt"
|
||||||
data-on:click="confirm('Delete this gear? This cannot be undone.') && @delete('/api/v1/gear/{{ gear.id }}')"
|
data-on:click="confirm('Delete this gear? This cannot be undone.') && @delete('/api/v1/gear/{{ gear.id }}')"
|
||||||
>
|
>
|
||||||
{{ icons::delete("h-4 w-4") }} Delete
|
{{ icons::delete("h-4 w-4") }} Delete
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,8 @@
|
||||||
data-show="$_extractError"
|
data-show="$_extractError"
|
||||||
data-text="$_extractError"
|
data-text="$_extractError"
|
||||||
style="display:none"
|
style="display:none"
|
||||||
class="mt-2 text-sm text-red-600 text-center"
|
class="mt-2 text-sm text-error text-center"
|
||||||
|
role="alert"
|
||||||
></p>
|
></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -95,7 +96,7 @@
|
||||||
<form
|
<form
|
||||||
class="rounded-lg border bg-surface p-5 flex flex-col gap-6"
|
class="rounded-lg border bg-surface p-5 flex flex-col gap-6"
|
||||||
data-on:submit="$_scanSubmitting = true; $_scanError = ''; @post('/api/v1/scan', {contentType: 'form'})"
|
data-on:submit="$_scanSubmitting = true; $_scanError = ''; @post('/api/v1/scan', {contentType: 'form'})"
|
||||||
data-on:datastar-fetch="if (!$_scanSubmitting) return; if (evt.detail.type === 'finished') { window.location.reload() } else if (evt.detail.type === 'error') { $_scanSubmitting = false; $_scanError = 'Save failed. Please try again.' }"
|
data-on:datastar-fetch="if (!$_scanSubmitting) return; if (evt.detail.type === 'finished') { sessionStorage.setItem('toast', 'Roast saved'); window.location.reload() } else if (evt.detail.type === 'error') { $_scanSubmitting = false; $_scanError = 'Save failed. Please try again.' }"
|
||||||
>
|
>
|
||||||
{% include "partials/forms/scan_result_form.html" %}
|
{% include "partials/forms/scan_result_form.html" %}
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@
|
||||||
|
|
||||||
<div
|
<div
|
||||||
id="login-error"
|
id="login-error"
|
||||||
class="mt-4 hidden rounded-md bg-red-100 border border-red-300 p-3 text-sm text-red-800"
|
class="mt-4 hidden rounded-md bg-error-bg border border-error-border p-3 text-sm text-error-text"
|
||||||
|
role="alert"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@
|
||||||
|
|
||||||
<div
|
<div
|
||||||
id="register-error"
|
id="register-error"
|
||||||
class="mt-4 hidden rounded-md bg-red-100 border border-red-300 p-3 text-sm text-red-800"
|
class="mt-4 hidden rounded-md bg-error-bg border border-error-border p-3 text-sm text-error-text"
|
||||||
|
role="alert"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|
@ -32,6 +33,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
id="display-name"
|
id="display-name"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
autofocus
|
autofocus
|
||||||
class="input-field"
|
class="input-field"
|
||||||
placeholder="Display name"
|
placeholder="Display name"
|
||||||
|
|
@ -44,6 +46,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
id="passkey-name"
|
id="passkey-name"
|
||||||
required
|
required
|
||||||
|
aria-required="true"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
placeholder="e.g. MacBook Touch ID, iPhone"
|
placeholder="e.g. MacBook Touch ID, iPhone"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
<div class="rounded-lg border bg-surface p-5 flex items-center gap-2">
|
<div class="rounded-lg border bg-surface p-5 flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium text-red-600 transition hover:bg-surface-alt"
|
class="inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium text-error transition hover:bg-surface-alt"
|
||||||
data-on:click="confirm('Delete this roast? This cannot be undone.') && @delete('/api/v1/roasts/{{ roast.id }}')"
|
data-on:click="confirm('Delete this roast? This cannot be undone.') && @delete('/api/v1/roasts/{{ roast.id }}')"
|
||||||
>
|
>
|
||||||
{{ icons::delete("h-4 w-4") }} Delete
|
{{ icons::delete("h-4 w-4") }} Delete
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@
|
||||||
<div class="rounded-lg border bg-surface p-5 flex items-center gap-2">
|
<div class="rounded-lg border bg-surface p-5 flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium text-red-600 transition hover:bg-surface-alt"
|
class="inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium text-error transition hover:bg-surface-alt"
|
||||||
data-on:click="confirm('Delete this roaster? This cannot be undone.') && @delete('/api/v1/roasters/{{ roaster.id }}')"
|
data-on:click="confirm('Delete this roaster? This cannot be undone.') && @delete('/api/v1/roasters/{{ roaster.id }}')"
|
||||||
>
|
>
|
||||||
{{ icons::delete("h-4 w-4") }} Delete
|
{{ icons::delete("h-4 w-4") }} Delete
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,12 @@
|
||||||
>
|
>
|
||||||
No more events.
|
No more events.
|
||||||
</p>
|
</p>
|
||||||
<p id="timeline-error" class="text-sm text-red-600" hidden></p>
|
<p
|
||||||
|
id="timeline-error"
|
||||||
|
class="text-sm text-error"
|
||||||
|
role="alert"
|
||||||
|
hidden
|
||||||
|
></p>
|
||||||
</div>
|
</div>
|
||||||
<div id="timeline-sentinel" class="h-1"></div>
|
<div id="timeline-sentinel" class="h-1"></div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,8 @@
|
||||||
data-show="$_scanError"
|
data-show="$_scanError"
|
||||||
data-text="$_scanError"
|
data-text="$_scanError"
|
||||||
style="display:none"
|
style="display:none"
|
||||||
class="text-sm text-red-600"
|
class="text-sm text-error"
|
||||||
|
role="alert"
|
||||||
></p>
|
></p>
|
||||||
<div
|
<div
|
||||||
data-show="$_scanSubmitting"
|
data-show="$_scanSubmitting"
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,11 @@
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="h-2.5 rounded-full bg-surface-alt overflow-hidden"
|
class="h-2.5 rounded-full bg-surface-alt overflow-hidden"
|
||||||
|
role="progressbar"
|
||||||
|
aria-valuenow="{{ bag.used_percent }}"
|
||||||
|
aria-valuemin="0"
|
||||||
|
aria-valuemax="100"
|
||||||
|
aria-label="Coffee used"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="h-full rounded-full bg-accent"
|
class="h-full rounded-full bg-accent"
|
||||||
|
|
@ -95,6 +100,11 @@
|
||||||
<td data-label="" class="card-progress md:hidden">
|
<td data-label="" class="card-progress md:hidden">
|
||||||
<div
|
<div
|
||||||
class="h-1.5 rounded-full bg-surface-alt overflow-hidden"
|
class="h-1.5 rounded-full bg-surface-alt overflow-hidden"
|
||||||
|
role="progressbar"
|
||||||
|
aria-valuenow="{{ bag.used_percent }}"
|
||||||
|
aria-valuemin="0"
|
||||||
|
aria-valuemax="100"
|
||||||
|
aria-label="Coffee used"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="h-full rounded-full bg-accent"
|
class="h-full rounded-full bg-accent"
|
||||||
|
|
@ -129,6 +139,7 @@
|
||||||
</span>
|
</span>
|
||||||
<a
|
<a
|
||||||
href="/bags/{{ bag.id }}"
|
href="/bags/{{ bag.id }}"
|
||||||
|
aria-label="View {{ bag.roast_name }}"
|
||||||
class="inline-flex h-8 w-8 items-center justify-center rounded-md text-text-muted transition hover:text-accent hover:bg-surface-alt"
|
class="inline-flex h-8 w-8 items-center justify-center rounded-md text-text-muted transition hover:text-accent hover:bg-surface-alt"
|
||||||
>
|
>
|
||||||
{{ icons::chevron_right("h-5 w-5") }}
|
{{ icons::chevron_right("h-5 w-5") }}
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,7 @@
|
||||||
<td data-label="" class="card-actions px-4 py-3 text-right">
|
<td data-label="" class="card-actions px-4 py-3 text-right">
|
||||||
<a
|
<a
|
||||||
href="/brews/{{ brew.id }}"
|
href="/brews/{{ brew.id }}"
|
||||||
|
aria-label="View {{ brew.roast_name }}"
|
||||||
class="inline-flex h-8 w-8 items-center justify-center rounded-md text-text-muted transition hover:text-accent hover:bg-surface-alt"
|
class="inline-flex h-8 w-8 items-center justify-center rounded-md text-text-muted transition hover:text-accent hover:bg-surface-alt"
|
||||||
>
|
>
|
||||||
{{ icons::chevron_right("h-5 w-5") }}
|
{{ icons::chevron_right("h-5 w-5") }}
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,7 @@
|
||||||
<td data-label="" class="card-actions px-4 py-3 text-right">
|
<td data-label="" class="card-actions px-4 py-3 text-right">
|
||||||
<a
|
<a
|
||||||
href="{{ cafe.detail_path }}"
|
href="{{ cafe.detail_path }}"
|
||||||
|
aria-label="View {{ cafe.name }}"
|
||||||
class="inline-flex h-8 w-8 items-center justify-center rounded-md text-text-muted transition hover:text-accent hover:bg-surface-alt"
|
class="inline-flex h-8 w-8 items-center justify-center rounded-md text-text-muted transition hover:text-accent hover:bg-surface-alt"
|
||||||
>
|
>
|
||||||
{{ icons::chevron_right("h-5 w-5") }}
|
{{ icons::chevron_right("h-5 w-5") }}
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@
|
||||||
<td data-label="" class="card-actions px-4 py-3 text-right">
|
<td data-label="" class="card-actions px-4 py-3 text-right">
|
||||||
<a
|
<a
|
||||||
href="/cups/{{ cup.id }}"
|
href="/cups/{{ cup.id }}"
|
||||||
|
aria-label="View {{ cup.roast_name }}"
|
||||||
class="inline-flex h-8 w-8 items-center justify-center rounded-md text-text-muted transition hover:text-accent hover:bg-surface-alt"
|
class="inline-flex h-8 w-8 items-center justify-center rounded-md text-text-muted transition hover:text-accent hover:bg-surface-alt"
|
||||||
>
|
>
|
||||||
{{ icons::chevron_right("h-5 w-5") }}
|
{{ icons::chevron_right("h-5 w-5") }}
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@
|
||||||
<td data-label="" class="card-actions px-4 py-3 text-right">
|
<td data-label="" class="card-actions px-4 py-3 text-right">
|
||||||
<a
|
<a
|
||||||
href="/gear/{{ item.id }}"
|
href="/gear/{{ item.id }}"
|
||||||
|
aria-label="View {{ item.full_name }}"
|
||||||
class="inline-flex h-8 w-8 items-center justify-center rounded-md text-text-muted transition hover:text-accent hover:bg-surface-alt"
|
class="inline-flex h-8 w-8 items-center justify-center rounded-md text-text-muted transition hover:text-accent hover:bg-surface-alt"
|
||||||
>
|
>
|
||||||
{{ icons::chevron_right("h-5 w-5") }}
|
{{ icons::chevron_right("h-5 w-5") }}
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,7 @@
|
||||||
<td data-label="" class="card-actions px-4 py-3 text-right">
|
<td data-label="" class="card-actions px-4 py-3 text-right">
|
||||||
<a
|
<a
|
||||||
href="{{ roast.detail_path }}"
|
href="{{ roast.detail_path }}"
|
||||||
|
aria-label="View {{ roast.name }}"
|
||||||
class="inline-flex h-8 w-8 items-center justify-center rounded-md text-text-muted transition hover:text-accent hover:bg-surface-alt"
|
class="inline-flex h-8 w-8 items-center justify-center rounded-md text-text-muted transition hover:text-accent hover:bg-surface-alt"
|
||||||
>
|
>
|
||||||
{{ icons::chevron_right("h-5 w-5") }}
|
{{ icons::chevron_right("h-5 w-5") }}
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,7 @@
|
||||||
<td data-label="" class="card-actions px-4 py-3 text-right">
|
<td data-label="" class="card-actions px-4 py-3 text-right">
|
||||||
<a
|
<a
|
||||||
href="{{ roaster.detail_path }}"
|
href="{{ roaster.detail_path }}"
|
||||||
|
aria-label="View {{ roaster.name }}"
|
||||||
class="inline-flex h-8 w-8 items-center justify-center rounded-md text-text-muted transition hover:text-accent hover:bg-surface-alt"
|
class="inline-flex h-8 w-8 items-center justify-center rounded-md text-text-muted transition hover:text-accent hover:bg-surface-alt"
|
||||||
>
|
>
|
||||||
{{ icons::chevron_right("h-5 w-5") }}
|
{{ icons::chevron_right("h-5 w-5") }}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@
|
||||||
data-show="{{ error_signal }}"
|
data-show="{{ error_signal }}"
|
||||||
data-text="{{ error_signal }}"
|
data-text="{{ error_signal }}"
|
||||||
style="display:none"
|
style="display:none"
|
||||||
class="mt-2 text-sm text-red-600"
|
class="mt-2 text-sm text-error"
|
||||||
|
role="alert"
|
||||||
></p>
|
></p>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue