clocktopus 1.6.10 → 1.6.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -80,6 +80,18 @@ export function indexPage() {
80
80
  .sessions-table .in-progress { color: #3fb950; font-style: italic; }
81
81
  .delete-btn { background: transparent; color: #8b949e; border: none; cursor: pointer; font-size: 1.1rem; line-height: 1; padding: 0 0.4rem; margin-top: 0; }
82
82
  .delete-btn:hover { color: #f85149; background: transparent; }
83
+ .delete-btn[disabled] { color: #484f58; cursor: not-allowed; opacity: 0.5; }
84
+ .delete-btn[disabled]:hover { color: #484f58; }
85
+ .modal-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.6); display: none; align-items: center; justify-content: center; z-index: 1000; }
86
+ .modal-backdrop.open { display: flex; }
87
+ .modal { background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 1.25rem 1.5rem; max-width: 420px; width: calc(100% - 2rem); }
88
+ .modal h3 { margin: 0 0 0.5rem 0; font-size: 1rem; color: #c9d1d9; }
89
+ .modal p { margin: 0 0 1rem 0; font-size: 0.875rem; color: #8b949e; }
90
+ .modal-actions { display: flex; gap: 0.5rem; justify-content: flex-end; }
91
+ .modal-actions button { margin-top: 0; padding: 0.4rem 0.9rem; font-size: 0.85rem; }
92
+ .modal-actions .cancel-btn { background: #30363d; }
93
+ .modal-actions .danger-btn { background: #da3633; }
94
+ .modal-actions .danger-btn:hover { background: #f85149; }
83
95
  .empty-state { color: #8b949e; font-size: 0.9rem; padding: 2rem; text-align: center; }
84
96
 
85
97
  /* Inline form row */
@@ -249,6 +261,18 @@ export function indexPage() {
249
261
  </div>
250
262
  </div>
251
263
 
264
+ <!-- Confirm modal -->
265
+ <div id="confirm-modal" class="modal-backdrop" role="dialog" aria-modal="true">
266
+ <div class="modal">
267
+ <h3 id="confirm-title">Are you sure?</h3>
268
+ <p id="confirm-message"></p>
269
+ <div class="modal-actions">
270
+ <button type="button" class="cancel-btn" id="confirm-cancel">Cancel</button>
271
+ <button type="button" class="danger-btn" id="confirm-ok">Delete</button>
272
+ </div>
273
+ </div>
274
+ </div>
275
+
252
276
  <!-- SETTINGS TAB -->
253
277
  <div id="tab-settings" class="tab-content">
254
278
  <div class="cards">
@@ -859,8 +883,13 @@ export function indexPage() {
859
883
  duration = '<span class="in-progress">In progress</span>';
860
884
  }
861
885
  const jira = s.jiraTicket || '-';
886
+ const canDelete = !!s.completedAt && !(s.jiraTicket && !s.jiraWorklogId);
887
+ const disabledAttr = canDelete ? '' : ' disabled';
888
+ const btnTitle = canDelete
889
+ ? 'Delete entry'
890
+ : 'Cannot delete: Jira worklog id not tracked for this entry';
862
891
  const deleteBtn = s.completedAt
863
- ? '<button class="delete-btn" title="Delete entry" onclick="deleteSession(\'' + escapeHtml(s.id) + '\')">&times;</button>'
892
+ ? '<button class="delete-btn" title="' + btnTitle + '" data-delete-id="' + escapeHtml(s.id) + '"' + disabledAttr + '>&times;</button>'
864
893
  : '';
865
894
  return '<tr>' +
866
895
  '<td>' + escapeHtml(s.description) + '</td>' +
@@ -889,8 +918,41 @@ export function indexPage() {
889
918
  loadSessions();
890
919
  }
891
920
 
921
+ function showConfirm(message) {
922
+ return new Promise(function(resolve) {
923
+ const backdrop = document.getElementById('confirm-modal');
924
+ const msg = document.getElementById('confirm-message');
925
+ const okBtn = document.getElementById('confirm-ok');
926
+ const cancelBtn = document.getElementById('confirm-cancel');
927
+ msg.textContent = message;
928
+ backdrop.classList.add('open');
929
+
930
+ function cleanup(result) {
931
+ backdrop.classList.remove('open');
932
+ okBtn.removeEventListener('click', onOk);
933
+ cancelBtn.removeEventListener('click', onCancel);
934
+ backdrop.removeEventListener('click', onBackdrop);
935
+ document.removeEventListener('keydown', onKey);
936
+ resolve(result);
937
+ }
938
+ function onOk() { cleanup(true); }
939
+ function onCancel() { cleanup(false); }
940
+ function onBackdrop(e) { if (e.target === backdrop) cleanup(false); }
941
+ function onKey(e) {
942
+ if (e.key === 'Escape') cleanup(false);
943
+ if (e.key === 'Enter') cleanup(true);
944
+ }
945
+ okBtn.addEventListener('click', onOk);
946
+ cancelBtn.addEventListener('click', onCancel);
947
+ backdrop.addEventListener('click', onBackdrop);
948
+ document.addEventListener('keydown', onKey);
949
+ okBtn.focus();
950
+ });
951
+ }
952
+
892
953
  async function deleteSession(id) {
893
- if (!confirm('Delete this entry from Clockify and Jira?')) return;
954
+ const ok = await showConfirm('Delete this entry from Clockify and Jira?');
955
+ if (!ok) return;
894
956
  try {
895
957
  const res = await fetch('/api/timer/' + encodeURIComponent(id), { method: 'DELETE' });
896
958
  const result = await res.json();
@@ -904,6 +966,12 @@ export function indexPage() {
904
966
  }
905
967
  }
906
968
 
969
+ document.addEventListener('click', function(e) {
970
+ const btn = e.target.closest && e.target.closest('[data-delete-id]');
971
+ if (!btn || btn.disabled) return;
972
+ deleteSession(btn.getAttribute('data-delete-id'));
973
+ });
974
+
907
975
  function escapeHtml(str) {
908
976
  const div = document.createElement('div');
909
977
  div.textContent = str;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clocktopus",
3
- "version": "1.6.10",
3
+ "version": "1.6.12",
4
4
  "description": "Time-tracking automation for Clockify with idle monitoring, Jira integration, Google Calendar sync, CLI, web dashboard, and desktop app.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",