@necrolab/dashboard 0.5.12 → 0.5.13

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.
Files changed (54) hide show
  1. package/.playwright-mcp/verification-accounts-desktop.png +0 -0
  2. package/.playwright-mcp/verification-tasks-desktop.png +0 -0
  3. package/.playwright-mcp/verification-tasks-mobile.png +0 -0
  4. package/README.md +21 -0
  5. package/docs/plans/2026-02-08-tailwind-consolidation-results.md +476 -0
  6. package/docs/plans/2026-02-08-tailwind-consolidation.md +2416 -0
  7. package/package.json +1 -1
  8. package/src/App.vue +2 -163
  9. package/src/assets/css/components/buttons.scss +43 -95
  10. package/src/assets/css/components/forms.scss +10 -28
  11. package/src/assets/css/components/search-groups.scss +80 -0
  12. package/src/assets/css/components/tables.scss +0 -8
  13. package/src/assets/css/main.scss +2 -43
  14. package/src/components/Editors/Account/Account.vue +14 -220
  15. package/src/components/Editors/Account/AccountCreator.vue +0 -4
  16. package/src/components/Editors/Account/AccountView.vue +0 -1
  17. package/src/components/Editors/Account/CreateAccount.vue +36 -107
  18. package/src/components/Editors/Profile/CreateProfile.vue +46 -135
  19. package/src/components/Editors/Profile/Profile.vue +10 -213
  20. package/src/components/Editors/Profile/ProfileView.vue +0 -1
  21. package/src/components/Filter/Filter.vue +14 -17
  22. package/src/components/Filter/FilterPreview.vue +0 -6
  23. package/src/components/Table/Row.vue +1 -1
  24. package/src/components/Table/Table.vue +2 -16
  25. package/src/components/Tasks/CreateTaskAXS.vue +45 -104
  26. package/src/components/Tasks/CreateTaskTM.vue +58 -96
  27. package/src/components/Tasks/Task.vue +22 -209
  28. package/src/components/Tasks/TaskView.vue +5 -4
  29. package/src/components/Tasks/ViewTask.vue +201 -214
  30. package/src/components/icons/Copy.vue +6 -0
  31. package/src/components/icons/index.js +3 -1
  32. package/src/components/ui/ActionButtonGroup.vue +70 -0
  33. package/src/components/ui/FormField.vue +74 -0
  34. package/src/components/ui/InfoRow.vue +98 -0
  35. package/src/components/ui/Modal.vue +6 -47
  36. package/src/components/ui/Navbar.vue +15 -43
  37. package/src/components/ui/ReconnectIndicator.vue +4 -4
  38. package/src/components/ui/SectionCard.vue +24 -0
  39. package/src/components/ui/Splash.vue +1 -6
  40. package/src/components/ui/StatusBadge.vue +37 -0
  41. package/src/components/ui/controls/CountryChooser.vue +14 -58
  42. package/src/components/ui/controls/atomic/Dropdown.vue +16 -24
  43. package/src/components/ui/controls/atomic/MultiDropdown.vue +7 -1
  44. package/src/components/ui/controls/atomic/Switch.vue +13 -29
  45. package/src/composables/useCopyToClipboard.js +25 -0
  46. package/src/composables/useRowSelection.js +48 -0
  47. package/src/views/Accounts.vue +0 -81
  48. package/src/views/Console.vue +4 -21
  49. package/src/views/Editor.vue +48 -138
  50. package/src/views/FilterBuilder.vue +0 -23
  51. package/src/views/Login.vue +14 -63
  52. package/src/views/Profiles.vue +0 -82
  53. package/src/views/Tasks.vue +3 -24
  54. package/tailwind.config.js +47 -5
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <Row
3
- class="relative grid-cols-10 gap-2 text-white lg:grid-cols-12"
3
+ class="relative grid-cols-10 gap-2 text-white lg:grid-cols-12 min-h-full"
4
4
  @click="ui.setOpenContextMenu('')"
5
5
  @dblclick="handleDoubleClick"
6
6
  @touchstart="handleTouchStart"
@@ -12,8 +12,8 @@
12
12
  @valueUpdate="ui.toggleTaskSelected(props.task.taskId)" />
13
13
  <div
14
14
  v-if="props.preferEventName && props.task.eventName"
15
- class="event-details hidden cursor-pointer lg:flex flex-col gap-0.5 justify-center"
16
- @click="copy(props.task.eventId)"
15
+ class="event-details hidden lg:flex flex-col gap-0.5 justify-center cursor-pointer"
16
+ @click="copy(props.task.eventId, 'Copied event ID')"
17
17
  :title="`Event ID: ${props.task.eventId}`">
18
18
  <div class="event-name text-white text-[11px] font-semibold leading-tight">
19
19
  {{ props.task.eventName }}
@@ -44,8 +44,8 @@
44
44
  </div>
45
45
  <div
46
46
  v-else
47
- class="event-id-details hidden cursor-pointer lg:flex flex-col gap-0.5 justify-center"
48
- @click="copy(props.task.eventId)">
47
+ class="event-id-details hidden lg:flex flex-col gap-0.5 justify-center cursor-pointer"
48
+ @click="copy(props.task.eventId, 'Copied event ID')">
49
49
  <div class="event-id-row flex items-center gap-1 text-[11px] text-white font-semibold">
50
50
  <svg class="event-icon" width="11" height="11" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24">
51
51
  <path d="M9 11l3 3L22 4"></path>
@@ -99,7 +99,7 @@
99
99
  </div>
100
100
  </div>
101
101
  <div class="col-span-2 flex lg:col-span-3 overflow-visible">
102
- <ul class="task-buttons overflow-visible">
102
+ <ActionButtonGroup class="overflow-visible">
103
103
  <li>
104
104
  <button v-if="task.active" @click="ui.stopTask(task.taskId)">
105
105
  <PauseIcon />
@@ -128,7 +128,7 @@
128
128
  <EyeIcon />
129
129
  </button>
130
130
  </li>
131
- </ul>
131
+ </ActionButtonGroup>
132
132
  </div>
133
133
  <div class="absolute right-5 top-4 col-span-1 hidden items-center justify-center md:block lg:flex">
134
134
  <h4 class="task-id text-center text-xs text-white">
@@ -178,20 +178,19 @@ h4 {
178
178
  }
179
179
 
180
180
  .event-icon {
181
- min-width: 10px !important;
182
- min-height: 10px !important;
183
- flex-shrink: 0 !important;
184
- color: oklch(0.60 0 0) !important;
185
- stroke: oklch(0.60 0 0) !important;
186
- fill: none !important;
181
+ @apply min-w-[10px] flex-shrink-0;
182
+ min-height: 10px;
183
+ color: oklch(0.60 0 0);
184
+ stroke: oklch(0.60 0 0);
185
+ fill: none;
187
186
 
188
187
  path,
189
188
  rect,
190
189
  line,
191
190
  circle,
192
191
  polyline {
193
- stroke: oklch(0.60 0 0) !important;
194
- fill: none !important;
192
+ stroke: oklch(0.60 0 0);
193
+ fill: none;
195
194
  }
196
195
  }
197
196
 
@@ -233,159 +232,11 @@ h4 {
233
232
  }
234
233
  }
235
234
 
236
- .task-buttons {
237
- @apply mx-auto flex items-center justify-center rounded;
238
- background: oklch(0.2046 0 0);
239
- border: 2px solid oklch(0.2809 0 0);
240
- padding: 2px;
241
- gap: 1px;
242
- flex-shrink: 0;
243
- overflow: visible;
244
-
245
- button {
246
- @apply relative flex items-center justify-center rounded border-0 outline-0 transition-all duration-150;
247
- background: transparent;
248
- width: 22px;
249
- height: 22px;
250
- color: oklch(0.90 0 0);
251
- border-radius: 4px;
252
-
253
- &:hover {
254
- background: oklch(0.72 0.15 145 / 0.15);
255
- color: oklch(1 0 0);
256
- }
257
-
258
- &:active {
259
- background: oklch(0.72 0.15 145 / 0.25);
260
- }
261
- }
262
-
263
- svg,
264
- img {
265
- width: 12px;
266
- height: 12px;
267
- position: relative;
268
- z-index: 1;
269
- }
270
-
271
- svg path {
272
- fill: currentColor;
273
- }
274
-
275
- span {
276
- @apply relative z-[1] text-xs font-medium;
277
- }
278
- }
279
-
280
- /* Tablet sizing - medium buttons */
281
- @media (min-width: 768px) and (max-width: 1023px) {
282
- .task-buttons {
283
- padding: 2px;
284
- gap: 1px;
285
- border-radius: 6px;
286
-
287
- button {
288
- width: 26px;
289
- height: 26px;
290
- border-radius: 5px;
291
- }
292
-
293
- svg,
294
- img {
295
- width: 14px;
296
- height: 14px;
297
- }
298
-
299
- span {
300
- font-size: 0.75rem;
301
- }
302
- }
303
- }
304
-
305
- /* Desktop sizing - large buttons */
306
- @media (min-width: 1024px) {
307
- .task-buttons {
308
- padding: 3px;
309
- gap: 2px;
310
- border-radius: 8px;
311
-
312
- button {
313
- width: 28px;
314
- height: 28px;
315
- border-radius: 6px;
316
- }
317
-
318
- svg,
319
- img {
320
- width: 16px;
321
- height: 16px;
322
- }
323
-
324
- span {
325
- font-size: 0.875rem;
326
- }
327
- }
328
- }
329
-
330
235
  /* Mobile specific styling - changed from 480px to 640px for earlier transition */
331
236
  @media (max-width: 640px) {
332
237
  /* Position adjustment for mobile taskId */
333
238
  .block.md\\:hidden {
334
- left: 4rem !important;
335
- top: 0.25rem !important;
336
- z-index: 1 !important;
337
- }
338
-
339
- /* Improved button layout for mobile */
340
- .task-buttons {
341
- padding: 1px;
342
- gap: 1px;
343
- border-radius: 4px;
344
- border: 2px solid oklch(0.2809 0 0) !important;
345
- max-width: 100%;
346
- min-height: 28px;
347
- height: auto;
348
- flex-wrap: wrap;
349
- justify-content: center;
350
- align-items: center;
351
- background: oklch(0.2046 0 0);
352
-
353
- button {
354
- width: 20px;
355
- height: 20px;
356
- border-radius: 3px;
357
- min-width: 20px;
358
- border: none !important;
359
- flex-shrink: 0;
360
- margin: 0.5px;
361
- background: transparent;
362
-
363
- &:hover {
364
- background: oklch(0.72 0.15 145 / 0.15);
365
- }
366
-
367
- &:active {
368
- background: oklch(0.72 0.15 145 / 0.25);
369
- }
370
- }
371
-
372
- svg,
373
- img {
374
- width: 10px;
375
- height: 10px;
376
- }
377
-
378
- span {
379
- font-size: 0.7rem;
380
- line-height: 1;
381
- }
382
-
383
- /* Improved list item layout */
384
- li {
385
- display: flex;
386
- margin: 0;
387
- padding: 0;
388
- }
239
+ @apply left-16 top-1 z-10;
389
240
  }
390
241
 
391
242
  /* Make the actions column more flexible */
@@ -400,8 +251,7 @@ h4 {
400
251
 
401
252
  /* Compact status container */
402
253
  .status-container {
403
- border: none !important;
404
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) !important;
254
+ @apply border-0 shadow-sm;
405
255
  padding: 2px 4px;
406
256
  font-size: 0.7rem;
407
257
  max-width: 100%;
@@ -411,11 +261,6 @@ h4 {
411
261
  font-size: 0.65rem;
412
262
  letter-spacing: 0;
413
263
  }
414
-
415
- /* Reduce row height to accommodate smaller elements */
416
- .task-row-container {
417
- min-height: 45px !important;
418
- }
419
264
  }
420
265
 
421
266
  .task-id {
@@ -471,12 +316,16 @@ h4 {
471
316
  import { Row } from "@/components/Table";
472
317
  import { PlayIcon, TrashIcon, BagWhiteIcon, PauseIcon, EditIcon, EyeIcon, StadiumIcon, TimerIcon } from "@/components/icons";
473
318
  import Checkbox from "@/components/ui/controls/atomic/Checkbox.vue";
319
+ import ActionButtonGroup from "@/components/ui/ActionButtonGroup.vue";
474
320
  import { useUIStore } from "@/stores/ui";
475
321
  import TaskLabel from "@/components/Tasks/TaskLabel.vue";
476
322
  import ViewTask from "@/components/Tasks/ViewTask.vue";
477
323
  import { computed, ref, onMounted, onUnmounted, nextTick } from "vue";
324
+ import { useRowSelection } from "@/composables/useRowSelection";
325
+ import { useCopyToClipboard } from "@/composables/useCopyToClipboard";
478
326
 
479
327
  const ui = useUIStore();
328
+ const { copy } = useCopyToClipboard();
480
329
 
481
330
  /** @type {{ task: Task }} */
482
331
  const props = defineProps({
@@ -508,40 +357,9 @@ const contextMenuPosition = ref({});
508
357
  const contextMenuRef = ref(null);
509
358
 
510
359
  // Double-click/tap selection
511
- let lastTapTime = 0;
512
- const DOUBLE_TAP_DELAY = 300; // ms
513
-
514
- const handleDoubleClick = (event) => {
515
- // Prevent if clicking on buttons or checkbox
516
- if (event.target.closest('button') || event.target.closest('.checkbox')) {
517
- return;
518
- }
360
+ const { handleDoubleClick, handleTouchStart, handleTouchEnd } = useRowSelection(() => {
519
361
  ui.toggleTaskSelected(props.task.taskId);
520
- };
521
-
522
- const handleTouchStart = (event) => {
523
- // Store touch time for double-tap detection
524
- const currentTime = Date.now();
525
- const tapGap = currentTime - lastTapTime;
526
-
527
- if (tapGap < DOUBLE_TAP_DELAY && tapGap > 0) {
528
- // Double-tap detected
529
- if (!event.target.closest('button') && !event.target.closest('.checkbox')) {
530
- event.preventDefault(); // Prevent zoom
531
- ui.toggleTaskSelected(props.task.taskId);
532
- }
533
- }
534
-
535
- lastTapTime = currentTime;
536
- };
537
-
538
- const handleTouchEnd = (event) => {
539
- // Prevent default to avoid potential zoom issues
540
- // but only if not interacting with buttons/checkbox
541
- if (event.target.closest('button') || event.target.closest('.checkbox')) {
542
- return;
543
- }
544
- };
362
+ });
545
363
 
546
364
  // Handle right-click to position context menu
547
365
  const handleRightClick = (event) => {
@@ -586,11 +404,6 @@ onUnmounted(() => {
586
404
  document.removeEventListener("click", handleClickOutside);
587
405
  });
588
406
 
589
- const copy = (txt) => {
590
- if (!txt) return;
591
- navigator.clipboard.writeText(txt);
592
- ui.showSuccess("Copied text");
593
- };
594
407
 
595
408
  const openViewTaskModal = () => {
596
409
  ui.selectedTaskForView = props.task;
@@ -78,9 +78,11 @@ h4 {
78
78
  @apply bg-dark-550 !important;
79
79
  }
80
80
 
81
- // Increase height when showing eventName to accommodate multi-line display
82
- &:has(.event-details) {
83
- min-height: 75px;
81
+ // Increase height when showing eventName to accommodate multi-line display (desktop only)
82
+ @media (min-width: 1024px) {
83
+ &:has(.event-details) {
84
+ min-height: 75px;
85
+ }
84
86
  }
85
87
 
86
88
  @media (max-width: 768px) {
@@ -94,7 +96,6 @@ h4 {
94
96
 
95
97
  .empty-state {
96
98
  @apply bg-dark-400;
97
- color: #969696;
98
99
  font-size: 14px;
99
100
  font-weight: 500;
100
101
  }