Special cakes UI: overlay drawer, basket integration, catalogue tag filter, form parsing, JS save/edit/remove. HTMX navigation: MainContent component, nav links with hx-get, handlers return partial on HX-Request. Dashboard: metrics grid, CakeBreakdown, OrderRow with status select, CakeCounts/OrderCounts, torty filter, tort badge, results-pane. Collapsible sidebar: toggle button, localStorage, CSS transitions. Location field: dropdown replaced with pill toggle. Inline style cleanup: moved to CSS classes (tag-grid, grow, icon.sm, header-actions, toggle-group, success-icon, etc.). Dead CSS removal: pagination, status-chip, badge-pill, login-page. Helpers: exported CakeTitle/OrderTitle, added tagNames, tagIDsComma, orderItemsDesc, specialCakeDetail, removed unused helpers.
91 lines
2.6 KiB
Text
91 lines
2.6 KiB
Text
package templates
|
|
|
|
import (
|
|
"strconv"
|
|
|
|
"git.bronku.xyz/bronku/cake-order-tracker/models"
|
|
)
|
|
|
|
templ CakesPage(cakes []models.Cake, allTags []models.Tag) {
|
|
@Layout("Katalog", templ.NopComponent, CakesMain(cakes, allTags))
|
|
}
|
|
|
|
templ CakesMain(cakes []models.Cake, allTags []models.Tag) {
|
|
<section class="card">
|
|
<header>
|
|
<h2>
|
|
<span class="material-symbols-outlined">inventory_2</span>
|
|
Zarządzanie katalogiem
|
|
</h2>
|
|
<a href="/cake" class="btn primary sm">
|
|
<span class="material-symbols-outlined">add</span>
|
|
Nowe ciasto
|
|
</a>
|
|
</header>
|
|
if len(allTags) > 0 {
|
|
<div class="filter-bar">
|
|
<span class="filter-label">Tagi:</span>
|
|
for _, t := range allTags {
|
|
<button type="button" class="tag-chip" data-tag={ t.Name } onclick="toggleCakeTagFilter(this)">{ t.Name }</button>
|
|
}
|
|
</div>
|
|
}
|
|
<div class="table-container">
|
|
<table class="data-table" id="cakes-table">
|
|
<thead>
|
|
<tr>
|
|
<th style="width:2rem"></th>
|
|
<th>Nazwa</th>
|
|
<th>SKU</th>
|
|
<th>Tagi</th>
|
|
<th class="right">Cena bazowa</th>
|
|
<th class="center"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
for _, c := range cakes {
|
|
<tr data-tags={ tagNames(c.Tags) }>
|
|
<td>
|
|
<div class="icon cake sm">
|
|
<span class="material-symbols-outlined">cake</span>
|
|
</div>
|
|
</td>
|
|
<td class="font-medium">{ c.Name }</td>
|
|
<td class="text-muted">#{ strconv.Itoa(c.ID) }</td>
|
|
<td>
|
|
for _, t := range c.Tags {
|
|
<span class="tag-chip">{ t.Name }</span>
|
|
}
|
|
</td>
|
|
<td class="text-right">{ c.Price.String() } PLN</td>
|
|
<td class="text-center">
|
|
<a href={ templ.URL("/cake/" + strconv.Itoa(c.ID)) } class="action-btn">
|
|
<span class="material-symbols-outlined">edit</span>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
<script>
|
|
var activeCakeTags = [];
|
|
function toggleCakeTagFilter(btn) {
|
|
var tag = btn.getAttribute('data-tag');
|
|
var idx = activeCakeTags.indexOf(tag);
|
|
if (idx >= 0) { activeCakeTags.splice(idx, 1); btn.classList.remove('active'); }
|
|
else { activeCakeTags.push(tag); btn.classList.add('active'); }
|
|
var rows = document.querySelectorAll('#cakes-table tbody tr');
|
|
for (var i = 0; i < rows.length; i++) {
|
|
if (activeCakeTags.length === 0) { rows[i].style.display = ''; continue; }
|
|
var rowTags = (rows[i].getAttribute('data-tags') || '').split(',');
|
|
var match = false;
|
|
for (var j = 0; j < activeCakeTags.length; j++) {
|
|
if (rowTags.indexOf(activeCakeTags[j]) >= 0) { match = true; break; }
|
|
}
|
|
rows[i].style.display = match ? '' : 'none';
|
|
}
|
|
}
|
|
</script>
|
|
}
|