Commit Graph

2747 Commits

Author SHA1 Message Date
Maurice 5f49c87f4a fix(admin): map the retired integration group for managed tabs
The settings redesign folded the integration sidebar group into config,
but ManagedAdminTab still accepts group 'integration'. Indexing the label
map with it was an implicit any under the strict check and would have put
such a tab under an empty heading. It now lands under config.
2026-11-13 23:30:35 +00:00
Maurice 01653f81e3 test(settings): follow the redesigned settings and admin pages
The tests now look for what the pages show after the redesign: the admin
Settings tab is Security and Services, MCP Access and User Defaults are MCP
and Defaults, notification channels and the 2FA rule are rows with a named
switch, hints sit behind the info mark, each addon type card counts its own
addons, OAuth scopes and the AI sharing note are shown in full, and the 2FA
disable password is the field beside the code. The phone's MCP admin header
keeps its own title, MCP Access. The mail pill test renders with
translations, so it finds From mail rather than the key.
2026-11-13 23:10:05 +00:00
Maurice a0764b9c96 fix(settings): the checks the settings redesign left red
The phone's MCP card still read the config fold state that the shared hook
no longer holds, so the client failed to typecheck. The phone keeps its
folded config, so it now holds that state itself.

SettingsColumns takes start and end instead of left and right: the prop
types counted as two physical sides for lint:rtl, and in a right-to-left
language the first column is the right one anyway.

Also: an unused icon import, seven files outside the format baseline run
through Prettier, Routing (de) and Services (fr) confirmed as the same word
as in English, and the size and warning baselines lowered to what the
redesign left (AdminPluginsPanel one wrapped line shorter, two promise
warnings, one long function and two non-null assertions in tests fewer).
2026-11-13 23:10:05 +00:00
Maurice 94ac7673a5 feat(settings): one look for every user and admin settings page
The settings pages had grown into a mix of styles: folds, dialogs behind
rows, full-width rows next to boxed ones, pills for every figure and buttons
in four sizes. Every page now uses the same kit.

- Cards keep their tinted head band; every part of a card is a rounded block
  with its own small head (SettingsBlock), and rows sit in rounded boxes.
- Nothing folds and nothing hides behind a dialog: the MCP config, OAuth
  scopes, mail how-to, AI privacy note and addon sub-options are all shown.
- Each page is split into two columns balanced by length (SettingsColumns).
- Long explanations moved into an info mark next to the label (HintMark).
- One button size everywhere (text-control); tab names are one word.
- Admin: the old Settings tab is split into Security and Services
  (?tab=settings still opens Security, and the phone keeps its single
  Settings section for both); the Integration group is gone, live flights
  and mail import sit next to the addons they configure.
- Addons: one card per type with rows, no search, filter or fold.
- Quota, TREK API facts, mail import status and Google's daily limit are
  compact lines and strips instead of tiles and pills.
- Language is a select; routing has its own card under the defaults;
  storage saves from the foot of its categories card.
- Unused keys, FoldSummary and the page-width wrapper are removed.
2026-11-13 23:18:28 +01:00
Maurice 5fe3dea4cc feat(settings): collapsible settings cards, first on the integrations tab
The settings pages list everything at once, so they read as a wall of
forms. SettingsCard and Section take 'collapsible': the card starts shut
and its badge says its state ('Connected', 'Not set up'); its title is the
button, stretched over the head, the head's action stays its own button.
The body stays mounted while shut. A link to #id opens the card and
scrolls to it; the browser remembers a card left open.

Every integrations card is collapsible now; AI parsing shows its saved
model and MCP the apps signed in. The assistant's settings link opens the
AI connection card. Tests that read into a card open it first
(tests/helpers/settingsCards.ts).
2026-11-13 21:57:20 +01:00
Maurice 608f76f32c feat(settings): two columns for the integrations tab
From xl up, as on the admin settings: what connects your trips with other
services (booking mails, photo servers, AirTrail, Dawarich, Wanderer) on
the left, AI and developer access (AI connection, AI parsing, API keys,
MCP) on the right. A column with nothing to show drops out and the other
takes the whole width.
2026-11-13 21:35:04 +01:00
Maurice a2cc7af695 fix(bookings): keep 'From mail' out of the card and list head
Behind the title stood the review mark, the 'From mail' pill and the two
actions, and the title was cut short. Where a booking came from is looked
up in its detail dialog, so the pill stays there only. The AirTrail note
sat above the mail pill; it is back above AirTrailPill.
2026-11-13 21:21:49 +01:00
Maurice dfbe23e5b8 fix: one status tile list for both admin cards; clear the copied timer on unmount
- The mail import's status tiles (which, labels, tones) were written out
  in the desktop and the mobile card alike; statTiles in the shared model
  holds them, each card keeps its own look.
- The journey share link set 'copied' back two seconds after a copy even
  when the dialog had closed, which also broke a client test run (window
  gone at teardown). useCopied, the trip share's hook, moves to hooks/ and
  serves both; it clears its timer on unmount.
- PlanLlmConfig is not exported: only the union is used outside.
2026-11-13 21:17:28 +01:00
Maurice 29816f81a9 fix(mail-ingest): send an uploaded .eml as multipart; publish the booking origin as a contract
- The upload posted its FormData through the client's JSON default, so the
  form went as JSON and the server saw no file (400). It goes through
  postMultipart now, like every other upload.
- reservation-origin.ts becomes reservation-origin.contract.ts: the booking
  import sets an origin, and a contract is what another domain may import.
- CI: the client's strict type check holds the whole program with strict
  checks and outgrew the default ~4 GB heap; it gets 8 GB like the server's.
2026-11-13 20:49:08 +01:00
Maurice 6789ee1cb6 refactor(server): keep the grown files at their size baselines; centre the inbox dialog
- auth.service: how an admin form value is stored (validated, encrypted, a
  masked echo kept) moves to the app settings registry as storedAdminValue
  and isMaskEcho.
- reservations.service: a booking's origin (withoutOrigin, originColumns)
  moves to reservation-origin.ts, its input shapes to reservation-inputs.ts,
  exported on from the service so importers stay as they were.
- Reservations.repository: the joined projection, the origin lookup and the
  origin columns move to _shared/, like the Wallet pass context before.
- test-sql: MailIngestApplyService.prepare is marked as no SQL statement.
- The pending bookings dialog opens centred, and says 'nothing waiting' once.
2026-11-13 20:28:17 +01:00
Maurice 7ed15a77b4 style(mail-ingest): a more compact admin status 2026-11-13 19:55:13 +01:00
Maurice 4806c62976 feat(mail-ingest): the admin status as tiles with a health dot
The last read sits in the head line with a green dot, amber while the last
run failed; the day's numbers are tiles with their icon in a soft tone
(mails, added on their own, waiting, failed turning red above zero); the
last error is its own amber note. Desktop and phone.
2026-11-13 19:53:30 +01:00
Maurice 3957b292be feat(mail-ingest): a blue note under each way of taking mail in
Upload only, IMAP and webhook each explain themselves in the admin card,
desktop and phone: what the way means, what it needs (a mailbox of its own
taking plus addresses, a signing secret), with its icon centred beside the
title and the sentence. All 28 locales.
2026-11-13 19:50:47 +01:00
Maurice 2ee413368b fix(mail-ingest): AI reading can be switched back on, and its hint names the addon
ai_available folded the person's own switch in, so switching AI reading off
greyed the switch out and it never came back. It now says only whether AI
reading works on the instance (the AI Parsing addon and a connection). When
it does not, the settings card says it needs the AI Parsing addon, by its
localized name, in all 28 locales; the wiki says the same.
2026-11-13 19:29:48 +01:00
Maurice 1aa586d829 test: type fixes the CI typecheck found, and the lowered test-new-service baseline
- mailInboxModel.test: actionsOf takes the entry alone since the AI reread left.
- booking-origin.test: its endpoint helper gives the sequence an endpoint has.
- test-new-service baseline follows booking-import's single service factory.
2026-11-13 19:20:09 +01:00
Maurice 53833fd212 fix(mail-ingest): server gates after joining dev, and smaller admin buttons
- lint:tx: the mail transports and the intake document why their writes stand
  alone (@txIndependent, network I/O between them, one copy per address); the
  JSDoc of NotificationsService.respond is back above it after the response
  actions were registered in between.
- lint:boundaries: booking-import asks McpToolGuardsService whether the person
  may edit the trip's bookings instead of reading the Users table; its unit
  test builds the service through one named factory.
- lint:query-api: findIdByExternal reads through Kysely.
- lint:test-mocks: the mail e2e hands its own snapshot db to the ORM instead
  of mocking the database module.
- Entities regenerated (index order only).
- Client: the review row reads the live booking, the shells share
  useMailInboxShell, and the admin card's buttons are one size down.
2026-11-13 18:58:55 +01:00
Maurice a7adef5353 feat(mail-ingest): admin card, review mark, inbox events and links, translations
- Admin settings (desktop and phone, self-hosted only): the instance switch,
  how mail arrives (IMAP or a signed webhook) with its fields, the address
  template, the AI allowance per person and day, a connection test and the
  status in counts. Changed fields only are saved; secrets stay masked.
- The review mark of an imported booking takes its mark off: the pill in the
  desktop dialog and a row in the phone's transport sheet ('Looks right'),
  through the ordinary booking update. Its hint no longer speaks of airports.
- The inbox follows mailInbox:changed and mailInbox:settings, and
  ?inbox=mail (the 'bookings waiting' notification) opens it.
- mailIngestRepo: the online-only path the views take, answers parsed
  against the shared contracts.
- Dropped 'read with AI' and the unused mail-review contract: the server
  reads a passing AI failure again on its own.
- mailInbox, mailAdmin and the new reservation, settings and notification
  strings in all 28 locales; the pending notice is a plural group.
- The live flight line on the booking cards is one of the card's fields,
  full width on the phone; the phone's live card keeps its map to the middle.
- Admin flight and mail cards sit in the left column.
2026-11-13 18:31:07 +01:00
Maurice bd11cd347d fix(mail-ingest): type errors and formatting after joining the flight work
The cron callbacks hand back nothing; the cursor's lookup is findCursor so it
no longer shadows the base repository's find(); the draft's suggested trip
uses the shared contract's type; endpoints spell out their optional columns;
the relay page's running count is typed. Prettier over every new file outside
the generated entities.
2026-11-13 18:03:14 +01:00
Maurice 8dedab0375 feat(mail-ingest): inbox, settings and trip address UI on desktop and phone (wip) 2026-11-13 17:46:19 +01:00
Maurice 1d052f725f fix(mail-ingest): send mailInbox:settings, and give both events their client place
The settings event was registered with no sender; renewing or revoking an
address and changing the switches now send it. The client lists both events
as not yet shown, which keeps the registry-parity test green until the mail
screens move them to their listener.
2026-11-13 17:43:55 +01:00
Maurice 64418489e2 fix(map): guard the GL bridge's resize frame on the layer it attaches
Reading the bridge's MaplibreGL export threw under the test mocks, which
define only the factory, so every mocked basemap counted as an unloaded
chunk. The guard now patches the class of the layer being attached, and
only a class that has _transitionEnd itself, so stand-in layers are left
alone.
2026-11-13 17:20:15 +01:00
Maurice 7238f8ae25 fix(flight-live): reuse the basemap's label hiding and keep the GL guard optional
The resize guard reached for the bridge's MaplibreGL class, which the test
mocks of the bridge do not provide, so every mocked basemap counted as an
unloaded chunk. The guard now only applies where the class exists. The
mini map hides place names through VectorBasemap's own hideLabelLayers
(a hideLabels prop) instead of a second copy of that logic, and the route
test no longer uses Array.prototype.at, which the client's lib target lacks.
2026-11-13 17:02:39 +01:00
Maurice e525e0b838 feat(flight-live): route mini map and a calmer live card; fix the CI gates
The live flight card shows a small real map behind the pass: the great
circle between the airports, solid where flown and dotted ahead, the
aircraft on the line turned along the route. The route is framed in the
free middle, the map fades out under the airports and times laid over it,
and its place names are hidden. Data and map credits sit on the map as its
hover text instead of a footer line.

The card head now follows the booking dialog: a tinted band with pills for
the state, what comes next and the delay; the numbers of the moment use the
dialog's fields on the desktop and the sheet's stat boxes on the phone; both
times sit on equal tiles in one line. Without live data the card, the strips
and the plan line show nothing, and the pulsing dots are gone.

Also fixes the failed dev run: unit symbols and the leg position are listed
as NOT_PLURAL, the aircraft label uses logical sides, the flight store is in
the test reset registry, the sync loop stops once no flight lies ahead (no
endless timers under fake timers), and timeCell and widgetFlight drop below
the max-params and complexity limits. Guards the GL bridge's resize frame
against a layer removed before it runs, and keeps the booking code pill as
tall as its neighbours.
2026-11-13 16:39:46 +01:00
Maurice 41134cc455 fix(native): read the flight widget's direction from the locale
Android lint refuses a composition reading the Configuration through
LocalContext. The arc's mirroring now follows the locale's layout direction,
which is what the widget's language decides anyway.
2026-11-13 15:36:43 +01:00
Maurice 27f12c2245 feat(native): a flight widget and a boarding-pass Live Activity
A new Flight widget on iOS (small to large and the lock screen) and Android
shows the trip's next flight or the one in the air: both airports, the arc
with the aircraft, a countdown the system keeps running, and the delay. It
follows the feed's phase, so a late departure is not flown along the arc.
Around a flight the travel day's Live Activity becomes a boarding pass with
a running bar and countdown, in the Dynamic Island too.
2026-11-13 15:29:21 +01:00
Maurice 4796615439 feat(flight-live): a live boarding pass on the web, desktop and phone
The booking dialog and the phone's transport sheet show a flight's moment,
both airports with planned and expected times, the route as an arc with the
aircraft on it and the numbers of the flight; booking cards and day plan
rows get a one-line state with a progress bar. The trip map draws the
aircraft in the air on Leaflet and GL, and the admin settings carry the
switch. Translated into every language.
2026-11-13 15:29:10 +01:00
Maurice 3365511b10 fix(bookings): the file buttons of a booking never wrap their label
"Vorhandene verknüpfen" broke onto two lines in the narrow half of the
booking dialog. The labels now truncate instead of wrapping, and the German
label is the shorter "Datei verknüpfen", matching "Datei anhängen".
2026-11-13 15:28:55 +01:00
Maurice c2c0535d52 fix(ci): list the packing share pair, and read a stored costs mode through its enum
The whole-list sharing runs on usePackingCategorySharing in the packing panel
and the phone tab, its sheet is markup fed by the tab. The trips service reads
a stored costs_mode as one of TripsCostsMode, which the export check wants used.
2026-11-13 13:30:37 +01:00
Maurice d6799d1205 fix(ci): the calendar query DTO as the contract it publishes, and two typed tests
feeds and trips import the calendar query DTO, so it lives in
calendar.contract.ts rather than as calendar's internal dto file. The places
controller tests pass the KML service in every call, and the revisit pill
test builds a whole list.
2026-11-13 13:13:13 +01:00
Maurice 9cbf31da27 fix(client): the trip form's review and the slow-open splash, and the tests that followed
Inside the trip settings, Escape and the backdrop no longer close the dialog
while the date question is up, and the embedded form does not load the
members it does not show. Every trip open is timed afresh for the splash.
The tests follow the cover image card, the danger button, the delayed splash
and the open and download mocks.
2026-11-13 12:57:57 +01:00
Maurice c2ba78253d fix(client): lint, theme and size ratchets of the quick-win round, and the tests that followed the features
New warnings fixed at their source (refs written in effects, typed tile
options, no stray imports, smaller functions and components), the payer and
split heads hold their switch instead of letting it overhang, the baselines
lowered where the round removed warnings, and the tests updated to the
solo trip, the shares view and the revisit status.
2026-11-13 12:40:17 +01:00
Maurice 75aeba29ed feat(mobile): calmer trip settings, list editing, selecting places and the journey map switch
The trip settings sheet opens on a list of its areas, each a page of its own
with a way back, and the journal there is drawn in the phone's own cards. A
new packing list is typed into a card with its add and cancel, selecting
places keeps its way out in view with the actions as icons below, and the
journey's satellite switch sits in its bar beside the menu.
2026-11-13 12:40:16 +01:00
Maurice bd58f17ec0 feat(trip-settings): edit the trip inside its settings, in one look across the areas and the trip form
Edit trip is an area of the settings dialog, beside People, Sharing and
Journal, instead of a second dialog. The side column names the areas only, the
dialog keeps one height, and every area uses the same cards, fields and
buttons; the trip form on its own takes the same head, cards and buttons. A
trip that loads quickly opens without the splash on the desktop too.
2026-11-13 12:40:16 +01:00
Maurice eda27f1e59 fix(costs): the receipt link is an icon, so the receipts title keeps one line (#2839) 2026-11-13 11:06:48 +01:00
Maurice a08d4b944e feat(mobile): icon-only action rows in the transport and booking sheets (#2585)
The transport sheet's row wrapped once a ticket and the wallet were there.
Its actions are icons now, named by their labels, and the attachments sit in
their own rows with a download, like the transports list. The wallet is a
black circle, beside the close button in the booking editor. A failed
download on the phone now says so.
2026-11-13 10:49:30 +01:00
Maurice 7861006bf5 fix(journey): theme tokens for the album date range (#2859) 2026-11-13 10:49:29 +01:00
Maurice 5f6594f697 fix(tours): Google Maps follows a tour on foot or by bike, CoMaps only gets a route it can follow (#2812)
The Google link carried no mode, so a hike opened as a drive, and a stop on
the equator or the prime meridian was dropped. CoMaps routes a start and an
end only; a tour with stops between would reach it as unrouted pins.
2026-11-13 10:49:28 +01:00
Maurice fcbb538c24 fix(costs): the count beside the total adds up what it does, and its left-out chip counts all again (#2819, #2839)
With a category left out the total card still counted every expense, and in
the table view the only way back was to switch views; the chip now brings
everything back. The phone says what is left out once, under the total.
Linking a receipt offers pictures and PDFs only, like the upload.
2026-11-13 10:49:28 +01:00
Maurice 43a8984e4c feat(planner): a calmer place inspector footer, and a folded place opens in full again
The footer wrapped once a place had a day, a website and a collection. Now
only adding to the day, navigation and edit carry words, website and
collection are icons, and removing from the day and delete sit behind one
"…". The words drop out when the panel is narrow (a tablet) instead of the
row wrapping. Folding is kept for the place on show only (#3039).
2026-11-13 10:49:17 +01:00
Maurice e958a9a2bb feat(ui): icon footer button, and a button menu at the foot opens above its button
DialogIconButton is the square footer button DeleteButton already was, for
any action named by its tooltip. A "…" menu near the bottom of the window
used to be pushed up over its own button; it now opens above it.
2026-11-13 10:49:17 +01:00
Maurice 318e71aa0c fix(pdf): shrink place photos without AbortSignal.timeout and decode them near thumbnail size (#1501)
Safari before 16 has no AbortSignal.timeout, which cost the whole export; a
controller and a timer replace it. Photos decode at twice the thumbnail
height and three at a time, so a long trip no longer ends the web view.
2026-11-13 10:48:58 +01:00
Maurice 308daec14e feat(packing): change the sharing of a whole list at once (#2961)
The list menu on desktop and phone gets "Sharing for Whole List": pick
Shared, Personal or Shared with people once and every item of yours in
that list moves there through the existing per-item sharing write. Items
other people added stay as they are, and the dialog says so up front.
2026-11-13 10:17:35 +01:00
Maurice 3f68c8a50e feat(bookings): download button on booking and transport attachments (#2585)
Each attached file row now has a download icon next to its name, on the
desktop booking cards and detail dialog and on the phone's bookings and
transports cards. On the phone the file rows move out of the card body
button so open and download are two real sibling buttons.
2026-11-13 10:17:35 +01:00
Maurice 924b4e0787 feat(auth): let admins keep the login page on OIDC-only instances (#2925)
A new app setting oidc_auto_redirect (default on) is part of the admin form
and the public app config. Off, an instance without password login no longer
sends visitors straight to the IdP: the login page shows its SSO button,
also while the cached config is read. Admin toggle on desktop and phone.
2026-11-13 10:17:26 +01:00
Maurice d5d9428cf2 feat(calendar): let a feed leave out the per-day summary events (#2602)
Both feed routes and the one-off export.ics take ?days=0, validated by a
shared query contract, and then skip the all-day "Day N" event per day; the
default is unchanged. The subscribe dialog gets a Day overview events switch
that adds the parameter to its links, export_trip_ics takes dayEvents and the
feed tools explain the parameter.
2026-11-13 10:17:25 +01:00
Maurice cd53d0a895 feat(tours): open a tour's route in Google Maps, Apple Maps or CoMaps (#2812)
The tour detail gets a Maps button beside Navigation that hands the whole route
to the apps that follow one, in the tour's own mode (walking or cycling). The
stops are spread evenly along the line and capped at ten, the most a Google
Maps directions link opens. Ski, paddle and horse tours are not offered, since
a map app would route them by road; the GPX export stays the way for those.
2026-11-13 10:17:14 +01:00
Maurice db6bb911c2 feat(roadtrip): show the place photo on the stop sheet and the stop dialog (#2629)
On the road a stop's picture is often what decides whether it is worth the
detour. The phone's stop sheet shows the place's picture full width under its
name and the desktop stop dialog shows it in place of the kind tile; both open
the place's photos on tap. The picture is the place's own image, else the
provider photo for its Google or OSM id through the shared photo cache.
2026-11-13 10:17:14 +01:00
Maurice 69552ffeef feat(journey): satellite base layer on every journey map, the shared page included (#2594)
The journey map gets the planner's round satellite switch in its top corner,
on the desktop aside, the phone views and the public page. Leaflet takes the
vector basemap off while the imagery shows and reattaches it on the way back;
the GL renderer slides the imagery under the journey's own layers. The choice
is per view and opens on the map, so no setting is read or written.
2026-11-13 10:17:13 +01:00
Maurice 1f710bd217 feat(journey): narrow an open photo album to a date range (#2859)
A shared Immich or Synology album can hold hundreds of photos from other
trips. Once an album is open the picker now offers a from/to range that
filters the already loaded assets by the day the grid files them under, so
Select all only takes the photos in range. No server change.
2026-11-13 10:17:13 +01:00
Maurice 11e4fe6ae3 feat(dawarich): preselect the stay's day in the accept dialog (#3068)
The accept dialog opened on "not on a day yet" although the stay carries its
local date. It now picks the trip day whose date matches, on open and when the
trip is switched, and falls back to no day when the trip has none that matches.
2026-11-13 10:17:13 +01:00