@necrolab/dashboard 0.4.61 → 0.4.209

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 +2 -64
  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 +76 -12
  8. package/backend/index.js +2 -2
  9. package/backend/mock-data.js +36 -27
  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 +8 -79
  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 +135 -377
  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 +37 -170
  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 +201 -173
  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
@@ -4,31 +4,27 @@ const log = (...args) => debug && console.log("[filter]", ...args);
4
4
  const colors = {
5
5
  HIGHLIGHT: "#d3f8e2",
6
6
  SELECTED: "#a9def9",
7
- SELECTED_EXPANDED: "#6bb6ff",
8
7
  EXCLUDED: "#f694c1",
9
- EXCLUDED_EXPANDED: "#f472b6",
10
8
  UNSELECTABLE: "#311432"
11
9
  };
12
10
 
13
- const BASE_CSS = `.svg-wrapper path[generaladmission]:hover {fill: ${colors.HIGHLIGHT};pointer-events:all;cursor:pointer;}`;
14
-
15
- const getAttributeValue = (element, attributeName) => {
16
- try {
17
- return element.attributes[attributeName]?.nodeValue?.trim() || "";
18
- } catch {
19
- return "";
20
- }
21
- };
22
-
23
- const getTextContent = (element) => {
24
- return element.innerHTML?.trim() || "";
25
- };
11
+ const BASE_CSS =
12
+ `path[row]:hover {stroke: ${colors.HIGHLIGHT};pointer-events:all;cursor:pointer;}` +
13
+ `path[generaladmission]:hover {fill: ${colors.HIGHLIGHT};pointer-events:all;cursor:pointer;}`;
26
14
 
27
15
  const isWheelchair = (p) => {
28
16
  if (p.row === "W" || p.sectionName === "W") return false;
29
17
  return [("W", "ADA")].some((part) => p.row !== "W" && (p.row?.includes(part) || p.sectionName?.includes(part)));
30
18
  };
31
19
 
20
+ const getWheelChairRows = () => {
21
+ const rows = [...document.querySelectorAll("path[row]")];
22
+ return rows
23
+ .map((p) => {
24
+ return { row: p.attributes.row.nodeValue, sectionName: p.attributes.sectionname.nodeValue };
25
+ })
26
+ .filter(isWheelchair);
27
+ };
32
28
  const sortAlphaNum = (a, b) => {
33
29
  var reA = /[^a-zA-Z]/g;
34
30
  var reN = /[^0-9]/g;
@@ -97,60 +93,52 @@ const cleanupFilter = (f) => {
97
93
  return ret;
98
94
  }
99
95
 
100
- if (f.rows && document.querySelectorAll(`path[section="${f.section}"]`)?.length === f.rows?.length) delete f.rows;
96
+ if (f.rows && document.querySelectorAll(`path[sectionname="${f.section}"]`)?.length === f.rows?.length)
97
+ delete f.rows;
101
98
  if (f.rows) ret.rows = f.rows.sort(sortAlphaNum);
102
99
 
103
100
  return ret;
104
101
  };
105
102
 
106
- const getSectionNameMapping = () => {
107
- const sectionRealName = {};
108
-
109
- [...document.querySelectorAll("path")].forEach((t) => {
110
- const sec = getAttributeValue(t, "section");
111
- const secName = getAttributeValue(t, "sectionname");
112
- if (sec && secName) {
113
- sectionRealName[sec] = secName;
114
- }
103
+ const getFirstRows = (rows) => {
104
+ const firstRows = {};
105
+ const parsedRows = rows.map((row) => {
106
+ return { sec: row.attributes.sectionname.nodeValue, row: row.attributes.row.nodeValue };
115
107
  });
108
+ // log("getFirstRows: parsedRows=", parsedRows);
109
+ parsedRows.forEach((row) => {
110
+ if (!firstRows[row.sec]) firstRows[row.sec] = [];
111
+ firstRows[row.sec].push(row.row);
112
+ });
113
+ // log("A getFirstRows: firstRows=", firstRows);
114
+ Object.entries(firstRows).forEach(([sec, rows]) => {
115
+ log(`sort:`, sec, rows);
116
+ firstRows[sec] = rows.sort(sortAlphaNum);
117
+ });
118
+ // log("B getFirstRows: firstRows=", firstRows);
119
+ return firstRows;
120
+ };
116
121
 
122
+ const getSectionNameMapping = () => {
123
+ const sectionRealName = {};
117
124
  [...document.querySelectorAll("tspan")].forEach((t) => {
118
- const sec = getAttributeValue(t, "section");
119
- const content = getTextContent(t);
120
-
121
- if (sec && content) {
122
- if (!sectionRealName[sec]) {
123
- sectionRealName[sec] = content;
124
- } else if (!sectionRealName[sec].includes(content)) {
125
- sectionRealName[sec] += " " + content;
126
- }
127
- }
125
+ const sec = t.attributes.section.nodeValue.trim();
126
+ if (!sectionRealName[sec]) sectionRealName[sec] = t.innerHTML.trim();
127
+ else if (!sectionRealName[sec]?.includes(t.innerHTML)) sectionRealName[sec] += " " + t.innerHTML.trim();
128
128
  });
129
129
 
130
130
  log("getSectionNameMapping:", sectionRealName);
131
131
  return sectionRealName;
132
132
  };
133
133
 
134
- const getReverseSectionNameMapping = () => {
135
- const reverseSectionName = {};
136
-
137
- [...document.querySelectorAll("path")].forEach((t) => {
138
- const sec = getAttributeValue(t, "section");
139
- const secName = getAttributeValue(t, "sectionname");
140
-
141
- if (sec && secName) {
142
- if (!reverseSectionName[secName]) {
143
- reverseSectionName[secName] = [];
144
- }
145
- if (!reverseSectionName[secName].includes(sec)) {
146
- reverseSectionName[secName].push(sec);
147
- }
148
- }
149
- });
150
-
151
- log("getReverseSectionNameMapping", reverseSectionName);
152
- return reverseSectionName;
153
- };
134
+ // const getFloorSections = () => {
135
+ // const tspans = [...document.querySelectorAll("tspan")];
136
+ // const blacklist = ["BL", "BARSTOOLS", "BW", "NL"];
137
+ // return tspans
138
+ // .map((t) => t.attributes.section.nodeValue)
139
+ // .filter((t) => t.match(/^[a-zA-Z]+\s?[\w\d]?$/))
140
+ // .filter((name) => !blacklist.some((b) => name.includes(b)));
141
+ // };
154
142
 
155
143
  export default class FilterBuilder {
156
144
  constructor() {
@@ -169,6 +157,7 @@ export default class FilterBuilder {
169
157
  CATCH_ALL_GA: 1,
170
158
  NORMAL: 2,
171
159
  NORMAL_FIRSTROW: 3,
160
+ GLOBAL_FIRSTROW: 4,
172
161
  CATCH_ALL_FLOOR: 5
173
162
  };
174
163
 
@@ -181,9 +170,6 @@ export default class FilterBuilder {
181
170
  this.reverseSectionNameMapping = {};
182
171
  this.getSectionNameMapping = getSectionNameMapping;
183
172
  this.updateHooks = [];
184
- this.unselectable = [];
185
- this.isDragging = false;
186
- this.highlightedSeatColor = "#d3f8e2";
187
173
  }
188
174
 
189
175
  onUpdate(fn) {
@@ -192,8 +178,6 @@ export default class FilterBuilder {
192
178
 
193
179
  setExpandedFilter(f) {
194
180
  this.expandedFilter = f;
195
- this.updateCss();
196
- this.updateHooks.forEach((fn) => fn());
197
181
  }
198
182
 
199
183
  selectFilter(f) {
@@ -210,80 +194,24 @@ export default class FilterBuilder {
210
194
  if (filter.buyAny) return this.filterTypes.CATCH_ALL;
211
195
  else if (filter.floor) return this.filterTypes.CATCH_ALL_FLOOR;
212
196
  else if (filter.generalAdmission) return this.filterTypes.CATCH_ALL_GA;
197
+ // else if (!!filter.section && filter.firstRow) return this.filterTypes.NORMAL_FIRSTROW;
198
+ // else if (!filter.section && filter.firstRow) return this.filterTypes.GLOBAL_FIRSTROW;
213
199
  else if (filter.section) return this.filterTypes.NORMAL;
214
200
  else return this.filterTypes.INVALID;
215
201
  }
216
202
 
217
- addRowHandlers() {
218
- for (let i = 0; i < this.rows.length; i++) {
219
- const row = this.rows[i];
220
- row.onclick = () => {
221
- const parsed = {
222
- section: getAttributeValue(row, "section"),
223
- row: getAttributeValue(row, "row"),
224
- id: getAttributeValue(row, "id")
225
- };
226
-
227
- log(`Adding filter ${parsed.section}`);
228
- const matchingFilter = this.filters.find(
229
- (f) =>
230
- this.isForCurrentEvent(f) &&
231
- f.section === parsed.section &&
232
- (f.rows?.includes(parsed.row) || !Array.isArray(f.rows))
233
- );
234
-
235
- if (matchingFilter) {
236
- log(`Filter for ${parsed.section}/${parsed.row} already exists (${matchingFilter.id})`);
237
- // Toggle expanded state
238
- if (this.expandedFilter === matchingFilter.id) {
239
- this.setExpandedFilter(null);
240
- } else {
241
- this.setExpandedFilter(matchingFilter.id);
242
- }
243
- return;
244
- }
245
-
246
- this.addFilter({
247
- section: parsed.section,
248
- event: this.currentEventId
249
- });
250
- };
251
-
252
- row.onmouseover = () => {
253
- const parsed = {
254
- section: getAttributeValue(row, "section"),
255
- row: getAttributeValue(row, "row"),
256
- id: getAttributeValue(row, "id")
257
- };
258
-
259
- const matchingFilter = this.filters.find(
260
- (f) =>
261
- this.isForCurrentEvent(f) &&
262
- f.section === parsed.section &&
263
- (f.rows?.includes(parsed.row) || !Array.isArray(f.rows))
264
- );
265
-
266
- if (!matchingFilter) return;
267
- if (Array.isArray(matchingFilter.rows))
268
- matchingFilter.rows.forEach((row) => this.highlight({ section: parsed.section, row: row }));
269
- else this.highlight({ section: matchingFilter.section });
270
- };
271
-
272
- row.onmouseout = () => {
273
- if (!this.isDragging) this.clearHighlight();
274
- };
275
- }
276
- }
277
-
278
203
  reload(eventId) {
279
204
  const paths = document.querySelectorAll("path");
280
205
  this.rows = [...paths].filter((r) => r.id.startsWith("s_"));
281
206
 
282
207
  this.addLabelHandlers();
283
- this.addGAHandlers();
284
208
  this.addRowHandlers();
209
+ this.addGAHandlers();
285
210
  this.currentEventId = eventId;
286
211
 
212
+ const wcRows = getWheelChairRows();
213
+ log("wcRows:", wcRows);
214
+ wcRows.forEach((r) => this.makeRowUnselectable(r.sectionName, r.row));
287
215
  this.updateCss();
288
216
  }
289
217
 
@@ -305,18 +233,6 @@ export default class FilterBuilder {
305
233
  addFilter(filter) {
306
234
  if (!this.filters) this.filters = [];
307
235
 
308
- // Check if a filter with the same properties (excluding id) already exists
309
- const existingFilter = this.filters.find((f) => {
310
- const n = { ...f };
311
- delete n.id;
312
- return JSON.stringify(n) === JSON.stringify(filter);
313
- });
314
-
315
- if (existingFilter) {
316
- console.log("Filter already exists:", existingFilter);
317
- return; // Don't add the filter if it already exists
318
- }
319
-
320
236
  if (this.expandedFilter) {
321
237
  const parent = this.filters.find((f) => f.event === filter.event && f.section === filter.section);
322
238
  if (parent) return this.addFilterToParent(filter, parent);
@@ -357,7 +273,23 @@ export default class FilterBuilder {
357
273
  this.rows = [];
358
274
  if (hard) this.unselectable = [];
359
275
  this.updateCss();
360
- this.updateHooks.forEach((fn) => fn());
276
+ }
277
+
278
+ selectFirstRow() {
279
+ const paths = document.querySelectorAll("path");
280
+ const rows = [...paths].filter((r) => r.id.startsWith("s_"));
281
+ const firstRows = getFirstRows(rows);
282
+
283
+ Object.entries(firstRows).forEach(([sec, rows]) => {
284
+ if (this.filters.some((f) => f.section === sec && f.rows.includes(rows[0]) && this.isForCurrentEvent(f))) {
285
+ log(`Filter for ${sec}/${rows[0]} already exists`);
286
+ return;
287
+ }
288
+ if (this.isUnselectable(sec, rows[0])) return;
289
+ this.addFilter({ event: this.currentEventId, section: sec, rows: [rows[0]] });
290
+ });
291
+
292
+ this.updateCss();
361
293
  }
362
294
 
363
295
  updateCss() {
@@ -368,9 +300,9 @@ export default class FilterBuilder {
368
300
  if (!this.isForCurrentEvent(filter)) return;
369
301
 
370
302
  const tryFindRealName = Object.entries(gaSectionNameMapping).find(
371
- ([, v]) => v === filter.section && v.includes(" ")
303
+ // eslint-disable-next-line no-unused-vars
304
+ ([_, v]) => v === filter.section && v.includes(" ")
372
305
  )?.[0];
373
-
374
306
  if (tryFindRealName) {
375
307
  log(`Real name for ${filter.section}: ${tryFindRealName}`);
376
308
  } else {
@@ -379,44 +311,33 @@ export default class FilterBuilder {
379
311
 
380
312
  const type = this.getFilterType(filter);
381
313
 
382
- let color;
383
- if (filter.exclude) {
384
- color = this.expandedFilter === filter.id ? colors.EXCLUDED_EXPANDED : colors.EXCLUDED;
385
- } else {
386
- color = this.expandedFilter === filter.id ? colors.SELECTED_EXPANDED : colors.SELECTED;
387
- }
388
-
314
+ let color = filter.exclude ? colors.EXCLUDED : colors.SELECTED;
315
+ if (this.expandedFilter === filter.id) color = colors.HIGHLIGHT;
389
316
  switch (type) {
390
317
  case this.filterTypes.NORMAL:
391
318
  // If it has no 'rows' property
392
319
  if (!Array.isArray(filter.rows)) {
393
- const isGA = document.querySelectorAll(`path[section='${filter.section}']`)?.length === 0;
320
+ const isGA = document.querySelectorAll(`path[sectionname='${filter.section}']`)?.length === 0;
394
321
 
395
- if (isGA && tryFindRealName) {
396
- this.cssClasses += `.svg-wrapper path[name="${tryFindRealName}"] {fill: ${color} !important;}\n`;
397
- } else {
398
- this.cssClasses += `.svg-wrapper path[section="${filter.section}"] {stroke: ${color} !important;}\n`;
399
- }
322
+ if (isGA) this.cssClasses += `path[name="${tryFindRealName}"] {fill: ${color} !important}`;
323
+ else this.cssClasses += `path[sectionname="${filter.section}"] {stroke: ${color} !important}`;
400
324
  } else {
401
325
  this.cssClasses += filter.rows
402
326
  .map(
403
- (row) => `.svg-wrapper path[section="${filter.section}"][row="${row}"] {stroke: ${color} !important;}`
327
+ (row) =>
328
+ `path[sectionname="${filter.section}"][row="${row}"] {stroke: ${color} !important}`
404
329
  )
405
- .join("\n") + "\n";
330
+ ?.join("\n");
406
331
  }
407
- break;
408
332
 
409
- case this.filterTypes.CATCH_ALL:
410
- // Scope to SVG paths only to prevent global CSS leak
411
- this.cssClasses += `.svg-wrapper path {stroke: ${color} !important;}\n`;
412
333
  break;
413
334
 
414
335
  case this.filterTypes.CATCH_ALL_GA:
415
- this.cssClasses += `.svg-wrapper path[generaladmission] {fill: ${color} !important;}\n`;
336
+ // this.cssClasses += `path[generaladmission] {fill: ${color} !important}`;
416
337
  break;
417
338
 
418
339
  case this.filterTypes.CATCH_ALL_FLOOR:
419
- // this.cssClasses += floors.map((f) => `path[name="${f}"] {fill: ${color} !important;}`).join("\n") + "\n";
340
+ // this.cssClasses += floors.map((f) => `path[name="${f}"] {fill: ${color} !important}`).join("\n");
420
341
  break;
421
342
 
422
343
  case this.filterTypes.INVALID:
@@ -427,13 +348,12 @@ export default class FilterBuilder {
427
348
  });
428
349
 
429
350
  this.unselectable.forEach((unselectable) => {
430
- const [section, row] = unselectable.split("/");
431
- log("UpdateCSS: adding unselectable", section, row);
351
+ const [sectionName, row] = unselectable.split("/");
352
+ log("UpdateCSS: adding unselectable", sectionName, row);
432
353
  const color = colors.UNSELECTABLE;
433
- this.cssClasses += `.svg-wrapper path[section="${section}"][row="${row}"] {stroke: ${color} !important;}\n`;
354
+ this.cssClasses += `path[sectionname="${sectionName}"][row="${row}"] {stroke: ${color} !important} `;
434
355
  });
435
-
436
- log("Generated CSS:", this.cssClasses);
356
+ log(this.cssClasses);
437
357
  }
438
358
 
439
359
  addLabelHandlers() {
@@ -443,7 +363,7 @@ export default class FilterBuilder {
443
363
 
444
364
  for (let i = 0; i < labels.length; i++) {
445
365
  const label = labels[i];
446
- const section = getAttributeValue(label, "section");
366
+ const section = label.attributes.section.nodeValue;
447
367
 
448
368
  label.onclick = () => {
449
369
  // filter exists already for section - return
@@ -452,40 +372,33 @@ export default class FilterBuilder {
452
372
  if (f.section !== section && f.section !== gaSectionNameMapping[section]) return false;
453
373
  return true;
454
374
  });
455
-
375
+ // (((f.section === section || f.section === gaSectionNameMapping[section]) && !f.rows) ||
376
+ // (f.section === section &&
377
+ // document.querySelectorAll(`path[sectionname="${section}"]`).length ===
378
+ // f.rows.length)) &&
379
+ // this.isForCurrentEvent(f)
456
380
  if (matchingFilter) {
457
381
  log(`Filter for ${section} already exists (${matchingFilter.id})`);
458
- // Toggle expanded state
459
- if (this.expandedFilter === matchingFilter.id) {
460
- this.setExpandedFilter(null);
461
- } else {
462
- this.setExpandedFilter(matchingFilter.id);
463
- }
382
+ if (this.expandedFilter === matchingFilter.id) this.setExpandedFilter(null);
383
+ else this.setExpandedFilter(matchingFilter.id);
384
+ this.updateCss();
464
385
  return;
465
386
  }
466
387
 
467
388
  if (isWheelchair({ section: gaSectionNameMapping[section] || section })) return;
468
389
 
469
- const isGA = document.querySelectorAll(`path[sectionname="${section}"][row]`).length === 0;
390
+ const isGA = document.querySelectorAll(`path[sectionname="${section.trim()}"][row]`).length === 0;
470
391
 
471
392
  try {
472
393
  if (isGA) {
473
- const realSectionName = gaSectionNameMapping[section];
474
- if (realSectionName) {
475
- this.addFilter({
476
- section: realSectionName,
477
- event: this.currentEventId
478
- });
479
- }
394
+ this.addFilter({
395
+ section: gaSectionNameMapping[section].trim(),
396
+ event: this.currentEventId
397
+ });
480
398
  } else {
481
- const reverseMapping = getReverseSectionNameMapping();
482
- const sections = reverseMapping[section] || [section];
483
- log("labelHandler: non-ga", sections, section);
484
- sections.forEach((s) => {
485
- this.addFilter({
486
- section: s,
487
- event: this.currentEventId
488
- });
399
+ this.addFilter({
400
+ section: section.trim(),
401
+ event: this.currentEventId
489
402
  });
490
403
  }
491
404
  log("Added labelHandler filter", section);
@@ -506,23 +419,21 @@ export default class FilterBuilder {
506
419
 
507
420
  for (let i = 0; i < GASections.length; i++) {
508
421
  const path = GASections[i];
509
- const section = getAttributeValue(path, "name");
422
+ const section = path.attributes.name.nodeValue;
510
423
  const sectionName = gaSectionNameMapping[section];
511
424
 
512
425
  path.onclick = () => {
513
- // filter exists already for section
426
+ // filter exists already for section - return
514
427
  const matchingFilter = this.filters.find((f) => f.section === sectionName && this.isForCurrentEvent(f));
515
428
  if (matchingFilter) {
516
429
  log(`Filter for ${sectionName} (GA) already exists (${matchingFilter.id})`);
517
- // Toggle expanded state
518
- if (this.expandedFilter === matchingFilter.id) {
519
- this.setExpandedFilter(null);
520
- } else {
521
- this.setExpandedFilter(matchingFilter.id);
522
- }
430
+ if (this.expandedFilter === matchingFilter.id) this.setExpandedFilter(null);
431
+ else this.setExpandedFilter(matchingFilter.id);
432
+ this.updateCss();
523
433
  return;
524
434
  }
525
435
  log(`GAHandler: adding filter for ${section} (realname: ${sectionName})`, section, sectionName);
436
+
526
437
  this.addFilter({
527
438
  section: sectionName,
528
439
  event: this.currentEventId
@@ -531,12 +442,67 @@ export default class FilterBuilder {
531
442
  }
532
443
  }
533
444
 
445
+ addRowHandlers() {
446
+ for (let i = 0; i < this.rows.length; i++) {
447
+ const row = this.rows[i];
448
+ row.onclick = () => {
449
+ const parsed = {
450
+ section: row.attributes.sectionname.nodeValue,
451
+ row: row.attributes.row.nodeValue,
452
+ id: row.attributes.id.nodeValue
453
+ };
454
+ log(`Adding filter ${parsed.section}/${parsed.row}`);
455
+ const matchingFilter = this.filters.find(
456
+ (f) =>
457
+ this.isForCurrentEvent(f) &&
458
+ f.section === parsed.section &&
459
+ (f.rows?.includes(parsed.row) || !Array.isArray(f.rows))
460
+ );
461
+ if (matchingFilter) {
462
+ log(`Filter for ${parsed.section}/${parsed.row} already exists (${matchingFilter.id})`);
463
+ if (this.expandedFilter === matchingFilter.id) this.setExpandedFilter(null);
464
+ else this.setExpandedFilter(matchingFilter.id);
465
+ this.updateCss();
466
+ return;
467
+ }
468
+
469
+ if (this.isUnselectable(parsed.section, parsed.row)) return;
470
+
471
+ this.addFilter({
472
+ section: parsed.section,
473
+ rows: [parsed.row],
474
+ event: this.currentEventId
475
+ });
476
+ };
477
+
478
+ row.onmouseover = () => {
479
+ const parsed = {
480
+ section: row.attributes.sectionname.nodeValue,
481
+ row: row.attributes.row.nodeValue,
482
+ id: row.attributes.id.nodeValue
483
+ };
484
+ const matchingFilter = this.filters.find(
485
+ (f) =>
486
+ this.isForCurrentEvent(f) &&
487
+ f.section === parsed.section &&
488
+ (f.rows?.includes(parsed.row) || !Array.isArray(f.rows))
489
+ );
490
+ if (!matchingFilter) return;
491
+ if (Array.isArray(matchingFilter.rows))
492
+ matchingFilter.rows.forEach((row) => this.highlight({ section: parsed.section, row: row }));
493
+ else this.highlight({ section: matchingFilter.section });
494
+ };
495
+ row.onmouseout = () => {
496
+ if (!this.isDragging) this.clearHighlight();
497
+ };
498
+ }
499
+ }
500
+
534
501
  deleteFilterById(id) {
535
502
  this.filters = this.filters.filter((f) => f.id !== id);
536
503
  // this.updateHooks = this.updateHooks.filter((i) => id !== i);
537
504
  if (this.expandedFilter === id) this.expandedFilter = "";
538
505
  this.updateCss();
539
- this.updateHooks.forEach((fn) => fn());
540
506
  }
541
507
 
542
508
  replaceById(id, filter) {
@@ -554,7 +520,6 @@ export default class FilterBuilder {
554
520
  id: this.filters[index].id
555
521
  };
556
522
  this.updateCss();
557
- this.updateHooks.forEach((fn) => fn());
558
523
  return this.filters[index];
559
524
  }
560
525
 
@@ -574,11 +539,11 @@ export default class FilterBuilder {
574
539
 
575
540
  if (isGA) {
576
541
  if (this.filters.find((f) => f.section === filter.name)) return;
577
- newCSS += `.svg-wrapper path[name="${filter.section || filter.name}"] {fill: ${colors.HIGHLIGHT} !important}`;
542
+ newCSS += `path[name="${filter.section || filter.name}"] {fill: ${colors.HIGHLIGHT} !important}`;
578
543
  } else if (filter.row) {
579
- newCSS += `.svg-wrapper path[section="${filter.section}"][row="${filter.row}"] {stroke: ${colors.HIGHLIGHT} !important}`;
544
+ newCSS += `path[sectionname="${filter.section}"][row="${filter.row}"] {stroke: ${colors.HIGHLIGHT} !important}`;
580
545
  } else {
581
- newCSS += `.svg-wrapper path[section="${filter.section}"] {stroke: ${colors.HIGHLIGHT} !important}`;
546
+ newCSS += `path[sectionname="${filter.section}"] {stroke: ${colors.HIGHLIGHT} !important}`;
582
547
  }
583
548
  this.temporaryCSS += newCSS;
584
549
  }
@@ -587,6 +552,11 @@ export default class FilterBuilder {
587
552
  this.temporaryCSS = "";
588
553
  }
589
554
 
555
+ makeRowUnselectable(section, row) {
556
+ log("adding unselectable:", section, row);
557
+ this.unselectable.push(`${section}/${row}`);
558
+ }
559
+
590
560
  isUnselectable(section, row) {
591
561
  return this.unselectable.includes(`${section}/${row}`);
592
562
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { register } from "register-service-worker";
4
4
 
5
- if (process.env.NODE_ENV != "development") {
5
+ if (process.env.NODE_ENV === "production") {
6
6
  register(`/sw.js`, {
7
7
  ready() {
8
8
  console.log(