Add badge filtering and shop category to the site

- Introduced a new 'shops' category in the build script and README.
- Implemented badge filtering in the filter functionality, allowing users to filter entries by badge.
This commit is contained in:
2026-01-25 23:37:20 +01:00
parent 2519520e1b
commit cfb35a3c38
6 changed files with 59 additions and 7 deletions

View File

@@ -33,10 +33,13 @@ The generated site will be in the `dist/` folder.
Create a Markdown file in the appropriate `content/` subfolder:
- `content/products/` - Swedish products
- `content/services/` - Swedish services
- `content/experiences/` - Swedish experiences
- `content/manufacturers/` - Swedish manufacturers
| Folder | Category | Description |
|--------|----------|-------------|
| `content/products/` | Produkter | Physical Swedish products (e.g., Dalahäst, Orrefors glass) |
| `content/manufacturers/` | Tillverkare | Companies that design/manufacture products (e.g., Fjällräven, Hasselblad) |
| `content/shops/` | Butiker | Retail stores selling products (e.g., Göteborg Manufaktur) |
| `content/services/` | Tjänster | Service providers, both physical and digital (e.g., SJ, Mediaflow) |
| `content/experiences/` | Upplevelser | Experiences, events, and cultural activities (e.g., Icehotel, Midsommar) |
### Content Format

View File

@@ -14,7 +14,8 @@ const CATEGORY_LABELS = {
manufacturers: 'Tillverkare',
products: 'Produkter',
services: 'Tjänster',
experiences: 'Upplevelser'
experiences: 'Upplevelser',
shops: 'Butiker'
};
// Badge tiers (highest to lowest)
@@ -207,8 +208,14 @@ function extractFilters(entries) {
const categories = [...new Set(entries.map(e => e.category).filter(Boolean))].sort();
const regions = [...new Set(entries.map(e => e.region).filter(Boolean))].sort();
const tags = [...new Set(entries.flatMap(e => e.tags || []))].sort();
const badges = [...new Set(entries.map(e => e.badge).filter(Boolean))].sort((a, b) => {
// Sort by tier (highest first)
const tierA = BADGE_TIERS[a]?.tier || 99;
const tierB = BADGE_TIERS[b]?.tier || 99;
return tierA - tierB;
});
return { categories, regions, tags };
return { categories, regions, tags, badges };
}
// Generate filter HTML
@@ -225,6 +232,10 @@ function generateFilterHTML(filters) {
`<option value="${t}">${t}</option>`
).join('');
const badgeOptions = filters.badges.map(b =>
`<option value="${b}">${BADGE_TIERS[b]?.label || b}</option>`
).join('');
return `
<div class="filters">
<div class="filter-group">
@@ -241,6 +252,13 @@ function generateFilterHTML(filters) {
${regionOptions}
</select>
</div>
<div class="filter-group">
<label for="badge-filter">Märkning</label>
<select id="badge-filter">
<option value="">Alla märkningar</option>
${badgeOptions}
</select>
</div>
<div class="filter-group">
<label for="tag-filter">Tagg</label>
<select id="tag-filter">

View File

@@ -0,0 +1,13 @@
---
title: Göteborg Manufaktur
region: Västra Götaland
tags: [jeans, denim, kläder, reparation, second-hand, hållbarhet]
website: https://goteborgmanufaktur.se
badge: svenskt-foretag
image: /static/images/goteborg-manufaktur-cover.jpeg
logo: /static/images/goteborg-manufaktur.avif
---
Göteborg Manufaktur är en denimbutik på Tredje Långgatan i Göteborg, grundad 2016 av Jonas Melin, Olof Norrman och Erik Davis. Butiken erbjuder ett noggrant utvalt sortiment av premiumjeans och kläder med fokus på japansk denim och workwear heritage.
Det som började som en liten jeansreparationsverkstad 2014 har vuxit till en fullskalig butik med etisk produktion i centrum. Alla jeansreparationer görs på plats och är gratis för produkter köpta i butiken. Genom "Worn Not Wasted" erbjuds även second-hand på kommission en cirkulär modell för hållbart mode.

View File

@@ -5,6 +5,7 @@
const categoryFilter = document.getElementById('category-filter');
const regionFilter = document.getElementById('region-filter');
const badgeFilter = document.getElementById('badge-filter');
const tagFilter = document.getElementById('tag-filter');
const searchInput = document.getElementById('search-input');
const clearFiltersBtn = document.getElementById('clear-filters');
@@ -26,6 +27,9 @@
if (regionFilter?.value) {
params.set('region', regionFilter.value);
}
if (badgeFilter?.value) {
params.set('badge', badgeFilter.value);
}
if (tagFilter?.value) {
params.set('tag', tagFilter.value);
}
@@ -51,6 +55,9 @@
if (params.has('region') && regionFilter) {
regionFilter.value = params.get('region');
}
if (params.has('badge') && badgeFilter) {
badgeFilter.value = params.get('badge');
}
if (params.has('tag') && tagFilter) {
tagFilter.value = params.get('tag');
}
@@ -64,6 +71,7 @@
function filterEntries(shouldUpdateURL = true) {
const categoryValue = categoryFilter?.value.toLowerCase() || '';
const regionValue = regionFilter?.value.toLowerCase() || '';
const badgeValue = badgeFilter?.value.toLowerCase() || '';
const tagValue = tagFilter?.value.toLowerCase() || '';
const searchValue = searchInput?.value.toLowerCase().trim() || '';
@@ -72,15 +80,17 @@
entries.forEach(entry => {
const entryCategory = (entry.dataset.category || '').toLowerCase();
const entryRegion = (entry.dataset.region || '').toLowerCase();
const entryBadge = (entry.dataset.badge || '').toLowerCase();
const entryTags = (entry.dataset.tags || '').toLowerCase().split(',');
const entryText = entry.textContent.toLowerCase();
const matchesCategory = !categoryValue || entryCategory === categoryValue;
const matchesRegion = !regionValue || entryRegion === regionValue;
const matchesBadge = !badgeValue || entryBadge === badgeValue;
const matchesTag = !tagValue || entryTags.includes(tagValue);
const matchesSearch = !searchValue || entryText.includes(searchValue);
const isVisible = matchesCategory && matchesRegion && matchesTag && matchesSearch;
const isVisible = matchesCategory && matchesRegion && matchesBadge && matchesTag && matchesSearch;
entry.hidden = !isVisible;
if (isVisible) visibleEntries++;
@@ -111,6 +121,7 @@
const hasFilters =
(categoryFilter?.value || '') !== '' ||
(regionFilter?.value || '') !== '' ||
(badgeFilter?.value || '') !== '' ||
(tagFilter?.value || '') !== '' ||
(searchInput?.value || '') !== '';
@@ -120,6 +131,7 @@
function clearFilters() {
if (categoryFilter) categoryFilter.value = '';
if (regionFilter) regionFilter.value = '';
if (badgeFilter) badgeFilter.value = '';
if (tagFilter) tagFilter.value = '';
if (searchInput) searchInput.value = '';
@@ -138,6 +150,8 @@
categoryFilter.value = filterValue;
} else if (filterType === 'region' && regionFilter) {
regionFilter.value = filterValue;
} else if (filterType === 'badge' && badgeFilter) {
badgeFilter.value = filterValue;
} else if (filterType === 'tag' && tagFilter) {
tagFilter.value = filterValue;
}
@@ -171,6 +185,10 @@
regionFilter.addEventListener('change', () => filterEntries());
}
if (badgeFilter) {
badgeFilter.addEventListener('change', () => filterEntries());
}
if (tagFilter) {
tagFilter.addEventListener('change', () => filterEntries());
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB