Add {contentType: 'form'} to all edit template @put() calls so form
inputs are submitted by name rather than as Datastar signals. Replace
the quick notes text input on the brew edit page with toggle pill
buttons matching the add form. Add CLI tests for brew and cup update
commands.
164 lines
5.6 KiB
HTML
164 lines
5.6 KiB
HTML
{% extends "base.html" %}
|
|
{% import "partials/icons.html" as icons %}
|
|
{% import "partials/image_section.html" as img %}
|
|
{% block title %}Brewlog · Edit Roast{% endblock %}
|
|
|
|
{% block content %}
|
|
<header class="flex flex-col gap-2">
|
|
<h1 class="text-3xl font-semibold">Edit Roast</h1>
|
|
<p class="max-w-2xl text-sm text-text-secondary">Update roast details.</p>
|
|
</header>
|
|
|
|
<section class="rounded-lg border bg-surface p-5">
|
|
<form
|
|
class="flex flex-col gap-4"
|
|
data-signals:_submitting="false"
|
|
data-signals:_submit-error="''"
|
|
data-signals:_name="'{{ name }}'"
|
|
data-signals:_origin="'{{ origin }}'"
|
|
data-signals:_region="'{{ region }}'"
|
|
data-signals:_producer="'{{ producer }}'"
|
|
data-signals:_process="'{{ process }}'"
|
|
data-signals:_tasting-notes="'{{ tasting_notes }}'"
|
|
data-on:submit="$_submitting = true; $_submitError = ''; @put('/api/v1/roasts/{{ id }}', {contentType: 'form'})"
|
|
data-on:datastar-fetch="if (!$_submitting) return;
|
|
if (evt.detail.type === 'finished') { $_submitting = false; sessionStorage.setItem('toast', 'Roast updated'); window.location.href = evt.detail.response.headers.get('location') || '/roasts/{{ id }}' }
|
|
else if (evt.detail.type === 'error') { $_submitting = false; $_submitError = 'Failed to save changes.' }"
|
|
>
|
|
<div class="flex flex-col gap-1 text-sm">
|
|
<span
|
|
class="text-xs font-semibold text-text-muted uppercase tracking-wide"
|
|
>Roaster*</span
|
|
>
|
|
<searchable-select
|
|
name="roaster_id"
|
|
placeholder="Type to search roasters…"
|
|
initial-value="{{ roaster_id }}"
|
|
>
|
|
{% for roaster in roaster_options %}
|
|
<button
|
|
type="button"
|
|
value="{{ roaster.id }}"
|
|
data-display="{{ roaster.name }}"
|
|
class="w-full px-3 py-2 text-left text-sm hover:bg-surface-alt transition"
|
|
>
|
|
<span class="font-medium text-text">{{ roaster.name }}</span>
|
|
</button>
|
|
{% endfor %}
|
|
</searchable-select>
|
|
</div>
|
|
<div class="grid gap-4 sm:grid-cols-2">
|
|
<label class="flex flex-col gap-1 text-sm">
|
|
<span
|
|
class="text-xs font-semibold text-text-muted uppercase tracking-wide"
|
|
>Roast Name*</span
|
|
>
|
|
<input
|
|
type="text"
|
|
name="name"
|
|
required
|
|
aria-required="true"
|
|
class="input-field"
|
|
placeholder="Ethiopia Yirgacheffe"
|
|
data-bind:_name
|
|
/>
|
|
</label>
|
|
<label class="flex flex-col gap-1 text-sm">
|
|
<span
|
|
class="text-xs font-semibold text-text-muted uppercase tracking-wide"
|
|
>Origin*</span
|
|
>
|
|
<input
|
|
type="text"
|
|
name="origin"
|
|
required
|
|
aria-required="true"
|
|
class="input-field"
|
|
placeholder="Ethiopia"
|
|
data-bind:_origin
|
|
/>
|
|
</label>
|
|
<label class="flex flex-col gap-1 text-sm">
|
|
<span
|
|
class="text-xs font-semibold text-text-muted uppercase tracking-wide"
|
|
>Region*</span
|
|
>
|
|
<input
|
|
type="text"
|
|
name="region"
|
|
required
|
|
aria-required="true"
|
|
class="input-field"
|
|
placeholder="Guji"
|
|
data-bind:_region
|
|
/>
|
|
</label>
|
|
<label class="flex flex-col gap-1 text-sm">
|
|
<span
|
|
class="text-xs font-semibold text-text-muted uppercase tracking-wide"
|
|
>Producer*</span
|
|
>
|
|
<input
|
|
type="text"
|
|
name="producer"
|
|
required
|
|
aria-required="true"
|
|
class="input-field"
|
|
placeholder="Chelbesa Cooperative"
|
|
data-bind:_producer
|
|
/>
|
|
</label>
|
|
<label class="flex flex-col gap-1 text-sm">
|
|
<span
|
|
class="text-xs font-semibold text-text-muted uppercase tracking-wide"
|
|
>Process*</span
|
|
>
|
|
<input
|
|
type="text"
|
|
name="process"
|
|
required
|
|
aria-required="true"
|
|
class="input-field"
|
|
placeholder="Washed"
|
|
data-bind:_process
|
|
/>
|
|
</label>
|
|
<label class="sm:col-span-2 flex flex-col gap-1 text-sm">
|
|
<span
|
|
class="text-xs font-semibold text-text-muted uppercase tracking-wide"
|
|
>Tasting Notes* (comma or newline separated)</span
|
|
>
|
|
<textarea
|
|
name="tasting_notes"
|
|
rows="2"
|
|
required
|
|
aria-required="true"
|
|
class="input-field"
|
|
placeholder="Blueberry, Jasmine"
|
|
data-bind:_tasting-notes
|
|
></textarea>
|
|
</label>
|
|
</div>
|
|
{{ img::deferred_upload("edit-roast-image", "Roast Image") }}
|
|
<p
|
|
data-show="$_submitError"
|
|
data-text="$_submitError"
|
|
style="display:none"
|
|
class="text-sm text-error"
|
|
role="alert"
|
|
></p>
|
|
<div class="flex items-center justify-end">
|
|
<button
|
|
type="submit"
|
|
class="inline-flex items-center justify-center gap-2 rounded-md bg-accent px-4 py-2 text-sm font-semibold text-accent-text transition hover:bg-accent-hover disabled:opacity-50"
|
|
data-attr:disabled="$_submitting"
|
|
>
|
|
<span data-show="$_submitting" style="display:none"
|
|
>{{ icons::spinner("h-4 w-4") }}</span
|
|
>
|
|
Save Changes
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</section>
|
|
{% endblock %}
|