OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

_item.php

12 KB
<?php
/**
 * Partial: ein einzelnes To-Do (Top-Level oder Unteraufgabe).
 *
 * Erwartete Variablen:
 *   $t    array  – To-Do-Datensatz (siehe TodoController::COLUMNS)
 *   $subs array  – Unteraufgaben (nur bei Top-Level-Items), optional
 *
 * Wird sowohl beim initialen Seitenaufbau als auch von den AJAX-Endpunkten
 * gerendert, damit Server und Client identisches Markup verwenden.
 */
$subs = $subs ?? [];

$isSub   = $t['parent_id'] !== null;
$isDone  = (int)$t['done'] === 1;
$prio    = (int)$t['priority'];
$recur   = (string)($t['recurrence'] ?? 'none');

$today    = date('Y-m-d');
$tomorrow = date('Y-m-d', strtotime('+1 day'));
$hasDue   = !empty($t['due_date']);

$isOverdue = !$isDone && $hasDue && $t['due_date'] < $today;
$isToday   = $hasDue && $t['due_date'] === $today;
$isSoon    = $hasDue && $t['due_date'] === $tomorrow;

$dueCls    = $isOverdue ? 'todo-due--overdue' : ($isToday ? 'todo-due--today' : ($isSoon ? 'todo-due--soon' : ''));
$duePrefix = $isOverdue ? 'Überfällig' : ($isToday ? 'Heute' : ($isSoon ? 'Morgen' : ''));
$dueFmt    = $hasDue ? date('d.m.Y', strtotime((string)$t['due_date'])) : '';
$timeVal   = !empty($t['due_time']) ? substr((string)$t['due_time'], 0, 5) : '';

$prioLabels = [1 => $this->te('Niedrig'), 2 => $this->te('Mittel'), 3 => $this->te('Hoch')];
$recurLabels = ['daily' => $this->te('Täglich'), 'weekly' => $this->te('Wöchentlich'), 'monthly' => $this->te('Monatlich'), 'yearly' => $this->te('Jährlich')];

$subTotal = count($subs);
$subDone  = 0;
foreach ($subs as $s) {
    if ((int)$s['done'] === 1) {
        $subDone++;
    }
}

// Icons
$icoCal   = '<svg viewBox="0 0 16 16" width="11" height="11" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="12" height="11" rx="1.5"/><line x1="5" y1="1" x2="5" y2="5"/><line x1="11" y1="1" x2="11" y2="5"/><line x1="2" y1="7" x2="14" y2="7"/></svg>';
$icoRepeat= '<svg viewBox="0 0 16 16" width="11" height="11" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><polyline points="3,5 3,2 6,2"/><path d="M3 8a5 5 0 0 1 9-3l1 1"/><polyline points="13,11 13,14 10,14"/><path d="M13 8a5 5 0 0 1-9 3l-1-1"/></svg>';
$icoTag   = '<svg viewBox="0 0 16 16" width="10" height="10" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M2 2h5l7 7-5 5-7-7V2z"/><circle cx="5" cy="5" r="1" fill="currentColor" stroke="none"/></svg>';
$icoClock = '<svg viewBox="0 0 16 16" width="10" height="10" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="8" r="6"/><polyline points="8,5 8,8 10,9"/></svg>';
?>
<?php if ($isSub): ?>
<li class="todo-sub<?= $isDone ? ' todo-sub--done' : '' ?>" data-id="<?= (int)$t['id'] ?>">
    <label class="todo-check-wrap todo-check-wrap--sm">
        <input type="checkbox" class="todo-checkbox" <?= $isDone ? 'checked' : '' ?> onchange="Todo.toggleDone(this)">
        <span class="todo-checkmark<?= $isDone ? ' checked' : '' ?>"></span>
    </label>
    <span class="todo-sub-title" ondblclick="Todo.startRename(this)"><?= $this->e($t['title']) ?></span>
    <input type="text" class="todo-rename-input" hidden maxlength="500"
           onblur="Todo.saveRename(this)" onkeydown="Todo.renameKey(event, this)">
    <button class="todo-act todo-act-del" onclick="Todo.del(this)" title="<?= $this->te('Löschen') ?>" type="button">
        <svg viewBox="0 0 16 16" width="12" height="12" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><line x1="2" y1="2" x2="14" y2="14"/><line x1="14" y1="2" x2="2" y2="14"/></svg>
    </button>
</li>
<?php return; ?>
<?php endif; ?>

<li class="todo-item todo-prio--<?= $prio ?><?= $isDone ? ' todo-item--done' : '' ?><?= $isOverdue ? ' todo-item--overdue' : '' ?>"
    data-id="<?= (int)$t['id'] ?>"
    data-due="<?= $this->e($t['due_date'] ?? '') ?>"
    data-time="<?= $this->e($timeVal) ?>"
    data-priority="<?= $prio ?>"
    data-category="<?= $this->e($t['category'] ?? '') ?>"
    data-recurrence="<?= $this->e($recur) ?>"
    data-lead="<?= (int)($t['remind_lead_days'] ?? 1) ?>"
    <?= $isDone ? '' : 'draggable="true"' ?>>

    <?php if (!$isDone): ?>
    <span class="todo-drag" title="<?= $this->te('Ziehen zum Sortieren') ?>">
        <svg viewBox="0 0 12 16" width="12" height="16" fill="none"><circle cx="4" cy="3" r="1.2" fill="currentColor"/><circle cx="4" cy="8" r="1.2" fill="currentColor"/><circle cx="4" cy="13" r="1.2" fill="currentColor"/><circle cx="8" cy="3" r="1.2" fill="currentColor"/><circle cx="8" cy="8" r="1.2" fill="currentColor"/><circle cx="8" cy="13" r="1.2" fill="currentColor"/></svg>
    </span>
    <?php endif; ?>

    <label class="todo-check-wrap">
        <input type="checkbox" class="todo-checkbox" <?= $isDone ? 'checked' : '' ?> onchange="Todo.toggleDone(this)">
        <span class="todo-checkmark<?= $isDone ? ' checked' : '' ?>"></span>
    </label>

    <div class="todo-main">
        <div class="todo-row1">
            <span class="todo-title<?= $isDone ? ' todo-title--done' : '' ?>" <?= $isDone ? '' : 'ondblclick="Todo.startRename(this)"' ?>><?= $this->e($t['title']) ?></span>
            <input type="text" class="todo-rename-input" hidden maxlength="500"
                   onblur="Todo.saveRename(this)" onkeydown="Todo.renameKey(event, this)">
        </div>

<?php $hasMeta = $prio > 0 || $hasDue || $recur !== 'none' || !empty($t['category']) || $subTotal > 0; ?>
        <?php if ($hasMeta): ?>
        <div class="todo-meta">
            <?php if ($prio > 0): ?>
            <span class="todo-badge todo-badge-prio todo-badge-prio--<?= $prio ?>"><span class="todo-prio-dot"></span><?= $prioLabels[$prio] ?></span>
            <?php endif; ?>
            <?php if ($hasDue): ?>
            <span class="todo-badge todo-badge-due <?= $dueCls ?>">
                <?= $icoCal ?><?= $duePrefix !== '' ? $this->e($duePrefix) . ' · ' : '' ?><?= $this->e($dueFmt) ?><?= $timeVal !== '' ? ' · ' . $this->e($timeVal) : '' ?>
            </span>
            <?php endif; ?>
            <?php if ($recur !== 'none'): ?>
            <span class="todo-badge todo-badge-recur" title="<?= $this->te('Wiederholung') ?>"><?= $icoRepeat ?><?= $this->e($recurLabels[$recur] ?? '') ?></span>
            <?php endif; ?>
            <?php if (!empty($t['category'])): ?>
            <span class="todo-badge todo-badge-cat"><?= $icoTag ?><?= $this->e($t['category']) ?></span>
            <?php endif; ?>
            <?php if ($subTotal > 0): ?>
            <span class="todo-badge todo-badge-subs<?= $subDone === $subTotal ? ' is-complete' : '' ?>" title="<?= $this->te('Unteraufgaben') ?>">
                <svg viewBox="0 0 16 16" width="10" height="10" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="2,8 6,12 14,4"/></svg>
                <?= $subDone ?>/<?= $subTotal ?>
            </span>
            <?php endif; ?>
        </div>
        <?php endif; ?>

        <?php if (!empty($t['notes'])): ?>
        <div class="todo-notes-view"><?= nl2br($this->e($t['notes'])) ?></div>
        <?php endif; ?>

        <?php if (!$isDone): ?>
        <!-- Editor-Panel -->
        <div class="todo-editor" hidden>
            <textarea class="todo-f-notes" rows="2" maxlength="5000" placeholder="<?= $this->te('Notiz hinzufügen …') ?>"><?= $this->e($t['notes'] ?? '') ?></textarea>
            <div class="todo-editor-grid">
                <label class="todo-f-label"><?= $this->te('Priorität') ?>
                    <select class="todo-f-priority">
                        <option value="0"<?= $prio === 0 ? ' selected' : '' ?>><?= $this->te('Keine') ?></option>
                        <option value="1"<?= $prio === 1 ? ' selected' : '' ?>><?= $this->te('Niedrig') ?></option>
                        <option value="2"<?= $prio === 2 ? ' selected' : '' ?>><?= $this->te('Mittel') ?></option>
                        <option value="3"<?= $prio === 3 ? ' selected' : '' ?>><?= $this->te('Hoch') ?></option>
                    </select>
                </label>
                <label class="todo-f-label"><?= $this->te('Kategorie') ?>
                    <input type="text" class="todo-f-category" list="todoCatList" maxlength="100"
                           value="<?= $this->e($t['category'] ?? '') ?>" placeholder="<?= $this->te('z. B. Arbeit') ?>">
                </label>
                <label class="todo-f-label"><?= $this->te('Fällig am') ?>
                    <input type="date" class="todo-f-due" value="<?= $this->e($t['due_date'] ?? '') ?>">
                </label>
                <label class="todo-f-label"><?= $this->te('Uhrzeit') ?>
                    <input type="time" class="todo-f-time" value="<?= $this->e($timeVal) ?>">
                </label>
                <label class="todo-f-label"><?= $this->te('Wiederholung') ?>
                    <select class="todo-f-recurrence">
                        <option value="none"<?= $recur === 'none' ? ' selected' : '' ?>><?= $this->te('Keine') ?></option>
                        <option value="daily"<?= $recur === 'daily' ? ' selected' : '' ?>><?= $this->te('Täglich') ?></option>
                        <option value="weekly"<?= $recur === 'weekly' ? ' selected' : '' ?>><?= $this->te('Wöchentlich') ?></option>
                        <option value="monthly"<?= $recur === 'monthly' ? ' selected' : '' ?>><?= $this->te('Monatlich') ?></option>
                        <option value="yearly"<?= $recur === 'yearly' ? ' selected' : '' ?>><?= $this->te('Jährlich') ?></option>
                    </select>
                </label>
                <label class="todo-f-label"><?= $this->te('Erinnerung (Tage vorher)') ?>
                    <input type="number" class="todo-f-lead" min="0" max="60" value="<?= (int)($t['remind_lead_days'] ?? 1) ?>">
                </label>
            </div>
            <div class="todo-editor-actions">
                <button type="button" class="todo-btn-save" onclick="Todo.saveEditor(this)"><?= $this->te('Speichern') ?></button>
                <button type="button" class="todo-btn-cancel" onclick="Todo.toggleEditor(this)"><?= $this->te('Abbrechen') ?></button>
            </div>
        </div>
        <?php endif; ?>

        <?php if (!$isDone): ?>
        <!-- Unteraufgaben -->
        <div class="todo-subs"<?= $subTotal > 0 ? '' : ' hidden' ?>>
            <ul class="todo-sublist">
                <?php foreach ($subs as $s): ?>
                    <?= $this->render('todos/_item', ['t' => $s, 'subs' => []]) ?>
                <?php endforeach; ?>
            </ul>
            <form class="todo-subadd" onsubmit="Todo.addSub(event, this)">
                <input type="text" class="todo-subadd-input" maxlength="500" placeholder="<?= $this->te('Unteraufgabe hinzufügen …') ?>" autocomplete="off">
            </form>
        </div>
        <?php endif; ?>
    </div>

    <div class="todo-actions">
        <?php if (!$isDone): ?>
        <button class="todo-act" onclick="Todo.toggleSubs(this)" title="<?= $this->te('Unteraufgaben') ?>" type="button">
            <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 2v7a3 3 0 0 0 3 3h6"/><polyline points="10,9 13,12 10,15"/></svg>
        </button>
        <button class="todo-act" onclick="Todo.toggleEditor(this)" title="<?= $this->te('Bearbeiten') ?>" type="button">
            <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M11.5 2.5a1.414 1.414 0 0 1 2 2L5 13l-3 1 1-3 8.5-8.5z"/></svg>
        </button>
        <?php elseif ($t['done_at']): ?>
        <span class="todo-done-date"><?= date('d.m.Y', strtotime((string)$t['done_at'])) ?></span>
        <?php endif; ?>
        <button class="todo-act todo-act-del" onclick="Todo.del(this)" title="<?= $this->te('Löschen') ?>" type="button">
            <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><line x1="2" y1="2" x2="14" y2="14"/><line x1="14" y1="2" x2="2" y2="14"/></svg>
        </button>
    </div>
</li>