@necrolab/dashboard 0.5.6 → 0.5.7

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.7",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": "rm -rf dist && npx workbox-cli generateSW workbox-config.cjs && vite build",
@@ -10,7 +10,25 @@
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
+ <div
14
+ v-if="props.preferEventName && props.task.eventName"
15
+ class="event-details hidden cursor-pointer lg:flex flex-col gap-0.5"
16
+ @click="copy(props.task.eventId)"
17
+ :title="`Event ID: ${props.task.eventId}`">
18
+ <div class="event-name text-white text-xs font-semibold leading-tight">
19
+ {{ props.task.eventName }}
20
+ </div>
21
+ <div v-if="props.task.eventVenue" class="event-venue flex items-center gap-1 text-[10px] text-light-400">
22
+ <StadiumIcon class="w-2.5 h-2.5 flex-shrink-0" />
23
+ <span class="truncate">{{ props.task.eventVenue }}</span>
24
+ </div>
25
+ <div v-if="props.task.eventLocalDate" class="event-date flex items-center gap-1 text-[10px] text-light-400">
26
+ <TimerIcon class="w-2.5 h-2.5 flex-shrink-0" />
27
+ <span>{{ formatEventDate(props.task.eventLocalDate) }}</span>
28
+ </div>
29
+ </div>
13
30
  <h4
31
+ v-else
14
32
  class="task-event-id mx-auto hidden cursor-pointer text-white hover:text-light-300 lg:block"
15
33
  @click="copy(props.task.eventId)">
16
34
  {{ props.task.eventId }}
@@ -375,7 +393,7 @@ h4 {
375
393
  /// <reference path="@/types/index.js" />
376
394
 
377
395
  import { Row } from "@/components/Table";
378
- import { PlayIcon, TrashIcon, BagWhiteIcon, PauseIcon, EditIcon, EyeIcon } from "@/components/icons";
396
+ import { PlayIcon, TrashIcon, BagWhiteIcon, PauseIcon, EditIcon, EyeIcon, StadiumIcon, TimerIcon } from "@/components/icons";
379
397
  import Checkbox from "@/components/ui/controls/atomic/Checkbox.vue";
380
398
  import { useUIStore } from "@/stores/ui";
381
399
  import TaskLabel from "@/components/Tasks/TaskLabel.vue";
@@ -386,9 +404,29 @@ const ui = useUIStore();
386
404
 
387
405
  /** @type {{ task: Task }} */
388
406
  const props = defineProps({
389
- task: { type: Object }
407
+ task: { type: Object },
408
+ preferEventName: { type: Boolean, default: false }
390
409
  });
391
410
 
411
+ // Format event date for display
412
+ const formatEventDate = (dateString) => {
413
+ if (!dateString) return '';
414
+ try {
415
+ const date = new Date(dateString);
416
+ const options = {
417
+ month: 'short',
418
+ day: 'numeric',
419
+ year: 'numeric',
420
+ hour: 'numeric',
421
+ minute: '2-digit',
422
+ hour12: true
423
+ };
424
+ return date.toLocaleString('en-US', options).replace(',', '');
425
+ } catch {
426
+ return dateString;
427
+ }
428
+ };
429
+
392
430
  // Context menu positioning
393
431
  const contextMenuPosition = ref({});
394
432
  const contextMenuRef = ref(null);
@@ -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-start lg: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"
@@ -104,12 +104,13 @@ import { useUIStore } from "@/stores/ui";
104
104
 
105
105
  const props = defineProps({
106
106
  tasks: { type: Object },
107
- searchQuery: { type: String, default: '' }
107
+ searchQuery: { type: String, default: "" },
108
+ preferEventName: { type: Boolean, default: false }
108
109
  });
109
110
 
110
111
  const shouldTaskShow = (task) => {
111
112
  if (ui.taskFilter === "All") return true;
112
- else if (ui.taskFilter === "Checkout") return task.expirationTime;
113
+ else if (ui.taskFilter === "Checkout") return Boolean(task.checkoutUrl);
113
114
  else return true;
114
115
  };
115
116
 
@@ -124,7 +125,7 @@ const siteIdEdgeCases = {
124
125
 
125
126
  const getTasksInOrder = () => {
126
127
  let out = [];
127
- const searchLower = props.searchQuery?.toLowerCase().trim() || '';
128
+ const searchLower = props.searchQuery?.toLowerCase().trim() || "";
128
129
 
129
130
  ui.taskIdOrder.forEach((id) => {
130
131
  if (props.tasks[id] && !props.tasks[id]?.hidden) {
@@ -150,7 +151,10 @@ const getTasksInOrder = () => {
150
151
  task.profileName,
151
152
  task.presaleCode,
152
153
  task.reservedTicketsList
153
- ].filter(Boolean).join(' ').toLowerCase();
154
+ ]
155
+ .filter(Boolean)
156
+ .join(" ")
157
+ .toLowerCase();
154
158
 
155
159
  if (!searchableText.includes(searchLower)) return;
156
160
  }
@@ -185,9 +189,14 @@ const dynamicTableHeight = computed(() => {
185
189
  const controlsHeight = windowWidth.value >= 650 ? 55 : 0; // Desktop controls
186
190
  // On desktop: stats + filters all in one row (45px)
187
191
  // 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
192
+ const filtersAndStatsHeight =
193
+ windowWidth.value >= 768
194
+ ? ui.queueStats.show
195
+ ? 50
196
+ : 45 // Desktop: single row
197
+ : ui.queueStats.show
198
+ ? 130
199
+ : 90; // Mobile: stats row + stacked filters
191
200
  const utilitiesHeight = windowWidth.value <= 768 ? 180 : 160; // Increase mobile utilities space to prevent cutoff
192
201
  const margins =
193
202
  windowWidth.value >= 1024 ? 20 : windowWidth.value <= 480 && windowHeight.value > windowWidth.value ? 8 : 12;
@@ -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 '';