From c6b1cc23daff8f9a9ea7e476ba3028fd4d7e527f Mon Sep 17 00:00:00 2001 From: MH Hung Date: Wed, 1 Oct 2025 12:19:01 +0800 Subject: [PATCH] feat: require add mode before map click opens modal --- client/src/features/map/TravelMap.tsx | 43 +++++++++++++++---- .../visits/components/VisitSummary.tsx | 37 +++++++++------- client/src/locales/en/translation.json | 2 +- client/src/locales/zh-Hant/translation.json | 2 +- 4 files changed, 59 insertions(+), 25 deletions(-) diff --git a/client/src/features/map/TravelMap.tsx b/client/src/features/map/TravelMap.tsx index 12f4e27..5f1a355 100644 --- a/client/src/features/map/TravelMap.tsx +++ b/client/src/features/map/TravelMap.tsx @@ -1,6 +1,6 @@ import { MapContainer, Marker, Popup, TileLayer, useMapEvents } from 'react-leaflet'; import 'leaflet/dist/leaflet.css'; -import { useEffect, useRef } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import type { LeafletMouseEvent } from 'leaflet'; import { ensureLeafletConfig } from './leafletConfig'; import { useVisitModalStore } from '../../state/useVisitModalStore'; @@ -14,13 +14,20 @@ import AddIcon from '@mui/icons-material/Add'; const INITIAL_POSITION: [number, number] = [20, 0]; const INITIAL_ZOOM = 2; -function MapEvents() { - const openForCreate = useVisitModalStore((state) => state.openForCreate); +interface MapEventsProps { + enabled: boolean; + onSelect: (location: { lat: number; lng: number }) => void; +} +function MapEvents({ enabled, onSelect }: MapEventsProps) { useMapEvents({ click: (event: LeafletMouseEvent) => { + if (!enabled) { + return; + } + const { lat, lng } = event.latlng; - openForCreate({ lat, lng }); + onSelect({ lat, lng }); } }); @@ -43,6 +50,25 @@ export function TravelMap({ onTriggerCreate, sidebarOpen = true }: TravelMapProp const { t } = useTranslation(); const mapRef = useRef(null); useResizeMap(mapRef, [sidebarOpen]); + const [isAwaitingLocation, setIsAwaitingLocation] = useState(false); + + const handleMapSelection = useCallback( + ({ lat, lng }: { lat: number; lng: number }) => { + setIsAwaitingLocation(false); + + if (onTriggerCreate) { + onTriggerCreate({ lat, lng }); + return; + } + + openForCreate({ lat, lng }); + }, + [onTriggerCreate, openForCreate] + ); + + const handleFabClick = useCallback(() => { + setIsAwaitingLocation((previous) => !previous); + }, []); return ( @@ -51,7 +77,7 @@ export function TravelMap({ onTriggerCreate, sidebarOpen = true }: TravelMapProp mapRef.current = mapInstance; mapInstance.invalidateSize(); }} - className="h-full w-full z-0" + className={`h-full w-full z-0 ${isAwaitingLocation ? 'cursor-crosshair' : ''}`} center={INITIAL_POSITION} zoom={INITIAL_ZOOM} scrollWheelZoom @@ -60,7 +86,7 @@ export function TravelMap({ onTriggerCreate, sidebarOpen = true }: TravelMapProp attribution='\u00a9 OpenStreetMap' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> - + {visits.map((visit) => ( @@ -78,9 +104,10 @@ export function TravelMap({ onTriggerCreate, sidebarOpen = true }: TravelMapProp (onTriggerCreate ? onTriggerCreate() : openForCreate())} + onClick={handleFabClick} + aria-pressed={isAwaitingLocation} > diff --git a/client/src/features/visits/components/VisitSummary.tsx b/client/src/features/visits/components/VisitSummary.tsx index f542982..749f47c 100644 --- a/client/src/features/visits/components/VisitSummary.tsx +++ b/client/src/features/visits/components/VisitSummary.tsx @@ -1,6 +1,6 @@ import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { Card, CardContent, Grid, Paper, Typography } from '@mui/material'; +import { Box, Card, CardContent, Typography } from '@mui/material'; import { useVisitsQuery } from '../hooks/useVisitQueries'; export function VisitSummary() { @@ -22,25 +22,32 @@ export function VisitSummary() { ]; return ( - - + + {t('summary.title')} - + {summaryItems.map((item) => ( - - - - {item.label} - - - {item.value} - - - + + + {item.label} + + + {item.value} + + ))} - + ); diff --git a/client/src/locales/en/translation.json b/client/src/locales/en/translation.json index 8e8e70f..09511cf 100644 --- a/client/src/locales/en/translation.json +++ b/client/src/locales/en/translation.json @@ -14,7 +14,7 @@ }, "sidebar": { "title": "My Journeys", - "subtitle": "Click anywhere on the map to start recording", + "subtitle": "Tap the + button, then click the map to start recording", "add": "Add Visit", "drawerTitleCreate": "Add Visit", "drawerTitleEdit": "Edit Visit", diff --git a/client/src/locales/zh-Hant/translation.json b/client/src/locales/zh-Hant/translation.json index 813f953..8ec8b59 100644 --- a/client/src/locales/zh-Hant/translation.json +++ b/client/src/locales/zh-Hant/translation.json @@ -14,7 +14,7 @@ }, "sidebar": { "title": "我的旅程", - "subtitle": "點擊地圖任意位置開始記錄", + "subtitle": "先點右下角的+按鈕,再點地圖任一處開始記錄", "add": "新增足跡", "drawerTitleCreate": "新增足跡", "drawerTitleEdit": "編輯足跡",