OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

categories.php

15 KB
<?php
$this->layout('layouts/main', [
    'page_title' => 'Vertragskategorien',
    'current_route' => 'contracts',
    'full_width' => true,
    'breadcrumbs' => [],
]);
?>

<div class="cv-wrap">

    <!-- Content Header -->
    <div class="content-header">
        <div>
            <h1><?= $this->te('Vertragskategorien') ?></h1>
            <ol class="breadcrumb">
                <li class="breadcrumb-item"><a href="/"><?= $this->te('Home') ?></a></li>
                <li class="breadcrumb-item"><a href="/contracts"><?= $this->te('Verträge') ?></a></li>
                <li class="breadcrumb-item active"><?= $this->te('Kategorien') ?></li>
            </ol>
        </div>
        <div class="content-header-actions">
            <a href="/contracts" class="btn btn-default">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"><polyline points="15 18 9 12 15 6"/></svg>
                <?= $this->te('Zurück zu Verträgen') ?>
            </a>
        </div>
    </div>

    <div class="page-body">
        <div class="cv-cat-layout">

            <!-- ── Kategorien-Liste ───────────────────────────── -->
            <div class="cv-cat-list-col">
                <div class="cv-card">
                    <div class="cv-card-head">
                        <h2 class="cv-card-title">
                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15"><path d="M4 6h16M4 12h8m-8 6h16"/></svg>
                            <?= $this->te('Kategorien') ?>
                            <span class="cv-count-badge"><?= count($categories) ?></span>
                        </h2>
                    </div>

                    <?php if (empty($categories)): ?>
                        <p class="cv-empty-hint" style="padding:1.5rem;margin:0;"><?= $this->te('Noch keine Kategorien angelegt. Legen Sie die erste Kategorie an.') ?></p>
                    <?php else: ?>
                    <div class="table-wrapper" style="border:none;border-radius:0;">
                        <table class="data-table data-table--fluid">
                            <thead>
                                <tr>
                                    <th scope="col" style="width:36px"></th>
                                    <th scope="col"><?= $this->te('Name') ?></th>
                                    <th scope="col"><?= $this->te('Beschreibung') ?></th>
                                    <th scope="col" style="width:80px;text-align:center"><?= $this->te('Verträge') ?></th>
                                    <?php if ($can_manage): ?>
                                    <th scope="col" style="width:130px"></th>
                                    <?php endif; ?>
                                </tr>
                            </thead>
                            <tbody>
                                <?php foreach ($categories as $cat): ?>
                                <tr class="cv-cat-row">
                                    <td style="text-align:center;">
                                        <span class="cv-cat-dot" style="background:<?= $this->e($cat['color']) ?>;"></span>
                                    </td>
                                    <td>
                                        <strong><?= $this->e($cat['name']) ?></strong>
                                    </td>
                                    <td class="cv-cat-desc">
                                        <?= $this->e($cat['description'] ?? '–') ?>
                                    </td>
                                    <td style="text-align:center;">
                                        <a href="/contracts?category=<?= $cat['id'] ?>" class="cv-cat-count-link">
                                            <?= (int)$cat['contract_count'] ?>
                                        </a>
                                    </td>
                                    <?php if ($can_manage): ?>
                                    <td style="white-space:nowrap;">
                                        <button type="button" class="btn btn-default btn-sm"
                                                onclick="openEditForm(<?= $cat['id'] ?>, '<?= addslashes($this->e($cat['name'])) ?>', '<?= addslashes($this->e($cat['color'])) ?>', '<?= addslashes($this->e($cat['description'] ?? '')) ?>', <?= (int)$cat['sort_order'] ?>)">
                                            <?= $this->te('Bearbeiten') ?>
                                        </button>
                                        <form action="/contracts/categories/<?= $cat['id'] ?>" method="POST" style="display:inline;"
                                              onsubmit="return confirm('Kategorie \'<?= addslashes($this->e($cat['name'])) ?>\' löschen?\nVerträge behalten ihre Daten, verlieren aber die Kategorie.');">
                                            <input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
                                            <input type="hidden" name="_method" value="DELETE">
                                            <button type="submit" class="btn btn-outline-danger btn-sm"><?= $this->te('Löschen') ?></button>
                                        </form>
                                    </td>
                                    <?php endif; ?>
                                </tr>
                                <?php endforeach; ?>
                            </tbody>
                        </table>
                    </div>
                    <?php endif; ?>
                </div>
            </div>

            <!-- ── Formular: Neue Kategorie / Bearbeiten ─────── -->
            <?php if ($can_manage): ?>
            <div class="cv-cat-form-col">
                <div class="cv-card" id="catFormCard">
                    <div class="cv-card-head cv-card-head-green" id="catFormHeadEl">
                        <h2 class="cv-card-title" id="catFormTitle">
                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
                            <?= $this->te('Neue Kategorie') ?>
                        </h2>
                    </div>
                    <div class="cv-card-body">
                        <form id="catForm" action="/contracts/categories" method="POST">
                            <input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
                            <input type="hidden" name="_method" id="catFormMethod" value="">

                            <div class="form-group">
                                <label for="cat_name"><?= $this->te('Name') ?> <span class="text-danger">*</span></label>
                                <input type="text" id="cat_name" name="name" class="form-control" required maxlength="100"
                                       placeholder="<?= $this->te('z.B. Software, Hardware, Infrastruktur…') ?>">
                            </div>

                            <div class="form-group">
                                <label for="cat_color"><?= $this->te('Farbe') ?></label>
                                <div style="display:flex;gap:0.75rem;align-items:center;flex-wrap:wrap;">
                                    <input type="color" id="cat_color" name="color" value="#3c8dbc"
                                           style="width:44px;height:32px;padding:2px;border:1px solid var(--gray-300);border-radius:0.25rem;cursor:pointer;">
                                    <div style="display:flex;gap:0.35rem;flex-wrap:wrap;">
                                        <?php foreach (['#3c8dbc','#16a34a','#dc2626','#d97706','#7c3aed','#0891b2','#db2777','#6b7280','#1a4a7a','#065f46'] as $pc): ?>
                                        <span class="cat-color-preset" data-color="<?= $pc ?>"
                                              style="display:inline-block;width:22px;height:22px;border-radius:50%;background:<?= $pc ?>;cursor:pointer;border:2px solid transparent;"
                                              title="<?= $pc ?>"></span>
                                        <?php endforeach; ?>
                                    </div>
                                </div>
                            </div>

                            <div class="form-group">
                                <label for="cat_description"><?= $this->te('Beschreibung') ?></label>
                                <input type="text" id="cat_description" name="description" class="form-control" maxlength="255"
                                       placeholder="<?= $this->te('Optional: kurze Erläuterung') ?>">
                            </div>

                            <div class="form-group">
                                <label for="cat_sort_order"><?= $this->te('Sortierreihenfolge') ?></label>
                                <input type="number" id="cat_sort_order" name="sort_order" class="form-control" value="0"
                                       min="0" max="999" style="width:90px;">
                                <span class="form-hint"><?= $this->te('Kleinere Zahlen erscheinen zuerst') ?></span>
                            </div>

                            <div style="display:flex;gap:0.5rem;margin-top:0.25rem;">
                                <button type="submit" class="btn btn-primary" id="catFormSubmit"><?= $this->te('Speichern') ?></button>
                                <button type="button" id="catFormReset" class="btn btn-default" onclick="resetForm()" style="display:none;"><?= $this->te('Abbrechen') ?></button>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
            <?php endif; ?>

        </div>
    </div>
</div>

<script>
function openEditForm(id, name, color, description, sortOrder) {
    var iconEdit = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>';
    document.getElementById('catFormTitle').innerHTML = iconEdit + ' Kategorie bearbeiten';
    document.getElementById('catFormHeadEl').className = 'cv-card-head cv-card-head-orange';
    document.getElementById('catForm').action = '/contracts/categories/' + id;
    document.getElementById('catFormMethod').value = 'PUT';
    document.getElementById('cat_name').value = name;
    document.getElementById('cat_color').value = color;
    document.getElementById('cat_description').value = description;
    document.getElementById('cat_sort_order').value = sortOrder;
    document.getElementById('catFormReset').style.display = '';
    document.getElementById('catFormSubmit').textContent = 'Aktualisieren';
    document.getElementById('catForm').scrollIntoView({ behavior: 'smooth', block: 'nearest' });
    document.getElementById('cat_name').focus();
}

function resetForm() {
    var iconNew = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>';
    document.getElementById('catFormTitle').innerHTML = iconNew + ' Neue Kategorie';
    document.getElementById('catFormHeadEl').className = 'cv-card-head cv-card-head-green';
    document.getElementById('catForm').action = '/contracts/categories';
    document.getElementById('catFormMethod').value = '';
    document.getElementById('cat_name').value = '';
    document.getElementById('cat_color').value = '#3c8dbc';
    document.getElementById('cat_description').value = '';
    document.getElementById('cat_sort_order').value = '0';
    document.getElementById('catFormReset').style.display = 'none';
    document.getElementById('catFormSubmit').textContent = 'Speichern';
    document.querySelectorAll('.cat-color-preset').forEach(p => p.style.borderColor = 'transparent');
}

document.querySelectorAll('.cat-color-preset').forEach(function(el) {
    el.addEventListener('click', function() {
        document.getElementById('cat_color').value = this.dataset.color;
        document.querySelectorAll('.cat-color-preset').forEach(p => p.style.borderColor = 'transparent');
        this.style.borderColor = '#374151';
    });
});
document.getElementById('cat_color').addEventListener('input', function() {
    document.querySelectorAll('.cat-color-preset').forEach(p => p.style.borderColor = 'transparent');
});
</script>

<style>
/* ====================================================
   Kategorien-Seite  ·  teilt cv- Prefix mit index.php
   ==================================================== */

.cv-wrap { display: flex; flex-direction: column; }


/* ── Zweispaltiges Layout ─────────────────────────── */
.cv-cat-layout {
    display: grid;
    grid-template-columns: 1fr 360px;
    gap: 1.5rem;
    align-items: start;
}
.cv-cat-layout > * { min-width: 0; }

/* ── Cards ────────────────────────────────────────── */
.cv-card {
    background: var(--color-bg);
    border-radius: 0.25rem;
    box-shadow: 0 1px 3px rgba(0,0,0,0.07);
    overflow: hidden;
}
.cv-card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.875rem 1.25rem;
    border-bottom: 1px solid var(--gray-100);
}
.cv-card-head-green  { border-left: 3px solid var(--green-600); }
.cv-card-head-orange { border-left: 3px solid var(--amber-600); }

.cv-card-title {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    margin: 0;
    font-size: 0.82rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--gray-700);
}
.cv-card-title svg { color: var(--gray-400); flex-shrink: 0; }

.cv-count-badge {
    background: var(--color-primary-soft);
    color: var(--color-primary-strong);
    border-radius: 9999px;
    font-size: 0.7rem;
    font-weight: 600;
    padding: 0.1rem 0.45rem;
    margin-left: 0.25rem;
}

.cv-card-body { padding: 1.25rem; }

/* Tabelle: Standard .table-wrapper/.data-table aus app.css */

.cv-cat-dot {
    display: inline-block;
    width: 14px; height: 14px;
    border-radius: 50%;
    vertical-align: middle;
}
.cv-cat-desc { color: var(--gray-500); font-size: 0.82rem; }
.cv-cat-count-link {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 28px; height: 22px;
    background: var(--color-primary-soft); color: var(--color-primary-strong);
    border-radius: 9999px; font-size: 0.75rem; font-weight: 600;
    text-decoration: none;
    transition: background 0.15s;
}
.cv-cat-count-link:hover { background: var(--color-primary); color: var(--color-on-accent); }

/* ── Leer-Zustand ─────────────────────────────────── */
.cv-empty-hint { color: var(--gray-400); font-size: 0.85rem; }

/* ── Responsive ───────────────────────────────────── */
@media (max-width: 900px) {
    .cv-cat-layout { grid-template-columns: 1fr; }
}

</style>