fix(journey): fix issue #704 — active logic, archive, places rename, search, trip reminders

- Derive journey lifecycle from linked trip dates (live/upcoming/completed/draft)
  instead of relying solely on status field; status=archived always wins
- Add Archive/Restore Journey action in journey settings dialog
- Rename cities → places end-to-end (SQL alias, TS types, stats field, all locales)
- Wire up search icon: toggles inline input, filters by title+subtitle client-side
- Fix channelConfigured check: trip reminders enabled by default since inapp is
  always available; remove channel check, controlled solely by admin setting
- Expose notify_trip_reminder toggle in Admin → Settings → Notifications
- Add trip_date_min/trip_date_max to listJourneys SQL for client-side lifecycle
- Add archived status to Journey type (server + client)
- Update all 15 locale files with new keys (search, archive, places, trip reminders)
This commit is contained in:
jubnl
2026-04-17 16:59:23 +02:00
parent 4a5a461d25
commit 3b94727c07
31 changed files with 507 additions and 89 deletions
@@ -202,7 +202,7 @@ describe('listJourneys', () => {
expect(result).toHaveLength(1);
expect(result[0].title).toBe('Road Trip');
expect(result[0].entry_count).toBe(2);
expect(result[0].city_count).toBe(2);
expect(result[0].place_count).toBe(2);
});
it('JOURNEY-SVC-012: includes journeys where user is contributor', () => {
@@ -226,6 +226,21 @@ describe('listJourneys', () => {
expect(result).toHaveLength(0);
});
it('JOURNEY-SVC-013b: returns trip_date_min/max aggregated from linked trips', () => {
const { user } = createUser(testDb);
const journey = createJourney(testDb, user.id, { title: 'Multi Trip' });
const trip1 = createTrip(testDb, user.id, { title: 'Trip A', start_date: '2025-06-01', end_date: '2025-06-10' });
const trip2 = createTrip(testDb, user.id, { title: 'Trip B', start_date: '2026-03-15', end_date: '2026-03-20' });
addTripToJourney(journey.id, trip1.id, user.id);
addTripToJourney(journey.id, trip2.id, user.id);
const result = listJourneys(user.id);
expect(result).toHaveLength(1);
expect(result[0].trip_date_min).toBe('2025-06-01');
expect(result[0].trip_date_max).toBe('2026-03-20');
});
});
describe('createJourney (service)', () => {
@@ -335,6 +350,26 @@ describe('updateJourney', () => {
expect(result).not.toBeNull();
expect(result!.title).toBe('Same');
});
it('JOURNEY-SVC-021b: accepts archived status', () => {
const { user } = createUser(testDb);
const journey = createJourney(testDb, user.id, { title: 'To Archive' });
const result = updateJourney(journey.id, user.id, { status: 'archived' });
expect(result).not.toBeNull();
expect(result!.status).toBe('archived');
});
it('JOURNEY-SVC-021c: ignores invalid status value', () => {
const { user } = createUser(testDb);
const journey = createJourney(testDb, user.id, { title: 'Stay Active' });
const result = updateJourney(journey.id, user.id, { status: 'bogus' });
expect(result).not.toBeNull();
expect(result!.status).toBe('active');
});
});
describe('deleteJourney', () => {
@@ -336,7 +336,7 @@ describe('getPublicJourney', () => {
expect(result!.entries).toHaveLength(2);
expect(result!.stats.entries).toBe(2);
expect(result!.stats.photos).toBe(1);
expect(result!.stats.cities).toBe(2);
expect(result!.stats.places).toBe(2);
expect(result!.permissions.share_timeline).toBe(true);
expect(result!.permissions.share_gallery).toBe(true);
expect(result!.permissions.share_map).toBe(false);
@@ -397,6 +397,6 @@ describe('getPublicJourney', () => {
expect(result!.entries).toEqual([]);
expect(result!.stats.entries).toBe(0);
expect(result!.stats.photos).toBe(0);
expect(result!.stats.cities).toBe(0);
expect(result!.stats.places).toBe(0);
});
});