@necrolab/dashboard 0.4.49 → 0.4.51

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.
@@ -1,58 +1,56 @@
1
1
  <template>
2
- <div @click="toggleOpened" class="dropdown" ref="dropdownRef">
3
- <span class="dropdown-display">
4
- <span class="dropdown-value" :class="capitalize ? 'capitalize' : ''">
5
- {{ displayValue }}
6
- </span>
7
- <div class="dropdown-counter">
8
- <span v-if="selectedOptions.length > 1" class="counter-badge">
9
- {{ selectedOptions.length }}
10
- </span>
11
- <DownIcon class="dropdown-arrow" :class="opened ? 'rotate-180' : ''" />
12
- </div>
13
- </span>
14
- <Teleport to="body">
15
- <transition name="dropdown-fade">
16
- <div
17
- v-if="opened"
18
- class="dropdown-menu-portal multi scrollable"
19
- :style="menuStyle"
20
- @click.stop
21
- @wheel.stop
22
- @touchmove.stop
23
- >
24
- <div class="option-list scrollable">
25
- <button
26
- v-for="(option, i) in props.options"
27
- :key="option.value"
28
- class="dropdown-item"
29
- :class="i !== 0 ? 'border-t border-dark-650' : ''"
30
- @click.stop="toggleOption(option.value)"
31
- >
32
- <span class="dropdown-item-text" :class="capitalize ? 'capitalize' : ''">
33
- {{ option.label }}
34
- </span>
35
- <CheckmarkIcon v-if="selectedOptions.includes(option.value)" class="ml-2" />
36
- </button>
37
- </div>
38
-
39
- <div v-if="selectedOptions.length > 0" class="selected-summary">
40
- <div class="flex items-center justify-between">
41
- <div class="selected-count">
42
- <span class="count-badge">
43
- {{ selectedOptions.length }}
44
- </span>
45
- <span class="count-label">
46
- item{{ selectedOptions.length === 1 ? "" : "s" }} selected
2
+ <div @click="toggleOpened" class="dropdown" ref="dropdownRef">
3
+ <span class="dropdown-display">
4
+ <span class="dropdown-value" :class="capitalize ? 'capitalize' : ''">
5
+ {{ displayValue }}
6
+ </span>
7
+ <div class="dropdown-counter">
8
+ <span v-if="selectedOptions.length > 1" class="counter-badge">
9
+ {{ selectedOptions.length }}
47
10
  </span>
48
- </div>
49
- <button class="clear-button" @click.stop="clearAll">Clear All</button>
11
+ <DownIcon class="dropdown-arrow" :class="opened ? 'rotate-180' : ''" />
50
12
  </div>
51
- </div>
52
- </div>
53
- </transition>
54
- </Teleport>
55
- </div>
13
+ </span>
14
+ <Teleport to="body">
15
+ <transition name="dropdown-fade">
16
+ <div
17
+ v-if="opened"
18
+ class="dropdown-menu-portal multi scrollable"
19
+ :style="menuStyle"
20
+ @click.stop
21
+ @wheel.stop
22
+ @touchmove.stop>
23
+ <div class="option-list scrollable">
24
+ <button
25
+ v-for="(option, i) in props.options"
26
+ :key="option.value"
27
+ class="dropdown-item"
28
+ :class="i !== 0 ? 'border-t border-dark-650' : ''"
29
+ @click.stop="toggleOption(option.value)">
30
+ <span class="dropdown-item-text" :class="capitalize ? 'capitalize' : ''">
31
+ {{ option.label }}
32
+ </span>
33
+ <CheckmarkIcon v-if="selectedOptions.includes(option.value)" class="ml-2" />
34
+ </button>
35
+ </div>
36
+
37
+ <div v-if="selectedOptions.length > 0" class="selected-summary">
38
+ <div class="flex items-center justify-between">
39
+ <div class="selected-count">
40
+ <span class="count-badge">
41
+ {{ selectedOptions.length }}
42
+ </span>
43
+ <span class="count-label">
44
+ item{{ selectedOptions.length === 1 ? "" : "s" }} selected
45
+ </span>
46
+ </div>
47
+ <button class="clear-button" @click.stop="clearAll">Clear All</button>
48
+ </div>
49
+ </div>
50
+ </div>
51
+ </transition>
52
+ </Teleport>
53
+ </div>
56
54
  </template>
57
55
 
58
56
  <script setup>
@@ -65,13 +63,13 @@ import { useClickOutside } from "@/composables/useClickOutside";
65
63
  const ui = useUIStore();
66
64
 
67
65
  const props = defineProps({
68
- onSelect: { type: Function },
69
- default: { type: String },
70
- options: { type: Array, required: true },
71
- rightAmount: { type: String },
72
- topPadding: { type: String },
73
- capitalize: { type: Boolean },
74
- includeAdjacentButtons: { type: Boolean, default: false },
66
+ onSelect: { type: Function },
67
+ default: { type: String },
68
+ options: { type: Array, required: true },
69
+ rightAmount: { type: String },
70
+ topPadding: { type: String },
71
+ capitalize: { type: Boolean },
72
+ includeAdjacentButtons: { type: Boolean, default: false }
75
73
  });
76
74
 
77
75
  const selectedOptions = ref([]);
@@ -80,283 +78,279 @@ const id = Math.random();
80
78
  const opened = computed(() => ui.currentDropdown === id);
81
79
 
82
80
  const displayValue = computed(() => {
83
- if (selectedOptions.value.length === 0) {
84
- return props.default || "Select options...";
85
- }
86
-
87
- if (selectedOptions.value.length === 1) {
88
- const option = props.options.find((opt) => opt.value === selectedOptions.value[0]);
89
- return option ? option.label : selectedOptions.value[0];
90
- }
91
-
92
- if (selectedOptions.value.length <= 2) {
93
- const labels = selectedOptions.value.map((val) => {
94
- const option = props.options.find((opt) => opt.value === val);
95
- return option ? option.label : val;
96
- });
97
- return labels.join(", ");
98
- }
99
-
100
- const firstOption = props.options.find((opt) => opt.value === selectedOptions.value[0]);
101
- const firstName = firstOption ? firstOption.label : selectedOptions.value[0];
102
- return `${firstName} +${selectedOptions.value.length - 1} more`;
81
+ if (selectedOptions.value.length === 0) {
82
+ return props.default || "Select options...";
83
+ }
84
+
85
+ if (selectedOptions.value.length === 1) {
86
+ const option = props.options.find((opt) => opt.value === selectedOptions.value[0]);
87
+ return option ? option.label : selectedOptions.value[0];
88
+ }
89
+
90
+ if (selectedOptions.value.length <= 2) {
91
+ const labels = selectedOptions.value.map((val) => {
92
+ const option = props.options.find((opt) => opt.value === val);
93
+ return option ? option.label : val;
94
+ });
95
+ return labels.join(", ");
96
+ }
97
+
98
+ const firstOption = props.options.find((opt) => opt.value === selectedOptions.value[0]);
99
+ const firstName = firstOption ? firstOption.label : selectedOptions.value[0];
100
+ return `${firstName} +${selectedOptions.value.length - 1} more`;
103
101
  });
104
102
 
105
103
  // Use composables for positioning and click outside
106
104
  const { menuStyle, updatePosition } = useDropdownPosition(dropdownRef, {
107
- maxHeight: 280,
108
- includeAdjacentButtons: props.includeAdjacentButtons,
109
- estimateHeight: () => {
110
- const optionsCount = props.options?.length || 0;
111
- const summaryHeight = selectedOptions.value.length > 0 ? 70 : 0;
112
- const baseMaxHeight = selectedOptions.value.length > 0 ? 280 : 200;
113
- const optionListHeight = Math.min(optionsCount * 44, 200);
114
- return Math.min(optionListHeight + summaryHeight, baseMaxHeight);
115
- },
105
+ maxHeight: 280,
106
+ includeAdjacentButtons: props.includeAdjacentButtons,
107
+ estimateHeight: () => {
108
+ const optionsCount = props.options?.length || 0;
109
+ const summaryHeight = selectedOptions.value.length > 0 ? 70 : 0;
110
+ const baseMaxHeight = selectedOptions.value.length > 0 ? 280 : 200;
111
+ const optionListHeight = Math.min(optionsCount * 44, 200);
112
+ return Math.min(optionListHeight + summaryHeight, baseMaxHeight);
113
+ }
116
114
  });
117
115
 
118
116
  useClickOutside(dropdownRef, () => {
119
- if (opened.value) {
120
- ui.setCurrentDropdown("");
121
- }
117
+ if (opened.value) {
118
+ ui.setCurrentDropdown("");
119
+ }
122
120
  });
123
121
 
124
122
  const toggleOpened = () => {
125
- if (opened.value) {
126
- ui.setCurrentDropdown("");
127
- } else {
128
- ui.setCurrentDropdown(id);
129
- updatePosition();
130
- }
123
+ if (opened.value) {
124
+ ui.setCurrentDropdown("");
125
+ } else {
126
+ ui.setCurrentDropdown(id);
127
+ updatePosition();
128
+ }
131
129
  };
132
130
 
133
131
  const toggleOption = (option) => {
134
- const index = selectedOptions.value.indexOf(option);
135
- if (index === -1) {
136
- selectedOptions.value.push(option);
137
- } else {
138
- selectedOptions.value.splice(index, 1);
139
- }
140
-
141
- // Handle default logic
142
- if (selectedOptions.value.length === 0 && props.default) {
143
- selectedOptions.value = [props.default];
144
- }
145
- if (
146
- selectedOptions.value.length > 1 &&
147
- selectedOptions.value.includes(props.default) &&
148
- option !== props.default
149
- ) {
150
- selectedOptions.value = selectedOptions.value.filter((e) => e !== props.default);
151
- } else if (option === props.default) {
152
- selectedOptions.value = [props.default];
153
- }
132
+ const index = selectedOptions.value.indexOf(option);
133
+ if (index === -1) {
134
+ selectedOptions.value.push(option);
135
+ } else {
136
+ selectedOptions.value.splice(index, 1);
137
+ }
154
138
 
155
- if (typeof props.onSelect === "function") {
156
- props.onSelect(selectedOptions.value);
157
- }
139
+ // Handle default logic
140
+ if (selectedOptions.value.length === 0 && props.default) {
141
+ selectedOptions.value = [props.default];
142
+ }
143
+ if (selectedOptions.value.length > 1 && selectedOptions.value.includes(props.default) && option !== props.default) {
144
+ selectedOptions.value = selectedOptions.value.filter((e) => e !== props.default);
145
+ } else if (option === props.default) {
146
+ selectedOptions.value = [props.default];
147
+ }
148
+
149
+ if (typeof props.onSelect === "function") {
150
+ props.onSelect(selectedOptions.value);
151
+ }
158
152
 
159
- // Recalculate position after selection changes
160
- updatePosition();
153
+ // Recalculate position after selection changes
154
+ updatePosition();
161
155
  };
162
156
 
163
157
  const clearAll = () => {
164
- // Default to first option instead of empty
165
- if (props.options && props.options.length > 0) {
166
- selectedOptions.value = [props.options[0].value];
167
- if (typeof props.onSelect === "function") {
168
- props.onSelect([props.options[0].value]);
158
+ // Default to first option instead of empty
159
+ if (props.options && props.options.length > 0) {
160
+ selectedOptions.value = [props.options[0].value];
161
+ if (typeof props.onSelect === "function") {
162
+ props.onSelect([props.options[0].value]);
163
+ }
164
+ } else if (props.default) {
165
+ selectedOptions.value = [props.default];
166
+ if (typeof props.onSelect === "function") {
167
+ props.onSelect([props.default]);
168
+ }
169
+ } else {
170
+ selectedOptions.value = [];
171
+ if (typeof props.onSelect === "function") {
172
+ props.onSelect([]);
173
+ }
169
174
  }
170
- } else if (props.default) {
171
- selectedOptions.value = [props.default];
172
- if (typeof props.onSelect === "function") {
173
- props.onSelect([props.default]);
174
- }
175
- } else {
176
- selectedOptions.value = [];
177
- if (typeof props.onSelect === "function") {
178
- props.onSelect([]);
179
- }
180
- }
181
175
 
182
- // Recalculate position after clearing
183
- updatePosition();
176
+ // Recalculate position after clearing
177
+ updatePosition();
184
178
  };
185
179
 
186
180
  // Initialize with default option if provided
187
181
  if (props.default && !selectedOptions.value.includes(props.default)) {
188
- selectedOptions.value = [props.default];
182
+ selectedOptions.value = [props.default];
189
183
  }
190
184
  </script>
191
185
 
192
186
  <style scoped>
193
187
  .dropdown {
194
- @apply relative w-full h-12 text-white ml-auto rounded-lg ring-0;
195
- background: linear-gradient(135deg, #2a2b30 0%, #2e2f34 100%);
196
- border: 1px solid #3d3e44;
197
- padding: 0.75rem;
188
+ @apply relative w-full h-10 text-white ml-auto rounded-lg ring-0;
189
+ background: linear-gradient(135deg, #2a2b30 0%, #2e2f34 100%);
190
+ border: 1px solid #3d3e44;
191
+ padding: 0.75rem;
198
192
  }
199
193
 
200
194
  .dropdown:hover {
201
- @apply border-dark-400;
195
+ @apply border-dark-400;
202
196
  }
203
197
 
204
198
  .dropdown:focus-within {
205
- @apply border-blue-500;
199
+ @apply border-blue-500;
206
200
  }
207
201
 
208
202
  @media (max-width: 810px) {
209
- .dropdown {
210
- @apply h-10;
211
- padding: 0.625rem;
212
- }
203
+ .dropdown {
204
+ @apply h-10;
205
+ padding: 0.625rem;
206
+ }
213
207
  }
214
208
 
215
209
  .dropdown-display {
216
- @apply flex items-center justify-between z-10;
210
+ @apply flex items-center justify-between z-10;
217
211
  }
218
212
 
219
213
  .dropdown-value {
220
- @apply w-full overflow-hidden block truncate pr-2 text-sm;
214
+ @apply w-full overflow-hidden block truncate pr-2 text-sm;
221
215
  }
222
216
 
223
217
  .dropdown-counter {
224
- @apply flex items-center gap-2 absolute right-2;
218
+ @apply flex items-center gap-2 absolute right-2;
225
219
  }
226
220
 
227
221
  .counter-badge {
228
- @apply bg-green-500 text-white text-xs font-semibold px-1.5 py-0.5 rounded-full text-center min-w-[18px] shadow-sm;
222
+ @apply bg-green-500 text-white text-xs font-semibold px-1.5 py-0.5 rounded-full text-center min-w-[18px] shadow-sm;
229
223
  }
230
224
 
231
225
  .dropdown-arrow {
232
- @apply min-w-4 min-h-4 transition-all duration-300;
226
+ @apply min-w-4 min-h-4 transition-all duration-300;
233
227
  }
234
228
 
235
229
  .dropdown-menu-portal {
236
- @apply rounded-xl shadow-2xl overflow-hidden;
237
- background: linear-gradient(135deg, #2a2b30 0%, #2e2f34 100%);
238
- border: 1px solid #3d3e44;
239
- backdrop-filter: blur(12px);
240
- box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.4), 0 10px 10px -5px rgba(0, 0, 0, 0.2),
241
- 0 0 0 1px rgba(255, 255, 255, 0.05);
242
- overscroll-behavior: contain !important;
243
- touch-action: pan-y !important;
244
- -webkit-overflow-scrolling: touch !important;
245
- scrollbar-width: thin;
246
- scrollbar-color: #4b5563 transparent;
230
+ @apply rounded-xl shadow-2xl overflow-hidden;
231
+ background: linear-gradient(135deg, #2a2b30 0%, #2e2f34 100%);
232
+ border: 1px solid #3d3e44;
233
+ backdrop-filter: blur(12px);
234
+ box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.4), 0 10px 10px -5px rgba(0, 0, 0, 0.2),
235
+ 0 0 0 1px rgba(255, 255, 255, 0.05);
236
+ overscroll-behavior: contain !important;
237
+ touch-action: pan-y !important;
238
+ -webkit-overflow-scrolling: touch !important;
239
+ scrollbar-width: thin;
240
+ scrollbar-color: #4b5563 transparent;
247
241
  }
248
242
 
249
243
  .dropdown-menu-portal::-webkit-scrollbar {
250
- width: 6px;
244
+ width: 6px;
251
245
  }
252
246
 
253
247
  .dropdown-menu-portal::-webkit-scrollbar-track {
254
- background: transparent;
248
+ background: transparent;
255
249
  }
256
250
 
257
251
  .dropdown-menu-portal::-webkit-scrollbar-thumb {
258
- background: #4b5563;
259
- border-radius: 3px;
252
+ background: #4b5563;
253
+ border-radius: 3px;
260
254
  }
261
255
 
262
256
  .dropdown-menu-portal::-webkit-scrollbar-thumb:hover {
263
- background: #6b7280;
257
+ background: #6b7280;
264
258
  }
265
259
 
266
260
  .dropdown-menu-portal.multi .option-list {
267
- @apply max-h-48 overflow-y-auto;
268
- overscroll-behavior: contain !important;
269
- touch-action: pan-y !important;
270
- -webkit-overflow-scrolling: touch !important;
261
+ @apply max-h-48 overflow-y-auto;
262
+ overscroll-behavior: contain !important;
263
+ touch-action: pan-y !important;
264
+ -webkit-overflow-scrolling: touch !important;
271
265
  }
272
266
 
273
267
  .dropdown-item {
274
- @apply cursor-pointer text-left w-full text-white transition-all duration-200 flex justify-between items-center;
275
- padding: 0.75rem 1rem;
276
- font-size: 0.875rem;
277
- font-weight: 500;
278
- border-bottom: 1px solid rgba(61, 62, 68, 0.3);
268
+ @apply cursor-pointer text-left w-full text-white transition-all duration-200 flex justify-between items-center;
269
+ padding: 0.75rem 1rem;
270
+ font-size: 0.875rem;
271
+ font-weight: 500;
272
+ border-bottom: 1px solid rgba(61, 62, 68, 0.3);
279
273
  }
280
274
 
281
275
  .dropdown-item:last-child {
282
- border-bottom: none;
276
+ border-bottom: none;
283
277
  }
284
278
 
285
279
  .dropdown-item:hover {
286
- @apply bg-dark-600;
287
- color: #ffffff;
280
+ @apply bg-dark-600;
281
+ color: #ffffff;
288
282
  }
289
283
 
290
284
  .dropdown-item:active {
291
- @apply bg-dark-650;
292
- box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4);
285
+ @apply bg-dark-650;
286
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4);
293
287
  }
294
288
 
295
289
  .dropdown-item:first-child {
296
- @apply rounded-t-xl;
290
+ @apply rounded-t-xl;
297
291
  }
298
292
 
299
293
  .dropdown-item:last-child {
300
- @apply rounded-b-xl;
294
+ @apply rounded-b-xl;
301
295
  }
302
296
 
303
297
  .dropdown-item-text {
304
- @apply overflow-hidden;
298
+ @apply overflow-hidden;
305
299
  }
306
300
 
307
301
  /* Checkmark styling */
308
302
  .dropdown-item svg {
309
- @apply w-4 h-4;
310
- color: #10b981;
303
+ @apply w-4 h-4;
304
+ color: #10b981;
311
305
  }
312
306
 
313
307
  .selected-summary {
314
- @apply border-t bg-dark-550 w-full px-4 py-3;
315
- border-top: 1px solid rgba(61, 62, 68, 0.5);
308
+ @apply border-t bg-dark-550 w-full px-4 py-3;
309
+ border-top: 1px solid rgba(61, 62, 68, 0.5);
316
310
  }
317
311
 
318
312
  .selected-count {
319
- @apply flex items-center gap-2;
313
+ @apply flex items-center gap-2;
320
314
  }
321
315
 
322
316
  .count-badge {
323
- @apply bg-green-500 text-white text-xs font-semibold px-2 py-1 rounded-full shadow-sm;
317
+ @apply bg-green-500 text-white text-xs font-semibold px-2 py-1 rounded-full shadow-sm;
324
318
  }
325
319
 
326
320
  .count-label {
327
- @apply text-xs font-medium text-light-400;
321
+ @apply text-xs font-medium text-light-400;
328
322
  }
329
323
 
330
324
  .clear-button {
331
- @apply text-xs bg-red-500 text-white transition-colors duration-200 font-medium px-3 py-1.5 rounded-lg shadow-sm;
325
+ @apply text-xs bg-red-500 text-white transition-colors duration-200 font-medium px-3 py-1.5 rounded-lg shadow-sm;
332
326
  }
333
327
 
334
328
  .clear-button:hover {
335
- @apply bg-red-400;
329
+ @apply bg-red-400;
336
330
  }
337
331
 
338
332
  /* Transition animations */
339
333
  .dropdown-fade-enter-active {
340
- @apply transition-all duration-300;
334
+ @apply transition-all duration-300;
341
335
  }
342
336
 
343
337
  .dropdown-fade-leave-active {
344
- @apply transition-all duration-200;
338
+ @apply transition-all duration-200;
345
339
  }
346
340
 
347
341
  .dropdown-fade-enter-from {
348
- @apply opacity-0;
349
- transform: translateY(-8px) scale(0.95);
342
+ @apply opacity-0;
343
+ transform: translateY(-8px) scale(0.95);
350
344
  }
351
345
 
352
346
  .dropdown-fade-leave-to {
353
- @apply opacity-0;
354
- transform: translateY(-4px) scale(0.98);
347
+ @apply opacity-0;
348
+ transform: translateY(-4px) scale(0.98);
355
349
  }
356
350
 
357
351
  .dropdown-fade-enter-to,
358
352
  .dropdown-fade-leave-from {
359
- @apply opacity-100;
360
- transform: translateY(0) scale(1);
353
+ @apply opacity-100;
354
+ transform: translateY(0) scale(1);
361
355
  }
362
356
  </style>
@@ -1,3 +1,5 @@
1
+ const queueStats = false;
2
+
1
3
  export default {
2
4
  Profile: {
3
5
  name: "Admin",
@@ -17,7 +19,7 @@ export default {
17
19
  email: "grantelam@hotmail.com",
18
20
  password: "Dkstrhf4srth56Gksj",
19
21
  status: "Event already happened long long long long long long longlong long long long",
20
- inQueue: true,
22
+ inQueue: queueStats,
21
23
  statusColor: "red",
22
24
  manual: false,
23
25
  incapsulaBypass: false,
@@ -324,7 +326,7 @@ export default {
324
326
  active: true,
325
327
  email: "taylortaxjbr26248@gmail.com",
326
328
  password: "OwZz9PfKtWsrS7",
327
- status: "Waiting for Queue",
329
+ status: queueStats ? "Waiting for Queue" : "Waiting for Stock",
328
330
  statusColor: "white",
329
331
  manual: false,
330
332
  incapsulaBypass: false,
package/src/stores/ui.js CHANGED
@@ -233,10 +233,7 @@ export const useUIStore = defineStore("ui", () => {
233
233
  let relevantTasks = Object.values(tasks.value).filter((t) => t.siteId === currentCountry.value.siteId);
234
234
  if (currentEvent.value) relevantTasks = relevantTasks.filter((t) => t.eventId === currentEvent.value);
235
235
 
236
- queueStats.value.total = 0;
237
236
  queueStats.value.queued = relevantTasks.filter((t) => t.inQueue).length || 0;
238
- // if (DEBUG) queueStats.value.queued += 50000;
239
- queueStats.value.show = queueStats.value.total > 0;
240
237
  const sleepingStatuses = ["sleeping in queue", "waiting for drop", "waiting for carting", "waiting for queue"];
241
238
  queueStats.value.sleeping = relevantTasks.filter((t) =>
242
239
  sleepingStatuses.includes(t.status.toLowerCase())
@@ -246,6 +243,10 @@ export const useUIStore = defineStore("ui", () => {
246
243
  .filter((e) => !isNaN(e))
247
244
  .filter(Boolean);
248
245
  queueStats.value.nextQueuePasses = positions.sort((a, b) => a - b);
246
+
247
+ // Only show stats if there are any queued tasks, sleeping tasks, or queue positions
248
+ queueStats.value.show =
249
+ queueStats.value.queued > 0 || queueStats.value.sleeping > 0 || queueStats.value.nextQueuePasses.length > 0;
249
250
  };
250
251
 
251
252
  const toggleModal = (name, clearValue = false) => {