Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

175 рядки
6.5KB

  1. /* kanban-modal.js — card create/edit modal */
  2. (function () {
  3. 'use strict';
  4. var modal = document.getElementById('cardModal');
  5. var bsModal = new bootstrap.Modal(modal);
  6. var titleEl = document.getElementById('cardModalLabel');
  7. var cardIdEl = document.getElementById('card-id');
  8. var colIdEl = document.getElementById('card-column-id');
  9. var laneIdEl = document.getElementById('card-lane-id');
  10. var jobNumEl = document.getElementById('card-job-number');
  11. var jobNameEl = document.getElementById('card-job-name');
  12. var errEl = document.getElementById('card-modal-error');
  13. var btnSave = document.getElementById('btn-save-card');
  14. var btnDelete = document.getElementById('btn-delete-card');
  15. var boardId = KANBAN.boardId;
  16. /* ── Helpers ─────────────────────────────────────────────── */
  17. function post(url, data, cb) {
  18. var params = new URLSearchParams();
  19. Object.keys(data).forEach(function (k) { params.append(k, data[k]); });
  20. fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: params.toString() })
  21. .then(function (r) { return r.json(); })
  22. .then(cb)
  23. .catch(function (e) { showError('Network error: ' + e); });
  24. }
  25. function showError(msg) {
  26. errEl.textContent = msg;
  27. errEl.classList.remove('d-none');
  28. }
  29. function clearError() {
  30. errEl.textContent = '';
  31. errEl.classList.add('d-none');
  32. }
  33. /* ── Open for create ──────────────────────────────────────── */
  34. function openCreate(bId, colId, laneId) {
  35. titleEl.textContent = 'Add Card';
  36. cardIdEl.value = '';
  37. colIdEl.value = colId || '';
  38. laneIdEl.value = laneId || '';
  39. jobNumEl.value = '';
  40. jobNameEl.value = '';
  41. btnDelete.classList.add('d-none');
  42. clearError();
  43. bsModal.show();
  44. jobNumEl.focus();
  45. }
  46. /* ── Open for edit ───────────────────────────────────────── */
  47. function openEdit(id, colId, laneId, jobNum, jobName) {
  48. titleEl.textContent = 'Edit Card';
  49. cardIdEl.value = id;
  50. colIdEl.value = colId;
  51. laneIdEl.value = laneId;
  52. jobNumEl.value = jobNum || '';
  53. jobNameEl.value = jobName || '';
  54. btnDelete.classList.remove('d-none');
  55. clearError();
  56. bsModal.show();
  57. jobNumEl.focus();
  58. }
  59. /* ── Save ─────────────────────────────────────────────────── */
  60. btnSave.addEventListener('click', function () {
  61. clearError();
  62. var id = cardIdEl.value;
  63. var colId = colIdEl.value;
  64. var laneId = laneIdEl.value;
  65. var jNum = jobNumEl.value.trim();
  66. var jName = jobNameEl.value.trim();
  67. if (!jNum && !jName) {
  68. showError('Enter at least a job number or job name.');
  69. return;
  70. }
  71. if (id) {
  72. // Update existing
  73. post('/cards/' + id, { job_number: jNum, job_name: jName }, function (res) {
  74. if (res.ok) {
  75. bsModal.hide();
  76. window.KanbanBoard.onCardUpdated(id, res.job_number, res.job_name);
  77. } else {
  78. showError(res.error || 'Save failed.');
  79. }
  80. });
  81. } else {
  82. // Create new — if no col/lane selected show column/lane picker
  83. if (!colId || !laneId) {
  84. showError('Please choose a column and swim lane first.');
  85. return;
  86. }
  87. post('/cards', {
  88. board_id: boardId,
  89. column_id: colId,
  90. swim_lane_id: laneId,
  91. job_number: jNum,
  92. job_name: jName
  93. }, function (res) {
  94. if (res.ok) {
  95. bsModal.hide();
  96. window.KanbanBoard.onCardCreated(res);
  97. } else {
  98. showError(res.error || 'Save failed.');
  99. }
  100. });
  101. }
  102. });
  103. /* ── Delete ──────────────────────────────────────────────── */
  104. btnDelete.addEventListener('click', function () {
  105. if (!confirm('Delete this card?')) return;
  106. var id = cardIdEl.value;
  107. post('/cards/' + id + '/delete', {}, function (res) {
  108. if (res.ok) {
  109. bsModal.hide();
  110. window.KanbanBoard.onCardDeleted(id);
  111. } else {
  112. showError(res.error || 'Delete failed.');
  113. }
  114. });
  115. });
  116. /* ── Column/Lane picker when Add Card clicked with no cell ── */
  117. // Populated lazily from board data
  118. modal.addEventListener('shown.bs.modal', function () {
  119. if (!cardIdEl.value && (!colIdEl.value || !laneIdEl.value)) {
  120. injectPicker();
  121. }
  122. });
  123. function injectPicker() {
  124. if (document.getElementById('card-picker')) return;
  125. var picker = document.createElement('div');
  126. picker.id = 'card-picker';
  127. picker.className = 'row g-2 mb-3';
  128. var colSel = '<select class="form-select form-select-sm" id="pick-col"><option value="">-- Column --</option>';
  129. var laneSel = '<select class="form-select form-select-sm" id="pick-lane"><option value="">-- Swim Lane --</option>';
  130. document.querySelectorAll('.kanban-col-header').forEach(function (el) {
  131. colSel += '<option value="' + el.dataset.colId + '">' + el.querySelector('.col-label').textContent + '</option>';
  132. });
  133. document.querySelectorAll('.kanban-lane-header').forEach(function (el) {
  134. laneSel += '<option value="' + el.dataset.laneId + '">' + el.querySelector('.lane-label').textContent + '</option>';
  135. });
  136. colSel += '</select>';
  137. laneSel += '</select>';
  138. picker.innerHTML =
  139. '<div class="col"><label class="form-label small">Column</label>' + colSel + '</div>' +
  140. '<div class="col"><label class="form-label small">Swim Lane</label>' + laneSel + '</div>';
  141. var first = document.getElementById('card-job-number').closest('.mb-3');
  142. modal.querySelector('.modal-body').insertBefore(picker, first);
  143. document.getElementById('pick-col').addEventListener('change', function () {
  144. colIdEl.value = this.value;
  145. });
  146. document.getElementById('pick-lane').addEventListener('change', function () {
  147. laneIdEl.value = this.value;
  148. });
  149. }
  150. /* ── Public API ──────────────────────────────────────────── */
  151. window.KanbanModal = { openCreate: openCreate, openEdit: openEdit };
  152. })();

Powered by TurnKey Linux.