Safari blocks SameSite=Lax cookies on <img> subresource requests,
causing 401 errors when loading Immich thumbnails and originals.
Replaced the token-based <img src> approach with direct fetch()
using credentials: 'include', which reliably sends cookies across
all browsers. Images are now loaded as blobs with ObjectURLs.
Added a concurrency limiter (max 6 parallel fetches) to prevent
ERR_INSUFFICIENT_RESOURCES when many photos load simultaneously.
Queue is cleared when the photo picker closes so gallery images
load immediately.
Date-only strings parsed with new Date(dateStr + 'T00:00:00') were
interpreted relative to the local timezone, causing off-by-one day
display for users west of UTC. Fixed across 16 files by parsing as
UTC ('T00:00:00Z') and displaying with timeZone: 'UTC'.
- toggleCompanyHoliday now calls loadStats() so the entitlement sidebar
updates immediately when a vacation day is converted to a company holiday
- deleteYear now deletes vacay_user_years rows for the removed year,
preventing stale entitlement data from persisting and re-appearing
when the year is re-created
- deleteYear recalculates carry-over for year+1 when year N is deleted,
using the new actual previous year as the source
- removeYear store action now calls loadStats() so the sidebar reflects
the recalculated carry-over without requiring a page refresh
- Add prev-year button (+[<] 2026 [>]+) so users can add years going
backwards after deleting a past year; add vacay.addPrevYear i18n key
to all 13 supported languages
Closes#371
When deleting the currently selected year, selectedYear was never
cleared, leaving the deleted year shown as active in the UI. Now
resets to the latest remaining year, or the current calendar year
if all years have been removed.
Fixes#369
When a user enters an http:// Immich URL that redirects to https://,
the test succeeded (GET follows redirects fine) but subsequent POST
requests (e.g. photo search) broke due to method downgrade on 301/302.
Now testConnection() checks resp.url against the input URL after a
successful fetch. If the only difference is http→https on the same
host and port, it returns a canonicalUrl so the frontend can update
the input field before the user saves — ensuring the correct URL is
stored.
- Fix attachment URLs to use /api/trips/:id/files/:id/download instead
of /uploads/files/... which was unconditionally blocked with 401
- Use getAuthUrl() with ephemeral tokens for displaying attachments and
opening them in a new tab (images, PDFs, documents)
- Replace htmlFor/id label pattern with ref.current.click() for the
file picker button in NoteFormModal — fixes file not being added to
pending list on first note creation
- Add integration tests COLLAB-028 to COLLAB-031 covering URL format,
listing URLs, ephemeral token download, and unauthenticated 401
* add test suite, mostly covers integration testing, tests are only backend side
* workflow runs the correct script
* workflow runs the correct script
* workflow runs the correct script
* unit tests incoming
* Fix multer silent rejections and error handler info leak
- Revert cb(null, false) to cb(new Error(...)) in auth.ts, collab.ts,
and files.ts so invalid uploads return an error instead of silently
dropping the file
- Error handler in app.ts now always returns 500 / "Internal server
error" instead of forwarding err.message to the client
* Use statusCode consistently for multer errors and error handler
- Error handler in app.ts reads err.statusCode to forward the correct
HTTP status while keeping the response body generic
New POST /api/trips/:id/copy endpoint that deep copies all trip
planning data (days, places, assignments, reservations, budget,
packing, accommodations, day notes) with proper FK remapping
inside a transaction. Skips files, collab data, and members.
Copy button on all dashboard card types (spotlight, grid, list,
archived) gated by trip_create permission. Translations for all
12 languages.
Also adds reminder_days to Trip interface (removes as-any casts).
Introduces a full in-app notification system with three types (simple,
boolean with server-side callbacks, navigate), three scopes (user, trip,
admin), fan-out persistence per recipient, and real-time push via
WebSocket. Includes a notification bell in the navbar, dropdown, dedicated
/notifications page, and a dev-only admin tab for testing all notification
variants.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Skip loadUser() and exclude /shared/ from the 401 redirect interceptor
so unauthenticated users can open shared trip links without being
redirected to /login. Fixes#308.
toast.warn does not exist in the toast library; calling it threw an error
that was caught and displayed as "Could not connect to Immich" even when
the save succeeded. Fixes#309.
- Auth middleware now tags its 401s with code: AUTH_REQUIRED so the
client interceptor only redirects to /login on genuine session failures,
not on upstream API errors
- Fix /albums and album sync routes using raw encrypted API key instead
of getImmichCredentials() (which decrypts it), causing Immich to reject
requests with 401
- Add toast error notifications for all Immich operations in MemoriesPanel
that previously swallowed errors silently
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- LoginPage now uses getApiErrorMessage() instead of err.message so
backend validation errors (e.g. "Password must be at least 8 characters")
are displayed instead of the generic "Request failed with status code 400"
- Add missing db import in server/src/index.ts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a native title tooltip to calendar day cells so hovering over a
public holiday reveals its name (and custom label if configured).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
useCallback captured tripStore at creation time (dep: [routeCalcEnabled]).
If assignments were empty on first render (trip still loading), the callback
would permanently see empty assignments and call setRoute(null) whenever
invoked — e.g. when clicking a place triggers onSelectDay → updateRouteForDay.
Fix: store tripStore in a ref updated on every render so the callback always
reads the latest assignments without needing to be recreated.
Implements a full undo history system for the Plan screen.
New hook: usePlannerHistory (client/src/hooks/usePlannerHistory.ts)
- Maintains a LIFO stack (up to 30 entries) of reversible actions
- Exposes pushUndo(label, fn), undo(), canUndo, lastActionLabel
Tracked actions:
- Assign place to day (undo: remove the assignment)
- Remove place from day (undo: re-assign at original position)
- Reorder places within a day (undo: restore previous order)
- Move place to a different day (undo: move back)
- Optimize route (undo: restore original order)
- Lock / unlock place (undo: toggle back)
- Delete place (undo: recreate place + restore all day assignments)
- Add place (undo: delete it)
- Import from GPX (undo: delete all imported places)
- Import from Google Maps list (undo: delete all imported places)
UI: Undo button (Undo2 icon) in DayPlanSidebar header. PDF, ICS and
Undo buttons all use custom instant hover tooltips instead of native
title attributes.
A toast notification confirms each undo action.
Translations: undo.* keys added to all 12 language files.
Inspector now ignores sidebar widths when window is under 900px,
preventing it from being squeezed when sidebars are visually hidden
but their width values are still set.
Map markers:
- Collapsing a day in the sidebar hides its places from the map
- Places assigned to multiple days only hide when all days collapsed
- Unplanned places always stay visible
Immich settings:
- New POST /integrations/immich/test endpoint validates credentials
without saving them
- Save button disabled until test connection passes
- Changing URL or API key resets test status
- i18n: testFirst key for all 12 languages
- Link Immich albums to trips — photos sync automatically
- Album picker shows all user's Immich albums
- Linked albums displayed as chips with sync/unlink buttons
- Auto-sync on link: fetches all album photos and adds to trip
- Manual re-sync button for each linked album
- DB migration: trip_album_links table
fix: shared Immich photos visible to other trip members
- Thumbnail/original proxy now uses photo owner's Immich credentials
when userId query param is provided, fixing 404 for shared photos
- i18n: album keys for all 12 languages