diff --git a/Layout/default/Preorder/Index.php b/Layout/default/Preorder/Index.php index f5939034d..7660a9dc1 100644 --- a/Layout/default/Preorder/Index.php +++ b/Layout/default/Preorder/Index.php @@ -100,53 +100,484 @@ $pagination_entity_name = "Vorbestellungen"; } } + + /* styles for documents */ + .document-upload-wrapper { + background: #fdfdfd; + border: 1px solid #e9ecef; + border-radius: .25rem; + } + .document-dropzone { + border: 2px dashed #ced4da; + border-radius: 0.25rem; + padding: 1.5rem; + text-align: center; + background-color: #f8f9fa; + transition: all 0.3s ease; + cursor: pointer; + } + .document-dropzone:hover { + border-color: #007bff; + background-color: #e9ecef; + } + .document-dropzone.active { + border-color: #007bff; + border-style: solid; + } + + .document-staging-area { + max-height: 250px; + overflow-y: auto; + } + .document-staging-item { + display: flex; + align-items: flex-start; + padding: 0.75rem; + border: 1px solid #dee2e6; + border-radius: 0.25rem; + margin-bottom: 0.5rem; + background-color: #fff; + } + .doc-staging-icon { + flex-shrink: 0; + width: 30px; + text-align: center; + padding-top: 0.25rem; + } + .doc-staging-details { + flex-grow: 1; + padding: 0 0.5rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + .doc-staging-filename { + font-size: 0.9em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + .doc-staging-filesize { + font-size: 0.8em; + } + .doc-staging-actions { + flex-shrink: 0; + } + + .document-list-wrapper { + min-height: 300px; + } + .doc-spinner { + display: inline-block; + width: 3rem; + height: 3rem; + vertical-align: text-bottom; + border: 0.25em solid currentColor; + border-right-color: transparent; + border-radius: 50%; + animation: doc-spin 0.75s linear infinite; + color: #007bff; + } + @keyframes doc-spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } + + .doc-row-icon i { font-size: 1.5rem; } + .doc-row-icon .fa-file-pdf { color: #dc3545; } + .doc-row-icon .fa-file-image { color: #28a745; } + .doc-row-icon .fa-file-word { color: #007bff; } + .doc-row-icon .fa-file-excel { color: #207245; } + .doc-row-icon .fa-file-archive { color: #ffc107; } + .doc-row-icon .fa-file { color: #6c757d; } + + .doc-preview-modal-body img, + .doc-preview-modal-body embed, + .doc-preview-modal-body iframe { + max-width: 100%; + max-height: 75vh; + border: none; + } + - + + initPreorderDocumentTabs(); + }); +
diff --git a/Layout/default/Preorder/include/preorder-detail.php b/Layout/default/Preorder/include/preorder-detail.php index 571fc6747..31849a571 100644 --- a/Layout/default/Preorder/include/preorder-detail.php +++ b/Layout/default/Preorder/include/preorder-detail.php @@ -22,6 +22,7 @@ +
@@ -1162,6 +1163,125 @@ +
+ +
+
+
+ +
+
Hochgeladene Dokumente
+
+ + + + + +
+
+ +
+
Neuer Upload
+
+
+
+ +

Dateien hier ablegen oder

+ + +
+
+ + + + +
+
+
+
+
+ + + + + + + +
+ diff --git a/application/File/FileController.php b/application/File/FileController.php index ee57bcc33..9d73018b1 100644 --- a/application/File/FileController.php +++ b/application/File/FileController.php @@ -35,6 +35,7 @@ class FileController extends mfBaseController { } if(preg_match('/\.([^.]+)/',$filename,$m)) { + if (!isset($ext)) $ext = ''; $ext .= $m[1]; } else { throw new Exception("File not found", 4042); diff --git a/application/Preorder/PreorderController.php b/application/Preorder/PreorderController.php index 279298cf5..6466a3be5 100644 --- a/application/Preorder/PreorderController.php +++ b/application/Preorder/PreorderController.php @@ -1985,4 +1985,56 @@ class PreorderController extends mfBaseController { unlink($filename); exit; } + + protected function uploadDocumentsAction() { + if (empty($_FILES['files']) || empty($_POST['preorderId']) || empty($_POST['descriptions'])) { + self::sendError('Erforderliche Daten fehlen (Dateien, preorderId oder Beschreibungen).'); + } + + $preorderId = $_POST['preorderId']; + $descriptions = $_POST['descriptions']; + + if (count($_FILES['files']['name']) !== count($descriptions)) { + self::sendError('Anzahl der Dateien und Beschreibungen stimmt nicht überein.'); + } + + $preorder = new Preorder($preorderId); + if (!$preorder->id) { + self::sendError('Bestellung nicht gefunden.'); + } + + $fileObjects = json_decode($preorder->files, true); + if (!is_array($fileObjects)) $fileObjects = []; + + foreach ($_FILES['files']['name'] as $index => $name) { + if ($_FILES['files']['error'][$index] !== UPLOAD_ERR_OK) continue; + + $_FILES['file'] = [ + 'name' => $name, + 'type' => $_FILES['files']['type'][$index], + 'tmp_name' => $_FILES['files']['tmp_name'][$index], + 'error' => $_FILES['files']['error'][$index], + 'size' => $_FILES['files']['size'][$index] + ]; + + $description = trim($descriptions[$index]); + if (empty($description)) continue; + + try { + $uploaded = mfUpload::handleFormUpload("file", false, "/PreorderDocuments"); + $fileObjects[] = ["id" => $uploaded->id, "description" => $description]; + } catch (Exception $e) {} + } + + $db = FronkDB::singleton(); + $escapedFilesJson = $db->escape(json_encode($fileObjects)); + $sql = "UPDATE `" . FRONKDB_DBNAME . "`.Preorder SET files = '$escapedFilesJson' WHERE id = " . (int)$preorder->id; + $db->query($sql); + + self::returnJson([ + 'success' => true, + 'message' => "Datei(en) erfolgreich hochgeladen.", + 'fileObjects' => $fileObjects + ]); + } } diff --git a/db/migrations/20251024091253_preorder_add_files.php b/db/migrations/20251024091253_preorder_add_files.php new file mode 100644 index 000000000..8fac5627f --- /dev/null +++ b/db/migrations/20251024091253_preorder_add_files.php @@ -0,0 +1,20 @@ +getEnvironment() == "thetool") { + $Preorder = $this->table("Preorder"); + $Preorder->addColumn("files", "text", ['null' => true, 'after' => 'notes']); + $Preorder->update(); + } + } + + public function down(): void { + if($this->getEnvironment() == "thetool") { + $this->table("Preorder")->removeColumn("files")->save(); + } + } +}