From df139a2d967d68efa8e90d3b6500d1e109e15d48 Mon Sep 17 00:00:00 2001 From: Maurice Date: Fri, 21 Aug 2026 12:47:15 +0200 Subject: [PATCH] fix(ui): point import controls inward and give Google Maps its own mark MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every import button in the client wore an outgoing arrow, so the control that pulls a booking or a place list in looked like it was about to send something out. The places-import sheet managed to contradict itself inside one screen: Download in the header, Upload on the row below it, Upload again on the file step. Imports now take Download, or FileDown where the button opens a file picker; genuine uploads and the desktop drop zones keep Upload. The mobile places-import entry point wore an ellipsis, which promises a menu. The "open in Google Maps" buttons drew the four-segment Google *search* G, flattened to one colour — a glyph that reads as "run a search". They get a map pin instead, factored into one component so the desktop and mobile copies stop drifting apart. Closes #2005 Closes #2006 --- .../Packing/PackingListPanel.test.tsx | 12 +++--- .../Packing/PackingListPanelHeader.tsx | 4 +- .../Packing/PackingListPanelImportModal.tsx | 4 +- .../src/components/Planner/DayPlanSidebar.tsx | 8 +--- .../Planner/PlacesSidebarHeader.tsx | 4 +- .../components/shared/GoogleMapsIcon.test.tsx | 35 ++++++++++++++++ .../src/components/shared/GoogleMapsIcon.tsx | 42 +++++++++++++++++++ .../screens/trip/places/MPlacesBrowser.tsx | 4 +- .../screens/trip/sheets/ImpFileStep.tsx | 4 +- .../screens/trip/sheets/MImportSheet.tsx | 4 +- .../screens/trip/tabs/MPackingImportSheet.tsx | 4 +- .../screens/trip/tabs/MPackingListTab.tsx | 4 +- client/src/pages/TripPlannerPage.tsx | 4 +- 13 files changed, 103 insertions(+), 30 deletions(-) create mode 100644 client/src/components/shared/GoogleMapsIcon.test.tsx create mode 100644 client/src/components/shared/GoogleMapsIcon.tsx diff --git a/client/src/components/Packing/PackingListPanel.test.tsx b/client/src/components/Packing/PackingListPanel.test.tsx index 9f18e9da3..a86cb9d28 100644 --- a/client/src/components/Packing/PackingListPanel.test.tsx +++ b/client/src/components/Packing/PackingListPanel.test.tsx @@ -447,7 +447,7 @@ describe('PackingListPanel', () => { const { container } = render(); // Click the Import button (Upload icon in the header) - const importBtn = container.querySelector('svg.lucide-upload')?.closest('button'); + const importBtn = container.querySelector('svg.lucide-download')?.closest('button'); expect(importBtn).toBeTruthy(); await user.click(importBtn!); @@ -538,7 +538,7 @@ describe('PackingListPanel', () => { const { container } = render(); // Open import modal - const importBtn = container.querySelector('svg.lucide-upload')?.closest('button'); + const importBtn = container.querySelector('svg.lucide-download')?.closest('button'); await user.click(importBtn!); await screen.findByText('Import Packing List'); @@ -655,7 +655,7 @@ describe('PackingListPanel', () => { const { container } = render(); // Open import modal - const importBtn = container.querySelector('svg.lucide-upload')?.closest('button'); + const importBtn = container.querySelector('svg.lucide-download')?.closest('button'); await user.click(importBtn!); await screen.findByText('Import Packing List'); @@ -929,7 +929,7 @@ describe('PackingListPanel', () => { const { container } = render(); // Open import modal - const importBtn = container.querySelector('svg.lucide-upload')?.closest('button'); + const importBtn = container.querySelector('svg.lucide-download')?.closest('button'); await user.click(importBtn!); await screen.findByText('Import Packing List'); @@ -1215,7 +1215,7 @@ describe('PackingListPanel', () => { const { container } = render(); // Open import modal - const importBtn = container.querySelector('svg.lucide-upload')?.closest('button'); + const importBtn = container.querySelector('svg.lucide-download')?.closest('button'); await user.click(importBtn!); await screen.findByText('Import Packing List'); @@ -1500,7 +1500,7 @@ describe('PackingListPanel', () => { const { container } = render(); // Open import modal - const importBtn = container.querySelector('svg.lucide-upload')?.closest('button'); + const importBtn = container.querySelector('svg.lucide-download')?.closest('button'); await user.click(importBtn!); await screen.findByText('Import Packing List'); diff --git a/client/src/components/Packing/PackingListPanelHeader.tsx b/client/src/components/Packing/PackingListPanelHeader.tsx index 2b66d7aed..4e49baa36 100644 --- a/client/src/components/Packing/PackingListPanelHeader.tsx +++ b/client/src/components/Packing/PackingListPanelHeader.tsx @@ -1,5 +1,5 @@ import { - X, Check, CheckCheck, Luggage, Package, FolderPlus, Upload, + X, Check, CheckCheck, Luggage, Package, FolderPlus, Download, } from 'lucide-react' import type { PackingState } from './usePackingListPanel' @@ -46,7 +46,7 @@ export function PackingHeader(S: PackingState) { border: '1px solid var(--border-primary)', fontSize: 'calc(12px * var(--fs-scale-body, 1))', fontWeight: 500, cursor: 'pointer', fontFamily: 'inherit', background: 'var(--bg-card)', color: 'var(--text-muted)', }}> - {t('packing.import')} + {t('packing.import')} )} {inlineHeader && canEdit && abgehakt > 0 && ( diff --git a/client/src/components/Packing/PackingListPanelImportModal.tsx b/client/src/components/Packing/PackingListPanelImportModal.tsx index 3b70871d3..05285acf5 100644 --- a/client/src/components/Packing/PackingListPanelImportModal.tsx +++ b/client/src/components/Packing/PackingListPanelImportModal.tsx @@ -1,5 +1,5 @@ import { createPortal } from 'react-dom' -import { Upload } from 'lucide-react' +import { FileDown } from 'lucide-react' import type { PackingState } from './usePackingListPanel' export function BulkImportModal(S: PackingState) { @@ -48,7 +48,7 @@ export function BulkImportModal(S: PackingState) { border: '1px dashed var(--border-primary)', borderRadius: 8, background: 'none', fontSize: 'calc(11px * var(--fs-scale-caption, 1))', color: 'var(--text-faint)', cursor: 'pointer', fontFamily: 'inherit', }}> - {t('packing.importCsv')} + {t('packing.importCsv')}
diff --git a/client/src/components/Planner/DayPlanSidebar.tsx b/client/src/components/Planner/DayPlanSidebar.tsx index adf236ce3..381a67f10 100644 --- a/client/src/components/Planner/DayPlanSidebar.tsx +++ b/client/src/components/Planner/DayPlanSidebar.tsx @@ -9,6 +9,7 @@ import { ChevronDown, ChevronRight, ChevronUp, Compass, Navigation, RotateCcw, E import { type PickedPlace } from './TransitSearchPanel' import { assignmentsApi, reservationsApi, daysApi } from '../../api/client' import { calculateRouteWithLegs, optimizeRoute, generateGoogleMapsUrl, generateCoMapsUrl, type NamedWaypoint } from '../Map/RouteCalculator' +import GoogleMapsIcon from '../shared/GoogleMapsIcon' import PlaceAvatar from '../shared/PlaceAvatar' import ConfirmDialog from '../shared/ConfirmDialog' import { useContextMenu, ContextMenu } from '../shared/ContextMenu' @@ -2583,12 +2584,7 @@ const DayPlanSidebar = React.memo(function DayPlanSidebar(props: DayPlanSidebarP cursor: 'pointer', fontFamily: 'inherit', flexShrink: 0, }} > - + {/* The same day, handed to CoMaps for offline navigation (#1904). The day's own travel mode rides along, so the route it builds walks diff --git a/client/src/components/Planner/PlacesSidebarHeader.tsx b/client/src/components/Planner/PlacesSidebarHeader.tsx index bd7e97775..c8a52e48d 100644 --- a/client/src/components/Planner/PlacesSidebarHeader.tsx +++ b/client/src/components/Planner/PlacesSidebarHeader.tsx @@ -1,4 +1,4 @@ -import { Search, Plus, X, Upload, ChevronDown, Check, MapPin, Star } from 'lucide-react' +import { Search, Plus, X, Upload, FileDown, ChevronDown, Check, MapPin, Star } from 'lucide-react' import { getCategoryIcon } from '../shared/categoryIcons' import Tooltip from '../shared/Tooltip' import type { SidebarState } from './usePlacesSidebar' @@ -54,7 +54,7 @@ export function PlacesHeader(S: SidebarState) { cursor: 'pointer', fontFamily: 'inherit', }} > - {t('places.importFile')} + {t('places.importFile')} )}
diff --git a/client/src/mobile/screens/trip/sheets/ImpFileStep.tsx b/client/src/mobile/screens/trip/sheets/ImpFileStep.tsx index a0d4e14b1..d5bc3514e 100644 --- a/client/src/mobile/screens/trip/sheets/ImpFileStep.tsx +++ b/client/src/mobile/screens/trip/sheets/ImpFileStep.tsx @@ -1,5 +1,5 @@ import { useRef, useState } from 'react' -import { Check, Upload } from 'lucide-react' +import { Check, FileDown } from 'lucide-react' import { placesApi } from '../../../../api/client' import { Eyebrow, FormSheetFooter } from './PlSheetChrome' import type { TripPlanner } from '../MTripShell' @@ -170,7 +170,7 @@ export default function ImpFileStep({ planner, onBack, onDone }: ImpFileStepProp onClick={() => inputRef.current?.click()} className="mt-3 flex min-h-[88px] w-full flex-col items-center justify-center gap-[6px] rounded-[14px] border-[1.5px] border-dashed border-[color:var(--m-trackoff)] p-4" > - + {files.length > 0 ? ( {files.map(f => f.name).join(', ')} diff --git a/client/src/mobile/screens/trip/sheets/MImportSheet.tsx b/client/src/mobile/screens/trip/sheets/MImportSheet.tsx index dc2d68a62..6c62b82f3 100644 --- a/client/src/mobile/screens/trip/sheets/MImportSheet.tsx +++ b/client/src/mobile/screens/trip/sheets/MImportSheet.tsx @@ -1,5 +1,5 @@ import { useEffect, useState, type ReactNode } from 'react' -import { ChevronLeft, ChevronRight, Download, MapPin, Upload } from 'lucide-react' +import { ChevronLeft, ChevronRight, Download, FileDown, MapPin } from 'lucide-react' import type { LucideIcon } from 'lucide-react' import MSheet from '../../../components/MSheet' import MIconBtn from '../../../components/MIconBtn' @@ -51,7 +51,7 @@ export default function MImportSheet({ planner, open, onClose }: MImportSheetPro {step === 'menu' && (
setStep('file')} diff --git a/client/src/mobile/screens/trip/tabs/MPackingImportSheet.tsx b/client/src/mobile/screens/trip/tabs/MPackingImportSheet.tsx index 3a83455b2..ed7339858 100644 --- a/client/src/mobile/screens/trip/tabs/MPackingImportSheet.tsx +++ b/client/src/mobile/screens/trip/tabs/MPackingImportSheet.tsx @@ -1,5 +1,5 @@ import { useRef, useState } from 'react' -import { Upload } from 'lucide-react' +import { FileDown } from 'lucide-react' import MSheet from '../../../components/MSheet' import { Eyebrow, FIELD_AREA_CLS, FormSheetFooter, FormSheetHeader } from '../sheets/PlSheetChrome' import { packingApi } from '../../../../api/client' @@ -74,7 +74,7 @@ export default function MPackingImportSheet({ planner, open, onClose }: MPacking onClick={() => fileInputRef.current?.click()} className="mt-2 flex items-center gap-[6px] rounded-full border border-[color:var(--m-rowbr)] bg-[color:var(--m-ic)] px-[13px] py-[7px] text-[0.75rem] font-semibold text-m-muted" > - + {t('packing.importCsv')} diff --git a/client/src/mobile/screens/trip/tabs/MPackingListTab.tsx b/client/src/mobile/screens/trip/tabs/MPackingListTab.tsx index eab16002c..65be985a3 100644 --- a/client/src/mobile/screens/trip/tabs/MPackingListTab.tsx +++ b/client/src/mobile/screens/trip/tabs/MPackingListTab.tsx @@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from 'react' import type { LucideIcon } from 'lucide-react' import { Briefcase, Check, CheckCheck, ChevronDown, ChevronUp, HandHelping, - LayoutTemplate, MoreHorizontal, Package, Pencil, Plus, RotateCcw, Save as SaveIcon, Trash2, Upload, UserPlus, UserRound, + Download, LayoutTemplate, MoreHorizontal, Package, Pencil, Plus, RotateCcw, Save as SaveIcon, Trash2, UserPlus, UserRound, } from 'lucide-react' import MDancingTrek from '../../../components/MDancingTrek' import { useAuthStore } from '../../../../store/authStore' @@ -323,7 +323,7 @@ export default function MPackingListTab({ planner }: { planner: TripPlanner }) { {isAdmin && items.length > 0 && ( setActionView('save')} /> )} - { setActionsOpen(false); setShowImportSheet(true) }} /> + { setActionsOpen(false); setShowImportSheet(true) }} /> )} {actionView === 'apply' && ( diff --git a/client/src/pages/TripPlannerPage.tsx b/client/src/pages/TripPlannerPage.tsx index 909b5135a..e7fc38464 100644 --- a/client/src/pages/TripPlannerPage.tsx +++ b/client/src/pages/TripPlannerPage.tsx @@ -43,7 +43,7 @@ import { useRouteCalculation } from '../hooks/useRouteCalculation' import { usePlaceSelection } from '../hooks/usePlaceSelection' import { usePlannerHistory } from '../hooks/usePlannerHistory' import type { Accommodation, TripMember, Day, Place, Reservation, PackingItem, TodoItem } from '../types' -import { ListTodo, Upload, Plus, Trash2, FolderPlus } from 'lucide-react' +import { ListTodo, Download, Plus, Trash2, FolderPlus } from 'lucide-react' import { useTripPlanner } from './tripPlanner/useTripPlanner' import { usePoiExplore } from '../components/Map/usePoiExplore' import PoiCategoryPill from '../components/Map/PoiCategoryPill' @@ -189,7 +189,7 @@ function ListsContainer({ tripId, packingItems, todoItems }: { tripId: number; p className={`${sharedBtnClass} bg-accent text-accent-text`} style={sharedBtnStyle} > - + {t('packing.import')}