diff --git a/client/src/components/Trips/TripMembersModal.test.tsx b/client/src/components/Trips/TripMembersModal.test.tsx
index 6abe9e1f8..157d7b640 100644
--- a/client/src/components/Trips/TripMembersModal.test.tsx
+++ b/client/src/components/Trips/TripMembersModal.test.tsx
@@ -1,6 +1,6 @@
-// FE-COMP-MEMBERS-001 to FE-COMP-MEMBERS-061
+// FE-COMP-MEMBERS-001 to FE-COMP-MEMBERS-063
import type { Mock } from 'vitest';
-import { act, render, screen, fireEvent, waitFor } from '../../../tests/helpers/render';
+import { act, render, screen, fireEvent, waitFor, within } from '../../../tests/helpers/render';
import userEvent from '@testing-library/user-event';
import { delay, http, HttpResponse } from 'msw';
import { server } from '../../../tests/helpers/msw/server';
@@ -1107,4 +1107,24 @@ describe('TripMembersModal', () => {
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('/join/inv9'));
view.unmount();
});
+
+ it('FE-COMP-MEMBERS-062: Edit Trip in the side column hands over to the trip form (#2811)', async () => {
+ const user = userEvent.setup();
+ const onEditTrip = vi.fn();
+ render();
+
+ // The column stands for it even where People is the only area.
+ const nav = await screen.findByRole('navigation', { name: 'Trip settings' });
+ await user.click(within(nav).getByRole('button', { name: 'Edit Trip' }));
+
+ expect(onEditTrip).toHaveBeenCalledTimes(1);
+ expect(within(nav).getByText('Title, dates, cover, description and currency.')).toBeInTheDocument();
+ });
+
+ it('FE-COMP-MEMBERS-063: without the right to edit the trip, the dialog offers no way to its form', async () => {
+ render();
+
+ expect(await screen.findByText('owner')).toBeInTheDocument();
+ expect(screen.queryByRole('button', { name: 'Edit Trip' })).toBeNull();
+ });
});
diff --git a/client/src/components/Trips/TripMembersModal.tsx b/client/src/components/Trips/TripMembersModal.tsx
index 8ff93705b..d9d8a228a 100644
--- a/client/src/components/Trips/TripMembersModal.tsx
+++ b/client/src/components/Trips/TripMembersModal.tsx
@@ -8,6 +8,8 @@ interface TripMembersModalProps {
/** Called after the roster changes (guest/member added, renamed or removed) so the
* planner can refresh its members for Costs participants, Collab, etc. */
onMembersChanged?: () => void
+ /** Opens the trip's form from the dialog, for someone who may edit the trip (#2811). */
+ onEditTrip?: () => void
}
/** Share: the trip's members, guests and links. The phone has its own sheet (MTripSettingsSheet) on the same hooks. */
diff --git a/client/src/components/Trips/TripShareDialog.tsx b/client/src/components/Trips/TripShareDialog.tsx
index e08913fba..fcf168fbc 100644
--- a/client/src/components/Trips/TripShareDialog.tsx
+++ b/client/src/components/Trips/TripShareDialog.tsx
@@ -18,6 +18,8 @@ interface TripShareDialogProps {
tripId: number
tripTitle: string
onMembersChanged?: () => void
+ /** Hands over to the trip's own form; left out for someone who may not edit the trip (#2811). */
+ onEditTrip?: () => void
}
/** A question the dialog asks before it acts, answered in a ConfirmDialog over it. */
@@ -35,6 +37,8 @@ const BADGE = 'inline-flex flex-none items-center gap-1 rounded-full px-2 py-[2p
/** A fixed height on a wide screen, so the dialog does not jump when another area is picked. */
const BODY = 'flex min-h-0 flex-1 max-md:flex-col md:h-[min(640px,calc(100dvh-240px))]'
const NAV = 'flex flex-none gap-1 border-edge-faint bg-surface-secondary p-3 md:w-[236px] md:flex-col md:border-e max-md:overflow-x-auto max-md:border-b'
+const NAV_ENTRY = 'flex flex-none items-center gap-2.5 rounded-[12px] px-2.5 py-2 text-start transition-colors'
+const NAV_ENTRY_OFF = 'text-content-muted hover:bg-surface-hover hover:text-content'
const DANGER_BUTTON = 'inline-flex items-center gap-1.5 rounded-[10px] bg-surface-card px-3.5 py-2 font-medium text-danger shadow-sm ring-1 ring-edge-faint hover:bg-danger-soft disabled:cursor-default disabled:opacity-50'
/**
@@ -54,7 +58,7 @@ const AREAS: { id: Area; icon: (on: boolean) => ReactNode; labelKey: string; hin
{ id: 'journal', icon: () => , labelKey: 'trip.journal.title', hintKey: 'trip.journal.hint' },
]
-function OpenDialog({ onClose, tripId, tripTitle, onMembersChanged }: TripShareDialogProps) {
+function OpenDialog({ onClose, tripId, tripTitle, onMembersChanged, onEditTrip }: TripShareDialogProps) {
const { t } = useTranslation()
const labelId = useId()
const m = useTripMembers({ isOpen: true, tripId, onClose, onMembersChanged })
@@ -65,13 +69,15 @@ function OpenDialog({ onClose, tripId, tripTitle, onMembersChanged }: TripShareD
const current = areas.find(a => a.id === area) ?? areas[0]
const panelOf = (id: Area) => areas.find(a => a.id === id)
const persons = `${m.realMembers.length} ${t('members.persons', { count: m.realMembers.length })}`
+ // The way to the trip's form needs the side nav even where People is the only area.
+ const withNav = areas.length > 1 || !!onEditTrip
return (
<>
1 ? 'wide' : 'detail'}
+ width={withNav ? 'wide' : 'detail'}
blocked={!!ask}
bodyClassName={BODY}
header={(
@@ -91,10 +97,10 @@ function OpenDialog({ onClose, tripId, tripTitle, onMembersChanged }: TripShareD
/>
)}
>
- {areas.length > 1 && }
+ {withNav && }
{/* Every area stays mounted, so its links and lists are loaded by the time it is picked. */}
- 1}>
+
{m.canManageMembers && }
{(m.isCurrentOwner || m.guests.length > 0) && }
@@ -124,8 +130,11 @@ function OpenDialog({ onClose, tripId, tripTitle, onMembersChanged }: TripShareD
)
}
-/** The areas down the side on a wide screen, a row of pills above the content on a narrow one. */
-function AreaNav({ areas, current, onPick }: { areas: typeof AREAS; current: Area; onPick: (area: Area) => void }) {
+/**
+ * The areas down the side on a wide screen, a row of pills above the content on a narrow one.
+ * Edit trip sits at the foot of the column: it opens the trip's form rather than an area of its own.
+ */
+function AreaNav({ areas, current, onPick, onEditTrip }: { areas: typeof AREAS; current: Area; onPick: (area: Area) => void; onEditTrip?: () => void }) {
const { t } = useTranslation()
return (
)
}
+/** An entry's tile, its name and, on a wide screen, the line under the name. */
+function NavEntryText({ icon, on, label, hint }: { icon: ReactNode; on: boolean; label: string; hint: string }) {
+ return (
+ <>
+ {icon}
+
+ {label}
+ {hint}
+
+ >
+ )
+}
+
/** One area: its name and what it is for, then its parts. Hidden rather than unmounted while another one shows. */
function AreaPanel({ area, shown, titled, children }: { area: (typeof AREAS)[number] | undefined; shown: boolean; titled: boolean; children: ReactNode }) {
const { t } = useTranslation()
diff --git a/client/src/pages/TripPlannerPage.tsx b/client/src/pages/TripPlannerPage.tsx
index 8b3c6ca05..d6053c365 100644
--- a/client/src/pages/TripPlannerPage.tsx
+++ b/client/src/pages/TripPlannerPage.tsx
@@ -92,7 +92,7 @@ function TripPlannerPageDesktop(): React.ReactElement | null {
highlightedAlternative, setHighlightedAlternative,
moveRoadtripStopToDay,
dropPoiOnRoute,
- showTripForm, setShowTripForm, showMembersModal, setShowMembersModal,
+ showTripForm, setShowTripForm, showMembersModal, setShowMembersModal, editTripFromSettings,
showReservationModal, setShowReservationModal, editingReservation, setEditingReservation,
showBookingImport, setShowBookingImport, bookingImportKind, setBookingImportKind, bookingImportAvailable,
airTrailAvailable, showAirTrailImport, setShowAirTrailImport,
@@ -830,7 +830,7 @@ function TripPlannerPageDesktop(): React.ReactElement | null {
trip={trip}
onCoverUpdate={applyTripCoverUpdate}
/>
- setShowMembersModal(false)} tripId={tripId} tripTitle={trip?.title} onMembersChanged={refreshMembers} />
+ setShowMembersModal(false)} tripId={tripId} tripTitle={trip?.title} onMembersChanged={refreshMembers} onEditTrip={can('trip_edit', trip) ? editTripFromSettings : undefined} />
{ if (importReviewActive) { advanceImportReview() } else { setShowReservationModal(false); setEditingReservation(null); setBookingForAssignmentId(null) } }} onSave={async (data, steps) => { const r = await handleSaveReservation(data, steps); if (importReviewActive && r) advanceImportReview(); return r }} reservation={editingReservation} prefill={reservationPrefill} days={days} places={places} assignments={assignments} selectedDayId={selectedDayId} files={files} onFileUpload={canUploadFiles ? (fd) => tripActions.addFile(tripId, fd) : undefined} onFileDelete={(id) => tripActions.deleteFile(tripId, id)} accommodations={tripAccommodations} defaultAssignmentId={bookingForAssignmentId} onOpenExpense={openBookingExpense} tripMembers={tripMembers} />
{showTransportModal && (
diff --git a/client/src/pages/TripPlannerPage.wiring.fixture.ts b/client/src/pages/TripPlannerPage.wiring.fixture.ts
index ca43e059b..8b77f8622 100644
--- a/client/src/pages/TripPlannerPage.wiring.fixture.ts
+++ b/client/src/pages/TripPlannerPage.wiring.fixture.ts
@@ -206,6 +206,7 @@ function dialogState(): HookState {
setShowTripForm: vi.fn(),
showMembersModal: false,
setShowMembersModal: vi.fn(),
+ editTripFromSettings: vi.fn(),
showReservationModal: false,
setShowReservationModal: vi.fn(),
editingReservation: null,
diff --git a/client/src/pages/TripPlannerPage.wiring.test.tsx b/client/src/pages/TripPlannerPage.wiring.test.tsx
index a07238bca..f588ea373 100644
--- a/client/src/pages/TripPlannerPage.wiring.test.tsx
+++ b/client/src/pages/TripPlannerPage.wiring.test.tsx
@@ -1,4 +1,4 @@
-// FE-PAGE-TPW-001 to FE-PAGE-TPW-074
+// FE-PAGE-TPW-001 to FE-PAGE-TPW-075
//
// The planner page is a wiring container: everything stateful lives in
// useTripPlanner (covered in src/pages/tripPlanner/useTripPlanner.test.tsx).
@@ -1552,6 +1552,14 @@ describe('TripPlannerPage — modals', () => {
expect(hookState.setShowMembersModal).toHaveBeenCalledWith(false)
})
+ it('FE-PAGE-TPW-075: the members modal leads to the trip form only for someone who may edit the trip (#2811)', () => {
+ renderPage()
+ expect(props('membersModal').onEditTrip).toBe(hookState.editTripFromSettings)
+
+ renderPage({ can: vi.fn((action: string) => action !== 'trip_edit') })
+ expect(props('membersModal').onEditTrip).toBeUndefined()
+ })
+
it('FE-PAGE-TPW-050: closing the booking modal outside a review resets the editor', async () => {
renderPage()
diff --git a/client/src/pages/tripPlanner/usePlannerDialogs.test.ts b/client/src/pages/tripPlanner/usePlannerDialogs.test.ts
index 696702641..bcb69e8e4 100644
--- a/client/src/pages/tripPlanner/usePlannerDialogs.test.ts
+++ b/client/src/pages/tripPlanner/usePlannerDialogs.test.ts
@@ -1,4 +1,4 @@
-// FE-TP-DIALOGS-001 to FE-TP-DIALOGS-012
+// FE-TP-DIALOGS-001 to FE-TP-DIALOGS-013
//
// The planner's dialogs and the URL intents that open them, driven straight through
// usePlannerDialogs. The search params come in as options, so each test owns the URL
@@ -248,4 +248,14 @@ describe('usePlannerDialogs', () => {
act(() => { result.current.changeTransitRoute(buildReservation({ id: 15, type: 'transit' })) })
expect(result.current.transitPrefill).toEqual({ from: null, to: null })
})
+
+ it('FE-TP-DIALOGS-013: editing the trip from its settings swaps the settings for the trip form (#2811)', () => {
+ const { result } = renderDialogs()
+ act(() => { result.current.setShowMembersModal(true) })
+
+ act(() => { result.current.editTripFromSettings() })
+
+ expect(result.current.showMembersModal).toBe(false)
+ expect(result.current.showTripForm).toBe(true)
+ })
})
diff --git a/client/src/pages/tripPlanner/usePlannerDialogs.ts b/client/src/pages/tripPlanner/usePlannerDialogs.ts
index bdb2840a1..44ac069ff 100644
--- a/client/src/pages/tripPlanner/usePlannerDialogs.ts
+++ b/client/src/pages/tripPlanner/usePlannerDialogs.ts
@@ -104,6 +104,12 @@ export function usePlannerDialogs(options: PlannerDialogsOptions) {
}, [searchParams])
const [showTripForm, setShowTripForm] = useState(false)
const [showMembersModal, setShowMembersModal] = useState(false)
+ // The trip settings hand over to the trip's form rather than stack a second dialog
+ // on their own: the planner's way to the title, dates, cover and currency (#2811).
+ const editTripFromSettings = useCallback(() => {
+ setShowMembersModal(false)
+ setShowTripForm(true)
+ }, [])
const [showReservationModal, setShowReservationModal] = useState(false)
const [editingReservation, setEditingReservation] = useState(null)
const [showBookingImport, setShowBookingImport] = useState(false)
@@ -167,7 +173,7 @@ export function usePlannerDialogs(options: PlannerDialogsOptions) {
editingAssignmentId, setEditingAssignmentId, placeFormDayId, setPlaceFormDayId,
placeFormPosition, setPlaceFormPosition, serviceStopForm, setServiceStopForm, serviceStopKind, setServiceStopKind,
stopDraft, setStopDraft, stayRelease, setStayRelease, reservationModalDayId, setReservationModalDayId,
- showTripForm, setShowTripForm, showMembersModal, setShowMembersModal,
+ showTripForm, setShowTripForm, showMembersModal, setShowMembersModal, editTripFromSettings,
showReservationModal, setShowReservationModal, editingReservation, setEditingReservation,
showBookingImport, setShowBookingImport, bookingImportKind, setBookingImportKind,
bookingImportAvailable, setBookingImportAvailable, airTrailAvailable, showAirTrailImport, setShowAirTrailImport,
diff --git a/client/src/pages/tripPlanner/useTripPlanner.ts b/client/src/pages/tripPlanner/useTripPlanner.ts
index 7221e0fdf..6d46d59cd 100644
--- a/client/src/pages/tripPlanner/useTripPlanner.ts
+++ b/client/src/pages/tripPlanner/useTripPlanner.ts
@@ -360,7 +360,7 @@ export function useTripPlanner() {
// The dialog state that only passes through to the shells.
const {
showPlaceForm, prefillCoords, reservationModalDayId, setReservationModalDayId,
- showTripForm, setShowTripForm, showMembersModal, setShowMembersModal, showReservationModal,
+ showTripForm, setShowTripForm, showMembersModal, setShowMembersModal, editTripFromSettings, showReservationModal,
showBookingImport, setShowBookingImport, bookingImportKind, setBookingImportKind, bookingImportAvailable,
airTrailAvailable, showAirTrailImport, setShowAirTrailImport, bookingForAssignmentId, setBookingForAssignmentId,
showTransportModal, transportModalDayId, transportModalAutomated, setTransportModalAutomated,
@@ -749,7 +749,7 @@ export function useTripPlanner() {
highlightedAlternative, setHighlightedAlternative,
moveRoadtripStopToDay,
dropPoiOnRoute,
- showTripForm, setShowTripForm, showMembersModal, setShowMembersModal,
+ showTripForm, setShowTripForm, showMembersModal, setShowMembersModal, editTripFromSettings,
showReservationModal, setShowReservationModal, editingReservation, setEditingReservation,
showBookingImport, setShowBookingImport, bookingImportKind, setBookingImportKind, bookingImportAvailable,
airTrailAvailable, showAirTrailImport, setShowAirTrailImport,
diff --git a/client/tests/helpers/mobileTrip.ts b/client/tests/helpers/mobileTrip.ts
index 37eb71438..7e988ee53 100644
--- a/client/tests/helpers/mobileTrip.ts
+++ b/client/tests/helpers/mobileTrip.ts
@@ -134,6 +134,7 @@ export function buildPlanner(overrides: Partial = {}): TripPlanner
setShowTripForm: vi.fn(),
showMembersModal: false,
setShowMembersModal: vi.fn(),
+ editTripFromSettings: vi.fn(),
showReservationModal: false,
setShowReservationModal: vi.fn(),
editingReservation: null,
diff --git a/shared/src/i18n/ar/trip.ts b/shared/src/i18n/ar/trip.ts
index 1dc714aca..30451a1d6 100644
--- a/shared/src/i18n/ar/trip.ts
+++ b/shared/src/i18n/ar/trip.ts
@@ -127,6 +127,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'من المشاركون في هذه الرحلة، والضيوف بلا حساب.',
'trip.settings.sharing': 'المشاركة',
'trip.settings.sharingHint': 'رابط للقراءة فقط لأي شخص، ورابط دعوة للانضمام.',
+ 'trip.settings.editTripHint': 'العنوان والتواريخ والغلاف والوصف والعملة.',
'trip.tabs.journal': 'اليوميات',
'trip.journal.title': 'اليوميات',
'trip.journal.hint': 'اربط إحدى يومياتك بهذه الرحلة. من تمنحه الوصول يفتحها هنا مباشرة في تبويب اليوميات، بشكل حي.',
diff --git a/shared/src/i18n/az/trip.ts b/shared/src/i18n/az/trip.ts
index c40dae85b..100dfe5dd 100644
--- a/shared/src/i18n/az/trip.ts
+++ b/shared/src/i18n/az/trip.ts
@@ -118,6 +118,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'Bu səyahətdə kimlərin olduğu və hesabı olmayan qonaqlar.',
'trip.settings.sharing': 'Paylaşma',
'trip.settings.sharingHint': 'Hər kəs üçün yalnız oxunan keçid və qoşulmaq üçün dəvət keçidi.',
+ 'trip.settings.editTripHint': 'Ad, tarixlər, üz qabığı, təsvir və valyuta.',
'trip.tabs.journal': 'Gündəlik',
'trip.journal.title': 'Gündəlik',
'trip.journal.hint':
diff --git a/shared/src/i18n/br/trip.ts b/shared/src/i18n/br/trip.ts
index c7e4cc9e0..acc23bdda 100644
--- a/shared/src/i18n/br/trip.ts
+++ b/shared/src/i18n/br/trip.ts
@@ -107,6 +107,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'Quem está nesta viagem e os convidados sem conta.',
'trip.settings.sharing': 'Compartilhar',
'trip.settings.sharingHint': 'Um link somente leitura para qualquer pessoa e um link de convite para entrar.',
+ 'trip.settings.editTripHint': 'Título, datas, capa, descrição e moeda.',
'trip.tabs.journal': 'Diário',
'trip.journal.title': 'Diário',
'trip.journal.hint':
diff --git a/shared/src/i18n/ca/trip.ts b/shared/src/i18n/ca/trip.ts
index 70e9e79d2..22bb1e635 100644
--- a/shared/src/i18n/ca/trip.ts
+++ b/shared/src/i18n/ca/trip.ts
@@ -107,6 +107,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'Qui és en aquest viatge i els convidats sense compte.',
'trip.settings.sharing': 'Compartir',
'trip.settings.sharingHint': "Un enllaç de només lectura per a tothom i un enllaç d'invitació per unir-se.",
+ 'trip.settings.editTripHint': 'Títol, dates, portada, descripció i moneda.',
'trip.tabs.journal': 'Diari',
'trip.journal.title': 'Diari',
'trip.journal.hint':
diff --git a/shared/src/i18n/cs/trip.ts b/shared/src/i18n/cs/trip.ts
index fd4c4e71c..10bb1b531 100644
--- a/shared/src/i18n/cs/trip.ts
+++ b/shared/src/i18n/cs/trip.ts
@@ -112,6 +112,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'Kdo je na této cestě a hosté bez účtu.',
'trip.settings.sharing': 'Sdílení',
'trip.settings.sharingHint': 'Odkaz jen pro čtení pro kohokoli a pozvánka k připojení.',
+ 'trip.settings.editTripHint': 'Název, termíny, obálka, popis a měna.',
'trip.tabs.journal': 'Deník',
'trip.journal.title': 'Deník',
'trip.journal.hint':
diff --git a/shared/src/i18n/de/trip.ts b/shared/src/i18n/de/trip.ts
index 011372084..b74b4c576 100644
--- a/shared/src/i18n/de/trip.ts
+++ b/shared/src/i18n/de/trip.ts
@@ -108,6 +108,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'Wer auf dieser Reise ist, und Gäste ohne Konto.',
'trip.settings.sharing': 'Teilen',
'trip.settings.sharingHint': 'Ein Nur-Lesen-Link für alle und ein Einladungslink zum Beitreten.',
+ 'trip.settings.editTripHint': 'Titel, Daten, Titelbild, Beschreibung und Währung.',
'trip.tabs.journal': 'Journal', // same-as-en
'trip.journal.title': 'Journal', // same-as-en
'trip.journal.hint':
diff --git a/shared/src/i18n/en/trip.ts b/shared/src/i18n/en/trip.ts
index a3c662c4c..ab8de2096 100644
--- a/shared/src/i18n/en/trip.ts
+++ b/shared/src/i18n/en/trip.ts
@@ -107,6 +107,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'Who is on this trip, and the guests without an account.',
'trip.settings.sharing': 'Sharing',
'trip.settings.sharingHint': 'A read-only link for anyone, and an invite link to join.',
+ 'trip.settings.editTripHint': 'Title, dates, cover, description and currency.',
'trip.tabs.journal': 'Journal',
'trip.journal.title': 'Journal',
'trip.journal.hint':
diff --git a/shared/src/i18n/es/trip.ts b/shared/src/i18n/es/trip.ts
index 181c63eb2..1c9d26888 100644
--- a/shared/src/i18n/es/trip.ts
+++ b/shared/src/i18n/es/trip.ts
@@ -107,6 +107,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'Quién está en este viaje y los invitados sin cuenta.',
'trip.settings.sharing': 'Compartir',
'trip.settings.sharingHint': 'Un enlace de solo lectura para cualquiera y un enlace de invitación para unirse.',
+ 'trip.settings.editTripHint': 'Título, fechas, portada, descripción y moneda.',
'trip.tabs.journal': 'Diario',
'trip.journal.title': 'Diario',
'trip.journal.hint':
diff --git a/shared/src/i18n/et/trip.ts b/shared/src/i18n/et/trip.ts
index 33b738349..648c9d374 100644
--- a/shared/src/i18n/et/trip.ts
+++ b/shared/src/i18n/et/trip.ts
@@ -107,6 +107,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'Kes on sellel reisil, ja kontota külalised.',
'trip.settings.sharing': 'Jagamine',
'trip.settings.sharingHint': 'Kirjutuskaitstud link kõigile ja kutselink liitumiseks.',
+ 'trip.settings.editTripHint': 'Pealkiri, kuupäevad, kaanepilt, kirjeldus ja valuuta.',
'trip.tabs.journal': 'Päevik',
'trip.journal.title': 'Päevik',
'trip.journal.hint':
diff --git a/shared/src/i18n/fr/trip.ts b/shared/src/i18n/fr/trip.ts
index 350b1f243..b6e9619bb 100644
--- a/shared/src/i18n/fr/trip.ts
+++ b/shared/src/i18n/fr/trip.ts
@@ -108,6 +108,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'Qui participe à ce voyage, et les invités sans compte.',
'trip.settings.sharing': 'Partage',
'trip.settings.sharingHint': "Un lien en lecture seule pour tous, et un lien d'invitation pour rejoindre.",
+ 'trip.settings.editTripHint': 'Titre, dates, couverture, description et devise.',
'trip.tabs.journal': 'Journal', // same-as-en
'trip.journal.title': 'Journal', // same-as-en
'trip.journal.hint':
diff --git a/shared/src/i18n/gr/trip.ts b/shared/src/i18n/gr/trip.ts
index 913d96b6f..26abe9e74 100644
--- a/shared/src/i18n/gr/trip.ts
+++ b/shared/src/i18n/gr/trip.ts
@@ -110,6 +110,7 @@ const trip: TranslationStrings = {
'trip.settings.sharing': 'Κοινοποίηση',
'trip.settings.sharingHint':
'Ένας σύνδεσμος μόνο για ανάγνωση για όλους και ένας σύνδεσμος πρόσκλησης για συμμετοχή.',
+ 'trip.settings.editTripHint': 'Τίτλος, ημερομηνίες, εξώφυλλο, περιγραφή και νόμισμα.',
'trip.tabs.journal': 'Ημερολόγιο',
'trip.journal.title': 'Ημερολόγιο',
'trip.journal.hint':
diff --git a/shared/src/i18n/he/trip.ts b/shared/src/i18n/he/trip.ts
index 28584d1e0..55a3cf01e 100644
--- a/shared/src/i18n/he/trip.ts
+++ b/shared/src/i18n/he/trip.ts
@@ -111,6 +111,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'מי בטיול הזה, והאורחים שאין להם חשבון.',
'trip.settings.sharing': 'שיתוף',
'trip.settings.sharingHint': 'קישור לקריאה בלבד לכל אחד, וקישור הזמנה להצטרפות.',
+ 'trip.settings.editTripHint': 'כותרת, תאריכים, תמונת שער, תיאור ומטבע.',
'trip.tabs.journal': 'יומן',
'trip.journal.title': 'יומן',
'trip.journal.hint':
diff --git a/shared/src/i18n/hu/trip.ts b/shared/src/i18n/hu/trip.ts
index a115cd3fd..69e4ed1a9 100644
--- a/shared/src/i18n/hu/trip.ts
+++ b/shared/src/i18n/hu/trip.ts
@@ -110,6 +110,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'Kik vannak ezen az utazáson, és a fiók nélküli vendégek.',
'trip.settings.sharing': 'Megosztás',
'trip.settings.sharingHint': 'Csak olvasható link bárkinek, és meghívó link a csatlakozáshoz.',
+ 'trip.settings.editTripHint': 'Cím, dátumok, borítókép, leírás és pénznem.',
'trip.tabs.journal': 'Napló',
'trip.journal.title': 'Napló',
'trip.journal.hint':
diff --git a/shared/src/i18n/id/trip.ts b/shared/src/i18n/id/trip.ts
index ec5899c5a..852913b4a 100644
--- a/shared/src/i18n/id/trip.ts
+++ b/shared/src/i18n/id/trip.ts
@@ -103,6 +103,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'Siapa saja dalam perjalanan ini, dan tamu tanpa akun.',
'trip.settings.sharing': 'Berbagi',
'trip.settings.sharingHint': 'Tautan hanya-baca untuk siapa saja, dan tautan undangan untuk bergabung.',
+ 'trip.settings.editTripHint': 'Judul, tanggal, sampul, deskripsi, dan mata uang.',
'trip.tabs.journal': 'Jurnal',
'trip.journal.title': 'Jurnal',
'trip.journal.hint':
diff --git a/shared/src/i18n/it/trip.ts b/shared/src/i18n/it/trip.ts
index 0ce4b885a..d1d3afa59 100644
--- a/shared/src/i18n/it/trip.ts
+++ b/shared/src/i18n/it/trip.ts
@@ -107,6 +107,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'Chi partecipa a questo viaggio e gli ospiti senza account.',
'trip.settings.sharing': 'Condivisione',
'trip.settings.sharingHint': 'Un link di sola lettura per tutti e un link di invito per unirsi.',
+ 'trip.settings.editTripHint': 'Titolo, date, copertina, descrizione e valuta.',
'trip.tabs.journal': 'Diario',
'trip.journal.title': 'Diario',
'trip.journal.hint':
diff --git a/shared/src/i18n/ja/trip.ts b/shared/src/i18n/ja/trip.ts
index 07c606e3a..18e163f0d 100644
--- a/shared/src/i18n/ja/trip.ts
+++ b/shared/src/i18n/ja/trip.ts
@@ -100,6 +100,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'この旅行の参加者と、アカウントのないゲスト。',
'trip.settings.sharing': '共有',
'trip.settings.sharingHint': '誰でも見られる閲覧専用リンクと、参加用の招待リンク。',
+ 'trip.settings.editTripHint': 'タイトル、日程、カバー画像、説明、通貨。',
'trip.tabs.journal': 'ジャーナル',
'trip.journal.title': 'ジャーナル',
'trip.journal.hint':
diff --git a/shared/src/i18n/ko/trip.ts b/shared/src/i18n/ko/trip.ts
index b683eeaf1..380202250 100644
--- a/shared/src/i18n/ko/trip.ts
+++ b/shared/src/i18n/ko/trip.ts
@@ -100,6 +100,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': '이 여행의 참여자와 계정 없는 게스트.',
'trip.settings.sharing': '공유',
'trip.settings.sharingHint': '누구나 볼 수 있는 읽기 전용 링크와 참여용 초대 링크.',
+ 'trip.settings.editTripHint': '제목, 날짜, 커버 이미지, 설명, 통화.',
'trip.tabs.journal': '저널',
'trip.journal.title': '저널',
'trip.journal.hint':
diff --git a/shared/src/i18n/nl/trip.ts b/shared/src/i18n/nl/trip.ts
index 8558736ab..eaa91e493 100644
--- a/shared/src/i18n/nl/trip.ts
+++ b/shared/src/i18n/nl/trip.ts
@@ -107,6 +107,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'Wie er op deze reis zijn, en gasten zonder account.',
'trip.settings.sharing': 'Delen',
'trip.settings.sharingHint': 'Een alleen-lezen-link voor iedereen en een uitnodigingslink om mee te doen.',
+ 'trip.settings.editTripHint': 'Titel, data, omslagfoto, beschrijving en valuta.',
'trip.tabs.journal': 'Dagboek',
'trip.journal.title': 'Dagboek',
'trip.journal.hint':
diff --git a/shared/src/i18n/pl/trip.ts b/shared/src/i18n/pl/trip.ts
index 6ef4f9d5d..2c1cd377b 100644
--- a/shared/src/i18n/pl/trip.ts
+++ b/shared/src/i18n/pl/trip.ts
@@ -117,6 +117,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'Kto bierze udział w podróży oraz goście bez konta.',
'trip.settings.sharing': 'Udostępnianie',
'trip.settings.sharingHint': 'Link tylko do odczytu dla każdego i link z zaproszeniem do dołączenia.',
+ 'trip.settings.editTripHint': 'Tytuł, daty, okładka, opis i waluta.',
'trip.tabs.journal': 'Dziennik',
'trip.journal.title': 'Dziennik',
'trip.journal.hint':
diff --git a/shared/src/i18n/ru/trip.ts b/shared/src/i18n/ru/trip.ts
index e0a35a058..bff015fb7 100644
--- a/shared/src/i18n/ru/trip.ts
+++ b/shared/src/i18n/ru/trip.ts
@@ -117,6 +117,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'Кто участвует в поездке, и гости без аккаунта.',
'trip.settings.sharing': 'Доступ по ссылке',
'trip.settings.sharingHint': 'Ссылка только для чтения для всех и ссылка-приглашение для участия.',
+ 'trip.settings.editTripHint': 'Название, даты, обложка, описание и валюта.',
'trip.tabs.journal': 'Дневник',
'trip.journal.title': 'Дневник',
'trip.journal.hint':
diff --git a/shared/src/i18n/sk/trip.ts b/shared/src/i18n/sk/trip.ts
index 17a94214a..0ee33ea24 100644
--- a/shared/src/i18n/sk/trip.ts
+++ b/shared/src/i18n/sk/trip.ts
@@ -112,6 +112,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'Kto je na tejto ceste a hostia bez účtu.',
'trip.settings.sharing': 'Zdieľanie',
'trip.settings.sharingHint': 'Odkaz len na čítanie pre kohokoľvek a pozvánka na pripojenie.',
+ 'trip.settings.editTripHint': 'Názov, termíny, obálka, popis a mena.',
'trip.tabs.journal': 'Denník',
'trip.journal.title': 'Denník',
'trip.journal.hint':
diff --git a/shared/src/i18n/sv/trip.ts b/shared/src/i18n/sv/trip.ts
index 300a80518..4b23cbf47 100644
--- a/shared/src/i18n/sv/trip.ts
+++ b/shared/src/i18n/sv/trip.ts
@@ -107,6 +107,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'Vilka som är med på resan, och gäster utan konto.',
'trip.settings.sharing': 'Dela',
'trip.settings.sharingHint': 'En skrivskyddad länk för alla och en inbjudningslänk för att gå med.',
+ 'trip.settings.editTripHint': 'Titel, datum, omslagsbild, beskrivning och valuta.',
'trip.tabs.journal': 'Dagbok',
'trip.journal.title': 'Dagbok',
'trip.journal.hint':
diff --git a/shared/src/i18n/th/trip.ts b/shared/src/i18n/th/trip.ts
index 35cbfce3c..c649e3fb5 100644
--- a/shared/src/i18n/th/trip.ts
+++ b/shared/src/i18n/th/trip.ts
@@ -102,6 +102,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'ใครอยู่ในทริปนี้ และแขกที่ไม่มีบัญชี',
'trip.settings.sharing': 'การแชร์',
'trip.settings.sharingHint': 'ลิงก์อ่านอย่างเดียวสำหรับทุกคน และลิงก์เชิญเพื่อเข้าร่วม',
+ 'trip.settings.editTripHint': 'ชื่อ วันที่ ภาพปก คำอธิบาย และสกุลเงิน',
'trip.tabs.journal': 'บันทึก',
'trip.journal.title': 'บันทึก',
'trip.journal.hint': 'เชื่อมบันทึกของคุณเล่มหนึ่งเข้ากับทริปนี้ ผู้ที่คุณให้สิทธิ์จะเปิดได้ที่แท็บบันทึกตรงนี้ แบบสด',
diff --git a/shared/src/i18n/tr/trip.ts b/shared/src/i18n/tr/trip.ts
index a3926fe34..300bbfc0f 100644
--- a/shared/src/i18n/tr/trip.ts
+++ b/shared/src/i18n/tr/trip.ts
@@ -108,6 +108,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'Bu seyahatte kimlerin olduğu ve hesabı olmayan misafirler.',
'trip.settings.sharing': 'Paylaşım',
'trip.settings.sharingHint': 'Herkes için salt okunur bir bağlantı ve katılmak için davet bağlantısı.',
+ 'trip.settings.editTripHint': 'Başlık, tarihler, kapak, açıklama ve para birimi.',
'trip.tabs.journal': 'Günlük',
'trip.journal.title': 'Günlük',
'trip.journal.hint':
diff --git a/shared/src/i18n/uk/trip.ts b/shared/src/i18n/uk/trip.ts
index aee121da2..f7e97e0a6 100644
--- a/shared/src/i18n/uk/trip.ts
+++ b/shared/src/i18n/uk/trip.ts
@@ -117,6 +117,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'Хто бере участь у подорожі, і гості без облікового запису.',
'trip.settings.sharing': 'Поширення',
'trip.settings.sharingHint': 'Посилання лише для читання для всіх і посилання-запрошення, щоб приєднатися.',
+ 'trip.settings.editTripHint': 'Назва, дати, обкладинка, опис і валюта.',
'trip.tabs.journal': 'Щоденник',
'trip.journal.title': 'Щоденник',
'trip.journal.hint':
diff --git a/shared/src/i18n/vi/trip.ts b/shared/src/i18n/vi/trip.ts
index 2200ed8c7..906b4d925 100644
--- a/shared/src/i18n/vi/trip.ts
+++ b/shared/src/i18n/vi/trip.ts
@@ -102,6 +102,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': 'Những ai trong chuyến đi này, và khách không có tài khoản.',
'trip.settings.sharing': 'Chia sẻ',
'trip.settings.sharingHint': 'Liên kết chỉ đọc cho mọi người và liên kết mời để tham gia.',
+ 'trip.settings.editTripHint': 'Tiêu đề, ngày, ảnh bìa, mô tả và tiền tệ.',
'trip.tabs.journal': 'Nhật ký',
'trip.journal.title': 'Nhật ký',
'trip.journal.hint':
diff --git a/shared/src/i18n/zh-TW/trip.ts b/shared/src/i18n/zh-TW/trip.ts
index 6307b9e23..a6734513a 100644
--- a/shared/src/i18n/zh-TW/trip.ts
+++ b/shared/src/i18n/zh-TW/trip.ts
@@ -99,6 +99,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': '參與此旅行的人,以及沒有帳戶的訪客。',
'trip.settings.sharing': '分享',
'trip.settings.sharingHint': '任何人可用的唯讀連結,以及用於加入的邀請連結。',
+ 'trip.settings.editTripHint': '標題、日期、封面、描述和貨幣。',
'trip.tabs.journal': '日誌',
'trip.journal.title': '日誌',
'trip.journal.hint': '將你的一本日誌連結到此旅行。獲得存取權限的人可在此處的日誌分頁中即時開啟它。',
diff --git a/shared/src/i18n/zh/trip.ts b/shared/src/i18n/zh/trip.ts
index 7d2251356..048dcf28f 100644
--- a/shared/src/i18n/zh/trip.ts
+++ b/shared/src/i18n/zh/trip.ts
@@ -99,6 +99,7 @@ const trip: TranslationStrings = {
'trip.settings.peopleHint': '参与此旅行的人,以及没有账户的访客。',
'trip.settings.sharing': '分享',
'trip.settings.sharingHint': '任何人可用的只读链接,以及用于加入的邀请链接。',
+ 'trip.settings.editTripHint': '标题、日期、封面、描述和货币。',
'trip.tabs.journal': '日志',
'trip.journal.title': '日志',
'trip.journal.hint': '将你的一本日志关联到此旅行。获得访问权限的人可在此处的日志标签中实时打开它。',
diff --git a/wiki/Currencies.md b/wiki/Currencies.md
index b44ad38b8..672e415e5 100644
--- a/wiki/Currencies.md
+++ b/wiki/Currencies.md
@@ -12,7 +12,7 @@ The short version: **the trip currency is the accounting base, the expense curre
## Trip currency
-Every trip has exactly one currency. It is set when you create the trip and can be changed later in the same dialog: on **My Trips**, click the pencil on the trip card (**Edit Trip**). This needs the `trip_edit` permission. The **Trip settings** pill beside the trip name in the planner does not hold the currency; it only holds People, Sharing and Journal. The new-trip dialog pre-fills it with **your display currency**, falling back to **EUR** when you have left that on *Trip currency*. A trip created through MCP or by a plugin without a currency gets the same value. This is the one moment the display currency touches stored data: it seeds the field, and from then on the trip's currency is its own.
+Every trip has exactly one currency. It is set when you create the trip and can be changed later in the same dialog: on **My Trips**, click the pencil on the trip card (**Edit Trip**), or in the planner open **Trip settings** and pick **Edit Trip** at the foot of its list. This needs the `trip_edit` permission. The new-trip dialog pre-fills it with **your display currency**, falling back to **EUR** when you have left that on *Trip currency*. A trip created through MCP or by a plugin without a currency gets the same value. This is the one moment the display currency touches stored data: it seeds the field, and from then on the trip's currency is its own.
This is the trip's **accounting base**. It is not a cosmetic label:
diff --git a/wiki/Trip-Settings.md b/wiki/Trip-Settings.md
index 754c1a672..7c3a3b324 100644
--- a/wiki/Trip-Settings.md
+++ b/wiki/Trip-Settings.md
@@ -9,6 +9,8 @@
On a wide screen the areas are listed down the left side, each with a line saying what it is for; on a narrow one they are a row of pills above the content. The head of the dialog shows how many people and guests the trip has.
+If you may edit the trip (`trip_edit`), **Edit Trip** sits at the foot of that list (at the end of the pills on a narrow screen). It closes Trip settings and opens the trip's own dialog, the same one as the pencil on the trip card in **My Trips**, so the title, description, dates, currency and cover can be changed without leaving the planner.
+
## People
*Who is on this trip, and the guests without an account.*