Commit Graph

3333 Commits

Author SHA1 Message Date
Maurice 745cd5f65d refactor(roadtrip): share the stay length draft between desktop and phone
The stay dialog and the phone stay sheet take their presets, the five minute step, the full day limit, the draft and the arrival and departure preview from useStayDraft. Each still seeds the draft when it opens and saves it its own way; the phone keeps whole minutes when a step moves a stored fraction, the desktop does not.
2026-10-09 15:19:43 +02:00
Maurice 97d3512aa7 refactor(planner): share the trip export between desktop and phone
The export dialog and the phone export sheet keep their rows and run the PDF, the calendar file and the GPX download through useTripExport, which also reads the print settings and whether my plan is offered. The desktop still runs one export at a time and closes after each one; the phone still lets a calendar or GPX file run beside a PDF and stays open after the PDF.
2026-10-09 15:19:43 +02:00
Maurice a4e3e83d3f refactor(planner): share the transport detail day and segment labels between desktop and phone
The transport detail dialog and the phone transport sheet name the booking's day and each segment's own booking code through transportDetailModel, next to the files filter they already share. The rest of the two views reads the booking differently on purpose and stays as it is.
2026-10-09 15:19:42 +02:00
Maurice fc3cc1bbbe refactor(planner): share the booking form and its hotel stay between desktop and phone
The booking dialog and the phone booking sheet keep their markup and run on useReservationForm: how a saved booking or an import fills the form, the end before start check, the start date and time, the place and hotel picks, the stay range and the request body with the stay a hotel booking creates come from reservationFormModel. The desktop's check-in window, its link to a stop inside the form and its imported price stay its own, behind options where the code is shared.
2026-10-09 15:19:42 +02:00
Maurice 203e425e7e refactor(planner): share the transport booking form between desktop and phone
The transport dialog and the phone transport sheet keep their markup and run on useTransportForm: the route rows, how a saved booking or an import fills them, the day options and the request body come from transportEndpoints. Where the two shells differ today (unknown type fallback, stopover days, which rows are reset, train anchors, empty metadata, the link field) the shared code takes an option. The phone's upload helper moves to bookingFormModel next to the traveller checks both booking forms use.
2026-10-09 15:19:42 +02:00
Maurice 131d57b755 refactor(budget): share the costs ledger and settlement form between desktop and phone
costsModel moves from the phone tabs to components/Budget.
useCostsLedger holds the settlement, the totals, the filters, the CSV
export and the payments, and useSettlementForm holds the payment form.
CostsPanel and MCostsTab both run on them.

The old differences stay behind flags. The desktop also loads the
settlement on a trip change, leaves expenses nobody paid out of my share
(#2225) and writes each currency as stored in the CSV. The phone sheet
starts blank, refills when it opens and ignores a second save while one
is running.
2026-10-09 15:19:42 +02:00
Maurice 80c66d3f83 refactor(budget): share the expense editor between desktop and phone
useExpenseForm holds the payers, the splits, the ticket items and the
receipts of the expense editor in CostsPanel and MCostSheet.

Flags keep the old differences. Only the desktop sends the people named
on a ticket line as members (#1382). The phone ignores a second save
while one is running and stays busy after a successful save. The
rebalance effect uses useEffectEvent instead of an eslint-disable.
2026-10-09 15:19:42 +02:00
Maurice ad220d9608 refactor(packing): share the bag editor and the bag weight totals between desktop and phone
useBagCardEditor holds the editing of a bag's name and weight limit for
the desktop bag card and MBagsSheet, and bagLoadSummary computes the
weight totals for the bag sidebar, the bag modal and the phone sheet.
The desktop card follows the bag when it changes elsewhere, the phone
row resets a name it did not save.
2026-10-09 15:19:42 +02:00
Maurice 4d4ef17de8 refactor(packing): share the packing list logic between desktop and phone
usePackingItemActions, usePackingTemplates and usePackingImport take the
item, template and bulk import logic out of usePackingListPanel and
MPackingListTab, and the phone listsModel keeps only its priority
tokens.

When adding to a category the desktop looks for the placeholder in the
store and the phone in its rendered items. The desktop toasts on an
empty import, the phone runs one import at a time. The effects run in
the same order as before.
2026-10-09 15:19:41 +02:00
Maurice 6dc8699bd1 refactor(todo): share the to-do filters and the task form between desktop and phone
The to-do filters, counts, sort order and categories move from the phone
listsModel into todoListModel. useTaskForm holds the name, the fields,
the load and the save of a task for the desktop detail and new task
panes and for MTaskSheet. Each side keeps its own guard and error
wording.
2026-10-09 15:19:41 +02:00
Maurice 5a0e03929d refactor(lists): share the remembered lists sub-tab between desktop and phone
useListsSubTab remembers whether the packing or the to-do sub tab was
open, for ListsContainer and MTripShell. The desktop restores any stored
value, the phone restores todo and otherwise falls back to packing.
2026-10-09 15:19:41 +02:00
Maurice 71289ba43e refactor(places): share the place form and the maps search between desktop and phone
usePlaceForm and usePlaceSearch take the form state, the prefill, the
opening hours selection, the duplicate name check, the payload and the
maps search out of PlaceFormModal. MPlaceEditSheet, PlPlaceSearch and
PlCategoryPicker run on the same hooks, and PlTimeFields uses the shared
timeCollisions.

Where the two shells behaved differently the hooks take a dialog or a
sheet variant: the coordinate pair rule, abort and console handling,
when the billing session ends, whether a pick clears the suggestions,
the empty search cue, whether a map link clears the results, and how a
new category is trimmed and guarded. The open effect and the submit flow
stay per shell, because service stops, expense intents and delete arming
differ too much to share.
2026-10-09 15:19:41 +02:00
Maurice b2fd0ae5aa refactor(collections): share the add place to list logic between desktop and phone
useAddPlaceToCollection holds the maps search, the pick and the save
that AddPlaceToCollectionModal and MCollAddSheet each carried, with the
pure helpers in addPlaceModel.

The differences stay as variants. The desktop resets and stays open
after an add, sends links and typed coordinates and shows the error's
own text. The phone closes, takes coordinates only from the picked hit,
ignores a second search or save while one is running and shows the
fallback text.
2026-10-09 15:19:41 +02:00
Maurice bbe3da7722 refactor(places): share the places pool filters and selection between desktop and phone
usePlacesPool holds the category filter, the search, the multi select
and the bulk mark visited of the places pool, for usePlacesSidebar and
for MPlacesBrowser with its filter controls and sheet. When selected
places are deleted, the desktop leaves select mode once none of them is
left and the phone drops only the missing ids. The hook keeps both.
2026-10-09 15:19:41 +02:00
Maurice 4dc446424f refactor(places): share the GPX, KML and KMZ file import between desktop and phone
usePlacesFileImport holds the GPX, KML and KMZ import that
FileImportModal and ImpFileStep each carried. The desktop keeps one
toast and one undo label and counts only the summary's skips. The phone
keeps a toast per format, the importFiles undo label for a mix and the
top level skipped fallback.
2026-10-09 15:19:41 +02:00
Maurice f2b6a14e9f refactor(places): share the Google and Naver list import between desktop and phone
useListImport holds the Google and Naver shared list import that
usePlacesSidebar and ImpListStep each carried. The desktop closes the
dialog and clears the link before the undo step, the phone calls onDone
after it, and the hook keeps both orders.
2026-10-09 15:19:41 +02:00
Maurice 8a28f5e22f refactor(collections): share the bulk save to list logic between desktop and phone
useSaveTripPlacesToList holds the logic that saves a selection of trip
places to a list, which SaveTripPlacesToListModal and
MPlacesSaveToCollectionSheet each carried as a copy. The two copies did
the same thing, so the hook has no variants.
2026-10-09 15:19:40 +02:00
Maurice 1b51181930 refactor(planner): share the bookings and transports list filters between desktop and phone
The bookings model moves from the phone tabs into components/Planner/bookings, so
both shells read the same sections, traveller predicate and type colours. The
phone bookings and transports tabs had the same traveller filter and folded
sections twice; that state now lives once in useReservationListFilter, which only
those two tabs use. The desktop panel keeps its own filters in useBookingsView
because it remembers them for the session.

The status switch and the delete on a booking card stay per shell. The desktop
hook renders its ConfirmDialog and awaits the delete, the phone asks in an
MConfirmSheet and lets the delete run on its own; sharing them would change
when the question closes.
2026-10-09 15:19:40 +02:00
Maurice 9529b55bd7 refactor(planner): share the booking files filter between desktop and phone
filesFor moves out of the desktop booking parts into utils, and the phone
transport sheet now reads a booking's files through it as the desktop
transport detail and booking cards do.
2026-10-09 07:16:40 +00:00
Maurice d01d9df93a refactor(planner): share the day note draft and save between desktop and phone
The desktop note dialog and the phone note sheet fill their editor with
dayNoteDraft and save through writeDayNote, so both trim the title, store an
empty detail as null and default the icon the same way. Both count the detail
line against one shared limit.
2026-10-09 07:16:40 +00:00
Maurice 70c96be193 refactor(planner): share the stay editor rules between desktop and phone
The desktop hotel picker and the phone accommodation sheet take their day
options, range changes, checkout default, places on offer and request body
from stayFormModel, and both save a stay through writeStay.
2026-10-09 07:16:40 +00:00
Maurice f6d51ba68e refactor(planner): share the day detail logic between desktop and phone
The desktop day panel and the phone day sheet load the day's forecast with
useDayForecast, rename the day with useDayRename, and read the day's
bookings, the stay labels and the display temperature from dayDetailModel.
2026-10-09 07:16:40 +00:00
Maurice 38c7d76c5d refactor(planner): share the per leg travel mode between desktop and phone
The day plan's connector menu and the phone timeline's are built by
legModeMenuItems, and both save a leg's mode through useLegModeActions, which
reports a refused write and reloads the days the same way on both.
2026-10-09 07:16:40 +00:00
Maurice 20d7e62afc refactor(planner): share the day plan reorder writes between desktop and phone
The desktop day plan and the phone timeline derive the place order, the note
and transport positions and the per leg positions of a reordered day from
mergedOrder, and write the moved bookings back through the same helpers.
2026-10-09 07:16:40 +00:00
Maurice ae31b55441 refactor(planner): share the day route export and optimize slots between desktop and phone
dayRoute moves from the phone's lib into components/Planner. The desktop day
plan builds its Google Maps and CoMaps stops with dayExportStops, and both
optimizers put the reordered stops around the fixed ones with fillAroundLocked.
2026-10-09 07:16:40 +00:00
Maurice 9f3e1f8c83 refactor(weather): share the day weather anchor and icon map between desktop and phone
The day plan, the day panel, the phone timeline and the phone day sheet read
the day local weather anchor from dayWeatherAnchor, and every weather icon
comes from one map in weatherIcons.
2026-10-09 07:16:40 +00:00
Maurice 34127b99bb refactor(planner): share the place detail actions between desktop and phone
The place inspector and the phone place sheet now run on one module for
their participants, file uploads, image picks, ratings and place updates,
and both build the save to collection target with placeToSaveTarget.
2026-10-09 07:16:40 +00:00
Maurice 8bd831238d test(server): cover the lease failures, sink errors and store passthroughs
The lease heartbeat and settle failures, the log sink and rotation
fallbacks, the runtime env getters, the public API summary edges, the
admin-only permission level and the WebSocket handler rejection had no
spec, which took six domains under their coverage floor.
2026-10-08 21:15:17 +02:00
Maurice 2b218a5528 refactor(server): drop the state helpers the injected stores replaced
setPermissionsCache and the free leaveRoom, leaveAllRooms and
leaveAllBooks lost their last caller when the services moved onto the
injected stores.
2026-10-08 21:15:17 +02:00
Maurice 9c39a0fc58 test(offline): count the trip member the v2 case stores 2026-10-08 20:22:17 +02:00
Maurice da97d7535f test(server): follow the signatures the session and port work changed
The session gate passes a session lookup to verifyJwtAndLoadUser, the
Immich save takes auto_upload, getAppUrl stays synchronous, the demo
session list marks the current session by the id it is handed, and the
lease no longer needs a transaction of its own, so its spec is gone.
2026-10-08 20:22:16 +02:00
Maurice 37af6f4ff9 test(server): seed bookings and places the way the services store them
The converted specs wrote accommodation_id as a plain id, while the
services store the legacy '<id>.0' text, and wrote read-only foreign key
twins instead of their relations. The files e2e schema gains the trip
columns the entity fills, and the idempotency spec seeds a real user.
2026-10-08 20:22:16 +02:00
Maurice ca0138dc45 test(server): make the row factories return what they promise
findRow and the default category lookup asked findOne with an empty
where, which MikroORM refuses. insertRows returned a single id because
insertMany on plain data reports only the last insert id; it now inserts
row by row and returns every id. insertRow and updateRows reject instead
of throwing before the caller holds a promise.
2026-10-08 20:22:16 +02:00
Maurice 7ae4df1ca2 test(trips): expect a failed day rebuild to roll the new dates back
TRIP-SVC-072 still pinned the old order, where the trip row committed
before the days were rebuilt. Both now run in one transaction, as
TRIP-SVC-018b already asserts.
2026-10-08 19:21:52 +02:00
Maurice 0172c5d12a ci(server): record the first Postgres probe measurement
All 100 dialect helper cases pass on both engines. Of 1402 repository
methods, 11 send one statement Postgres refuses as SQL and 4 send none
the probe can see; both lists may only shrink from here.
2026-10-08 19:19:15 +02:00
Maurice ce56efefc6 docs(wiki): list the dialect, Kysely type and Postgres probe commands 2026-10-08 19:18:42 +02:00
Maurice 0fa47e4297 test(demo): give the session reset spec a baseline to reset to
resetDemoUser returns early when no baseline file exists and asks the
port whether it can snapshot, so the spec needs both to reach the swap.
2026-10-08 19:18:42 +02:00
Maurice be016f180e test(server): seed the session, lease and rollback specs through the factories
The specs written alongside the session, transaction and lifecycle work
still seeded and read with raw statements. They go through the ORM
factories now; the few that test SQL below the ORM carry a test-sql-allow
reason. The raw SQL baseline holds 177 statements in 6 files.
2026-10-08 19:05:04 +02:00
Maurice ccf8c4309a ci(server): check the generated Kysely types and the SQL dialect baseline
check:db-types and lint:dialect were documented as gates but not run.
2026-10-08 19:05:04 +02:00
Maurice 8c66964961 test(server): fit the session, transaction and seeding specs together
The session carry-over specs were written against the old close and
reinitialize calls; they now drive the database backup port. The rollback
specs read through the factories and await the helpers that became async
when the seeding moved to the ORM. Two readers that lost their await and a
duplicated import are fixed as well.
2026-10-08 19:05:04 +02:00
Maurice 68504b337f refactor(scheduling): take a cron lease in one conditional upsert
acquire() made sure a row existed and then raced on a conditional UPDATE,
two statements the registrar wrapped in a transaction of its own. lint:tx
cannot follow a repository reached through a local variable, so it
reported the method. One INSERT ... ON CONFLICT DO UPDATE ... WHERE now
takes a free lease, renews the holder's and leaves anyone else's alone,
atomically on SQLite and on Postgres, and needs no transaction.
2026-10-08 19:05:03 +02:00
Maurice fac139723e build(db): generate the Kysely types for the lease and session tables
Both tables arrived in migrations written before the generator existed,
so src/db/kysely/ did not describe them and check:db-types would fail.
Regenerated with gen:db-types.
2026-10-08 19:05:03 +02:00
Maurice 115f76ffae style(server): re-indent the user deletion assertions in the admin spec
The conversion left 23 assertions of the user deletion case at column 0 in the middle of its indented body.
2026-10-08 18:47:25 +02:00
Maurice e90ed065e5 test(server): clear and age fixture rows through the ORM in converted specs
These specs left the raw SQL baseline but still emptied tables with
`db.exec('DELETE FROM ...')` and aged a Dawarich connection with a raw
UPDATE, which the ratchet cannot see because it only counts `.prepare(`.
They now go through deleteRows and updateRows like the rest of the file.

packing_item_recipients and packing_bag_members have no entity; both
cascade from their item and bag, so the packing spec relies on that with
foreign keys on in the snapshot database.
2026-10-08 18:47:25 +02:00
Maurice df10dc6ec2 test(server): assert the row exists before checking its stamped columns
findRow returns null for a missing row, and `row?.deleted_at` is then
undefined, which `.not.toBeNull()` accepts. The converted soft delete,
album sync and passkey counter checks would therefore pass if the row were
removed outright. They now assert the row is there first, as the raw
`.get()` reads they replace did by throwing on a missing row.
2026-10-08 18:47:25 +02:00
Maurice f579f03692 test(server): drop the dashes from two reworded spec comments
The permissions and plugin RPC fallback comments were reworded when
their raw SQL went away; this keeps the new wording in line with the
comment style used elsewhere in the converted specs.
2026-10-08 18:47:24 +02:00
Maurice 4c69804c81 test(server): lower the raw SQL baseline to what the converted specs hold
The specs converted to the factories no longer carry raw prepare()
calls, or keep only ones marked with test-sql-allow, so their baseline
entries are dropped. Leaving them in place would let raw SQL grow back
into those files unnoticed.
2026-10-08 18:47:24 +02:00
Maurice 6a7b55b13b test(server): seed the reservations service spec through the factories
Converts the raw prepare() fixtures and reads in the reservations service
spec to the ORM factories: expense links, stays, stops, endpoints, day
positions and guest flags are written through insertRow and updateRows,
and the narrow SELECT post-conditions read whole rows through findRow and
findRows, compared with toMatchObject so they still assert exactly the
columns the projection named. stopsOn keeps returning only the three
columns its toEqual assertions compare, and the helpers that now await
become async.

The one write left raw carries a test-sql-allow marker: it plants
dangling references inside the synchronous foreign_keys = OFF window,
which an async ORM write would outlive. The unused canAccessTrip and
isOwner entries are dropped from the database mock.
2026-10-08 18:47:21 +02:00
Maurice 24a2624488 test(server): seed the users repository spec through the factories
Converts the raw prepare() fixtures and post-condition reads in the users
repository spec to updateRows, findRow and readUser. The Immich read
helper becomes async and still returns only the three columns its toEqual
assertions compare, and the guest insert check compares its columns with
toMatchObject now that the read returns the whole row.

The raw statements that remain carry a test-sql-allow marker: the legacy
statements run as parity oracles for the repository reads, the
NOT NULL DEFAULT 0 probe on is_guest, which has to bypass the entity
default, and the unflushed-entity check, which must read the table
without an EntityManager that could flush first.
2026-10-08 18:47:13 +02:00
Maurice a0a584e1df test(server): seed the places service spec through the factories
Converts the raw prepare() fixtures and reads in the places service spec
to the ORM factories: tours, waypoints, files, links, expenses, collection
places and ratings are written through insertRow and updateRows, and what
the service left behind is read back through findRow, findRows and
countRows. The track colour cases insert their seeded tracks with
insertRows in place of INSERT ... RETURNING, tracksOf and the file attach
helper become async, and the stale updated_at is computed with dbNow
instead of datetime('now', '-1 hour'). The rating reads keep comparing
only the rating column, as the narrow SELECT did.
2026-10-08 18:47:12 +02:00