@necrolab/dashboard 0.4.56 → 0.4.58

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/README.md CHANGED
@@ -1,4 +1,8 @@
1
- # 🎫 Necro Dashboard
1
+ <div align="center">
2
+ <img src="./artwork/image.png" alt="Dashboard" width="400">
3
+ </div>
4
+
5
+ # 🎫 Dashboard
2
6
 
3
7
  A sophisticated PWA dashboard for managing ticket purchasing automation across multiple platforms. Built with Vue 3 and real-time WebSocket communication.
4
8
 
@@ -20,34 +24,32 @@ Visit `http://localhost:5173` and you're live.
20
24
  | `npm run build` | Production build with PWA |
21
25
  | `npm run lint` | Code formatting |
22
26
 
23
- **Frontend Stack**
27
+ ### 🧱 Frontend Stack
24
28
 
25
- - Vue 3 + Composition API
26
- - Pinia state management
27
- - TailwindCSS + SCSS
28
- - PWA with offline support
29
+ - Vue 3 + Composition API
30
+ - Pinia state management
31
+ - TailwindCSS + SCSS
32
+ - PWA with offline support
29
33
 
30
- **Backend Stack**
34
+ ### 🛠 Backend Stack
31
35
 
32
- - Express.js with WebSockets
33
- - MessagePack serialization for big socket messages
34
- - Real-time message batching
35
- - Platform abstraction layer
36
+ - Express.js with WebSockets
37
+ - MessagePack serialization
38
+ - Real-time message batching
39
+ - Platform abstraction layer
36
40
 
37
41
  ## 📱 PWA Support
38
42
 
39
- Built as a Progressive Web App with:
40
-
41
- - 📲 Install to home screen
42
- - 🔄 Background sync
43
- - 📶 Offline functionality
44
- - 🎨 Native app feel
43
+ - 📲 Install to home screen
44
+ - 🔄 Background sync
45
+ - 📶 Offline functionality
46
+ - 🎨 Native app feel
45
47
 
46
48
  ## 🛠️ Development
47
49
 
48
- The app runs in mock mode by default for development. Backend simulates bot operations while frontend provides full UI functionality.
50
+ Runs in mock mode by default. Backend simulates bot operations; frontend provides full UI functionality.
49
51
 
50
- **File Structure**
52
+ ### 🗂️ File Structure
51
53
 
52
54
  ```
53
55
  src/
@@ -61,5 +63,3 @@ backend/
61
63
  ├─ mock-data.js # Development data
62
64
  └─ endpoints.js # API route definitions
63
65
  ```
64
-
65
- ---
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@necrolab/dashboard",
3
- "version": "0.4.56",
3
+ "version": "0.4.58",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": "rm -rf dist && npx workbox-cli generateSW workbox-config.cjs && vite build",
@@ -15,7 +15,7 @@
15
15
  "dependencies": {
16
16
  "@faker-js/faker": "^7.6.0",
17
17
  "@msgpack/msgpack": "^3.0.0-beta2",
18
- "@necrolab/tm-renderer": "^0.1.11",
18
+ "@necrolab/tm-renderer": "^0.1.12",
19
19
  "@vitejs/plugin-vue": "^5.2.1",
20
20
  "@vueuse/core": "^11.3.0",
21
21
  "autoprefixer": "^10.4.21",
@@ -251,7 +251,7 @@ const baseTask = ref({
251
251
  const task = ref(ui.modalData[`task_${ui.currentCountry.siteId}`] || baseTask);
252
252
 
253
253
  function createTask() {
254
- ui.logger.Info("Created new task", task.value);
254
+ ui.logger.Info("Created new task", task.value.taskId);
255
255
  const eventIds = task.value.eventId.split(",").map((t) => t.trim());
256
256
  const presaleCodes = task.value.presaleCode ? task.value.presaleCode.split(",").map((t) => t.trim()) : [undefined];
257
257
  eventIds.forEach((eventId) => {
@@ -380,7 +380,7 @@ baseTask.value.loginAfterCart = isEU(ui.currentCountry.siteId);
380
380
  const task = ref(ui.modalData[`task_${ui.currentCountry.siteId}`] || baseTask);
381
381
 
382
382
  function createTask() {
383
- ui.logger.Info("Created new task", task.value);
383
+ ui.logger.Info("Created new task", task.value.taskId);
384
384
  const eventIds = task.value.eventId.split(",").map((t) => t.trim());
385
385
  const presaleCodes = task.value.presaleCode ? task.value.presaleCode.split(",").map((t) => t.trim()) : [undefined];
386
386
  eventIds.forEach((eventId) => {
@@ -504,7 +504,7 @@ const openInBrowser = (debug) => {
504
504
  if (!props.task.openerLink) return;
505
505
  ui.showSuccess(`Opening in browser ${debug ? "(debug)" : ""}`);
506
506
  const input = props.task.openerLink;
507
- const data = JSON.parse(atob(input.split("://")[1]));
507
+ const data = JSON.parse(atob(input.split("://").pop()));
508
508
  data.config.debug = debug;
509
509
  const out = "necro://" + btoa(JSON.stringify(data));
510
510
  openInNewTab(out);
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { register } from "register-service-worker";
4
4
 
5
- if (process.env.NODE_ENV === "production") {
5
+ if (process.env.NODE_ENV != "development") {
6
6
  register(`/sw.js`, {
7
7
  ready() {
8
8
  console.log(
package/src/stores/ui.js CHANGED
@@ -546,8 +546,7 @@ export const useUIStore = defineStore("ui", () => {
546
546
  // Event
547
547
  currentEvent,
548
548
  setCurrentEvent: (event) => {
549
- console.log(event);
550
- logger.Info("Setting current event", event);
549
+ if (event) logger.Info("Setting current event to:", event);
551
550
  currentEvent.value = event?.match(/\s\((.*?)\)$/)?.[1] || "";
552
551
  localStorage.setItem("current-event", currentEvent.value);
553
552
  refreshQueueStats();
@@ -398,7 +398,7 @@ const parseTmEventUrl = (url) => {
398
398
  };
399
399
  };
400
400
 
401
- const isEU = (siteId) => !["US", "AU", "UK", "LN", "CA", "NZ", "IE"].includes(siteId.replaceAll("TM_", ""));
401
+ const isEU = (siteId) => !["US", "AU", "UK", "CA", "NZ", "IE"].includes(siteId.split("_")?.[1] || siteId);
402
402
 
403
403
  const removeDuplicates = (arr) => [...new Set(arr)];
404
404
 
@@ -1,15 +1,15 @@
1
1
  <template>
2
2
  <div>
3
- <h4 class="text-white font-bold mb-2 pt-5 flex gap-2 text-sm items-center lg:mt-1">
3
+ <h4 class="mb-2 flex items-center gap-2 pt-5 text-sm font-bold text-white lg:mt-1">
4
4
  Console
5
5
  <ConsoleIcon />
6
6
  </h4>
7
7
 
8
8
  <div>
9
- <div class="flex items-center justify-between mb-3">
9
+ <div class="mb-3 flex items-center justify-between">
10
10
  <div class="w-64">
11
11
  <Dropdown
12
- class="console-dropdown input-default w-64 bg-dark-500 border-2 border-dark-550"
12
+ class="console-dropdown input-default w-64 border-2 border-dark-550 bg-dark-500"
13
13
  rightAmount="right-2"
14
14
  default="All logs"
15
15
  :allowDefault="true"
@@ -23,45 +23,45 @@
23
23
  </div>
24
24
  <div class="flex items-center gap-3">
25
25
  <!-- Hide Monitors and Auto buttons only on desktop -->
26
- <div class="hidden md:flex items-center gap-3">
27
- <button class="flex rounded gap-3 card-dark px-2 h-10 flex-center">
26
+ <div class="hidden items-center gap-3 md:flex">
27
+ <button class="card-dark flex-center flex h-10 gap-3 rounded px-2">
28
28
  <h3 class="text-sm text-white">Hide Monitors</h3>
29
29
  <Switch class="scale-75" v-model="filteredLogs" />
30
30
  </button>
31
- <button class="flex rounded gap-3 card-dark px-2 h-10 flex-center relative">
31
+ <button class="card-dark flex-center relative flex h-10 gap-3 rounded px-2">
32
32
  <h3 class="text-sm text-white">Auto</h3>
33
33
  <Switch class="scale-75" v-model="autoscrollToggled" @change="onAutoscrollToggle" />
34
34
  <div
35
35
  v-if="userScrolledUp && autoscrollToggled"
36
- class="absolute -top-1 -right-1 w-2 h-2 bg-yellow-500 rounded-full animate-pulse"
36
+ class="absolute -right-1 -top-1 h-2 w-2 animate-pulse rounded-full bg-yellow-500"
37
37
  title="Autoscroll paused - scroll to bottom to resume"></div>
38
38
  </button>
39
39
  </div>
40
40
  <!-- Scroll buttons always visible -->
41
41
  <button
42
- class="rounded card-dark w-10 h-10 flex-center hover:bg-dark-300 active:bg-dark-200 transition-colors duration-150"
42
+ class="card-dark flex-center h-10 w-10 rounded transition-colors duration-150 hover:bg-dark-300 active:bg-dark-200"
43
43
  @mousedown="startScrolling('up')"
44
44
  @mouseup="stopScrolling"
45
45
  @mouseleave="stopScrolling"
46
46
  @touchstart="startScrolling('up')"
47
47
  @touchend="stopScrolling">
48
- <UpIcon class="w-5 h-5 pointer-events-none" />
48
+ <UpIcon class="pointer-events-none h-5 w-5" />
49
49
  </button>
50
50
  <button
51
- class="rounded card-dark w-10 h-10 flex-center hover:bg-dark-300 active:bg-dark-200 transition-colors duration-150"
51
+ class="card-dark flex-center h-10 w-10 rounded transition-colors duration-150 hover:bg-dark-300 active:bg-dark-200"
52
52
  @mousedown="startScrolling('down')"
53
53
  @mouseup="stopScrolling"
54
54
  @mouseleave="stopScrolling"
55
55
  @touchstart="startScrolling('down')"
56
56
  @touchend="stopScrolling">
57
- <DownIcon class="w-5 h-5 pointer-events-none" />
57
+ <DownIcon class="pointer-events-none h-5 w-5" />
58
58
  </button>
59
59
  </div>
60
60
  </div>
61
61
 
62
62
  <Smoothie
63
63
  :weight="0.2"
64
- class="console overflow-y-auto overflow-x-hidden font-mono text-white scrollable smooth-scroll"
64
+ class="console scrollable smooth-scroll overflow-y-auto overflow-x-hidden font-mono text-white"
65
65
  style="min-height: 12rem !important"
66
66
  ref="$autoscroll"
67
67
  @wheel.stop
@@ -74,10 +74,10 @@
74
74
  : logLines.filter((l) => (filteredLogs ? !['-DISCORD'].some((s) => l.includes(s)) : true))
75
75
  ).length === 0
76
76
  "
77
- class="flex flex-col items-center justify-center h-full empty-state text-center">
78
- <ConsoleIcon class="w-12 h-12 text-dark-400 mb-3 opacity-50" />
79
- <p class="text-light-400 text-sm">No logs yet</p>
80
- <p class="text-light-500 text-xs mt-1">Logs will appear here in real time</p>
77
+ class="empty-state flex h-full flex-col items-center justify-center text-center">
78
+ <ConsoleIcon class="mb-3 h-12 w-12 text-dark-400 opacity-50" />
79
+ <p class="text-sm text-light-400">No logs yet</p>
80
+ <p class="mt-1 text-xs text-light-500">Logs will appear here in real time</p>
81
81
  </div>
82
82
  <pre
83
83
  v-else
@@ -88,17 +88,17 @@
88
88
  v-bind:key="`log-${index}`"
89
89
  :style="{ '--index': index }"><code class="md:text-sm lg:text-base" v-html="line"></code></pre>
90
90
  </Smoothie>
91
- <div class="flex md:hidden justify-between mt-3">
92
- <button class="flex rounded gap-3 card-dark px-2 h-10 flex-center">
91
+ <div class="mt-3 flex justify-between md:hidden">
92
+ <button class="card-dark flex-center flex h-10 gap-3 rounded px-2">
93
93
  <h3 class="text-sm text-white">Hide Monitors</h3>
94
94
  <Switch class="scale-75" v-model="filteredLogs" />
95
95
  </button>
96
- <button class="flex rounded gap-3 card-dark px-2 h-10 flex-center relative">
96
+ <button class="card-dark flex-center relative flex h-10 gap-3 rounded px-2">
97
97
  <h3 class="text-sm text-white">Auto</h3>
98
98
  <Switch class="scale-75" v-model="autoscrollToggled" @change="onAutoscrollToggle" />
99
99
  <div
100
100
  v-if="userScrolledUp && autoscrollToggled"
101
- class="absolute -top-1 -right-1 w-2 h-2 bg-yellow-500 rounded-full animate-pulse"
101
+ class="absolute -right-1 -top-1 h-2 w-2 animate-pulse rounded-full bg-yellow-500"
102
102
  title="Autoscroll paused - scroll to bottom to resume"></div>
103
103
  </button>
104
104
  </div>
@@ -107,7 +107,7 @@
107
107
  </template>
108
108
  <style lang="scss" scoped>
109
109
  .console {
110
- @apply bg-dark-400 lg:p-5 p-2 rounded relative border-dark-550 border-2;
110
+ @apply relative rounded border-2 border-dark-550 bg-dark-400 p-2 lg:p-5;
111
111
  height: calc(100vh - 14rem);
112
112
 
113
113
  // Enable scrollbars for console
@@ -169,13 +169,20 @@
169
169
  // Empty state styling
170
170
  .empty-state {
171
171
  min-height: 14rem;
172
- font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
172
+ font-family:
173
+ "Inter",
174
+ -apple-system,
175
+ BlinkMacSystemFont,
176
+ "Segoe UI",
177
+ Helvetica,
178
+ Arial,
179
+ sans-serif;
173
180
  }
174
181
 
175
182
  textarea {
176
183
  background: transparent;
177
184
  resize: none;
178
- @apply w-full focus:outline-none text-white;
185
+ @apply w-full text-white focus:outline-none;
179
186
  }
180
187
  }
181
188
 
@@ -1,50 +1,50 @@
1
1
  <template>
2
- <div class="filter-builder-container w-full min-h-0 flex flex-col overflow-hidden">
2
+ <div class="filter-builder-container flex min-h-0 w-full flex-col overflow-hidden">
3
3
  <!-- Heading -->
4
- <div class="flex-between pt-4 pb-1 mt-1">
4
+ <div class="flex-between mt-1 pb-1 pt-4">
5
5
  <div class="flex-center gap-4">
6
- <FilterIcon class="cursor-pointer smooth-hover text-white" />
6
+ <FilterIcon class="smooth-hover cursor-pointer text-white" />
7
7
  <h4 class="text-heading">Filter creator</h4>
8
8
  </div>
9
9
  <div class="flex items-center">
10
10
  <input
11
- class="h-10 text-white text-sm p-2 bg-dark-500 w-48 flex items-center rounded-l rounded-r-none relative border-2 border-dark-550"
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
12
  placeholder="Event ID"
13
13
  v-model="eventId" />
14
14
  <button
15
- class="h-10 text-white text-sm px-3 bg-dark-400 flex items-center rounded-r relative font-medium border-2 border-dark-550 smooth-hover"
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
16
  @click="updateShownVenue">
17
17
  Load
18
18
  </button>
19
19
  </div>
20
20
  </div>
21
21
 
22
- <div class="card-dark mb-2 p-3 overflow-hidden">
23
- <div class="w-full h-full">
22
+ <div class="card-dark mb-2 overflow-hidden p-3">
23
+ <div class="h-full w-full">
24
24
  <!-- Main -->
25
- <div class="grid grid-cols-1 lg:grid-cols-5 gap-3 lg:gap-4 w-full h-full">
25
+ <div class="grid h-full w-full grid-cols-1 gap-3 lg:grid-cols-5 lg:gap-4">
26
26
  <!-- Map -->
27
27
  <div
28
- class="col-span-1 lg:col-span-3 w-full h-full rounded-lg relative flex flex-col lg:max-w-none lg:overflow-hidden">
29
- <div v-if="svg" class="flex items-center mb-1">
30
- <div class="flex items-center justify-between w-20 px-2 text-white font-black text-sm">
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">
31
31
  <span class="cursor-pointer" @click="handleZoom(true)">+</span>
32
32
  <span class="cursor-pointer" @click="handleZoom(false)">-</span>
33
- <ReloadIcon class="cursor-pointer w-4 h-4" @click="handleZoom('r')" />
33
+ <ReloadIcon class="h-4 w-4 cursor-pointer" @click="handleZoom('r')" />
34
34
  </div>
35
35
  </div>
36
- <div class="overflow-hidden selecto-wrapper flex-1">
36
+ <div class="selecto-wrapper flex-1 overflow-hidden">
37
37
  <div
38
38
  v-if="svg"
39
- class="h-full overflow-auto hidden-scrollbars p-2 rounded shadow border-2 border-dark-550 svg-container">
39
+ class="hidden-scrollbars svg-container h-full overflow-auto rounded border-2 border-dark-550 p-2 shadow">
40
40
  <div class="svg-wrapper" id="svg-wrapper" v-html="svg"></div>
41
41
  </div>
42
42
  <div
43
43
  v-else
44
- class="h-full flex items-center justify-center p-2 rounded shadow border-2 border-dark-550 svg-container">
44
+ class="svg-container flex h-full items-center justify-center rounded border-2 border-dark-550 p-2 shadow">
45
45
  <div class="text-center">
46
46
  <svg
47
- class="w-12 h-12 mb-3 mx-auto opacity-50"
47
+ class="mx-auto mb-3 h-12 w-12 opacity-50"
48
48
  viewBox="0 0 19 19"
49
49
  fill="none"
50
50
  xmlns="http://www.w3.org/2000/svg">
@@ -52,24 +52,24 @@
52
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
53
  fill="#F5F5F5" />
54
54
  </svg>
55
- <p class="text-light-400 text-sm">No Map</p>
56
- <p class="text-light-500 text-xs">
55
+ <p class="text-sm text-light-400">No Map</p>
56
+ <p class="text-xs text-light-500">
57
57
  Enter an event ID and click "Load" to display the venue map
58
58
  </p>
59
59
  </div>
60
60
  </div>
61
61
  </div>
62
62
  </div>
63
- <div class="col-span-1 lg:col-span-2 w-full flex flex-col h-full">
64
- <div class="flex justify-between mb-2 items-center text-white">
65
- <div class="rounded flex gap-2 items-center" v-if="hasLoaded">
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">
66
66
  <PriceSortToggle
67
67
  :current="filterBuilder.globalFilter.priceSort"
68
68
  class="smooth-hover"
69
69
  @change="(e) => filterBuilder.updateGlobalFilter({ priceSort: e })" />
70
70
  </div>
71
71
  <div class="flex items-center gap-1" v-if="hasLoaded">
72
- <SavingsIcon class="w-4 h-4 text-light-400" />
72
+ <SavingsIcon class="h-4 w-4 text-light-400" />
73
73
  <label class="text-sm text-light-400">Max Price:</label>
74
74
  <input
75
75
  type="number"
@@ -80,34 +80,34 @@
80
80
  maxPrice: parseInt(e.target.value || 0)
81
81
  })
82
82
  "
83
- class="w-14 bg-dark-500 border-2 border-dark-550 px-1 pl-2 h-8 rounded"
83
+ class="h-8 w-14 rounded border-2 border-dark-550 bg-dark-500 px-1 pl-2"
84
84
  placeholder="max" />
85
85
  </div>
86
86
  </div>
87
- <Table class="border-2 border-dark-550 shadow-xl flex-1 flex flex-col">
87
+ <Table class="flex flex-1 flex-col border-2 border-dark-550 shadow-xl">
88
88
  <Header class="flex-shrink-0">
89
- <div class="flex items-center font-bold gap-2 justify-between w-full">
89
+ <div class="flex w-full items-center justify-between gap-2 font-bold">
90
90
  <div class="flex items-center gap-2">
91
91
  <span class="text-base">Filters</span>
92
92
  <span class="text-base text-light-300">{{ filterBuilder.filters.length }}</span>
93
93
  </div>
94
- <div class="flex gap-2 items-center">
94
+ <div class="flex items-center gap-2">
95
95
  <PriceSortToggle
96
- class="w-14 smooth-hover"
96
+ class="smooth-hover w-14"
97
97
  :options="['All', 'WL', 'BL']"
98
98
  @change="(e) => (shownFilters = e)" />
99
99
  <button class="header-btn save-btn" @click="saveFilter">
100
- <EditIcon class="w-4 h-4" />
101
- <span class="lg:block hidden">Save</span>
100
+ <EditIcon class="h-4 w-4" />
101
+ <span class="hidden lg:block">Save</span>
102
102
  </button>
103
103
  <button class="header-btn clear-btn" @click="filterBuilder.reset(false)">
104
- <TrashIcon class="w-4 h-4" />
105
- <span class="lg:block hidden">Clear</span>
104
+ <TrashIcon class="h-4 w-4" />
105
+ <span class="hidden lg:block">Clear</span>
106
106
  </button>
107
107
  </div>
108
108
  </div>
109
109
  </Header>
110
- <div class="bg-dark-400 overflow-auto hidden-scrollbars flex-1">
110
+ <div class="hidden-scrollbars flex-1 overflow-auto bg-dark-400">
111
111
  <div v-if="filterBuilder.filters.length" class="filters-container">
112
112
  <draggable
113
113
  :list="filterBuilder.filters"
@@ -138,29 +138,29 @@
138
138
  <div
139
139
  v-else
140
140
  class="empty-state flex flex-col items-center justify-center py-8 text-center">
141
- <FilterIcon class="w-12 h-12 text-dark-400 mb-3 opacity-50" />
142
- <p class="text-light-400 text-sm">No filters yet</p>
143
- <p class="text-light-500 text-xs mt-1">Click on the map to create filters</p>
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
144
  </div>
145
145
  </div>
146
146
  </Table>
147
- <div class="flex items-center justify-between text-white gap-2 mt-1 mb-2 md:mb-0 flex-shrink-0">
147
+ <div class="mb-2 mt-1 flex flex-shrink-0 items-center justify-between gap-2 text-white md:mb-0">
148
148
  <button
149
149
  @click="addWildcardFilter"
150
150
  :disabled="hasWildcardFilter"
151
151
  :class="[
152
- 'border-2 rounded border-dark-550 px-2 h-7 text-xs bg-dark-500 overflow-hidden shadow transition-all duration-200',
152
+ 'h-7 overflow-hidden rounded border-2 border-dark-550 bg-dark-500 px-2 text-xs shadow transition-all duration-200',
153
153
  hasWildcardFilter
154
- ? 'text-gray opacity-50 cursor-not-allowed'
155
- : 'text-gray smooth-hover hover:bg-dark-400 hover:border-light-300'
154
+ ? 'text-gray cursor-not-allowed opacity-50'
155
+ : 'text-gray smooth-hover hover:border-light-300 hover:bg-dark-400'
156
156
  ]"
157
157
  :title="hasWildcardFilter ? 'Wildcard filter already exists' : 'Add wildcard filter'">
158
158
  * Wildcard
159
159
  </button>
160
160
  <button
161
161
  @click="ui.toggleModal('preview-filter')"
162
- class="border-2 gap-1 flex justify-between items-center rounded border-dark-550 px-2 h-7 text-gray text-xs bg-dark-500 overflow-hidden shadow smooth-hover">
163
- <CameraIcon class="w-3 h-3" />
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" />
164
164
  JSON
165
165
  </button>
166
166
  </div>
@@ -491,12 +491,16 @@ const updateShownVenue = async () => {
491
491
  return;
492
492
  }
493
493
 
494
- renderer = rendererFactory.createRenderer(eventId.value, "", country);
495
- filterBuilder.value.highlightedSeatColor = renderer.c.highlightedSeatColor;
496
- renderer.c.logFullError = true;
497
- renderer.c.includeDetailedAttributes = true;
498
- renderer.c.renderRowBlocks = true;
499
- //renderer.setUrlProxy("/api/cors?url=");
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
+ });
500
504
 
501
505
  try {
502
506
  await renderer.render();
@@ -647,12 +651,12 @@ watch(renderSeats, async () => {
647
651
  }
648
652
 
649
653
  .sortable-drag {
650
- @apply shadow-xl transform rotate-1 scale-105 z-50;
654
+ @apply z-50 rotate-1 scale-105 transform shadow-xl;
651
655
  }
652
656
 
653
657
  /* Header button styling */
654
658
  .header-btn {
655
- @apply flex items-center gap-2 px-3 py-2 border rounded-md font-medium text-sm transition-all duration-200 hover:scale-105;
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;
656
660
  }
657
661
 
658
662
  .save-btn {
@@ -209,7 +209,7 @@ const uniqEventIds = computed(() => {
209
209
  const ids = [
210
210
  ...new Set(
211
211
  Object.values(ui.tasks)
212
- .filter((t) => t.siteId === ui.currentCountry.siteId && !t.eventId.includes("@"))
212
+ .filter((t) => t.siteId === ui.currentCountry.siteId && !t.eventId?.includes("@"))
213
213
  .map((v) => `${v.eventName} (${v.eventId})`)
214
214
  ),
215
215
  ];
@@ -0,0 +1,41 @@
1
+ #!/bin/bash
2
+
3
+ # USAGE: ./git-force-switch.sh target-branch-name
4
+
5
+ set -e
6
+
7
+ TARGET_BRANCH="$1"
8
+
9
+ if [ -z "$TARGET_BRANCH" ]; then
10
+ echo "❌ Usage: $0 <branch-name>"
11
+ exit 1
12
+ fi
13
+
14
+ echo "🔧 Forcing checkout to '$TARGET_BRANCH'..."
15
+
16
+ # Step 1: Make sure Git respects case differences
17
+ git config core.ignoreCase false
18
+
19
+ # Step 2: Try checkout to see if it fails and capture errors
20
+ ERROR_OUTPUT=$(git checkout "$TARGET_BRANCH" 2>&1) || true
21
+
22
+ # Step 3: Check if it failed due to untracked files
23
+ if echo "$ERROR_OUTPUT" | grep -q "would be overwritten by checkout"; then
24
+ echo "⚠️ Conflicting untracked files detected. Cleaning them up..."
25
+
26
+ # Extract file paths and delete them
27
+ echo "$ERROR_OUTPUT" |
28
+ grep "^\s" |
29
+ sed 's/^[ \t]*//' |
30
+ while read -r FILE; do
31
+ if [ -f "$FILE" ] || [ -d "$FILE" ]; then
32
+ echo "🗑️ Removing $FILE"
33
+ rm -rf "$FILE"
34
+ fi
35
+ done
36
+
37
+ echo "🔁 Retrying checkout..."
38
+ git checkout "$TARGET_BRANCH"
39
+ else
40
+ echo "✅ Switched to $TARGET_BRANCH successfully."
41
+ fi
@@ -1,60 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(npm run lint)",
5
- "Bash(find:*)",
6
- "Bash(ls:*)",
7
- "Bash(grep:*)",
8
- "Bash(npm run build:*)",
9
- "Bash(npm view:*)",
10
- "Bash(npm run dev:*)",
11
- "Bash(sudo rm:*)",
12
- "Bash(rm:*)",
13
- "Bash(rg:*)",
14
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -n \"const DEBUG\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/views/)",
15
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -n \"border-2 border-dark-550\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/)",
16
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -n \"bg-dark-500\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/)",
17
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -n \"flex items-center justify-center\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/)",
18
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -n \"col-span.*flex.*items-center.*justify-center\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/)",
19
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -n \"button.*flex.*items-center.*justify-center.*gap\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/)",
20
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -n \"import.*useUIStore\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/)",
21
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -n \"const.*ref\\(\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/)",
22
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -n \"button-default.*bg-dark-400.*w-48.*text-xs.*flex.*items-center.*justify-center.*gap-x-2\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/)",
23
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -n \"const DEBUG.*window\\.location\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/)",
24
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -A5 -B5 \"input-incrementer\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/components/Tasks/CreateTaskTM.vue)",
25
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -A2 -B2 \"label-override\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/assets/css/main.scss)",
26
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -n \"MultiDropdown\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/)",
27
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -n \"ant-select.*multiple\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/)",
28
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -n \"multiple.*true\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/)",
29
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -n \"a-select\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/)",
30
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -n \"Select\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/)",
31
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -A5 -B5 \"mode.*multiple\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/components/Tasks/)",
32
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -A10 -B5 \"a-select\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/components/Tasks/CreateTaskTM.vue)",
33
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -n \"CountryChooser\\|currentCountry\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/views/Tasks.vue)",
34
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -n \"currentModule\\|CountryChooser\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/components/ui/Navbar.vue)",
35
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -l \"CountryChooser\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/)",
36
- "Bash(/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/arm64-darwin/rg -n \"bg-dark-400.*button\\|button.*bg-dark-400\" /Users/luca/Documents/GitHub/Necro/Dashboard/src/views/Tasks.vue)",
37
- "Bash(npm install:*)",
38
- "Bash(sudo chown:*)",
39
- "Bash(sudo npm install:*)",
40
- "Bash(node:*)",
41
- "Bash(git checkout:*)",
42
- "Bash(npx vue-tsc:*)",
43
- "Bash(npx vite build:*)",
44
- "Bash(npx eslint:*)",
45
- "Bash(cp:*)",
46
- "Bash(sed:*)",
47
- "Bash(true)",
48
- "Bash(./run build)",
49
- "Bash(mv:*)",
50
- "Bash(chown:*)",
51
- "Bash(chmod:*)",
52
- "Bash(timeout 10s npm run dev)",
53
- "Bash(gtimeout:*)",
54
- "Bash(echo $SHELL)",
55
- "Bash(mkdir:*)",
56
- "Bash(base64:*)"
57
- ],
58
- "deny": []
59
- }
60
- }