feat: filemanager s NAS pro projekty

- NasFileManager.php - filesystem helper (browse, upload, download, delete, rename, mkdir)
- project-files.php API - CRUD operace nad soubory projektu
- ProjectFileManager.jsx - React komponenta v detailu projektu
- Automaticke vytvoreni slozky pri vytvoreni projektu (rucne i z objednavky)
- Prejmenovani slozky pri zmene nazvu projektu
- Checkbox "Smazat i soubory na disku" pri mazani projektu/objednavky
- Path traversal ochrana, MIME validace, blocklist nebezpecnych typu
- Bily spinner v primary tlacitkach, ConfirmModal message jako div
- Case-insensitive rename fix pro Windows filesystem

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 13:06:34 +01:00
parent 9e3c95e576
commit 45fd930f76
69 changed files with 2776 additions and 71 deletions

View File

@@ -128,6 +128,11 @@ function handleGetDetail(PDO $pdo, int $id): void
$stmt->execute([$id]);
$order['project'] = $stmt->fetch() ?: null;
if ($order['project']) {
$fm = new NasFileManager();
$order['project']['has_nas_folder'] = $fm->projectFolderExists($order['project']['project_number']);
}
// Get linked invoice
$stmt = $pdo->prepare('SELECT id, invoice_number, status FROM invoices WHERE order_id = ? LIMIT 1');
$stmt->execute([$id]);
@@ -352,6 +357,10 @@ function handleCreateOrder(PDO $pdo): void
$pdo->commit();
$pdo->query("SELECT RELEASE_LOCK('boha_order_number')");
// Vytvorit slozku na NAS pro novy projekt
$fm = new NasFileManager();
$fm->createProjectFolder($orderNumber, $projectName);
AuditLog::logCreate('orders_order', $orderId, [
'order_number' => $orderNumber,
'quotation_number' => $quotation['quotation_number'],
@@ -482,6 +491,9 @@ function handleUpdateOrder(PDO $pdo, int $id): void
function handleDeleteOrder(PDO $pdo, int $id): void
{
$input = getJsonInput();
$deleteFiles = (bool) ($input['delete_files'] ?? false);
$stmt = $pdo->prepare(
'SELECT id, order_number, quotation_id FROM orders WHERE id = ?'
);
@@ -492,8 +504,21 @@ function handleDeleteOrder(PDO $pdo, int $id): void
errorResponse('Objednávka nebyla nalezena', 404);
}
// Nacist projekt pred smazanim (pro NAS slozku)
$stmt = $pdo->prepare('SELECT project_number FROM projects WHERE order_id = ?');
$stmt->execute([$id]);
$project = $stmt->fetch();
$pdo->beginTransaction();
try {
// Delete project notes
if ($project) {
$stmt = $pdo->prepare(
'DELETE FROM project_notes WHERE project_id IN (SELECT id FROM projects WHERE order_id = ?)'
);
$stmt->execute([$id]);
}
// Delete project linked to this order
$stmt = $pdo->prepare('DELETE FROM projects WHERE order_id = ?');
$stmt->execute([$id]);
@@ -515,10 +540,16 @@ function handleDeleteOrder(PDO $pdo, int $id): void
$pdo->commit();
// Smazat NAS slozku pokud pozadovano
if ($deleteFiles && $project) {
$fm = new NasFileManager();
$fm->deleteProjectFolder($project['project_number']);
}
AuditLog::logDelete('orders_order', $id, [
'order_number' => $order['order_number'],
'quotation_id' => $order['quotation_id'],
], "Smazána objednávka '{$order['order_number']}'");
], "Smazána objednávka '{$order['order_number']}'" . ($deleteFiles ? ' (včetně souborů)' : ''));
successResponse(null, 'Objednávka byla smazána');
} catch (PDOException $e) {