| @@ -79,6 +79,24 @@ class ColumnsController extends Controller | |||
| return $this->json(['ok' => true]); | |||
| } | |||
| public function toggleExport(Request $request, int $id): mixed | |||
| { | |||
| if (!AuthService::isLoggedIn()) { | |||
| return $this->json(['ok' => false, 'error' => 'Unauthorized'], 401); | |||
| } | |||
| $row = $this->columns()->find($id); | |||
| if ($row === null) { | |||
| return $this->json(['ok' => false, 'error' => 'Not found'], 404); | |||
| } | |||
| $show = (string) $request->input('show_export_button', '0') === '1'; | |||
| $this->columns()->updateShowExportButton($id, $show, date('Y-m-d H:i:s'), AuthService::getCurrentUsername()); | |||
| return $this->json(['ok' => true, 'show_export_button' => $show]); | |||
| } | |||
| public function toggleCount(Request $request, int $id): mixed | |||
| { | |||
| if (!AuthService::isLoggedIn()) { | |||
| @@ -141,7 +159,7 @@ class ColumnsController extends Controller | |||
| $cards = $this->cards()->findByColumnId($id); | |||
| $out = fopen('php://memory', 'wb'); | |||
| fputcsv($out, ['Job #', 'Job Name', 'Customer', 'Delivery Date', 'Quantity', 'Notes']); | |||
| fputcsv($out, ['Job #', 'Job Name', 'Customer', 'Delivery Date', 'Quantity', 'Notes'], ',', '"', '\\'); | |||
| foreach ($cards as $card) { | |||
| fputcsv($out, [ | |||
| $card->jobNumber, | |||
| @@ -150,7 +168,7 @@ class ColumnsController extends Controller | |||
| $card->deliveryDate ?? '', | |||
| $card->quantity ?? '', | |||
| $card->notes, | |||
| ]); | |||
| ], ',', '"', '\\'); | |||
| } | |||
| rewind($out); | |||
| $csv = (string) stream_get_contents($out); | |||
| @@ -79,6 +79,24 @@ class SwimLanesController extends Controller | |||
| return $this->json(['ok' => true]); | |||
| } | |||
| public function toggleExport(Request $request, int $id): mixed | |||
| { | |||
| if (!AuthService::isLoggedIn()) { | |||
| return $this->json(['ok' => false, 'error' => 'Unauthorized'], 401); | |||
| } | |||
| $row = $this->lanes()->find($id); | |||
| if ($row === null) { | |||
| return $this->json(['ok' => false, 'error' => 'Not found'], 404); | |||
| } | |||
| $show = (string) $request->input('show_export_button', '0') === '1'; | |||
| $this->lanes()->updateShowExportButton($id, $show, date('Y-m-d H:i:s'), AuthService::getCurrentUsername()); | |||
| return $this->json(['ok' => true, 'show_export_button' => $show]); | |||
| } | |||
| public function updateCardAgeSettings(Request $request, int $id): mixed | |||
| { | |||
| if (!AuthService::isLoggedIn()) { | |||
| @@ -123,7 +141,7 @@ class SwimLanesController extends Controller | |||
| $cards = $this->cards()->findBySwimLaneId($id); | |||
| $out = fopen('php://memory', 'wb'); | |||
| fputcsv($out, ['Job #', 'Job Name', 'Customer', 'Delivery Date', 'Quantity', 'Notes']); | |||
| fputcsv($out, ['Job #', 'Job Name', 'Customer', 'Delivery Date', 'Quantity', 'Notes'], ',', '"', '\\'); | |||
| foreach ($cards as $card) { | |||
| fputcsv($out, [ | |||
| $card->jobNumber, | |||
| @@ -132,7 +150,7 @@ class SwimLanesController extends Controller | |||
| $card->deliveryDate ?? '', | |||
| $card->quantity ?? '', | |||
| $card->notes, | |||
| ]); | |||
| ], ',', '"', '\\'); | |||
| } | |||
| rewind($out); | |||
| $csv = (string) stream_get_contents($out); | |||
| @@ -11,6 +11,7 @@ class BoardColumn | |||
| public string $name = ''; | |||
| public int $position = 0; | |||
| public bool $showCardCount = false; | |||
| public bool $showExportButton = false; | |||
| public bool $showCardAge = false; | |||
| public int $cardAgeWarningDays = 0; | |||
| public ?string $createdAt = null; | |||
| @@ -26,6 +27,7 @@ class BoardColumn | |||
| $model->name = (string) ($row['name'] ?? ''); | |||
| $model->position = (int) ($row['position'] ?? 0); | |||
| $model->showCardCount = (bool) ($row['show_card_count'] ?? false); | |||
| $model->showExportButton = (bool) ($row['show_export_button'] ?? false); | |||
| $model->showCardAge = (bool) ($row['show_card_age'] ?? false); | |||
| $model->cardAgeWarningDays = (int) ($row['card_age_warning_days'] ?? 0); | |||
| $model->createdAt = $row['created_at'] ?? null; | |||
| @@ -10,6 +10,7 @@ class SwimLane | |||
| public int $boardId = 0; | |||
| public string $name = ''; | |||
| public int $position = 0; | |||
| public bool $showExportButton = false; | |||
| public bool $showCardAge = false; | |||
| public int $cardAgeWarningDays = 0; | |||
| public ?string $createdAt = null; | |||
| @@ -24,6 +25,7 @@ class SwimLane | |||
| $model->boardId = (int) ($row['board_id'] ?? 0); | |||
| $model->name = (string) ($row['name'] ?? ''); | |||
| $model->position = (int) ($row['position'] ?? 0); | |||
| $model->showExportButton = (bool) ($row['show_export_button'] ?? false); | |||
| $model->showCardAge = (bool) ($row['show_card_age'] ?? false); | |||
| $model->cardAgeWarningDays = (int) ($row['card_age_warning_days'] ?? 0); | |||
| $model->createdAt = $row['created_at'] ?? null; | |||
| @@ -35,13 +35,14 @@ class BoardColumnRepository extends Repository | |||
| public function insert(BoardColumn $col): BoardColumn | |||
| { | |||
| $this->database->execute( | |||
| 'INSERT INTO board_columns (board_id, name, position, show_card_count, show_card_age, card_age_warning_days, created_at, created_by, updated_at, updated_by) | |||
| VALUES (:board_id, :name, :position, :show_card_count, :show_card_age, :card_age_warning_days, :created_at, :created_by, :updated_at, :updated_by)', | |||
| 'INSERT INTO board_columns (board_id, name, position, show_card_count, show_export_button, show_card_age, card_age_warning_days, created_at, created_by, updated_at, updated_by) | |||
| VALUES (:board_id, :name, :position, :show_card_count, :show_export_button, :show_card_age, :card_age_warning_days, :created_at, :created_by, :updated_at, :updated_by)', | |||
| [ | |||
| 'board_id' => $col->boardId, | |||
| 'name' => $col->name, | |||
| 'position' => $col->position, | |||
| 'show_card_count' => $col->showCardCount ? 1 : 0, | |||
| 'show_export_button' => $col->showExportButton ? 1 : 0, | |||
| 'show_card_age' => $col->showCardAge ? 1 : 0, | |||
| 'card_age_warning_days' => $col->cardAgeWarningDays, | |||
| 'created_at' => $col->createdAt, | |||
| @@ -65,6 +66,14 @@ class BoardColumnRepository extends Repository | |||
| ); | |||
| } | |||
| public function updateShowExportButton(int $id, bool $show, string $updatedAt, string $updatedBy): void | |||
| { | |||
| $this->database->execute( | |||
| 'UPDATE board_columns SET show_export_button = :show_export_button, updated_at = :updated_at, updated_by = :updated_by WHERE id = :id', | |||
| ['show_export_button' => $show ? 1 : 0, 'updated_at' => $updatedAt, 'updated_by' => $updatedBy, 'id' => $id] | |||
| ); | |||
| } | |||
| public function updateShowCardCount(int $id, bool $show, string $updatedAt, string $updatedBy): void | |||
| { | |||
| $this->database->execute( | |||
| @@ -35,12 +35,13 @@ class SwimLaneRepository extends Repository | |||
| public function insert(SwimLane $lane): SwimLane | |||
| { | |||
| $this->database->execute( | |||
| 'INSERT INTO swim_lanes (board_id, name, position, show_card_age, card_age_warning_days, created_at, created_by, updated_at, updated_by) | |||
| VALUES (:board_id, :name, :position, :show_card_age, :card_age_warning_days, :created_at, :created_by, :updated_at, :updated_by)', | |||
| 'INSERT INTO swim_lanes (board_id, name, position, show_export_button, show_card_age, card_age_warning_days, created_at, created_by, updated_at, updated_by) | |||
| VALUES (:board_id, :name, :position, :show_export_button, :show_card_age, :card_age_warning_days, :created_at, :created_by, :updated_at, :updated_by)', | |||
| [ | |||
| 'board_id' => $lane->boardId, | |||
| 'name' => $lane->name, | |||
| 'position' => $lane->position, | |||
| 'show_export_button' => $lane->showExportButton ? 1 : 0, | |||
| 'show_card_age' => $lane->showCardAge ? 1 : 0, | |||
| 'card_age_warning_days' => $lane->cardAgeWarningDays, | |||
| 'created_at' => $lane->createdAt, | |||
| @@ -64,6 +65,14 @@ class SwimLaneRepository extends Repository | |||
| ); | |||
| } | |||
| public function updateShowExportButton(int $id, bool $show, string $updatedAt, string $updatedBy): void | |||
| { | |||
| $this->database->execute( | |||
| 'UPDATE swim_lanes SET show_export_button = :show_export_button, updated_at = :updated_at, updated_by = :updated_by WHERE id = :id', | |||
| ['show_export_button' => $show ? 1 : 0, 'updated_at' => $updatedAt, 'updated_by' => $updatedBy, 'id' => $id] | |||
| ); | |||
| } | |||
| public function updateCardAgeSettings(int $id, bool $showCardAge, int $cardAgeWarningDays, string $updatedAt, string $updatedBy): void | |||
| { | |||
| $this->database->execute( | |||
| @@ -53,6 +53,9 @@ | |||
| <?php foreach ($columns as $col): ?> | |||
| <div class="kanban-col-header" data-col-id="<?= e((string) $col->id) ?>"> | |||
| <span class="col-label"><?= e($col->name) ?></span> | |||
| <?php if ($col->showExportButton): ?> | |||
| <a href="/columns/<?= e((string) $col->id) ?>/export" class="col-export-btn" title="Export column to CSV"><i class="bi bi-download"></i></a> | |||
| <?php endif; ?> | |||
| </div> | |||
| <?php endforeach; ?> | |||
| @@ -65,6 +68,9 @@ | |||
| <i class="bi bi-chevron-down" aria-hidden="true"></i> | |||
| </button> | |||
| <span class="lane-label"><?= e($lane->name) ?></span> | |||
| <?php if ($lane->showExportButton): ?> | |||
| <a href="/swimlanes/<?= e((string) $lane->id) ?>/export" class="lane-export-btn" title="Export swim lane to CSV"><i class="bi bi-download"></i></a> | |||
| <?php endif; ?> | |||
| </div> | |||
| <?php foreach ($columns as $col): ?> | |||
| <div class="kanban-cell" | |||
| @@ -86,8 +92,8 @@ var KANBAN = { | |||
| boardSlug: "<?= e($board->slug) ?>", | |||
| cards: <?= $cardsJson ?> | |||
| }; | |||
| var KANBAN_COLS = <?= json_encode(array_map(fn($c) => ['id' => $c->id, 'name' => $c->name, 'position' => $c->position, 'show_card_count' => $c->showCardCount, 'show_card_age' => $c->showCardAge, 'card_age_warning_days' => $c->cardAgeWarningDays], $columns)) ?>; | |||
| var KANBAN_LANES = <?= json_encode(array_map(fn($l) => ['id' => $l->id, 'name' => $l->name, 'position' => $l->position, 'show_card_age' => $l->showCardAge, 'card_age_warning_days' => $l->cardAgeWarningDays], $lanes)) ?>; | |||
| var KANBAN_COLS = <?= json_encode(array_map(fn($c) => ['id' => $c->id, 'name' => $c->name, 'position' => $c->position, 'show_card_count' => $c->showCardCount, 'show_export_button' => $c->showExportButton, 'show_card_age' => $c->showCardAge, 'card_age_warning_days' => $c->cardAgeWarningDays], $columns)) ?>; | |||
| var KANBAN_LANES = <?= json_encode(array_map(fn($l) => ['id' => $l->id, 'name' => $l->name, 'position' => $l->position, 'show_export_button' => $l->showExportButton, 'show_card_age' => $l->showCardAge, 'card_age_warning_days' => $l->cardAgeWarningDays], $lanes)) ?>; | |||
| </script> | |||
| <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script> | |||
| @@ -37,12 +37,13 @@ | |||
| <input class="form-check-input col-count-toggle" type="checkbox" role="switch" | |||
| <?= $col->showCardCount ? 'checked' : '' ?>> | |||
| </div> | |||
| <div class="form-check form-switch m-0" title="Show export button on board"> | |||
| <input class="form-check-input col-export-toggle" type="checkbox" role="switch" | |||
| <?= $col->showExportButton ? 'checked' : '' ?>> | |||
| </div> | |||
| <button class="btn btn-sm btn-link p-0 text-secondary btn-toggle-col-age" title="Card age settings"> | |||
| <i class="bi bi-clock-history"></i> | |||
| </button> | |||
| <button class="btn btn-sm btn-link p-0 text-secondary btn-export-col" title="Export to CSV"> | |||
| <i class="bi bi-download"></i> | |||
| </button> | |||
| <button class="btn btn-sm btn-link p-0 text-secondary btn-edit-col" title="Rename"> | |||
| <i class="bi bi-pencil"></i> | |||
| </button> | |||
| @@ -93,12 +94,13 @@ | |||
| <div class="d-flex align-items-center gap-2"> | |||
| <i class="bi bi-grip-vertical text-muted drag-handle" style="cursor:grab;"></i> | |||
| <span class="flex-grow-1 lane-label-text"><?= e($lane->name) ?></span> | |||
| <div class="form-check form-switch m-0" title="Show export button on board"> | |||
| <input class="form-check-input lane-export-toggle" type="checkbox" role="switch" | |||
| <?= $lane->showExportButton ? 'checked' : '' ?>> | |||
| </div> | |||
| <button class="btn btn-sm btn-link p-0 text-secondary btn-toggle-lane-age" title="Card age settings"> | |||
| <i class="bi bi-clock-history"></i> | |||
| </button> | |||
| <button class="btn btn-sm btn-link p-0 text-secondary btn-export-lane" title="Export to CSV"> | |||
| <i class="bi bi-download"></i> | |||
| </button> | |||
| <button class="btn btn-sm btn-link p-0 text-secondary btn-edit-lane" title="Rename"> | |||
| <i class="bi bi-pencil"></i> | |||
| </button> | |||
| @@ -0,0 +1,21 @@ | |||
| <?php | |||
| declare(strict_types=1); | |||
| use Core\Database; | |||
| use Core\Migration; | |||
| return new class extends Migration | |||
| { | |||
| public function up(Database $database): void | |||
| { | |||
| $database->execute( | |||
| 'ALTER TABLE board_columns ADD COLUMN show_export_button INTEGER NOT NULL DEFAULT 0' | |||
| ); | |||
| } | |||
| public function down(Database $database): void | |||
| { | |||
| // SQLite cannot drop columns without recreating the table. | |||
| } | |||
| }; | |||
| @@ -0,0 +1,21 @@ | |||
| <?php | |||
| declare(strict_types=1); | |||
| use Core\Database; | |||
| use Core\Migration; | |||
| return new class extends Migration | |||
| { | |||
| public function up(Database $database): void | |||
| { | |||
| $database->execute( | |||
| 'ALTER TABLE swim_lanes ADD COLUMN show_export_button INTEGER NOT NULL DEFAULT 0' | |||
| ); | |||
| } | |||
| public function down(Database $database): void | |||
| { | |||
| // SQLite cannot drop columns without recreating the table. | |||
| } | |||
| }; | |||
| @@ -6,6 +6,8 @@ | |||
| var laneCollapseStorageKey = 'kanban_lane_collapsed_' + String(boardId); | |||
| var collapsedLaneIds = loadCollapsedLaneIds(); | |||
| var columnShowCount = {}; | |||
| var columnShowExport = {}; | |||
| var laneShowExport = {}; | |||
| var columnAgeSettings = {}; | |||
| var laneAgeSettings = {}; | |||
| var searchState = { | |||
| @@ -152,6 +154,53 @@ | |||
| }); | |||
| } | |||
| function refreshColumnExport(colId) { | |||
| var header = document.querySelector('.kanban-col-header[data-col-id="' + colId + '"]'); | |||
| if (!header) return; | |||
| var btn = header.querySelector('.col-export-btn'); | |||
| if (columnShowExport[String(colId)]) { | |||
| if (!btn) { | |||
| btn = document.createElement('a'); | |||
| btn.className = 'col-export-btn'; | |||
| btn.href = '/columns/' + colId + '/export'; | |||
| btn.title = 'Export column to CSV'; | |||
| btn.innerHTML = '<i class="bi bi-download"></i>'; | |||
| header.appendChild(btn); | |||
| } | |||
| } else { | |||
| if (btn) btn.remove(); | |||
| } | |||
| } | |||
| function refreshLaneExport(laneId) { | |||
| var header = document.querySelector('.kanban-lane-header[data-lane-id="' + laneId + '"]'); | |||
| if (!header) return; | |||
| var btn = header.querySelector('.lane-export-btn'); | |||
| if (laneShowExport[String(laneId)]) { | |||
| if (!btn) { | |||
| btn = document.createElement('a'); | |||
| btn.className = 'lane-export-btn'; | |||
| btn.href = '/swimlanes/' + laneId + '/export'; | |||
| btn.title = 'Export swim lane to CSV'; | |||
| btn.innerHTML = '<i class="bi bi-download"></i>'; | |||
| header.appendChild(btn); | |||
| } | |||
| } else { | |||
| if (btn) btn.remove(); | |||
| } | |||
| } | |||
| function initExportButtons() { | |||
| KANBAN_COLS.forEach(function (col) { | |||
| columnShowExport[String(col.id)] = !!col.show_export_button; | |||
| refreshColumnExport(col.id); | |||
| }); | |||
| KANBAN_LANES.forEach(function (lane) { | |||
| laneShowExport[String(lane.id)] = !!lane.show_export_button; | |||
| refreshLaneExport(lane.id); | |||
| }); | |||
| } | |||
| function initCardAgeSettings() { | |||
| KANBAN_COLS.forEach(function (col) { | |||
| columnAgeSettings[String(col.id)] = { | |||
| @@ -505,6 +554,7 @@ | |||
| grid.insertBefore(hdr, refNode); | |||
| columnShowCount[String(col.id)] = false; | |||
| columnShowExport[String(col.id)] = false; | |||
| columnAgeSettings[String(col.id)] = { showCardAge: false, cardAgeWarningDays: 0 }; | |||
| var laneHeaders = grid.querySelectorAll('.kanban-lane-header'); | |||
| @@ -526,6 +576,7 @@ | |||
| document.querySelectorAll('.kanban-cell[data-col-id="' + colId + '"]').forEach(function (el) { el.remove(); }); | |||
| KANBAN.cards = KANBAN.cards.filter(function (c) { return String(c.column_id) !== String(colId); }); | |||
| delete columnShowCount[String(colId)]; | |||
| delete columnShowExport[String(colId)]; | |||
| delete columnAgeSettings[String(colId)]; | |||
| applyGridTemplate(); | |||
| }, | |||
| @@ -544,6 +595,7 @@ | |||
| grid.appendChild(lh); | |||
| bindLaneHeaderToggle(lh); | |||
| laneShowExport[String(lane.id)] = false; | |||
| laneAgeSettings[String(lane.id)] = { showCardAge: false, cardAgeWarningDays: 0 }; | |||
| colHeaders.forEach(function (ch) { | |||
| @@ -565,6 +617,7 @@ | |||
| document.querySelector('.kanban-lane-header[data-lane-id="' + laneId + '"]').remove(); | |||
| document.querySelectorAll('.kanban-cell[data-lane-id="' + laneId + '"]').forEach(function (el) { el.remove(); }); | |||
| KANBAN.cards = KANBAN.cards.filter(function (c) { return String(c.swim_lane_id) !== String(laneId); }); | |||
| delete laneShowExport[String(laneId)]; | |||
| delete laneAgeSettings[String(laneId)]; | |||
| if (collapsedLaneIds[String(laneId)]) { | |||
| delete collapsedLaneIds[String(laneId)]; | |||
| @@ -583,6 +636,14 @@ | |||
| columnShowCount[String(colId)] = show; | |||
| refreshColumnCount(colId); | |||
| }, | |||
| setColumnShowExport: function (colId, show) { | |||
| columnShowExport[String(colId)] = !!show; | |||
| refreshColumnExport(colId); | |||
| }, | |||
| setLaneShowExport: function (laneId, show) { | |||
| laneShowExport[String(laneId)] = !!show; | |||
| refreshLaneExport(laneId); | |||
| }, | |||
| setColumnCardAge: function (colId, showCardAge, cardAgeWarningDays) { | |||
| columnAgeSettings[String(colId)] = { | |||
| showCardAge: !!showCardAge, | |||
| @@ -601,6 +662,7 @@ | |||
| applyGridTemplate(); | |||
| initCardAgeSettings(); | |||
| initExportButtons(); | |||
| renderCards(); | |||
| initSortables(); | |||
| initJobSearch(); | |||
| @@ -57,8 +57,10 @@ | |||
| '<div class="form-check form-switch m-0" title="Show card count in column header">' + | |||
| '<input class="form-check-input col-count-toggle" type="checkbox" role="switch">' + | |||
| '</div>' : '') + | |||
| '<div class="form-check form-switch m-0" title="Show export button on board">' + | |||
| '<input class="form-check-input ' + agePrefix + '-export-toggle" type="checkbox" role="switch">' + | |||
| '</div>' + | |||
| '<button class="btn btn-sm btn-link p-0 text-secondary btn-toggle-' + agePrefix + '-age" title="Card age settings"><i class="bi bi-clock-history"></i></button>' + | |||
| '<button class="btn btn-sm btn-link p-0 text-secondary btn-export-' + agePrefix + '" title="Export to CSV"><i class="bi bi-download"></i></button>' + | |||
| '<button class="btn btn-sm btn-link p-0 text-secondary ' + editClass + '" title="Rename"><i class="bi bi-pencil"></i></button>' + | |||
| '<button class="btn btn-sm btn-link p-0 text-danger ' + deleteClass + '" title="Delete"><i class="bi bi-trash"></i></button>' + | |||
| '</div>' + | |||
| @@ -202,9 +204,20 @@ | |||
| }); | |||
| }); | |||
| } | |||
| li.querySelector('.btn-export-col').addEventListener('click', function () { | |||
| window.location.href = '/columns/' + li.dataset.id + '/export'; | |||
| }); | |||
| var colExportToggle = li.querySelector('.col-export-toggle'); | |||
| if (colExportToggle) { | |||
| colExportToggle.addEventListener('change', function () { | |||
| var show = colExportToggle.checked; | |||
| post('/columns/' + li.dataset.id + '/toggle-export', { show_export_button: show ? '1' : '0' }, function (res) { | |||
| if (res.ok) { | |||
| window.KanbanBoard.setColumnShowExport(li.dataset.id, show); | |||
| } else { | |||
| colExportToggle.checked = !show; | |||
| alert(res.error || 'Update failed'); | |||
| } | |||
| }); | |||
| }); | |||
| } | |||
| bindAgeSettings(li, 'col', '/columns/', function (id, show, days) { | |||
| window.KanbanBoard.setColumnCardAge(id, show, days); | |||
| }); | |||
| @@ -265,9 +278,20 @@ | |||
| } | |||
| }); | |||
| }); | |||
| li.querySelector('.btn-export-lane').addEventListener('click', function () { | |||
| window.location.href = '/swimlanes/' + li.dataset.id + '/export'; | |||
| }); | |||
| var laneExportToggle = li.querySelector('.lane-export-toggle'); | |||
| if (laneExportToggle) { | |||
| laneExportToggle.addEventListener('change', function () { | |||
| var show = laneExportToggle.checked; | |||
| post('/swimlanes/' + li.dataset.id + '/toggle-export', { show_export_button: show ? '1' : '0' }, function (res) { | |||
| if (res.ok) { | |||
| window.KanbanBoard.setLaneShowExport(li.dataset.id, show); | |||
| } else { | |||
| laneExportToggle.checked = !show; | |||
| alert(res.error || 'Update failed'); | |||
| } | |||
| }); | |||
| }); | |||
| } | |||
| bindAgeSettings(li, 'lane', '/swimlanes/', function (id, show, days) { | |||
| window.KanbanBoard.setLaneCardAge(id, show, days); | |||
| }); | |||
| @@ -29,17 +29,19 @@ $router->post('/cards/{id}/delete', [CardsController::class, 'destroy']); | |||
| $router->post('/cards/{id}', [CardsController::class, 'update']); | |||
| // Columns (JSON API) — /columns/reorder MUST be before /columns/{id} | |||
| $router->get('/columns/{id}/export', [ColumnsController::class, 'export']); | |||
| $router->post('/columns/reorder', [ColumnsController::class, 'reorder']); | |||
| $router->post('/columns/{id}/toggle-count', [ColumnsController::class, 'toggleCount']); | |||
| $router->get('/columns/{id}/export', [ColumnsController::class, 'export']); | |||
| $router->post('/columns/reorder', [ColumnsController::class, 'reorder']); | |||
| $router->post('/columns/{id}/toggle-export', [ColumnsController::class, 'toggleExport']); | |||
| $router->post('/columns/{id}/toggle-count', [ColumnsController::class, 'toggleCount']); | |||
| $router->post('/columns/{id}/card-age-settings', [ColumnsController::class, 'updateCardAgeSettings']); | |||
| $router->post('/columns/{id}/delete', [ColumnsController::class, 'destroy']); | |||
| $router->post('/columns/{id}', [ColumnsController::class, 'update']); | |||
| $router->post('/columns', [ColumnsController::class, 'store']); | |||
| // Swim lanes (JSON API) — /swimlanes/reorder MUST be before /swimlanes/{id} | |||
| $router->get('/swimlanes/{id}/export', [SwimLanesController::class, 'export']); | |||
| $router->post('/swimlanes/reorder', [SwimLanesController::class, 'reorder']); | |||
| $router->get('/swimlanes/{id}/export', [SwimLanesController::class, 'export']); | |||
| $router->post('/swimlanes/reorder', [SwimLanesController::class, 'reorder']); | |||
| $router->post('/swimlanes/{id}/toggle-export', [SwimLanesController::class, 'toggleExport']); | |||
| $router->post('/swimlanes/{id}/card-age-settings', [SwimLanesController::class, 'updateCardAgeSettings']); | |||
| $router->post('/swimlanes/{id}/delete', [SwimLanesController::class, 'destroy']); | |||
| $router->post('/swimlanes/{id}', [SwimLanesController::class, 'update']); | |||
Powered by TurnKey Linux.