@necrolab/dashboard 0.5.6 → 0.5.8

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@necrolab/dashboard",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": "rm -rf dist && npx workbox-cli generateSW workbox-config.cjs && vite build",
@@ -5,16 +5,62 @@
5
5
  @dblclick="handleDoubleClick"
6
6
  @touchstart="handleTouchStart"
7
7
  @touchend="handleTouchEnd">
8
- <div class="col-span-1 flex items-center justify-start lg:col-span-2">
8
+ <div class="col-span-1 flex items-start justify-start lg:col-span-2 py-2">
9
9
  <Checkbox
10
10
  class="ml-2 mr-4 flex-shrink-0"
11
11
  :toggled="props.task.selected"
12
12
  @valueUpdate="ui.toggleTaskSelected(props.task.taskId)" />
13
- <h4
14
- class="task-event-id mx-auto hidden cursor-pointer text-white hover:text-light-300 lg:block"
13
+ <div
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)"
17
+ :title="`Event ID: ${props.task.eventId}`">
18
+ <div class="event-name text-white text-[11px] font-semibold leading-tight">
19
+ {{ props.task.eventName }}
20
+ </div>
21
+ <div v-if="props.task.venueName || props.task.eventCity" class="event-venue flex items-start gap-1 text-[9px] leading-tight">
22
+ <svg class="event-icon mt-[1px]" width="10" height="10" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
23
+ <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path>
24
+ <circle cx="12" cy="10" r="3"></circle>
25
+ </svg>
26
+ <span class="truncate text-light-500">{{ [props.task.venueName, props.task.eventCity].filter(Boolean).join(', ') }}</span>
27
+ </div>
28
+ <div v-if="props.task.eventLocalDate" class="event-date flex items-start gap-1 text-[9px] leading-tight">
29
+ <svg class="event-icon mt-[1px]" width="10" height="10" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
30
+ <rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect>
31
+ <line x1="16" y1="2" x2="16" y2="6"></line>
32
+ <line x1="8" y1="2" x2="8" y2="6"></line>
33
+ <line x1="3" y1="10" x2="21" y2="10"></line>
34
+ </svg>
35
+ <span class="text-light-500">{{ formatEventDate(props.task.eventLocalDate) }}</span>
36
+ </div>
37
+ <div v-if="props.task.email" class="event-email flex items-start gap-1 text-[9px] leading-tight">
38
+ <svg class="event-icon mt-[1px]" width="10" height="10" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
39
+ <path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path>
40
+ <polyline points="22,6 12,13 2,6"></polyline>
41
+ </svg>
42
+ <span class="truncate text-light-500">{{ props.task.email }}</span>
43
+ </div>
44
+ </div>
45
+ <div
46
+ v-else
47
+ class="event-id-details hidden cursor-pointer lg:flex flex-col gap-0.5 justify-center"
15
48
  @click="copy(props.task.eventId)">
16
- {{ props.task.eventId }}
17
- </h4>
49
+ <div class="event-id-row flex items-center gap-1 text-[11px] text-white font-semibold">
50
+ <svg class="event-icon" width="11" height="11" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24">
51
+ <path d="M9 11l3 3L22 4"></path>
52
+ <path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path>
53
+ </svg>
54
+ <span>{{ props.task.eventId }}</span>
55
+ </div>
56
+ <div v-if="props.task.email" class="event-email flex items-start gap-1 text-[9px] leading-tight">
57
+ <svg class="event-icon mt-[1px]" width="10" height="10" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
58
+ <path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path>
59
+ <polyline points="22,6 12,13 2,6"></polyline>
60
+ </svg>
61
+ <span class="truncate text-light-500">{{ props.task.email }}</span>
62
+ </div>
63
+ </div>
18
64
  </div>
19
65
  <div class="col-span-2 overflow-hidden">
20
66
  <h4 class="text-white text-xs leading-tight">
@@ -115,6 +161,54 @@ h4 {
115
161
  color: oklch(0.90 0 0);
116
162
  }
117
163
 
164
+ .event-details,
165
+ .event-id-details {
166
+ max-width: 100%;
167
+ min-width: 0;
168
+ gap: 2px;
169
+
170
+ .event-name,
171
+ .event-id-row {
172
+ max-width: 180px;
173
+ overflow: hidden;
174
+ text-overflow: ellipsis;
175
+ white-space: nowrap;
176
+ font-size: 11px;
177
+ line-height: 1.3;
178
+ }
179
+
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;
187
+
188
+ path,
189
+ rect,
190
+ line,
191
+ circle,
192
+ polyline {
193
+ stroke: oklch(0.60 0 0) !important;
194
+ fill: none !important;
195
+ }
196
+ }
197
+
198
+ .event-venue,
199
+ .event-date,
200
+ .event-email {
201
+ line-height: 1.2;
202
+ min-height: 11px;
203
+
204
+ span {
205
+ color: oklch(0.60 0 0);
206
+ font-size: 9px;
207
+ line-height: 1.2;
208
+ }
209
+ }
210
+ }
211
+
118
212
  .status-container {
119
213
  @apply mx-auto flex w-fit items-center justify-center rounded-lg border border-dark-650 bg-dark-500;
120
214
  padding: 6px 12px;
@@ -375,7 +469,7 @@ h4 {
375
469
  /// <reference path="@/types/index.js" />
376
470
 
377
471
  import { Row } from "@/components/Table";
378
- import { PlayIcon, TrashIcon, BagWhiteIcon, PauseIcon, EditIcon, EyeIcon } from "@/components/icons";
472
+ import { PlayIcon, TrashIcon, BagWhiteIcon, PauseIcon, EditIcon, EyeIcon, StadiumIcon, TimerIcon } from "@/components/icons";
379
473
  import Checkbox from "@/components/ui/controls/atomic/Checkbox.vue";
380
474
  import { useUIStore } from "@/stores/ui";
381
475
  import TaskLabel from "@/components/Tasks/TaskLabel.vue";
@@ -386,9 +480,29 @@ const ui = useUIStore();
386
480
 
387
481
  /** @type {{ task: Task }} */
388
482
  const props = defineProps({
389
- task: { type: Object }
483
+ task: { type: Object },
484
+ preferEventName: { type: Boolean, default: false }
390
485
  });
391
486
 
487
+ // Format event date for display
488
+ const formatEventDate = (dateString) => {
489
+ if (!dateString) return '';
490
+ try {
491
+ const date = new Date(dateString);
492
+ const options = {
493
+ month: 'short',
494
+ day: 'numeric',
495
+ year: 'numeric',
496
+ hour: 'numeric',
497
+ minute: '2-digit',
498
+ hour12: true
499
+ };
500
+ return date.toLocaleString('en-US', options).replace(',', '');
501
+ } catch {
502
+ return dateString;
503
+ }
504
+ };
505
+
392
506
  // Context menu positioning
393
507
  const contextMenuPosition = ref({});
394
508
  const contextMenuRef = ref(null);
@@ -494,9 +608,9 @@ const isTotalPrice = (line, index, lines) => {
494
608
  if (!isLastLine) return false;
495
609
 
496
610
  // Check if line is a standalone price (not in parentheses)
497
- // Matches: $345.88, €345.88, £345.88, ¥345.88, etc.
611
+ // Matches: $345.88, €345.88, USD 345.88, EUR 345.88, etc.
498
612
  // Does NOT match: ($86.47) or 2× 301/E ($86.47)
499
- const totalPricePattern = /^[$€£¥₹₽¢]\s*[\d,]+\.?\d*$/;
613
+ const totalPricePattern = /^([$€£¥₹₽¢]|[A-Z]{3})\s*[\d,]+\.?\d*$/;
500
614
  return totalPricePattern.test(trimmed) && !trimmed.includes('(') && !trimmed.includes(')');
501
615
  };
502
616
 
@@ -14,31 +14,31 @@
14
14
  <UpIcon v-if="ui.sortData.sortBy === 'eventId' && ui.sortData.reversed" class="ml-1" />
15
15
  </div>
16
16
  </div>
17
- <div class="col-span-2 flex items-center justify-start" v-once>
17
+ <div class="col-span-2 flex items-center justify-center" v-once>
18
18
  <TicketIcon class="mr-0 lg:mr-3" />
19
19
  <h4 class="hidden lg:flex">Tickets</h4>
20
20
  </div>
21
- <div class="col-span-5 md:col-span-4 lg:col-span-5 flex items-center justify-center" @click="ui.toggleSort('status')">
21
+ <div
22
+ class="col-span-5 flex items-center justify-center md:col-span-4 lg:col-span-5"
23
+ @click="ui.toggleSort('status')">
22
24
  <StatusIcon class="mr-0 lg:mr-3" />
23
25
  <h4 class="hidden lg:flex">Status</h4>
24
26
  <DownIcon v-if="ui.sortData.sortBy === 'status' && !ui.sortData.reversed" class="ml-1" />
25
27
  <UpIcon v-if="ui.sortData.sortBy === 'status' && ui.sortData.reversed" class="ml-1" />
26
28
  </div>
27
- <div class="col-span-2 lg:col-span-3 flex items-center justify-center" v-once>
29
+ <div class="col-span-2 flex items-center justify-center lg:col-span-3" v-once>
28
30
  <ClickIcon class="mr-0 lg:mr-3" />
29
31
  <h4 class="hidden lg:flex">Actions</h4>
30
32
  </div>
31
- <div class="absolute right-5 top-3.5 hidden items-center lg:flex" @click="ui.toggleSort('taskId')">
33
+ <div class="absolute right-5 top-3.5 hidden items-center lg:flex">
32
34
  <h4 class="text-center text-xs">ID</h4>
33
- <DownIcon v-if="ui.sortData.sortBy === 'taskId' && !ui.sortData.reversed" class="ml-1" />
34
- <UpIcon v-if="ui.sortData.sortBy === 'taskId' && ui.sortData.reversed" class="ml-1" />
35
35
  </div>
36
36
  </Header>
37
37
  <div
38
38
  class="hidden-scrollbars stop-pan flex flex-col divide-y divide-dark-650 overflow-y-auto overflow-x-hidden"
39
39
  :style="{ maxHeight: dynamicTableHeight }">
40
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'" />
41
+ <Task :task="task" :preferEventName="props.preferEventName" :class="i % 2 == 1 ? 'table-row-even' : 'table-row-odd'" />
42
42
  </div>
43
43
  <div
44
44
  v-if="getTasksInOrder().length === 0"
@@ -78,6 +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;
84
+ }
85
+
81
86
  @media (max-width: 768px) {
82
87
  min-height: 58px;
83
88
  }
@@ -104,12 +109,13 @@ import { useUIStore } from "@/stores/ui";
104
109
 
105
110
  const props = defineProps({
106
111
  tasks: { type: Object },
107
- searchQuery: { type: String, default: '' }
112
+ searchQuery: { type: String, default: "" },
113
+ preferEventName: { type: Boolean, default: false }
108
114
  });
109
115
 
110
116
  const shouldTaskShow = (task) => {
111
117
  if (ui.taskFilter === "All") return true;
112
- else if (ui.taskFilter === "Checkout") return task.expirationTime;
118
+ else if (ui.taskFilter === "Checkout") return Boolean(task.checkoutUrl);
113
119
  else return true;
114
120
  };
115
121
 
@@ -124,7 +130,7 @@ const siteIdEdgeCases = {
124
130
 
125
131
  const getTasksInOrder = () => {
126
132
  let out = [];
127
- const searchLower = props.searchQuery?.toLowerCase().trim() || '';
133
+ const searchLower = props.searchQuery?.toLowerCase().trim() || "";
128
134
 
129
135
  ui.taskIdOrder.forEach((id) => {
130
136
  if (props.tasks[id] && !props.tasks[id]?.hidden) {
@@ -150,7 +156,10 @@ const getTasksInOrder = () => {
150
156
  task.profileName,
151
157
  task.presaleCode,
152
158
  task.reservedTicketsList
153
- ].filter(Boolean).join(' ').toLowerCase();
159
+ ]
160
+ .filter(Boolean)
161
+ .join(" ")
162
+ .toLowerCase();
154
163
 
155
164
  if (!searchableText.includes(searchLower)) return;
156
165
  }
@@ -185,9 +194,14 @@ const dynamicTableHeight = computed(() => {
185
194
  const controlsHeight = windowWidth.value >= 650 ? 55 : 0; // Desktop controls
186
195
  // On desktop: stats + filters all in one row (45px)
187
196
  // On mobile: stats row (40px) + filters stacked (90px) = 130px
188
- const filtersAndStatsHeight = windowWidth.value >= 768
189
- ? (ui.queueStats.show ? 50 : 45) // Desktop: single row
190
- : (ui.queueStats.show ? 130 : 90); // Mobile: stats row + stacked filters
197
+ const filtersAndStatsHeight =
198
+ windowWidth.value >= 768
199
+ ? ui.queueStats.show
200
+ ? 50
201
+ : 45 // Desktop: single row
202
+ : ui.queueStats.show
203
+ ? 130
204
+ : 90; // Mobile: stats row + stacked filters
191
205
  const utilitiesHeight = windowWidth.value <= 768 ? 180 : 160; // Increase mobile utilities space to prevent cutoff
192
206
  const margins =
193
207
  windowWidth.value >= 1024 ? 20 : windowWidth.value <= 480 && windowHeight.value > windowWidth.value ? 8 : 12;
@@ -23,10 +23,10 @@
23
23
  <span class="info-label">Event Name</span>
24
24
  <span class="info-value">{{ task.eventName }}</span>
25
25
  </div>
26
- <div class="info-row" v-if="task.eventVenue">
26
+ <div class="info-row" v-if="task.venueName || task.eventCity">
27
27
  <StadiumWhiteIcon class="info-icon" />
28
28
  <span class="info-label">Venue</span>
29
- <span class="info-value">{{ task.eventVenue }}</span>
29
+ <span class="info-value">{{ [task.venueName, task.eventCity].filter(Boolean).join(', ') }}</span>
30
30
  </div>
31
31
  <div class="info-row" v-if="task.eventLocalDate">
32
32
  <StadiumWhiteIcon class="info-icon" />
@@ -40,6 +40,24 @@
40
40
  </div>
41
41
  <span class="info-value uppercase font-semibold whitespace-normal break-words">{{ task.status }}</span>
42
42
  </div>
43
+ <div class="info-row" v-if="task._timeLeftString && task._timeLeftString !== 'No Cartholds'">
44
+ <TimerIcon class="info-icon" />
45
+ <span class="info-label">Cart Expiration</span>
46
+ <span class="info-value font-semibold" :class="{ 'text-red-400': task._timeLeftString === '00:00' }">
47
+ {{ task._timeLeftString !== '00:00' ? task._timeLeftString : 'Expired' }}
48
+ </span>
49
+ </div>
50
+ <div class="info-row" v-if="task.reservedTicketsList">
51
+ <TicketIcon class="info-icon" />
52
+ <span class="info-label">Reserved Tickets</span>
53
+ <div class="info-value text-left">
54
+ <div v-for="(line, index) in task.reservedTicketsList.split('\n')" :key="index" class="text-xs leading-tight">
55
+ <span v-if="line.trim()" :class="{ 'text-green-400 font-bold': isTotalPrice(line, index, task.reservedTicketsList.split('\n')) }">
56
+ {{ line.trim() }}
57
+ </span>
58
+ </div>
59
+ </div>
60
+ </div>
43
61
  </div>
44
62
  </div>
45
63
 
@@ -173,6 +191,7 @@ import {
173
191
  EyeIcon,
174
192
  StadiumIcon,
175
193
  StadiumWhiteIcon,
194
+ TicketIcon,
176
195
  MailIcon,
177
196
  KeyIcon,
178
197
  ProfileIcon,
@@ -218,8 +237,30 @@ const copy = (text) => {
218
237
 
219
238
  const formatDate = (dateString) => {
220
239
  if (!dateString) return '';
221
- const date = new Date(dateString);
222
- return date.toLocaleString();
240
+ try {
241
+ const date = new Date(dateString);
242
+ const options = {
243
+ month: 'short',
244
+ day: 'numeric',
245
+ year: 'numeric',
246
+ hour: 'numeric',
247
+ minute: '2-digit',
248
+ hour12: true
249
+ };
250
+ return date.toLocaleString('en-US', options).replace(',', '');
251
+ } catch {
252
+ return dateString;
253
+ }
254
+ };
255
+
256
+ const isTotalPrice = (line, index, lines) => {
257
+ const trimmed = line.trim();
258
+ const nonEmptyLines = lines.filter((l) => l.trim());
259
+ const isLastLine = index === lines.lastIndexOf(nonEmptyLines[nonEmptyLines.length - 1]);
260
+ if (!isLastLine) return false;
261
+ // Match currency symbols ($, €, £, etc.) OR currency codes (USD, EUR, AUD, etc.)
262
+ const totalPricePattern = /^([$€£¥₹₽¢]|[A-Z]{3})\s*[\d,]+\.?\d*$/;
263
+ return totalPricePattern.test(trimmed) && !trimmed.includes('(');
223
264
  };
224
265
 
225
266
  const colorToClass = (color) => {
@@ -38,6 +38,7 @@ export default {
38
38
  profileTags: ["Any"],
39
39
  eventName: "Taylor Swift | The Eras Tour",
40
40
  eventDate: "2023-05-19T22:30:00.000Z",
41
+ eventLocalDate: "2023-05-19T22:30:00.000Z",
41
42
  venueName: "Gillette Stadium",
42
43
  eventCity: "Foxborough, MA",
43
44
  eventUrl:
@@ -72,6 +73,7 @@ export default {
72
73
  profileTags: ["Any"],
73
74
  eventName: "Morgan Wallen: One Night At A Time World Tour",
74
75
  eventDate: "2024-06-06T23:00:00.000Z",
76
+ eventLocalDate: "2024-06-06T23:00:00.000Z",
75
77
  venueName: "Veterans United Home Loans Amphitheater at Virginia Beach",
76
78
  eventCity: "Virginia Beach, VA",
77
79
  eventUrl:
@@ -105,6 +107,7 @@ export default {
105
107
  profileTags: ["Any"],
106
108
  eventName: "Taylor Swift | The Eras Tour",
107
109
  eventDate: "2023-05-21T22:30:00.000Z",
110
+ eventLocalDate: "2023-05-21T22:30:00.000Z",
108
111
  venueName: "Gillette Stadium",
109
112
  eventCity: "Foxborough, MA",
110
113
  eventUrl:
@@ -138,6 +141,7 @@ export default {
138
141
  profileTags: ["Any"],
139
142
  eventName: "Taylor Swift | The Eras Tour",
140
143
  eventDate: "2023-05-19T22:30:00.000Z",
144
+ eventLocalDate: "2023-05-19T22:30:00.000Z",
141
145
  venueName: "Gillette Stadium",
142
146
  eventCity: "Foxborough, MA",
143
147
  eventUrl:
@@ -171,6 +175,7 @@ export default {
171
175
  profileTags: ["Any"],
172
176
  eventName: "Taylor Swift | The Eras Tour",
173
177
  eventDate: "2023-05-19T22:30:00.000Z",
178
+ eventLocalDate: "2023-05-19T22:30:00.000Z",
174
179
  venueName: "Gillette Stadium",
175
180
  eventCity: "Foxborough, MA",
176
181
  eventUrl:
@@ -303,6 +308,7 @@ export default {
303
308
  profileTags: ["Any"],
304
309
  eventName: "Taylor Swift | The Eras Tour",
305
310
  eventDate: "2023-05-21T22:30:00.000Z",
311
+ eventLocalDate: "2023-05-21T22:30:00.000Z",
306
312
  venueName: "Gillette Stadium",
307
313
  eventCity: "Foxborough, MA",
308
314
  eventUrl:
@@ -336,6 +342,7 @@ export default {
336
342
  profileTags: ["Any"],
337
343
  eventName: "Taylor Swift | The Eras Tour",
338
344
  eventDate: "2023-05-21T22:30:00.000Z",
345
+ eventLocalDate: "2023-05-21T22:30:00.000Z",
339
346
  venueName: "Gillette Stadium",
340
347
  eventCity: "Foxborough, MA",
341
348
  eventUrl:
@@ -369,6 +376,7 @@ export default {
369
376
  profileTags: ["Any"],
370
377
  eventName: "Taylor Swift | The Eras Tour",
371
378
  eventDate: "2023-05-21T22:30:00.000Z",
379
+ eventLocalDate: "2023-05-21T22:30:00.000Z",
372
380
  venueName: "Gillette Stadium",
373
381
  eventCity: "Foxborough, MA",
374
382
  eventUrl:
@@ -402,6 +410,7 @@ export default {
402
410
  profileTags: ["Any"],
403
411
  eventName: "Taylor Swift | The Eras Tour",
404
412
  eventDate: "2023-05-21T22:30:00.000Z",
413
+ eventLocalDate: "2023-05-21T22:30:00.000Z",
405
414
  venueName: "Gillette Stadium",
406
415
  eventCity: "Foxborough, MA",
407
416
  eventUrl:
@@ -435,6 +444,7 @@ export default {
435
444
  profileTags: ["Any"],
436
445
  eventName: "Taylor Swift | The Eras Tour",
437
446
  eventDate: "2023-05-21T22:30:00.000Z",
447
+ eventLocalDate: "2023-05-21T22:30:00.000Z",
438
448
  venueName: "Gillette Stadium",
439
449
  eventCity: "Foxborough, MA",
440
450
  hidden: true,
@@ -465,6 +475,7 @@ export default {
465
475
  profileTags: ["Any"],
466
476
  eventName: "Taylor Swift | The Eras Tour",
467
477
  eventDate: "2023-05-21T22:30:00.000Z",
478
+ eventLocalDate: "2023-05-21T22:30:00.000Z",
468
479
  venueName: "Gillette Stadium",
469
480
  eventCity: "Foxborough, MA",
470
481
  hidden: true,
@@ -495,6 +506,7 @@ export default {
495
506
  profileTags: ["Any"],
496
507
  eventName: "Taylor Swift | The Eras Tour",
497
508
  eventDate: "2023-05-21T22:30:00.000Z",
509
+ eventLocalDate: "2023-05-21T22:30:00.000Z",
498
510
  venueName: "Gillette Stadium",
499
511
  eventCity: "Foxborough, MA",
500
512
  hidden: true,
@@ -525,6 +537,7 @@ export default {
525
537
  profileTags: ["Any"],
526
538
  eventName: "Taylor Swift | The Eras Tour",
527
539
  eventDate: "2023-05-21T22:30:00.000Z",
540
+ eventLocalDate: "2023-05-21T22:30:00.000Z",
528
541
  venueName: "Gillette Stadium",
529
542
  eventCity: "Foxborough, MA",
530
543
  hidden: true,
@@ -555,6 +568,7 @@ export default {
555
568
  profileTags: ["Any"],
556
569
  eventName: "Taylor Swift | The Eras Tour",
557
570
  eventDate: "2023-05-21T22:30:00.000Z",
571
+ eventLocalDate: "2023-05-21T22:30:00.000Z",
558
572
  venueName: "Gillette Stadium",
559
573
  eventCity: "Foxborough, MA",
560
574
  hidden: false,
@@ -585,6 +599,7 @@ export default {
585
599
  profileTags: ["Any"],
586
600
  eventName: "Taylor Swift | The Eras Tour",
587
601
  eventDate: "2023-05-21T22:30:00.000Z",
602
+ eventLocalDate: "2023-05-21T22:30:00.000Z",
588
603
  venueName: "Gillette Stadium",
589
604
  eventCity: "Foxborough, MA",
590
605
  hidden: true,
@@ -615,6 +630,7 @@ export default {
615
630
  profileTags: ["Any"],
616
631
  eventName: "Taylor Swift | The Eras Tour",
617
632
  eventDate: "2023-05-21T22:30:00.000Z",
633
+ eventLocalDate: "2023-05-21T22:30:00.000Z",
618
634
  venueName: "Gillette Stadium",
619
635
  eventCity: "Foxborough, MA",
620
636
  hidden: true,
@@ -645,6 +661,7 @@ export default {
645
661
  profileTags: ["Any"],
646
662
  eventName: "Taylor Swift | The Eras Tour",
647
663
  eventDate: "2023-05-21T22:30:00.000Z",
664
+ eventLocalDate: "2023-05-21T22:30:00.000Z",
648
665
  venueName: "Gillette Stadium",
649
666
  eventCity: "Foxborough, MA",
650
667
  hidden: true,
@@ -675,6 +692,7 @@ export default {
675
692
  profileTags: ["Any"],
676
693
  eventName: "Taylor Swift | The Eras Tour",
677
694
  eventDate: "2023-05-21T22:30:00.000Z",
695
+ eventLocalDate: "2023-05-21T22:30:00.000Z",
678
696
  venueName: "Gillette Stadium",
679
697
  eventCity: "Foxborough, MA",
680
698
  hidden: true,
@@ -45,13 +45,19 @@
45
45
 
46
46
  <div class="flex md:hidden items-center justify-between gap-2 mb-1">
47
47
  <Stats class="stats-component flex-1" />
48
- <PriceSortToggle
49
- class="min-w-20 max-w-28 flex-shrink-0 h-8"
50
- :options="['All', 'Checkout']"
51
- :darker="true"
52
- :current="ui.taskFilter"
53
- @change="(e) => ui.setTaskFilter(e)"
54
- />
48
+ <div class="flex gap-2 items-center">
49
+ <div class="bg-dark-400 border border-dark-650 justify-between px-2 flex text-white text-xs font-medium items-center rounded-md h-8">
50
+ <p class="text-[10px]">Name</p>
51
+ <Switch class="scale-75" v-model="preferEventName" />
52
+ </div>
53
+ <PriceSortToggle
54
+ class="min-w-20 max-w-28 flex-shrink-0 h-8"
55
+ :options="['All', 'Checkout']"
56
+ :darker="true"
57
+ :current="ui.taskFilter"
58
+ @change="(e) => ui.setTaskFilter(e)"
59
+ />
60
+ </div>
55
61
  </div>
56
62
 
57
63
  <div class="hidden md:flex items-center gap-2 lg:mb-2 mb-1">
@@ -80,6 +86,10 @@
80
86
  <span v-if="taskSearchQuery" class="ml-2 text-xs text-light-500">{{ filteredTaskCount }}</span>
81
87
  </div>
82
88
  </div>
89
+ <div class="bg-dark-400 border border-dark-650 justify-between px-3 w-32 flex text-white text-xs font-medium items-center rounded-md h-10">
90
+ <p>Name</p>
91
+ <Switch class="scale-75" v-model="preferEventName" />
92
+ </div>
83
93
  <PriceSortToggle
84
94
  class="min-w-24 max-w-28 flex-shrink-0"
85
95
  :options="['All', 'Checkout']"
@@ -118,7 +128,7 @@
118
128
  </div>
119
129
  </div>
120
130
 
121
- <TaskView class="lg:mb-3 mb-2" :tasks="ui.tasks" :searchQuery="taskSearchQuery" />
131
+ <TaskView class="lg:mb-3 mb-2" :tasks="ui.tasks" :searchQuery="taskSearchQuery" :preferEventName="preferEventName" />
122
132
 
123
133
  <Utilities class="utilities-section" />
124
134
  </div>
@@ -230,7 +240,7 @@
230
240
  }
231
241
  </style>
232
242
  <script setup>
233
- import { computed, onMounted, ref } from "vue";
243
+ import { computed, onMounted, ref, watch } from "vue";
234
244
  import { DesktopControls } from "@/components/Tasks/Controls";
235
245
  import TaskView from "@/components/Tasks/TaskView.vue";
236
246
  import ViewTask from "@/components/Tasks/ViewTask.vue";
@@ -242,6 +252,7 @@ import ScrapeVenue from "@/components/Tasks/ScrapeVenue.vue";
242
252
  import MassEditPresaleCode from "@/components/Tasks/MassEdit.vue";
243
253
  import Dropdown from "@/components/ui/controls/atomic/Dropdown.vue";
244
254
  import Stats from "@/components/Tasks/Stats.vue";
255
+ import Switch from "@/components/ui/controls/atomic/Switch.vue";
245
256
  import { useUIStore } from "@/stores/ui";
246
257
  import { TrashIcon, GearIcon, PlusIcon, PlayIcon, PauseIcon } from "@/components/icons";
247
258
  import QuickSettings from "@/components/Tasks/QuickSettings.vue";
@@ -253,6 +264,13 @@ const taskCount = computed(() => Object.keys(ui.getSelectedTasks()).length);
253
264
  ui.refreshQueueStats();
254
265
 
255
266
  const taskSearchQuery = ref('');
267
+ // Load preference from localStorage, default to true
268
+ const preferEventName = ref(localStorage.getItem('preferEventName') !== 'false');
269
+
270
+ // Persist preference to localStorage when changed
271
+ watch(preferEventName, (newValue) => {
272
+ localStorage.setItem('preferEventName', String(newValue));
273
+ });
256
274
 
257
275
  const filteredTaskCount = computed(() => {
258
276
  if (!taskSearchQuery.value.trim()) return '';