diff --git a/admin/app/controllers/maps_controller.ts b/admin/app/controllers/maps_controller.ts
index 31035ee..dd93a8b 100644
--- a/admin/app/controllers/maps_controller.ts
+++ b/admin/app/controllers/maps_controller.ts
@@ -167,11 +167,19 @@ export default class MapsController {
vine.object({
name: vine.string().trim().minLength(1).maxLength(255).optional(),
color: vine.string().trim().maxLength(20).optional(),
+ longitude: vine.number().min(-180).max(180).optional(),
+ latitude: vine.number().min(-90).max(90).optional(),
+ notes: vine.string().trim().nullable().optional(),
+ marker_type: vine.string().trim().maxLength(20).optional(),
})
)
)
if (payload.name !== undefined) marker.name = payload.name
if (payload.color !== undefined) marker.color = payload.color
+ if (payload.longitude !== undefined) marker.longitude = payload.longitude
+ if (payload.latitude !== undefined) marker.latitude = payload.latitude
+ if (payload.notes !== undefined) marker.notes = payload.notes
+ if (payload.marker_type !== undefined) marker.marker_type = payload.marker_type
await marker.save()
return marker
}
diff --git a/admin/inertia/components/maps/MapComponent.tsx b/admin/inertia/components/maps/MapComponent.tsx
index d738638..1fe5208 100644
--- a/admin/inertia/components/maps/MapComponent.tsx
+++ b/admin/inertia/components/maps/MapComponent.tsx
@@ -16,7 +16,7 @@ import MarkerPin from './MarkerPin'
import MarkerPanel from './MarkerPanel'
import ViewMapMarkerPopup from './ViewMapMarkerPopup'
import MapMarkerFormPopup from './MapMarkerFormPopup'
-import ScaleUnitControl from './ScaleUnitControl'
+import ScaleUnitToggle from './ScaleUnitToggle'
type ScaleUnit = 'imperial' | 'metric'
@@ -94,7 +94,7 @@ export default function MapComponent() {
-
+
{markers.map((marker) => (
)}
- {selectedMarker && editingMarkerId !== selectedMarker.id && (
- setSelectedMarkerId(null)}
- onEdit={() => setEditingMarkerId(selectedMarker.id)}
- />
- )}
-
{selectedMarker && editingMarkerId === selectedMarker.id && (
{
- const preset = PIN_COLORS.find((pinColor) => pinColor.id === color)
-
- if (preset) return preset.label
-
- return color.replace('#', '').toLowerCase()
-}
-
type SortField = 'name' | 'color'
type SortDirection = 'asc' | 'desc'
+const normalizeColorHex = (color: string) => {
+ const preset = PIN_COLORS.find((pinColor) => pinColor.id === color)
+ return preset?.hex ?? color
+}
+
+const getHueSortValue = (color: string) => {
+ const hex = normalizeColorHex(color).replace('#', '')
+
+ if (!/^[0-9a-fA-F]{6}$/.test(hex)) return 0
+
+ const r = parseInt(hex.slice(0, 2), 16) / 255
+ const g = parseInt(hex.slice(2, 4), 16) / 255
+ const b = parseInt(hex.slice(4, 6), 16) / 255
+
+ const max = Math.max(r, g, b)
+ const min = Math.min(r, g, b)
+ const delta = max - min
+
+ if (delta === 0) return 0
+
+ let hue = 0
+
+ if (max === r) {
+ hue = ((g - b) / delta) % 6
+ } else if (max === g) {
+ hue = (b - r) / delta + 2
+ } else {
+ hue = (r - g) / delta + 4
+ }
+
+ return Math.round(hue * 60 + 360) % 360
+}
+
export default function MarkerPanel({
markers,
onDelete,
@@ -35,6 +60,15 @@ export default function MarkerPanel({
const [sortField, setSortField] = useState('name')
const [sortDirection, setSortDirection] = useState('asc')
+ const sortDirectionLabel =
+ sortField === 'name'
+ ? sortDirection === 'asc'
+ ? 'A → Z'
+ : 'Z → A'
+ : sortDirection === 'asc'
+ ? 'Hue ↑'
+ : 'Hue ↓'
+
const visibleMarkers = useMemo(() => {
const query = searchQuery.trim().toLowerCase()
@@ -44,12 +78,10 @@ export default function MarkerPanel({
return marker.name.toLowerCase().includes(query)
})
.sort((a, b) => {
- const aValue = sortField === 'name' ? a.name : getColorSortValue(a.color)
- const bValue = sortField === 'name' ? b.name : getColorSortValue(b.color)
-
- const result = aValue.localeCompare(bValue, undefined, {
- sensitivity: 'base',
- })
+ const result =
+ sortField === 'name'
+ ? a.name.localeCompare(b.name, undefined, { sensitivity: 'base' })
+ : getHueSortValue(a.color) - getHueSortValue(b.color)
return sortDirection === 'asc' ? result : -result
})
@@ -116,7 +148,7 @@ export default function MarkerPanel({
className="flex-1 rounded border border-border-default bg-surface-primary px-2 py-1 text-xs text-text-primary focus:border-desert-green focus:outline-none"
>
-
+
@@ -154,7 +186,7 @@ export default function MarkerPanel({
size={16}
className="shrink-0"
style={{
- color: PIN_COLORS.find((color) => color.id === marker.color)?.hex ?? '#a84a12',
+ color: normalizeColorHex(marker.color),
}}
/>
diff --git a/admin/inertia/components/maps/ScaleUnitControl.tsx b/admin/inertia/components/maps/ScaleUnitToggle.tsx
similarity index 93%
rename from admin/inertia/components/maps/ScaleUnitControl.tsx
rename to admin/inertia/components/maps/ScaleUnitToggle.tsx
index 06d94f9..32cb788 100644
--- a/admin/inertia/components/maps/ScaleUnitControl.tsx
+++ b/admin/inertia/components/maps/ScaleUnitToggle.tsx
@@ -5,7 +5,7 @@ type ScaleUnitControlProps = {
onChange: (unit: ScaleUnit) => void
}
-export default function ScaleUnitControl({ scaleUnit, onChange }: ScaleUnitControlProps) {
+export default function ScaleUnitToggle({ scaleUnit, onChange }: ScaleUnitControlProps) {
return (