@necrolab/dashboard 0.5.15 → 0.5.16

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 (121) hide show
  1. package/backend/api.js +2 -3
  2. package/eslint.config.js +46 -0
  3. package/index.html +2 -1
  4. package/package.json +5 -2
  5. package/src/App.vue +140 -170
  6. package/src/assets/css/base/mixins.scss +72 -0
  7. package/src/assets/css/base/reset.scss +0 -2
  8. package/src/assets/css/base/scroll.scss +43 -36
  9. package/src/assets/css/base/typography.scss +9 -10
  10. package/src/assets/css/base/variables.scss +43 -0
  11. package/src/assets/css/components/accessibility.scss +37 -0
  12. package/src/assets/css/components/buttons.scss +58 -15
  13. package/src/assets/css/components/forms.scss +31 -32
  14. package/src/assets/css/components/headers.scss +12 -20
  15. package/src/assets/css/components/modals.scss +2 -2
  16. package/src/assets/css/components/search-groups.scss +28 -22
  17. package/src/assets/css/components/tables.scss +5 -7
  18. package/src/assets/css/components/toasts.scss +7 -7
  19. package/src/assets/css/components/utilities.scss +220 -0
  20. package/src/assets/css/main.scss +66 -77
  21. package/src/components/Auth/LoginForm.vue +5 -84
  22. package/src/components/Editors/Account/Account.vue +8 -10
  23. package/src/components/Editors/Account/AccountCreator.vue +28 -59
  24. package/src/components/Editors/Account/AccountView.vue +38 -86
  25. package/src/components/Editors/Account/CreateAccount.vue +8 -50
  26. package/src/components/Editors/Profile/CreateProfile.vue +74 -131
  27. package/src/components/Editors/Profile/Profile.vue +15 -17
  28. package/src/components/Editors/Profile/ProfileCountryChooser.vue +16 -60
  29. package/src/components/Editors/Profile/ProfileView.vue +46 -96
  30. package/src/components/Editors/TagLabel.vue +16 -55
  31. package/src/components/Editors/TagToggle.vue +20 -8
  32. package/src/components/Filter/Filter.vue +62 -75
  33. package/src/components/Filter/FilterPreview.vue +161 -135
  34. package/src/components/Filter/PriceSortToggle.vue +36 -43
  35. package/src/components/Table/Header.vue +1 -1
  36. package/src/components/Table/Table.vue +45 -51
  37. package/src/components/Tasks/CheckStock.vue +7 -16
  38. package/src/components/Tasks/Controls/DesktopControls.vue +15 -60
  39. package/src/components/Tasks/Controls/MobileControls.vue +5 -20
  40. package/src/components/Tasks/CreateTaskAXS.vue +20 -118
  41. package/src/components/Tasks/CreateTaskTM.vue +33 -189
  42. package/src/components/Tasks/EventDetailRow.vue +21 -0
  43. package/src/components/Tasks/MassEdit.vue +6 -16
  44. package/src/components/Tasks/QuickSettings.vue +140 -216
  45. package/src/components/Tasks/ScrapeVenue.vue +4 -13
  46. package/src/components/Tasks/Stats.vue +19 -38
  47. package/src/components/Tasks/Task.vue +65 -268
  48. package/src/components/Tasks/TaskLabel.vue +9 -3
  49. package/src/components/Tasks/TaskView.vue +43 -63
  50. package/src/components/Tasks/Utilities.vue +10 -44
  51. package/src/components/Tasks/ViewTask.vue +23 -107
  52. package/src/components/icons/Close.vue +2 -8
  53. package/src/components/icons/Gear.vue +8 -8
  54. package/src/components/icons/Hash.vue +5 -0
  55. package/src/components/icons/Key.vue +2 -8
  56. package/src/components/icons/Pencil.vue +2 -8
  57. package/src/components/icons/Profile.vue +2 -8
  58. package/src/components/icons/Sell.vue +2 -8
  59. package/src/components/icons/Spinner.vue +4 -7
  60. package/src/components/icons/SquareCheck.vue +2 -8
  61. package/src/components/icons/SquareUncheck.vue +2 -8
  62. package/src/components/icons/Wildcard.vue +2 -8
  63. package/src/components/icons/index.js +3 -1
  64. package/src/components/ui/ActionButtonGroup.vue +113 -52
  65. package/src/components/ui/BalanceIndicator.vue +60 -0
  66. package/src/components/ui/EmptyState.vue +24 -0
  67. package/src/components/ui/EnableDisableToggle.vue +23 -0
  68. package/src/components/ui/FormField.vue +48 -48
  69. package/src/components/ui/IconLabel.vue +23 -0
  70. package/src/components/ui/InfoRow.vue +21 -54
  71. package/src/components/ui/Modal.vue +89 -56
  72. package/src/components/ui/Navbar.vue +60 -41
  73. package/src/components/ui/ReadonlyFieldsSection.vue +31 -0
  74. package/src/components/ui/ReconnectIndicator.vue +111 -124
  75. package/src/components/ui/SectionCard.vue +6 -14
  76. package/src/components/ui/Splash.vue +2 -10
  77. package/src/components/ui/StatusBadge.vue +26 -28
  78. package/src/components/ui/TaskToggle.vue +54 -0
  79. package/src/components/ui/controls/CountryChooser.vue +27 -64
  80. package/src/components/ui/controls/EyeToggle.vue +1 -1
  81. package/src/components/ui/controls/atomic/Checkbox.vue +40 -121
  82. package/src/components/ui/controls/atomic/Dropdown.vue +103 -139
  83. package/src/components/ui/controls/atomic/MultiDropdown.vue +71 -119
  84. package/src/components/ui/controls/atomic/Switch.vue +21 -84
  85. package/src/composables/useColorMapping.js +15 -0
  86. package/src/composables/useCopyToClipboard.js +1 -1
  87. package/src/composables/useDateFormatting.js +21 -0
  88. package/src/composables/useDeviceDetection.js +14 -0
  89. package/src/composables/useDropdownPosition.js +3 -4
  90. package/src/composables/useDynamicTableHeight.js +31 -0
  91. package/src/composables/useRowSelection.js +0 -3
  92. package/src/composables/useTicketPricing.js +16 -0
  93. package/src/composables/useWindowDimensions.js +21 -0
  94. package/src/libs/Filter.js +14 -20
  95. package/src/libs/panzoom.js +1 -5
  96. package/src/libs/utils/array.js +60 -0
  97. package/src/{stores/utils.js → libs/utils/dataGeneration.js} +2 -250
  98. package/src/libs/utils/eventUrl.js +40 -0
  99. package/src/libs/utils/string.js +28 -0
  100. package/src/libs/utils/time.js +20 -0
  101. package/src/libs/utils/validation.js +88 -0
  102. package/src/main.js +0 -2
  103. package/src/stores/connection.js +1 -4
  104. package/src/stores/logger.js +6 -12
  105. package/src/stores/sampleData.js +1 -2
  106. package/src/stores/ui.js +59 -36
  107. package/src/views/Accounts.vue +13 -24
  108. package/src/views/Console.vue +70 -172
  109. package/src/views/Editor.vue +211 -379
  110. package/src/views/FilterBuilder.vue +188 -371
  111. package/src/views/Login.vue +3 -28
  112. package/src/views/Profiles.vue +8 -15
  113. package/src/views/Tasks.vue +49 -36
  114. package/tailwind.config.js +82 -71
  115. package/workbox-config.cjs +47 -5
  116. package/docs/plans/2026-02-08-tailwind-consolidation.md +0 -2438
  117. package/exit +0 -209
  118. package/run +0 -177
  119. package/src/assets/css/base/color-fallbacks.scss +0 -10
  120. package/switch-branch.sh +0 -41
  121. /package/public/{reconnect-logo.png → img/reconnect-logo.png} +0 -0
package/src/stores/ui.js CHANGED
@@ -3,7 +3,8 @@ import { defineStore } from "pinia";
3
3
  import { ConnectionHandler } from "@/stores/connection.js";
4
4
  import { toast } from "vue3-toastify";
5
5
  import { createLogger } from "@/stores/logger";
6
- import { timeDifference, betterSort, sortTaskIds } from "@/stores/utils";
6
+ import { timeDifference } from "@/libs/utils/time";
7
+ import { betterSort, sortTaskIds } from "@/libs/utils/array";
7
8
 
8
9
  import mockTaskData from "@/stores/sampleData.js";
9
10
  import { useRouter } from "vue-router";
@@ -60,9 +61,6 @@ export const useUIStore = defineStore("ui", () => {
60
61
  const selectedTaskForView = ref(null);
61
62
  const connection = new ConnectionHandler();
62
63
 
63
- const startPoint = ref(0);
64
- const pullChange = ref(0);
65
-
66
64
  const currentEvent = ref("");
67
65
  const currentCountry = ref({ id: "US", siteId: "TM_US", url: "https://www.ticketmaster.com" });
68
66
  const currentModule = ref("TM");
@@ -99,19 +97,33 @@ export const useUIStore = defineStore("ui", () => {
99
97
  });
100
98
  const currentDropdown = ref("");
101
99
 
102
- // Set the _timeLeftString property every second
103
- setInterval(() => {
104
- const t1 = new Date();
105
- for (const [key, value] of Object.entries(tasks.value)) {
106
- if (value.expirationTime)
107
- tasks.value[key]._timeLeftString =
108
- value.expirationTime == "Invalid Date"
109
- ? "No Cartholds"
110
- : timeDifference(Date.parse(value.expirationTime), t1.getTime());
111
- else tasks.value[key]._timeLeftString = undefined;
100
+ // Optimized: Use requestAnimationFrame instead of setInterval to reduce CPU waste
101
+ // Only update times when tab is visible and at most once per second
102
+ let lastTimeUpdate = 0;
103
+ let lastSyncCheck = 0;
104
+ let rafId = null;
105
+
106
+ const updateTaskTimes = (timestamp) => {
107
+ // Only update once per second
108
+ if (timestamp - lastTimeUpdate >= 1000) {
109
+ lastTimeUpdate = timestamp;
110
+ const now = Date.now();
111
+
112
+ for (const [key, value] of Object.entries(tasks.value)) {
113
+ if (value.expirationTime) {
114
+ tasks.value[key]._timeLeftString =
115
+ value.expirationTime == "Invalid Date"
116
+ ? "No Cartholds"
117
+ : timeDifference(Date.parse(value.expirationTime), now);
118
+ } else {
119
+ tasks.value[key]._timeLeftString = undefined;
120
+ }
121
+ }
112
122
  }
113
123
 
114
- if (t1.getSeconds() % 10 === 0) {
124
+ // Sync taskIdOrder every 10 seconds (optimized with Map for O(1) lookups)
125
+ if (timestamp - lastSyncCheck >= 10000) {
126
+ lastSyncCheck = timestamp;
115
127
  const allIds = new Set(Object.keys(tasks.value));
116
128
  const orderIds = new Set(taskIdOrder.value);
117
129
 
@@ -123,20 +135,44 @@ export const useUIStore = defineStore("ui", () => {
123
135
 
124
136
  if (missingIds.length > 0) {
125
137
  missingIds.sort(sortTaskIds);
138
+ // Optimized: Binary search for insertion point
126
139
  for (const id of missingIds) {
127
- let pos = taskIdOrder.value.length;
128
- for (let i = 0; i < taskIdOrder.value.length; i++) {
129
- if (sortTaskIds(id, taskIdOrder.value[i]) < 0) {
130
- pos = i;
131
- break;
140
+ let left = 0;
141
+ let right = taskIdOrder.value.length;
142
+ while (left < right) {
143
+ const mid = Math.floor((left + right) / 2);
144
+ if (sortTaskIds(id, taskIdOrder.value[mid]) < 0) {
145
+ right = mid;
146
+ } else {
147
+ left = mid + 1;
132
148
  }
133
149
  }
134
- taskIdOrder.value.splice(pos, 0, id);
150
+ taskIdOrder.value.splice(left, 0, id);
135
151
  }
136
152
  }
137
153
  }
138
154
  }
139
- }, 1000);
155
+
156
+ rafId = requestAnimationFrame(updateTaskTimes);
157
+ };
158
+
159
+ rafId = requestAnimationFrame(updateTaskTimes);
160
+
161
+ // Pause updates when tab is hidden to save CPU
162
+ document.addEventListener('visibilitychange', () => {
163
+ if (document.hidden) {
164
+ if (rafId) {
165
+ cancelAnimationFrame(rafId);
166
+ rafId = null;
167
+ }
168
+ } else {
169
+ if (!rafId) {
170
+ lastTimeUpdate = 0;
171
+ lastSyncCheck = 0;
172
+ rafId = requestAnimationFrame(updateTaskTimes);
173
+ }
174
+ }
175
+ });
140
176
 
141
177
  connection.init("/api/updates?type=tasks");
142
178
 
@@ -155,7 +191,6 @@ export const useUIStore = defineStore("ui", () => {
155
191
  document.body.style.top = `-${scrollY}px`;
156
192
  document.body.style.width = "100%";
157
193
  document.body.style.height = "100%";
158
- document.body.style.left = "0"; // Add left positioning
159
194
 
160
195
  // iOS specific properties
161
196
  document.body.style.overflow = "hidden";
@@ -372,7 +407,7 @@ export const useUIStore = defineStore("ui", () => {
372
407
  };
373
408
 
374
409
  const startSpinner = (msg) => {
375
- if (router.currentRoute.value.name == "login" || window.location.href.includes(":5173")) return;
410
+ if (router.currentRoute.value.name == "login") return;
376
411
  showSpinner.value = true;
377
412
  spinnerMessage.value = msg;
378
413
  preventScroll();
@@ -421,10 +456,6 @@ export const useUIStore = defineStore("ui", () => {
421
456
  reverseTasks,
422
457
 
423
458
  // refresh
424
- startPoint,
425
- pullChange,
426
- setStartPoint: (point) => (startPoint.value = point),
427
- setPullChange: () => (pullChange.value = length),
428
459
  tasks,
429
460
 
430
461
  // top main checkbox
@@ -472,14 +503,6 @@ export const useUIStore = defineStore("ui", () => {
472
503
  const selectedTasks = getSelectedTasks();
473
504
  for (const value of Object.values(selectedTasks)) connection.sendDeleteTask(value.taskId);
474
505
  },
475
- foldTasks: () => {
476
- const selectedTasks = getSelectedTasks();
477
- for (const key of Object.keys(selectedTasks)) tasks.value[key].isExpanded = false;
478
- },
479
- expandTasks: () => {
480
- const selectedTasks = getSelectedTasks();
481
- for (const key of Object.keys(selectedTasks)) tasks.value[key].isExpanded = true;
482
- },
483
506
  startTasks: () => {
484
507
  const selectedTasks = getSelectedTasks();
485
508
  for (const value of Object.values(selectedTasks)) if (!value.active) connection.sendStartTask(value.taskId);
@@ -13,7 +13,8 @@
13
13
  <button
14
14
  :disabled="ui.disabledButtons['create-accounts']"
15
15
  @click="ui.toggleModal('account-creator', true)"
16
- class="smooth-hover">
16
+ class="smooth-hover"
17
+ aria-label="Create accounts">
17
18
  <PlayIcon class="w-4 h-4" />
18
19
  </button>
19
20
  </li>
@@ -21,7 +22,8 @@
21
22
  <button
22
23
  :disabled="ui.disabledButtons['add-accounts']"
23
24
  @click="ui.toggleModal('create-account', true)"
24
- class="smooth-hover">
25
+ class="smooth-hover"
26
+ aria-label="Add account">
25
27
  <PlusIcon class="w-4 h-4" />
26
28
  </button>
27
29
  </li>
@@ -38,6 +40,7 @@
38
40
  <input
39
41
  class="h-10 w-44 text-white text-sm p-2 bg-dark-500 flex items-center relative"
40
42
  placeholder="Search Email"
43
+ aria-label="Search email"
41
44
  autocomplete="new-password"
42
45
  data-dashlane-rid=""
43
46
  data-dashlane-label=""
@@ -64,7 +67,7 @@
64
67
  </div>
65
68
  <button
66
69
  :disabled="ui.disabledButtons['create-accounts']"
67
- class="bg-dark-400 disabled:opacity-70 smooth-hover border border-dark-650 hover:border-dark-700 w-44 flex text-white text-xs font-medium justify-center items-center rounded-md ml-auto h-10"
70
+ class="bg-dark-400 disabled:opacity-70 smooth-hover border border-dark-650 hover:border-dark-700 btn-focus-ring w-44 flex text-white text-xs font-medium justify-center items-center rounded-md ml-auto h-10"
68
71
  @click="ui.toggleModal('account-creator')">
69
72
  Create Accounts
70
73
  <PlayIcon class="ml-2" />
@@ -72,7 +75,7 @@
72
75
 
73
76
  <button
74
77
  :disabled="ui.disabledButtons['add-accounts']"
75
- class="bg-dark-400 disabled:opacity-70 smooth-hover border border-dark-650 hover:border-dark-700 w-44 flex text-white text-xs font-medium justify-center items-center rounded-md ml-auto h-10"
78
+ class="bg-dark-400 disabled:opacity-70 smooth-hover border border-dark-650 hover:border-dark-700 btn-focus-ring w-44 flex text-white text-xs font-medium justify-center items-center rounded-md ml-auto h-10"
76
79
  @click="ui.toggleModal('create-account')">
77
80
  Add Account
78
81
  <PlusIcon class="ml-2" />
@@ -81,7 +84,7 @@
81
84
  </div>
82
85
 
83
86
  <!-- Tasks (Table) -->
84
- <AccountView :accounts="processedTasks" class="max-h-big-acc" />
87
+ <AccountView :accounts="processedTasks" />
85
88
 
86
89
  <!-- Modal -->
87
90
  <transition-group name="fade">
@@ -90,27 +93,9 @@
90
93
  </transition-group>
91
94
  </div>
92
95
  </template>
93
- <style lang="scss" scoped>
94
- /* Page buttons styling - match Tasks page */
95
- button.bg-dark-400 {
96
- &:active,
97
- &:focus {
98
- outline: 1px solid oklch(0.72 0.15 145);
99
- outline-offset: 0;
100
- border-color: oklch(0.72 0.15 145) !important;
101
- }
102
- }
103
-
104
- .max-h-big-acc {
105
- max-height: calc(100vh - 14rem);
106
- overflow: auto;
107
- }
108
- </style>
109
96
  <script setup>
110
- import { computed, watch, ref } from "vue";
97
+ import { computed, defineAsyncComponent, watch, ref } from "vue";
111
98
  import AccountView from "@/components/Editors/Account/AccountView.vue";
112
- import AccountCreator from "@/components/Editors/Account/AccountCreator.vue";
113
- import CreateAccount from "@/components/Editors/Account/CreateAccount.vue";
114
99
  import { useUIStore } from "@/stores/ui";
115
100
  import { PlusIcon, PlayIcon, MailIcon } from "@/components/icons";
116
101
  import Dropdown from "@/components/ui/controls/atomic/Dropdown.vue";
@@ -118,6 +103,10 @@ import TagToggle from "@/components/Editors/TagToggle.vue";
118
103
  import Switch from "@/components/ui/controls/atomic/Switch.vue";
119
104
  import EyeToggle from "@/components/ui/controls/EyeToggle.vue";
120
105
 
106
+ // Lazy-loaded modal components
107
+ const AccountCreator = defineAsyncComponent(() => import("@/components/Editors/Account/AccountCreator.vue"));
108
+ const CreateAccount = defineAsyncComponent(() => import("@/components/Editors/Account/CreateAccount.vue"));
109
+
121
110
  const ui = useUIStore();
122
111
  const activeModal = computed(() => ui.activeModal);
123
112
  const allTags = ref([]);