www
/
wwwroot
/
scriptexpert.net
/
admin
➕ New
📤 Upload
✎ Editing:
tasks.php
← Back
<?php include('../includes/config.php'); include('../includes/config.inc.php'); $project_id = isset($_GET['project_id']) ? (int)$_GET['project_id'] : 0; if ($project_id <= 0) { header("Location: projects.php?msg=no_project"); exit; } // === Load project (titlu etc.) === $pr = $database->execute("SELECT id, title, slug FROM projects WHERE id = ? LIMIT 1", [$project_id]); if (!$pr['status'] || !$pr['rows']) { header("Location: projects.php?msg=project_not_found"); exit; } $project = $pr['data'][0]; // Helpers function normalize_labels(string $csv): string { $parts = array_filter(array_map(function($s) { $s = trim($s); $s = preg_replace('/\s+/', ' ', $s); return mb_strtolower($s); }, explode(',', $csv))); $uniq = []; foreach ($parts as $p) { if ($p !== '' && !in_array($p, $uniq, true)) $uniq[] = $p; } return implode(',', $uniq); } function checklist_from_textarea(?string $text): array { $items = []; if (!$text) return $items; foreach (preg_split('/\r\n|\r|\n/', $text) as $i => $line) { $label = trim($line); if ($label !== '') $items[] = ['label' => $label, 'is_done' => 0, 'sort_order' => $i]; } return $items; } function json_response(array $payload, int $code = 200): void { http_response_code($code); // elimină absolut orice e în buffer (BOM, spații, warnings) if (function_exists('ob_get_length') && ob_get_length()) { @ob_clean(); } header('Content-Type: application/json; charset=utf-8'); echo json_encode($payload); exit; } $action = $_GET['action'] ?? ''; // ==================== // AJAX: list tasks (JSON, grouped by status, ordered by sort_order) // ==================== if ($action === 'ajax_list') { $sql = " SELECT t.*, (SELECT COUNT(*) FROM project_task_checklist c WHERE c.task_id = t.id) AS total_items, (SELECT COUNT(*) FROM project_task_checklist c WHERE c.task_id = t.id AND c.is_done = 1) AS done_items FROM project_tasks t WHERE t.project_id = ? ORDER BY t.status, t.sort_order ASC, t.id ASC "; $rs = $database->execute($sql, [$project_id]); $out = ['todo'=>[], 'in_progress'=>[], 'done'=>[]]; if ($rs['status'] && $rs['rows']) { foreach ($rs['data'] as $t) { $total = (int)($t['total_items'] ?? 0); $done = (int)($t['done_items'] ?? 0); $t['progress_pct'] = $total > 0 ? (int)round(($done*100)/$total) : 0; $out[$t['status']][] = $t; } } json_response(['ok'=>true,'data'=>$out]); // << fără echo/header manual } // ===================== // AJAX: create / update // ===================== if ($_SERVER['REQUEST_METHOD'] === 'POST' && $action === 'ajax_create_task') { $task_id = (int)($_POST['task_id'] ?? 0); $title = trim($_POST['title'] ?? ''); $status = trim($_POST['status'] ?? 'todo'); // 'todo','in_progress','done' $labels_csv = normalize_labels(trim($_POST['labels_csv'] ?? '')); $description = $_POST['description_html'] ?? ''; $due_date = trim($_POST['due_date'] ?? ''); $check_text = $_POST['checklist_text'] ?? ''; if ($title === '') json_response(['ok'=>false,'error'=>'Title is required'], 400); if ($task_id > 0) { // UPDATE $res = $database->execute(" UPDATE project_tasks SET title = ?, description_html = ?, status = ?, labels_csv = ?, due_date = NULLIF(?, ''), updated_at = NOW() WHERE id = ? AND project_id = ? LIMIT 1 ", [$title, $description, $status, $labels_csv, $due_date, $task_id, $project_id]); if (!$res['status']) json_response(['ok'=>false,'error'=>$res['error'] ?? 'Update failed'], 500); // (opțional) reînlocuiește checklist-ul if (isset($_POST['checklist_json']) || isset($_POST['checklist_text'])) { $database->execute("DELETE FROM project_task_checklist WHERE task_id = ?", [$task_id]); $items = []; $jsonRaw = $_POST['checklist_json'] ?? ''; if ($jsonRaw !== '') { $decoded = json_decode($jsonRaw, true); if (is_array($decoded)) { foreach ($decoded as $i => $it) { $label = trim((string)($it['label'] ?? '')); if ($label === '') continue; $is_done = (int)($it['is_done'] ?? 0) === 1 ? 1 : 0; $sort = (int)($it['sort_order'] ?? $i); $items[] = ['label'=>$label, 'is_done'=>$is_done, 'sort_order'=>$sort]; } } } // fallback pe textarea (ca înainte), implicit is_done=0 if (!$items && isset($_POST['checklist_text'])) { $items = checklist_from_textarea($_POST['checklist_text'] ?? ''); } foreach ($items as $item) { $database->execute(" INSERT INTO project_task_checklist (task_id, label, is_done, sort_order, created_at, updated_at) VALUES (?, ?, ?, ?, NOW(), NOW()) ", [$task_id, $item['label'], (int)$item['is_done'], (int)$item['sort_order']]); } } // returnează task-ul actualizat $row = $database->execute(" SELECT t.*, (SELECT COUNT(*) FROM project_task_checklist c WHERE c.task_id = t.id) AS total_items, (SELECT COUNT(*) FROM project_task_checklist c WHERE c.task_id = t.id AND c.is_done = 1) AS done_items FROM project_tasks t WHERE t.id = ? AND t.project_id = ? LIMIT 1 ", [$task_id, $project_id]); if (!$row['status'] || !$row['rows']) json_response(['ok'=>false,'error'=>'Task reload failed'], 500); $t = $row['data'][0]; $total = (int)($t['total_items'] ?? 0); $done = (int)($t['done_items'] ?? 0); $pct = $total > 0 ? (int)round(($done*100)/$total) : 0; $t['progress_pct'] = $pct; json_response(['ok'=>true,'mode'=>'update','task'=>$t]); } else { // INSERT // sort_order: pune la finalul coloanei $getMax = $database->execute("SELECT COALESCE(MAX(sort_order),0) AS m FROM project_tasks WHERE project_id = ? AND status = ?", [$project_id, $status]); $nextSort = ($getMax['status'] && $getMax['rows']) ? ((int)$getMax['data'][0]['m'] + 1) : 0; $res = $database->execute(" INSERT INTO project_tasks (project_id, title, description_html, status, labels_csv, due_date, sort_order, created_at, updated_at) VALUES (?, ?, ?, ?, ?, NULLIF(?, ''), ?, NOW(), NOW()) ", [$project_id, $title, $description, $status, $labels_csv, $due_date, $nextSort]); if (!$res['status']) json_response(['ok'=>false,'error'=>$res['error'] ?? 'Insert failed'], 500); $newId = (int)($res['last_id'] ?? 0); $items = []; $jsonRaw = $_POST['checklist_json'] ?? ''; if ($jsonRaw !== '') { $decoded = json_decode($jsonRaw, true); if (is_array($decoded)) { foreach ($decoded as $i => $it) { $label = trim((string)($it['label'] ?? '')); if ($label === '') continue; $is_done = (int)($it['is_done'] ?? 0) === 1 ? 1 : 0; $sort = (int)($it['sort_order'] ?? $i); $items[] = ['label'=>$label, 'is_done'=>$is_done, 'sort_order'=>$sort]; } } } // fallback pe textarea (ca înainte), implicit is_done=0 if (!$items && isset($_POST['checklist_text'])) { $items = checklist_from_textarea($_POST['checklist_text'] ?? ''); } foreach ($items as $item) { $database->execute(" INSERT INTO project_task_checklist (task_id, label, is_done, sort_order, created_at, updated_at) VALUES (?, ?, ?, ?, NOW(), NOW()) ", [$newId, $item['label'], (int)$item['is_done'], (int)$item['sort_order']]); } $row = $database->execute(" SELECT t.*, (SELECT COUNT(*) FROM project_task_checklist c WHERE c.task_id = t.id) AS total_items, (SELECT COUNT(*) FROM project_task_checklist c WHERE c.task_id = t.id AND c.is_done = 1) AS done_items FROM project_tasks t WHERE t.id = ? AND t.project_id = ? LIMIT 1 ", [$newId, $project_id]); if (!$row['status'] || !$row['rows']) json_response(['ok'=>false,'error'=>'Task fetch failed'], 500); $t = $row['data'][0]; $total = (int)($t['total_items'] ?? 0); $done = (int)($t['done_items'] ?? 0); $pct = $total > 0 ? (int)round(($done*100)/$total) : 0; $t['progress_pct'] = $pct; json_response(['ok'=>true,'mode'=>'create','task'=>$t]); } } // ==================== // AJAX: move (drag&drop) // ==================== if ($_SERVER['REQUEST_METHOD'] === 'POST' && $action === 'ajax_move_task') { $task_id = (int)($_POST['task_id'] ?? 0); $new_status = trim($_POST['new_status'] ?? 'todo'); // 'todo','in_progress','done' if ($task_id <= 0) json_response(['ok'=>false,'error'=>'Bad task id'], 400); // pune la finalul noii coloane $getMax = $database->execute("SELECT COALESCE(MAX(sort_order),0) AS m FROM project_tasks WHERE project_id = ? AND status = ?", [$project_id, $new_status]); $nextSort = ($getMax['status'] && $getMax['rows']) ? ((int)$getMax['data'][0]['m'] + 1) : 0; $res = $database->execute(" UPDATE project_tasks SET status = ?, sort_order = ?, updated_at = NOW() WHERE id = ? AND project_id = ? LIMIT 1 ", [$new_status, $nextSort, $task_id, $project_id]); if (!$res['status']) json_response(['ok'=>false,'error'=>$res['error'] ?? 'Move failed'], 500); json_response(['ok'=>true]); } // ==================== // AJAX: reorder within a column (set status + sort_order) // ==================== if ($_SERVER['REQUEST_METHOD'] === 'POST' && $action === 'ajax_reorder_column') { $status = $_POST['status'] ?? 'todo'; // 'todo','in_progress','done' $ids = $_POST['ids'] ?? $_POST['ids'] ?? []; // acceptă și string "1,2,3" / "1|2|3" dacă vine greșit if (!is_array($ids)) { $tmp = preg_split('/[,\|\s]+/', (string)$ids, -1, PREG_SPLIT_NO_EMPTY); $ids = $tmp ?: []; } // sanităm $clean = []; foreach ($ids as $v) { $iv = (int)$v; if ($iv > 0) $clean[] = $iv; } if (empty($clean)) { json_response(['ok'=>false,'error'=>'No valid ids'], 400); } if (!in_array($status, ['todo','in_progress','done'], true)) { json_response(['ok'=>false,'error'=>'Bad status'], 400); } // persistăm în tranzacție $database->begin(); $ok = true; foreach ($clean as $idx => $tid) { $res = $database->execute(" UPDATE project_tasks SET status = ?, sort_order = ?, updated_at = NOW() WHERE id = ? AND project_id = ? LIMIT 1 ", [$status, $idx, $tid, $project_id]); if (!$res['status']) { $ok = false; break; } } if ($ok) { $database->commit(); } else { $database->rollback(); } json_response(['ok'=>$ok, 'count'=>count($clean)]); } // ==================== // Delete (non-AJAX link) // ==================== if ($action === 'delete' && isset($_GET['id'])) { $tid = (int)$_GET['id']; if ($tid > 0) { $database->execute("DELETE FROM project_tasks WHERE id = ? AND project_id = ? LIMIT 1", [$tid, $project_id]); } header("Location: tasks.php?project_id={$project_id}&msg=task_deleted"); exit; } // ==================== // AJAX: checklist for a task // ==================== if ($action === 'ajax_checklist') { $task_id = (int)($_GET['task_id'] ?? $_POST['task_id'] ?? 0); if ($task_id <= 0) json_response(['ok'=>false,'error'=>'Bad task id'], 400); $rs = $database->execute(" SELECT id, label, is_done, sort_order FROM project_task_checklist WHERE task_id = ? ORDER BY sort_order ASC, id ASC ", [$task_id]); if (!$rs['status']) json_response(['ok'=>false,'error'=>$rs['error'] ?? 'Checklist fetch failed'], 500); json_response(['ok'=>true,'items'=>$rs['data'] ?? []]); } // ==================== // Render standard page // ==================== $sql = " SELECT t.*, (SELECT COUNT(*) FROM project_task_checklist c WHERE c.task_id = t.id) AS total_items, (SELECT COUNT(*) FROM project_task_checklist c WHERE c.task_id = t.id AND c.is_done = 1) AS done_items FROM project_tasks t WHERE t.project_id = ? ORDER BY t.sort_order ASC, t.id DESC "; $all = $database->execute($sql, [$project_id]); $tasks = $all['status'] ? $all['data'] : []; $tasks_todo = []; $tasks_inp = []; $tasks_done = []; foreach ($tasks as $t) { $total = (int)($t['total_items'] ?? 0); $done = (int)($t['done_items'] ?? 0); $pct = $total > 0 ? (int)round(($done*100)/$total) : 0; $t['progress_pct'] = $pct; $labels = array_filter(array_map('trim', explode(',', (string)$t['labels_csv']))); $t['labels'] = $labels; if ($t['status'] === 'todo') $tasks_todo[] = $t; elseif ($t['status'] === 'in_progress') $tasks_inp[] = $t; else $tasks_done[] = $t; } $msg = $_GET['msg'] ?? ''; $smarty->assign('project', $project); $smarty->assign('project_id', $project_id); $smarty->assign('tasks_todo', $tasks_todo); $smarty->assign('tasks_inp', $tasks_inp); $smarty->assign('tasks_done', $tasks_done); $smarty->assign('msg', $msg); $smarty->display(ADMIN_TEMPLATE_PATH . 'tasks.html');
💾 Save Changes
Cancel
📤 Upload File
×
Select File
Upload
Cancel
➕ Create New
×
Type
📄 File
📁 Folder
Name
Create
Cancel
✎ Rename Item
×
Current Name
New Name
Rename
Cancel
🔐 Change Permissions
×
Target File
Permission (e.g., 0755, 0644)
0755
0644
0777
Apply
Cancel