@necrolab/dashboard 0.5.14 → 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 (120) 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 +119 -0
  15. package/src/assets/css/components/modals.scss +2 -2
  16. package/src/assets/css/components/search-groups.scss +28 -19
  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 +72 -75
  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 +61 -12
  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 -42
  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 +78 -37
  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 +102 -95
  83. package/src/components/ui/controls/atomic/MultiDropdown.vue +72 -94
  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 +5 -6
  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 +17 -31
  108. package/src/views/Console.vue +76 -176
  109. package/src/views/Editor.vue +217 -383
  110. package/src/views/FilterBuilder.vue +190 -373
  111. package/src/views/Login.vue +3 -28
  112. package/src/views/Profiles.vue +12 -22
  113. package/src/views/Tasks.vue +51 -38
  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 -2416
  117. package/exit +0 -209
  118. package/run +0 -177
  119. package/switch-branch.sh +0 -41
  120. /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);
@@ -1,12 +1,9 @@
1
1
  <template>
2
2
  <div>
3
- <div class="flex items-center justify-between pt-5 pb-2">
4
- <div class="flex items-center justify-center gap-4">
5
- <MailIcon class="cursor-pointer smooth-hover text-white" />
6
- <h4 class="text-base font-semibold text-light-300">
7
- Accounts
8
- <span class="text-sm font-medium text-light-400 pl-1">{{ ui.getSelectedAccounts().length }}</span>
9
- </h4>
3
+ <div class="page-header">
4
+ <div class="page-header-card">
5
+ <MailIcon />
6
+ <h4>Accounts</h4>
10
7
  </div>
11
8
  <ul class="mobile-icons">
12
9
  <li>
@@ -16,7 +13,8 @@
16
13
  <button
17
14
  :disabled="ui.disabledButtons['create-accounts']"
18
15
  @click="ui.toggleModal('account-creator', true)"
19
- class="smooth-hover">
16
+ class="smooth-hover"
17
+ aria-label="Create accounts">
20
18
  <PlayIcon class="w-4 h-4" />
21
19
  </button>
22
20
  </li>
@@ -24,7 +22,8 @@
24
22
  <button
25
23
  :disabled="ui.disabledButtons['add-accounts']"
26
24
  @click="ui.toggleModal('create-account', true)"
27
- class="smooth-hover">
25
+ class="smooth-hover"
26
+ aria-label="Add account">
28
27
  <PlusIcon class="w-4 h-4" />
29
28
  </button>
30
29
  </li>
@@ -41,6 +40,7 @@
41
40
  <input
42
41
  class="h-10 w-44 text-white text-sm p-2 bg-dark-500 flex items-center relative"
43
42
  placeholder="Search Email"
43
+ aria-label="Search email"
44
44
  autocomplete="new-password"
45
45
  data-dashlane-rid=""
46
46
  data-dashlane-label=""
@@ -67,7 +67,7 @@
67
67
  </div>
68
68
  <button
69
69
  :disabled="ui.disabledButtons['create-accounts']"
70
- 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"
71
71
  @click="ui.toggleModal('account-creator')">
72
72
  Create Accounts
73
73
  <PlayIcon class="ml-2" />
@@ -75,7 +75,7 @@
75
75
 
76
76
  <button
77
77
  :disabled="ui.disabledButtons['add-accounts']"
78
- 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"
79
79
  @click="ui.toggleModal('create-account')">
80
80
  Add Account
81
81
  <PlusIcon class="ml-2" />
@@ -84,7 +84,7 @@
84
84
  </div>
85
85
 
86
86
  <!-- Tasks (Table) -->
87
- <AccountView :accounts="processedTasks" class="max-h-big-acc" />
87
+ <AccountView :accounts="processedTasks" />
88
88
 
89
89
  <!-- Modal -->
90
90
  <transition-group name="fade">
@@ -93,27 +93,9 @@
93
93
  </transition-group>
94
94
  </div>
95
95
  </template>
96
- <style lang="scss" scoped>
97
- /* Page buttons styling - match Tasks page */
98
- button.bg-dark-400 {
99
- &:active,
100
- &:focus {
101
- outline: 1px solid oklch(0.72 0.15 145);
102
- outline-offset: 0;
103
- border-color: oklch(0.72 0.15 145) !important;
104
- }
105
- }
106
-
107
- .max-h-big-acc {
108
- max-height: calc(100vh - 14rem);
109
- overflow: auto;
110
- }
111
- </style>
112
96
  <script setup>
113
- import { computed, watch, ref } from "vue";
97
+ import { computed, defineAsyncComponent, watch, ref } from "vue";
114
98
  import AccountView from "@/components/Editors/Account/AccountView.vue";
115
- import AccountCreator from "@/components/Editors/Account/AccountCreator.vue";
116
- import CreateAccount from "@/components/Editors/Account/CreateAccount.vue";
117
99
  import { useUIStore } from "@/stores/ui";
118
100
  import { PlusIcon, PlayIcon, MailIcon } from "@/components/icons";
119
101
  import Dropdown from "@/components/ui/controls/atomic/Dropdown.vue";
@@ -121,6 +103,10 @@ import TagToggle from "@/components/Editors/TagToggle.vue";
121
103
  import Switch from "@/components/ui/controls/atomic/Switch.vue";
122
104
  import EyeToggle from "@/components/ui/controls/EyeToggle.vue";
123
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
+
124
110
  const ui = useUIStore();
125
111
  const activeModal = computed(() => ui.activeModal);
126
112
  const allTags = ref([]);