@necrolab/dashboard 0.5.14 → 0.5.16
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/backend/api.js +2 -3
- package/eslint.config.js +46 -0
- package/index.html +2 -1
- package/package.json +5 -2
- package/src/App.vue +140 -170
- package/src/assets/css/base/mixins.scss +72 -0
- package/src/assets/css/base/reset.scss +0 -2
- package/src/assets/css/base/scroll.scss +43 -36
- package/src/assets/css/base/typography.scss +9 -10
- package/src/assets/css/base/variables.scss +43 -0
- package/src/assets/css/components/accessibility.scss +37 -0
- package/src/assets/css/components/buttons.scss +58 -15
- package/src/assets/css/components/forms.scss +31 -32
- package/src/assets/css/components/headers.scss +119 -0
- package/src/assets/css/components/modals.scss +2 -2
- package/src/assets/css/components/search-groups.scss +28 -19
- package/src/assets/css/components/tables.scss +5 -7
- package/src/assets/css/components/toasts.scss +7 -7
- package/src/assets/css/components/utilities.scss +220 -0
- package/src/assets/css/main.scss +72 -75
- package/src/components/Auth/LoginForm.vue +5 -84
- package/src/components/Editors/Account/Account.vue +8 -10
- package/src/components/Editors/Account/AccountCreator.vue +28 -59
- package/src/components/Editors/Account/AccountView.vue +38 -86
- package/src/components/Editors/Account/CreateAccount.vue +8 -50
- package/src/components/Editors/Profile/CreateProfile.vue +74 -131
- package/src/components/Editors/Profile/Profile.vue +15 -17
- package/src/components/Editors/Profile/ProfileCountryChooser.vue +16 -60
- package/src/components/Editors/Profile/ProfileView.vue +46 -96
- package/src/components/Editors/TagLabel.vue +16 -55
- package/src/components/Editors/TagToggle.vue +20 -8
- package/src/components/Filter/Filter.vue +62 -75
- package/src/components/Filter/FilterPreview.vue +161 -135
- package/src/components/Filter/PriceSortToggle.vue +36 -43
- package/src/components/Table/Header.vue +1 -1
- package/src/components/Table/Table.vue +61 -12
- package/src/components/Tasks/CheckStock.vue +7 -16
- package/src/components/Tasks/Controls/DesktopControls.vue +15 -60
- package/src/components/Tasks/Controls/MobileControls.vue +5 -20
- package/src/components/Tasks/CreateTaskAXS.vue +20 -118
- package/src/components/Tasks/CreateTaskTM.vue +33 -189
- package/src/components/Tasks/EventDetailRow.vue +21 -0
- package/src/components/Tasks/MassEdit.vue +6 -16
- package/src/components/Tasks/QuickSettings.vue +140 -216
- package/src/components/Tasks/ScrapeVenue.vue +4 -13
- package/src/components/Tasks/Stats.vue +19 -38
- package/src/components/Tasks/Task.vue +65 -268
- package/src/components/Tasks/TaskLabel.vue +9 -3
- package/src/components/Tasks/TaskView.vue +43 -63
- package/src/components/Tasks/Utilities.vue +10 -42
- package/src/components/Tasks/ViewTask.vue +23 -107
- package/src/components/icons/Close.vue +2 -8
- package/src/components/icons/Gear.vue +8 -8
- package/src/components/icons/Hash.vue +5 -0
- package/src/components/icons/Key.vue +2 -8
- package/src/components/icons/Pencil.vue +2 -8
- package/src/components/icons/Profile.vue +2 -8
- package/src/components/icons/Sell.vue +2 -8
- package/src/components/icons/Spinner.vue +4 -7
- package/src/components/icons/SquareCheck.vue +2 -8
- package/src/components/icons/SquareUncheck.vue +2 -8
- package/src/components/icons/Wildcard.vue +2 -8
- package/src/components/icons/index.js +3 -1
- package/src/components/ui/ActionButtonGroup.vue +113 -52
- package/src/components/ui/BalanceIndicator.vue +60 -0
- package/src/components/ui/EmptyState.vue +24 -0
- package/src/components/ui/EnableDisableToggle.vue +23 -0
- package/src/components/ui/FormField.vue +48 -48
- package/src/components/ui/IconLabel.vue +23 -0
- package/src/components/ui/InfoRow.vue +21 -54
- package/src/components/ui/Modal.vue +78 -37
- package/src/components/ui/Navbar.vue +60 -41
- package/src/components/ui/ReadonlyFieldsSection.vue +31 -0
- package/src/components/ui/ReconnectIndicator.vue +111 -124
- package/src/components/ui/SectionCard.vue +6 -14
- package/src/components/ui/Splash.vue +2 -10
- package/src/components/ui/StatusBadge.vue +26 -28
- package/src/components/ui/TaskToggle.vue +54 -0
- package/src/components/ui/controls/CountryChooser.vue +27 -64
- package/src/components/ui/controls/EyeToggle.vue +1 -1
- package/src/components/ui/controls/atomic/Checkbox.vue +40 -121
- package/src/components/ui/controls/atomic/Dropdown.vue +102 -95
- package/src/components/ui/controls/atomic/MultiDropdown.vue +72 -94
- package/src/components/ui/controls/atomic/Switch.vue +21 -84
- package/src/composables/useColorMapping.js +15 -0
- package/src/composables/useCopyToClipboard.js +1 -1
- package/src/composables/useDateFormatting.js +21 -0
- package/src/composables/useDeviceDetection.js +14 -0
- package/src/composables/useDropdownPosition.js +5 -6
- package/src/composables/useDynamicTableHeight.js +31 -0
- package/src/composables/useRowSelection.js +0 -3
- package/src/composables/useTicketPricing.js +16 -0
- package/src/composables/useWindowDimensions.js +21 -0
- package/src/libs/Filter.js +14 -20
- package/src/libs/panzoom.js +1 -5
- package/src/libs/utils/array.js +60 -0
- package/src/{stores/utils.js → libs/utils/dataGeneration.js} +2 -250
- package/src/libs/utils/eventUrl.js +40 -0
- package/src/libs/utils/string.js +28 -0
- package/src/libs/utils/time.js +20 -0
- package/src/libs/utils/validation.js +88 -0
- package/src/main.js +0 -2
- package/src/stores/connection.js +1 -4
- package/src/stores/logger.js +6 -12
- package/src/stores/sampleData.js +1 -2
- package/src/stores/ui.js +59 -36
- package/src/views/Accounts.vue +17 -31
- package/src/views/Console.vue +76 -176
- package/src/views/Editor.vue +217 -383
- package/src/views/FilterBuilder.vue +190 -373
- package/src/views/Login.vue +3 -28
- package/src/views/Profiles.vue +12 -22
- package/src/views/Tasks.vue +51 -38
- package/tailwind.config.js +82 -71
- package/workbox-config.cjs +47 -5
- package/docs/plans/2026-02-08-tailwind-consolidation.md +0 -2416
- package/exit +0 -209
- package/run +0 -177
- package/switch-branch.sh +0 -41
- /package/public/{reconnect-logo.png → img/reconnect-logo.png} +0 -0
|
@@ -18,8 +18,7 @@
|
|
|
18
18
|
class="dropdown-menu-portal multi scrollable"
|
|
19
19
|
:style="menuStyle"
|
|
20
20
|
@click.stop
|
|
21
|
-
@wheel.stop
|
|
22
|
-
@touchmove.stop>
|
|
21
|
+
@wheel.stop>
|
|
23
22
|
<div class="option-list scrollable">
|
|
24
23
|
<button
|
|
25
24
|
v-for="(option, i) in props.options"
|
|
@@ -72,13 +71,34 @@ import { useClickOutside } from "@/composables/useClickOutside";
|
|
|
72
71
|
const ui = useUIStore();
|
|
73
72
|
|
|
74
73
|
const props = defineProps({
|
|
75
|
-
onSelect: {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
74
|
+
onSelect: {
|
|
75
|
+
type: Function,
|
|
76
|
+
default: null
|
|
77
|
+
},
|
|
78
|
+
default: {
|
|
79
|
+
type: String,
|
|
80
|
+
default: 'Select options...'
|
|
81
|
+
},
|
|
82
|
+
options: {
|
|
83
|
+
type: Array,
|
|
84
|
+
required: true
|
|
85
|
+
},
|
|
86
|
+
rightAmount: {
|
|
87
|
+
type: String,
|
|
88
|
+
default: ''
|
|
89
|
+
},
|
|
90
|
+
topPadding: {
|
|
91
|
+
type: String,
|
|
92
|
+
default: ''
|
|
93
|
+
},
|
|
94
|
+
capitalize: {
|
|
95
|
+
type: Boolean,
|
|
96
|
+
default: false
|
|
97
|
+
},
|
|
98
|
+
includeAdjacentButtons: {
|
|
99
|
+
type: Boolean,
|
|
100
|
+
default: false
|
|
101
|
+
}
|
|
82
102
|
});
|
|
83
103
|
|
|
84
104
|
const selectedOptions = ref([]);
|
|
@@ -192,24 +212,22 @@ if (props.default && !selectedOptions.value.includes(props.default)) {
|
|
|
192
212
|
|
|
193
213
|
<style scoped>
|
|
194
214
|
.dropdown {
|
|
195
|
-
@apply relative w-full h-10 text-white ml-auto rounded-lg ring-0;
|
|
196
|
-
|
|
197
|
-
border: 1px solid oklch(0.26 0 0);
|
|
215
|
+
@apply relative w-full h-10 text-white ml-auto rounded-lg ring-0 border-2;
|
|
216
|
+
@apply bg-bg-input border-border;
|
|
198
217
|
padding: 0.75rem;
|
|
199
218
|
}
|
|
200
219
|
|
|
201
220
|
.dropdown:hover {
|
|
202
|
-
border-
|
|
221
|
+
@apply border-dark-650;
|
|
203
222
|
}
|
|
204
223
|
|
|
205
224
|
.dropdown:focus-within,
|
|
206
225
|
.dropdown.opened {
|
|
207
|
-
border-
|
|
208
|
-
outline:
|
|
209
|
-
outline-offset: 0;
|
|
226
|
+
@apply border-primary outline outline-1 outline-primary;
|
|
227
|
+
outline-offset: 0 !important;
|
|
210
228
|
}
|
|
211
229
|
|
|
212
|
-
@
|
|
230
|
+
@screen modal {
|
|
213
231
|
.dropdown {
|
|
214
232
|
@apply h-10;
|
|
215
233
|
padding: 0.625rem;
|
|
@@ -225,40 +243,30 @@ if (props.default && !selectedOptions.value.includes(props.default)) {
|
|
|
225
243
|
}
|
|
226
244
|
|
|
227
245
|
.dropdown-counter {
|
|
228
|
-
@apply flex items-center gap-2 absolute;
|
|
229
|
-
right: 0.75rem;
|
|
230
|
-
top: 50%;
|
|
231
|
-
transform: translateY(-50%);
|
|
246
|
+
@apply flex items-center gap-2 absolute right-2;
|
|
232
247
|
}
|
|
233
248
|
|
|
234
249
|
.counter-badge {
|
|
235
|
-
@apply text-white text-xs font-semibold px-1.5 py-0.5 rounded-full text-center min-w-
|
|
236
|
-
|
|
250
|
+
@apply text-white text-xs font-semibold px-1.5 py-0.5 rounded-full text-center min-w-4.5 shadow-sm;
|
|
251
|
+
@apply bg-primary;
|
|
237
252
|
}
|
|
238
253
|
|
|
239
254
|
.dropdown-arrow {
|
|
240
255
|
@apply min-w-4 min-h-4 transition-all duration-300;
|
|
241
|
-
display: flex;
|
|
242
|
-
align-items: center;
|
|
243
|
-
justify-content: center;
|
|
244
256
|
}
|
|
245
257
|
|
|
246
258
|
.dropdown-menu-portal {
|
|
247
|
-
@apply rounded-xl shadow-
|
|
248
|
-
background: linear-gradient(135deg, oklch(0.18 0 0) 0%, oklch(0.20 0 0) 100%);
|
|
249
|
-
border: 1px solid oklch(0.26 0 0);
|
|
250
|
-
backdrop-filter: blur(12px);
|
|
251
|
-
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.4), 0 10px 10px -5px rgba(0, 0, 0, 0.2),
|
|
252
|
-
0 0 0 1px rgba(255, 255, 255, 0.05);
|
|
253
|
-
overflow-x: auto !important;
|
|
254
|
-
overflow-y: auto !important;
|
|
255
|
-
overscroll-behavior: contain !important;
|
|
256
|
-
touch-action: pan-x pan-y !important;
|
|
257
|
-
-webkit-overflow-scrolling: touch !important;
|
|
258
|
-
scrollbar-width: thin;
|
|
259
|
-
scrollbar-color: oklch(0.35 0 0) oklch(0.19 0 0);
|
|
259
|
+
@apply rounded-xl z-modal border-2 border-dark-650 bg-dark-400 shadow-dropdown;
|
|
260
260
|
min-width: 200px;
|
|
261
|
-
max-width: 90vw;
|
|
261
|
+
max-width: min(90vw, 600px);
|
|
262
|
+
overflow-x: auto;
|
|
263
|
+
overflow-y: auto;
|
|
264
|
+
overscroll-behavior: contain;
|
|
265
|
+
touch-action: pan-x pan-y;
|
|
266
|
+
-webkit-overflow-scrolling: touch;
|
|
267
|
+
scrollbar-width: thin;
|
|
268
|
+
scrollbar-color: theme('colors.dark.750') theme('colors.dark.350');
|
|
269
|
+
scrollbar-gutter: stable;
|
|
262
270
|
}
|
|
263
271
|
|
|
264
272
|
.dropdown-menu-portal::-webkit-scrollbar {
|
|
@@ -267,31 +275,37 @@ if (props.default && !selectedOptions.value.includes(props.default)) {
|
|
|
267
275
|
}
|
|
268
276
|
|
|
269
277
|
.dropdown-menu-portal::-webkit-scrollbar-track {
|
|
270
|
-
|
|
278
|
+
@apply bg-transparent;
|
|
279
|
+
margin: 12px 0;
|
|
271
280
|
}
|
|
272
281
|
|
|
273
282
|
.dropdown-menu-portal::-webkit-scrollbar-thumb {
|
|
274
|
-
|
|
275
|
-
border
|
|
283
|
+
@apply bg-dark-750 rounded-full;
|
|
284
|
+
border: 2px solid transparent;
|
|
285
|
+
background-clip: padding-box;
|
|
276
286
|
}
|
|
277
287
|
|
|
278
288
|
.dropdown-menu-portal::-webkit-scrollbar-thumb:hover {
|
|
279
|
-
|
|
289
|
+
@apply bg-dark-800;
|
|
280
290
|
}
|
|
281
291
|
|
|
282
292
|
.dropdown-menu-portal.multi .option-list {
|
|
283
293
|
@apply max-h-48 overflow-y-auto;
|
|
284
|
-
overscroll-behavior: contain
|
|
285
|
-
|
|
286
|
-
-
|
|
294
|
+
overscroll-behavior: contain;
|
|
295
|
+
-webkit-overflow-scrolling: touch;
|
|
296
|
+
scrollbar-width: none;
|
|
297
|
+
-ms-overflow-style: none;
|
|
298
|
+
touch-action: pan-y;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.dropdown-menu-portal.multi .option-list::-webkit-scrollbar {
|
|
302
|
+
display: none;
|
|
287
303
|
}
|
|
288
304
|
|
|
289
305
|
.dropdown-item {
|
|
290
306
|
@apply cursor-pointer text-left w-full text-white transition-all duration-200 flex justify-between items-center;
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
font-weight: 500;
|
|
294
|
-
border-bottom: 1px solid rgba(61, 62, 68, 0.3);
|
|
307
|
+
@apply px-4 py-3 text-sm font-medium;
|
|
308
|
+
border-bottom: 1px solid theme('colors.dark.500 / 0.3');
|
|
295
309
|
}
|
|
296
310
|
|
|
297
311
|
.dropdown-item:last-child {
|
|
@@ -299,13 +313,11 @@ if (props.default && !selectedOptions.value.includes(props.default)) {
|
|
|
299
313
|
}
|
|
300
314
|
|
|
301
315
|
.dropdown-item:hover {
|
|
302
|
-
@apply bg-dark-
|
|
303
|
-
color: oklch(1 0 0);
|
|
316
|
+
@apply bg-dark-550 text-white;
|
|
304
317
|
}
|
|
305
318
|
|
|
306
319
|
.dropdown-item:active {
|
|
307
|
-
@apply bg-dark-650;
|
|
308
|
-
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4);
|
|
320
|
+
@apply bg-dark-650 shadow-input-inset;
|
|
309
321
|
}
|
|
310
322
|
|
|
311
323
|
.dropdown-item:first-child {
|
|
@@ -323,13 +335,12 @@ if (props.default && !selectedOptions.value.includes(props.default)) {
|
|
|
323
335
|
|
|
324
336
|
/* Checkmark styling */
|
|
325
337
|
.dropdown-item svg {
|
|
326
|
-
@apply w-4 h-4;
|
|
327
|
-
color: oklch(0.72 0.15 145);
|
|
338
|
+
@apply w-4 h-4 text-primary;
|
|
328
339
|
}
|
|
329
340
|
|
|
330
341
|
.selected-summary {
|
|
331
342
|
@apply border-t bg-dark-550 w-full px-4 py-3;
|
|
332
|
-
border-top: 1px solid
|
|
343
|
+
border-top: 1px solid theme('colors.dark.500 / 0.5');
|
|
333
344
|
}
|
|
334
345
|
|
|
335
346
|
.selected-count {
|
|
@@ -338,7 +349,7 @@ if (props.default && !selectedOptions.value.includes(props.default)) {
|
|
|
338
349
|
|
|
339
350
|
.count-badge {
|
|
340
351
|
@apply text-white text-xs font-semibold px-2 py-1 rounded-full shadow-sm;
|
|
341
|
-
|
|
352
|
+
@apply bg-primary;
|
|
342
353
|
}
|
|
343
354
|
|
|
344
355
|
.count-label {
|
|
@@ -347,53 +358,20 @@ if (props.default && !selectedOptions.value.includes(props.default)) {
|
|
|
347
358
|
|
|
348
359
|
.clear-button {
|
|
349
360
|
@apply text-xs text-white transition-all duration-200 font-medium px-2 py-1.5 rounded-lg shadow-sm flex items-center justify-center;
|
|
350
|
-
|
|
361
|
+
@apply bg-red-400 hover:bg-red-300;
|
|
351
362
|
min-width: 32px;
|
|
352
363
|
}
|
|
353
364
|
|
|
354
|
-
.clear-button:hover {
|
|
355
|
-
background: oklch(0.60 0.22 25);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
365
|
.done-button {
|
|
359
366
|
@apply text-xs text-white transition-all duration-200 font-medium px-2 py-1.5 rounded-lg shadow-sm flex items-center justify-center;
|
|
360
|
-
|
|
367
|
+
@apply bg-primary hover:bg-green-400;
|
|
361
368
|
min-width: 32px;
|
|
362
369
|
}
|
|
363
370
|
|
|
364
|
-
.done-button:hover {
|
|
365
|
-
background: oklch(0.68 0.15 145);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
371
|
@media (min-width: 640px) {
|
|
369
372
|
.clear-button,
|
|
370
373
|
.done-button {
|
|
371
374
|
padding: 0.375rem 0.75rem;
|
|
372
375
|
}
|
|
373
376
|
}
|
|
374
|
-
|
|
375
|
-
/* Transition animations */
|
|
376
|
-
.dropdown-fade-enter-active {
|
|
377
|
-
@apply transition-all duration-300;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
.dropdown-fade-leave-active {
|
|
381
|
-
@apply transition-all duration-200;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
.dropdown-fade-enter-from {
|
|
385
|
-
@apply opacity-0;
|
|
386
|
-
transform: translateY(-8px) scale(0.95);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
.dropdown-fade-leave-to {
|
|
390
|
-
@apply opacity-0;
|
|
391
|
-
transform: translateY(-4px) scale(0.98);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
.dropdown-fade-enter-to,
|
|
395
|
-
.dropdown-fade-leave-from {
|
|
396
|
-
@apply opacity-100;
|
|
397
|
-
transform: translateY(0) scale(1);
|
|
398
|
-
}
|
|
399
377
|
</style>
|
|
@@ -1,96 +1,33 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<label
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
<label
|
|
3
|
+
class="relative inline-block w-[51px] h-[31px] max-modal:w-11 max-modal:min-w-11 max-modal:h-6.5"
|
|
4
|
+
:class="disabled ? 'pointer-events-none' : ''"
|
|
5
|
+
>
|
|
6
|
+
<input
|
|
7
|
+
type="checkbox"
|
|
8
|
+
v-model="value"
|
|
9
|
+
:disabled="disabled"
|
|
10
|
+
class="opacity-0 w-0 h-0 peer"
|
|
11
|
+
role="switch"
|
|
12
|
+
:aria-checked="value"
|
|
13
|
+
/>
|
|
14
|
+
<span
|
|
15
|
+
class="absolute inset-0 cursor-pointer border-2 rounded-[31px] transition-all duration-300 ease-out
|
|
16
|
+
before:absolute before:content-[''] before:size-[23px] before:left-0.5 before:bottom-0.5 before:rounded-full before:shadow-switch before:transition-all before:duration-300 before:ease-out
|
|
17
|
+
bg-dark-550 border-dark-550 before:bg-dark-200
|
|
18
|
+
peer-checked:bg-accent-green peer-checked:border-accent-green peer-checked:before:bg-white peer-checked:before:translate-x-5
|
|
19
|
+
peer-disabled:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:before:opacity-70
|
|
20
|
+
max-modal:before:size-[22px] max-modal:peer-checked:before:translate-x-[18px]"
|
|
21
|
+
></span>
|
|
5
22
|
</label>
|
|
6
23
|
</template>
|
|
7
24
|
|
|
8
25
|
<script setup>
|
|
9
26
|
const value = defineModel();
|
|
10
|
-
|
|
27
|
+
defineProps({
|
|
11
28
|
disabled: {
|
|
12
29
|
type: Boolean,
|
|
13
30
|
default: false
|
|
14
31
|
}
|
|
15
32
|
});
|
|
16
33
|
</script>
|
|
17
|
-
|
|
18
|
-
<style lang="scss" scoped>
|
|
19
|
-
/* iOS-style switch */
|
|
20
|
-
.switch {
|
|
21
|
-
@apply relative inline-block;
|
|
22
|
-
width: 51px;
|
|
23
|
-
height: 31px;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/* Hide default HTML checkbox */
|
|
27
|
-
.switch input {
|
|
28
|
-
@apply opacity-0 w-0 h-0;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/* The slider */
|
|
32
|
-
.slider {
|
|
33
|
-
@apply absolute cursor-pointer inset-0;
|
|
34
|
-
@apply border-2;
|
|
35
|
-
background-color: oklch(0.26 0 0);
|
|
36
|
-
border-color: oklch(0.35 0 0);
|
|
37
|
-
transition: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.slider:before {
|
|
41
|
-
@apply absolute;
|
|
42
|
-
@apply w-[23px] h-[23px] left-0.5 bottom-0.5;
|
|
43
|
-
content: "";
|
|
44
|
-
background-color: oklch(0.50 0 0);
|
|
45
|
-
transition: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
46
|
-
box-shadow: 0 1px 3px oklch(0 0 0 / 0.3);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
input:checked + .slider {
|
|
50
|
-
background-color: oklch(0.72 0.15 145);
|
|
51
|
-
border-color: oklch(0.72 0.15 145);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
input:checked + .slider:before {
|
|
55
|
-
background-color: oklch(1 0 0);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
input:checked + .slider:before {
|
|
59
|
-
transform: translateX(20px);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
.switch.disabled {
|
|
63
|
-
@apply pointer-events-none;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
input:disabled + .slider {
|
|
67
|
-
@apply opacity-50 cursor-not-allowed;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
input:disabled + .slider:before {
|
|
71
|
-
@apply opacity-70;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/* Rounded sliders */
|
|
75
|
-
.slider.round {
|
|
76
|
-
border-radius: 31px;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
.slider.round:before {
|
|
80
|
-
border-radius: 50%;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
@media (max-width: 810px) {
|
|
84
|
-
.switch {
|
|
85
|
-
@apply w-11 min-w-11 h-[26px];
|
|
86
|
-
|
|
87
|
-
.slider:before {
|
|
88
|
-
@apply w-[22px] h-[22px] left-0.5 bottom-0.5;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
input:checked + .slider:before {
|
|
93
|
-
@apply translate-x-[18px];
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
</style>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function useColorMapping() {
|
|
2
|
+
const colorToClass = (color) => {
|
|
3
|
+
const colorMap = {
|
|
4
|
+
green: "bg-green-400",
|
|
5
|
+
red: "bg-red-400",
|
|
6
|
+
yellow: "bg-yellow-400",
|
|
7
|
+
blue: "bg-blue-400",
|
|
8
|
+
error: "bg-red-400",
|
|
9
|
+
success: "bg-green-400"
|
|
10
|
+
};
|
|
11
|
+
return colorMap[color?.toLowerCase()] || "bg-white";
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
return { colorToClass };
|
|
15
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function useDateFormatting() {
|
|
2
|
+
const formatEventDate = (dateString) => {
|
|
3
|
+
if (!dateString) return '';
|
|
4
|
+
try {
|
|
5
|
+
const date = new Date(dateString);
|
|
6
|
+
const options = {
|
|
7
|
+
month: 'short',
|
|
8
|
+
day: 'numeric',
|
|
9
|
+
year: 'numeric',
|
|
10
|
+
hour: 'numeric',
|
|
11
|
+
minute: '2-digit',
|
|
12
|
+
hour12: true
|
|
13
|
+
};
|
|
14
|
+
return date.toLocaleString('en-US', options).replace(',', '');
|
|
15
|
+
} catch {
|
|
16
|
+
return dateString;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
return { formatEventDate };
|
|
21
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function useDeviceDetection() {
|
|
2
|
+
const isIOS = () => {
|
|
3
|
+
if (/iPad|iPhone|iPod/.test(navigator.platform)) {
|
|
4
|
+
return true;
|
|
5
|
+
}
|
|
6
|
+
return navigator.maxTouchPoints && navigator.maxTouchPoints > 2 && /MacIntel/.test(navigator.platform);
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const isIpadOS = () => {
|
|
10
|
+
return navigator.maxTouchPoints && navigator.maxTouchPoints > 2 && /MacIntel/.test(navigator.platform);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
return { isIOS, isIpadOS };
|
|
14
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ref, nextTick, onMounted, onUnmounted } from "vue";
|
|
2
|
+
import { DEBUG } from "@/utils/debug";
|
|
2
3
|
|
|
3
4
|
export function useDropdownPosition(dropdownRef, options = {}) {
|
|
4
5
|
const menuStyle = ref({});
|
|
@@ -6,10 +7,9 @@ export function useDropdownPosition(dropdownRef, options = {}) {
|
|
|
6
7
|
offset = { x: -1, y: 4 },
|
|
7
8
|
zIndex = 50000,
|
|
8
9
|
minWidth = null,
|
|
9
|
-
maxHeight =
|
|
10
|
+
maxHeight = window.innerHeight * 0.8, // Use 80% of viewport height
|
|
10
11
|
estimateHeight = null,
|
|
11
|
-
includeAdjacentButtons = false
|
|
12
|
-
containerSelector = null
|
|
12
|
+
includeAdjacentButtons = false
|
|
13
13
|
} = options;
|
|
14
14
|
|
|
15
15
|
const calculateMenuPosition = () => {
|
|
@@ -24,7 +24,7 @@ export function useDropdownPosition(dropdownRef, options = {}) {
|
|
|
24
24
|
const menuHeight = estimateHeight ? estimateHeight() : maxHeight;
|
|
25
25
|
|
|
26
26
|
// Calculate width including adjacent buttons if specified
|
|
27
|
-
let menuWidth = minWidth || rect.width
|
|
27
|
+
let menuWidth = minWidth || rect.width;
|
|
28
28
|
|
|
29
29
|
if (includeAdjacentButtons) {
|
|
30
30
|
// Look for parent container with buttons
|
|
@@ -109,8 +109,7 @@ export function useDropdownPosition(dropdownRef, options = {}) {
|
|
|
109
109
|
maxHeight: `${maxHeight}px`
|
|
110
110
|
};
|
|
111
111
|
} catch (error) {
|
|
112
|
-
console.warn("Error calculating dropdown position:", error);
|
|
113
|
-
// Fallback to basic positioning
|
|
112
|
+
if (DEBUG) console.warn("Error calculating dropdown position:", error);
|
|
114
113
|
menuStyle.value = {
|
|
115
114
|
position: "fixed",
|
|
116
115
|
top: "0px",
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { computed } from "vue";
|
|
2
|
+
import { useWindowDimensions } from "./useWindowDimensions";
|
|
3
|
+
|
|
4
|
+
export function useDynamicTableHeight(options = {}) {
|
|
5
|
+
const { windowHeight, windowWidth } = useWindowDimensions();
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
topReservedSpace = 243,
|
|
9
|
+
bottomBuffer = 16,
|
|
10
|
+
rowHeight = 64,
|
|
11
|
+
minRowsToShow = 2
|
|
12
|
+
} = options;
|
|
13
|
+
|
|
14
|
+
const dynamicTableHeight = computed(() => {
|
|
15
|
+
// Detect PWA mode and small screens
|
|
16
|
+
const isPWA = window.matchMedia('(display-mode: standalone)').matches;
|
|
17
|
+
const isMobile = windowWidth.value <= 768;
|
|
18
|
+
|
|
19
|
+
// Extra buffer for iPhone PWA to prevent overflow
|
|
20
|
+
const extraBuffer = isPWA && isMobile ? 60 : 0;
|
|
21
|
+
|
|
22
|
+
const availableHeight = windowHeight.value - topReservedSpace - bottomBuffer - extraBuffer;
|
|
23
|
+
const minHeight = minRowsToShow * rowHeight;
|
|
24
|
+
const maxCompleteRows = Math.floor(Math.max(availableHeight, minHeight) / rowHeight);
|
|
25
|
+
const exactHeight = maxCompleteRows * rowHeight;
|
|
26
|
+
|
|
27
|
+
return exactHeight + "px";
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return { dynamicTableHeight };
|
|
31
|
+
}
|
|
@@ -12,7 +12,6 @@ export function useRowSelection(toggleCallback) {
|
|
|
12
12
|
const DOUBLE_TAP_DELAY = 300
|
|
13
13
|
|
|
14
14
|
const handleDoubleClick = (event) => {
|
|
15
|
-
// Don't trigger on button or checkbox clicks
|
|
16
15
|
if (event.target.closest('button') || event.target.closest('.checkbox')) {
|
|
17
16
|
return
|
|
18
17
|
}
|
|
@@ -24,7 +23,6 @@ export function useRowSelection(toggleCallback) {
|
|
|
24
23
|
const tapGap = currentTime - lastTapTime
|
|
25
24
|
|
|
26
25
|
if (tapGap < DOUBLE_TAP_DELAY && tapGap > 0) {
|
|
27
|
-
// Don't trigger on button or checkbox taps
|
|
28
26
|
if (!event.target.closest('button') && !event.target.closest('.checkbox')) {
|
|
29
27
|
event.preventDefault()
|
|
30
28
|
toggleCallback()
|
|
@@ -34,7 +32,6 @@ export function useRowSelection(toggleCallback) {
|
|
|
34
32
|
}
|
|
35
33
|
|
|
36
34
|
const handleTouchEnd = (event) => {
|
|
37
|
-
// Prevent default touch behavior on buttons/checkboxes
|
|
38
35
|
if (event.target.closest('button') || event.target.closest('.checkbox')) {
|
|
39
36
|
return
|
|
40
37
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function useTicketPricing() {
|
|
2
|
+
const isTotalPrice = (line, index, lines) => {
|
|
3
|
+
const trimmed = line.trim();
|
|
4
|
+
if (!trimmed) return false;
|
|
5
|
+
|
|
6
|
+
const nonEmptyLines = lines.filter(l => l.trim());
|
|
7
|
+
const isLastLine = index === lines.lastIndexOf(nonEmptyLines[nonEmptyLines.length - 1]);
|
|
8
|
+
|
|
9
|
+
if (!isLastLine) return false;
|
|
10
|
+
|
|
11
|
+
const totalPricePattern = /^([$€£¥₹₽¢]|[A-Z]{3})\s*[\d,]+\.?\d*$/;
|
|
12
|
+
return totalPricePattern.test(trimmed) && !trimmed.includes('(') && !trimmed.includes(')');
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
return { isTotalPrice };
|
|
16
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ref, onMounted, onUnmounted } from "vue";
|
|
2
|
+
|
|
3
|
+
export function useWindowDimensions() {
|
|
4
|
+
const windowHeight = ref(window.innerHeight);
|
|
5
|
+
const windowWidth = ref(window.innerWidth);
|
|
6
|
+
|
|
7
|
+
const updateDimensions = () => {
|
|
8
|
+
windowHeight.value = window.innerHeight;
|
|
9
|
+
windowWidth.value = window.innerWidth;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
onMounted(() => {
|
|
13
|
+
window.addEventListener("resize", updateDimensions);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
onUnmounted(() => {
|
|
17
|
+
window.removeEventListener("resize", updateDimensions);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
return { windowHeight, windowWidth };
|
|
21
|
+
}
|
package/src/libs/Filter.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
const log = (...args) =>
|
|
1
|
+
import { DEBUG } from "@/utils/debug";
|
|
2
|
+
const log = (...args) => DEBUG && console.log("[filter]", ...args);
|
|
3
3
|
|
|
4
4
|
const colors = {
|
|
5
5
|
HIGHLIGHT: "#d3f8e2",
|
|
@@ -30,27 +30,23 @@ const isWheelchair = (p) => {
|
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
const sortAlphaNum = (a, b) => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
const reA = /[^a-zA-Z]/g;
|
|
34
|
+
const reN = /[^0-9]/g;
|
|
35
|
+
const AInt = parseInt(a, 10);
|
|
36
|
+
const BInt = parseInt(b, 10);
|
|
37
37
|
|
|
38
38
|
if (isNaN(AInt) && isNaN(BInt)) {
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
const aA = a.replace(reA, "");
|
|
40
|
+
const bA = b.replace(reA, "");
|
|
41
41
|
if (aA === bA) {
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
const aN = parseInt(a.replace(reN, ""), 10);
|
|
43
|
+
const bN = parseInt(b.replace(reN, ""), 10);
|
|
44
44
|
return aN === bN ? 0 : aN > bN ? 1 : -1;
|
|
45
45
|
} else {
|
|
46
46
|
return aA > bA ? 1 : -1;
|
|
47
47
|
}
|
|
48
|
-
} else if (isNaN(AInt)) {
|
|
49
|
-
//A is not an Int
|
|
50
|
-
return 1; //to make alphanumeric sort first return -1 here
|
|
51
48
|
} else if (isNaN(BInt)) {
|
|
52
|
-
|
|
53
|
-
return -1; //to make alphanumeric sort first return 1 here
|
|
49
|
+
return -1;
|
|
54
50
|
} else {
|
|
55
51
|
return AInt > BInt ? 1 : -1;
|
|
56
52
|
}
|
|
@@ -313,7 +309,7 @@ export default class FilterBuilder {
|
|
|
313
309
|
});
|
|
314
310
|
|
|
315
311
|
if (existingFilter) {
|
|
316
|
-
|
|
312
|
+
if (DEBUG) log("Filter already exists:", existingFilter);
|
|
317
313
|
return; // Don't add the filter if it already exists
|
|
318
314
|
}
|
|
319
315
|
|
|
@@ -385,7 +381,7 @@ export default class FilterBuilder {
|
|
|
385
381
|
} else {
|
|
386
382
|
color = this.expandedFilter === filter.id ? colors.SELECTED_EXPANDED : colors.SELECTED;
|
|
387
383
|
}
|
|
388
|
-
|
|
384
|
+
|
|
389
385
|
switch (type) {
|
|
390
386
|
case this.filterTypes.NORMAL:
|
|
391
387
|
// If it has no 'rows' property
|
|
@@ -416,7 +412,6 @@ export default class FilterBuilder {
|
|
|
416
412
|
break;
|
|
417
413
|
|
|
418
414
|
case this.filterTypes.CATCH_ALL_FLOOR:
|
|
419
|
-
// this.cssClasses += floors.map((f) => `path[name="${f}"] {fill: ${color} !important;}`).join("\n") + "\n";
|
|
420
415
|
break;
|
|
421
416
|
|
|
422
417
|
case this.filterTypes.INVALID:
|
|
@@ -432,7 +427,7 @@ export default class FilterBuilder {
|
|
|
432
427
|
const color = colors.UNSELECTABLE;
|
|
433
428
|
this.cssClasses += `.svg-wrapper path[section="${section}"][row="${row}"] {stroke: ${color} !important;}\n`;
|
|
434
429
|
});
|
|
435
|
-
|
|
430
|
+
|
|
436
431
|
log("Generated CSS:", this.cssClasses);
|
|
437
432
|
}
|
|
438
433
|
|
|
@@ -533,7 +528,6 @@ export default class FilterBuilder {
|
|
|
533
528
|
|
|
534
529
|
deleteFilterById(id) {
|
|
535
530
|
this.filters = this.filters.filter((f) => f.id !== id);
|
|
536
|
-
// this.updateHooks = this.updateHooks.filter((i) => id !== i);
|
|
537
531
|
if (this.expandedFilter === id) this.expandedFilter = "";
|
|
538
532
|
this.updateCss();
|
|
539
533
|
this.updateHooks.forEach((fn) => fn());
|