@necrolab/dashboard 0.5.20 → 0.5.22

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.20",
3
+ "version": "0.5.22",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": "rm -rf dist && vite build && npx workbox-cli generateSW workbox-config.cjs",
@@ -292,15 +292,7 @@ props.filterBuilder.onUpdate(() => {
292
292
 
293
293
  <style scoped>
294
294
  .filter-card {
295
- @apply bg-dark-500 border border-dark-625/30 relative mb-2 transition-all duration-200;
296
- }
297
-
298
- .filter-card:hover {
299
- transform: scale(1.03);
300
- }
301
-
302
- .filter-card:active {
303
- transform: scale(0.98);
295
+ @apply bg-dark-500 border border-dark-625/30 relative mb-2 transition-colors duration-200;
304
296
  }
305
297
 
306
298
  .filter-card:hover:not(.expanded-filter) {
@@ -353,17 +345,29 @@ props.filterBuilder.onUpdate(() => {
353
345
 
354
346
  .drag-btn {
355
347
  @apply text-light-500;
348
+ transition: all 0.2s ease;
356
349
 
357
350
  &:hover {
358
351
  @apply bg-dark-300 border-dark-550 text-yellow-400 shadow-md;
352
+ transform: scale(1.1);
353
+ }
354
+
355
+ &:active {
356
+ transform: scale(0.95);
359
357
  }
360
358
  }
361
359
 
362
360
  .delete-btn {
363
361
  @apply bg-error-500/30 border-error-400/50 text-error-300;
362
+ transition: all 0.2s ease;
364
363
 
365
364
  &:hover {
366
365
  @apply bg-error-400/80 border-error-500 text-white shadow-md;
366
+ transform: scale(1.1);
367
+ }
368
+
369
+ &:active {
370
+ transform: scale(0.95);
367
371
  }
368
372
  }
369
373
 
@@ -3,8 +3,8 @@ const log = (...args) => DEBUG && console.log("[filter]", ...args);
3
3
 
4
4
  const colors = {
5
5
  HIGHLIGHT: "#d3f8e2",
6
- SELECTED: "#a9def9",
7
- SELECTED_EXPANDED: "#6bb6ff",
6
+ SELECTED: "#7bc999",
7
+ SELECTED_EXPANDED: "#a0ddb5",
8
8
  EXCLUDED: "#f694c1",
9
9
  EXCLUDED_EXPANDED: "#f472b6",
10
10
  UNSELECTABLE: "#311432"
@@ -363,14 +363,16 @@ export default class FilterBuilder {
363
363
  this.filters.forEach((filter) => {
364
364
  if (!this.isForCurrentEvent(filter)) return;
365
365
 
366
- const tryFindRealName = Object.entries(gaSectionNameMapping).find(
367
- ([, v]) => v === filter.section && v.includes(" ")
366
+ // Try to find the short name (attribute name) from the real name (stored in filter)
367
+ const shortName = Object.entries(gaSectionNameMapping).find(
368
+ ([, v]) => v === filter.section
368
369
  )?.[0];
369
370
 
370
- if (tryFindRealName) {
371
- log(`Real name for ${filter.section}: ${tryFindRealName}`);
372
- } else {
373
- log(filter.section, "is already real name", gaSectionNameMapping);
371
+ // Check if this is a GA section by looking for paths with this name attribute
372
+ const isGA = shortName && document.querySelectorAll(`path[name="${shortName}"][generaladmission="true"]`)?.length > 0;
373
+
374
+ if (shortName && isGA) {
375
+ log(`GA section detected: ${filter.section} -> short name: ${shortName}`);
374
376
  }
375
377
 
376
378
  const type = this.getFilterType(filter);
@@ -386,11 +388,11 @@ export default class FilterBuilder {
386
388
  case this.filterTypes.NORMAL:
387
389
  // If it has no 'rows' property
388
390
  if (!Array.isArray(filter.rows)) {
389
- const isGA = document.querySelectorAll(`path[section='${filter.section}']`)?.length === 0;
390
-
391
- if (isGA && tryFindRealName) {
392
- this.cssClasses += `.svg-wrapper path[name="${tryFindRealName}"] {fill: ${color} !important;}\n`;
391
+ if (isGA && shortName) {
392
+ // GA sections use 'name' attribute and 'fill' for styling
393
+ this.cssClasses += `.svg-wrapper path[name="${shortName}"][generaladmission="true"] {fill: ${color} !important;}\n`;
393
394
  } else {
395
+ // Regular sections use 'section' attribute and 'stroke' for styling
394
396
  this.cssClasses += `.svg-wrapper path[section="${filter.section}"] {stroke: ${color} !important;}\n`;
395
397
  }
396
398
  } else {
@@ -1,18 +1,19 @@
1
1
  <template>
2
2
  <div class="flex w-full flex-col">
3
- <div class="page-header">
3
+ <div class="page-header flex-wrap gap-3">
4
4
  <div class="page-header-card flex-shrink-0">
5
5
  <FilterIcon />
6
6
  <h4>Filter creator</h4>
7
7
  </div>
8
- <div class="unified-search-group flex w-auto items-center">
8
+ <div class="unified-search-group flex w-full flex-1 items-center md:w-auto md:flex-initial md:min-w-64">
9
9
  <input
10
- class="h-10 w-32 flex-1 px-3 text-sm text-white placeholder-light-500 sm:w-48 md:w-64"
10
+ class="flex-1 h-10 px-3 text-sm text-white placeholder-light-500"
11
11
  placeholder="Event ID"
12
12
  v-model="eventId"
13
13
  aria-label="Event ID" />
14
14
  <button
15
- class="transition-standard hover:bg-dark-450 flex h-10 w-9 flex-shrink-0 items-center justify-center bg-dark-400 text-white"
15
+ class="hover:bg-dark-450 flex h-10 w-10 flex-shrink-0 items-center justify-center bg-dark-400 text-white"
16
+ style="transition: all 0.2s ease"
16
17
  @click="updateShownVenue"
17
18
  aria-label="Load venue">
18
19
  <ReloadIcon class="icon-md" />
@@ -21,26 +22,29 @@
21
22
  </div>
22
23
 
23
24
  <div
24
- class="mb-3 flex flex-1 flex-col overflow-hidden rounded border border-dark-650 bg-dark-400 p-3 shadow-sm md:mb-4">
25
- <div class="flex h-full w-full flex-col gap-3 lg:flex-row lg:gap-4">
25
+ class="mb-3 flex flex-1 flex-col overflow-hidden rounded border border-dark-650 bg-dark-400 p-2 shadow-sm md:p-3 md:mb-4"
26
+ style="max-height: calc(100vh - 200px);">
27
+ <div class="flex h-full w-full flex-col gap-2 md:gap-3 lg:flex-row lg:gap-4">
26
28
  <div
27
- class="min-h-75 lg:min-h-125 relative flex w-full min-w-0 flex-col overflow-hidden rounded-lg lg:w-3/5">
28
- <div v-if="svg" class="flex-gap-2 mb-2 items-center">
29
+ class="relative flex w-full min-w-0 flex-col overflow-hidden rounded-lg flex-1 lg:w-3/5 svg-container"
30
+ :class="{ 'has-svg': svg }">
31
+ <div v-if="svg" class="flex-gap-2 mb-2 flex-shrink-0 items-center">
29
32
  <button @click="handleZoom(true)" class="btn-icon-small" aria-label="Zoom in">+</button>
30
33
  <button @click="handleZoom(false)" class="btn-icon-small" aria-label="Zoom out">-</button>
31
34
  <button @click="handleZoom('r')" class="btn-icon-small" aria-label="Reset zoom">
32
35
  <ReloadIcon class="icon-md text-white" />
33
36
  </button>
34
37
  </div>
35
- <div class="selecto-wrapper flex-1 overflow-hidden">
38
+ <div class="selecto-wrapper flex-1 overflow-hidden" style="max-height: 100%;">
36
39
  <div
37
40
  v-if="svg"
38
- class="hidden-scrollbars min-h-87.5 relative h-full w-full overflow-auto rounded border border-dark-550 bg-dark-500 p-2 shadow">
41
+ class="hidden-scrollbars relative h-full w-full overflow-auto rounded border border-dark-550 bg-dark-500 p-1 md:p-2 shadow">
39
42
  <div class="svg-wrapper" id="svg-wrapper" v-html="svg"></div>
40
43
  </div>
41
44
  <div
42
45
  v-else
43
- class="min-h-87.5 relative flex h-full w-full items-center justify-center rounded border border-dark-550 bg-dark-500 p-2 shadow">
46
+ class="relative flex h-full w-full items-center justify-center rounded border border-dark-550 bg-dark-500 p-2 shadow"
47
+ style="min-height: 200px;">
44
48
  <div class="text-center">
45
49
  <FilterIcon class="empty-state-icon mx-auto" />
46
50
  <p class="text-sm text-light-400">No Map</p>
@@ -51,8 +55,8 @@
51
55
  </div>
52
56
  </div>
53
57
  </div>
54
- <div class="min-h-75 lg:min-h-125 flex w-full min-w-0 flex-col lg:w-2/5">
55
- <div class="mb-2 flex flex-shrink-0 flex-wrap items-center gap-2 text-white" v-if="hasLoaded">
58
+ <div class="flex w-full min-w-0 flex-col flex-1 lg:w-2/5 filters-container">
59
+ <div class="mb-2 flex flex-shrink-0 flex-wrap items-center gap-1.5 md:gap-2 text-white text-xs md:text-sm" v-if="hasLoaded">
56
60
  <PriceSortToggle
57
61
  :current="filterBuilder.globalFilter.priceSort"
58
62
  class="smooth-hover h-8"
@@ -71,8 +75,9 @@
71
75
  placeholder="999" />
72
76
  </div>
73
77
  <div
74
- class="flex min-h-0 flex-1 flex-col overflow-hidden rounded-lg border border-dark-550 bg-dark-500 shadow-sm">
75
- <div class="flex-shrink-0 border-b border-dark-550 bg-dark-300 px-4 py-3 text-xs text-white">
78
+ class="flex min-h-0 flex-1 flex-col overflow-hidden rounded-lg border border-dark-550 bg-dark-500 shadow-sm"
79
+ style="max-height: 100%;">
80
+ <div class="flex-shrink-0 border-b border-dark-550 bg-dark-300 px-2 md:px-4 py-2 md:py-3 text-xs text-white">
76
81
  <div class="flex w-full items-center justify-between gap-2">
77
82
  <div class="flex-gap-2 items-center">
78
83
  <span class="text-sm font-medium text-white">Filters</span>
@@ -98,8 +103,8 @@
98
103
  </div>
99
104
  </div>
100
105
  </div>
101
- <div class="hidden-scrollbars flex-1 overflow-auto bg-dark-400">
102
- <draggable
106
+ <div class="hidden-scrollbars flex-1 overflow-y-auto overflow-x-hidden bg-dark-400" style="max-height: 100%; min-height: 150px;">
107
+ <Draggable
103
108
  v-if="filterBuilder.filters.length"
104
109
  v-model="draggableFilters"
105
110
  handle=".handle"
@@ -117,7 +122,7 @@
117
122
  :filterBuilder="filterBuilder"
118
123
  class="!p-1 !text-xs" />
119
124
  </template>
120
- </draggable>
125
+ </Draggable>
121
126
  <div v-else class="empty-state flex flex-col items-center justify-center py-8 text-center">
122
127
  <FilterIcon class="empty-state-icon" />
123
128
  <p class="text-sm text-light-400">No filters yet</p>
@@ -129,9 +134,10 @@
129
134
  <button
130
135
  @click="addWildcardFilter"
131
136
  :disabled="hasWildcardFilter"
132
- class="filter-wildcard-btn"
137
+ class="filter-action-btn"
133
138
  :title="hasWildcardFilter ? 'Wildcard filter already exists' : 'Add wildcard filter'">
134
- * Wildcard
139
+ <WildcardIcon class="h-3 w-3 flex-shrink-0" />
140
+ <span>* Wildcard</span>
135
141
  </button>
136
142
  <button @click="ui.toggleModal('preview-filter')" class="filter-action-btn">
137
143
  <CameraIcon class="h-3 w-3" />
@@ -149,11 +155,11 @@
149
155
  </template>
150
156
 
151
157
  <script setup>
152
- import draggable from "vuedraggable";
153
- import { ref, computed, defineAsyncComponent, watch, nextTick } from "vue";
158
+ import Draggable from "vuedraggable";
159
+ import { ref, computed, defineAsyncComponent, watch, nextTick, onMounted } from "vue";
154
160
  import { useFilterCSS } from "@/composables/useFilterCSS";
155
161
  import Filter from "@/components/Filter/Filter.vue";
156
- import { FilterIcon } from "@/components/icons";
162
+ import { FilterIcon, WildcardIcon } from "@/components/icons";
157
163
  import { ReloadIcon, TrashIcon, EditIcon, CameraIcon } from "@/components/icons";
158
164
  import { sendSaveFilter } from "@/stores/requests";
159
165
  import PriceSortToggle from "@/components/Filter/PriceSortToggle.vue";
@@ -190,6 +196,8 @@ const draggableFilters = computed({
190
196
  get: () => filterBuilder.value.filters,
191
197
  set: (value) => {
192
198
  filterBuilder.value.filters = value;
199
+ filterBuilder.value.updateCss();
200
+ filterBuilder.value.updateHooks.forEach((fn) => fn());
193
201
  }
194
202
  });
195
203
 
@@ -262,9 +270,9 @@ const updateShownVenue = async () => {
262
270
  renderer = rendererFactory.createRenderer(eventId.value, {
263
271
  proxy: "",
264
272
  country: country,
265
- seatColor: "#0557ae",
266
- nonAvSeatColor: "#dadcde",
267
- highlightedSeatColor: "#d0006f"
273
+ seatColor: "#6b9b82",
274
+ nonAvSeatColor: "#4b4b4b",
275
+ highlightedSeatColor: "#7bc999"
268
276
  });
269
277
 
270
278
  filterBuilder.value.highlightedSeatColor = renderer.config.highlightedSeatColor;
@@ -414,13 +422,120 @@ watch(renderSeats, async () => {
414
422
  }
415
423
 
416
424
  .svg-wrapper path[generaladmission] {
417
- pointer-events: auto;
418
- cursor: pointer;
425
+ pointer-events: auto !important;
426
+ cursor: pointer !important;
427
+ }
428
+
429
+ /* Hover effects */
430
+ .svg-wrapper path:hover {
431
+ opacity: 0.8;
419
432
  }
420
433
 
421
- /* Hover effects for general admission seats */
422
434
  .svg-wrapper path[generaladmission]:hover {
423
- pointer-events: all;
424
- cursor: pointer;
435
+ opacity: 1;
436
+ fill: #7ee8fa !important;
437
+ }
438
+
439
+ /* Mobile responsiveness - bulletproof on all devices */
440
+ @media (max-width: 768px) {
441
+ .page-header {
442
+ flex-direction: column;
443
+ align-items: stretch !important;
444
+ gap: 0.75rem;
445
+ }
446
+
447
+ .unified-search-group {
448
+ width: 100% !important;
449
+ min-width: 100% !important;
450
+ }
451
+ }
452
+
453
+ @media (max-width: 1024px) {
454
+ .flex.w-full.min-w-0.flex-col.flex-1 {
455
+ min-height: 350px !important;
456
+ }
457
+ }
458
+
459
+ @media (max-width: 768px) {
460
+ .page-header {
461
+ flex-direction: column !important;
462
+ }
463
+ }
464
+
465
+ @media (max-width: 480px) {
466
+ .page-header {
467
+ padding-bottom: 0.75rem;
468
+ }
469
+
470
+ .unified-search-group input {
471
+ min-width: 0;
472
+ font-size: 13px;
473
+ }
474
+
475
+ .filter-action-btn {
476
+ font-size: 11px;
477
+ padding: 0.25rem 0.5rem;
478
+ }
479
+
480
+ .btn-icon-small {
481
+ width: 1.75rem;
482
+ height: 1.75rem;
483
+ font-size: 12px;
484
+ }
485
+
486
+ .flex.w-full.min-w-0.flex-col.flex-1 {
487
+ min-height: 400px !important;
488
+ }
489
+ }
490
+
491
+ /* SVG and Filters container responsive sizing */
492
+ .svg-container {
493
+ min-height: 250px;
494
+ max-height: 50%;
495
+ }
496
+
497
+ .svg-container.has-svg {
498
+ min-height: 400px;
499
+ max-height: 55%;
500
+ }
501
+
502
+ .filters-container {
503
+ min-height: 350px;
504
+ max-height: 100%;
505
+ }
506
+
507
+ @media (min-width: 1024px) {
508
+ .svg-container {
509
+ min-height: 400px;
510
+ max-height: 100%;
511
+ }
512
+
513
+ .svg-container.has-svg {
514
+ min-height: 400px;
515
+ max-height: 100%;
516
+ }
517
+
518
+ .filters-container {
519
+ min-height: 400px;
520
+ max-height: 100%;
521
+ }
522
+ }
523
+
524
+ /* Ensure filters list always scrolls */
525
+ .hidden-scrollbars::-webkit-scrollbar {
526
+ width: 4px;
527
+ }
528
+
529
+ .hidden-scrollbars::-webkit-scrollbar-track {
530
+ background: transparent;
531
+ }
532
+
533
+ .hidden-scrollbars::-webkit-scrollbar-thumb {
534
+ background: oklch(0.35 0 0);
535
+ border-radius: 4px;
536
+ }
537
+
538
+ .hidden-scrollbars::-webkit-scrollbar-thumb:hover {
539
+ background: oklch(0.45 0 0);
425
540
  }
426
541
  </style>