refactor: reorganize templates into pages/, partials/lists/, and forms/
Move 9 page templates to templates/pages/, nav to partials/, 7 list partials + table.html to partials/lists/. Extract duplicated scan result form (~85 lines) from home.html and add.html into partials/forms/scan_result_form.html. Update all Askama template paths, include/import directives.
This commit is contained in:
parent
4e917cee75
commit
56109f3afe
24 changed files with 110 additions and 192 deletions
|
|
@ -69,7 +69,7 @@ fn format_date(dt: DateTime<Utc>) -> String {
|
||||||
// --- Templates ---
|
// --- Templates ---
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "account.html")]
|
#[template(path = "pages/account.html")]
|
||||||
struct AccountTemplate {
|
struct AccountTemplate {
|
||||||
nav_active: &'static str,
|
nav_active: &'static str,
|
||||||
is_authenticated: bool,
|
is_authenticated: bool,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ pub struct LoginQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "login.html")]
|
#[template(path = "pages/login.html")]
|
||||||
struct LoginTemplate {
|
struct LoginTemplate {
|
||||||
nav_active: &'static str,
|
nav_active: &'static str,
|
||||||
is_authenticated: bool,
|
is_authenticated: bool,
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ const SESSION_COOKIE_NAME: &str = "brewlog_session";
|
||||||
// --- Templates ---
|
// --- Templates ---
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "register.html")]
|
#[template(path = "pages/register.html")]
|
||||||
struct RegisterTemplate {
|
struct RegisterTemplate {
|
||||||
nav_active: &'static str,
|
nav_active: &'static str,
|
||||||
is_authenticated: bool,
|
is_authenticated: bool,
|
||||||
|
|
@ -34,7 +34,7 @@ struct RegisterTemplate {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "cli_callback.html")]
|
#[template(path = "pages/cli_callback.html")]
|
||||||
struct CliCallbackTemplate {
|
struct CliCallbackTemplate {
|
||||||
nav_active: &'static str,
|
nav_active: &'static str,
|
||||||
is_authenticated: bool,
|
is_authenticated: bool,
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ use crate::domain::roasts::{RoastSortKey, RoastWithRoaster};
|
||||||
use crate::domain::timeline::TimelineSortKey;
|
use crate::domain::timeline::TimelineSortKey;
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "partials/roaster_list.html")]
|
#[template(path = "partials/lists/roaster_list.html")]
|
||||||
pub struct RoasterListTemplate {
|
pub struct RoasterListTemplate {
|
||||||
pub is_authenticated: bool,
|
pub is_authenticated: bool,
|
||||||
pub roasters: Paginated<RoasterView>,
|
pub roasters: Paginated<RoasterView>,
|
||||||
|
|
@ -23,7 +23,7 @@ pub struct RoasterListTemplate {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "partials/roast_list.html")]
|
#[template(path = "partials/lists/roast_list.html")]
|
||||||
pub struct RoastListTemplate {
|
pub struct RoastListTemplate {
|
||||||
pub is_authenticated: bool,
|
pub is_authenticated: bool,
|
||||||
pub roasts: Paginated<RoastView>,
|
pub roasts: Paginated<RoastView>,
|
||||||
|
|
@ -31,7 +31,7 @@ pub struct RoastListTemplate {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "timeline.html")]
|
#[template(path = "pages/timeline.html")]
|
||||||
pub struct TimelineTemplate {
|
pub struct TimelineTemplate {
|
||||||
pub nav_active: &'static str,
|
pub nav_active: &'static str,
|
||||||
pub is_authenticated: bool,
|
pub is_authenticated: bool,
|
||||||
|
|
@ -52,7 +52,7 @@ pub struct TimelineChunkTemplate {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "partials/bag_list.html")]
|
#[template(path = "partials/lists/bag_list.html")]
|
||||||
pub struct BagListTemplate {
|
pub struct BagListTemplate {
|
||||||
pub is_authenticated: bool,
|
pub is_authenticated: bool,
|
||||||
pub bags: Paginated<BagView>,
|
pub bags: Paginated<BagView>,
|
||||||
|
|
@ -60,7 +60,7 @@ pub struct BagListTemplate {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "partials/gear_list.html")]
|
#[template(path = "partials/lists/gear_list.html")]
|
||||||
pub struct GearListTemplate {
|
pub struct GearListTemplate {
|
||||||
pub is_authenticated: bool,
|
pub is_authenticated: bool,
|
||||||
pub gear: Paginated<GearView>,
|
pub gear: Paginated<GearView>,
|
||||||
|
|
@ -74,7 +74,7 @@ pub struct RoastOptionsTemplate {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "partials/brew_list.html")]
|
#[template(path = "partials/lists/brew_list.html")]
|
||||||
pub struct BrewListTemplate {
|
pub struct BrewListTemplate {
|
||||||
pub is_authenticated: bool,
|
pub is_authenticated: bool,
|
||||||
pub brews: Paginated<BrewView>,
|
pub brews: Paginated<BrewView>,
|
||||||
|
|
@ -82,7 +82,7 @@ pub struct BrewListTemplate {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "partials/cafe_list.html")]
|
#[template(path = "partials/lists/cafe_list.html")]
|
||||||
pub struct CafeListTemplate {
|
pub struct CafeListTemplate {
|
||||||
pub is_authenticated: bool,
|
pub is_authenticated: bool,
|
||||||
pub cafes: Paginated<CafeView>,
|
pub cafes: Paginated<CafeView>,
|
||||||
|
|
@ -90,7 +90,7 @@ pub struct CafeListTemplate {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "partials/cup_list.html")]
|
#[template(path = "partials/lists/cup_list.html")]
|
||||||
pub struct CupListTemplate {
|
pub struct CupListTemplate {
|
||||||
pub is_authenticated: bool,
|
pub is_authenticated: bool,
|
||||||
pub cups: Paginated<CupView>,
|
pub cups: Paginated<CupView>,
|
||||||
|
|
@ -98,7 +98,7 @@ pub struct CupListTemplate {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "home.html")]
|
#[template(path = "pages/home.html")]
|
||||||
pub struct HomeTemplate {
|
pub struct HomeTemplate {
|
||||||
pub nav_active: &'static str,
|
pub nav_active: &'static str,
|
||||||
pub is_authenticated: bool,
|
pub is_authenticated: bool,
|
||||||
|
|
@ -111,7 +111,7 @@ pub struct HomeTemplate {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "checkin.html")]
|
#[template(path = "pages/checkin.html")]
|
||||||
pub struct CheckInTemplate {
|
pub struct CheckInTemplate {
|
||||||
pub nav_active: &'static str,
|
pub nav_active: &'static str,
|
||||||
pub is_authenticated: bool,
|
pub is_authenticated: bool,
|
||||||
|
|
@ -128,7 +128,7 @@ pub struct NearbyCafesFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "data.html")]
|
#[template(path = "pages/data.html")]
|
||||||
pub struct DataTemplate {
|
pub struct DataTemplate {
|
||||||
pub nav_active: &'static str,
|
pub nav_active: &'static str,
|
||||||
pub is_authenticated: bool,
|
pub is_authenticated: bool,
|
||||||
|
|
@ -150,7 +150,7 @@ pub struct Tab {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "add.html")]
|
#[template(path = "pages/add.html")]
|
||||||
pub struct AddTemplate {
|
pub struct AddTemplate {
|
||||||
pub nav_active: &'static str,
|
pub nav_active: &'static str,
|
||||||
pub is_authenticated: bool,
|
pub is_authenticated: bool,
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@
|
||||||
</head>
|
</head>
|
||||||
<body class="min-h-screen bg-page text-stone-900">
|
<body class="min-h-screen bg-page text-stone-900">
|
||||||
<main class="mx-auto flex w-full max-w-5xl flex-col gap-8 px-6 py-10">
|
<main class="mx-auto flex w-full max-w-5xl flex-col gap-8 px-6 py-10">
|
||||||
{% include "nav.html" %} {% block content %}{% endblock %}
|
{% include "partials/nav.html" %} {% block content %}{% endblock %}
|
||||||
<footer class="mt-8 text-center text-xs tracking-widest text-stone-400" style="font-variant: small-caps">
|
<footer class="mt-8 text-center text-xs tracking-widest text-stone-400" style="font-variant: small-caps">
|
||||||
<p>B{rew}log by <a href="https://jnsgr.uk" class="text-stone-500 hover:text-accent transition" target="_blank" rel="noreferrer noopener">jnsgruk</a> · {{ version_info.version }} · <a href="https://github.com/jnsgruk/brewlog/commit/{{ version_info.commit }}" class="text-stone-500 hover:text-accent transition" target="_blank" rel="noreferrer noopener">{{ version_info.commit }}</a></p>
|
<p>B{rew}log by <a href="https://jnsgr.uk" class="text-stone-500 hover:text-accent transition" target="_blank" rel="noreferrer noopener">jnsgruk</a> · {{ version_info.version }} · <a href="https://github.com/jnsgruk/brewlog/commit/{{ version_info.commit }}" class="text-stone-500 hover:text-accent transition" target="_blank" rel="noreferrer noopener">{{ version_info.commit }}</a></p>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
|
||||||
|
|
@ -44,90 +44,7 @@
|
||||||
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') { $_scanSubmitting = false; window.location.href = '/data?type=bags' } 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') { $_scanSubmitting = false; window.location.href = '/data?type=bags' } else if (evt.detail.type === 'error') { $_scanSubmitting = false; $_scanError = 'Save failed. Please try again.' }"
|
||||||
>
|
>
|
||||||
<div>
|
{% include "partials/forms/scan_result_form.html" %}
|
||||||
<h3 class="text-base font-semibold text-stone-800">Roaster</h3>
|
|
||||||
<p class="mt-1 text-sm text-stone-600">If this roaster already exists, it will be matched automatically.</p>
|
|
||||||
<div class="mt-4 grid gap-4 sm:grid-cols-2">
|
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">Name *</span>
|
|
||||||
<input type="text" name="roaster_name" required class="input-field" placeholder="Example Coffee Roasters" data-bind:_roaster-name />
|
|
||||||
</label>
|
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">Country *</span>
|
|
||||||
<input type="text" name="roaster_country" required class="input-field" placeholder="United States" data-bind:_roaster-country />
|
|
||||||
</label>
|
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">City</span>
|
|
||||||
<input type="text" name="roaster_city" class="input-field" placeholder="Portland" data-bind:_roaster-city />
|
|
||||||
</label>
|
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">Homepage</span>
|
|
||||||
<input type="url" name="roaster_homepage" class="input-field" placeholder="https://example.coffee" data-bind:_roaster-homepage />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h3 class="text-base font-semibold text-stone-800">Roast</h3>
|
|
||||||
<p class="mt-1 text-sm text-stone-600">Details about this specific coffee.</p>
|
|
||||||
<div class="mt-4 grid gap-4 sm:grid-cols-2">
|
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">Roast Name *</span>
|
|
||||||
<input type="text" name="roast_name" required class="input-field" placeholder="Ethiopia Yirgacheffe" data-bind:_roast-name />
|
|
||||||
</label>
|
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">Origin *</span>
|
|
||||||
<input type="text" name="origin" required class="input-field" placeholder="Ethiopia" data-bind:_origin />
|
|
||||||
</label>
|
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">Region *</span>
|
|
||||||
<input type="text" name="region" required class="input-field" placeholder="Guji" data-bind:_region />
|
|
||||||
</label>
|
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">Producer *</span>
|
|
||||||
<input type="text" name="producer" required class="input-field" placeholder="Chelbesa Cooperative" data-bind:_producer />
|
|
||||||
</label>
|
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">Process *</span>
|
|
||||||
<input type="text" name="process" required class="input-field" placeholder="Washed" data-bind:_process />
|
|
||||||
</label>
|
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">Tasting Notes * (comma separated)</span>
|
|
||||||
<textarea name="tasting_notes" rows="2" required class="input-field" placeholder="Blueberry, Jasmine" data-bind:_tasting-notes></textarea>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label class="inline-flex items-center gap-2 text-sm cursor-pointer">
|
|
||||||
<input type="checkbox" name="open_bag" value="true" class="accent-orange-700" data-bind:_open-bag />
|
|
||||||
<span class="font-semibold text-stone-800">Open a bag of this coffee</span>
|
|
||||||
</label>
|
|
||||||
<div data-show="$_openBag" class="mt-3 grid gap-4 sm:grid-cols-2">
|
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">Amount (grams)</span>
|
|
||||||
<input type="number" name="bag_amount" min="1" step="any" class="input-field" placeholder="250" data-bind:_bag-amount />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p data-show="$_scanError" data-text="$_scanError" style="display:none" class="text-sm text-red-600"></p>
|
|
||||||
<div data-show="$_scanSubmitting" style="display:none" class="flex items-center gap-3 text-sm text-stone-800">
|
|
||||||
{% call icons::spinner("h-5 w-5") %}
|
|
||||||
Saving…
|
|
||||||
</div>
|
|
||||||
<div data-show="!$_scanSubmitting" class="flex items-center justify-end gap-2">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
data-on:click="$_scanExtracted = false"
|
|
||||||
class="rounded-md border px-4 py-2 text-sm font-semibold text-stone-700 transition hover:border-accent hover:text-stone-800"
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
class="rounded-md bg-accent px-4 py-2 text-sm font-semibold text-accent-text transition hover:bg-accent-hover"
|
|
||||||
>
|
|
||||||
Save Roaster & Roast
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -66,90 +66,7 @@
|
||||||
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') { $_scanSubmitting = false; $_scanExtracted = false; $_roasterName = ''; $_roasterCountry = ''; $_roasterCity = ''; $_roasterHomepage = ''; $_roastName = ''; $_origin = ''; $_region = ''; $_producer = ''; $_process = ''; $_tastingNotes = ''; $_openBag = true; $_bagAmount = 250; 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') { $_scanSubmitting = false; $_scanExtracted = false; $_roasterName = ''; $_roasterCountry = ''; $_roasterCity = ''; $_roasterHomepage = ''; $_roastName = ''; $_origin = ''; $_region = ''; $_producer = ''; $_process = ''; $_tastingNotes = ''; $_openBag = true; $_bagAmount = 250; window.location.reload() } else if (evt.detail.type === 'error') { $_scanSubmitting = false; $_scanError = 'Save failed. Please try again.' }"
|
||||||
>
|
>
|
||||||
<div>
|
{% include "partials/forms/scan_result_form.html" %}
|
||||||
<h3 class="text-base font-semibold text-stone-800">Roaster</h3>
|
|
||||||
<p class="mt-1 text-sm text-stone-600">If this roaster already exists, it will be matched automatically.</p>
|
|
||||||
<div class="mt-4 grid gap-4 sm:grid-cols-2">
|
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">Name *</span>
|
|
||||||
<input type="text" name="roaster_name" required class="input-field" placeholder="Example Coffee Roasters" data-bind:_roaster-name />
|
|
||||||
</label>
|
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">Country *</span>
|
|
||||||
<input type="text" name="roaster_country" required class="input-field" placeholder="United States" data-bind:_roaster-country />
|
|
||||||
</label>
|
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">City</span>
|
|
||||||
<input type="text" name="roaster_city" class="input-field" placeholder="Portland" data-bind:_roaster-city />
|
|
||||||
</label>
|
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">Homepage</span>
|
|
||||||
<input type="url" name="roaster_homepage" class="input-field" placeholder="https://example.coffee" data-bind:_roaster-homepage />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h3 class="text-base font-semibold text-stone-800">Roast</h3>
|
|
||||||
<p class="mt-1 text-sm text-stone-600">Details about this specific coffee.</p>
|
|
||||||
<div class="mt-4 grid gap-4 sm:grid-cols-2">
|
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">Roast Name *</span>
|
|
||||||
<input type="text" name="roast_name" required class="input-field" placeholder="Ethiopia Yirgacheffe" data-bind:_roast-name />
|
|
||||||
</label>
|
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">Origin *</span>
|
|
||||||
<input type="text" name="origin" required class="input-field" placeholder="Ethiopia" data-bind:_origin />
|
|
||||||
</label>
|
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">Region *</span>
|
|
||||||
<input type="text" name="region" required class="input-field" placeholder="Guji" data-bind:_region />
|
|
||||||
</label>
|
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">Producer *</span>
|
|
||||||
<input type="text" name="producer" required class="input-field" placeholder="Chelbesa Cooperative" data-bind:_producer />
|
|
||||||
</label>
|
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">Process *</span>
|
|
||||||
<input type="text" name="process" required class="input-field" placeholder="Washed" data-bind:_process />
|
|
||||||
</label>
|
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">Tasting Notes * (comma separated)</span>
|
|
||||||
<textarea name="tasting_notes" rows="2" required class="input-field" placeholder="Blueberry, Jasmine" data-bind:_tasting-notes></textarea>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label class="inline-flex items-center gap-2 text-sm cursor-pointer">
|
|
||||||
<input type="checkbox" name="open_bag" value="true" class="accent-orange-700" data-bind:_open-bag />
|
|
||||||
<span class="font-semibold text-stone-800">Open a bag of this coffee</span>
|
|
||||||
</label>
|
|
||||||
<div data-show="$_openBag" class="mt-3 grid gap-4 sm:grid-cols-2">
|
|
||||||
<label class="flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">Amount (grams)</span>
|
|
||||||
<input type="number" name="bag_amount" min="1" step="any" class="input-field" placeholder="250" data-bind:_bag-amount />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p data-show="$_scanError" data-text="$_scanError" style="display:none" class="text-sm text-red-600"></p>
|
|
||||||
<div data-show="$_scanSubmitting" style="display:none" class="flex items-center gap-3 text-sm text-accent">
|
|
||||||
{% call icons::spinner("h-5 w-5") %}
|
|
||||||
Saving…
|
|
||||||
</div>
|
|
||||||
<div data-show="!$_scanSubmitting" class="flex items-center justify-end gap-2">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
data-on:click="$_scanExtracted = false"
|
|
||||||
class="rounded-md border px-4 py-2 text-sm font-semibold text-stone-700 transition hover:bg-surface-alt"
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
class="rounded-md bg-accent px-4 py-2 text-sm font-semibold text-accent-text transition hover:bg-accent-hover"
|
|
||||||
>
|
|
||||||
Save Roaster & Roast
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
84
templates/partials/forms/scan_result_form.html
Normal file
84
templates/partials/forms/scan_result_form.html
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
<div>
|
||||||
|
<h3 class="text-base font-semibold text-stone-800">Roaster</h3>
|
||||||
|
<p class="mt-1 text-sm text-stone-600">If this roaster already exists, it will be matched automatically.</p>
|
||||||
|
<div class="mt-4 grid gap-4 sm:grid-cols-2">
|
||||||
|
<label class="flex flex-col gap-1 text-sm">
|
||||||
|
<span class="text-stone-700">Name *</span>
|
||||||
|
<input type="text" name="roaster_name" required class="input-field" placeholder="Example Coffee Roasters" data-bind:_roaster-name />
|
||||||
|
</label>
|
||||||
|
<label class="flex flex-col gap-1 text-sm">
|
||||||
|
<span class="text-stone-700">Country *</span>
|
||||||
|
<input type="text" name="roaster_country" required class="input-field" placeholder="United States" data-bind:_roaster-country />
|
||||||
|
</label>
|
||||||
|
<label class="flex flex-col gap-1 text-sm">
|
||||||
|
<span class="text-stone-700">City</span>
|
||||||
|
<input type="text" name="roaster_city" class="input-field" placeholder="Portland" data-bind:_roaster-city />
|
||||||
|
</label>
|
||||||
|
<label class="flex flex-col gap-1 text-sm">
|
||||||
|
<span class="text-stone-700">Homepage</span>
|
||||||
|
<input type="url" name="roaster_homepage" class="input-field" placeholder="https://example.coffee" data-bind:_roaster-homepage />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 class="text-base font-semibold text-stone-800">Roast</h3>
|
||||||
|
<p class="mt-1 text-sm text-stone-600">Details about this specific coffee.</p>
|
||||||
|
<div class="mt-4 grid gap-4 sm:grid-cols-2">
|
||||||
|
<label class="flex flex-col gap-1 text-sm">
|
||||||
|
<span class="text-stone-700">Roast Name *</span>
|
||||||
|
<input type="text" name="roast_name" required class="input-field" placeholder="Ethiopia Yirgacheffe" data-bind:_roast-name />
|
||||||
|
</label>
|
||||||
|
<label class="flex flex-col gap-1 text-sm">
|
||||||
|
<span class="text-stone-700">Origin *</span>
|
||||||
|
<input type="text" name="origin" required class="input-field" placeholder="Ethiopia" data-bind:_origin />
|
||||||
|
</label>
|
||||||
|
<label class="flex flex-col gap-1 text-sm">
|
||||||
|
<span class="text-stone-700">Region *</span>
|
||||||
|
<input type="text" name="region" required class="input-field" placeholder="Guji" data-bind:_region />
|
||||||
|
</label>
|
||||||
|
<label class="flex flex-col gap-1 text-sm">
|
||||||
|
<span class="text-stone-700">Producer *</span>
|
||||||
|
<input type="text" name="producer" required class="input-field" placeholder="Chelbesa Cooperative" data-bind:_producer />
|
||||||
|
</label>
|
||||||
|
<label class="flex flex-col gap-1 text-sm">
|
||||||
|
<span class="text-stone-700">Process *</span>
|
||||||
|
<input type="text" name="process" required class="input-field" placeholder="Washed" data-bind:_process />
|
||||||
|
</label>
|
||||||
|
<label class="flex flex-col gap-1 text-sm">
|
||||||
|
<span class="text-stone-700">Tasting Notes * (comma separated)</span>
|
||||||
|
<textarea name="tasting_notes" rows="2" required class="input-field" placeholder="Blueberry, Jasmine" data-bind:_tasting-notes></textarea>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="inline-flex items-center gap-2 text-sm cursor-pointer">
|
||||||
|
<input type="checkbox" name="open_bag" value="true" class="accent-orange-700" data-bind:_open-bag />
|
||||||
|
<span class="font-semibold text-stone-800">Open a bag of this coffee</span>
|
||||||
|
</label>
|
||||||
|
<div data-show="$_openBag" class="mt-3 grid gap-4 sm:grid-cols-2">
|
||||||
|
<label class="flex flex-col gap-1 text-sm">
|
||||||
|
<span class="text-stone-700">Amount (grams)</span>
|
||||||
|
<input type="number" name="bag_amount" min="1" step="any" class="input-field" placeholder="250" data-bind:_bag-amount />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p data-show="$_scanError" data-text="$_scanError" style="display:none" class="text-sm text-red-600"></p>
|
||||||
|
<div data-show="$_scanSubmitting" style="display:none" class="flex items-center gap-3 text-sm text-accent">
|
||||||
|
{% call icons::spinner("h-5 w-5") %}
|
||||||
|
Saving…
|
||||||
|
</div>
|
||||||
|
<div data-show="!$_scanSubmitting" class="flex items-center justify-end gap-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-on:click="$_scanExtracted = false"
|
||||||
|
class="rounded-md border px-4 py-2 text-sm font-semibold text-stone-700 transition hover:bg-surface-alt"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="rounded-md bg-accent px-4 py-2 text-sm font-semibold text-accent-text transition hover:bg-accent-hover"
|
||||||
|
>
|
||||||
|
Save Roaster & Roast
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{% import "partials/table.html" as table %}
|
{% import "partials/lists/table.html" as table %}
|
||||||
{% import "partials/icons.html" as icons %}
|
{% import "partials/icons.html" as icons %}
|
||||||
|
|
||||||
<div id="bag-list" class="mt-6" data-star-scope="bags">
|
<div id="bag-list" class="mt-6" data-star-scope="bags">
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{% import "partials/table.html" as table %}
|
{% import "partials/lists/table.html" as table %}
|
||||||
{% import "partials/icons.html" as icons %}
|
{% import "partials/icons.html" as icons %}
|
||||||
|
|
||||||
<div id="brew-list" class="mt-6" data-star-scope="brews">
|
<div id="brew-list" class="mt-6" data-star-scope="brews">
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{% import "partials/table.html" as table %}
|
{% import "partials/lists/table.html" as table %}
|
||||||
{% import "partials/icons.html" as icons %}
|
{% import "partials/icons.html" as icons %}
|
||||||
|
|
||||||
<div id="cafe-list" class="mt-6" data-star-scope="cafes">
|
<div id="cafe-list" class="mt-6" data-star-scope="cafes">
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{% import "partials/table.html" as table %}
|
{% import "partials/lists/table.html" as table %}
|
||||||
{% import "partials/icons.html" as icons %}
|
{% import "partials/icons.html" as icons %}
|
||||||
|
|
||||||
<div id="cup-list" class="mt-6" data-star-scope="cups">
|
<div id="cup-list" class="mt-6" data-star-scope="cups">
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{% import "partials/table.html" as table %}
|
{% import "partials/lists/table.html" as table %}
|
||||||
{% import "partials/icons.html" as icons %}
|
{% import "partials/icons.html" as icons %}
|
||||||
|
|
||||||
<div id="gear-list" class="mt-6" data-star-scope="gear">
|
<div id="gear-list" class="mt-6" data-star-scope="gear">
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{% import "partials/table.html" as table %}
|
{% import "partials/lists/table.html" as table %}
|
||||||
{% import "partials/icons.html" as icons %}
|
{% import "partials/icons.html" as icons %}
|
||||||
|
|
||||||
<div id="roast-list" class="mt-6" data-star-scope="roasts">
|
<div id="roast-list" class="mt-6" data-star-scope="roasts">
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{% import "partials/table.html" as table %}
|
{% import "partials/lists/table.html" as table %}
|
||||||
{% import "partials/icons.html" as icons %}
|
{% import "partials/icons.html" as icons %}
|
||||||
|
|
||||||
<div id="roaster-list" class="mt-6" data-star-scope="roasters">
|
<div id="roaster-list" class="mt-6" data-star-scope="roasters">
|
||||||
Loading…
Reference in a new issue