@necrolab/dashboard 0.4.52 → 0.4.54

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/.prettierrc CHANGED
@@ -30,5 +30,7 @@
30
30
  "arrowParens": "always"
31
31
  }
32
32
  }
33
- ]
33
+ ],
34
+ "plugins": ["prettier-plugin-tailwindcss"],
35
+ "tailwindConfig": "./tailwind.config.js"
34
36
  }
@@ -1,3 +1,3 @@
1
1
  {
2
- "recommendations": ["Vue.volar"]
2
+ "recommendations": ["Vue.volar", "esbenp.prettier-vscode"]
3
3
  }
@@ -24,7 +24,7 @@ export default class TicketMaster {
24
24
  this.data.eventDate = Date.now();
25
25
  this.data.eventLocalDate = Date.now();
26
26
  this.data.eventName = "Test Event";
27
- this.data.reservedTicketsList = "ticket1 | ticket2 | ticket3\n$100";
27
+ this.data.reservedTicketsList = " 2x 301/E ($86.47) \n• 2x 306/U ($86.47) \n$345.88";
28
28
 
29
29
  refreshTaskOnFrontEnd(taskData);
30
30
  }
@@ -89,4 +89,4 @@ export default class TicketMaster {
89
89
  scrapeSeats() {
90
90
  this.logger.Info("Scraping seats");
91
91
  }
92
- };
92
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@necrolab/dashboard",
3
- "version": "0.4.52",
3
+ "version": "0.4.54",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": "rm -rf dist && npx workbox-cli generateSW workbox-config.cjs && vite build",
@@ -15,7 +15,7 @@
15
15
  "dependencies": {
16
16
  "@faker-js/faker": "^7.6.0",
17
17
  "@msgpack/msgpack": "^3.0.0-beta2",
18
- "@necrolab/tm-renderer": "^0.1.9",
18
+ "@necrolab/tm-renderer": "^0.1.10",
19
19
  "@vitejs/plugin-vue": "^5.2.1",
20
20
  "@vueuse/core": "^11.3.0",
21
21
  "autoprefixer": "^10.4.21",
@@ -28,6 +28,8 @@
28
28
  "ipaddr.js": "^2.2.0",
29
29
  "pinia": "^2.3.0",
30
30
  "postcss": "^8.4.49",
31
+ "prettier": "^3.5.3",
32
+ "prettier-plugin-tailwindcss": "^0.6.12",
31
33
  "register-service-worker": "^1.7.2",
32
34
  "sass": "^1.83.0",
33
35
  "tailwindcss": "^3.4.17",
@@ -1,8 +1,8 @@
1
1
  <template>
2
- <Row class="relative text-white grid-cols-10 gap-2 lg:grid-cols-12" @click="ui.setOpenContextMenu('')">
3
- <div class="block md:hidden absolute left-1 top-1">
2
+ <Row class="relative grid-cols-10 gap-2 text-white lg:grid-cols-12" @click="ui.setOpenContextMenu('')">
3
+ <div class="absolute left-1 top-1 block md:hidden">
4
4
  <h4
5
- class="text-xs text-white font-bold"
5
+ class="text-xs font-bold text-white"
6
6
  style="
7
7
  background: transparent !important;
8
8
  border: none !important;
@@ -16,13 +16,13 @@
16
16
  {{ props.task.taskId }}
17
17
  </h4>
18
18
  </div>
19
- <div class="col-span-1 lg:col-span-2 flex items-center justify-start">
19
+ <div class="col-span-1 flex items-center justify-start lg:col-span-2">
20
20
  <Checkbox
21
21
  class="ml-2 mr-4 flex-shrink-0"
22
22
  :toggled="props.task.selected"
23
23
  @valueUpdate="ui.toggleTaskSelected(props.task.taskId)" />
24
24
  <h4
25
- class="task-event-id mx-auto hidden lg:block text-white cursor-pointer hover:text-light-300"
25
+ class="task-event-id mx-auto hidden cursor-pointer text-white hover:text-light-300 lg:block"
26
26
  @click="copy(props.task.eventId)">
27
27
  {{ props.task.eventId }}
28
28
  </h4>
@@ -31,7 +31,7 @@
31
31
  <h4 class="text-white">
32
32
  <span v-if="!props.task.reservedTicketsList">-</span>
33
33
  <div v-else>
34
- <div v-for="l in props.task.reservedTicketsList.split(' | ')" :key="l">
34
+ <div v-for="l in props.task.reservedTicketsList.split('\n')" :key="l">
35
35
  <span v-if="!!l.trim()">{{ l.trim() }}</span>
36
36
  </div>
37
37
  </div>
@@ -45,7 +45,7 @@
45
45
  </span>
46
46
  </h4>
47
47
  </div>
48
- <div class="col-span-5 md:col-span-4 lg:col-span-5 text-center justify-center">
48
+ <div class="col-span-5 justify-center text-center md:col-span-4 lg:col-span-5">
49
49
  <div class="status-container">
50
50
  <div
51
51
  class="status-indicator"
@@ -59,7 +59,7 @@
59
59
  <span class="status-text">{{ props.task.status }}</span>
60
60
  </div>
61
61
  </div>
62
- <div class="col-span-2 lg:col-span-3 flex">
62
+ <div class="col-span-2 flex lg:col-span-3">
63
63
  <ul class="task-buttons">
64
64
  <li>
65
65
  <button v-if="task.active" @click="ui.stopTask(task.taskId)">
@@ -91,14 +91,14 @@
91
91
  </li>
92
92
  </ul>
93
93
  </div>
94
- <div class="hidden md:block col-span-1 absolute right-5 top-4 lg:flex items-center justify-center">
95
- <h4 class="text-center text-xs task-id text-white">
94
+ <div class="absolute right-5 top-4 col-span-1 hidden items-center justify-center md:block lg:flex">
95
+ <h4 class="task-id text-center text-xs text-white">
96
96
  {{ props.task.taskId }}
97
97
  </h4>
98
98
  </div>
99
99
  <transition name="fade">
100
100
  <div
101
- class="col-span-10 lg:col-span-12 flex flex-wrap gap-x-4 gap-y-4 lg:gap-x-10 pt-8 pb-2 xl:justify-around will-change-auto"
101
+ class="col-span-10 flex flex-wrap gap-x-4 gap-y-4 pb-2 pt-8 will-change-auto lg:col-span-12 lg:gap-x-10 xl:justify-around"
102
102
  v-if="props.task.isExpanded">
103
103
  <!-- Details -->
104
104
  <TaskLabel
@@ -149,7 +149,7 @@
149
149
  <div
150
150
  v-if="ui.openContextMenu === task.taskId"
151
151
  ref="contextMenuRef"
152
- class="fixed bg-dark-500 text-white w-42 grid grid-cols-1 p-1 gap-1 rounded-lg shadow-xl border border-dark-650 context-menu z-max"
152
+ class="w-42 context-menu z-max fixed grid grid-cols-1 gap-1 rounded-lg border border-dark-650 bg-dark-500 p-1 text-white shadow-xl"
153
153
  :style="contextMenuPosition">
154
154
  <button class="btn-primary" @click="openInNewTab(`${ui.currentCountry.url}/event/${task.eventId}`)">
155
155
  Open Event
@@ -171,30 +171,30 @@ h4 {
171
171
  }
172
172
 
173
173
  .status-container {
174
- @apply flex items-center justify-center mx-auto w-fit bg-dark-500 border border-dark-650 rounded-lg;
174
+ @apply mx-auto flex w-fit items-center justify-center rounded-lg border border-dark-650 bg-dark-500;
175
175
  padding: 6px 12px;
176
176
  gap: 6px;
177
177
  transition: all 0.15s ease;
178
178
  max-width: 100%;
179
179
 
180
180
  &:hover {
181
- @apply bg-dark-550 border-dark-700;
181
+ @apply border-dark-700 bg-dark-550;
182
182
  }
183
183
  }
184
184
 
185
185
  .status-text {
186
- @apply font-medium text-sm truncate uppercase;
186
+ @apply truncate text-sm font-medium uppercase;
187
187
  color: #e2e2e5;
188
188
  letter-spacing: 0.025em;
189
189
  }
190
190
 
191
191
  .task-buttons {
192
- @apply flex items-center justify-center mx-auto bg-dark-500 border border-dark-650 rounded;
192
+ @apply mx-auto flex items-center justify-center rounded border border-dark-650 bg-dark-500;
193
193
  padding: 2px;
194
194
  gap: 1px;
195
195
 
196
196
  button {
197
- @apply flex items-center justify-center transition-all duration-150 border-0 outline-0 relative rounded;
197
+ @apply relative flex items-center justify-center rounded border-0 outline-0 transition-all duration-150;
198
198
  background: transparent;
199
199
  width: 22px;
200
200
  height: 22px;
@@ -226,7 +226,7 @@ h4 {
226
226
  }
227
227
 
228
228
  span {
229
- @apply text-xs font-medium relative z-[1];
229
+ @apply relative z-[1] text-xs font-medium;
230
230
  }
231
231
  }
232
232
 
@@ -1,188 +1,143 @@
1
1
  <template>
2
- <div class="table-component">
3
- <Header class="text-center grid-cols-10 gap-2 lg:grid-cols-12">
4
- <div class="col-span-1 lg:col-span-2 flex items-center justify-start">
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
- />
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>
13
+ <DownIcon v-if="ui.sortData.sortBy === 'eventId' && !ui.sortData.reversed" class="ml-1" />
14
+ <UpIcon v-if="ui.sortData.sortBy === 'eventId' && ui.sortData.reversed" class="ml-1" />
15
+ </div>
16
+ </div>
17
+ <div class="flex-center col-span-2" v-once>
18
+ <TicketIcon class="mr-0 lg:mr-3" />
19
+ <h4 class="hidden lg:flex">Tickets</h4>
20
+ </div>
21
+ <div class="flex-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>
24
+ <DownIcon v-if="ui.sortData.sortBy === 'status' && !ui.sortData.reversed" class="ml-1" />
25
+ <UpIcon v-if="ui.sortData.sortBy === 'status' && ui.sortData.reversed" class="ml-1" />
26
+ </div>
27
+ <div class="flex-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>
30
+ </div>
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>
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
+ </div>
36
+ </Header>
11
37
  <div
12
- class="mx-auto hidden lg:flex items-center"
13
- @click="ui.toggleSort('eventId')"
14
- >
15
- <EventIcon class="lg:mr-3" />
16
- <h4 class="hidden lg:flex">Event</h4>
17
- <DownIcon
18
- v-if="ui.sortData.sortBy === 'eventId' && !ui.sortData.reversed"
19
- class="ml-1"
20
- />
21
- <UpIcon
22
- v-if="ui.sortData.sortBy === 'eventId' && ui.sortData.reversed"
23
- class="ml-1"
24
- />
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'" />
42
+ </div>
43
+ <div
44
+ v-if="getTasksInOrder().length === 0"
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>
59
+ </div>
25
60
  </div>
26
- </div>
27
- <div class="col-span-2 flex-center" v-once>
28
- <TicketIcon class="mr-0 lg:mr-3" />
29
- <h4 class="hidden lg:flex">Tickets</h4>
30
- </div>
31
- <div
32
- class="col-span-5 md:col-span-4 lg:col-span-5 flex-center"
33
- @click="ui.toggleSort('status')"
34
- >
35
- <StatusIcon class="mr-0 lg:mr-3" />
36
- <h4 class="hidden lg:flex">Status</h4>
37
- <DownIcon
38
- v-if="ui.sortData.sortBy === 'status' && !ui.sortData.reversed"
39
- class="ml-1"
40
- />
41
- <UpIcon
42
- v-if="ui.sortData.sortBy === 'status' && ui.sortData.reversed"
43
- class="ml-1"
44
- />
45
- </div>
46
- <div class="col-span-2 lg:col-span-3 flex-center" v-once>
47
- <ClickIcon class="mr-0 lg:mr-3" />
48
- <h4 class="hidden lg:flex">Actions</h4>
49
- </div>
50
- <div
51
- class="absolute right-5 hidden lg:flex items-center top-3.5"
52
- @click="ui.toggleSort('taskId')"
53
- >
54
- <h4 class="text-center text-xs">ID</h4>
55
- <DownIcon
56
- v-if="ui.sortData.sortBy === 'taskId' && !ui.sortData.reversed"
57
- class="ml-1"
58
- />
59
- <UpIcon
60
- v-if="ui.sortData.sortBy === 'taskId' && ui.sortData.reversed"
61
- class="ml-1"
62
- />
63
- </div>
64
- </Header>
65
- <div
66
- class="flex flex-col divide-y divide-dark-650 overflow-y-auto hidden-scrollbars overflow-x-hidden stop-pan"
67
- :style="{ maxHeight: dynamicTableHeight }"
68
- >
69
- <div
70
- v-for="(task, i) in getTasksInOrder()"
71
- :key="task.taskId"
72
- class="task-row-container"
73
- >
74
- <Task :task="task" :class="i % 2 == 1 ? 'table-row-even' : 'table-row-odd'" />
75
- </div>
76
- <div
77
- v-if="getTasksInOrder().length === 0"
78
- class="flex flex-col items-center justify-center py-8 empty-state text-center"
79
- >
80
- <div
81
- v-if="
82
- !ui.queueStats.queued &&
83
- !ui.queueStats.sleeping &&
84
- ui.queueStats.nextQueuePasses.length === 0
85
- "
86
- >
87
- <TasksIcon class="w-12 h-12 text-dark-400 mb-3 opacity-50 mx-auto" />
88
- <p class="text-light-400 text-sm">No tasks yet</p>
89
- <p class="text-light-500 text-xs mt-1">Create tasks to get started</p>
90
- </div>
91
- <div v-else>
92
- <TasksIcon class="w-12 h-12 text-dark-400 mb-3 opacity-50 mx-auto" />
93
- <p class="text-light-400 text-sm">No tasks match current filters</p>
94
- <p class="text-light-500 text-xs mt-1">Adjust filters to see tasks</p>
95
- </div>
96
- </div>
97
61
  </div>
98
- </div>
99
62
  </template>
100
63
  <style lang="scss" scoped>
101
64
  h4 {
102
- @apply text-white;
65
+ @apply text-white;
103
66
  }
104
67
 
105
68
  .stop-pan {
106
- touch-action: pan-y pan-up pan-down;
69
+ touch-action: pan-y pan-up pan-down;
107
70
  }
108
71
 
109
72
  .task-row-container {
110
- min-height: 69px;
111
- flex-shrink: 0;
112
- transition: background-color 0.15s ease;
73
+ min-height: 69px;
74
+ flex-shrink: 0;
75
+ transition: background-color 0.15s ease;
113
76
 
114
- &:hover {
115
- @apply bg-dark-550 !important;
116
- }
77
+ &:hover {
78
+ @apply bg-dark-550 !important;
79
+ }
117
80
 
118
- @media (max-width: 768px) {
119
- min-height: 58px;
120
- }
81
+ @media (max-width: 768px) {
82
+ min-height: 58px;
83
+ }
121
84
 
122
- @media (max-width: 480px) and (orientation: portrait) {
123
- min-height: 50px;
124
- }
85
+ @media (max-width: 480px) and (orientation: portrait) {
86
+ min-height: 50px;
87
+ }
125
88
  }
126
89
 
127
90
  .empty-state {
128
- @apply bg-dark-400;
129
- color: #969696;
130
- font-size: 14px;
131
- font-weight: 500;
91
+ @apply bg-dark-400;
92
+ color: #969696;
93
+ font-size: 14px;
94
+ font-weight: 500;
132
95
  }
133
96
  </style>
134
97
  <script setup>
135
98
  import { computed, ref, onMounted, onUnmounted } from "vue";
136
99
  import { Table, Header } from "@/components/Table";
137
- import {
138
- EventIcon,
139
- TicketIcon,
140
- StatusIcon,
141
- ClickIcon,
142
- DownIcon,
143
- UpIcon,
144
- TasksIcon,
145
- } from "@/components/icons";
100
+ import { EventIcon, TicketIcon, StatusIcon, ClickIcon, DownIcon, UpIcon, TasksIcon } from "@/components/icons";
146
101
  import Task from "./Task.vue";
147
102
  import Checkbox from "@/components/ui/controls/atomic/Checkbox.vue";
148
103
  import { useUIStore } from "@/stores/ui";
149
104
 
150
105
  const props = defineProps({
151
- tasks: { type: Object },
106
+ tasks: { type: Object }
152
107
  });
153
108
 
154
109
  const shouldTaskShow = (task) => {
155
- if (ui.taskFilter === "All") return true;
156
- else if (ui.taskFilter === "Checkout") return task.expirationTime || task.noCartholds;
157
- else return true;
110
+ if (ui.taskFilter === "All") return true;
111
+ else if (ui.taskFilter === "Checkout") return task.expirationTime;
112
+ else return true;
158
113
  };
159
114
 
160
115
  const ui = useUIStore();
161
116
 
162
117
  const siteIdEdgeCases = {
163
- LN_US: ["TM_US"],
164
- TM_CA: ["TM_US"],
165
- TM_IE: ["TM_UK"],
166
- TM_NZ: ["TM_AU"],
118
+ LN_US: ["TM_US"],
119
+ TM_CA: ["TM_US"],
120
+ TM_IE: ["TM_UK"],
121
+ TM_NZ: ["TM_AU"]
167
122
  };
168
123
 
169
124
  const getTasksInOrder = () => {
170
- let out = [];
171
- ui.taskIdOrder.forEach((id) => {
172
- if (props.tasks[id] && !props.tasks[id]?.hidden) {
173
- const task = props.tasks[id];
174
-
175
- if (
176
- task.siteId !== ui.currentCountry.siteId &&
177
- !siteIdEdgeCases[task.siteId]?.includes(ui.currentCountry.siteId)
178
- )
179
- return;
180
- if (ui.currentEvent && task.eventId !== ui.currentEvent) return;
181
- if (!shouldTaskShow(task)) return;
182
- out.push(task);
183
- }
184
- });
185
- return out;
125
+ let out = [];
126
+ ui.taskIdOrder.forEach((id) => {
127
+ if (props.tasks[id] && !props.tasks[id]?.hidden) {
128
+ const task = props.tasks[id];
129
+
130
+ if (
131
+ task.siteId !== ui.currentCountry.siteId &&
132
+ !siteIdEdgeCases[task.siteId]?.includes(ui.currentCountry.siteId)
133
+ )
134
+ return;
135
+ if (ui.currentEvent && task.eventId !== ui.currentEvent) return;
136
+ if (!shouldTaskShow(task)) return;
137
+ out.push(task);
138
+ }
139
+ });
140
+ return out;
186
141
  };
187
142
 
188
143
  // Dynamic height calculation to prevent page scrolling
@@ -190,54 +145,42 @@ const windowHeight = ref(window.innerHeight);
190
145
  const windowWidth = ref(window.innerWidth);
191
146
 
192
147
  const updateDimensions = () => {
193
- windowHeight.value = window.innerHeight;
194
- windowWidth.value = window.innerWidth;
148
+ windowHeight.value = window.innerHeight;
149
+ windowWidth.value = window.innerWidth;
195
150
  };
196
151
 
197
152
  onMounted(() => {
198
- window.addEventListener("resize", updateDimensions);
153
+ window.addEventListener("resize", updateDimensions);
199
154
  });
200
155
 
201
156
  onUnmounted(() => {
202
- window.removeEventListener("resize", updateDimensions);
157
+ window.removeEventListener("resize", updateDimensions);
203
158
  });
204
159
 
205
160
  const dynamicTableHeight = computed(() => {
206
- // Calculate available space for table
207
- const headerHeight = 60; // Header + navbar
208
- const titleHeight = 50; // Tasks title and mobile controls
209
- const statsHeight = 50; // Queue stats
210
- const controlsHeight = windowWidth.value >= 650 ? 60 : 0; // Desktop controls
211
- const filtersHeight = 50; // Event dropdown and filter toggle
212
- const utilitiesHeight =
213
- windowWidth.value <= 480 && windowHeight.value > windowWidth.value ? 150 : 200; // Reduce utilities height in mobile portrait
214
- const margins =
215
- windowWidth.value >= 1024
216
- ? 40
217
- : windowWidth.value <= 480 && windowHeight.value > windowWidth.value
218
- ? 15
219
- : 20; // Reduce margins in mobile portrait
220
-
221
- const totalUsedSpace =
222
- headerHeight +
223
- titleHeight +
224
- statsHeight +
225
- controlsHeight +
226
- filtersHeight +
227
- utilitiesHeight +
228
- margins;
229
- const availableHeight = windowHeight.value - totalUsedSpace;
230
-
231
- // Calculate row height based on screen size
232
- const rowHeight = windowWidth.value <= 768 ? 58 : 69; // Mobile vs desktop row height
233
- const minRowsToShow = 2; // Always show at least 2 rows
234
- const minHeight = minRowsToShow * rowHeight;
235
-
236
- // Calculate how many complete rows can fit
237
- const maxCompleteRows =
238
- Math.floor(Math.max(availableHeight, minHeight) / rowHeight) + 1;
239
- const exactHeight = maxCompleteRows * rowHeight;
240
-
241
- return exactHeight + "px";
161
+ // Calculate available space for table
162
+ const headerHeight = 60; // Header + navbar
163
+ const titleHeight = 50; // Tasks title and mobile controls
164
+ const statsHeight = 50; // Queue stats
165
+ const controlsHeight = windowWidth.value >= 650 ? 60 : 0; // Desktop controls
166
+ const filtersHeight = 50; // Event dropdown and filter toggle
167
+ const utilitiesHeight = windowWidth.value <= 480 && windowHeight.value > windowWidth.value ? 150 : 200; // Reduce utilities height in mobile portrait
168
+ const margins =
169
+ windowWidth.value >= 1024 ? 40 : windowWidth.value <= 480 && windowHeight.value > windowWidth.value ? 15 : 20; // Reduce margins in mobile portrait
170
+
171
+ const totalUsedSpace =
172
+ headerHeight + titleHeight + statsHeight + controlsHeight + filtersHeight + utilitiesHeight + margins;
173
+ const availableHeight = windowHeight.value - totalUsedSpace;
174
+
175
+ // Calculate row height based on screen size
176
+ const rowHeight = windowWidth.value <= 768 ? 58 : 69; // Mobile vs desktop row height
177
+ const minRowsToShow = 2; // Always show at least 2 rows
178
+ const minHeight = minRowsToShow * rowHeight;
179
+
180
+ // Calculate how many complete rows can fit
181
+ const maxCompleteRows = Math.floor(Math.max(availableHeight, minHeight) / rowHeight) + 1;
182
+ const exactHeight = maxCompleteRows * rowHeight;
183
+
184
+ return exactHeight + "px";
242
185
  });
243
186
  </script>
@@ -28,7 +28,7 @@ export default {
28
28
  quantity: 8,
29
29
  proxy: "http://events1597:yEXBe4Hs@23.26.22.61:61234",
30
30
  eventId: "01005D5C92031D57",
31
- reservedTicketsList: "-",
31
+ reservedTicketsList: "• 2x 301/E ($86.47) \n• 2x 306/U ($86.47) \n$345.88",
32
32
  expirationTime: null,
33
33
  doNotPay: false,
34
34
  agedAccount: true,
@@ -63,7 +63,7 @@ export default {
63
63
  quantity: 8,
64
64
  proxy: "http://events1338:xN4PBVze@23.26.21.58:61234",
65
65
  eventId: "01005D85964A1747",
66
- reservedTicketsList: "-",
66
+ reservedTicketsList: "• 2x 301/E ($86.47) \n• 2x 306/U ($86.47) \n$345.88",
67
67
  expirationTime: null,
68
68
  doNotPay: false,
69
69
  agedAccount: false,
package/src/stores/ui.js CHANGED
@@ -103,8 +103,10 @@ export const useUIStore = defineStore("ui", () => {
103
103
  const t1 = new Date();
104
104
  for (const [key, value] of Object.entries(tasks.value)) {
105
105
  if (value.expirationTime)
106
- tasks.value[key]._timeLeftString = timeDifference(Date.parse(value.expirationTime), t1.getTime());
107
- else if (value.noCartholds) tasks.value[key]._timeLeftString = "No Cartholds";
106
+ tasks.value[key]._timeLeftString =
107
+ value.expirationTime == "Invalid Date"
108
+ ? "No Cartholds"
109
+ : timeDifference(Date.parse(value.expirationTime), t1.getTime());
108
110
  else tasks.value[key]._timeLeftString = undefined;
109
111
  }
110
112