Quelltext des aktuellen Standes
<?php
$this->layout('layouts/main', [
'page_title' => 'Kanban-Kategorien',
'full_width' => true,
'current_route' => 'kanban',
]);
$categories = $categories ?? [];
$csrfToken = $this->csrf();
$view = $this;
$colorPalette = [
'#fca5a5', '#f87171', '#ef4444', '#dc2626', '#b91c1c', '#991b1b', '#7f1d1d',
'#fdba74', '#fb923c', '#f97316', '#ea580c', '#f59e0b', '#d97706', '#b45309',
'#fde047', '#facc15', '#eab308', '#ca8a04', '#a3e635', '#84cc16', '#65a30d',
'#86efac', '#4ade80', '#22c55e', '#16a34a', '#15803d', '#166534', '#14532d',
'#67e8f9', '#22d3ee', '#06b6d4', '#0891b2', '#5eead4', '#14b8a6', '#0d9488',
'#93c5fd', '#60a5fa', '#3b82f6', '#2563eb', '#1d4ed8', '#1e40af', '#1e3a8a',
'#c4b5fd', '#a78bfa', '#8b5cf6', '#7c3aed', '#f0abfc', '#e879f9', '#d946ef',
'#cbd5e1', '#94a3b8', '#64748b', '#475569', '#334155', '#1e293b', '#0f172a',
];
?>
<div class="cat-fullwidth">
<div class="content-header">
<div>
<h1><?= $this->te('Kanban-Labels') ?></h1>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/"><?= $this->te('Home') ?></a></li>
<li class="breadcrumb-item"><a href="/kanban"><?= $this->te('Kanban-Board') ?></a></li>
<li class="breadcrumb-item active"><?= $this->te('Labels') ?></li>
</ol>
</div>
</div>
<div class="cat-body">
<div class="cat-container">
<!-- Neue Kategorie -->
<div class="cat-card">
<div class="cat-card-header">
<h2><?= $this->te('✚ Neues Label erstellen') ?></h2>
</div>
<div class="cat-card-body">
<form method="POST" action="/kanban/categories">
<input type="hidden" name="_csrf" value="<?= $csrfToken ?>">
<div class="cat-create-row">
<div class="cat-color-wrap">
<input type="hidden" name="color" id="new-cat-color" value="#6366f1">
<button type="button" class="cat-color-trigger" onclick="togglePicker('new-cat')" title="<?= $this->te('Farbe auswählen') ?>">
<span class="cat-swatch" id="new-cat-swatch" style="background:var(--indigo-500)"></span>
</button>
<div class="cat-picker" id="new-cat-picker">
<div class="cat-picker-title"><?= $this->te('Farbe wählen') ?></div>
<div class="cat-picker-grid">
<?php foreach ($colorPalette as $c): ?>
<button type="button" class="cat-picker-dot" style="background:<?= $c ?>" onclick="pickColor('new-cat', '<?= $c ?>')" title="<?= $c ?>"></button>
<?php endforeach; ?>
</div>
<div class="cat-picker-footer">
<button type="button" class="cat-picker-reset" onclick="pickColor('new-cat', '#6366f1')"><?= $this->te('Standard-Farbe') ?></button>
</div>
</div>
</div>
<input type="text" name="name" class="cat-input" placeholder="<?= $this->te('Label-Name eingeben...') ?>" required>
<button type="submit" class="btn btn-primary"><?= $this->te('Erstellen') ?></button>
</div>
</form>
</div>
</div>
<!-- Bestehende Kategorien -->
<div class="cat-card">
<div class="cat-card-header">
<h2><?= $this->te('🏷 Bestehende Labels') ?></h2>
<span class="status-pill status-pill--neutral"><?= count($categories) ?></span>
</div>
<?php if (empty($categories)): ?>
<div class="cat-card-body">
<div class="empty-state" style="border:none;background:transparent;">
<div class="empty-icon">🏷</div>
<p><?= $this->te('Noch keine Kategorien vorhanden.') ?></p>
<p class="cat-empty-hint"><?= $this->te('Erstellen Sie oben das erste Label, um Ihre Aufgaben zu gruppieren.') ?></p>
</div>
</div>
<?php else: ?>
<div class="cat-list">
<?php foreach ($categories as $cat): ?>
<form method="POST" action="/kanban/categories/<?= $cat['id'] ?>" class="cat-row">
<input type="hidden" name="_csrf" value="<?= $csrfToken ?>">
<div class="cat-color-wrap">
<input type="hidden" name="color" id="cat-<?= $cat['id'] ?>-color" value="<?= $view->e($cat['color'] ?? '#6366f1') ?>">
<button type="button" class="cat-color-trigger" onclick="togglePicker('cat-<?= $cat['id'] ?>')" title="<?= $this->te('Farbe ändern') ?>">
<span class="cat-swatch" id="cat-<?= $cat['id'] ?>-swatch" style="background:<?= $view->e($cat['color'] ?? 'var(--indigo-500)') ?>"></span>
</button>
<div class="cat-picker" id="cat-<?= $cat['id'] ?>-picker">
<div class="cat-picker-title"><?= $this->te('Farbe wählen') ?></div>
<div class="cat-picker-grid">
<?php foreach ($colorPalette as $c): ?>
<button type="button" class="cat-picker-dot <?= ($cat['color'] ?? '') === $c ? 'active' : '' ?>" style="background:<?= $c ?>" onclick="pickColor('cat-<?= $cat['id'] ?>', '<?= $c ?>')" title="<?= $c ?>"></button>
<?php endforeach; ?>
</div>
<div class="cat-picker-footer">
<button type="button" class="cat-picker-reset" onclick="pickColor('cat-<?= $cat['id'] ?>', '#6366f1')"><?= $this->te('Standard-Farbe') ?></button>
</div>
</div>
</div>
<div class="cat-name-wrap">
<input type="text" name="name" class="cat-input cat-input-inline" value="<?= $view->e($cat['name']) ?>" required>
</div>
<div class="cat-meta">
<span class="cat-meta-count"><?= (int)($cat['task_count'] ?? 0) ?></span>
<span class="cat-meta-label"><?= $this->te(((int)($cat['task_count'] ?? 0)) === 1 ? 'Aufgabe' : 'Aufgaben') ?></span>
</div>
<div class="cat-row-actions">
<button type="submit" class="btn btn-primary btn-sm"><?= $this->te('Speichern') ?></button>
<?php if (((int)($cat['task_count'] ?? 0)) === 0): ?>
<button type="button" class="btn btn-outline-danger btn-sm" onclick="deleteCat(<?= $cat['id'] ?>, '<?= $csrfToken ?>')"><?= $this->te('Löschen') ?></button>
<?php endif; ?>
</div>
</form>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<style>
.cat-fullwidth {}
.cat-body { padding:2rem; background:var(--slate-50); min-height:calc(100vh - 250px); }
.cat-container { max-width:860px; margin:0 auto; }
.cat-card { background:var(--color-bg); border-radius:0.75rem; box-shadow:0 1px 3px rgba(0,0,0,0.05); margin-bottom:1.5rem; }
.cat-card:last-child { margin-bottom:0; }
.cat-card-header { padding:1rem 1.25rem; background:var(--gray-50); border-bottom:1px solid var(--gray-200); border-radius:0.75rem 0.75rem 0 0; display:flex; align-items:center; justify-content:space-between; }
.cat-card-header h2 { margin:0; font-size:0.95rem; font-weight:600; color:var(--gray-900); }
.cat-card-body { padding:1.25rem; }
.cat-input { width:100%; padding:0.625rem 0.875rem; border:1px solid var(--gray-300); border-radius:0.5rem; font-size:0.95rem; color:var(--gray-900); background:var(--color-bg); outline:none; transition:border-color 0.15s,box-shadow 0.15s; font-family:inherit; box-sizing:border-box; }
.cat-input:focus { border-color:var(--blue-600); box-shadow:0 0 0 3px rgba(37,99,235,0.1); }
.cat-input::placeholder { color:var(--gray-400); }
.cat-input-inline { border-color:transparent; background:transparent; font-weight:500; padding:0.4rem 0.625rem; }
.cat-input-inline:hover { border-color:var(--gray-200); }
.cat-input-inline:focus { border-color:var(--blue-600); background:var(--color-bg); box-shadow:0 0 0 3px rgba(37,99,235,0.1); }.cat-create-row { display:flex; align-items:center; gap:0.75rem; }
.cat-create-row .cat-input { flex:1; }
.cat-color-wrap { position:relative; flex-shrink:0; }
.cat-color-trigger { width:38px; height:38px; padding:0; background:var(--gray-50); border:1px solid var(--gray-300); border-radius:0.5rem; cursor:pointer; display:flex; align-items:center; justify-content:center; transition:all 0.15s; }
.cat-color-trigger:hover { border-color:var(--gray-400); background:var(--gray-100); }
.cat-swatch { width:22px; height:22px; border-radius:5px; background:var(--gray-200); border:1px solid rgba(0,0,0,0.08); display:block; }
.cat-picker { position:absolute; top:calc(100% + 8px); left:0; z-index:200; background:var(--color-bg); border:1px solid var(--gray-200); border-radius:0.75rem; box-shadow:0 16px 48px rgba(0,0,0,0.2); padding:1rem; width:290px; opacity:0; visibility:hidden; pointer-events:none; transition:opacity 0.15s,visibility 0.15s; }
.cat-picker.open { opacity:1; visibility:visible; pointer-events:auto; }
.cat-picker-title { font-size:0.75rem; font-weight:600; color:var(--gray-500); text-transform:uppercase; letter-spacing:0.04em; margin-bottom:0.625rem; }
.cat-picker-grid { display:grid; grid-template-columns:repeat(7,1fr); gap:6px; }
.cat-picker-dot { width:30px; height:30px; border-radius:6px; border:2px solid transparent; cursor:pointer; transition:all 0.1s; padding:0; }
.cat-picker-dot:hover { transform:scale(1.2); border-color:rgba(0,0,0,0.3); }
.cat-picker-dot.active { border-color:var(--gray-900); box-shadow:0 0 0 2px var(--color-bg),0 0 0 4px var(--gray-900); transform:scale(1.1); }
.cat-picker-footer { margin-top:0.625rem; padding-top:0.5rem; border-top:1px solid var(--gray-100); }
.cat-picker-reset { width:100%; padding:0.4rem; background:var(--gray-100); border:1px solid var(--gray-200); border-radius:0.375rem; font-size:0.8rem; color:var(--gray-500); cursor:pointer; transition:background 0.15s; font-family:inherit; }
.cat-picker-reset:hover { background:var(--gray-200); }
.cat-list {}
.cat-row { display:flex; align-items:center; gap:0.75rem; padding:0.875rem 1.25rem; border-bottom:1px solid var(--gray-100); transition:background 0.1s; }
.cat-row:last-child { border-bottom:none; }
.cat-row:hover { background:var(--slate-50); }
.cat-name-wrap { flex:1; min-width:0; }
.cat-meta { display:flex; align-items:center; gap:0.375rem; flex-shrink:0; }
.cat-meta-count { font-size:0.85rem; font-weight:700; color:var(--gray-600); background:var(--gray-100); width:28px; height:28px; display:flex; align-items:center; justify-content:center; border-radius:50%; }
.cat-meta-label { font-size:0.78rem; color:var(--gray-400); }
.cat-row-actions { display:flex; gap:0.5rem; flex-shrink:0; }
.cat-empty-hint { margin-top:0.5rem!important; color:var(--gray-400)!important; font-size:0.85rem!important; }
@media(max-width:640px) {
.cat-body { padding:1rem; }
.cat-create-row { flex-direction:column; align-items:stretch; }
.cat-create-row .cat-color-wrap { align-self:flex-start; }
.cat-row { flex-wrap:wrap; gap:0.5rem; }
.cat-name-wrap { flex:1 1 100%; order:2; }
.cat-meta { order:3; }
.cat-row-actions { order:4; margin-left:auto; }
}
</style>
<script>
function togglePicker(prefix) {
var picker = document.getElementById(prefix + '-picker');
document.querySelectorAll('.cat-picker').forEach(function(p) {
if (p !== picker) p.classList.remove('open');
});
picker.classList.toggle('open');
}
function pickColor(prefix, color) {
document.getElementById(prefix + '-color').value = color;
var swatch = document.getElementById(prefix + '-swatch');
swatch.style.background = color;
document.getElementById(prefix + '-picker').classList.remove('open');
}
document.addEventListener('click', function(e) {
if (!e.target.closest('.cat-color-wrap')) {
document.querySelectorAll('.cat-picker').forEach(function(p) {
p.classList.remove('open');
});
}
});
function deleteCat(catId, csrf) {
if (!confirm('Kategorie wirklich l\u00f6schen?')) return;
var form = document.createElement('form');
form.method = 'POST';
form.action = '/kanban/categories/' + catId + '/delete';
form.innerHTML = '<input type="hidden" name="_csrf" value="' + csrf + '">';
document.body.appendChild(form);
form.submit();
}
</script>