refactor(templates): extract inline SVG icons into shared icons partial

Replace duplicated inline SVGs across 16 templates with reusable Askama
macros from a new icons.html partial, reducing repetition and making
icon updates a single-point change.
This commit is contained in:
Jon Seager 2026-02-04 16:32:18 +00:00
parent a31c91211c
commit 3bbcd33933
No known key found for this signature in database
17 changed files with 152 additions and 211 deletions

View file

@ -1,4 +1,4 @@
{% extends "base.html" %} {% block title %}Brewlog · Cafe · {{ cafe.name }}{% endblock %} {% {% extends "base.html" %} {% import "partials/icons.html" as icons %} {% block title %}Brewlog · Cafe · {{ cafe.name }}{% endblock %} {%
block content %} block content %}
<header class="flex flex-col gap-2"> <header class="flex flex-col gap-2">
<h1 class="text-3xl font-semibold">{{ cafe.name }}</h1> <h1 class="text-3xl font-semibold">{{ cafe.name }}</h1>
@ -28,9 +28,7 @@ block content %}
rel="noreferrer noopener" rel="noreferrer noopener"
aria-label="Open {{ cafe.name }} in Google Maps" aria-label="Open {{ cafe.name }} in Google Maps"
> >
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::location("h-4 w-4") %}
<path fill-rule="evenodd" d="M9.69 18.933l.003.001C9.89 19.02 10 19 10 19s.11.02.308-.066l.002-.001.006-.003.018-.008a5.741 5.741 0 00.281-.14c.186-.096.446-.24.757-.433.62-.384 1.445-.966 2.274-1.765C15.302 14.988 17 12.493 17 9A7 7 0 103 9c0 3.492 1.698 5.988 3.355 7.584a13.731 13.731 0 002.273 1.765 11.842 11.842 0 00.976.544l.062.029.018.008.006.003zM10 11.25a2.25 2.25 0 100-4.5 2.25 2.25 0 000 4.5z" clip-rule="evenodd" />
</svg>
<span>Open in Maps</span> <span>Open in Maps</span>
</a> </a>
</dd> </dd>
@ -46,16 +44,7 @@ block content %}
rel="noreferrer noopener" rel="noreferrer noopener"
aria-label="Visit {{ cafe.name }} website" aria-label="Visit {{ cafe.name }} website"
> >
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::external_link("h-4 w-4") %}
<path
fill-rule="evenodd"
d="M11.3 2a.7.7 0 0 0 0 1.4h3.3l-8.1 8.1a.7.7 0 1 0 1 1l8.1-8.1v3.3a.7.7 0 1 0 1.4 0V2.7A.7.7 0 0 0 16.3 2h-5Z"
clip-rule="evenodd"
/>
<path
d="M4.7 5.2a1.5 1.5 0 0 1 1.5-1.5h2.1a.7.7 0 1 0 0-1.4H6.2a2.9 2.9 0 0 0-2.9 2.9v7.6a2.9 2.9 0 0 0 2.9 2.9h7.6a2.9 2.9 0 0 0 2.9-2.9v-2.1a.7.7 0 1 0-1.4 0v2.1a1.5 1.5 0 0 1-1.5 1.5H6.1a1.5 1.5 0 0 1-1.5-1.5V5.2Z"
/>
</svg>
<span>Visit</span> <span>Visit</span>
</a> </a>
{% else %} {% else %}

View file

@ -1,4 +1,4 @@
{% extends "base.html" %} {% block title %}Brewlog · Cafes{% endblock %} {% extends "base.html" %} {% import "partials/icons.html" as icons %} {% block title %}Brewlog · Cafes{% endblock %}
{% block head %} {% block head %}
{% if is_authenticated %} {% if is_authenticated %}
@ -211,9 +211,7 @@
onclick="locateUser(this)" onclick="locateUser(this)"
class="inline-flex items-center gap-2 rounded-md border border-amber-500 px-3 py-2 text-sm font-medium text-amber-700 transition hover:bg-amber-50" class="inline-flex items-center gap-2 rounded-md border border-amber-500 px-3 py-2 text-sm font-medium text-amber-700 transition hover:bg-amber-50"
> >
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::location("h-4 w-4") %}
<path fill-rule="evenodd" d="M9.69 18.933l.003.001C9.89 19.02 10 19 10 19s.11.02.308-.066l.002-.001.006-.003.018-.008a5.741 5.741 0 00.281-.14c.186-.096.446-.24.757-.433.62-.384 1.445-.966 2.274-1.765C15.302 14.988 17 12.493 17 9A7 7 0 103 9c0 3.492 1.698 5.988 3.355 7.584a13.731 13.731 0 002.273 1.765 11.842 11.842 0 00.976.544l.062.029.018.008.006.003zM10 11.25a2.25 2.25 0 100-4.5 2.25 2.25 0 000 4.5z" clip-rule="evenodd" />
</svg>
Find nearby Find nearby
</button> </button>
<label class="inline-flex items-center gap-1.5 text-sm text-stone-600 cursor-pointer select-none"> <label class="inline-flex items-center gap-1.5 text-sm text-stone-600 cursor-pointer select-none">
@ -238,10 +236,9 @@
placeholder="Search for a cafe&hellip;" placeholder="Search for a cafe&hellip;"
autocomplete="off" autocomplete="off"
/> />
<svg id="nearby-spinner" class="hidden absolute right-2 top-1/2 -translate-y-1/2 h-4 w-4 animate-spin text-amber-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" aria-hidden="true"> <span id="nearby-spinner" class="hidden absolute right-2 top-1/2 -translate-y-1/2">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> {% call icons::spinner("h-4 w-4") %}
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path> </span>
</svg>
</div> </div>
</div> </div>
<p id="nearby-error" class="hidden mt-2 text-sm text-red-600"></p> <p id="nearby-error" class="hidden mt-2 text-sm text-red-600"></p>

View file

@ -1,4 +1,4 @@
{% extends "base.html" %} {% block title %}Brewlog · Check In{% endblock %} {% extends "base.html" %} {% import "partials/icons.html" as icons %} {% block title %}Brewlog · Check In{% endblock %}
{% block head %} {% block head %}
<script> <script>
@ -76,9 +76,7 @@ function locateUser() {
style="display: none" style="display: none"
> >
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<svg class="h-4 w-4 text-amber-600 shrink-0" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::location("h-4 w-4 text-amber-600 shrink-0") %}
<path fill-rule="evenodd" d="M9.69 18.933l.003.001C9.89 19.02 10 19 10 19s.11.02.308-.066l.002-.001.006-.003.018-.008a5.741 5.741 0 00.281-.14c.186-.096.446-.24.757-.433.62-.384 1.445-.966 2.274-1.765C15.302 14.988 17 12.493 17 9A7 7 0 103 9c0 3.492 1.698 5.988 3.355 7.584a13.731 13.731 0 002.273 1.765 11.842 11.842 0 00.976.544l.062.029.018.008.006.003ZM10 11.25a2.25 2.25 0 100-4.5 2.25 2.25 0 000 4.5Z" clip-rule="evenodd" />
</svg>
<span class="font-medium text-amber-800" data-text="$_cafeName"></span> <span class="font-medium text-amber-800" data-text="$_cafeName"></span>
<span class="text-xs text-stone-500" data-show="$_cafeCity" data-text="$_cafeCity" style="display: none"></span> <span class="text-xs text-stone-500" data-show="$_cafeCity" data-text="$_cafeCity" style="display: none"></span>
</div> </div>
@ -96,9 +94,7 @@ function locateUser() {
style="display: none" style="display: none"
> >
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<svg class="h-4 w-4 text-amber-600 shrink-0" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"> {% call icons::bag("h-4 w-4 text-amber-600 shrink-0") %}
<path fill-rule="evenodd" d="M2 6a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2h2a3 3 0 0 1 0 6h-1.1A5.002 5.002 0 0 1 14 16H6a5 5 0 0 1-5-5V6Zm16 4h2a1 1 0 1 0 0-2h-2v2ZM4 20a1 1 0 0 1 1-1h10a1 1 0 1 1 0 2H5a1 1 0 0 1-1-1Z" clip-rule="evenodd" />
</svg>
<span class="font-medium text-amber-800" data-text="$_roastName"></span> <span class="font-medium text-amber-800" data-text="$_roastName"></span>
<span class="text-xs text-stone-500" data-show="$_roasterName" data-text="$_roasterName" style="display: none"></span> <span class="text-xs text-stone-500" data-show="$_roasterName" data-text="$_roasterName" style="display: none"></span>
</div> </div>
@ -155,16 +151,11 @@ function locateUser() {
class="inline-flex items-center gap-2 rounded-md border border-amber-500 px-4 py-2.5 text-sm font-medium text-amber-700 transition hover:bg-amber-50" class="inline-flex items-center gap-2 rounded-md border border-amber-500 px-4 py-2.5 text-sm font-medium text-amber-700 transition hover:bg-amber-50"
data-attr:disabled="$_locating || $_locationFound" data-attr:disabled="$_locating || $_locationFound"
> >
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::location("h-4 w-4") %}
<path fill-rule="evenodd" d="M9.69 18.933l.003.001C9.89 19.02 10 19 10 19s.11.02.308-.066l.002-.001.006-.003.018-.008a5.741 5.741 0 00.281-.14c.186-.096.446-.24.757-.433.62-.384 1.445-.966 2.274-1.765C15.302 14.988 17 12.493 17 9A7 7 0 103 9c0 3.492 1.698 5.988 3.355 7.584a13.731 13.731 0 002.273 1.765 11.842 11.842 0 00.976.544l.062.029.018.008.006.003ZM10 11.25a2.25 2.25 0 100-4.5 2.25 2.25 0 000 4.5Z" clip-rule="evenodd" />
</svg>
<span data-text="$_locating ? 'Locating\u2026' : $_locationFound ? 'Location found' : 'Use My Location'">Use My Location</span> <span data-text="$_locating ? 'Locating\u2026' : $_locationFound ? 'Location found' : 'Use My Location'">Use My Location</span>
</button> </button>
<div data-show="!$_searchByCity && $_locating" style="display: none"> <div data-show="!$_searchByCity && $_locating" style="display: none">
<svg class="h-5 w-5 animate-spin text-amber-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" aria-hidden="true"> {% call icons::spinner("h-5 w-5") %}
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
</svg>
</div> </div>
<input <input
type="text" type="text"
@ -281,9 +272,7 @@ function locateUser() {
onclick="document.getElementById('checkin-photo').click()" onclick="document.getElementById('checkin-photo').click()"
class="inline-flex items-center gap-2 rounded-md border border-amber-500 px-4 py-2.5 text-sm font-medium text-amber-700 transition hover:bg-amber-50" class="inline-flex items-center gap-2 rounded-md border border-amber-500 px-4 py-2.5 text-sm font-medium text-amber-700 transition hover:bg-amber-50"
> >
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::camera("h-5 w-5") %}
<path fill-rule="evenodd" d="M1 8a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 018.07 3h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0016.07 6H17a2 2 0 012 2v7a2 2 0 01-2 2H3a2 2 0 01-2-2V8zm13.5 3a4.5 4.5 0 11-9 0 4.5 4.5 0 019 0zM10 14a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd" />
</svg>
Scan Bag Scan Bag
</button> </button>
<span class="text-xs text-stone-400">or</span> <span class="text-xs text-stone-400">or</span>
@ -312,10 +301,7 @@ function locateUser() {
data-show="$_scanWaiting" data-show="$_scanWaiting"
style="display: none" style="display: none"
> >
<svg class="h-5 w-5 animate-spin text-amber-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" aria-hidden="true"> {% call icons::spinner("h-5 w-5") %}
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
</svg>
Scanning &amp; saving&hellip; Scanning &amp; saving&hellip;
</div> </div>
@ -356,9 +342,7 @@ function locateUser() {
data-class:text-stone-300="$_rating < {{ i }}" data-class:text-stone-300="$_rating < {{ i }}"
aria-label="Rate {{ i }} stars" aria-label="Rate {{ i }} stars"
> >
<svg class="h-8 w-8" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::star("h-8 w-8") %}
<path fill-rule="evenodd" d="M10.868 2.884c-.321-.772-1.415-.772-1.736 0l-1.83 4.401-4.753.381c-.833.067-1.171 1.107-.536 1.651l3.62 3.102-1.106 4.637c-.194.813.691 1.456 1.405 1.02L10 15.591l4.069 2.485c.713.436 1.598-.207 1.404-1.02l-1.106-4.637 3.62-3.102c.635-.544.297-1.584-.536-1.65l-4.752-.382-1.831-4.401Z" clip-rule="evenodd" />
</svg>
</button> </button>
{% endfor %} {% endfor %}
</div> </div>

View file

@ -1,4 +1,4 @@
{% extends "base.html" %} {% import "partials/bag_card.html" as bag_card %} {% block title %}Brewlog{% endblock %} {% extends "base.html" %} {% import "partials/bag_card.html" as bag_card %} {% import "partials/icons.html" as icons %} {% block title %}Brewlog{% endblock %}
{% block head %} {% block head %}
{% if is_authenticated %} {% if is_authenticated %}
@ -43,19 +43,14 @@
data-on:click="$_showScan = !$_showScan; !$_showScan && ($_scanExtracted = false, $_extracting = false, $_extractError = '', $_scanError = '')" data-on:click="$_showScan = !$_showScan; !$_showScan && ($_scanExtracted = false, $_extracting = false, $_extractError = '', $_scanError = '')"
class="flex items-center justify-center gap-3 rounded-lg border-2 border-amber-500 px-6 py-5 text-lg font-semibold text-amber-700 shadow-sm transition hover:bg-amber-50" class="flex items-center justify-center gap-3 rounded-lg border-2 border-amber-500 px-6 py-5 text-lg font-semibold text-amber-700 shadow-sm transition hover:bg-amber-50"
> >
<svg class="h-6 w-6" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::camera("h-6 w-6") %}
<path fill-rule="evenodd" d="M1 8a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 018.07 3h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0016.07 6H17a2 2 0 012 2v7a2 2 0 01-2 2H3a2 2 0 01-2-2V8zm13.5 3a4.5 4.5 0 11-9 0 4.5 4.5 0 019 0zM10 14a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd" />
</svg>
Scan Bag Scan Bag
</button> </button>
<a <a
href="/check-in" href="/check-in"
class="flex items-center justify-center gap-3 rounded-lg border-2 border-amber-500 px-6 py-5 text-lg font-semibold text-amber-700 shadow-sm transition hover:bg-amber-50" class="flex items-center justify-center gap-3 rounded-lg border-2 border-amber-500 px-6 py-5 text-lg font-semibold text-amber-700 shadow-sm transition hover:bg-amber-50"
> >
<svg class="h-6 w-6" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::pen("h-6 w-6") %}
<path d="M15.993 1.385a1.87 1.87 0 0 1 2.623 2.622l-4.03 5.27a12.75 12.75 0 0 1-4.223 3.358 7.724 7.724 0 0 1-2.71.832 2.14 2.14 0 0 1-.588-.085 1.07 1.07 0 0 1-.725-.723 2.14 2.14 0 0 1-.083-.588 7.724 7.724 0 0 1 .832-2.71 12.75 12.75 0 0 1 3.358-4.223l5.27-4.03.176.177-.176-.177Z" />
<path d="M6 6.878V6a2.25 2.25 0 0 1 2.25-2.25h7.5A2.25 2.25 0 0 1 18 6v3.75a2.25 2.25 0 0 1-2.25 2.25h-.878a15.262 15.262 0 0 0 .878 3h.75a3.75 3.75 0 0 0 3.75-3.75V6a3.75 3.75 0 0 0-3.75-3.75h-7.5A3.75 3.75 0 0 0 5.25 6v.75a15.262 15.262 0 0 0 .75.128Z" />
</svg>
Check In Check In
</a> </a>
</section> </section>
@ -90,9 +85,7 @@
onclick="document.getElementById('scan-photo').click()" onclick="document.getElementById('scan-photo').click()"
class="inline-flex items-center gap-2 rounded-md border border-amber-500 px-4 py-3 text-sm font-medium text-amber-700 transition hover:bg-amber-50" class="inline-flex items-center gap-2 rounded-md border border-amber-500 px-4 py-3 text-sm font-medium text-amber-700 transition hover:bg-amber-50"
> >
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::camera("h-5 w-5") %}
<path fill-rule="evenodd" d="M1 8a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 018.07 3h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0016.07 6H17a2 2 0 012 2v7a2 2 0 01-2 2H3a2 2 0 01-2-2V8zm13.5 3a4.5 4.5 0 11-9 0 4.5 4.5 0 019 0zM10 14a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd" />
</svg>
Take Photo Take Photo
</button> </button>
<span class="text-xs text-stone-400">or</span> <span class="text-xs text-stone-400">or</span>
@ -112,10 +105,7 @@
</div> </div>
</div> </div>
<div data-show="$_extracting" style="display:none" class="flex items-center gap-3 text-sm text-amber-700"> <div data-show="$_extracting" style="display:none" class="flex items-center gap-3 text-sm text-amber-700">
<svg class="h-5 w-5 animate-spin text-amber-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" aria-hidden="true"> {% call icons::spinner("h-5 w-5") %}
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
</svg>
Waiting for response&hellip; Waiting for response&hellip;
</div> </div>
<p data-show="$_extractError" data-text="$_extractError" style="display:none" class="mt-2 text-sm text-red-600"></p> <p data-show="$_extractError" data-text="$_extractError" style="display:none" class="mt-2 text-sm text-red-600"></p>
@ -183,10 +173,7 @@
</div> </div>
<p data-show="$_scanError" data-text="$_scanError" style="display:none" class="text-sm text-red-600"></p> <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-amber-700"> <div data-show="$_scanSubmitting" style="display:none" class="flex items-center gap-3 text-sm text-amber-700">
<svg class="h-5 w-5 animate-spin text-amber-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" aria-hidden="true"> {% call icons::spinner("h-5 w-5") %}
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
</svg>
Saving&hellip; Saving&hellip;
</div> </div>
<div data-show="!$_scanSubmitting" class="flex items-center justify-end gap-2"> <div data-show="!$_scanSubmitting" class="flex items-center justify-end gap-2">
@ -251,9 +238,7 @@
type="submit" type="submit"
class="inline-flex items-center gap-2 rounded-md bg-amber-600 px-4 py-2 text-sm font-semibold text-amber-50 transition hover:bg-amber-500" class="inline-flex items-center gap-2 rounded-md bg-amber-600 px-4 py-2 text-sm font-semibold text-amber-50 transition hover:bg-amber-500"
> >
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::refresh("h-4 w-4") %}
<path fill-rule="evenodd" d="M15.312 11.424a5.5 5.5 0 0 1-9.201 2.466l-.312-.311h2.433a.75.75 0 0 0 0-1.5H3.989a.75.75 0 0 0-.75.75v4.242a.75.75 0 0 0 1.5 0v-2.43l.31.31a7 7 0 0 0 11.712-3.138.75.75 0 0 0-1.449-.39Zm1.23-3.723a.75.75 0 0 0 .219-.53V2.929a.75.75 0 0 0-1.5 0v2.43l-.31-.31A7 7 0 0 0 3.239 8.188a.75.75 0 1 0 1.448.389 5.5 5.5 0 0 1 9.2-2.466l.312.311h-2.433a.75.75 0 0 0 0 1.5h4.243a.75.75 0 0 0 .53-.22Z" clip-rule="evenodd" />
</svg>
Brew Again Brew Again
</button> </button>
</form> </form>

View file

@ -1,3 +1,4 @@
{% import "partials/icons.html" as icons %}
<nav class="relative rounded-lg border border-amber-300 bg-amber-100/80 p-3 text-sm text-stone-600" data-signals:_nav-open="false"> <nav class="relative rounded-lg border border-amber-300 bg-amber-100/80 p-3 text-sm text-stone-600" data-signals:_nav-open="false">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="font-semibold uppercase tracking-[0.25em] text-amber-700"><a href="/">B{rew}log</a></div> <div class="font-semibold uppercase tracking-[0.25em] text-amber-700"><a href="/">B{rew}log</a></div>
@ -12,9 +13,7 @@
<a class="border-b-2 pb-1 transition {% if nav_active == "gear" %}text-amber-700 border-amber-500{% else %}text-stone-500 border-transparent hover:text-amber-600 hover:border-amber-400{% endif %}" href="/gear">Gear</a> <a class="border-b-2 pb-1 transition {% if nav_active == "gear" %}text-amber-700 border-amber-500{% else %}text-stone-500 border-transparent hover:text-amber-600 hover:border-amber-400{% endif %}" href="/gear">Gear</a>
<a class="border-b-2 pb-1 transition {% if nav_active == "timeline" %}text-amber-700 border-amber-500{% else %}text-stone-500 border-transparent hover:text-amber-600 hover:border-amber-400{% endif %}" href="/timeline">Timeline</a> <a class="border-b-2 pb-1 transition {% if nav_active == "timeline" %}text-amber-700 border-amber-500{% else %}text-stone-500 border-transparent hover:text-amber-600 hover:border-amber-400{% endif %}" href="/timeline">Timeline</a>
<a class="border-b-2 pb-1 transition {% if nav_active == "home" %}text-amber-700 border-amber-500{% else %}text-stone-500 border-transparent hover:text-amber-600 hover:border-amber-400{% endif %}" href="/" aria-label="Home"> <a class="border-b-2 pb-1 transition {% if nav_active == "home" %}text-amber-700 border-amber-500{% else %}text-stone-500 border-transparent hover:text-amber-600 hover:border-amber-400{% endif %}" href="/" aria-label="Home">
<svg class="inline h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::home("inline h-4 w-4") %}
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z" />
</svg>
</a> </a>
{% if is_authenticated %} {% if is_authenticated %}
<form method="post" action="/logout" class="inline"> <form method="post" action="/logout" class="inline">
@ -26,12 +25,8 @@
</div> </div>
<!-- Hamburger button (mobile only) --> <!-- Hamburger button (mobile only) -->
<button class="md:hidden p-1 text-amber-700" data-on:click="$_navOpen = !$_navOpen" aria-label="Toggle menu"> <button class="md:hidden p-1 text-amber-700" data-on:click="$_navOpen = !$_navOpen" aria-label="Toggle menu">
<svg data-show="!$_navOpen" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> <span data-show="!$_navOpen">{% call icons::hamburger("h-6 w-6") %}</span>
<path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16" /> <span data-show="$_navOpen" style="display:none">{% call icons::close("h-6 w-6") %}</span>
</svg>
<svg data-show="$_navOpen" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" style="display:none">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button> </button>
</div> </div>
<!-- Mobile menu --> <!-- Mobile menu -->
@ -45,9 +40,7 @@
<a class="py-1 transition {% if nav_active == "gear" %}text-amber-700 font-medium{% else %}text-stone-500 hover:text-amber-600{% endif %}" href="/gear">Gear</a> <a class="py-1 transition {% if nav_active == "gear" %}text-amber-700 font-medium{% else %}text-stone-500 hover:text-amber-600{% endif %}" href="/gear">Gear</a>
<a class="py-1 transition {% if nav_active == "timeline" %}text-amber-700 font-medium{% else %}text-stone-500 hover:text-amber-600{% endif %}" href="/timeline">Timeline</a> <a class="py-1 transition {% if nav_active == "timeline" %}text-amber-700 font-medium{% else %}text-stone-500 hover:text-amber-600{% endif %}" href="/timeline">Timeline</a>
<a class="py-1 transition inline-flex items-center gap-1 {% if nav_active == "home" %}text-amber-700 font-medium{% else %}text-stone-500 hover:text-amber-600{% endif %}" href="/"> <a class="py-1 transition inline-flex items-center gap-1 {% if nav_active == "home" %}text-amber-700 font-medium{% else %}text-stone-500 hover:text-amber-600{% endif %}" href="/">
<svg class="inline h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::home("inline h-4 w-4") %}
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z" />
</svg>
Home Home
</a> </a>
{% if is_authenticated %} {% if is_authenticated %}

View file

@ -1,3 +1,4 @@
{% import "partials/icons.html" as icons %}
{% macro card(bag, is_authenticated) %} {% macro card(bag, is_authenticated) %}
<div id="bag-card-{{ bag.id }}" class="rounded-lg border border-amber-200 bg-amber-50 p-4 shadow-sm"> <div id="bag-card-{{ bag.id }}" class="rounded-lg border border-amber-200 bg-amber-50 p-4 shadow-sm">
<div> <div>
@ -11,9 +12,7 @@
{% if is_authenticated %} {% if is_authenticated %}
<div class="mt-3 pt-3 border-t border-amber-100 flex gap-3"> <div class="mt-3 pt-3 border-t border-amber-100 flex gap-3">
<a href="/brews?bag_id={{ bag.id }}" class="inline-flex items-center gap-1 text-sm font-medium text-amber-700 hover:text-amber-500"> <a href="/brews?bag_id={{ bag.id }}" class="inline-flex items-center gap-1 text-sm font-medium text-amber-700 hover:text-amber-500">
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::plus_circle("h-4 w-4") %}
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm.75-11.25a.75.75 0 00-1.5 0v2.5h-2.5a.75.75 0 000 1.5h2.5v2.5a.75.75 0 001.5 0v-2.5h2.5a.75.75 0 000-1.5h-2.5v-2.5z" clip-rule="evenodd" />
</svg>
Brew Brew
</a> </a>
<button <button
@ -21,9 +20,7 @@
class="inline-flex items-center gap-1 text-sm font-medium text-stone-500 hover:text-stone-700" class="inline-flex items-center gap-1 text-sm font-medium text-stone-500 hover:text-stone-700"
onclick="closeBag('{{ bag.id }}', document.getElementById('bag-card-{{ bag.id }}'))" onclick="closeBag('{{ bag.id }}', document.getElementById('bag-card-{{ bag.id }}'))"
> >
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::x_circle("h-4 w-4") %}
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd" />
</svg>
Close Close
</button> </button>
</div> </div>

View file

@ -1,4 +1,5 @@
{% import "partials/table.html" as table %} {% import "partials/table.html" as table %}
{% 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">
{% if brews.items.is_empty() && !navigator.has_search() %} {% if brews.items.is_empty() && !navigator.has_search() %}
@ -92,13 +93,7 @@
title="Brew again" title="Brew again"
> >
<span class="sr-only">Brew again</span> <span class="sr-only">Brew again</span>
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::refresh("h-4 w-4") %}
<path
fill-rule="evenodd"
d="M15.312 11.424a5.5 5.5 0 0 1-9.201 2.466l-.312-.311h2.433a.75.75 0 0 0 0-1.5H3.989a.75.75 0 0 0-.75.75v4.242a.75.75 0 0 0 1.5 0v-2.43l.31.31a7 7 0 0 0 11.712-3.138.75.75 0 0 0-1.449-.39Zm1.23-3.723a.75.75 0 0 0 .219-.53V2.929a.75.75 0 0 0-1.5 0v2.43l-.31-.31A7 7 0 0 0 3.239 8.188a.75.75 0 1 0 1.448.389 5.5 5.5 0 0 1 9.2-2.466l.312.311h-2.433a.75.75 0 0 0 0 1.5h4.243a.75.75 0 0 0 .53-.22Z"
clip-rule="evenodd"
/>
</svg>
</button> </button>
</form> </form>
<button <button
@ -108,13 +103,7 @@
data-on:click="confirm('Delete this brew?') && @delete('/api/v1/brews/{{ brew.id }}?{{ navigator.query() }}', {responseOverrides: {selector: '#brew-list', mode: 'replace'}})" data-on:click="confirm('Delete this brew?') && @delete('/api/v1/brews/{{ brew.id }}?{{ navigator.query() }}', {responseOverrides: {selector: '#brew-list', mode: 'replace'}})"
> >
<span class="sr-only">Delete</span> <span class="sr-only">Delete</span>
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::delete("h-4 w-4") %}
<path
fill-rule="evenodd"
d="M7.5 3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1H15a1 1 0 1 1 0 2h-.4l-.74 10.36A2 2 0 0 1 11.87 17H8.13a2 2 0 0 1-1.99-1.64L5.4 5H5a1 1 0 1 1 0-2h2.5Zm.9 4.5a.75.75 0 0 1 .75.75v6a.75.75 0 1 1-1.5 0v-6a.75.75 0 0 1 .75-.75Zm3.4 0a.75.75 0 0 1 .75.75v6a.75.75 0 1 1-1.5 0v-6a.75.75 0 0 1 .75-.75Z"
clip-rule="evenodd"
/>
</svg>
</button> </button>
</div> </div>
</td> </td>

View file

@ -1,4 +1,5 @@
{% import "partials/table.html" as table %} {% import "partials/table.html" as table %}
{% 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">
{% if cafes.items.is_empty() && !navigator.has_search() %} {% if cafes.items.is_empty() && !navigator.has_search() %}
@ -59,9 +60,7 @@
title="Open in Google Maps" title="Open in Google Maps"
> >
<span class="sr-only">Open {{ cafe.name }} in Google Maps</span> <span class="sr-only">Open {{ cafe.name }} in Google Maps</span>
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::location("h-4 w-4") %}
<path fill-rule="evenodd" d="M9.69 18.933l.003.001C9.89 19.02 10 19 10 19s.11.02.308-.066l.002-.001.006-.003.018-.008a5.741 5.741 0 00.281-.14c.186-.096.446-.24.757-.433.62-.384 1.445-.966 2.274-1.765C15.302 14.988 17 12.493 17 9A7 7 0 103 9c0 3.492 1.698 5.988 3.355 7.584a13.731 13.731 0 002.273 1.765 11.842 11.842 0 00.976.544l.062.029.018.008.006.003zM10 11.25a2.25 2.25 0 100-4.5 2.25 2.25 0 000 4.5z" clip-rule="evenodd" />
</svg>
</a> </a>
{% if cafe.has_website %} {% if cafe.has_website %}
<a <a
@ -72,16 +71,7 @@
title="Visit website" title="Visit website"
> >
<span class="sr-only">Visit {{ cafe.name }} website</span> <span class="sr-only">Visit {{ cafe.name }} website</span>
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::external_link("h-4 w-4") %}
<path
fill-rule="evenodd"
d="M11.3 2a.7.7 0 0 0 0 1.4h3.3l-8.1 8.1a.7.7 0 1 0 1 1l8.1-8.1v3.3a.7.7 0 1 0 1.4 0V2.7A.7.7 0 0 0 16.3 2h-5Z"
clip-rule="evenodd"
/>
<path
d="M4.7 5.2a1.5 1.5 0 0 1 1.5-1.5h2.1a.7.7 0 1 0 0-1.4H6.2a2.9 2.9 0 0 0-2.9 2.9v7.6a2.9 2.9 0 0 0 2.9 2.9h7.6a2.9 2.9 0 0 0 2.9-2.9v-2.1a.7.7 0 1 0-1.4 0v2.1a1.5 1.5 0 0 1-1.5 1.5H6.1a1.5 1.5 0 0 1-1.5-1.5V5.2Z"
/>
</svg>
</a> </a>
{% else %} {% else %}
<span <span
@ -89,16 +79,7 @@
title="No website" title="No website"
aria-disabled="true" aria-disabled="true"
> >
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::external_link("h-4 w-4") %}
<path
fill-rule="evenodd"
d="M11.3 2a.7.7 0 0 0 0 1.4h3.3l-8.1 8.1a.7.7 0 1 0 1 1l8.1-8.1v3.3a.7.7 0 1 0 1.4 0V2.7A.7.7 0 0 0 16.3 2h-5Z"
clip-rule="evenodd"
/>
<path
d="M4.7 5.2a1.5 1.5 0 0 1 1.5-1.5h2.1a.7.7 0 1 0 0-1.4H6.2a2.9 2.9 0 0 0-2.9 2.9v7.6a2.9 2.9 0 0 0 2.9 2.9h7.6a2.9 2.9 0 0 0 2.9-2.9v-2.1a.7.7 0 1 0-1.4 0v2.1a1.5 1.5 0 0 1-1.5 1.5H6.1a1.5 1.5 0 0 1-1.5-1.5V5.2Z"
/>
</svg>
</span> </span>
{% endif %} {% endif %}
{% if is_authenticated %} {% if is_authenticated %}
@ -109,13 +90,7 @@
data-on:click="confirm('Delete this cafe?') && @delete('/api/v1/cafes/{{ cafe.id }}?{{ navigator.query() }}', {responseOverrides: {selector: '#cafe-list', mode: 'replace'}})" data-on:click="confirm('Delete this cafe?') && @delete('/api/v1/cafes/{{ cafe.id }}?{{ navigator.query() }}', {responseOverrides: {selector: '#cafe-list', mode: 'replace'}})"
> >
<span class="sr-only">Delete</span> <span class="sr-only">Delete</span>
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::delete("h-4 w-4") %}
<path
fill-rule="evenodd"
d="M7.5 3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1H15a1 1 0 1 1 0 2h-.4l-.74 10.36A2 2 0 0 1 11.87 17H8.13a2 2 0 0 1-1.99-1.64L5.4 5H5a1 1 0 1 1 0-2h2.5Zm.9 4.5a.75.75 0 0 1 .75.75v6a.75.75 0 1 1-1.5 0v-6a.75.75 0 0 1 .75-.75Zm3.4 0a.75.75 0 0 1 .75.75v6a.75.75 0 1 1-1.5 0v-6a.75.75 0 0 1 .75-.75Z"
clip-rule="evenodd"
/>
</svg>
</button> </button>
{% endif %} {% endif %}
</div> </div>

View file

@ -1,4 +1,5 @@
{% import "partials/table.html" as table %} {% import "partials/table.html" as table %}
{% 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">
{% if cups.items.is_empty() && !navigator.has_search() %} {% if cups.items.is_empty() && !navigator.has_search() %}
@ -61,13 +62,7 @@
data-on:click="confirm('Delete this cup?') && @delete('/api/v1/cups/{{ cup.id }}?{{ navigator.query() }}', {responseOverrides: {selector: '#cup-list', mode: 'replace'}})" data-on:click="confirm('Delete this cup?') && @delete('/api/v1/cups/{{ cup.id }}?{{ navigator.query() }}', {responseOverrides: {selector: '#cup-list', mode: 'replace'}})"
> >
<span class="sr-only">Delete</span> <span class="sr-only">Delete</span>
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::delete("h-4 w-4") %}
<path
fill-rule="evenodd"
d="M7.5 3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1H15a1 1 0 1 1 0 2h-.4l-.74 10.36A2 2 0 0 1 11.87 17H8.13a2 2 0 0 1-1.99-1.64L5.4 5H5a1 1 0 1 1 0-2h2.5Zm.9 4.5a.75.75 0 0 1 .75.75v6a.75.75 0 1 1-1.5 0v-6a.75.75 0 0 1 .75-.75Zm3.4 0a.75.75 0 0 1 .75.75v6a.75.75 0 1 1-1.5 0v-6a.75.75 0 0 1 .75-.75Z"
clip-rule="evenodd"
/>
</svg>
</button> </button>
</div> </div>
{% endif %} {% endif %}

View file

@ -1,4 +1,5 @@
{% import "partials/table.html" as table %} {% import "partials/table.html" as table %}
{% 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">
{% if gear.items.is_empty() && !navigator.has_search() %} {% if gear.items.is_empty() && !navigator.has_search() %}
@ -54,9 +55,7 @@
data-on:click="confirm('Delete this gear?') && @delete('/api/v1/gear/{{ item.id }}?{{ navigator.query() }}', {responseOverrides: {selector: '#gear-list', mode: 'replace'}})" data-on:click="confirm('Delete this gear?') && @delete('/api/v1/gear/{{ item.id }}?{{ navigator.query() }}', {responseOverrides: {selector: '#gear-list', mode: 'replace'}})"
> >
<span class="sr-only">Delete</span> <span class="sr-only">Delete</span>
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::delete("h-4 w-4") %}
<path fill-rule="evenodd" d="M7.5 3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1H15a1 1 0 1 1 0 2h-.4l-.74 10.36A2 2 0 0 1 11.87 17H8.13a2 2 0 0 1-1.99-1.64L5.4 5H5a1 1 0 1 1 0-2h2.5Zm.9 4.5a.75.75 0 0 1 .75.75v6a.75.75 0 1 1-1.5 0v-6a.75.75 0 0 1 .75-.75Zm3.4 0a.75.75 0 0 1 .75.75v6a.75.75 0 1 1-1.5 0v-6a.75.75 0 0 1 .75-.75Z" clip-rule="evenodd" />
</svg>
</button> </button>
</td> </td>
{% endif %} {% endif %}

View file

@ -0,0 +1,92 @@
{% macro camera(class) %}
<svg class="{{ class }}" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M1 8a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 018.07 3h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0016.07 6H17a2 2 0 012 2v7a2 2 0 01-2 2H3a2 2 0 01-2-2V8zm13.5 3a4.5 4.5 0 11-9 0 4.5 4.5 0 019 0zM10 14a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd" />
</svg>
{% endmacro %}
{% macro spinner(class) %}
<svg class="{{ class }} animate-spin text-amber-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" aria-hidden="true">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
</svg>
{% endmacro %}
{% macro delete(class) %}
<svg class="{{ class }}" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M7.5 3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1H15a1 1 0 1 1 0 2h-.4l-.74 10.36A2 2 0 0 1 11.87 17H8.13a2 2 0 0 1-1.99-1.64L5.4 5H5a1 1 0 1 1 0-2h2.5Zm.9 4.5a.75.75 0 0 1 .75.75v6a.75.75 0 1 1-1.5 0v-6a.75.75 0 0 1 .75-.75Zm3.4 0a.75.75 0 0 1 .75.75v6a.75.75 0 1 1-1.5 0v-6a.75.75 0 0 1 .75-.75Z" clip-rule="evenodd" />
</svg>
{% endmacro %}
{% macro external_link(class) %}
<svg class="{{ class }}" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M11.3 2a.7.7 0 0 0 0 1.4h3.3l-8.1 8.1a.7.7 0 1 0 1 1l8.1-8.1v3.3a.7.7 0 1 0 1.4 0V2.7A.7.7 0 0 0 16.3 2h-5Z" clip-rule="evenodd" />
<path d="M4.7 5.2a1.5 1.5 0 0 1 1.5-1.5h2.1a.7.7 0 1 0 0-1.4H6.2a2.9 2.9 0 0 0-2.9 2.9v7.6a2.9 2.9 0 0 0 2.9 2.9h7.6a2.9 2.9 0 0 0 2.9-2.9v-2.1a.7.7 0 1 0-1.4 0v2.1a1.5 1.5 0 0 1-1.5 1.5H6.1a1.5 1.5 0 0 1-1.5-1.5V5.2Z" />
</svg>
{% endmacro %}
{% macro location(class) %}
<svg class="{{ class }}" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M9.69 18.933l.003.001C9.89 19.02 10 19 10 19s.11.02.308-.066l.002-.001.006-.003.018-.008a5.741 5.741 0 00.281-.14c.186-.096.446-.24.757-.433.62-.384 1.445-.966 2.274-1.765C15.302 14.988 17 12.493 17 9A7 7 0 103 9c0 3.492 1.698 5.988 3.355 7.584a13.731 13.731 0 002.273 1.765 11.842 11.842 0 00.976.544l.062.029.018.008.006.003ZM10 11.25a2.25 2.25 0 100-4.5 2.25 2.25 0 000 4.5Z" clip-rule="evenodd" />
</svg>
{% endmacro %}
{% macro refresh(class) %}
<svg class="{{ class }}" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M15.312 11.424a5.5 5.5 0 0 1-9.201 2.466l-.312-.311h2.433a.75.75 0 0 0 0-1.5H3.989a.75.75 0 0 0-.75.75v4.242a.75.75 0 0 0 1.5 0v-2.43l.31.31a7 7 0 0 0 11.712-3.138.75.75 0 0 0-1.449-.39Zm1.23-3.723a.75.75 0 0 0 .219-.53V2.929a.75.75 0 0 0-1.5 0v2.43l-.31-.31A7 7 0 0 0 3.239 8.188a.75.75 0 1 0 1.448.389 5.5 5.5 0 0 1 9.2-2.466l.312.311h-2.433a.75.75 0 0 0 0 1.5h4.243a.75.75 0 0 0 .53-.22Z" clip-rule="evenodd" />
</svg>
{% endmacro %}
{% macro home(class) %}
<svg class="{{ class }}" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z" />
</svg>
{% endmacro %}
{% macro star(class) %}
<svg class="{{ class }}" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10.868 2.884c-.321-.772-1.415-.772-1.736 0l-1.83 4.401-4.753.381c-.833.067-1.171 1.107-.536 1.651l3.62 3.102-1.106 4.637c-.194.813.691 1.456 1.405 1.02L10 15.591l4.069 2.485c.713.436 1.598-.207 1.404-1.02l-1.106-4.637 3.62-3.102c.635-.544.297-1.584-.536-1.65l-4.752-.382-1.831-4.401Z" clip-rule="evenodd" />
</svg>
{% endmacro %}
{% macro bag(class) %}
<svg class="{{ class }}" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M2 6a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2h2a3 3 0 0 1 0 6h-1.1A5.002 5.002 0 0 1 14 16H6a5 5 0 0 1-5-5V6Zm16 4h2a1 1 0 1 0 0-2h-2v2ZM4 20a1 1 0 0 1 1-1h10a1 1 0 1 1 0 2H5a1 1 0 0 1-1-1Z" clip-rule="evenodd" />
</svg>
{% endmacro %}
{% macro pen(class) %}
<svg class="{{ class }}" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path d="M15.993 1.385a1.87 1.87 0 0 1 2.623 2.622l-4.03 5.27a12.75 12.75 0 0 1-4.223 3.358 7.724 7.724 0 0 1-2.71.832 2.14 2.14 0 0 1-.588-.085 1.07 1.07 0 0 1-.725-.723 2.14 2.14 0 0 1-.083-.588 7.724 7.724 0 0 1 .832-2.71 12.75 12.75 0 0 1 3.358-4.223l5.27-4.03.176.177-.176-.177Z" />
<path d="M6 6.878V6a2.25 2.25 0 0 1 2.25-2.25h7.5A2.25 2.25 0 0 1 18 6v3.75a2.25 2.25 0 0 1-2.25 2.25h-.878a15.262 15.262 0 0 0 .878 3h.75a3.75 3.75 0 0 0 3.75-3.75V6a3.75 3.75 0 0 0-3.75-3.75h-7.5A3.75 3.75 0 0 0 5.25 6v.75a15.262 15.262 0 0 0 .75.128Z" />
</svg>
{% endmacro %}
{% macro hamburger(class) %}
<svg class="{{ class }}" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16" />
</svg>
{% endmacro %}
{% macro close(class) %}
<svg class="{{ class }}" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
{% endmacro %}
{% macro arrow_up(class) %}
<svg class="{{ class }}" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 17a.75.75 0 0 1-.75-.75V5.612L5.29 9.77a.75.75 0 0 1-1.08-1.04l5.25-5.5a.75.75 0 0 1 1.08 0l5.25 5.5a.75.75 0 1 1-1.08 1.04l-3.96-4.158V16.25A.75.75 0 0 1 10 17Z" clip-rule="evenodd" />
</svg>
{% endmacro %}
{% macro plus_circle(class) %}
<svg class="{{ class }}" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm.75-11.25a.75.75 0 00-1.5 0v2.5h-2.5a.75.75 0 000 1.5h2.5v2.5a.75.75 0 001.5 0v-2.5h2.5a.75.75 0 000-1.5h-2.5v-2.5z" clip-rule="evenodd" />
</svg>
{% endmacro %}
{% macro x_circle(class) %}
<svg class="{{ class }}" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd" />
</svg>
{% endmacro %}

View file

@ -1,4 +1,5 @@
{% import "partials/table.html" as table %} {% import "partials/table.html" as table %}
{% 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">
{% if roasts.items.is_empty() && !navigator.has_search() %} {% if roasts.items.is_empty() && !navigator.has_search() %}
@ -73,13 +74,7 @@
data-on:click="confirm('Delete this roast?') && @delete('/api/v1/roasts/{{ roast.full_id }}?{{ navigator.query() }}', {responseOverrides: {selector: '#roast-list', mode: 'replace'}})" data-on:click="confirm('Delete this roast?') && @delete('/api/v1/roasts/{{ roast.full_id }}?{{ navigator.query() }}', {responseOverrides: {selector: '#roast-list', mode: 'replace'}})"
> >
<span class="sr-only">Delete</span> <span class="sr-only">Delete</span>
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::delete("h-4 w-4") %}
<path
fill-rule="evenodd"
d="M7.5 3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1H15a1 1 0 1 1 0 2h-.4l-.74 10.36A2 2 0 0 1 11.87 17H8.13a2 2 0 0 1-1.99-1.64L5.4 5H5a1 1 0 1 1 0-2h2.5Zm.9 4.5a.75.75 0 0 1 .75.75v6a.75.75 0 1 1-1.5 0v-6a.75.75 0 0 1 .75-.75Zm3.4 0a.75.75 0 0 1 .75.75v6a.75.75 0 1 1-1.5 0v-6a.75.75 0 0 1 .75-.75Z"
clip-rule="evenodd"
/>
</svg>
</button> </button>
</td> </td>
{% endif %} {% endif %}

View file

@ -1,4 +1,5 @@
{% import "partials/table.html" as table %} {% import "partials/table.html" as table %}
{% 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">
{% if roasters.items.is_empty() && !navigator.has_search() %} {% if roasters.items.is_empty() && !navigator.has_search() %}
@ -64,16 +65,7 @@
title="Visit homepage" title="Visit homepage"
> >
<span class="sr-only">Visit {{ roaster.name }} homepage</span> <span class="sr-only">Visit {{ roaster.name }} homepage</span>
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::external_link("h-4 w-4") %}
<path
fill-rule="evenodd"
d="M11.3 2a.7.7 0 0 0 0 1.4h3.3l-8.1 8.1a.7.7 0 1 0 1 1l8.1-8.1v3.3a.7.7 0 1 0 1.4 0V2.7A.7.7 0 0 0 16.3 2h-5Z"
clip-rule="evenodd"
/>
<path
d="M4.7 5.2a1.5 1.5 0 0 1 1.5-1.5h2.1a.7.7 0 1 0 0-1.4H6.2a2.9 2.9 0 0 0-2.9 2.9v7.6a2.9 2.9 0 0 0 2.9 2.9h7.6a2.9 2.9 0 0 0 2.9-2.9v-2.1a.7.7 0 1 0-1.4 0v2.1a1.5 1.5 0 0 1-1.5 1.5H6.1a1.5 1.5 0 0 1-1.5-1.5V5.2Z"
/>
</svg>
</a> </a>
{% endif %} {% endif %}
{% if is_authenticated %} {% if is_authenticated %}
@ -84,13 +76,7 @@
data-on:click="confirm('Delete this roaster?') && @delete('/api/v1/roasters/{{ roaster.id }}?{{ navigator.query() }}', {responseOverrides: {selector: '#roaster-list', mode: 'replace'}})" data-on:click="confirm('Delete this roaster?') && @delete('/api/v1/roasters/{{ roaster.id }}?{{ navigator.query() }}', {responseOverrides: {selector: '#roaster-list', mode: 'replace'}})"
> >
<span class="sr-only">Delete</span> <span class="sr-only">Delete</span>
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::delete("h-4 w-4") %}
<path
fill-rule="evenodd"
d="M7.5 3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1H15a1 1 0 1 1 0 2h-.4l-.74 10.36A2 2 0 0 1 11.87 17H8.13a2 2 0 0 1-1.99-1.64L5.4 5H5a1 1 0 1 1 0-2h2.5Zm.9 4.5a.75.75 0 0 1 .75.75v6a.75.75 0 1 1-1.5 0v-6a.75.75 0 0 1 .75-.75Zm3.4 0a.75.75 0 0 1 .75.75v6a.75.75 0 1 1-1.5 0v-6a.75.75 0 0 1 .75-.75Z"
clip-rule="evenodd"
/>
</svg>
</button> </button>
{% endif %} {% endif %}
</div> </div>

View file

@ -1,9 +1,8 @@
{% import "partials/icons.html" as icons %}
<h2 id="{{ month.anchor }}" class="timeline-heading scroll-mt-24 mb-6 text-2xl font-semibold text-amber-800" data-timeline-month> <h2 id="{{ month.anchor }}" class="timeline-heading scroll-mt-24 mb-6 text-2xl font-semibold text-amber-800" data-timeline-month>
{{ month.heading }} {{ month.heading }}
<button type="button" class="timeline-top-btn" onclick="window.scrollTo({top: 0, behavior: 'smooth'})" aria-label="Back to top"> <button type="button" class="timeline-top-btn" onclick="window.scrollTo({top: 0, behavior: 'smooth'})" aria-label="Back to top">
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::arrow_up("h-5 w-5") %}
<path fill-rule="evenodd" d="M10 17a.75.75 0 0 1-.75-.75V5.612L5.29 9.77a.75.75 0 0 1-1.08-1.04l5.25-5.5a.75.75 0 0 1 1.08 0l5.25 5.5a.75.75 0 1 1-1.08 1.04l-3.96-4.158V16.25A.75.75 0 0 1 10 17Z" clip-rule="evenodd" />
</svg>
</button> </button>
</h2> </h2>
{% for event in month.events %} {% for event in month.events %}
@ -37,16 +36,7 @@
rel="noreferrer noopener" rel="noreferrer noopener"
aria-label="Open external link" aria-label="Open external link"
> >
<svg class="h-3.5 w-3.5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::external_link("h-3.5 w-3.5") %}
<path
fill-rule="evenodd"
d="M11.3 2a.7.7 0 0 0 0 1.4h3.3l-8.1 8.1a.7.7 0 1 0 1 1l8.1-8.1v3.3a.7.7 0 1 0 1.4 0V2.7A.7.7 0 0 0 16.3 2h-5Z"
clip-rule="evenodd"
/>
<path
d="M4.7 5.2a1.5 1.5 0 0 1 1.5-1.5h2.1a.7.7 0 1 0 0-1.4H6.2a2.9 2.9 0 0 0-2.9 2.9v7.6a2.9 2.9 0 0 0 2.9 2.9h7.6a2.9 2.9 0 0 0 2.9-2.9v-2.1a.7.7 0 1 0-1.4 0v2.1a1.5 1.5 0 0 1-1.5 1.5H6.1a1.5 1.5 0 0 1-1.5-1.5V5.2Z"
/>
</svg>
<span class="sr-only">Open external link</span> <span class="sr-only">Open external link</span>
</a> </a>
{% endif %} {% endif %}
@ -72,13 +62,7 @@
title="Brew again" title="Brew again"
> >
<span class="sr-only">Brew again</span> <span class="sr-only">Brew again</span>
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::refresh("h-4 w-4") %}
<path
fill-rule="evenodd"
d="M15.312 11.424a5.5 5.5 0 0 1-9.201 2.466l-.312-.311h2.433a.75.75 0 0 0 0-1.5H3.989a.75.75 0 0 0-.75.75v4.242a.75.75 0 0 0 1.5 0v-2.43l.31.31a7 7 0 0 0 11.712-3.138.75.75 0 0 0-1.449-.39Zm1.23-3.723a.75.75 0 0 0 .219-.53V2.929a.75.75 0 0 0-1.5 0v2.43l-.31-.31A7 7 0 0 0 3.239 8.188a.75.75 0 1 0 1.448.389 5.5 5.5 0 0 1 9.2-2.466l.312.311h-2.433a.75.75 0 0 0 0 1.5h4.243a.75.75 0 0 0 .53-.22Z"
clip-rule="evenodd"
/>
</svg>
</button> </button>
</form> </form>
{% endif %} {% endif %}

View file

@ -1,4 +1,4 @@
{% extends "base.html" %} {% block title %}Brewlog · Roaster · {{ roaster.name }}{% endblock %} {% {% extends "base.html" %} {% import "partials/icons.html" as icons %} {% block title %}Brewlog · Roaster · {{ roaster.name }}{% endblock %} {%
block content %} block content %}
<header class="flex flex-col gap-2"> <header class="flex flex-col gap-2">
<h1 class="text-3xl font-semibold">{{ roaster.name }}</h1> <h1 class="text-3xl font-semibold">{{ roaster.name }}</h1>
@ -25,16 +25,7 @@ block content %}
rel="noreferrer noopener" rel="noreferrer noopener"
aria-label="Visit {{ roaster.name }} homepage" aria-label="Visit {{ roaster.name }} homepage"
> >
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::external_link("h-4 w-4") %}
<path
fill-rule="evenodd"
d="M11.3 2a.7.7 0 0 0 0 1.4h3.3l-8.1 8.1a.7.7 0 1 0 1 1l8.1-8.1v3.3a.7.7 0 1 0 1.4 0V2.7A.7.7 0 0 0 16.3 2h-5Z"
clip-rule="evenodd"
/>
<path
d="M4.7 5.2a1.5 1.5 0 0 1 1.5-1.5h2.1a.7.7 0 1 0 0-1.4H6.2a2.9 2.9 0 0 0-2.9 2.9v7.6a2.9 2.9 0 0 0 2.9 2.9h7.6a2.9 2.9 0 0 0 2.9-2.9v-2.1a.7.7 0 1 0-1.4 0v2.1a1.5 1.5 0 0 1-1.5 1.5H6.1a1.5 1.5 0 0 1-1.5-1.5V5.2Z"
/>
</svg>
<span>Visit</span> <span>Visit</span>
</a> </a>
{% else %} {% else %}

View file

@ -1,4 +1,4 @@
{% extends "base.html" %} {% block title %}Brewlog · Roasters{% endblock %} {% extends "base.html" %} {% import "partials/icons.html" as icons %} {% block title %}Brewlog · Roasters{% endblock %}
{% block content %} {% block content %}
<section data-signals:_show-form="false" data-signals:_extracting="false" data-signals:_extract-error="''" data-signals:_submitting="false"> <section data-signals:_show-form="false" data-signals:_extracting="false" data-signals:_extract-error="''" data-signals:_submitting="false">
@ -49,9 +49,7 @@
onclick="document.getElementById('roaster-photo').click()" onclick="document.getElementById('roaster-photo').click()"
class="inline-flex items-center gap-2 rounded-md border border-amber-500 px-3 py-2 text-sm font-medium text-amber-700 transition hover:bg-amber-50" class="inline-flex items-center gap-2 rounded-md border border-amber-500 px-3 py-2 text-sm font-medium text-amber-700 transition hover:bg-amber-50"
> >
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::camera("h-4 w-4") %}
<path fill-rule="evenodd" d="M1 8a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 018.07 3h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0016.07 6H17a2 2 0 012 2v7a2 2 0 01-2 2H3a2 2 0 01-2-2V8zm13.5 3a4.5 4.5 0 11-9 0 4.5 4.5 0 019 0zM10 14a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd" />
</svg>
Extract from photo Extract from photo
</button> </button>
<span class="text-xs text-stone-400">or</span> <span class="text-xs text-stone-400">or</span>
@ -71,10 +69,7 @@
</div> </div>
</div> </div>
<div data-show="$_extracting" style="display:none" class="flex items-center gap-3 text-sm text-amber-700"> <div data-show="$_extracting" style="display:none" class="flex items-center gap-3 text-sm text-amber-700">
<svg class="h-4 w-4 animate-spin text-amber-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" aria-hidden="true"> {% call icons::spinner("h-4 w-4") %}
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
</svg>
Waiting for response&hellip; Waiting for response&hellip;
</div> </div>
<p data-show="$_extractError" data-text="$_extractError" style="display:none" class="mt-2 text-sm text-red-600"></p> <p data-show="$_extractError" data-text="$_extractError" style="display:none" class="mt-2 text-sm text-red-600"></p>

View file

@ -1,4 +1,4 @@
{% extends "base.html" %} {% block title %}Brewlog · Roasts{% endblock %} {% extends "base.html" %} {% import "partials/icons.html" as icons %} {% block title %}Brewlog · Roasts{% endblock %}
{% block content %} {% block content %}
<section data-signals:_show-form="false" data-signals:_extracting="false" data-signals:_extract-error="''" data-signals:_submitting="false"> <section data-signals:_show-form="false" data-signals:_extracting="false" data-signals:_extract-error="''" data-signals:_submitting="false">
@ -63,9 +63,7 @@
onclick="document.getElementById('roast-photo').click()" onclick="document.getElementById('roast-photo').click()"
class="inline-flex items-center gap-2 rounded-md border border-amber-500 px-3 py-2 text-sm font-medium text-amber-700 transition hover:bg-amber-50" class="inline-flex items-center gap-2 rounded-md border border-amber-500 px-3 py-2 text-sm font-medium text-amber-700 transition hover:bg-amber-50"
> >
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> {% call icons::camera("h-4 w-4") %}
<path fill-rule="evenodd" d="M1 8a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 018.07 3h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0016.07 6H17a2 2 0 012 2v7a2 2 0 01-2 2H3a2 2 0 01-2-2V8zm13.5 3a4.5 4.5 0 11-9 0 4.5 4.5 0 019 0zM10 14a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd" />
</svg>
Extract from photo Extract from photo
</button> </button>
<span class="text-xs text-stone-400">or</span> <span class="text-xs text-stone-400">or</span>
@ -85,10 +83,7 @@
</div> </div>
</div> </div>
<div data-show="$_extracting" style="display:none" class="flex items-center gap-3 text-sm text-amber-700"> <div data-show="$_extracting" style="display:none" class="flex items-center gap-3 text-sm text-amber-700">
<svg class="h-4 w-4 animate-spin text-amber-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" aria-hidden="true"> {% call icons::spinner("h-4 w-4") %}
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
</svg>
Waiting for response&hellip; Waiting for response&hellip;
</div> </div>
<p data-show="$_extractError" data-text="$_extractError" style="display:none" class="mt-2 text-sm text-red-600"></p> <p data-show="$_extractError" data-text="$_extractError" style="display:none" class="mt-2 text-sm text-red-600"></p>