OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

categories.php

14 KB
<?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('&#10010; 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&auml;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&auml;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('&#127991; 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">&#127991;</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 &auml;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&auml;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&ouml;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>