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( - `