@necrolab/dashboard 0.4.220 → 0.5.1

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 (140) hide show
  1. package/.prettierrc +27 -1
  2. package/.vscode/extensions.json +1 -1
  3. package/README.md +64 -2
  4. package/artwork/image.png +0 -0
  5. package/backend/api.js +26 -24
  6. package/backend/auth.js +2 -2
  7. package/backend/batching.js +1 -1
  8. package/backend/endpoints.js +8 -11
  9. package/backend/index.js +2 -2
  10. package/backend/mock-data.js +27 -36
  11. package/backend/mock-src/classes/logger.js +5 -7
  12. package/backend/mock-src/classes/utils.js +3 -2
  13. package/backend/mock-src/ticketmaster.js +4 -4
  14. package/backend/validator.js +2 -2
  15. package/config/configs.json +0 -1
  16. package/dev-server.js +134 -0
  17. package/exit +209 -0
  18. package/index.html +78 -8
  19. package/index.js +1 -1
  20. package/jsconfig.json +16 -0
  21. package/package.json +39 -25
  22. package/postcss.config.js +1 -1
  23. package/postinstall.js +124 -20
  24. package/public/android-chrome-192x192.png +0 -0
  25. package/public/android-chrome-512x512.png +0 -0
  26. package/public/apple-touch-icon.png +0 -0
  27. package/public/favicon-16x16.png +0 -0
  28. package/public/favicon-32x32.png +0 -0
  29. package/public/favicon.ico +0 -0
  30. package/public/img/logo_trans.png +0 -0
  31. package/public/img/necro_logo.png +0 -0
  32. package/public/manifest.json +16 -10
  33. package/run +176 -9
  34. package/src/App.vue +498 -85
  35. package/src/assets/css/base/reset.scss +43 -0
  36. package/src/assets/css/base/scroll.scss +114 -0
  37. package/src/assets/css/base/typography.scss +37 -0
  38. package/src/assets/css/components/buttons.scss +216 -0
  39. package/src/assets/css/components/forms.scss +221 -0
  40. package/src/assets/css/components/modals.scss +13 -0
  41. package/src/assets/css/components/tables.scss +27 -0
  42. package/src/assets/css/components/toasts.scss +100 -0
  43. package/src/assets/css/main.scss +201 -122
  44. package/src/assets/img/background.svg +2 -2
  45. package/src/assets/img/background.svg.backup +11 -0
  46. package/src/assets/img/logo_trans.png +0 -0
  47. package/src/components/Auth/LoginForm.vue +62 -11
  48. package/src/components/Editors/Account/Account.vue +116 -40
  49. package/src/components/Editors/Account/AccountCreator.vue +88 -39
  50. package/src/components/Editors/Account/AccountView.vue +102 -34
  51. package/src/components/Editors/Account/CreateAccount.vue +80 -32
  52. package/src/components/Editors/Profile/CreateProfile.vue +269 -83
  53. package/src/components/Editors/Profile/Profile.vue +132 -47
  54. package/src/components/Editors/Profile/ProfileCountryChooser.vue +82 -20
  55. package/src/components/Editors/Profile/ProfileView.vue +89 -32
  56. package/src/components/Editors/TagLabel.vue +67 -6
  57. package/src/components/Editors/TagToggle.vue +7 -2
  58. package/src/components/Filter/Filter.vue +288 -71
  59. package/src/components/Filter/FilterPreview.vue +202 -31
  60. package/src/components/Filter/PriceSortToggle.vue +76 -6
  61. package/src/components/Table/Header.vue +1 -1
  62. package/src/components/Table/Row.vue +1 -1
  63. package/src/components/Table/Table.vue +19 -2
  64. package/src/components/Tasks/CheckStock.vue +6 -8
  65. package/src/components/Tasks/Controls/DesktopControls.vue +27 -17
  66. package/src/components/Tasks/Controls/MobileControls.vue +8 -45
  67. package/src/components/Tasks/CreateTaskAXS.vue +80 -72
  68. package/src/components/Tasks/CreateTaskTM.vue +95 -141
  69. package/src/components/Tasks/MassEdit.vue +4 -6
  70. package/src/components/Tasks/QuickSettings.vue +199 -30
  71. package/src/components/Tasks/ScrapeVenue.vue +5 -6
  72. package/src/components/Tasks/Stats.vue +50 -24
  73. package/src/components/Tasks/Task.vue +384 -179
  74. package/src/components/Tasks/TaskLabel.vue +2 -2
  75. package/src/components/Tasks/TaskView.vue +136 -48
  76. package/src/components/Tasks/Utilities.vue +25 -10
  77. package/src/components/Tasks/ViewTask.vue +321 -0
  78. package/src/components/icons/Bag.vue +1 -1
  79. package/src/components/icons/Check.vue +5 -0
  80. package/src/components/icons/Close.vue +21 -0
  81. package/src/components/icons/CloseX.vue +5 -0
  82. package/src/components/icons/Eye.vue +6 -0
  83. package/src/components/icons/Key.vue +21 -0
  84. package/src/components/icons/Loyalty.vue +1 -1
  85. package/src/components/icons/Mail.vue +2 -2
  86. package/src/components/icons/Pencil.vue +21 -0
  87. package/src/components/icons/Play.vue +2 -2
  88. package/src/components/icons/Profile.vue +18 -0
  89. package/src/components/icons/Reload.vue +4 -5
  90. package/src/components/icons/Sandclock.vue +2 -2
  91. package/src/components/icons/Sell.vue +21 -0
  92. package/src/components/icons/Spinner.vue +42 -0
  93. package/src/components/icons/SquareCheck.vue +18 -0
  94. package/src/components/icons/SquareUncheck.vue +18 -0
  95. package/src/components/icons/Stadium.vue +1 -1
  96. package/src/components/icons/Wildcard.vue +18 -0
  97. package/src/components/icons/index.js +26 -1
  98. package/src/components/ui/Modal.vue +107 -13
  99. package/src/components/ui/Navbar.vue +175 -40
  100. package/src/components/ui/ReconnectIndicator.vue +351 -55
  101. package/src/components/ui/Splash.vue +5 -35
  102. package/src/components/ui/controls/CountryChooser.vue +200 -62
  103. package/src/components/ui/controls/atomic/Checkbox.vue +119 -10
  104. package/src/components/ui/controls/atomic/Dropdown.vue +216 -39
  105. package/src/components/ui/controls/atomic/LoadingButton.vue +45 -0
  106. package/src/components/ui/controls/atomic/MultiDropdown.vue +300 -37
  107. package/src/components/ui/controls/atomic/Switch.vue +53 -25
  108. package/src/composables/useClickOutside.js +21 -0
  109. package/src/composables/useDropdownPosition.js +174 -0
  110. package/src/libs/Filter.js +60 -24
  111. package/src/registerServiceWorker.js +1 -1
  112. package/src/stores/connection.js +4 -4
  113. package/src/stores/sampleData.js +172 -199
  114. package/src/stores/ui.js +55 -20
  115. package/src/stores/utils.js +30 -4
  116. package/src/types/index.js +41 -0
  117. package/src/utils/debug.js +1 -0
  118. package/src/views/Accounts.vue +116 -50
  119. package/src/views/Console.vue +394 -79
  120. package/src/views/Editor.vue +1176 -123
  121. package/src/views/FilterBuilder.vue +528 -250
  122. package/src/views/Login.vue +76 -14
  123. package/src/views/Profiles.vue +119 -34
  124. package/src/views/Tasks.vue +266 -98
  125. package/static/offline.html +192 -50
  126. package/switch-branch.sh +41 -0
  127. package/tailwind.config.js +119 -27
  128. package/vite.config.js +73 -16
  129. package/workbox-config.cjs +63 -0
  130. package/ICONS.md +0 -21
  131. package/public/img/background.svg +0 -14
  132. package/public/img/logo.png +0 -0
  133. package/public/img/logo_icon.png +0 -0
  134. package/public/img/logo_icon_2.png +0 -0
  135. package/src/assets/css/_input.scss +0 -143
  136. package/src/assets/img/logo.png +0 -0
  137. package/src/assets/img/logo_icon.png +0 -0
  138. package/src/assets/img/logo_icon_2.png +0 -0
  139. package/vue.config.js +0 -32
  140. package/workbox-config.js +0 -7
@@ -1,58 +1,67 @@
1
1
  <template>
2
- <Table>
3
- <Header class="text-center">
4
- <div class="col-span-1 lg:col-span-2 flex">
5
- <Checkbox class="mr-3" :toggled="ui.mainCheckbox.tasks" @valueUpdate="ui.toggleMainCheckbox('tasks')" />
6
- <div class="mx-auto hidden md:flex items-center" @click="ui.toggleSort('eventId')">
7
- <EventIcon class="ipadlg:mr-3" />
8
- <h4 class="hidden ipadlg:flex">Event</h4>
2
+ <div class="table-component">
3
+ <Header class="grid-cols-10 gap-2 text-center lg:grid-cols-12">
4
+ <div class="col-span-1 flex items-center justify-start lg:col-span-2">
5
+ <Checkbox
6
+ class="ml-2 mr-4 flex-shrink-0"
7
+ :toggled="ui.mainCheckbox.tasks"
8
+ @valueUpdate="ui.toggleMainCheckbox('tasks')"
9
+ :isHeader="true" />
10
+ <div class="mx-auto hidden items-center lg:flex" @click="ui.toggleSort('eventId')">
11
+ <EventIcon class="lg:mr-3" />
12
+ <h4 class="hidden lg:flex">Event</h4>
9
13
  <DownIcon v-if="ui.sortData.sortBy === 'eventId' && !ui.sortData.reversed" class="ml-1" />
10
14
  <UpIcon v-if="ui.sortData.sortBy === 'eventId' && ui.sortData.reversed" class="ml-1" />
11
15
  </div>
12
16
  </div>
13
- <div class="col-span-2 items-center justify-center hidden md:flex" v-once>
14
- <CartIcon class="mr-0 ipadlg:mr-3" />
15
-
16
- <h4 class="hidden ipadlg:flex">Quantity</h4>
17
- </div>
18
- <div class="col-span-2 flex items-center justify-center" v-once>
19
- <TicketIcon class="mr-0 ipadlg:mr-3" />
20
- <h4 class="hidden ipadlg:flex">Tickets</h4>
17
+ <div class="flex items-center justify-center col-span-2" v-once>
18
+ <TicketIcon class="mr-0 lg:mr-3" />
19
+ <h4 class="hidden lg:flex">Tickets</h4>
21
20
  </div>
22
- <div
23
- class="col-span-6 md:col-span-4 lg:col-span-3 flex items-center justify-center"
24
- @click="ui.toggleSort('status')"
25
- >
26
- <StatusIcon class="mr-0 ipadlg:mr-3" />
27
- <h4 class="hidden ipadlg:flex">Status</h4>
21
+ <div class="flex items-center justify-center col-span-5 md:col-span-4 lg:col-span-5" @click="ui.toggleSort('status')">
22
+ <StatusIcon class="mr-0 lg:mr-3" />
23
+ <h4 class="hidden lg:flex">Status</h4>
28
24
  <DownIcon v-if="ui.sortData.sortBy === 'status' && !ui.sortData.reversed" class="ml-1" />
29
25
  <UpIcon v-if="ui.sortData.sortBy === 'status' && ui.sortData.reversed" class="ml-1" />
30
26
  </div>
31
- <div class="col-span-2 flex items-center justify-center" v-once>
32
- <ClickIcon class="mr-0 ipadlg:mr-3" />
33
- <h4 class="hidden ipadlg:flex">Actions</h4>
27
+ <div class="flex items-center justify-center col-span-2 lg:col-span-3" v-once>
28
+ <ClickIcon class="mr-0 lg:mr-3" />
29
+ <h4 class="hidden lg:flex">Actions</h4>
34
30
  </div>
35
- <div class="absolute right-5 hidden md:flex items-center top-3,5" @click="ui.toggleSort('taskId')">
36
- <h4 class="">ID</h4>
31
+ <div class="absolute right-5 top-3.5 hidden items-center lg:flex" @click="ui.toggleSort('taskId')">
32
+ <h4 class="text-center text-xs">ID</h4>
37
33
  <DownIcon v-if="ui.sortData.sortBy === 'taskId' && !ui.sortData.reversed" class="ml-1" />
38
34
  <UpIcon v-if="ui.sortData.sortBy === 'taskId' && ui.sortData.reversed" class="ml-1" />
39
35
  </div>
40
36
  </Header>
41
37
  <div
42
- class="flex flex-col divide-y-2 divide-border overflow-y-auto hidden-scrollbars overflow-x-hidden stop-pan max-h-big tasks-table-height"
43
- >
44
- <div v-for="(task, i) in getTasksInOrder()" :key="i">
45
- <Task :task="task" :class="[i % 2 == 1 ? 'bg-dark-500' : 'bg-dark-550']" />
38
+ class="hidden-scrollbars stop-pan flex flex-col divide-y divide-dark-650 overflow-y-auto overflow-x-hidden"
39
+ :style="{ maxHeight: dynamicTableHeight }">
40
+ <div v-for="(task, i) in getTasksInOrder()" :key="task.taskId" class="task-row-container">
41
+ <Task :task="task" :class="i % 2 == 1 ? 'table-row-even' : 'table-row-odd'" />
46
42
  </div>
47
43
  <div
48
44
  v-if="getTasksInOrder().length === 0"
49
- class="flex justify-center text-light-400 py-2 bg-dark-500 border-b-2 border-border"
50
- >
51
- <span v-if="ui.queueStats.total === 0"> No tasks yet.</span>
52
- <span v-else>{{ ui.queueStats.total }} hidden task{{ ui.queueStats.total === 1 ? "" : "s" }}</span>
45
+ class="empty-state flex flex-col items-center justify-center py-8 text-center">
46
+ <div
47
+ v-if="
48
+ !ui.queueStats.queued && !ui.queueStats.sleeping && ui.queueStats.nextQueuePasses.length === 0
49
+ ">
50
+ <TasksIcon class="mx-auto mb-3 h-12 w-12 text-dark-400 opacity-50" />
51
+ <p class="text-sm text-light-400">No tasks yet</p>
52
+ <p class="mt-1 text-xs text-light-500">Create tasks to get started</p>
53
+ </div>
54
+ <div v-else>
55
+ <TasksIcon class="mx-auto mb-3 h-12 w-12 text-dark-400 opacity-50" />
56
+ <p class="text-sm text-light-400">No tasks match current filters</p>
57
+ <p class="mt-1 text-xs text-light-500">Adjust filters to see tasks</p>
58
+ </div>
53
59
  </div>
54
60
  </div>
55
- </Table>
61
+
62
+ <!-- View Task Modal -->
63
+ <ViewTask v-if="ui.activeModal === 'view-task' && ui.selectedTaskForView" :task="ui.selectedTaskForView" />
64
+ </div>
56
65
  </template>
57
66
  <style lang="scss" scoped>
58
67
  h4 {
@@ -63,36 +72,48 @@ h4 {
63
72
  touch-action: pan-y pan-up pan-down;
64
73
  }
65
74
 
66
- .tasks-table-height {
67
- max-height: 33rem !important;
68
- }
75
+ .task-row-container {
76
+ min-height: 69px;
77
+ flex-shrink: 0;
78
+ transition: background-color 0.15s ease;
69
79
 
70
- @media only screen and (min-device-width: 768px) and (max-device-width: 1366px) and (orientation: landscape) and (-webkit-min-device-pixel-ratio: 2) {
71
- .tasks-table-height {
72
- max-height: 45vh !important;
80
+ &:hover {
81
+ @apply bg-dark-550 !important;
73
82
  }
74
- }
75
83
 
76
- @media only screen and (min-device-width: 768px) and (max-device-width: 1366px) and (orientation: portrait) and (-webkit-min-device-pixel-ratio: 2) {
77
- .tasks-table-height {
78
- max-height: 58vh !important;
84
+ @media (max-width: 768px) {
85
+ min-height: 58px;
79
86
  }
87
+
88
+ @media (max-width: 480px) and (orientation: portrait) {
89
+ min-height: 50px;
90
+ }
91
+ }
92
+
93
+ .empty-state {
94
+ @apply bg-dark-400;
95
+ color: #969696;
96
+ font-size: 14px;
97
+ font-weight: 500;
80
98
  }
81
99
  </style>
82
100
  <script setup>
101
+ import { computed, ref, onMounted, onUnmounted } from "vue";
83
102
  import { Table, Header } from "@/components/Table";
84
- import { EventIcon, CartIcon, TicketIcon, StatusIcon, ClickIcon, DownIcon, UpIcon } from "@/components/icons";
103
+ import { EventIcon, TicketIcon, StatusIcon, ClickIcon, DownIcon, UpIcon, TasksIcon } from "@/components/icons";
85
104
  import Task from "./Task.vue";
105
+ import ViewTask from "./ViewTask.vue";
86
106
  import Checkbox from "@/components/ui/controls/atomic/Checkbox.vue";
87
107
  import { useUIStore } from "@/stores/ui";
88
108
 
89
109
  const props = defineProps({
90
- tasks: { type: Object }
110
+ tasks: { type: Object },
111
+ searchQuery: { type: String, default: '' }
91
112
  });
92
113
 
93
114
  const shouldTaskShow = (task) => {
94
115
  if (ui.taskFilter === "All") return true;
95
- else if (ui.taskFilter === "Checkout") return task.expirationTime || task.noCartholds;
116
+ else if (ui.taskFilter === "Checkout") return task.expirationTime;
96
117
  else return true;
97
118
  };
98
119
 
@@ -107,6 +128,8 @@ const siteIdEdgeCases = {
107
128
 
108
129
  const getTasksInOrder = () => {
109
130
  let out = [];
131
+ const searchLower = props.searchQuery?.toLowerCase().trim() || '';
132
+
110
133
  ui.taskIdOrder.forEach((id) => {
111
134
  if (props.tasks[id] && !props.tasks[id]?.hidden) {
112
135
  const task = props.tasks[id];
@@ -118,9 +141,74 @@ const getTasksInOrder = () => {
118
141
  return;
119
142
  if (ui.currentEvent && task.eventId !== ui.currentEvent) return;
120
143
  if (!shouldTaskShow(task)) return;
144
+
145
+ // Search filter
146
+ if (searchLower) {
147
+ const searchableText = [
148
+ task.eventId,
149
+ task.eventName,
150
+ task.eventVenue,
151
+ task.email,
152
+ task.taskId,
153
+ task.status,
154
+ task.profileName,
155
+ task.presaleCode,
156
+ task.reservedTicketsList
157
+ ].filter(Boolean).join(' ').toLowerCase();
158
+
159
+ if (!searchableText.includes(searchLower)) return;
160
+ }
161
+
121
162
  out.push(task);
122
163
  }
123
164
  });
124
165
  return out;
125
166
  };
167
+
168
+ // Dynamic height calculation to prevent page scrolling
169
+ const windowHeight = ref(window.innerHeight);
170
+ const windowWidth = ref(window.innerWidth);
171
+
172
+ const updateDimensions = () => {
173
+ windowHeight.value = window.innerHeight;
174
+ windowWidth.value = window.innerWidth;
175
+ };
176
+
177
+ onMounted(() => {
178
+ window.addEventListener("resize", updateDimensions);
179
+ });
180
+
181
+ onUnmounted(() => {
182
+ window.removeEventListener("resize", updateDimensions);
183
+ });
184
+
185
+ const dynamicTableHeight = computed(() => {
186
+ // Calculate available space for table
187
+ const headerHeight = 55; // Header + navbar
188
+ const titleHeight = 45; // Tasks title and mobile controls
189
+ const controlsHeight = windowWidth.value >= 650 ? 55 : 0; // Desktop controls
190
+ // On desktop: stats + filters all in one row (45px)
191
+ // On mobile: stats row (40px) + filters stacked (90px) = 130px
192
+ const filtersAndStatsHeight = windowWidth.value >= 768
193
+ ? (ui.queueStats.show ? 50 : 45) // Desktop: single row
194
+ : (ui.queueStats.show ? 130 : 90); // Mobile: stats row + stacked filters
195
+ const utilitiesHeight = windowWidth.value <= 768 ? 90 : 160; // Utilities section height (ensure buttons fully visible)
196
+ const margins =
197
+ windowWidth.value >= 1024 ? 20 : windowWidth.value <= 480 && windowHeight.value > windowWidth.value ? 8 : 12;
198
+
199
+ const totalUsedSpace =
200
+ headerHeight + titleHeight + controlsHeight + filtersAndStatsHeight + utilitiesHeight + margins;
201
+ const availableHeight = windowHeight.value - totalUsedSpace;
202
+
203
+ // Calculate row height based on screen size
204
+ const rowHeight = windowWidth.value <= 768 ? 58 : 69; // Mobile vs desktop row height
205
+ const minRowsToShow = 2; // Always show at least 2 rows
206
+ const minHeight = minRowsToShow * rowHeight;
207
+
208
+ // Calculate how many complete rows can fit
209
+ const maxCompleteRows = Math.floor(Math.max(availableHeight, minHeight) / rowHeight) + 1;
210
+ const exactHeight = maxCompleteRows * rowHeight;
211
+
212
+ return exactHeight + "px";
213
+ });
126
214
  </script>
@@ -1,16 +1,16 @@
1
1
  <template>
2
- <div class="grid grid-cols-1 gap-3 lg:grid-cols-1" v-once v-if="ui.currentModule == 'TM'">
2
+ <div class="grid grid-cols-1 gap-3 lg:grid-cols-1" v-if="ui.currentModule == 'TM'">
3
3
  <div class="lg:justify-self-end">
4
- <h4 class="hidden lg:block text-white opacity-40 uppercase font-medium">Utils</h4>
5
- <div class="flex gap-3">
6
- <button class="button-default active:opacity-50 w-44" @click="ui.toggleModal('scrape-venue')">
7
- <ScrapeIcon />
4
+ <h4 class="hidden lg:block text-white opacity-40 uppercase font-medium mb-1">Utils</h4>
5
+ <div class="flex gap-3 justify-between lg:justify-start">
6
+ <button class="button-default w-44 bg-dark-400 flex items-center justify-center gap-x-2 utility-btn" @click="ui.toggleModal('scrape-venue')">
8
7
  Scrape Venue
8
+ <ScrapeIcon />
9
9
  </button>
10
- <button class="button-default w-44" @click="ui.toggleModal('check-stock')">
11
- <BoxIcon />Check Stock
10
+ <button class="button-default w-44 bg-dark-400 flex items-center justify-center gap-x-2 utility-btn" @click="ui.toggleModal('check-stock')">
11
+ Check Stock
12
+ <BoxIcon />
12
13
  </button>
13
- <!-- Mobile Label -->
14
14
  </div>
15
15
  <h4 class="text-white opacity-40 uppercase font-medium block lg:hidden">Utils</h4>
16
16
  </div>
@@ -23,10 +23,25 @@ import { useUIStore } from "@/stores/ui";
23
23
  const ui = useUIStore();
24
24
  </script>
25
25
  <style lang="scss" scoped>
26
- button {
26
+ .utility-btn {
27
27
  height: 50px;
28
- @apply bg-dark-400 text-xs flex items-center justify-center gap-x-2 hover:opacity-70;
28
+
29
+ @media (max-width: 768px) {
30
+ height: 40px;
31
+ font-size: 0.75rem;
32
+ }
29
33
  }
34
+
35
+ button {
36
+ font-size: 0.75rem;
37
+
38
+ svg {
39
+ width: 14px;
40
+ height: 14px;
41
+ flex-shrink: 0;
42
+ }
43
+ }
44
+
30
45
  h4 {
31
46
  font-size: 12px;
32
47
  }
@@ -0,0 +1,321 @@
1
+ <template>
2
+ <Modal>
3
+ <template #header>
4
+ <span class="flex">View Task <EyeIcon class="ml-4 w-4" /></span>
5
+ </template>
6
+
7
+ <!-- Task Overview Card -->
8
+ <div class="section-card mb-4 mt-4">
9
+ <h3 class="section-title">Task Overview</h3>
10
+ <div class="grid grid-cols-1 gap-3">
11
+ <div class="info-row copyable" @click="copy(task.taskId)">
12
+ <span class="info-icon flex items-center justify-center text-base font-bold">#</span>
13
+ <span class="info-label">Task ID</span>
14
+ <span class="info-value">{{ task.taskId }}</span>
15
+ </div>
16
+ <div class="info-row" @click="copy(task.eventId)">
17
+ <StadiumIcon class="info-icon" />
18
+ <span class="info-label">Event ID</span>
19
+ <span class="info-value copyable">{{ task.eventId }}</span>
20
+ </div>
21
+ <div class="info-row" v-if="task.eventName">
22
+ <StadiumWhiteIcon class="info-icon" />
23
+ <span class="info-label">Event Name</span>
24
+ <span class="info-value">{{ task.eventName }}</span>
25
+ </div>
26
+ <div class="info-row" v-if="task.eventVenue">
27
+ <StadiumWhiteIcon class="info-icon" />
28
+ <span class="info-label">Venue</span>
29
+ <span class="info-value">{{ task.eventVenue }}</span>
30
+ </div>
31
+ <div class="info-row" v-if="task.eventLocalDate">
32
+ <StadiumWhiteIcon class="info-icon" />
33
+ <span class="info-label">Date</span>
34
+ <span class="info-value">{{ formatDate(task.eventLocalDate) }}</span>
35
+ </div>
36
+ <div class="info-row">
37
+ <div class="flex items-center gap-2">
38
+ <div class="status-indicator" :class="colorToClass(task.active || task.status.toLowerCase() === 'idle' ? task.statusColor : 'red')"></div>
39
+ <span class="info-label">Status</span>
40
+ </div>
41
+ <span class="info-value uppercase font-semibold whitespace-normal break-words">{{ task.status }}</span>
42
+ </div>
43
+ </div>
44
+ </div>
45
+
46
+ <!-- Account Details Card -->
47
+ <div class="section-card mb-4">
48
+ <h3 class="section-title">Account Details</h3>
49
+ <div class="grid grid-cols-1 gap-3">
50
+ <div class="info-row" v-if="task.email" @click="copy(task.email)">
51
+ <MailIcon class="info-icon" />
52
+ <span class="info-label">Email</span>
53
+ <span class="info-value copyable">{{ task.email }}</span>
54
+ </div>
55
+ <div class="info-row" v-if="task.password" @click="copy(task.password)">
56
+ <KeyIcon class="info-icon" />
57
+ <span class="info-label">Password</span>
58
+ <span class="info-value copyable">{{ showPassword ? task.password : '••••••••' }}</span>
59
+ <button @click.stop="showPassword = !showPassword" class="ml-auto">
60
+ <EyeToggle :visible="showPassword" />
61
+ </button>
62
+ </div>
63
+ <div class="info-row" v-if="!task.email && !task.password">
64
+ <MailIcon class="info-icon" />
65
+ <span class="info-label">Account</span>
66
+ <span class="info-value text-light-500">No account chosen</span>
67
+ </div>
68
+ <div class="info-row" v-if="task.profileName">
69
+ <ProfileIcon class="info-icon" />
70
+ <span class="info-label">Profile</span>
71
+ <span class="info-value">{{ task.profileName }}</span>
72
+ </div>
73
+ </div>
74
+ </div>
75
+
76
+ <!-- Task Configuration -->
77
+ <div class="section-card mb-4">
78
+ <h3 class="section-title">Task Configuration</h3>
79
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-3">
80
+ <div class="info-row">
81
+ <BagIcon class="info-icon" />
82
+ <span class="info-label">Ticket Quantity</span>
83
+ <span class="info-value">{{ task.quantity }}</span>
84
+ </div>
85
+ <div class="info-row">
86
+ <ScannerIcon class="info-icon" />
87
+ <span class="info-label">Account Tag</span>
88
+ <span class="info-value">{{ task.accountTag || 'None' }}</span>
89
+ </div>
90
+ <div class="info-row">
91
+ <SellIcon class="info-icon" />
92
+ <span class="info-label">Profile Tags</span>
93
+ <span class="info-value">{{ task.profileTags?.join(', ') || 'None' }}</span>
94
+ </div>
95
+ <div class="info-row" v-if="task.presaleCode" @click="copy(task.presaleCode)">
96
+ <PencilIcon class="info-icon" />
97
+ <span class="info-label">Presale Code</span>
98
+ <span class="info-value copyable">{{ task.presaleCode }}</span>
99
+ </div>
100
+ <div class="info-row" v-if="task.startOffset">
101
+ <ShieldIcon class="info-icon" />
102
+ <span class="info-label">Start Offset</span>
103
+ <span class="info-value">{{ task.startOffset }} min</span>
104
+ </div>
105
+ </div>
106
+ </div>
107
+
108
+ <!-- Task Settings Toggles -->
109
+ <div class="section-card mb-4">
110
+ <h3 class="section-title mb-3">Task Settings</h3>
111
+ <div class="grid grid-cols-2 md:grid-cols-3 gap-4">
112
+ <div class="toggle-item">
113
+ <div class="toggle-label">
114
+ <TimerIcon class="w-4 h-4" />
115
+ <span>Smart Timer</span>
116
+ </div>
117
+ <Switch v-model="toggles.smartTimer" disabled />
118
+ </div>
119
+ <div class="toggle-item">
120
+ <div class="toggle-label">
121
+ <GroupIcon class="w-4 h-4" />
122
+ <span>Login Later</span>
123
+ </div>
124
+ <Switch v-model="toggles.loginAfterCart" disabled />
125
+ </div>
126
+ <div class="toggle-item">
127
+ <div class="toggle-label">
128
+ <HandIcon class="w-4 h-4" />
129
+ <span>Manual</span>
130
+ </div>
131
+ <Switch v-model="toggles.manual" disabled />
132
+ </div>
133
+ <div class="toggle-item">
134
+ <div class="toggle-label">
135
+ <SavingsIcon class="w-4 h-4" />
136
+ <span>Do Not Pay</span>
137
+ </div>
138
+ <Switch v-model="toggles.doNotPay" disabled />
139
+ </div>
140
+ <div class="toggle-item">
141
+ <div class="toggle-label">
142
+ <LoyaltyIcon class="w-4 h-4" />
143
+ <span>Presale Mode</span>
144
+ </div>
145
+ <Switch v-model="toggles.presaleMode" disabled />
146
+ </div>
147
+ <div class="toggle-item">
148
+ <div class="toggle-label">
149
+ <SkiIcon class="w-4 h-4" />
150
+ <span>Quick Queue</span>
151
+ </div>
152
+ <Switch v-model="toggles.quickQueue" disabled />
153
+ </div>
154
+ <div class="toggle-item">
155
+ <div class="toggle-label">
156
+ <SandclockIcon class="w-4 h-4" />
157
+ <span>Aged Account</span>
158
+ </div>
159
+ <Switch v-model="toggles.agedAccount" disabled />
160
+ </div>
161
+ </div>
162
+ </div>
163
+ </Modal>
164
+ </template>
165
+
166
+ <script setup>
167
+ import { ref, computed, watch } from 'vue';
168
+ import Modal from '@/components/ui/Modal.vue';
169
+ import Switch from '@/components/ui/controls/atomic/Switch.vue';
170
+ import EyeToggle from '@/components/ui/controls/EyeToggle.vue';
171
+ import { useUIStore } from '@/stores/ui';
172
+ import {
173
+ EyeIcon,
174
+ StadiumIcon,
175
+ StadiumWhiteIcon,
176
+ MailIcon,
177
+ KeyIcon,
178
+ ProfileIcon,
179
+ BagIcon,
180
+ ScannerIcon,
181
+ SellIcon,
182
+ PencilIcon,
183
+ ShieldIcon,
184
+ TimerIcon,
185
+ GroupIcon,
186
+ HandIcon,
187
+ SavingsIcon,
188
+ LoyaltyIcon,
189
+ SkiIcon,
190
+ SandclockIcon
191
+ } from '@/components/icons';
192
+
193
+ const ui = useUIStore();
194
+ const showPassword = ref(false);
195
+
196
+ const props = defineProps({
197
+ task: {
198
+ type: Object,
199
+ required: true
200
+ }
201
+ });
202
+
203
+
204
+ const toggles = computed(() => ({
205
+ smartTimer: props.task.smartTimer,
206
+ loginAfterCart: props.task.loginAfterCart,
207
+ manual: props.task.manual,
208
+ doNotPay: props.task.doNotPay,
209
+ presaleMode: props.task.presaleMode,
210
+ quickQueue: props.task.quickQueue,
211
+ agedAccount: props.task.agedAccount
212
+ }));
213
+
214
+ const copy = (text) => {
215
+ navigator.clipboard.writeText(text);
216
+ ui.showSuccess(`Copied: ${text}`);
217
+ };
218
+
219
+ const formatDate = (dateString) => {
220
+ if (!dateString) return '';
221
+ const date = new Date(dateString);
222
+ return date.toLocaleString();
223
+ };
224
+
225
+ const colorToClass = (color) => {
226
+ const colorMap = {
227
+ red: 'bg-red-400',
228
+ green: 'bg-green-400',
229
+ yellow: 'bg-yellow-400',
230
+ blue: 'bg-blue-400'
231
+ };
232
+ return colorMap[color?.toLowerCase()] || 'bg-gray-400';
233
+ };
234
+ </script>
235
+
236
+ <style lang="scss" scoped>
237
+ .section-card {
238
+ @apply rounded-lg border p-4;
239
+ background: oklch(0.2046 0 0);
240
+ border-color: oklch(0.2809 0 0);
241
+ }
242
+
243
+ .section-title {
244
+ @apply text-sm font-semibold mb-3;
245
+ color: oklch(0.90 0 0);
246
+ }
247
+
248
+ .info-row {
249
+ @apply flex items-center gap-3 px-3 py-2 rounded-lg;
250
+ background: oklch(0.2603 0 0);
251
+ border: 1px solid oklch(0.2809 0 0);
252
+ min-height: 40px;
253
+
254
+ &.copyable {
255
+ cursor: pointer;
256
+
257
+ &:hover {
258
+ border-color: oklch(0.72 0.15 145);
259
+ background: oklch(0.72 0.15 145 / 0.08);
260
+ }
261
+ }
262
+ }
263
+
264
+ .info-icon {
265
+ @apply w-4 h-4 flex-shrink-0;
266
+ color: oklch(0.90 0 0) !important;
267
+
268
+ svg {
269
+ color: oklch(0.90 0 0) !important;
270
+ width: 16px !important;
271
+ height: 16px !important;
272
+ }
273
+ }
274
+
275
+ .info-label {
276
+ @apply text-xs font-medium flex-shrink-0;
277
+ color: oklch(0.65 0 0);
278
+ min-width: 100px;
279
+ }
280
+
281
+ .info-value {
282
+ @apply text-sm flex-1;
283
+ color: oklch(0.90 0 0);
284
+ overflow-wrap: break-word;
285
+ word-break: break-word;
286
+ }
287
+
288
+ .copyable {
289
+ cursor: pointer;
290
+ }
291
+
292
+ .toggle-item {
293
+ @apply flex flex-col items-center gap-2;
294
+ }
295
+
296
+ .toggle-label {
297
+ @apply flex items-center gap-2 text-xs;
298
+ color: oklch(0.90 0 0);
299
+
300
+ svg {
301
+ color: oklch(0.90 0 0) !important;
302
+ width: 16px !important;
303
+ height: 16px !important;
304
+ }
305
+ }
306
+
307
+ .status-indicator {
308
+ @apply w-2 h-2 rounded-full flex-shrink-0;
309
+ }
310
+
311
+ @media (max-width: 768px) {
312
+ .info-label {
313
+ min-width: 80px;
314
+ font-size: 11px;
315
+ }
316
+
317
+ .info-value {
318
+ font-size: 13px;
319
+ }
320
+ }
321
+ </style>
@@ -2,7 +2,7 @@
2
2
  <svg width="15" height="18" viewBox="0 0 15 18" fill="none" xmlns="http://www.w3.org/2000/svg">
3
3
  <path
4
4
  d="M1.95833 17.4167C1.52292 17.4167 1.15004 17.2618 0.839708 16.952C0.529903 16.6416 0.375 16.2688 0.375 15.8334V6.33335C0.375 5.89794 0.529903 5.52533 0.839708 5.21552C1.15004 4.90519 1.52292 4.75002 1.95833 4.75002H3.54167C3.54167 3.65488 3.92774 2.72124 4.69988 1.9491C5.47149 1.17749 6.40486 0.791687 7.5 0.791687C8.59514 0.791687 9.52878 1.17749 10.3009 1.9491C11.0725 2.72124 11.4583 3.65488 11.4583 4.75002H13.0417C13.4771 4.75002 13.85 4.90519 14.1603 5.21552C14.4701 5.52533 14.625 5.89794 14.625 6.33335V15.8334C14.625 16.2688 14.4701 16.6416 14.1603 16.952C13.85 17.2618 13.4771 17.4167 13.0417 17.4167H1.95833ZM1.95833 15.8334H13.0417V6.33335H1.95833V15.8334ZM7.5 11.0834C8.59514 11.0834 9.52878 10.6973 10.3009 9.92515C11.0725 9.15353 11.4583 8.22016 11.4583 7.12502H9.875C9.875 7.78474 9.6441 8.34551 9.18229 8.80731C8.72049 9.26912 8.15972 9.50002 7.5 9.50002C6.84028 9.50002 6.27951 9.26912 5.81771 8.80731C5.3559 8.34551 5.125 7.78474 5.125 7.12502H3.54167C3.54167 8.22016 3.92774 9.15353 4.69988 9.92515C5.47149 10.6973 6.40486 11.0834 7.5 11.0834ZM5.125 4.75002H9.875C9.875 4.0903 9.6441 3.52953 9.18229 3.06773C8.72049 2.60592 8.15972 2.37502 7.5 2.37502C6.84028 2.37502 6.27951 2.60592 5.81771 3.06773C5.3559 3.52953 5.125 4.0903 5.125 4.75002Z"
5
- fill="#6E7084"
5
+ fill="currentColor"
6
6
  />
7
7
  </svg>
8
8
  </template>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">
3
+ <path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z"/>
4
+ </svg>
5
+ </template>
@@ -0,0 +1,21 @@
1
+ <template>
2
+ <svg
3
+ width="17"
4
+ height="17"
5
+ viewBox="0 0 17 17"
6
+ fill="none"
7
+ xmlns="http://www.w3.org/2000/svg"
8
+ class="close-icon"
9
+ >
10
+ <path
11
+ d="M2.20006 16.375L0.625061 14.8L6.92506 8.5L0.625061 2.2L2.20006 0.625L8.50006 6.925L14.8001 0.625L16.3751 2.2L10.0751 8.5L16.3751 14.8L14.8001 16.375L8.50006 10.075L2.20006 16.375Z"
12
+ fill="currentColor"
13
+ />
14
+ </svg>
15
+ </template>
16
+
17
+ <style scoped>
18
+ .close-icon {
19
+ @apply w-4 h-4;
20
+ }
21
+ </style>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">
3
+ <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/>
4
+ </svg>
5
+ </template>