|
|
@@ -395,30 +395,32 @@
|
|
|
.attr('data-col', 'sw-' + sw.id)
|
|
|
.attr('data-sort-value-sw-' + sw.id, v.toFixed(2));
|
|
|
|
|
|
- const $input = $('<input type="number" min="0" step="0.5" data-assign class="w-14 rounded border border-slate-200 px-1 py-1 text-center font-mono focus:outline-none focus:ring-2 focus:ring-slate-400">')
|
|
|
- .val(fmtDays(v))
|
|
|
- .attr('data-sw-id', sw.id);
|
|
|
-
|
|
|
+ // Phase 18: when the feature is enabled the status attrs +
|
|
|
+ // colour class go directly on the <td>. New rows always start
|
|
|
+ // at the default status (no nested wrapper).
|
|
|
if (taskStatusEnabled) {
|
|
|
- // New tasks always start with the default status.
|
|
|
- const $cell = $('<span class="assign-cell assign-status-zugewiesen"></span>')
|
|
|
- .attr('data-assign-cell', '')
|
|
|
+ $td.addClass('assign-status-zugewiesen')
|
|
|
+ .attr('data-assign-cell', '')
|
|
|
+ .attr('data-status', 'zugewiesen')
|
|
|
+ .attr('data-sw-id', sw.id);
|
|
|
+ }
|
|
|
+
|
|
|
+ $td.append(
|
|
|
+ $('<input type="number" min="0" step="0.5" data-assign class="w-14 rounded border border-slate-200 px-1 py-1 text-center font-mono focus:outline-none focus:ring-2 focus:ring-slate-400">')
|
|
|
+ .val(fmtDays(v))
|
|
|
.attr('data-sw-id', sw.id)
|
|
|
- .attr('data-status', 'zugewiesen');
|
|
|
- $cell.append($input);
|
|
|
+ );
|
|
|
|
|
|
+ if (taskStatusEnabled) {
|
|
|
const $status = $('<select data-assign-status aria-label="Status" class="assign-status-select"></select>')
|
|
|
.attr('data-sw-id', sw.id);
|
|
|
STATUSES.forEach(function (s) {
|
|
|
$('<option>').val(s).text(s).appendTo($status);
|
|
|
});
|
|
|
$status.val('zugewiesen');
|
|
|
- $cell.append($status);
|
|
|
-
|
|
|
- $td.append($cell);
|
|
|
- } else {
|
|
|
- $td.append($input);
|
|
|
+ $td.append($status);
|
|
|
}
|
|
|
+
|
|
|
$tr.append($td);
|
|
|
});
|
|
|
|
|
|
@@ -579,7 +581,8 @@
|
|
|
// --- Phase 18: per-cell status save pipeline -------------------------
|
|
|
// Independent of the days pipeline: hits /tasks/{id}/assignments/status
|
|
|
// (signed-in route, gated by app_settings.task_status_enabled). Same
|
|
|
- // debounce semantics + same audit weight (one row per changed cell).
|
|
|
+ // debounce + audit semantics as the days pipeline (one row per changed
|
|
|
+ // cell). Skipped entirely when the feature flag is off.
|
|
|
|
|
|
const pendingStatus = new Map(); // taskId -> Map<swId, status>
|
|
|
const statusTimers = {};
|
|
|
@@ -608,30 +611,34 @@
|
|
|
.catch(function (e) { flash(e.message, true); });
|
|
|
}
|
|
|
|
|
|
- function applyStatusClass($cell, next) {
|
|
|
- // Replace any existing assign-status-* with the new one. Keep the
|
|
|
- // class set deterministic (any unknown classes get scrubbed).
|
|
|
- STATUSES.forEach(function (s) { $cell.removeClass('assign-status-' + s); });
|
|
|
- $cell.addClass('assign-status-' + next);
|
|
|
- $cell.attr('data-status', next);
|
|
|
+ // Status change handler — only attach when the feature is on, and bail
|
|
|
+ // defensively if the cell doesn't carry the data attributes we expect
|
|
|
+ // (e.g. on a partially-rendered task row right after `+ Add task`).
|
|
|
+ if (taskStatusEnabled) {
|
|
|
+ $root.on('change', '[data-assign-status]', function () {
|
|
|
+ const $sel = $(this);
|
|
|
+ const next = String($sel.val() || '');
|
|
|
+ if (STATUSES.indexOf(next) === -1) { return; }
|
|
|
+
|
|
|
+ const $cell = $sel.closest('[data-assign-cell]');
|
|
|
+ if ($cell.length === 0) { return; }
|
|
|
+
|
|
|
+ const $tr = $sel.closest('tr');
|
|
|
+ const taskId = parseInt($tr.attr('data-task-id'), 10);
|
|
|
+ const swId = parseInt($sel.attr('data-sw-id'), 10);
|
|
|
+ if (!Number.isFinite(taskId) || !Number.isFinite(swId)) { return; }
|
|
|
+
|
|
|
+ // Swap the cell's colour class + data-status. Done first so the
|
|
|
+ // visual flip is instant; the server save is debounced.
|
|
|
+ STATUSES.forEach(function (s) { $cell.removeClass('assign-status-' + s); });
|
|
|
+ $cell.addClass('assign-status-' + next);
|
|
|
+ $cell.attr('data-status', next);
|
|
|
+
|
|
|
+ queueStatus(taskId, swId, next);
|
|
|
+ applyFilters();
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
- $root.on('change', '[data-assign-status]', function () {
|
|
|
- const $sel = $(this);
|
|
|
- const next = String($sel.val() || '');
|
|
|
- if (STATUSES.indexOf(next) === -1) { return; }
|
|
|
- const $cell = $sel.closest('[data-assign-cell]');
|
|
|
- applyStatusClass($cell, next);
|
|
|
-
|
|
|
- const taskId = parseInt($sel.closest('tr').data('task-id'), 10);
|
|
|
- const swId = parseInt($sel.data('sw-id'), 10);
|
|
|
- queueStatus(taskId, swId, next);
|
|
|
-
|
|
|
- // Re-evaluate the status filter immediately so the row hides /
|
|
|
- // shows without waiting for the server round-trip.
|
|
|
- applyFilters();
|
|
|
- });
|
|
|
-
|
|
|
function applyServerCapacity(perWorker) {
|
|
|
if (!perWorker || typeof perWorker !== 'object') { return; }
|
|
|
Object.keys(perWorker).forEach(function (swIdStr) {
|