Korjaa alikategoria-ongelmat

- api.php: getOrInitCategories() päivittää automaattisesti subcategories-kentän
  olemassaolevaan categories.json-tiedostoon ilman manuaalista seedausta
- index.html: alikategoria-valitsin julkaisulomakkeeseen (näkyy kun kategorialla on alikategoriat)
- script.js: subcategory tallennetaan uuteen julkaisuun
- script.js: filterPosts null-turva + hakukenttä etsii myös alikategoria-id:llä
- script.js: updateSubcategoryPicker() päivittää alikategoriat kategorian vaihtuessa

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-08 08:24:24 +02:00
parent 88e17d2b92
commit 993a137508
3 changed files with 50 additions and 12 deletions

16
api.php
View File

@@ -315,7 +315,21 @@ function getOrInitCategories(): array {
writeData('categories.json', $cats); writeData('categories.json', $cats);
return $cats; return $cats;
} }
return readData('categories.json', []); $cats = readData('categories.json', []);
// Merge in subcategories if existing file doesn't have them yet
$defaults = defaultCategories();
$defaultMap = [];
foreach ($defaults as $d) { $defaultMap[$d['id']] = $d; }
$changed = false;
foreach ($cats as &$cat) {
if (!isset($cat['subcategories']) && isset($defaultMap[$cat['id']]['subcategories'])) {
$cat['subcategories'] = $defaultMap[$cat['id']]['subcategories'];
$changed = true;
}
}
unset($cat);
if ($changed) writeData('categories.json', $cats);
return $cats;
} }
// ─── Routing ─────────────────────────────────────────────────── // ─── Routing ───────────────────────────────────────────────────

View File

@@ -109,7 +109,12 @@
<div class="form-group"> <div class="form-group">
<label>Kategoria</label> <label>Kategoria</label>
<select id="sub-category"></select> <select id="sub-category" onchange="updateSubcategoryPicker()"></select>
</div>
<div class="form-group" id="sub-subcategory-group" style="display:none">
<label>Alikategoria</label>
<select id="sub-subcategory"></select>
</div> </div>
<div class="form-group"> <div class="form-group">

View File

@@ -239,9 +239,10 @@ function filterPosts() {
cards.forEach(card => { cards.forEach(card => {
const matchesCat = currentFilter === 'all' || card.dataset.category === currentFilter; const matchesCat = currentFilter === 'all' || card.dataset.category === currentFilter;
const matchesSub = currentSubFilter === 'all' || card.dataset.subcategory === currentSubFilter; const matchesSub = currentSubFilter === 'all' || card.dataset.subcategory === currentSubFilter;
const title = card.querySelector('h3').textContent.toLowerCase(); const title = (card.querySelector('h3')?.textContent || '').toLowerCase();
const desc = card.querySelector('p:not(.card-author)').textContent.toLowerCase(); const desc = (card.querySelector('p:not(.card-author)')?.textContent || '').toLowerCase();
const matchesSearch = title.includes(query) || desc.includes(query); const subLbl = (card.dataset.subcategory || '').toLowerCase();
const matchesSearch = !query || title.includes(query) || desc.includes(query) || subLbl.includes(query);
const show = matchesCat && matchesSub && matchesSearch; const show = matchesCat && matchesSub && matchesSearch;
card.style.display = show ? '' : 'none'; card.style.display = show ? '' : 'none';
if (show) visible++; if (show) visible++;
@@ -402,10 +403,27 @@ function setSubmitType(type) {
document.getElementById('sub-typePostBtn').classList.toggle('active', type === 'post'); document.getElementById('sub-typePostBtn').classList.toggle('active', type === 'post');
} }
function updateSubcategoryPicker() {
const catId = document.getElementById('sub-category').value;
const cat = APP.categories.find(c => c.id === catId);
const subs = cat?.subcategories || [];
const group = document.getElementById('sub-subcategory-group');
const sel = document.getElementById('sub-subcategory');
if (!subs.length) {
group.style.display = 'none';
sel.innerHTML = '';
} else {
sel.innerHTML = `<option value="">— Valitse alikategoria —</option>` +
subs.map(s => `<option value="${s.id}">${s.fi}</option>`).join('');
group.style.display = '';
}
}
function openSubmitModal() { function openSubmitModal() {
const cats = APP.categories; const cats = APP.categories;
const sel = document.getElementById('sub-category'); const sel = document.getElementById('sub-category');
sel.innerHTML = cats.map(c => `<option value="${c.id}">${c.emoji || ''} ${c.fi}</option>`).join(''); sel.innerHTML = cats.map(c => `<option value="${c.id}">${c.emoji || ''} ${c.fi}</option>`).join('');
updateSubcategoryPicker();
setSubmitType('recipe'); setSubmitType('recipe');
document.getElementById('submitOverlay').classList.add('open'); document.getElementById('submitOverlay').classList.add('open');
document.body.style.overflow = 'hidden'; document.body.style.overflow = 'hidden';
@@ -508,6 +526,7 @@ async function submitPublicPost() {
} }
const category = document.getElementById('sub-category').value; const category = document.getElementById('sub-category').value;
const subcategory = document.getElementById('sub-subcategory')?.value || '';
const cat = APP.categories.find(c => c.id === category); const cat = APP.categories.find(c => c.id === category);
const emoji = cat?.emoji || '📝'; const emoji = cat?.emoji || '📝';
const author = document.getElementById('sub-author').value.trim() || 'Vieras'; const author = document.getElementById('sub-author').value.trim() || 'Vieras';
@@ -520,7 +539,7 @@ async function submitPublicPost() {
const post = { const post = {
id: title.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/\s+/g,'_').replace(/[^a-z0-9_]/g,'') + '_' + Date.now(), id: title.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/\s+/g,'_').replace(/[^a-z0-9_]/g,'') + '_' + Date.now(),
title, emoji, category, author, desc, images, title, emoji, category, subcategory, author, desc, images,
type: submitType, type: submitType,
}; };