pretix-map 0.1.8__tar.gz → 0.1.9__tar.gz

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 (57) hide show
  1. {pretix_map-0.1.8/pretix_map.egg-info → pretix_map-0.1.9}/PKG-INFO +1 -1
  2. {pretix_map-0.1.8 → pretix_map-0.1.9/pretix_map.egg-info}/PKG-INFO +1 -1
  3. pretix_map-0.1.9/pretix_mapplugin/__init__.py +1 -0
  4. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/js/salesmap.js +28 -92
  5. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/templates/pretix_mapplugin/map_page.html +2 -2
  6. pretix_map-0.1.8/pretix_mapplugin/__init__.py +0 -1
  7. {pretix_map-0.1.8 → pretix_map-0.1.9}/LICENSE +0 -0
  8. {pretix_map-0.1.8 → pretix_map-0.1.9}/MANIFEST.in +0 -0
  9. {pretix_map-0.1.8 → pretix_map-0.1.9}/README.md +0 -0
  10. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_map.egg-info/SOURCES.txt +0 -0
  11. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_map.egg-info/dependency_links.txt +0 -0
  12. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_map.egg-info/entry_points.txt +0 -0
  13. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_map.egg-info/requires.txt +0 -0
  14. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_map.egg-info/top_level.txt +0 -0
  15. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/apps.py +0 -0
  16. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/geocoding.py +0 -0
  17. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/locale/de/LC_MESSAGES/django.po +0 -0
  18. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/locale/de_Informal/.gitkeep +0 -0
  19. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/locale/de_Informal/LC_MESSAGES/django.po +0 -0
  20. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/management/__init__.py +0 -0
  21. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/management/commands/__init__.py +0 -0
  22. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/management/commands/geocode_existing_orders.py +0 -0
  23. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/migrations/0001_initial.py +0 -0
  24. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/migrations/0002_remove_ordergeocodedata_geocoded_timestamp_and_more.py +0 -0
  25. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/migrations/0003_mapmilestone.py +0 -0
  26. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/migrations/0004_ordergeocodedata_success_level.py +0 -0
  27. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/migrations/__init__.py +0 -0
  28. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/models.py +0 -0
  29. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/signals.py +0 -0
  30. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/.gitkeep +0 -0
  31. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/css/salesmap.css +0 -0
  32. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.Default.css +0 -0
  33. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/MarkerCluster.css +0 -0
  34. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/layers-2x.png +0 -0
  35. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/layers.png +0 -0
  36. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/marker-icon-2x.png +0 -0
  37. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/marker-icon.png +0 -0
  38. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/images/marker-shadow.png +0 -0
  39. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-heat.js +0 -0
  40. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.esm.js +0 -0
  41. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.esm.js.map +0 -0
  42. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.js +0 -0
  43. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet-src.js.map +0 -0
  44. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.css +0 -0
  45. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.js +0 -0
  46. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.js.map +0 -0
  47. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.markercluster.js +0 -0
  48. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/static/pretix_mapplugin/libs/leaflet-sales-map/leaflet.markercluster.js.map +0 -0
  49. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/tasks.py +0 -0
  50. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/templates/pretix_mapplugin/.gitkeep +0 -0
  51. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/templates/pretix_mapplugin/milestones.html +0 -0
  52. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/urls.py +0 -0
  53. {pretix_map-0.1.8 → pretix_map-0.1.9}/pretix_mapplugin/views.py +0 -0
  54. {pretix_map-0.1.8 → pretix_map-0.1.9}/pyproject.toml +0 -0
  55. {pretix_map-0.1.8 → pretix_map-0.1.9}/setup.cfg +0 -0
  56. {pretix_map-0.1.8 → pretix_map-0.1.9}/setup.py +0 -0
  57. {pretix_map-0.1.8 → pretix_map-0.1.9}/tests/test_main.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pretix-map
3
- Version: 0.1.8
3
+ Version: 0.1.9
4
4
  Summary: An overview map of the catchment area of previous orders. Measured by postcode
5
5
  Author-email: MarkenJaden <jjsch1410@gmail.com>
6
6
  Maintainer-email: MarkenJaden <jjsch1410@gmail.com>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pretix-map
3
- Version: 0.1.8
3
+ Version: 0.1.9
4
4
  Summary: An overview map of the catchment area of previous orders. Measured by postcode
5
5
  Author-email: MarkenJaden <jjsch1410@gmail.com>
6
6
  Maintainer-email: MarkenJaden <jjsch1410@gmail.com>
@@ -0,0 +1 @@
1
+ __version__ = "0.1.9"
@@ -53,6 +53,7 @@ document.addEventListener('DOMContentLoaded', function () {
53
53
  compareLabel: document.getElementById('compare-map-label'),
54
54
  mainLabel: document.getElementById('main-map-label'),
55
55
  heatmapReset: document.getElementById('heatmap-reset-btn'),
56
+ timelineContainer: document.getElementById('timeline-controls'),
56
57
  timeSlider: document.getElementById('timeline-slider'),
57
58
  timeDisplay: document.getElementById('timeline-date-display'),
58
59
  timePlayBtn: document.getElementById('timeline-play-btn'),
@@ -152,22 +153,14 @@ document.addEventListener('DOMContentLoaded', function () {
152
153
  sel.blurVal.textContent = sel.blurIn.value;
153
154
  sel.maxZoomVal.textContent = sel.maxZoomIn.value;
154
155
 
155
- if (heatmapLayer) {
156
- heatmapLayer.setOptions({
157
- radius: parseInt(sel.radiusIn.value),
158
- blur: parseInt(sel.blurIn.value),
159
- maxZoom: parseInt(sel.maxZoomIn.value),
160
- minOpacity: 0.4
161
- });
162
- }
163
- if (heatmapLayerComp) {
164
- heatmapLayerComp.setOptions({
165
- radius: parseInt(sel.radiusIn.value),
166
- blur: parseInt(sel.blurIn.value),
167
- maxZoom: parseInt(sel.maxZoomIn.value),
168
- minOpacity: 0.4
169
- });
170
- }
156
+ const opt = {
157
+ radius: parseInt(sel.radiusIn.value),
158
+ blur: parseInt(sel.blurIn.value),
159
+ maxZoom: parseInt(sel.maxZoomIn.value),
160
+ minOpacity: 0.4
161
+ };
162
+ if (heatmapLayer) heatmapLayer.setOptions(opt);
163
+ if (heatmapLayerComp) heatmapLayerComp.setOptions(opt);
171
164
  };
172
165
  [sel.radiusIn, sel.blurIn, sel.maxZoomIn].forEach(i => i.oninput = updateHeat);
173
166
  sel.heatmapReset.onclick = () => {
@@ -180,8 +173,10 @@ document.addEventListener('DOMContentLoaded', function () {
180
173
 
181
174
  function switchDisplayMode(mode) {
182
175
  currentDisplayMode = mode;
183
- sel.map.style.display = (mode === 'map' ? 'block' : 'none');
184
- sel.mapSplitRoot.style.display = (mode === 'map' ? 'flex' : 'none'); // FIX
176
+ const isMap = (mode === 'map');
177
+ sel.map.style.display = isMap ? 'block' : 'none';
178
+ sel.mapSplitRoot.style.display = isMap ? 'flex' : 'none';
179
+ sel.timelineContainer.style.display = isMap ? 'block' : 'none';
185
180
  sel.failedContainer.style.display = (mode === 'list' ? 'block' : 'none');
186
181
  sel.analyticsView.style.display = (mode === 'stats' ? 'block' : 'none');
187
182
 
@@ -190,9 +185,11 @@ document.addEventListener('DOMContentLoaded', function () {
190
185
  sel.listToggle.textContent = (mode === 'list' ? 'Show Map' : 'Failed Orders');
191
186
  sel.statsToggle.textContent = (mode === 'stats' ? 'Show Map' : 'Analytics View');
192
187
 
193
- if (mode === 'map') {
194
- map.invalidateSize();
195
- if (mapCompare) mapCompare.invalidateSize();
188
+ if (isMap) {
189
+ setTimeout(() => {
190
+ map.invalidateSize();
191
+ if (mapCompare) mapCompare.invalidateSize();
192
+ }, 50);
196
193
  }
197
194
  }
198
195
 
@@ -266,7 +263,6 @@ document.addEventListener('DOMContentLoaded', function () {
266
263
  const ctx = sel.timelineChart.getContext('2d');
267
264
  if (charts.timeline) charts.timeline.destroy();
268
265
 
269
- // Calculate counts per step from filteredData
270
266
  const stepCounts = steps.map(ts => {
271
267
  const nextTsIndex = steps.indexOf(ts) + 1;
272
268
  const nextTs = nextTsIndex < steps.length ? steps[nextTsIndex] : Infinity;
@@ -289,10 +285,7 @@ document.addEventListener('DOMContentLoaded', function () {
289
285
  responsive: true,
290
286
  maintainAspectRatio: false,
291
287
  plugins: { legend: { display: false }, tooltip: { enabled: false } },
292
- scales: {
293
- x: { display: false },
294
- y: { display: false, beginAtZero: true }
295
- }
288
+ scales: { x: { display: false }, y: { display: false, beginAtZero: true } }
296
289
  }
297
290
  });
298
291
  }
@@ -300,20 +293,13 @@ document.addEventListener('DOMContentLoaded', function () {
300
293
  function renderMilestoneMarkers(steps) {
301
294
  if (!sel.timelineMarkers || !steps || steps.length === 0) return;
302
295
  sel.timelineMarkers.innerHTML = '';
303
-
304
296
  milestones.forEach(m => {
305
- // Find closest step index
306
- let closestIdx = -1;
307
- let minDiff = Infinity;
297
+ let closestIdx = -1, minDiff = Infinity;
308
298
  steps.forEach((ts, idx) => {
309
299
  const diff = Math.abs(ts - m.ts);
310
- if (diff < minDiff) {
311
- minDiff = diff;
312
- closestIdx = idx;
313
- }
300
+ if (diff < minDiff) { minDiff = diff; closestIdx = idx; }
314
301
  });
315
-
316
- if (closestIdx !== -1 && minDiff < 86400000 * 2) { // Only show if close enough (2 days)
302
+ if (closestIdx !== -1 && minDiff < 86400000 * 2) {
317
303
  const percent = (closestIdx / (steps.length - 1)) * 100;
318
304
  const marker = document.createElement('div');
319
305
  marker.className = 'timeline-milestone-marker';
@@ -324,7 +310,6 @@ document.addEventListener('DOMContentLoaded', function () {
324
310
  marker.style.height = '100%';
325
311
  marker.style.background = '#d9534f';
326
312
  marker.style.zIndex = '2';
327
-
328
313
  const label = document.createElement('span');
329
314
  label.textContent = m.label;
330
315
  label.style.position = 'absolute';
@@ -334,7 +319,6 @@ document.addEventListener('DOMContentLoaded', function () {
334
319
  label.style.whiteSpace = 'nowrap';
335
320
  label.style.background = 'rgba(255,255,255,0.7)';
336
321
  label.style.padding = '0 2px';
337
-
338
322
  marker.appendChild(label);
339
323
  sel.timelineMarkers.appendChild(marker);
340
324
  }
@@ -344,10 +328,7 @@ document.addEventListener('DOMContentLoaded', function () {
344
328
  function renderItemAvailability(steps) {
345
329
  if (!sel.itemAvailList || !steps || steps.length === 0) return;
346
330
  sel.itemAvailList.innerHTML = '';
347
-
348
- const minTs = steps[0];
349
- const maxTs = steps[steps.length - 1];
350
- const range = maxTs - minTs;
331
+ const minTs = steps[0], maxTs = steps[steps.length - 1], range = maxTs - minTs;
351
332
 
352
333
  Object.keys(itemAvailData).forEach(itemName => {
353
334
  const d = itemAvailData[itemName];
@@ -379,7 +360,6 @@ document.addEventListener('DOMContentLoaded', function () {
379
360
  barContainer.style.borderRadius = '3px';
380
361
  row.appendChild(barContainer);
381
362
 
382
- // Planned availability (light grey bar)
383
363
  if (availFrom || availUntil) {
384
364
  const start = availFrom ? Math.max(availFrom, minTs) : minTs;
385
365
  const end = availUntil ? Math.min(availUntil, maxTs) : maxTs;
@@ -393,12 +373,10 @@ document.addEventListener('DOMContentLoaded', function () {
393
373
  bar.style.height = '100%';
394
374
  bar.style.background = '#ddd';
395
375
  bar.style.borderRadius = '3px';
396
- bar.title = `${itemName} (Configured Availability)`;
397
376
  barContainer.appendChild(bar);
398
377
  }
399
378
  }
400
379
 
401
- // Actual sales period (blue bar)
402
380
  if (firstTs && lastTs) {
403
381
  const start = Math.max(firstTs, minTs);
404
382
  const end = Math.min(lastTs, maxTs);
@@ -413,11 +391,9 @@ document.addEventListener('DOMContentLoaded', function () {
413
391
  bar.style.background = '#36A2EB';
414
392
  bar.style.borderRadius = '3px';
415
393
  bar.style.zIndex = '1';
416
- bar.title = `${itemName} (Actual Sales)`;
417
394
  barContainer.appendChild(bar);
418
395
  }
419
396
  }
420
-
421
397
  sel.itemAvailList.appendChild(row);
422
398
  });
423
399
  }
@@ -455,14 +431,12 @@ document.addEventListener('DOMContentLoaded', function () {
455
431
  function refreshLayers() {
456
432
  if (!map) return;
457
433
 
458
- // Remove old layers from both maps
459
434
  [pinLayer, heatmapLayer, gridLayer].forEach(l => { if (l && map.hasLayer(l)) map.removeLayer(l); });
460
435
  if (mapCompare) {
461
436
  [pinLayerComp, heatmapLayerComp, gridLayerComp, eventMarkerLayerComp].forEach(l => { if (l && mapCompare.hasLayer(l)) mapCompare.removeLayer(l); });
462
437
  }
463
438
  if (comparisonLayer && map.hasLayer(comparisonLayer)) map.removeLayer(comparisonLayer);
464
439
 
465
- // Check if split mode is needed (Comparison selected AND heatmap/grid view)
466
440
  const isSplitNeeded = currentCompareSlug && (currentView === 'heatmap' || currentView === 'grid');
467
441
 
468
442
  if (isSplitNeeded) {
@@ -477,14 +451,11 @@ document.addEventListener('DOMContentLoaded', function () {
477
451
  setTimeout(() => { map.invalidateSize(); }, 50);
478
452
  }
479
453
 
480
- // Create layers for main map
481
454
  createPinLayer(); createHeatmapLayer(); createGridLayer();
482
455
 
483
- // Create layers for comparison map if in split mode
484
456
  if (isSplitNeeded) {
485
457
  createPinLayer(true); createHeatmapLayer(true); createGridLayer(true);
486
458
  } else if (currentCompareSlug && currentView === 'pins') {
487
- // Pins comparison: Dots on the main map
488
459
  const dots = compareData.map(l => l.lat ? L.circleMarker([l.lat, l.lon], { radius: 4, color: '#888', fillOpacity: 0.5 }).bindTooltip("Comparison Order") : null).filter(l => l);
489
460
  comparisonLayer = L.layerGroup(dots);
490
461
  }
@@ -495,7 +466,6 @@ document.addEventListener('DOMContentLoaded', function () {
495
466
  function createPinLayer(isCompare = false) {
496
467
  const data = isCompare ? compareDisplayData : displayData;
497
468
  if (data.length === 0) { if(isCompare) pinLayerComp = null; else pinLayer = null; return; }
498
-
499
469
  const markers = data.map(loc => {
500
470
  if (loc.lat == null) return null;
501
471
  let icon = icons.blue;
@@ -506,7 +476,6 @@ document.addEventListener('DOMContentLoaded', function () {
506
476
  if (loc.order_url) m.on('click', () => window.open(loc.order_url, '_blank'));
507
477
  return m;
508
478
  }).filter(m => m !== null);
509
-
510
479
  const layer = isClustering ? L.markerClusterGroup().addLayers(markers) : L.layerGroup(markers);
511
480
  if (isCompare) pinLayerComp = layer; else pinLayer = layer;
512
481
  }
@@ -514,7 +483,6 @@ document.addEventListener('DOMContentLoaded', function () {
514
483
  function createHeatmapLayer(isCompare = false) {
515
484
  const data = isCompare ? compareDisplayData : displayData;
516
485
  if (data.length === 0) { if(isCompare) heatmapLayerComp = null; else heatmapLayer = null; return; }
517
-
518
486
  let maxRev = Math.max(...data.map(d => d.revenue || 0), 1);
519
487
  const points = data.map(l => [l.lat, l.lon, isWeighted ? (l.revenue || 0) / maxRev : 1.0]);
520
488
  const layer = L.heatLayer(points, {
@@ -529,11 +497,9 @@ document.addEventListener('DOMContentLoaded', function () {
529
497
  function createGridLayer(isCompare = false) {
530
498
  const data = isCompare ? compareDisplayData : displayData;
531
499
  if (data.length === 0) { if(isCompare) gridLayerComp = null; else gridLayer = null; return; }
532
-
533
500
  const rawVal = parseInt(sel.gridSizeSlider.value);
534
501
  const gridSize = 0.4 * Math.pow(rawVal / 100, 2);
535
502
  sel.gridSizeVal.textContent = gridSize.toFixed(3);
536
-
537
503
  const bins = {}; let maxVal = 0;
538
504
  data.forEach(l => {
539
505
  if (l.lat == null) return;
@@ -553,24 +519,16 @@ document.addEventListener('DOMContentLoaded', function () {
553
519
 
554
520
  function showCurrentView() {
555
521
  if (!map) return;
556
-
557
- // Main map view
558
522
  if (currentView === 'pins' && pinLayer) map.addLayer(pinLayer);
559
523
  else if (currentView === 'heatmap' && heatmapLayer) map.addLayer(heatmapLayer);
560
524
  else if (currentView === 'grid' && gridLayer) map.addLayer(gridLayer);
561
-
562
- // Control panel updates
563
525
  sel.heatmapPanel.style.display = (currentView === 'heatmap' || currentView === 'grid' ? 'block' : 'none');
564
526
  sel.heatmapControls.style.display = (currentView === 'heatmap' ? 'block' : 'none');
565
527
  sel.gridControls.style.display = (currentView === 'grid' ? 'block' : 'none');
566
-
567
528
  if (sel.clusterToggle) sel.clusterToggle.style.display = (currentView === 'pins' ? 'inline-block' : 'none');
568
529
  if (eventMarkerLayer) map.addLayer(eventMarkerLayer);
569
-
570
- // Dot comparison on main map
571
530
  if (comparisonLayer && currentView === 'pins') map.addLayer(comparisonLayer);
572
531
 
573
- // Comparison map view (Split mode)
574
532
  if (mapCompare && sel.mapSplitRoot.classList.contains('map-split-active')) {
575
533
  if (currentView === 'pins' && pinLayerComp) mapCompare.addLayer(pinLayerComp);
576
534
  else if (currentView === 'heatmap' && heatmapLayerComp) mapCompare.addLayer(heatmapLayerComp);
@@ -582,10 +540,10 @@ document.addEventListener('DOMContentLoaded', function () {
582
540
  function renderEventMarker(em, isCompare = false) {
583
541
  const layer = L.marker([em.lat, em.lon], { icon: icons.red, zIndexOffset: 2000 }).bindTooltip(`<strong>EVENT: ${em.name}</strong><br>${em.location}`);
584
542
  if (isCompare) {
585
- if (eventMarkerLayerComp && mapCompare.hasLayer(eventMarkerLayerComp)) mapCompare.removeLayer(eventMarkerLayerComp);
543
+ if (eventMarkerLayerComp) mapCompare.removeLayer(eventMarkerLayerComp);
586
544
  eventMarkerLayerComp = layer.addTo(mapCompare);
587
545
  } else {
588
- if (eventMarkerLayer && map.hasLayer(eventMarkerLayer)) map.removeLayer(eventMarkerLayer);
546
+ if (eventMarkerLayer) map.removeLayer(eventMarkerLayer);
589
547
  eventMarkerLayer = layer.addTo(map);
590
548
  }
591
549
  }
@@ -597,18 +555,12 @@ document.addEventListener('DOMContentLoaded', function () {
597
555
  sel.compareLabel.textContent = "";
598
556
  refreshLayers(); return;
599
557
  }
600
-
601
558
  const option = sel.compareSelect.options[sel.compareSelect.selectedIndex];
602
559
  sel.compareLabel.textContent = option.text;
603
-
604
560
  const newUrl = sel.map.dataset.dataUrl.replace(/\/event\/([^\/]+)\/([^\/]+)\//, `/event/$1/${slug}/`);
605
561
  fetch(newUrl).then(r => r.json()).then(data => {
606
562
  compareData = (data.locations || []).map(loc => { loc.ts = loc.date ? new Date(loc.date).getTime() : 0; return loc; }).sort((a, b) => a.ts - b.ts);
607
-
608
- // Handle event marker for comparison map
609
563
  if (data.event_marker) renderEventMarker(data.event_marker, true);
610
-
611
- // Sync filters for comparison data
612
564
  applyFilters();
613
565
  });
614
566
  }
@@ -626,12 +578,8 @@ document.addEventListener('DOMContentLoaded', function () {
626
578
  function applyFilters() {
627
579
  showCanceled = sel.showCanceledCheck.checked;
628
580
  const filterFn = loc => (loc.items.length === 0 || loc.items.some(i => selectedItems.has(i))) && (showCanceled || loc.status !== 'canceled');
629
-
630
581
  filteredData = allData.filter(filterFn);
631
- if (compareData.length > 0) {
632
- compareFilteredData = compareData.filter(filterFn);
633
- }
634
-
582
+ if (compareData.length > 0) compareFilteredData = compareData.filter(filterFn);
635
583
  updateTimelineSlider();
636
584
  }
637
585
 
@@ -641,20 +589,8 @@ document.addEventListener('DOMContentLoaded', function () {
641
589
  let statusBadge = `<span class="label label-danger">Failed</span>`;
642
590
  if (o.success_level === 'city') statusBadge = `<span class="label label-warning">Only City</span>`;
643
591
  else if (o.success_level === 'zip') statusBadge = `<span class="label label-info">Only Zip</span>`;
644
-
645
- return `<tr>
646
- <td><code>${o.code}</code></td>
647
- <td>${o.address}</td>
648
- <td>${statusBadge}</td>
649
- <td>
650
- <div class="btn-group">
651
- <a href="${o.url}" target="_blank" class="btn btn-default btn-xs">View</a>
652
- <button class="btn btn-warning btn-xs retry-btn" data-url="${o.retry_url}">Retry</button>
653
- </div>
654
- </td>
655
- </tr>`;
592
+ return `<tr><td><code>${o.code}</code></td><td>${o.address}</td><td>${statusBadge}</td><td><div class="btn-group"><a href="${o.url}" target="_blank" class="btn btn-default btn-xs">View</a><button class="btn btn-warning btn-xs retry-btn" data-url="${o.retry_url}">Retry</button></div></td></tr>`;
656
593
  }).join('') || '<tr><td colspan="4">None</td></tr>';
657
-
658
594
  sel.failedTbody.querySelectorAll('.retry-btn').forEach(btn => btn.onclick = () => {
659
595
  btn.disabled = true; btn.innerHTML = '...';
660
596
  fetch(btn.dataset.url, { method: 'POST', headers: { 'X-CSRFToken': csrfToken } }).then(r => r.json()).then(d => { if(d.success) { btn.className = 'btn btn-success btn-xs'; btn.innerHTML = '✔'; setTimeout(() => fetchData(sel.map.dataset.dataUrl), 1000); } });
@@ -662,4 +598,4 @@ document.addEventListener('DOMContentLoaded', function () {
662
598
  }
663
599
 
664
600
  init();
665
- });
601
+ });
@@ -151,7 +151,7 @@
151
151
  </div>
152
152
  </div>
153
153
 
154
- <div id="timeline-controls" style="margin-top: 10px; padding: 15px; background: #f5f5f5; border-radius: 4px; border: 1px solid #ddd; position: relative;">
154
+ <div id="timeline-controls" style="margin-top: 10px; padding: 15px; background: #f5f5f5; border-radius: 4px; border: 1px solid #ddd; position: relative; flex-shrink: 0;">
155
155
  <div id="timeline-chart-container" style="position: relative; height: 50px; margin-bottom: 10px; background: white; border: 1px solid #eee; overflow: hidden;">
156
156
  <canvas id="timeline-volume-chart" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></canvas>
157
157
  <div id="timeline-milestone-markers" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none;"></div>
@@ -164,7 +164,7 @@
164
164
  </div>
165
165
  </div>
166
166
 
167
- <div id="timeline-item-availability" style="margin-top: 10px; background: white; border: 1px solid #eee; padding: 10px 0;">
167
+ <div id="timeline-item-availability" style="margin-top: 10px; background: white; border: 1px solid #eee; padding: 10px 0; max-height: 150px; overflow-y: auto;">
168
168
  <div id="item-availability-list" style="position: relative; padding: 0 5px;"></div>
169
169
  </div>
170
170
 
@@ -1 +0,0 @@
1
- __version__ = "0.1.8"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes