Implement complete web interface for the Gear entity: - Add GearView model with category badges and formatted display - Create GearTemplate and GearListTemplate for Askama rendering - Build main gear page with collapsible add form (Datastar-powered) - Implement gear list table with sortable columns and pagination - Use trash icon for delete actions matching roasts table design - Add Gear navigation link in main menu between Bags and Timeline - Integrate gear events into timeline view with proper labels and links The web UI follows the established patterns from other entities with Datastar for reactive fragment updates and proper authentication gating. Apply code formatting fixes across all gear-related modules.
72 lines
3.3 KiB
HTML
72 lines
3.3 KiB
HTML
{% import "partials/table.html" as table %}
|
|
|
|
<div id="gear-list">
|
|
{% if gear.items.is_empty() %}
|
|
<div class="rounded-lg border border-stone-200 bg-stone-50 p-8 text-center">
|
|
<p class="text-stone-500">No gear found.</p>
|
|
{% if is_authenticated %}
|
|
<p class="mt-2 text-sm text-stone-500">Add your first piece of brewing equipment above.</p>
|
|
{% endif %}
|
|
</div>
|
|
{% else %}
|
|
<div class="rounded-lg border border-amber-300 bg-amber-100/80 shadow-sm">
|
|
{% call table::pagination_header(gear, navigator, "#gear-list") %}
|
|
|
|
<div class="overflow-x-auto">
|
|
<table class="min-w-full divide-y divide-amber-200 text-left text-sm text-stone-700">
|
|
<thead class="bg-amber-200/60 text-xs font-semibold tracking-wide text-amber-900">
|
|
<tr>
|
|
{% call table::sortable_header("Category", "category", navigator, "#gear-list") %}
|
|
{% call table::sortable_header("Make", "make", navigator, "#gear-list") %}
|
|
{% call table::sortable_header("Model", "model", navigator, "#gear-list") %}
|
|
<th scope="col" class="px-4 py-3">Notes</th>
|
|
{% call table::sortable_header("Added", "created-at", navigator, "#gear-list") %}
|
|
{% if is_authenticated %}
|
|
<th scope="col" class="relative px-4 py-3">
|
|
<span class="sr-only">Actions</span>
|
|
</th>
|
|
{% endif %}
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-amber-200/70">
|
|
{% for item in gear.items %}
|
|
<tr class="bg-amber-50/40 transition hover:bg-amber-50">
|
|
<td class="px-4 py-3 whitespace-nowrap">
|
|
<span class="inline-flex rounded-full bg-amber-100 px-2 py-1 text-xs font-medium text-amber-800">
|
|
{{ item.category_label }}
|
|
</span>
|
|
</td>
|
|
<td class="px-4 py-3 whitespace-nowrap font-medium text-stone-600">
|
|
{{ item.make }}
|
|
</td>
|
|
<td class="px-4 py-3 whitespace-nowrap">
|
|
{{ item.model }}
|
|
</td>
|
|
<td class="px-4 py-3">
|
|
<div class="max-w-xs truncate">{{ item.notes }}</div>
|
|
</td>
|
|
<td class="px-4 py-3 whitespace-nowrap">
|
|
{{ item.created_at }}
|
|
</td>
|
|
{% if is_authenticated %}
|
|
<td class="px-4 py-3 whitespace-nowrap text-right text-sm font-medium">
|
|
<button
|
|
type="button"
|
|
class="text-red-600 hover:text-red-900"
|
|
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>
|
|
<svg class="h-4 w-4" 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>
|
|
</button>
|
|
</td>
|
|
{% endif %}
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|