Quelltext des aktuellen Standes
<?php
/**
* Wiederverwendbare Datei-Sektion für das Vertrags-Modul (cs- Prefix).
*
* Erwartete Variablen:
* $files array Liste der Dateien (contract_files/company_files)
* $title string Überschrift
* $icon string Roh-SVG für die Überschrift
* $file_base string Basis-URL der Datei-Endpunkte (z. B. '/contracts/files')
* $empty_text string Text wenn keine Dateien vorhanden
* $can_manage bool Darf hochladen?
* $can_delete bool Darf löschen? (Standard: = can_manage)
* $section_class string optionale Zusatzklasse für <section>
* $upload ?array Upload-Formular-Konfig oder null (kein Upload):
* [ 'action' => url, 'input_id' => str,
* 'label' => str, 'file_type' => ?str ]
* $delete_confirm string Bestätigungstext beim Löschen
*/
$files = $files ?? [];
$can_delete = $can_delete ?? ($can_manage ?? false);
$section_class = $section_class ?? '';
$delete_confirm = $delete_confirm ?? 'Datei löschen?';
$upload = $upload ?? null;
?>
<section class="cs-card <?= $section_class ?>">
<div class="cs-card-head">
<h2 class="cs-card-title">
<?= $icon ?>
<?= $this->e($title) ?>
<?php if (!empty($title_sub)): ?><span class="cs-card-title-sub"><?= $this->e($title_sub) ?></span><?php endif; ?>
<?php if (!empty($files)): ?><span class="cs-count"><?= count($files) ?></span><?php endif; ?>
</h2>
<?php if (!empty($head_link)): ?>
<a href="<?= $this->e($head_link['href']) ?>" class="btn btn-default btn-sm"><?= $this->e($head_link['label']) ?> →</a>
<?php endif; ?>
</div>
<?php if (($can_manage ?? false) && $upload !== null): ?>
<form action="<?= $this->e($upload['action']) ?>" method="POST" enctype="multipart/form-data" class="cs-upload-form">
<input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
<?php if (!empty($upload['file_type'])): ?>
<input type="hidden" name="file_type" value="<?= $this->e($upload['file_type']) ?>">
<?php endif; ?>
<label class="cs-upload-drop" for="<?= $this->e($upload['input_id']) ?>">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
<span data-cs-upload-name><?= $this->e($upload['label']) ?></span>
</label>
<input type="file" name="file" id="<?= $this->e($upload['input_id']) ?>" class="cs-upload-hidden"
accept=".pdf,.doc,.docx,.xls,.xlsx,.odt,.ods,.png,.jpg,.jpeg,.gif,.webp,.tif,.tiff,.txt,.csv,.eml,.msg,.zip" required>
<button type="submit" class="btn btn-primary btn-sm"><?= $this->te('Hochladen') ?></button>
</form>
<script>
(function () {
if (!window.cfBindUpload) {
window.cfBindUpload = function (inputId, defaultText) {
var input = document.getElementById(inputId);
if (!input) return;
var label = input.previousElementSibling;
var n = label ? label.querySelector('[data-cs-upload-name]') : null;
function showName() {
if (!n) return;
n.textContent = input.files[0] ? input.files[0].name : defaultText;
n.style.color = input.files[0] ? '#374151' : '';
n.style.fontWeight = input.files[0] ? '500' : '';
}
input.addEventListener('change', showName);
if (label) {
['dragenter', 'dragover'].forEach(function (ev) {
label.addEventListener(ev, function (e) { e.preventDefault(); label.classList.add('cs-upload-dragging'); });
});
['dragleave', 'drop'].forEach(function (ev) {
label.addEventListener(ev, function (e) { e.preventDefault(); label.classList.remove('cs-upload-dragging'); });
});
label.addEventListener('drop', function (e) {
if (e.dataTransfer && e.dataTransfer.files.length) { input.files = e.dataTransfer.files; showName(); }
});
}
};
}
window.cfBindUpload(<?= json_encode($upload['input_id']) ?>, <?= json_encode($upload['label']) ?>);
})();
</script>
<?php endif; ?>
<?php if (empty($files)): ?>
<p class="cs-empty"><?= $this->e($empty_text ?? 'Noch keine Dokumente hochgeladen.') ?></p>
<?php else: ?>
<div class="cs-file-list">
<?php foreach ($files as $f): ?>
<?php
$ext = strtolower(pathinfo($f['original_filename'], PATHINFO_EXTENSION));
$extColor = match ($ext) {
'pdf' => '#dc2626', 'doc', 'docx' => '#2563eb',
'xls', 'xlsx' => '#16a34a', 'jpg', 'jpeg', 'png', 'gif', 'webp' => '#d97706',
default => '#6b7280'
};
?>
<div class="cs-file-row">
<div class="cs-file-icon-wrap">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="20" height="20" style="color:<?= $extColor ?>"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
</div>
<div class="cs-file-info">
<span class="cs-file-name"><?= $this->e($f['original_filename']) ?></span>
<span class="cs-file-meta"><?= $this->formatFileSize((int)$f['file_size']) ?> · <?= $this->relativeTime($f['created_at']) ?></span>
</div>
<?= att_view_link($file_base . '/' . $f['id'] . '/view', $f['original_filename'], $f['mime_type'] ?? '') ?>
<a href="<?= $this->e($file_base) ?>/<?= $f['id'] ?>/download" class="cs-file-dl" title="<?= $this->te('Herunterladen') ?>">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
</a>
<?php if ($can_delete): ?>
<form action="<?= $this->e($file_base) ?>/<?= $f['id'] ?>" method="POST" onsubmit="return confirm(<?= $this->e(json_encode($delete_confirm)) ?>);" style="margin:0;">
<input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
<input type="hidden" name="_method" value="DELETE">
<button type="submit" class="cs-file-del" title="<?= $this->te('Löschen') ?>">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="13" height="13"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg>
</button>
</form>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</section>