From 59226491f2cb4af5f11a6bc6d6b562439fb15177 Mon Sep 17 00:00:00 2001 From: orangecoding Date: Mon, 26 Jan 2026 11:20:02 +0100 Subject: [PATCH] improved tooltip in map, improved user-settings handling --- lib/api/routes/userSettingsRoute.js | 14 ++++-- lib/services/storage/settingsStorage.js | 2 +- ui/src/App.less | 2 + ui/src/views/listings/Map.jsx | 49 ++++++++++++++++--- ui/src/views/listings/Map.less | 56 +++++++++++++++++++++- ui/src/views/userSettings/UserSettings.jsx | 3 +- 6 files changed, 110 insertions(+), 16 deletions(-) diff --git a/lib/api/routes/userSettingsRoute.js b/lib/api/routes/userSettingsRoute.js index 956b1f4..ba82749 100644 --- a/lib/api/routes/userSettingsRoute.js +++ b/lib/api/routes/userSettingsRoute.js @@ -12,6 +12,7 @@ import { calculateDistanceForUser } from '../../services/geocoding/distanceServi import { fromJson } from '../../utils.js'; import { trackFeature } from '../../services/tracking/Tracker.js'; import { FEATURES } from '../../features.js'; +import logger from '../../services/logger.js'; const service = restana(); const userSettingsRouter = service.newRouter(); @@ -34,11 +35,12 @@ userSettingsRouter.get('/autocomplete', async (req, res) => { res.body = results; res.send(); } catch (error) { - res.status(500).send({ error: error.message }); + res.statusCode = 500; + res.send({ error: error.message }); } }); -userSettingsRouter.post('/', async (req, res) => { +userSettingsRouter.post('/home-address', async (req, res) => { const userId = req.session.currentUser; const { home_address } = req.body; @@ -51,15 +53,17 @@ userSettingsRouter.post('/', async (req, res) => { calculateDistanceForUser(userId); res.send({ success: true, coords }); } else { - res.status(400).send({ error: 'Could not geocode address' }); + res.statusCode = 400; + res.send({ error: 'Could not geocode address' }); } } else { - // If address is empty, maybe clear it? upsertSettings({ home_address: null }, userId); res.send({ success: true }); } } catch (error) { - res.status(500).send({ error: error.message }); + logger.error('Error updating home address settings', error); + res.statusCode = 500; + res.send({ error: error.message }); } }); diff --git a/lib/services/storage/settingsStorage.js b/lib/services/storage/settingsStorage.js index 92cb506..def3d44 100644 --- a/lib/services/storage/settingsStorage.js +++ b/lib/services/storage/settingsStorage.js @@ -92,7 +92,7 @@ export function upsertSettings(settingsMapOrEntry, userId = null) { for (const [name, rawValue] of entries) { const id = nanoid(); const create_date = Date.now(); - const json = toJson(rawValue); + const json = toJson(rawValue === null ? 'null' : rawValue); SqliteConnection.execute( `INSERT INTO settings (id, create_date, name, value, user_id) VALUES (@id, @create_date, @name, @value, @userId) diff --git a/ui/src/App.less b/ui/src/App.less index b9e881e..3168d09 100644 --- a/ui/src/App.less +++ b/ui/src/App.less @@ -40,6 +40,8 @@ a:active { text-decoration: underline; } +a {outline : none;} + .semi-icon:not(.semi-tabs-bar .semi-tabs-tab .semi-icon) { vertical-align: middle; } diff --git a/ui/src/views/listings/Map.jsx b/ui/src/views/listings/Map.jsx index 721071d..2c9587d 100644 --- a/ui/src/views/listings/Map.jsx +++ b/ui/src/views/listings/Map.jsx @@ -4,16 +4,20 @@ */ import React, { useEffect, useRef, useState } from 'react'; +import { renderToString } from 'react-dom/server'; import maplibregl from 'maplibre-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; import { useSelector, useActions } from '../../services/state/store.js'; import { distanceMeters, generateCircleCoords, getBoundsFromCenter } from './mapUtils.js'; -import { Select, Space, Typography, Button, Popover, Divider, Switch, Banner } from '@douyinfe/semi-ui-19'; -import { IconFilter } from '@douyinfe/semi-icons'; +import { Select, Space, Typography, Button, Popover, Divider, Switch, Banner, Toast } from '@douyinfe/semi-ui-19'; +import { IconFilter, IconLink } from '@douyinfe/semi-icons'; +import { IconDelete } from '@douyinfe/semi-icons'; + import no_image from '../../assets/no_image.jpg'; import RangeSlider from 'react-range-slider-input'; import 'react-range-slider-input/dist/style.css'; import './Map.less'; +import { xhrDelete } from '../../services/xhr.js'; const { Text } = Typography; @@ -97,6 +101,22 @@ export default function MapView() { return listings.filter((listing) => listing.price && listing.price >= min && listing.price <= max); }; + useEffect(() => { + window.deleteListing = async (id) => { + try { + await xhrDelete('/api/listings/', { ids: [id] }); + Toast.success('Listing successfully removed'); + fetchListings(); + } catch (error) { + Toast.error(error.message || 'Error deleting listing'); + } + }; + + return () => { + delete window.deleteListing; + }; + }, []); + useEffect(() => { if (map.current) return; @@ -325,8 +345,8 @@ export default function MapView() { ? listing.provider.charAt(0).toUpperCase() + listing.provider.slice(1) : 'N/A'; - const popup = new maplibregl.Popup({ offset: 25 }).setHTML( - `
+ const popupContent = ` +
${listing.title}

${listing.title}

@@ -334,10 +354,25 @@ export default function MapView() { Address: ${listing.address || 'N/A'} Job: ${listing.job_name || 'N/A'} Provider: ${capitalizedProvider} - View Listing + Size: ${listing.size != null ? `${listing.size} m²` : 'N/A'} +
+ + +
-
`, - ); +
`; + + const popup = new maplibregl.Popup({ offset: 25 }).setHTML(popupContent); let color = '#3FB1CE'; // Default blue-ish if (distanceFilter > 0 && homeAddress?.coords) { diff --git a/ui/src/views/listings/Map.less b/ui/src/views/listings/Map.less index 75f5e48..b3a7249 100644 --- a/ui/src/views/listings/Map.less +++ b/ui/src/views/listings/Map.less @@ -43,10 +43,62 @@ } .info { - font-size: 0.9rem; + font-size: 0.8rem; display: flex; flex-direction: column; - gap: 0.2rem; + gap: 0.1rem; + } + + &__linkButton { + background: var(--semi-color-primary); + color: white; + font-size: 14px; + line-height: 20px; + font-weight: 600; + height: 32px; + width: 32px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 6px; + + a { + color: white; + display: flex; + align-items: center; + justify-content: center; + text-decoration: none; + width: 100%; + height: 100%; + } + + &:hover { + background: var(--semi-color-primary-hover); + cursor: pointer; + } + } + + &__deleteButton { + background: var(--semi-color-danger); + color: white; + border: none; + font-size: 14px; + height: 32px; + width: 32px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 6px; + cursor: pointer; + padding: 0; + + &:hover { + background: var(--semi-color-danger-hover); + } + + svg { + fill: currentColor; + } } } diff --git a/ui/src/views/userSettings/UserSettings.jsx b/ui/src/views/userSettings/UserSettings.jsx index 3552128..4ca57fb 100644 --- a/ui/src/views/userSettings/UserSettings.jsx +++ b/ui/src/views/userSettings/UserSettings.jsx @@ -29,7 +29,7 @@ const UserSettings = () => { const handleSave = async () => { setSaving(true); try { - const response = await xhrPost('/api/user/settings', { home_address: address }); + const response = await xhrPost('/api/user/settings/home-address', { home_address: address }); if (response.status === 200) { setCoords(response.json.coords); await actions.userSettings.getUserSettings(); @@ -81,6 +81,7 @@ const UserSettings = () => { setAddress(v)} onSearch={searchAddress} placeholder="Enter your home address"