@necrolab/dashboard 0.4.61 → 0.4.208

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.
Files changed (133) hide show
  1. package/.prettierrc +1 -27
  2. package/.vscode/extensions.json +1 -1
  3. package/README.md +79 -43
  4. package/backend/api.js +48 -40
  5. package/backend/auth.js +3 -3
  6. package/backend/batching.js +1 -1
  7. package/backend/endpoints.js +77 -13
  8. package/backend/index.js +2 -2
  9. package/backend/mock-data.js +38 -29
  10. package/backend/mock-src/classes/logger.js +8 -8
  11. package/backend/mock-src/classes/utils.js +3 -7
  12. package/backend/mock-src/database.js +0 -0
  13. package/backend/mock-src/ticketmaster.js +79 -79
  14. package/backend/validator.js +2 -2
  15. package/config/configs.json +3 -2
  16. package/config/filter.json +3 -2
  17. package/index.html +10 -81
  18. package/index.js +1 -1
  19. package/package.json +25 -40
  20. package/postcss.config.js +1 -1
  21. package/postinstall.js +17 -98
  22. package/public/android-chrome-192x192.png +0 -0
  23. package/public/android-chrome-512x512.png +0 -0
  24. package/public/apple-touch-icon.png +0 -0
  25. package/public/favicon-16x16.png +0 -0
  26. package/public/favicon-32x32.png +0 -0
  27. package/public/favicon.ico +0 -0
  28. package/public/manifest.json +7 -12
  29. package/public/sw.js +2 -0
  30. package/public/workbox-49fdaf31.js +2 -0
  31. package/public/workbox-49fdaf31.js.map +1 -0
  32. package/public/workbox-88575b92.js +2 -0
  33. package/public/workbox-88575b92.js.map +1 -0
  34. package/public/workbox-a67a7b11.js +2 -0
  35. package/public/workbox-a67a7b11.js.map +1 -0
  36. package/public/workbox-d4314735.js +2 -0
  37. package/public/workbox-d4314735.js.map +1 -0
  38. package/public/workbox-e0f89ef3.js +2 -0
  39. package/public/workbox-e0f89ef3.js.map +1 -0
  40. package/run +9 -176
  41. package/src/App.vue +85 -498
  42. package/src/assets/css/_input.scss +99 -144
  43. package/src/assets/css/main.scss +99 -450
  44. package/src/assets/img/background.svg +2 -2
  45. package/src/assets/img/logo_icon.png +0 -0
  46. package/src/components/Auth/LoginForm.vue +11 -62
  47. package/src/components/Editors/Account/Account.vue +40 -116
  48. package/src/components/Editors/Account/AccountCreator.vue +39 -88
  49. package/src/components/Editors/Account/AccountView.vue +34 -102
  50. package/src/components/Editors/Account/CreateAccount.vue +32 -80
  51. package/src/components/Editors/Profile/CreateProfile.vue +83 -269
  52. package/src/components/Editors/Profile/Profile.vue +47 -132
  53. package/src/components/Editors/Profile/ProfileCountryChooser.vue +20 -82
  54. package/src/components/Editors/Profile/ProfileView.vue +34 -91
  55. package/src/components/Editors/TagLabel.vue +6 -67
  56. package/src/components/Filter/Filter.vue +72 -289
  57. package/src/components/Filter/FilterPreview.vue +30 -171
  58. package/src/components/Filter/PriceSortToggle.vue +4 -74
  59. package/src/components/Table/Header.vue +1 -1
  60. package/src/components/Table/Row.vue +1 -1
  61. package/src/components/Table/Table.vue +2 -19
  62. package/src/components/Tasks/CheckStock.vue +13 -28
  63. package/src/components/Tasks/Controls/DesktopControls.vue +17 -17
  64. package/src/components/Tasks/Controls/MobileControls.vue +45 -8
  65. package/src/components/Tasks/CreateTaskAXS.vue +73 -79
  66. package/src/components/Tasks/CreateTaskTM.vue +142 -94
  67. package/src/components/Tasks/MassEdit.vue +7 -9
  68. package/src/components/Tasks/QuickSettings.vue +55 -169
  69. package/src/components/Tasks/ScrapeVenue.vue +4 -7
  70. package/src/components/Tasks/Stats.vue +23 -52
  71. package/src/components/Tasks/Task.vue +136 -378
  72. package/src/components/Tasks/TaskView.vue +47 -107
  73. package/src/components/Tasks/Utilities.vue +6 -5
  74. package/src/components/icons/Bag.vue +1 -1
  75. package/src/components/icons/Loyalty.vue +1 -1
  76. package/src/components/icons/Mail.vue +2 -2
  77. package/src/components/icons/Play.vue +2 -2
  78. package/src/components/icons/Reload.vue +5 -4
  79. package/src/components/icons/Sandclock.vue +2 -2
  80. package/src/components/icons/Stadium.vue +1 -1
  81. package/src/components/icons/index.js +1 -24
  82. package/src/components/ui/Modal.vue +13 -105
  83. package/src/components/ui/Navbar.vue +38 -171
  84. package/src/components/ui/ReconnectIndicator.vue +55 -351
  85. package/src/components/ui/Splash.vue +35 -5
  86. package/src/components/ui/controls/CountryChooser.vue +62 -200
  87. package/src/components/ui/controls/atomic/Checkbox.vue +10 -119
  88. package/src/components/ui/controls/atomic/Dropdown.vue +39 -208
  89. package/src/components/ui/controls/atomic/MultiDropdown.vue +37 -300
  90. package/src/libs/Filter.js +170 -200
  91. package/src/registerServiceWorker.js +1 -1
  92. package/src/stores/connection.js +53 -51
  93. package/src/stores/logger.js +3 -11
  94. package/src/stores/sampleData.js +235 -207
  95. package/src/stores/ui.js +44 -112
  96. package/src/stores/utils.js +6 -90
  97. package/src/views/Accounts.vue +35 -44
  98. package/src/views/Console.vue +90 -341
  99. package/src/views/Editor.vue +123 -1176
  100. package/src/views/FilterBuilder.vue +251 -607
  101. package/src/views/Login.vue +14 -76
  102. package/src/views/Profiles.vue +25 -44
  103. package/src/views/Tasks.vue +100 -187
  104. package/static/offline.html +50 -192
  105. package/tailwind.config.js +26 -104
  106. package/vite.config.js +16 -73
  107. package/vue.config.js +32 -0
  108. package/workbox-config.js +11 -0
  109. package/artwork/image.png +0 -0
  110. package/dev-server.js +0 -136
  111. package/exit +0 -209
  112. package/jsconfig.json +0 -16
  113. package/src/assets/css/_utilities.scss +0 -388
  114. package/src/assets/img/background.svg.backup +0 -11
  115. package/src/components/icons/Check.vue +0 -5
  116. package/src/components/icons/Close.vue +0 -21
  117. package/src/components/icons/CloseX.vue +0 -5
  118. package/src/components/icons/Key.vue +0 -21
  119. package/src/components/icons/Pencil.vue +0 -21
  120. package/src/components/icons/Profile.vue +0 -18
  121. package/src/components/icons/Sell.vue +0 -21
  122. package/src/components/icons/Spinner.vue +0 -42
  123. package/src/components/icons/SquareCheck.vue +0 -18
  124. package/src/components/icons/SquareUncheck.vue +0 -18
  125. package/src/components/icons/Wildcard.vue +0 -18
  126. package/src/components/ui/controls/atomic/LoadingButton.vue +0 -45
  127. package/src/composables/useClickOutside.js +0 -21
  128. package/src/composables/useDropdownPosition.js +0 -174
  129. package/src/types/index.js +0 -41
  130. package/src/utils/debug.js +0 -1
  131. package/switch-branch.sh +0 -41
  132. package/workbox-config.cjs +0 -63
  133. /package/src/assets/img/{logo_icon-old.png → logo_icon_2.png} +0 -0
@@ -1,76 +1,67 @@
1
1
  <template>
2
- <div class="filter-builder-container flex min-h-0 w-full flex-col overflow-hidden">
3
- <!-- Heading -->
4
- <div class="flex-between mt-1 pb-1 pt-4">
5
- <div class="flex-center gap-4">
6
- <FilterIcon class="smooth-hover cursor-pointer text-white" />
7
- <h4 class="text-heading">Filter creator</h4>
8
- </div>
9
- <div class="flex items-center">
10
- <input
11
- class="relative flex h-10 w-48 items-center rounded-l rounded-r-none border-2 border-dark-550 bg-dark-500 p-2 text-sm text-white"
12
- placeholder="Event ID"
13
- v-model="eventId" />
14
- <button
15
- class="smooth-hover relative flex h-10 items-center rounded-r border-2 border-dark-550 bg-dark-400 px-3 text-sm font-medium text-white"
16
- @click="updateShownVenue">
17
- Load
18
- </button>
19
- </div>
20
- </div>
21
-
22
- <div class="card-dark mb-2 overflow-hidden p-3">
23
- <div class="h-full w-full">
2
+ <div v-html="`<style>${filterBuilder.cssClasses}</style>`" />
3
+ <div v-html="`<style>${filterBuilder.temporaryCSS}</style>`" />
4
+ <div class="ios-wrapper mt-3">
5
+ <div class="bg-dark-500 mt-6 m-4 p-2 rounded shadow border-2 border-dark-550">
6
+ <div class="lg:px-3 px-0 mx-3 pb-8 mt-5">
24
7
  <!-- Main -->
25
- <div class="grid h-full w-full grid-cols-1 gap-3 lg:grid-cols-5 lg:gap-4">
8
+ <div class="grid grid-cols-1 md:grid-cols-5 gap-0 md:gap-8">
26
9
  <!-- Map -->
27
- <div
28
- class="relative col-span-1 flex h-full w-full flex-col rounded-lg lg:col-span-3 lg:max-w-none lg:overflow-hidden">
29
- <div v-if="svg" class="mb-1 flex items-center">
30
- <div class="flex w-20 items-center justify-between px-2 text-sm font-black text-white">
10
+ <div class="col-span-3 w-auto h-auto rounded-lg relative">
11
+ <!-- Heading -->
12
+ <div class="text-white">
13
+ <h4 class="text-2xl font-bold" @click="filterBuilder.log()">Filter creator</h4>
14
+ <div class="flex items-center">
15
+ <input
16
+ class="h-6 mt-1 text-sm p-2 bg-dark-500 w-40 flex items-center rounded-l relative border-2 border-dark-550"
17
+ placeholder="Event"
18
+ v-model="eventId"
19
+ />
20
+ <button
21
+ class="h-6 mt-1 text-sm p-2 bg-dark-550 flex items-center rounded-r relative font-bold border-2 border-dark-550 smooth-hover"
22
+ @click="updateShownVenue"
23
+ >
24
+ Load
25
+ </button>
26
+ </div>
27
+ </div>
28
+ <div v-if="svg" class="flex items-center">
29
+ <div class="flex items-center justify-between w-20 px-2 text-white font-black text-xl">
31
30
  <span class="cursor-pointer" @click="handleZoom(true)">+</span>
32
31
  <span class="cursor-pointer" @click="handleZoom(false)">-</span>
33
- <ReloadIcon class="h-4 w-4 cursor-pointer" @click="handleZoom('r')" />
32
+ <ReloadIcon class="cursor-pointer" @click="handleZoom('r')" />
33
+
34
+ <!-- <h3 class="ml-10 text-sm text-white">Auto</h3>
35
+ <Switch class="scale-75 slider-dim" v-model="renderSeats" /> -->
34
36
  </div>
35
37
  </div>
36
- <div class="selecto-wrapper flex-1 overflow-hidden">
38
+ <div class="overflow-hidden selecto-wrapper">
37
39
  <div
38
40
  v-if="svg"
39
- class="hidden-scrollbars svg-container h-full overflow-auto rounded border-2 border-dark-550 p-2 shadow">
41
+ class="mt-2 overflow-scroll hidden-scrollbars p-2 rounded shadow border-2 border-dark-550"
42
+ >
43
+ <!-- <drag-select v-model="selection"> -->
40
44
  <div class="svg-wrapper" id="svg-wrapper" v-html="svg"></div>
41
- </div>
42
- <div
43
- v-else
44
- class="svg-container flex h-full items-center justify-center rounded border-2 border-dark-550 p-2 shadow">
45
- <div class="text-center">
46
- <svg
47
- class="mx-auto mb-3 h-12 w-12 opacity-50"
48
- viewBox="0 0 19 19"
49
- fill="none"
50
- xmlns="http://www.w3.org/2000/svg">
51
- <path
52
- d="M2.37499 5.54165V2.37498L5.54166 3.95831L2.37499 5.54165ZM14.25 5.54165V2.37498L17.4167 3.95831L14.25 5.54165ZM8.70833 4.74998V1.58331L11.875 3.16665L8.70833 4.74998ZM8.70833 17.4166C7.70555 17.3903 6.77218 17.3079 5.9082 17.1696C5.0437 17.0308 4.29162 16.8559 3.65195 16.6448C3.01176 16.4337 2.50694 16.1896 2.13749 15.9125C1.76805 15.6354 1.58333 15.3451 1.58333 15.0416V7.91665C1.58333 7.58678 1.79127 7.27988 2.20716 6.99594C2.62252 6.71252 3.18645 6.46183 3.89895 6.24385C4.61145 6.02641 5.4493 5.85488 6.4125 5.72927C7.37569 5.60419 8.40486 5.54165 9.49999 5.54165C10.5951 5.54165 11.6243 5.60419 12.5875 5.72927C13.5507 5.85488 14.3885 6.02641 15.101 6.24385C15.8135 6.46183 16.3775 6.71252 16.7928 6.99594C17.2087 7.27988 17.4167 7.58678 17.4167 7.91665V15.0416C17.4167 15.3451 17.2319 15.6354 16.8625 15.9125C16.493 16.1896 15.9885 16.4337 15.3488 16.6448C14.7086 16.8559 13.9565 17.0308 13.0926 17.1696C12.2281 17.3079 11.2944 17.3903 10.2917 17.4166V14.25H8.70833V17.4166ZM9.49999 8.70831C10.7799 8.70831 11.885 8.63231 12.8155 8.48031C13.7454 8.32884 14.4875 8.15415 15.0417 7.95623C15.0417 7.89026 14.5403 7.73509 13.5375 7.49073C12.5347 7.2469 11.1889 7.12498 9.49999 7.12498C7.81111 7.12498 6.46527 7.2469 5.46249 7.49073C4.45972 7.73509 3.95833 7.89026 3.95833 7.95623C4.51249 8.15415 5.25481 8.32884 6.18529 8.48031C7.11523 8.63231 8.22013 8.70831 9.49999 8.70831ZM7.12499 15.7146V12.6666H11.875V15.7146C12.9305 15.609 13.7948 15.4538 14.4677 15.2491C15.1406 15.0448 15.5958 14.8635 15.8333 14.7052V9.34165C15.1076 9.63192 14.1972 9.86283 13.1021 10.0344C12.0069 10.2059 10.8062 10.2916 9.49999 10.2916C8.19374 10.2916 6.99305 10.2059 5.89791 10.0344C4.80277 9.86283 3.89236 9.63192 3.16666 9.34165V14.7052C3.40416 14.8635 3.85937 15.0448 4.53229 15.2491C5.2052 15.4538 6.06944 15.609 7.12499 15.7146Z"
53
- fill="#F5F5F5" />
54
- </svg>
55
- <p class="text-sm text-light-400">No Map</p>
56
- <p class="text-xs text-light-500">
57
- Enter an event ID and click "Load" to display the venue map
58
- </p>
59
- </div>
45
+ <!-- </drag-select> -->
60
46
  </div>
61
47
  </div>
62
48
  </div>
63
- <div class="col-span-1 flex h-full w-full flex-col lg:col-span-2">
64
- <div class="mb-2 flex items-center justify-between text-white">
65
- <div class="flex items-center gap-2 rounded" v-if="hasLoaded">
49
+ <div class="col-span-2 mt-10">
50
+ <div class="flex justify-between mb-2 items-center text-white">
51
+ <button
52
+ @click="filterBuilder.selectFirstRow"
53
+ class="border-2 rounded border-dark-550 px-1 h-8 w-full min-w-14 text-gray bg-dark-500 overflow-hidden smooth-hover"
54
+ >
55
+ First rows
56
+ </button>
57
+
58
+ <div class="rounded flex justify-between gap-2 items-center" v-if="hasLoaded">
66
59
  <PriceSortToggle
67
60
  :current="filterBuilder.globalFilter.priceSort"
68
61
  class="smooth-hover"
69
- @change="(e) => filterBuilder.updateGlobalFilter({ priceSort: e })" />
70
- </div>
71
- <div class="flex items-center gap-1" v-if="hasLoaded">
72
- <SavingsIcon class="h-4 w-4 text-light-400" />
73
- <label class="text-sm text-light-400">Max Price:</label>
62
+ @change="(e) => filterBuilder.updateGlobalFilter({ priceSort: e })"
63
+ />
64
+
74
65
  <input
75
66
  type="number"
76
67
  :value="filterBuilder.globalFilter.maxPrice"
@@ -80,87 +71,72 @@
80
71
  maxPrice: parseInt(e.target.value || 0)
81
72
  })
82
73
  "
83
- class="h-8 w-14 rounded border-2 border-dark-550 bg-dark-500 px-1 pl-2"
84
- placeholder="max" />
74
+ class="w-14 bg-dark-500 border-2 border-dark-550 px-1 pl-2 h-8 rounded"
75
+ placeholder="max"
76
+ />
85
77
  </div>
86
78
  </div>
87
- <Table class="flex flex-1 flex-col border-2 border-dark-550 shadow-xl">
88
- <Header class="flex-shrink-0">
89
- <div class="flex w-full items-center justify-between gap-2 font-bold">
90
- <div class="flex items-center gap-2">
91
- <span class="text-base">Filters</span>
92
- <span class="text-base text-light-300">{{ filterBuilder.filters.length }}</span>
79
+ <Table class="border-2 border-dark-550 shadow-xl">
80
+ <Header>
81
+ <div class="flex items-center font-bold gap-2">
82
+ <span class="text-xl lg:block hidden">Filters</span>
83
+ <span class="text-xl text-light-300">{{ filterBuilder.filters.length }}</span>
84
+ <PriceSortToggle
85
+ class="w-14 smooth-hover"
86
+ :options="['All', 'WL', 'BL']"
87
+ @change="(e) => (shownFilters = e)"
88
+ />
89
+ <div
90
+ class="flex gap-1 h-8 items-center justify-self-end border-2 border-dark-550 p-2 rounded smooth-hover"
91
+ @click="saveFilter"
92
+ >
93
+ <EditIcon />
94
+ <button class="lg:block hidden">Save</button>
93
95
  </div>
94
- <div class="flex items-center gap-2">
95
- <PriceSortToggle
96
- class="smooth-hover w-14"
97
- :options="['All', 'WL', 'BL']"
98
- @change="(e) => (shownFilters = e)" />
99
- <button class="header-btn save-btn" @click="saveFilter">
100
- <EditIcon class="h-4 w-4" />
101
- <span class="hidden lg:block">Save</span>
102
- </button>
103
- <button class="header-btn clear-btn" @click="filterBuilder.reset(false)">
104
- <TrashIcon class="h-4 w-4" />
105
- <span class="hidden lg:block">Clear</span>
106
- </button>
96
+ <div
97
+ class="flex gap-1 h-8 items-center absolute right-5 justify-self-end border-2 border-dark-550 p-2 rounded smooth-hover"
98
+ @click="filterBuilder.reset(false)"
99
+ >
100
+ <TrashIcon />
101
+ <button class="lg:block hidden">Clear</button>
107
102
  </div>
108
103
  </div>
109
104
  </Header>
110
- <div class="hidden-scrollbars flex-1 overflow-auto bg-dark-400">
111
- <div v-if="filterBuilder.filters.length" class="filters-container">
105
+ <div class="overflow-scroll hidden-scrollbars h-96">
106
+ <div v-if="filterBuilder.filters.length">
112
107
  <draggable
113
108
  :list="filterBuilder.filters"
114
109
  handle=".handle"
115
110
  item-key="id"
116
- ghost-class="sortable-ghost"
117
- chosen-class="sortable-chosen"
118
- drag-class="sortable-drag"
119
- :animation="200"
120
- :easing="'cubic-bezier(0.4, 0, 0.2, 1)'"
121
- :force-fallback="false"
122
- @change="
123
- () => {
124
- filterBuilder.updateCss();
125
- cssUpdateTrigger++;
126
- }
127
- ">
111
+ @change="filterBuilder.updateCss()"
112
+ >
128
113
  <template #item="{ element: f, index: i }">
129
114
  <Filter
130
115
  v-if="doesFilterShow(f)"
131
116
  :filter="f"
132
117
  :index="i"
133
118
  :filterBuilder="filterBuilder"
134
- class="compact-filter" />
119
+ />
135
120
  </template>
136
121
  </draggable>
137
122
  </div>
138
- <div
139
- v-else
140
- class="empty-state flex flex-col items-center justify-center py-8 text-center">
141
- <FilterIcon class="mb-3 h-12 w-12 text-dark-400 opacity-50" />
142
- <p class="text-sm text-light-400">No filters yet</p>
143
- <p class="mt-1 text-xs text-light-500">Click on the map to create filters</p>
144
- </div>
123
+ <p v-else class="text-dark-400 m-2">No filters yet...</p>
145
124
  </div>
146
125
  </Table>
147
- <div class="mb-2 mt-1 flex flex-shrink-0 items-center justify-between gap-2 text-white md:mb-0">
126
+ <div class="flex items-center justify-between font-bold text-white gap-2 my-2">
148
127
  <button
149
- @click="addWildcardFilter"
150
- :disabled="hasWildcardFilter"
151
- :class="[
152
- 'h-7 overflow-hidden rounded border-2 border-dark-550 bg-dark-500 px-2 text-xs shadow transition-all duration-200',
153
- hasWildcardFilter
154
- ? 'text-gray cursor-not-allowed opacity-50'
155
- : 'text-gray smooth-hover hover:border-light-300 hover:bg-dark-400'
156
- ]"
157
- :title="hasWildcardFilter ? 'Wildcard filter already exists' : 'Add wildcard filter'">
128
+ @click="
129
+ filterBuilder.addFilter({ buyAny: true, eventId: filterBuilder.currentEventId })
130
+ "
131
+ class="border-2 rounded border-dark-550 px-2 h-8 text-gray bg-dark-500 overflow-hidden shadow smooth-hover"
132
+ >
158
133
  * Wildcard
159
134
  </button>
160
135
  <button
161
136
  @click="ui.toggleModal('preview-filter')"
162
- class="text-gray smooth-hover flex h-7 items-center justify-between gap-1 overflow-hidden rounded border-2 border-dark-550 bg-dark-500 px-2 text-xs shadow">
163
- <CameraIcon class="h-3 w-3" />
137
+ class="border-2 gap-2 flex justify-between items-center rounded border-dark-550 px-2 h-8 text-gray bg-dark-500 overflow-hidden shadow smooth-hover"
138
+ >
139
+ <CameraIcon />
164
140
  JSON
165
141
  </button>
166
142
  </div>
@@ -168,7 +144,7 @@
168
144
  </div>
169
145
  </div>
170
146
 
171
- <transition-group name="fade">
147
+ <transition-group name="fade" mode="out-in">
172
148
  <FilterPreview v-if="activeModal === 'preview-filter'" :filter="filterBuilder" />
173
149
  </transition-group>
174
150
  </div>
@@ -177,15 +153,20 @@
177
153
 
178
154
  <script setup>
179
155
  import draggable from "vuedraggable";
180
- import { ref, nextTick, watch, computed, onMounted, onUnmounted } from "vue";
181
- import { onBeforeRouteLeave } from "vue-router";
156
+ import { ref, nextTick, watch, computed } from "vue";
182
157
  import { Table, Header } from "@/components/Table";
183
158
  import Filter from "@/components/Filter/Filter.vue";
184
- import { FilterIcon } from "@/components/icons";
185
159
  import DragSelect from "dragselect";
186
- import { DEBUG } from "@/utils/debug";
187
160
 
188
- import { ReloadIcon, TrashIcon, EditIcon, CameraIcon, StadiumIcon, SavingsIcon } from "@/components/icons";
161
+ const DEBUG = window.location.href.startsWith("http://localhost:5173");
162
+ var RendererFactory;
163
+ if (DEBUG) {
164
+ RendererFactory = class {
165
+ constructor() {}
166
+ };
167
+ } else RendererFactory = import("@necrolab/tm-renderer");
168
+
169
+ import { ReloadIcon, TrashIcon, EditIcon, CameraIcon } from "@/components/icons";
189
170
  import { sendSaveFilter } from "@/stores/requests";
190
171
  import PriceSortToggle from "@/components/Filter/PriceSortToggle.vue";
191
172
  import { useUIStore } from "@/stores/ui";
@@ -197,273 +178,151 @@ const activeModal = computed(() => ui.activeModal);
197
178
  const ui = useUIStore();
198
179
  const hasLoaded = ref(false);
199
180
 
200
- // let isShiftPressed = false;
201
- // window.onkeyup = (e) => {
202
- // if (e.keyCode === 16) isShiftPressed = false;
203
- // };
204
- // window.onkeydown = (e) => {
205
- // if (e.keyCode === 16) isShiftPressed = true;
206
- // };
207
-
208
- // const doDragSelect = () => {
209
- // // https://dragselect.com/docs/API/Settings
210
- // const ds = new DragSelect({
211
- // selectables: [
212
- // ...document.querySelectorAll("path[row]"),
213
- // ...document.querySelectorAll("path[generaladmission]")
214
- // ],
215
- // area: document.getElementById("svg-wrapper"),
216
- // selectionThreshold: 0.1,
217
- // multiSelectKeys: []
218
- // });
219
-
220
- // ds.subscribe("DS:start:pre", () => {
221
- // // ui.logger.Info("DS:start:pre - shift pressed:", isShiftPressed);
222
- // if (!isShiftPressed) {
223
- // ds.stop(false, false);
224
- // ds.start();
225
- // } else {
226
- // panzoomInstance.pause();
227
- // }
228
- // });
229
-
230
- // const parseSelected = (items) => {
231
- // const parsed = items
232
- // .map((e) => {
233
- // return {
234
- // section: e.attributes.sectionname?.nodeValue || e.attributes.name?.nodeValue,
235
- // row: e.attributes.row?.nodeValue,
236
- // GA: e.attributes.generaladmission?.nodeValue,
237
- // name: e.attributes.name?.nodeValue
238
- // };
239
- // })
240
- // .filter((e) => !filterBuilder.value.isUnselectable(e.section, e.row));
241
-
242
- // const sectionMapping = {};
243
- // parsed
244
- // .filter((p) => !p.GA)
245
- // .forEach((p) => {
246
- // if (!sectionMapping[p.section]) sectionMapping[p.section] = [];
247
- // sectionMapping[p.section].push(p);
248
- // });
249
- // parsed
250
- // .filter((p) => p.GA)
251
- // .forEach((p) => {
252
- // sectionMapping[p.section] = {
253
- // GA: true,
254
- // section: p.section,
255
- // name: p.name
256
- // };
257
- // });
258
- // return sectionMapping;
259
- // };
260
-
261
- // ds.subscribe("DS:update:pre", (e) => {
262
- // // ui.logger.Info("DS:update:pre - shift pressed:", isShiftPressed, e.items);
263
- // filterBuilder.value.isDragging = true;
264
- // if (!isShiftPressed) return;
265
- // const sectionMapping = parseSelected(e.items);
266
- // filterBuilder.value.clearHighlight();
267
- // Object.entries(sectionMapping).forEach(([section, rows]) => {
268
- // if (rows.GA) {
269
- // const isSelected = filterBuilder.value.filters.find(
270
- // (f) => filterBuilder.value.isForCurrentEvent(f) && f.section === section
271
- // );
272
- // if (isSelected) return;
273
- // filterBuilder.value.highlight({ section: section }, true);
274
- // } else
275
- // rows.forEach((row) => {
276
- // const isSelected = filterBuilder.value.filters.find(
277
- // (f) =>
278
- // (f.section === section && f.rows?.includes(row.row)) ||
279
- // (!f.rows && f.section === section && filterBuilder.value.isForCurrentEvent(f))
280
- // );
281
- // if (isSelected) return;
282
-
283
- // filterBuilder.value.highlight({ section, row: row.row });
284
- // });
285
- // });
286
- // });
287
-
288
- // ds.subscribe("DS:end", (e) => {
289
- // filterBuilder.value.isDragging = false;
290
- // panzoomInstance.resume();
291
- // filterBuilder.value.clearHighlight();
292
- // [...document.querySelectorAll("path")].map((f) => (f.style = ""));
293
- // if (!isShiftPressed) return;
294
- // const sectionMapping = parseSelected(e.items);
295
- // const secNameMapping = filterBuilder.value.getSectionNameMapping();
296
-
297
- // Object.entries(sectionMapping).forEach(([section, rows]) => {
298
- // if (rows.GA) {
299
- // const isSelected = filterBuilder.value.filters.find(
300
- // (f) => filterBuilder.value.isForCurrentEvent(f) && f.section === secNameMapping[section]
301
- // );
302
- // if (isSelected) return;
303
- // filterBuilder.value.addFilter({
304
- // event: filterBuilder.value.currentEventId,
305
- // section: secNameMapping[section]
306
- // });
307
- // } else {
308
- // const unselectedRows = [];
309
- // rows.forEach((row) => {
310
- // const isSelected = filterBuilder.value.filters.find(
311
- // (f) =>
312
- // (f.section === section && f.rows?.includes(row.row)) ||
313
- // (!f.rows && f.section === section && filterBuilder.value.isForCurrentEvent(f))
314
- // );
315
- // if (isSelected) return;
316
- // unselectedRows.push(row.row);
317
- // });
318
- // if (unselectedRows.length === 0) return;
319
- // filterBuilder.value.addFilter({
320
- // event: filterBuilder.value.currentEventId,
321
- // section,
322
- // rows: unselectedRows
323
- // });
324
- // }
325
- // });
326
- // isShiftPressed = false;
327
- // });
328
- // };
329
-
330
- const panzoomOptions = {
331
- maxZoom: 8, // max zoom-in factor
332
- minZoom: 0.8, // max zoom-out factor
333
- panOnlyWhenZoomed: true,
334
- bounds: true,
335
- boundsPadding: 0.1, // Add padding to ensure it stays within bounds
336
- transformOrigin: { x: 0.5, y: 0.5 }, // Center the zoom
337
- contain: true // Force the image to stay within the parent container
181
+ let isShiftPressed = false;
182
+ window.onkeyup = (e) => {
183
+ if (e.keyCode === 16) isShiftPressed = false;
338
184
  };
339
- let panzoomInstance;
340
-
341
- const svg = ref("");
342
- const eventId = ref("");
343
- const renderSeats = ref(false);
344
- let renderer;
345
-
346
- const shownFilters = ref("All");
347
- const filterBuilder = ref(new FilterBuilder());
348
-
349
- const hasWildcardFilter = computed(() => {
350
- return filterBuilder.value.filters.some((filter) => filter.buyAny && filterBuilder.value.isForCurrentEvent(filter));
351
- });
352
-
353
- const addWildcardFilter = () => {
354
- if (!hasWildcardFilter.value) {
355
- // Close any expanded filter first
356
- filterBuilder.value.setExpandedFilter(null);
357
- filterBuilder.value.addFilter({ buyAny: true, eventId: filterBuilder.value.currentEventId });
358
- filterBuilder.value.updateCss();
359
- }
185
+ window.onkeydown = (e) => {
186
+ if (e.keyCode === 16) isShiftPressed = true;
360
187
  };
361
188
 
362
- // Initialize RendererFactory
363
- let RendererFactory = import("@necrolab/tm-renderer");
364
-
365
- // Real-time CSS injection system
366
- let styleElement = null;
367
- const cssUpdateTrigger = ref(0);
368
- const STYLE_ELEMENT_ID = "filter-builder-styles";
369
-
370
- const injectStyles = () => {
371
- // Only inject styles if we have an SVG to style
372
- const svgWrapper = document.getElementById("svg-wrapper");
373
- if (!svgWrapper || !svg.value) return;
189
+ const doDragSelect = () => {
190
+ // https://dragselect.com/docs/API/Settings
191
+ const ds = new DragSelect({
192
+ selectables: [
193
+ ...document.querySelectorAll("path[row]"),
194
+ ...document.querySelectorAll("path[generaladmission]")
195
+ ],
196
+ area: document.getElementById("svg-wrapper"),
197
+ selectionThreshold: 0.1,
198
+ multiSelectKeys: []
199
+ });
374
200
 
375
- if (!styleElement) {
376
- // Remove any existing style element first
377
- const existingStyle = document.getElementById(STYLE_ELEMENT_ID);
378
- if (existingStyle) {
379
- existingStyle.remove();
201
+ ds.subscribe("DS:start:pre", () => {
202
+ // ui.logger.Info("DS:start:pre - shift pressed:", isShiftPressed);
203
+ if (!isShiftPressed) {
204
+ ds.stop(false, false);
205
+ ds.start();
206
+ } else {
207
+ panzoomInstance.pause();
380
208
  }
209
+ });
381
210
 
382
- styleElement = document.createElement("style");
383
- styleElement.id = STYLE_ELEMENT_ID;
384
- document.head.appendChild(styleElement);
385
- }
386
-
387
- const combinedCSS = filterBuilder.value.cssClasses + filterBuilder.value.temporaryCSS;
388
- if (styleElement.textContent !== combinedCSS) {
389
- styleElement.textContent = combinedCSS;
390
- }
391
- };
392
-
393
- // Setup real-time CSS updates
394
- const setupCSSUpdates = () => {
395
- // Override updateCss method to trigger immediate updates
396
- const originalUpdateCss = filterBuilder.value.updateCss.bind(filterBuilder.value);
397
- filterBuilder.value.updateCss = () => {
398
- originalUpdateCss();
399
- nextTick(() => {
400
- injectStyles();
401
- cssUpdateTrigger.value++;
402
- });
211
+ const parseSelected = (items) => {
212
+ const parsed = items
213
+ .map((e) => {
214
+ return {
215
+ section: e.attributes.sectionname?.nodeValue || e.attributes.name?.nodeValue,
216
+ row: e.attributes.row?.nodeValue,
217
+ GA: e.attributes.generaladmission?.nodeValue,
218
+ name: e.attributes.name?.nodeValue
219
+ };
220
+ })
221
+ .filter((e) => !filterBuilder.value.isUnselectable(e.section, e.row));
222
+
223
+ const sectionMapping = {};
224
+ parsed
225
+ .filter((p) => !p.GA)
226
+ .forEach((p) => {
227
+ if (!sectionMapping[p.section]) sectionMapping[p.section] = [];
228
+ sectionMapping[p.section].push(p);
229
+ });
230
+ parsed
231
+ .filter((p) => p.GA)
232
+ .forEach((p) => {
233
+ sectionMapping[p.section] = {
234
+ GA: true,
235
+ section: p.section,
236
+ name: p.name
237
+ };
238
+ });
239
+ return sectionMapping;
403
240
  };
404
241
 
405
- // Override highlight method for immediate temporary CSS
406
- const originalHighlight = filterBuilder.value.highlight.bind(filterBuilder.value);
407
- filterBuilder.value.highlight = (...args) => {
408
- originalHighlight(...args);
409
- nextTick(() => {
410
- injectStyles();
242
+ ds.subscribe("DS:update:pre", (e) => {
243
+ // ui.logger.Info("DS:update:pre - shift pressed:", isShiftPressed, e.items);
244
+ filterBuilder.value.isDragging = true;
245
+ if (!isShiftPressed) return;
246
+ const sectionMapping = parseSelected(e.items);
247
+ filterBuilder.value.clearHighlight();
248
+ Object.entries(sectionMapping).forEach(([section, rows]) => {
249
+ if (rows.GA) {
250
+ const isSelected = filterBuilder.value.filters.find(
251
+ (f) => filterBuilder.value.isForCurrentEvent(f) && f.section === section
252
+ );
253
+ if (isSelected) return;
254
+ filterBuilder.value.highlight({ section: section }, true);
255
+ } else
256
+ rows.forEach((row) => {
257
+ const isSelected = filterBuilder.value.filters.find(
258
+ (f) =>
259
+ (f.section === section && f.rows?.includes(row.row)) ||
260
+ (!f.rows && f.section === section && filterBuilder.value.isForCurrentEvent(f))
261
+ );
262
+ if (isSelected) return;
263
+
264
+ filterBuilder.value.highlight({ section, row: row.row });
265
+ });
411
266
  });
412
- };
267
+ });
413
268
 
414
- // Override clearHighlight method
415
- const originalClearHighlight = filterBuilder.value.clearHighlight.bind(filterBuilder.value);
416
- filterBuilder.value.clearHighlight = () => {
417
- originalClearHighlight();
418
- nextTick(() => {
419
- injectStyles();
269
+ ds.subscribe("DS:end", (e) => {
270
+ filterBuilder.value.isDragging = false;
271
+ panzoomInstance.resume();
272
+ filterBuilder.value.clearHighlight();
273
+ [...document.querySelectorAll("path")].map((f) => (f.style = ""));
274
+ if (!isShiftPressed) return;
275
+ const sectionMapping = parseSelected(e.items);
276
+ const secNameMapping = filterBuilder.value.getSectionNameMapping();
277
+
278
+ Object.entries(sectionMapping).forEach(([section, rows]) => {
279
+ if (rows.GA) {
280
+ const isSelected = filterBuilder.value.filters.find(
281
+ (f) => filterBuilder.value.isForCurrentEvent(f) && f.section === secNameMapping[section]
282
+ );
283
+ if (isSelected) return;
284
+ filterBuilder.value.addFilter({
285
+ event: filterBuilder.value.currentEventId,
286
+ section: secNameMapping[section]
287
+ });
288
+ } else {
289
+ const unselectedRows = [];
290
+ rows.forEach((row) => {
291
+ const isSelected = filterBuilder.value.filters.find(
292
+ (f) =>
293
+ (f.section === section && f.rows?.includes(row.row)) ||
294
+ (!f.rows && f.section === section && filterBuilder.value.isForCurrentEvent(f))
295
+ );
296
+ if (isSelected) return;
297
+ unselectedRows.push(row.row);
298
+ });
299
+ if (unselectedRows.length === 0) return;
300
+ filterBuilder.value.addFilter({
301
+ event: filterBuilder.value.currentEventId,
302
+ section,
303
+ rows: unselectedRows
304
+ });
305
+ }
420
306
  });
421
- };
422
- };
423
-
424
- // Cleanup function
425
- const cleanupStyles = () => {
426
- // Remove by ID to ensure we get it even if reference is lost
427
- const existingStyle = document.getElementById(STYLE_ELEMENT_ID);
428
- if (existingStyle) {
429
- existingStyle.remove();
430
- }
431
-
432
- if (styleElement) {
433
- styleElement.remove();
434
- styleElement = null;
435
- }
307
+ isShiftPressed = false;
308
+ });
436
309
  };
437
310
 
438
- // Basic cleanup monitoring
439
- const debugCleanup = () => {
440
- console.log("FilterBuilder cleanup completed");
311
+ const panzoomOptions = {
312
+ maxZoom: 8, // max zoom-in facor
313
+ minZoom: 0.8, // max zoom-out factor
314
+ panOnlyWhenZoomed: true,
315
+ bounds: true
441
316
  };
317
+ let panzoomInstance;
442
318
 
443
- // Clean up any leftover styles from previous instances
444
- cleanupStyles();
445
-
446
- // Initialize CSS system
447
- setupCSSUpdates();
448
-
449
- // Watch for reactive updates
450
- watch(
451
- cssUpdateTrigger,
452
- () => {
453
- injectStyles();
454
- },
455
- { immediate: true }
456
- );
457
-
458
- // Cleanup on unmount
459
- onUnmounted(() => {
460
- cleanupStyles();
461
- });
319
+ const svg = ref("");
320
+ const eventId = ref("");
321
+ const renderSeats = ref(false);
322
+ let renderer;
462
323
 
463
- // Also cleanup on route change using beforeRouteLeave
464
- onBeforeRouteLeave(() => {
465
- cleanupStyles();
466
- });
324
+ const shownFilters = ref("All");
325
+ const filterBuilder = ref(new FilterBuilder());
467
326
 
468
327
  const doesFilterShow = (filter) => {
469
328
  if ((filter.event || filter.eventId) !== filterBuilder.value.currentEventId) return;
@@ -473,12 +332,10 @@ const doesFilterShow = (filter) => {
473
332
  };
474
333
 
475
334
  let rendererFactory;
476
- RendererFactory.then((r) => {
477
- rendererFactory = new r.default();
478
- rendererFactory.init();
479
- }).catch((error) => {
480
- console.error("Failed to initialize renderer:", error);
481
- });
335
+ if (!DEBUG)
336
+ RendererFactory.then((r) => {
337
+ rendererFactory = new r.default();
338
+ });
482
339
 
483
340
  const updateShownVenue = async () => {
484
341
  eventId.value = eventId.value.trim();
@@ -491,16 +348,13 @@ const updateShownVenue = async () => {
491
348
  return;
492
349
  }
493
350
 
494
- renderer = rendererFactory.createRenderer(eventId.value, {
495
- proxy: "",
496
- country: country
497
- });
498
- filterBuilder.value.highlightedSeatColor = renderer.config.highlightedSeatColor;
499
- renderer.setCustomConfig({
500
- logFullError: true,
501
- includeDetailedAttributes: true,
502
- renderRowBlocks: true
503
- });
351
+ renderer = rendererFactory.createRenderer(eventId.value, "", country);
352
+ filterBuilder.value.highlightedSeatColor = renderer.c.highlightedSeatColor;
353
+ renderer.c.logFullError = true;
354
+ renderer.c.includeDetailedAttributes = true;
355
+ renderer.c.renderRowBlocks = true;
356
+ renderer.setFetchAvailabilityData(false);
357
+ renderer.setUrlProxy("/api/cors?url=");
504
358
 
505
359
  try {
506
360
  await renderer.render();
@@ -522,46 +376,18 @@ const updateShownVenue = async () => {
522
376
  if (panzoomInstance?.dispose) panzoomInstance.dispose();
523
377
  const elem = document.getElementById("svg-wrapper");
524
378
  try {
525
- // Ensure SVG has appropriate sizing before initializing panzoom
526
- if (elem && elem.children[0]) {
527
- const svg = elem.children[0];
528
- const isDesktop = window.innerWidth >= 1024;
529
-
530
- // Ensure SVG doesn't exceed container width
531
- svg.style.maxWidth = "100%";
532
- svg.style.height = "auto";
533
- svg.setAttribute("preserveAspectRatio", "xMidYMid meet");
534
-
535
- // Additional desktop constraints to prevent expansion
536
- if (isDesktop) {
537
- svg.style.maxHeight = "100%";
538
- svg.style.width = "100%";
539
- svg.style.objectFit = "contain";
540
- }
541
-
542
- // Initialize panzoom with updated options
543
- panzoomInstance = panzoom(svg, panzoomOptions);
544
-
545
- // Reset to initial position
546
- panzoomInstance.moveTo(0, 0);
547
- panzoomInstance.zoomAbs(0, 0, 1);
548
- }
379
+ panzoomInstance = panzoom(elem.children[0], panzoomOptions);
549
380
  } catch (e) {
550
381
  ui.logger.Error("Couldnt use panzoom", e);
551
382
  }
552
383
  await nextTick();
553
384
  await loadFilter();
554
385
  filterBuilder.value.reload(eventId.value);
555
-
556
- // Re-setup CSS system after reload
557
- setupCSSUpdates();
558
- filterBuilder.value.updateCss();
559
- // doDragSelect();
386
+ doDragSelect();
560
387
  };
561
388
 
562
389
  const loadFilter = async () => {
563
390
  try {
564
- if (!eventId.value) return;
565
391
  const res = await fetch(`/api/filter/load?eventId=${eventId.value}`);
566
392
  const data = await res.json();
567
393
  if (eventId.value) ui.showSuccess("Loaded filter data");
@@ -589,22 +415,16 @@ const saveFilter = async () => {
589
415
  const handleZoom = (zoomEvent) => {
590
416
  // handle zoom reset
591
417
  if (zoomEvent === "r") {
592
- // Reset both zoom and position
593
- panzoomInstance.moveTo(0, 0);
594
- panzoomInstance.zoomAbs(0, 0, 1);
418
+ // reset zoom
419
+ panzoomInstance.smoothZoomAbs(0, 0, 1);
420
+ // reset location
595
421
  return;
596
422
  }
597
423
 
598
- // Get current transform
424
+ // handle zoom in / out
599
425
  const { scale } = panzoomInstance.getTransform();
600
-
601
- // Calculate new scale based on zoom direction
602
- const newScale = zoomEvent ? scale + scale / 3 : scale - scale / 3;
603
-
604
- // Apply zoom with bounds checking
605
- if (newScale >= panzoomOptions.minZoom && newScale <= panzoomOptions.maxZoom) {
606
- panzoomInstance.smoothZoom(0, 0, newScale / scale);
607
- }
426
+ if (zoomEvent) return panzoomInstance.smoothZoomAbs(0, 0, scale + scale / 3);
427
+ else return panzoomInstance.smoothZoomAbs(0, 0, scale - scale / 3);
608
428
  };
609
429
 
610
430
  watch(renderSeats, async () => {
@@ -617,191 +437,15 @@ watch(renderSeats, async () => {
617
437
  })();
618
438
  </script>
619
439
 
620
- <style scoped>
621
- .slider-dim {
622
- width: 60px;
623
- height: 30px;
624
- }
625
-
626
- .compact-filter {
627
- font-size: 0.75rem;
628
- padding: 0.25rem !important;
629
- }
630
-
631
- .compact-filter .handle {
632
- transform: scale(0.8);
633
- }
634
-
635
- /* Enhanced table and filter styling */
636
- .filters-container {
637
- @apply space-y-0;
638
- padding: 4px;
639
- }
640
-
641
- /* Dragging states for better UX */
642
- .sortable-ghost {
643
- @apply opacity-30;
644
- border: 1px solid #44454b;
645
- background-color: rgba(68, 69, 75, 0.1);
646
- }
647
-
648
- .sortable-chosen .drag-handle {
649
- border: 1px solid #44454b;
650
- background-color: rgba(68, 69, 75, 0.2);
651
- }
652
-
653
- .sortable-drag {
654
- @apply z-50 rotate-1 scale-105 transform shadow-xl;
655
- }
656
-
657
- /* Header button styling */
658
- .header-btn {
659
- @apply flex items-center gap-2 rounded-md border px-3 py-2 text-sm font-medium transition-all duration-200 hover:scale-105;
660
- }
661
-
662
- .save-btn {
663
- background-color: rgba(34, 197, 94, 0.2);
664
- border-color: rgba(74, 222, 128, 0.5);
665
- }
666
-
667
- .save-btn:hover {
668
- background-color: rgba(34, 197, 94, 0.3);
669
- }
670
-
671
- .clear-btn {
672
- background-color: rgba(239, 68, 68, 0.2);
673
- border-color: rgba(248, 113, 113, 0.5);
674
- }
675
-
676
- .clear-btn:hover {
677
- background-color: rgba(239, 68, 68, 0.3);
678
- }
679
-
680
- .filter-builder-container {
681
- height: 90vh;
682
- overflow: hidden;
683
- }
684
-
685
- .card-dark {
686
- height: calc(100vh - 175px);
687
- min-height: 500px;
688
- overflow: hidden;
689
- }
690
-
691
- @media (max-width: 768px) {
692
- .filter-builder-container {
693
- overflow: auto;
694
- }
695
-
696
- .card-dark {
697
- height: auto;
698
- overflow: auto;
699
- }
700
- }
701
-
702
- /* Mobile portrait header spacing fixes */
703
- @media (max-width: 640px) {
704
- .filter-builder-container .flex-center {
705
- gap: 0.5rem;
706
- }
707
- }
708
-
709
- /* iPhone landscape and tablet portrait mode fixes */
710
- @media (max-width: 1023px) and (max-height: 768px) {
711
- .filter-builder-container {
712
- height: auto;
713
- min-height: 100vh;
714
- overflow: auto;
715
- }
716
-
717
- .card-dark {
718
- height: auto;
719
- min-height: 500px;
720
- overflow: auto;
721
- }
722
- }
723
-
724
- /* Desktop: enforce grid column constraints */
725
- @media (min-width: 1024px) {
726
- .lg\\:grid-cols-5 > .lg\\:col-span-3 {
727
- max-width: 60%; /* 3/5 of grid */
728
- flex-shrink: 0;
729
- }
730
- }
731
-
732
- .svg-container {
733
- min-height: 300px;
734
- height: 100%;
735
- width: 100%;
736
- position: relative;
737
- overflow: auto;
738
- }
739
-
740
- /* Desktop: constrain svg-container to prevent expansion */
741
- @media (min-width: 1024px) {
742
- .svg-container {
743
- position: relative;
744
- max-width: 100%;
745
- max-height: 100%;
746
- overflow: hidden;
747
- }
748
- }
749
- </style>
750
-
751
440
  <style>
752
- /* Global styles needed for dynamically injected SVG content and Filter.js CSS injection */
753
- .svg-wrapper {
754
- position: relative;
755
- transform-origin: 0 0;
756
- min-width: 100%;
757
- min-height: 100%;
758
- }
759
-
760
- /* Desktop constraints - prevent svg-wrapper from expanding beyond container */
761
- @media (min-width: 1024px) {
762
- .svg-wrapper {
763
- position: absolute;
764
- top: 0;
765
- left: 0;
766
- width: 100%;
767
- height: 100%;
768
- max-width: 100%;
769
- max-height: 100%;
770
- min-width: unset;
771
- min-height: unset;
772
- overflow: hidden;
773
- }
774
- }
775
-
776
441
  .svg-wrapper > svg {
777
- width: 100%;
778
- height: auto;
779
442
  background: center center no-repeat;
443
+ overflow: hidden;
780
444
  background-size: contain;
781
445
  }
782
446
 
783
- /* Desktop SVG constraints */
784
- @media (min-width: 1024px) {
785
- .svg-wrapper > svg {
786
- max-width: 100%;
787
- max-height: 100%;
788
- }
789
- }
790
-
791
- /* Ensure Filter.js generated CSS works properly */
792
- .svg-wrapper path {
793
- pointer-events: auto;
794
- cursor: pointer;
795
- }
796
-
797
- .svg-wrapper path[generaladmission] {
798
- pointer-events: auto;
799
- cursor: pointer;
800
- }
801
-
802
- /* Hover effects for general admission seats */
803
- .svg-wrapper path[generaladmission]:hover {
804
- pointer-events: all;
805
- cursor: pointer;
447
+ .slider-dim {
448
+ width: 60px;
449
+ height: 30px;
806
450
  }
807
451
  </style>