OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

responsibilities.php

21 KB
<?php
$topic_ancestors = $topic_ancestors ?? [];

$this->layout('layouts/main', [
    'page_title' => 'Verantwortlichkeiten: ' . $page['title'],
    'breadcrumbs' => [],
    'full_width' => true
]);
?>

<div class="resp-fullwidth">
    <div class="content-header">
        <div>
            <h1><?= $this->te('Verantwortlichkeiten') ?></h1>
            <ol class="breadcrumb">
                <li class="breadcrumb-item"><a href="/"><?= $this->te('Home') ?></a></li>
                <li class="breadcrumb-item"><a href="/topics"><?= $this->te('Themengebiete') ?></a></li>
                <?php foreach ($topic_ancestors as $ancestor): ?>
                    <li class="breadcrumb-item"><a href="/topics/<?= $ancestor['id'] ?>"><?= $this->e($ancestor['name']) ?></a></li>
                <?php endforeach; ?>
                <?php if ($topic): ?>
                    <li class="breadcrumb-item"><a href="/topics/<?= $topic['id'] ?>"><?= $this->e($topic['name']) ?></a></li>
                <?php endif; ?>
                <li class="breadcrumb-item"><a href="/pages/<?= $page['id'] ?>"><?= $this->e($page['title']) ?></a></li>
                <li class="breadcrumb-item active"><?= $this->te('Verantwortlichkeiten') ?></li>
            </ol>
        </div>
        <div class="content-header-actions">
            <a href="/pages/<?= $page['id'] ?>" class="btn btn-default btn-sm"><?= $this->te('&larr; Zur&uuml;ck zur Seite') ?></a>
        </div>
    </div>

    <div class="resp-body">
        <div class="resp-layout">
            <!-- Hauptinhalt -->
            <main class="resp-main">
                <!-- Aktuelle Zuständigkeiten -->
                <div class="resp-card">
                    <div class="resp-card-header">
                        <h2><?= $this->te('&#128101; Aktuelle Zust&auml;ndigkeiten') ?></h2>
                    </div>
                    <div class="resp-card-body">
                        <div class="owner-grid">
                            <div class="owner-card">
                                <div class="owner-type">
                                    <span class="owner-type-icon" aria-hidden="true">&#128736;</span>
                                    <?= $this->te('Technisch verantwortlich') ?>
                                </div>
                                <?php if ($responsibilities && !empty($responsibilities['technical_owner'])): ?>
                                    <div class="owner-info">
                                        <div class="owner-avatar" aria-hidden="true">
                                            <?= $this->e(strtoupper(substr($responsibilities['technical_owner_name'] ?? 'T', 0, 1))) ?>
                                        </div>
                                        <div class="owner-details">
                                            <span class="owner-name"><?= $this->e($responsibilities['technical_owner_name'] ?? '') ?></span>
                                            <?php if (!empty($responsibilities['technical_owner_email'])): ?>
                                                <span class="owner-email"><?= $this->e($responsibilities['technical_owner_email']) ?></span>
                                            <?php endif; ?>
                                        </div>
                                    </div>
                                <?php else: ?>
                                    <div class="not-assigned">
                                        <span class="not-assigned-icon" aria-hidden="true">&#8212;</span>
                                        <?= $this->te('Nicht zugewiesen') ?>
                                    </div>
                                <?php endif; ?>
                            </div>

                            <div class="owner-card">
                                <div class="owner-type">
                                    <span class="owner-type-icon" aria-hidden="true">&#128188;</span>
                                    <?= $this->te('Fachlich verantwortlich') ?>
                                </div>
                                <?php if ($responsibilities && !empty($responsibilities['business_owner'])): ?>
                                    <div class="owner-info">
                                        <div class="owner-avatar" aria-hidden="true">
                                            <?= $this->e(strtoupper(substr($responsibilities['business_owner_name'] ?? 'F', 0, 1))) ?>
                                        </div>
                                        <div class="owner-details">
                                            <span class="owner-name"><?= $this->e($responsibilities['business_owner_name'] ?? '') ?></span>
                                            <?php if (!empty($responsibilities['business_owner_email'])): ?>
                                                <span class="owner-email"><?= $this->e($responsibilities['business_owner_email']) ?></span>
                                            <?php endif; ?>
                                        </div>
                                    </div>
                                <?php else: ?>
                                    <div class="not-assigned">
                                        <span class="not-assigned-icon" aria-hidden="true">&#8212;</span>
                                        <?= $this->te('Nicht zugewiesen') ?>
                                    </div>
                                <?php endif; ?>
                            </div>
                        </div>
                    </div>
                </div>

                <!-- Review-Status -->
                <div class="resp-card">
                    <div class="resp-card-header">
                        <h2><?= $this->te('&#128197; Review-Status') ?></h2>
                    </div>
                    <div class="resp-card-body">
                        <dl class="review-list">
                            <div class="review-row">
                                <dt><?= $this->te('Letzte Pr&uuml;fung') ?></dt>
                                <dd>
                                    <?php if ($responsibilities && !empty($responsibilities['last_review_date'])): ?>
                                        <?= date('d.m.Y', strtotime($responsibilities['last_review_date'])) ?>
                                        <span class="text-muted">von <?= $this->e($responsibilities['last_reviewed_by_name'] ?? 'Unbekannt') ?></span>
                                    <?php else: ?>
                                        <span class="text-muted"><?= $this->te('Noch nie gepr&uuml;ft') ?></span>
                                    <?php endif; ?>
                                </dd>
                            </div>

                            <div class="review-row <?= ($responsibilities && !empty($responsibilities['next_review_date']) && strtotime($responsibilities['next_review_date']) < time()) ? 'overdue' : '' ?>">
                                <dt><?= $this->te('N&auml;chste Pr&uuml;fung') ?></dt>
                                <dd>
                                    <?php if ($responsibilities && !empty($responsibilities['next_review_date'])): ?>
                                        <?php if (strtotime($responsibilities['next_review_date']) < time()): ?>
                                            <span class="status-pill status-pill--danger">
                                                <span aria-hidden="true">&#9888;</span> &Uuml;berf&auml;llig seit <?= date('d.m.Y', strtotime($responsibilities['next_review_date'])) ?>
                                            </span>
                                        <?php else: ?>
                                            <?= date('d.m.Y', strtotime($responsibilities['next_review_date'])) ?>
                                        <?php endif; ?>
                                    <?php else: ?>
                                        <span class="text-muted"><?= $this->te('Nicht festgelegt') ?></span>
                                    <?php endif; ?>
                                </dd>
                            </div>

                            <div class="review-row">
                                <dt><?= $this->te('Review-Intervall') ?></dt>
                                <dd>
                                    <?php if ($responsibilities && !empty($responsibilities['review_interval_days'])): ?>
                                        Alle <?= (int)$responsibilities['review_interval_days'] ?> Tage
                                    <?php else: ?>
                                        <span class="text-muted"><?= $this->te('Standard (365 Tage)') ?></span>
                                    <?php endif; ?>
                                </dd>
                            </div>
                        </dl>

                        <?php if ($can_manage ?? false): ?>
                            <div class="review-action">
                                <form action="/pages/<?= $page['id'] ?>/review" method="POST" class="inline-form">
                                    <input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
                                    <button type="submit" class="btn btn-success">
                                        <span aria-hidden="true">&#9989;</span> <?= $this->te('Als gepr&uuml;ft markieren') ?>
                                    </button>
                                </form>
                            </div>
                        <?php endif; ?>
                    </div>
                </div>

                <!-- Verantwortlichkeiten bearbeiten -->
                <?php if ($can_manage ?? false): ?>
                <div class="resp-card">
                    <div class="resp-card-header">
                        <h2><?= $this->te('&#9998; Verantwortlichkeiten bearbeiten') ?></h2>
                    </div>
                    <div class="resp-card-body">
                        <form action="/pages/<?= $page['id'] ?>/responsibilities" method="POST">
                            <input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
                            <input type="hidden" name="_method" value="PUT">

                            <div class="form-grid-2col">
                                <div class="form-group">
                                    <label for="technical_owner" class="form-label"><?= $this->te('Technisch verantwortlich') ?></label>
                                    <select name="technical_owner" id="technical_owner" class="form-control">
                                        <option value=""><?= $this->te('-- Nicht zugewiesen --') ?></option>
                                        <?php foreach ($users as $user): ?>
                                            <option value="<?= $user['id'] ?>"
                                                    <?= ($responsibilities && ($responsibilities['technical_owner'] ?? '') == $user['id']) ? 'selected' : '' ?>>
                                                <?= $this->e($user['display_name']) ?>
                                            </option>
                                        <?php endforeach; ?>
                                    </select>
                                </div>

                                <div class="form-group">
                                    <label for="business_owner" class="form-label"><?= $this->te('Fachlich verantwortlich') ?></label>
                                    <select name="business_owner" id="business_owner" class="form-control">
                                        <option value=""><?= $this->te('-- Nicht zugewiesen --') ?></option>
                                        <?php foreach ($users as $user): ?>
                                            <option value="<?= $user['id'] ?>"
                                                    <?= ($responsibilities && ($responsibilities['business_owner'] ?? '') == $user['id']) ? 'selected' : '' ?>>
                                                <?= $this->e($user['display_name']) ?>
                                            </option>
                                        <?php endforeach; ?>
                                    </select>
                                </div>
                            </div>

                            <div class="form-grid-2col">
                                <div class="form-group">
                                    <label for="review_interval_days" class="form-label"><?= $this->te('Review-Intervall (Tage)') ?></label>
                                    <input type="number" id="review_interval_days" name="review_interval_days"
                                           class="form-control"
                                           value="<?= $this->e((int)($responsibilities['review_interval_days'] ?? 365)) ?>"
                                           min="1" max="1825">
                                </div>

                                <div class="form-group">
                                    <label for="next_review_date" class="form-label"><?= $this->te('N&auml;chste Pr&uuml;fung') ?></label>
                                    <input type="date" id="next_review_date" name="next_review_date"
                                           class="form-control"
                                           value="<?= $this->e($responsibilities['next_review_date'] ?? '') ?>">
                                </div>
                            </div>

                            <div class="form-actions">
                                <button type="submit" class="btn btn-primary">
                                    <span aria-hidden="true">&#10003;</span> <?= $this->te('Speichern') ?>
                                </button>
                            </div>
                        </form>
                    </div>
                </div>
                <?php endif; ?>
            </main>

            <!-- Sidebar -->
            <aside class="resp-sidebar">
                <div class="sidebar-card">
                    <h3><?= $this->te('&#128712; Informationen') ?></h3>
                    <dl class="info-list">
                        <dt><?= $this->te('Seite') ?></dt>
                        <dd><?= $this->e($page['title']) ?></dd>

                        <?php if ($topic): ?>
                        <dt><?= $this->te('Themengebiet') ?></dt>
                        <dd><?= $this->e($topic['name']) ?></dd>
                        <?php endif; ?>

                        <dt><?= $this->te('Version') ?></dt>
                        <dd>v<?= $this->e($page['version_number'] ?? 1) ?></dd>
                    </dl>
                </div>

                <div class="sidebar-card">
                    <h3><?= $this->te('&#9881; Aktionen') ?></h3>
                    <div class="action-list">
                        <a href="/pages/<?= $page['id'] ?>" class="action-link">
                            <span class="action-icon" aria-hidden="true">&#128196;</span>
                            <?= $this->te('Zur&uuml;ck zur Seite') ?>
                        </a>
                        <a href="/pages/<?= $page['id'] ?>/edit" class="action-link">
                            <span class="action-icon" aria-hidden="true">&#9998;</span>
                            <?= $this->te('Seite bearbeiten') ?>
                        </a>
                        <a href="/pages/<?= $page['id'] ?>/versions" class="action-link">
                            <span class="action-icon" aria-hidden="true">&#128197;</span>
                            <?= $this->te('Versionshistorie') ?>
                        </a>
                    </div>
                </div>
            </aside>
        </div>
    </div>
</div>

<style>
.resp-fullwidth {
}

/* Body */
.resp-body {
    padding: 2rem;
    background: var(--slate-50);
    min-height: calc(100vh - 300px);
}

.resp-layout {
    display: grid;
    grid-template-columns: 1fr 320px;
    gap: 2rem;
    max-width: 1400px;
    margin: 0 auto;
}

/* Main */
.resp-main {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
}

/* Cards */
.resp-card {
    background: var(--color-bg);
    border-radius: 0.75rem;
    box-shadow: 0 1px 3px rgba(0,0,0,0.05);
    overflow: hidden;
}

.resp-card-header {
    padding: 1rem 1.5rem;
    background: var(--gray-50);
    border-bottom: 1px solid var(--gray-200);
}

.resp-card-header h2 {
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
    color: var(--gray-900);
}

.resp-card-body {
    padding: 1.5rem;
}

/* Owner Grid */
.owner-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1.25rem;
}

.owner-card {
    padding: 1.25rem;
    background: var(--gray-50);
    border-radius: 0.5rem;
}

.owner-type {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--gray-500);
    text-transform: uppercase;
    letter-spacing: 0.03em;
    margin-bottom: 1rem;
}

.owner-type-icon {
    font-size: 1rem;
}

.owner-info {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.owner-avatar {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--blue-600), var(--blue-500));
    color: var(--color-on-accent);
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 1rem;
    flex-shrink: 0;
}

.owner-details {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
}

.owner-name {
    font-weight: 600;
    color: var(--gray-900);
    font-size: 0.95rem;
}

.owner-email {
    font-size: 0.8rem;
    color: var(--gray-500);
}

.not-assigned {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    color: var(--gray-400);
    font-size: 0.9rem;
    font-style: italic;
}

.not-assigned-icon {
    font-size: 1.25rem;
}

/* Review List */
.review-list {
    margin: 0;
}

.review-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.875rem 0;
    border-bottom: 1px solid var(--gray-100);
}

.review-row:last-of-type {
    border-bottom: none;
}

.review-row dt {
    font-size: 0.9rem;
    font-weight: 500;
    color: var(--gray-700);
    margin: 0;
}

.review-row dd {
    margin: 0;
    font-size: 0.9rem;
    color: var(--gray-900);
    text-align: right;
}

.text-muted {
    color: var(--gray-400);
    font-size: 0.85rem;
}

.review-action {
    margin-top: 1.25rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--gray-200);
}

.inline-form {
    display: inline;
}

/* Form */
.form-grid-2col {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1.25rem;
    margin-bottom: 1.25rem;
}

.form-group {
    margin-bottom: 0;
}

.form-label {
    display: block;
    font-weight: 500;
    color: var(--gray-700);
    margin-bottom: 0.5rem;
    font-size: 0.9rem;
}
.form-actions {
    padding-top: 0.5rem;
}

/* Sidebar */
.resp-sidebar {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.sidebar-card {
    background: var(--color-bg);
    border-radius: 0.75rem;
    padding: 1.25rem;
    box-shadow: 0 1px 3px rgba(0,0,0,0.05);
}

.sidebar-card h3 {
    margin: 0 0 1rem 0;
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--gray-700);
}

.info-list {
    margin: 0;
}

.info-list dt {
    font-size: 0.75rem;
    color: var(--gray-500);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    margin-top: 0.75rem;
}

.info-list dt:first-child {
    margin-top: 0;
}

.info-list dd {
    margin: 0.25rem 0 0 0;
    font-size: 0.9rem;
    color: var(--gray-900);
}

.action-list {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.action-link {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.625rem 0.75rem;
    border-radius: 0.375rem;
    text-decoration: none;
    color: var(--gray-700);
    font-size: 0.9rem;
    transition: all 0.15s;
}

.action-link:hover {
    background: var(--color-primary-pale);
    color: var(--color-primary);
    text-decoration: none;
}

.action-icon {
    font-size: 1.1rem;
}

/* Responsive */
@media (max-width: 1024px) {
    .resp-layout {
        grid-template-columns: 1fr;
    }

    .resp-sidebar {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    }
}

@media (max-width: 768px) {
    .resp-body {
        padding: 1rem;
    }

    .owner-grid {
        grid-template-columns: 1fr;
    }

    .form-grid-2col {
        grid-template-columns: 1fr;
    }

    .review-row {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.25rem;
    }

    .review-row dd {
        text-align: left;
    }

    .resp-sidebar {
        grid-template-columns: 1fr;
    }
}
</style>