mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-09 08:47:11 -05:00
* fix: move foreman to global gems to fix startup crash (#1971) * Update exporting code to stream points data to file in batches to red… (#1980) * Update exporting code to stream points data to file in batches to reduce memory usage * Update changelog * Update changelog * Feature/maplibre frontend (#1953) * Add a plan to use MapLibre GL JS for the frontend map rendering, replacing Leaflet * Implement phase 1 * Phases 1-3 + part of 4 * Fix e2e tests * Phase 6 * Implement fog of war * Phase 7 * Next step: fix specs, phase 7 done * Use our own map tiles * Extract v2 map logic to separate manager classes * Update settings panel on v2 map * Update v2 e2e tests structure * Reimplement location search in maps v2 * Update speed routes * Implement visits and places creation in v2 * Fix last failing test * Implement visits merging * Fix a routes e2e test and simplify the routes layer styling. * Extract js to modules from maps_v2_controller.js * Implement area creation * Fix spec problem * Fix some e2e tests * Implement live mode in v2 map * Update icons and panel * Extract some styles * Remove unused file * Start adding dark theme to popups on MapLibre maps * Make popups respect dark theme * Move v2 maps to maplibre namespace * Update v2 references to maplibre * Put place, area and visit info into side panel * Update API to use safe settings config method * Fix specs * Fix method name to config in SafeSettings and update usages accordingly * Add missing public files * Add handling for real time points * Fix remembering enabled/disabled layers of the v2 map * Fix lots of e2e tests * Add settings to select map version * Use maps/v2 as main path for MapLibre maps * Update routing * Update live mode * Update maplibre controller * Update changelog * Remove some console.log statements --------- Co-authored-by: Robin Tuszik <mail@robin.gg>
655 lines
28 KiB
Text
655 lines
28 KiB
Text
<div class="map-control-panel" data-maps--maplibre-target="settingsPanel" data-controller="map-panel">
|
|
<!-- Vertical Icon Tabs (Left Side) -->
|
|
<div class="panel-tabs">
|
|
<button class="tab-btn active"
|
|
data-action="click->map-panel#switchTab"
|
|
data-tab="layers"
|
|
data-map-panel-target="tabButton"
|
|
title="Map Layers">
|
|
<%= icon 'layer' %>
|
|
</button>
|
|
|
|
<button class="tab-btn"
|
|
data-action="click->map-panel#switchTab"
|
|
data-tab="search"
|
|
data-map-panel-target="tabButton"
|
|
title="Search">
|
|
<%= icon 'search' %>
|
|
</button>
|
|
|
|
|
|
<button class="tab-btn"
|
|
data-action="click->map-panel#switchTab"
|
|
data-tab="tools"
|
|
data-map-panel-target="tabButton"
|
|
title="Tools">
|
|
<%= icon 'pocket-knife' %>
|
|
</button>
|
|
|
|
<button class="tab-btn"
|
|
data-action="click->map-panel#switchTab"
|
|
data-tab="settings"
|
|
data-map-panel-target="tabButton"
|
|
title="Settings">
|
|
<%= icon 'settings' %>
|
|
</button>
|
|
|
|
<% if !DawarichSettings.self_hosted? %>
|
|
<button class="tab-btn"
|
|
data-action="click->map-panel#switchTab"
|
|
data-tab="links"
|
|
data-map-panel-target="tabButton"
|
|
title="Links">
|
|
<%= icon 'info' %>
|
|
</button>
|
|
<% end %>
|
|
</div>
|
|
|
|
<!-- Panel Content -->
|
|
<div class="panel-content">
|
|
<!-- Panel Header -->
|
|
<div class="panel-header">
|
|
<h3 class="panel-title" data-map-panel-target="title">Layers</h3>
|
|
<button class="btn btn-ghost btn-sm btn-circle"
|
|
data-action="click->maps--maplibre#toggleSettings"
|
|
title="Close panel">
|
|
<%= icon 'x' %>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Panel Body -->
|
|
<div class="panel-body">
|
|
<!-- Search Tab -->
|
|
<div class="tab-content" data-tab-content="search" data-map-panel-target="tabContent">
|
|
<div class="form-control w-full">
|
|
<label class="label">
|
|
<span class="label-text">Search for a place</span>
|
|
</label>
|
|
<div class="relative">
|
|
<input type="text"
|
|
placeholder="Enter name of a place"
|
|
class="input input-bordered w-full"
|
|
data-maps--maplibre-target="searchInput"
|
|
autocomplete="off" />
|
|
<!-- Search Results -->
|
|
<div class="absolute z-50 w-full mt-1 bg-base-100 rounded-lg shadow-lg border border-base-300 hidden max-h-full overflow-y-auto"
|
|
data-maps--maplibre-target="searchResults">
|
|
<!-- Results will be populated by SearchManager -->
|
|
</div>
|
|
</div>
|
|
<p class="text-xs text-base-content/60 mt-2">
|
|
Search for a location to find places you visited
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Layers Tab -->
|
|
<div class="tab-content active" data-tab-content="layers" data-map-panel-target="tabContent">
|
|
<div class="space-y-4">
|
|
<!-- Points Layer -->
|
|
<div class="form-control">
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input type="checkbox"
|
|
class="toggle toggle-primary"
|
|
data-maps--maplibre-target="pointsToggle"
|
|
data-action="change->maps--maplibre#togglePoints" />
|
|
<span class="label-text font-medium">Points</span>
|
|
</label>
|
|
<p class="text-sm text-base-content/60 ml-14">Show individual location points</p>
|
|
</div>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<!-- Routes Layer -->
|
|
<div class="form-control">
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input type="checkbox"
|
|
class="toggle toggle-primary"
|
|
data-maps--maplibre-target="routesToggle"
|
|
data-action="change->maps--maplibre#toggleRoutes" />
|
|
<span class="label-text font-medium">Routes</span>
|
|
</label>
|
|
<p class="text-sm text-base-content/60 ml-14">Show connected route lines</p>
|
|
</div>
|
|
|
|
<!-- Speed-Colored Routes Options (conditionally shown) -->
|
|
<div class="ml-14 space-y-3" data-maps--maplibre-target="routesOptions" style="display: none;">
|
|
<div class="form-control">
|
|
<label class="label cursor-pointer py-2">
|
|
<span class="label-text text-sm">Color by speed</span>
|
|
<input type="checkbox"
|
|
class="toggle toggle-sm toggle-primary"
|
|
data-maps--maplibre-target="speedColoredToggle"
|
|
data-action="change->maps--maplibre#toggleSpeedColoredRoutes" />
|
|
</label>
|
|
</div>
|
|
|
|
<!-- Speed Color Scale Editor (shown when speed colors enabled) -->
|
|
<div class="hidden" data-maps--maplibre-target="speedColorScaleContainer">
|
|
<button type="button"
|
|
class="btn btn-sm btn-outline w-full"
|
|
data-action="click->maps--maplibre#openSpeedColorEditor">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01" />
|
|
</svg>
|
|
Edit Color Gradient
|
|
</button>
|
|
<input type="hidden" data-maps--maplibre-target="speedColorScaleInput" value="" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<!-- Heatmap Layer -->
|
|
<div class="form-control">
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input type="checkbox"
|
|
class="toggle toggle-primary"
|
|
data-maps--maplibre-target="heatmapToggle"
|
|
data-action="change->maps--maplibre#toggleHeatmap" />
|
|
<span class="label-text font-medium">Heatmap</span>
|
|
</label>
|
|
<p class="text-sm text-base-content/60 ml-14">Show density heatmap</p>
|
|
</div>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<!-- Visits Layer -->
|
|
<div class="form-control">
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input type="checkbox"
|
|
class="toggle toggle-primary"
|
|
data-maps--maplibre-target="visitsToggle"
|
|
data-action="change->maps--maplibre#toggleVisits" />
|
|
<span class="label-text font-medium">Visits</span>
|
|
</label>
|
|
<p class="text-sm text-base-content/60 ml-14">Show detected area visits</p>
|
|
</div>
|
|
|
|
<!-- Visits Search (conditionally shown) -->
|
|
<div class="ml-14 space-y-2" data-maps--maplibre-target="visitsSearch" style="display: none;">
|
|
<input type="text"
|
|
id="visits-search"
|
|
placeholder="Filter by name..."
|
|
class="input input-sm input-bordered w-full"
|
|
data-action="input->maps--maplibre#searchVisits" />
|
|
|
|
<select class="select select-bordered w-full"
|
|
data-action="change->maps--maplibre#filterVisits">
|
|
<option value="all">All Visits</option>
|
|
<option value="confirmed">Confirmed Only</option>
|
|
<option value="suggested">Suggested Only</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<!-- Places Layer -->
|
|
<div class="form-control">
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input type="checkbox"
|
|
class="toggle toggle-primary"
|
|
data-maps--maplibre-target="placesToggle"
|
|
data-action="change->maps--maplibre#togglePlaces" />
|
|
<span class="label-text font-medium">Places</span>
|
|
</label>
|
|
<p class="text-sm text-base-content/60 ml-14">Show your saved places</p>
|
|
</div>
|
|
|
|
<!-- Places Tags (conditionally shown) -->
|
|
<div class="ml-14 space-y-2" data-maps--maplibre-target="placesFilters" style="display: none;">
|
|
<div class="form-control">
|
|
<label class="label cursor-pointer justify-start gap-2">
|
|
<input type="checkbox"
|
|
class="toggle toggle-sm"
|
|
data-maps--maplibre-target="enableAllPlaceTagsToggle"
|
|
data-action="change->maps--maplibre#toggleAllPlaceTags">
|
|
<span class="label-text text-sm">Enable All Tags</span>
|
|
</label>
|
|
</div>
|
|
<div class="form-control">
|
|
<label class="label">
|
|
<span class="label-text text-sm">Filter by Tags</span>
|
|
</label>
|
|
<div class="flex flex-wrap gap-2">
|
|
<!-- Untagged option -->
|
|
<label class="cursor-pointer">
|
|
<input type="checkbox"
|
|
name="place_tag_ids[]"
|
|
value="untagged"
|
|
class="checkbox checkbox-xs hidden peer"
|
|
data-action="change->maps--maplibre#filterPlacesByTags">
|
|
<span class="badge badge-sm badge-outline transition-all peer-checked:scale-105"
|
|
style="border-color: #94a3b8; color: #94a3b8;"
|
|
data-checked-style="background-color: #94a3b8; color: white;">
|
|
🏷️ Untagged
|
|
</span>
|
|
</label>
|
|
|
|
<% current_user.tags.ordered.each do |tag| %>
|
|
<label class="cursor-pointer">
|
|
<input type="checkbox"
|
|
name="place_tag_ids[]"
|
|
value="<%= tag.id %>"
|
|
class="checkbox checkbox-xs hidden peer"
|
|
data-action="change->maps--maplibre#filterPlacesByTags">
|
|
<span class="badge badge-sm badge-outline transition-all peer-checked:scale-105"
|
|
style="border-color: <%= tag.color %>; color: <%= tag.color %>;"
|
|
data-checked-style="background-color: <%= tag.color %>; color: white;">
|
|
<%= tag.icon %> #<%= tag.name %>
|
|
</span>
|
|
</label>
|
|
<% end %>
|
|
</div>
|
|
<label class="label">
|
|
<span class="label-text-alt">Click tags to filter places</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<!-- Photos Layer -->
|
|
<div class="form-control">
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input type="checkbox"
|
|
class="toggle toggle-primary"
|
|
data-maps--maplibre-target="photosToggle"
|
|
data-action="change->maps--maplibre#togglePhotos" />
|
|
<span class="label-text font-medium">Photos</span>
|
|
</label>
|
|
<p class="text-sm text-base-content/60 ml-14">Show geotagged photos</p>
|
|
</div>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<!-- Areas Layer -->
|
|
<div class="form-control">
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input type="checkbox"
|
|
class="toggle toggle-primary"
|
|
data-maps--maplibre-target="areasToggle"
|
|
data-action="change->maps--maplibre#toggleAreas" />
|
|
<span class="label-text font-medium">Areas</span>
|
|
</label>
|
|
<p class="text-sm text-base-content/60 ml-14">Show defined areas</p>
|
|
</div>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<!-- Tracks Layer -->
|
|
<%# <div class="form-control">
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input type="checkbox"
|
|
class="toggle toggle-primary"
|
|
data-maps--maplibre-target="tracksToggle"
|
|
data-action="change->maps--maplibre#toggleTracks" />
|
|
<span class="label-text font-medium">Tracks</span>
|
|
</label>
|
|
<p class="text-sm text-base-content/60 ml-14">Show saved tracks</p>
|
|
</div> %>
|
|
|
|
<%# <div class="divider"></div> %>
|
|
|
|
<!-- Fog of War Layer -->
|
|
<div class="form-control">
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input type="checkbox"
|
|
class="toggle toggle-primary"
|
|
data-maps--maplibre-target="fogToggle"
|
|
data-action="change->maps--maplibre#toggleFog" />
|
|
<span class="label-text font-medium">Fog of War</span>
|
|
</label>
|
|
<p class="text-sm text-base-content/60 ml-14">Show explored areas</p>
|
|
</div>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<!-- Scratch Map Layer -->
|
|
<div class="form-control">
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input type="checkbox"
|
|
class="toggle toggle-primary"
|
|
data-maps--maplibre-target="scratchToggle"
|
|
data-action="change->maps--maplibre#toggleScratch" />
|
|
<span class="label-text font-medium">Scratch Map</span>
|
|
</label>
|
|
<p class="text-sm text-base-content/60 ml-14">Show scratched countries</p>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Settings Tab -->
|
|
<div class="tab-content" data-tab-content="settings" data-map-panel-target="tabContent">
|
|
<form data-action="submit->maps--maplibre#updateAdvancedSettings" class="space-y-4">
|
|
<!-- Map Style -->
|
|
<div class="form-control w-full">
|
|
<label class="label">
|
|
<span class="label-text font-medium">Map Style</span>
|
|
</label>
|
|
<select class="select select-bordered w-full"
|
|
name="mapStyle"
|
|
data-action="change->maps--maplibre#updateMapStyle">
|
|
<option value="light" selected>Light</option>
|
|
<option value="dark">Dark</option>
|
|
<option value="white">White</option>
|
|
<option value="black">Black</option>
|
|
<option value="grayscale">Grayscale</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<!-- Route Opacity -->
|
|
<div class="form-control w-full">
|
|
<label class="label">
|
|
<span class="label-text font-medium">Route Opacity</span>
|
|
<span class="label-text-alt">%</span>
|
|
</label>
|
|
<input type="range"
|
|
name="routeOpacity"
|
|
min="10"
|
|
max="100"
|
|
step="10"
|
|
value="100"
|
|
class="range range-sm"
|
|
data-maps--maplibre-target="routeOpacityRange"
|
|
data-action="input->maps--maplibre#updateRouteOpacity" />
|
|
<div class="w-full flex justify-between text-xs px-2 mt-1">
|
|
<span>10%</span>
|
|
<span>50%</span>
|
|
<span>100%</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<!-- Fog of War Settings -->
|
|
<div class="form-control w-full">
|
|
<label class="label">
|
|
<span class="label-text font-medium">Fog of War Radius</span>
|
|
<span class="label-text-alt" data-maps--maplibre-target="fogRadiusValue">1000m</span>
|
|
</label>
|
|
<input type="range"
|
|
name="fogOfWarRadius"
|
|
min="5"
|
|
max="2000"
|
|
step="5"
|
|
value="1000"
|
|
class="range range-sm"
|
|
data-action="input->maps--maplibre#updateFogRadiusDisplay" />
|
|
<div class="w-full flex justify-between text-xs px-2 mt-1">
|
|
<span>5m</span>
|
|
<span>1000m</span>
|
|
<span>2000m</span>
|
|
</div>
|
|
<p class="text-xs text-base-content/60 mt-1">Clear radius around visited points</p>
|
|
</div>
|
|
|
|
<div class="form-control w-full">
|
|
<label class="label">
|
|
<span class="label-text font-medium">Fog of War Threshold</span>
|
|
<span class="label-text-alt" data-maps--maplibre-target="fogThresholdValue">1</span>
|
|
</label>
|
|
<input type="range"
|
|
name="fogOfWarThreshold"
|
|
min="1"
|
|
max="10"
|
|
step="1"
|
|
value="1"
|
|
class="range range-sm"
|
|
data-action="input->maps--maplibre#updateFogThresholdDisplay" />
|
|
<div class="w-full flex justify-between text-xs px-2 mt-1">
|
|
<span>1</span>
|
|
<span>5</span>
|
|
<span>10</span>
|
|
</div>
|
|
<p class="text-xs text-base-content/60 mt-1">Minimum points to clear fog</p>
|
|
</div>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<!-- Route Generation Settings -->
|
|
<div class="form-control w-full">
|
|
<label class="label">
|
|
<span class="label-text font-medium">Meters Between Routes</span>
|
|
<span class="label-text-alt" data-maps--maplibre-target="metersBetweenValue">500m</span>
|
|
</label>
|
|
<input type="range"
|
|
name="metersBetweenRoutes"
|
|
min="100"
|
|
max="5000"
|
|
step="100"
|
|
value="500"
|
|
class="range range-sm"
|
|
data-action="input->maps--maplibre#updateMetersBetweenDisplay" />
|
|
<div class="w-full flex justify-between text-xs px-2 mt-1">
|
|
<span>100m</span>
|
|
<span>2500m</span>
|
|
<span>5000m</span>
|
|
</div>
|
|
<p class="text-xs text-base-content/60 mt-1">Distance threshold for route splitting</p>
|
|
</div>
|
|
|
|
<div class="form-control w-full">
|
|
<label class="label">
|
|
<span class="label-text font-medium">Minutes Between Routes</span>
|
|
<span class="label-text-alt" data-maps--maplibre-target="minutesBetweenValue">60min</span>
|
|
</label>
|
|
<input type="range"
|
|
name="minutesBetweenRoutes"
|
|
min="1"
|
|
max="180"
|
|
step="1"
|
|
value="60"
|
|
class="range range-sm"
|
|
data-action="input->maps--maplibre#updateMinutesBetweenDisplay" />
|
|
<div class="w-full flex justify-between text-xs px-2 mt-1">
|
|
<span>1min</span>
|
|
<span>90min</span>
|
|
<span>180min</span>
|
|
</div>
|
|
<p class="text-xs text-base-content/60 mt-1">Time threshold for route splitting</p>
|
|
</div>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<!-- Points Rendering Mode -->
|
|
<div class="form-control w-full">
|
|
<label class="label">
|
|
<span class="label-text font-medium">Points Rendering Mode</span>
|
|
</label>
|
|
<div class="flex flex-col gap-2">
|
|
<label class="label cursor-pointer justify-start gap-3 py-1">
|
|
<input type="radio"
|
|
name="pointsRenderingMode"
|
|
value="raw"
|
|
class="radio radio-primary radio-sm"
|
|
checked />
|
|
<span class="label-text">Raw (all points)</span>
|
|
</label>
|
|
<label class="label cursor-pointer justify-start gap-3 py-1">
|
|
<input type="radio"
|
|
name="pointsRenderingMode"
|
|
value="simplified"
|
|
class="radio radio-primary radio-sm" />
|
|
<span class="label-text">Simplified (reduced points)</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<!-- Speed-Colored Routes -->
|
|
<div class="form-control">
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input type="checkbox"
|
|
name="speedColoredRoutes"
|
|
class="toggle toggle-primary" />
|
|
<span class="label-text font-medium">Speed-Colored Routes</span>
|
|
</label>
|
|
<p class="text-sm text-base-content/60 mt-1">Color routes by speed</p>
|
|
</div>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<!-- Live Mode -->
|
|
<div class="form-control">
|
|
<label class="label cursor-pointer justify-start gap-3">
|
|
<input type="checkbox"
|
|
class="toggle toggle-primary"
|
|
data-action="change->maps--maplibre-realtime#toggleLiveMode"
|
|
data-maps--maplibre-realtime-target="liveModeToggle" />
|
|
<span class="label-text font-medium">Live Mode</span>
|
|
</label>
|
|
<p class="text-sm text-base-content/60 mt-1">Show new points in real-time</p>
|
|
</div>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<!-- Update Button -->
|
|
<button type="submit" class="btn btn-sm btn-primary btn-block">
|
|
<%= icon 'save' %>
|
|
Apply Settings
|
|
</button>
|
|
|
|
<!-- Reset Settings -->
|
|
<button type="button"
|
|
class="btn btn-sm btn-outline btn-block"
|
|
data-action="click->maps--maplibre#resetSettings">
|
|
<%= icon 'rotate-ccw' %>
|
|
Reset to Defaults
|
|
</button>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Tools Tab -->
|
|
<div class="tab-content" data-tab-content="tools" data-map-panel-target="tabContent">
|
|
<div class="space-y-4">
|
|
<!-- Tools Grid: Full width on mobile/tablet, 2 columns on large screens -->
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-3">
|
|
<!-- Create a Visit Button -->
|
|
<button type="button"
|
|
class="btn btn-sm btn-outline"
|
|
data-action="click->maps--maplibre#startCreateVisit">
|
|
<%= icon 'map-pin-check' %>
|
|
Create a Visit
|
|
</button>
|
|
|
|
<!-- Create a Place Button -->
|
|
<button type="button"
|
|
class="btn btn-sm btn-outline"
|
|
data-action="click->maps--maplibre#startCreatePlace">
|
|
<%= icon 'map-pin-plus' %>
|
|
Create a Place
|
|
</button>
|
|
|
|
<!-- Select Area Button -->
|
|
<button type="button"
|
|
class="btn btn-sm btn-outline"
|
|
data-maps--maplibre-target="selectAreaButton"
|
|
data-action="click->maps--maplibre#startSelectArea">
|
|
<%= icon 'square-dashed-mouse-pointer' %>
|
|
Select Area
|
|
</button>
|
|
|
|
<!-- Create Area Button -->
|
|
<button type="button"
|
|
class="btn btn-sm btn-outline"
|
|
data-action="click->maps--maplibre#startCreateArea">
|
|
<%= icon 'circle-plus' %>
|
|
Create an Area
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Info Display (shown when clicking on visit/area/place) -->
|
|
<div class="hidden mt-4" data-maps--maplibre-target="infoDisplay">
|
|
<div class="card bg-base-200 shadow-md">
|
|
<div class="card-body p-4">
|
|
<div class="flex justify-between items-start mb-2">
|
|
<h4 class="card-title text-base" data-maps--maplibre-target="infoTitle"></h4>
|
|
<button class="btn btn-ghost btn-xs btn-circle" data-action="click->maps--maplibre#closeInfo" title="Close">✕</button>
|
|
</div>
|
|
<div class="space-y-2 text-sm" data-maps--maplibre-target="infoContent">
|
|
<!-- Content will be dynamically inserted -->
|
|
</div>
|
|
<div class="card-actions justify-end mt-3" data-maps--maplibre-target="infoActions">
|
|
<!-- Action buttons will be dynamically inserted -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Selection Actions (shown after area is selected) -->
|
|
<div class="hidden mt-4 space-y-2" data-maps--maplibre-target="selectionActions">
|
|
<button type="button"
|
|
class="btn btn-sm btn-outline btn-error btn-block"
|
|
data-action="click->maps--maplibre#deleteSelectedPoints"
|
|
data-maps--maplibre-target="deletePointsButton">
|
|
<%= icon 'trash-2' %>
|
|
<span data-maps--maplibre-target="deleteButtonText">Delete Selected Points</span>
|
|
</button>
|
|
|
|
<!-- Selected Visits Container -->
|
|
<div class="hidden mt-4 max-h-full overflow-y-auto" data-maps--maplibre-target="selectedVisitsContainer">
|
|
<!-- Visit cards will be dynamically inserted here -->
|
|
</div>
|
|
|
|
<!-- Bulk Actions for Visits -->
|
|
<div class="hidden" data-maps--maplibre-target="selectedVisitsBulkActions">
|
|
<!-- Bulk action buttons will be dynamically inserted here -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<% if !DawarichSettings.self_hosted? %>
|
|
<!-- Links Tab -->
|
|
<div class="tab-content" data-tab-content="links" data-map-panel-target="tabContent">
|
|
<div class="space-y-6">
|
|
<!-- Community Section -->
|
|
<div>
|
|
<h4 class="font-semibold text-base mb-3">Community</h4>
|
|
<div class="flex flex-col gap-2">
|
|
<a href="https://discord.gg/pHsBjpt5J8" target="_blank" class="link-hover text-sm">Discord</a>
|
|
<a href="https://x.com/freymakesstuff" target="_blank" class="link-hover text-sm">X</a>
|
|
<a href="https://github.com/Freika/dawarich" target="_blank" class="link-hover text-sm">Github</a>
|
|
<a href="https://mastodon.social/@dawarich" target="_blank" class="link-hover text-sm">Mastodon</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<!-- Docs Section -->
|
|
<div>
|
|
<h4 class="font-semibold text-base mb-3">Docs</h4>
|
|
<div class="flex flex-col gap-2">
|
|
<a href="https://dawarich.app/docs/intro" target="_blank" class="link-hover text-sm">Tutorial</a>
|
|
<a href="https://dawarich.app/docs/tutorials/import-existing-data" target="_blank" class="link-hover text-sm">Import existing data</a>
|
|
<a href="https://dawarich.app/docs/tutorials/export-your-data" target="_blank" class="link-hover text-sm">Exporting data</a>
|
|
<a href="https://dawarich.app/docs/FAQ" target="_blank" class="link-hover text-sm">FAQ</a>
|
|
<a href="https://dawarich.app/contact" target="_blank" class="link-hover text-sm">Contact</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<!-- More Section -->
|
|
<div>
|
|
<h4 class="font-semibold text-base mb-3">More</h4>
|
|
<div class="flex flex-col gap-2">
|
|
<a href="https://dawarich.app/privacy-policy" target="_blank" class="link-hover text-sm">Privacy policy</a>
|
|
<a href="https://dawarich.app/terms-and-conditions" target="_blank" class="link-hover text-sm">Terms and Conditions</a>
|
|
<a href="https://dawarich.app/refund-policy" target="_blank" class="link-hover text-sm">Refund policy</a>
|
|
<a href="https://dawarich.app/impressum" target="_blank" class="link-hover text-sm">Impressum</a>
|
|
<a href="https://dawarich.app/blog" target="_blank" class="link-hover text-sm">Blog</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<% end %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|