Visit plans
A visit plan is an ordered route of contacts to meet, prepared beforehand and executed in the field from the mobile app. It replaces the old "tours": stop order automatically optimized, distance and duration computed, progress tracking, notes taken on site.
Access your plan list from the Contacts menu β My plans, or via the
URL /app/contact/tournees. On mobile: /mobile/contacts/tournees.
Anatomy of a planβ
A plan is made of:
| Element | Description |
|---|---|
| Name | Free label (Thursday tour, Passover visits Bastille...) |
| Description | Optional text (objective, context) |
| Date | Planned execution date (optional, used for filtering and tabs) |
| Profile | Travel mode: On foot, By bike or By car |
| startLocation | Departure point: your GPS location at creation time, or the organization's address |
| Waypoints | Ordered list of stops (contact + address + lat/lng + order) |
| Distance / duration | Computed by OpenRouteService according to the profile |
| Polyline (geojson) | Route trace displayed on the map |
| Status | PLANIFIE, EN_COURS, PAUSED, TERMINEE or ARCHIVEE |
| Author | User who created the plan |
Each waypoint also stores visitedAt, notes and activityId to track
each visit actually done (linked to an entry in the contact's interaction
history).
Create a planβ
There are two ways to create a plan, from the contact map:
A. Manual stop selectionβ
- On the map, click New plan in the SubHeader.
- The map switches to selection mode (pins get a different outline).
- Click the pins to visit. Each selected marker becomes a numbered pink circle.
- In the right-side Visit plan panel:
- Compute itinerary draws the polyline.
- Optimize reorders the stops to minimize total distance (TSP algorithm).
- Drag & drop to reorder manually.
- Save β modal to name and persist it in the database.
B. All visible contactsβ
If your filters narrow the map to 2 to 50 contacts, a panel at the bottom of the filters offers "Create an optimized plan with these contacts β ". One click adds all visible contacts to the plan and launches optimization automatically.
The "My plans" pageβ
The page /app/contact/tournees lists all your plans with:
Stat cardsβ
At the top of the page, 5 clickable cards show counters per status:
| Card | Status |
|---|---|
| Planned | PLANIFIE β plans created but not yet started |
| In progress | EN_COURS β at least one stop visited, plan not finished |
| Paused | PAUSED β manually interrupted |
| Completed | TERMINEE β all stops visited or skipped |
| Archived | ARCHIVEE β deleted (soft-delete) |
Clicking a card filters the table below.
All / Active / Completed tabsβ
Three main tabs for daily sorting:
- All: no filter
- Active:
PLANIFIE + EN_COURS + PAUSED - Completed:
TERMINEE
Tableβ
For each plan, the table shows:
- Colored avatar + name + description
- Status (colored chip)
- Date
- Number of stops + distance + duration
- Progress bar: ratio of visited stops over total
- Author
- Actions: Rename / Duplicate / Open on the map / Start (mobile) / Delete
Actionsβ
| Action | Effect |
|---|---|
| Rename | Opens a modal to edit name, description, date |
| Duplicate | Creates a copy with (copy) suffixed and status PLANIFIE |
| Open on the map | Navigates to /app/contact/maps?planId=xxx β the plan is reloaded in the RoutePanel, editable and re-launchable |
| Delete | Soft-delete (transitions to ARCHIVEE); visits already recorded via this plan remain in the contacts' history |
Field mode (mobile)β
This is the core of the feature. Once the plan is saved, open it on mobile to execute the tour step by step.
Start a planβ
- Open
/mobile/contacts/tournees(from the mobile map'slist-checksFAB, or from the mobile menu). - Three tabs: Today / Upcoming / Completed.
- Tap a PLANIFIE plan β you land on the Active plan screen
(
/mobile/contacts/tournee-active?id=xxx).
The Active plan screenβ
Fullscreen layout (TabBar hidden):
- Fullscreen map with:
- The route polyline
- Numbered waypoints (colored circles based on visited / upcoming / skipped)
- Your GPS location (pulsing blue circle that follows your motion)
- followMyLocation enabled: the map periodically recenters on you as you move
- Minimal transparent header with a back button.
- At the bottom, the non-dismissible MobNavigationPanel:
- Step counter
Stop 3 of 8 - Next contact's avatar + name + address
- Real-time ETA:
5 min Β· 850 m(recomputed by theuseGeoTrackinghook) - 3 action buttons:
- Step counter
| Button | Action |
|---|---|
| Navigate (blue) | Opens native Google Maps (iOS: maps://, Android: https://www.google.com/maps/dir/?api=1&destination=...) in guided navigation toward the stop |
| Visit done (green) | Opens the Visit done sheet (see below) |
Skip (gray, skip-forward icon) | Moves to the next without marking the visit done β the stop stays skipped (not visited, not removed from stats) |
- A floating All stops button opens a drawer listing the whole route,
with each stop's status (
β visited,Β· in progress,β upcoming,β skipped). Tap an item to recenter the map on it. - A small X button on the header right cancels the current plan (deletion in database after confirmation).
The "Visit done" sheetβ
When you tap Visit done, a sheet rises with:
- Avatar + contact name
- Action chips (multi-select):
Tefilim,Mezuzah,Discussion,Donation,Study,Moral supportCorresponds to theactions[]field ofcontact_activityentries. - Text note: free input area.
- Voice note: π€ button to record up to 15 seconds of audio (WebM/Opus). The audio is uploaded then automatically transcribed by Whisper STT on the server side; the transcription is appended to the text note.
On tap on Confirm visit:
- The audio (if any) is uploaded via
POST /tournees/:id/voice-note. - A
contact_activityentry of typeVisiteis created with the current user as author, the selected actions, and the note (text + transcription). - The waypoint moves to
visitedAt: now. - The navigation panel automatically jumps to the next unvisited stop.
Automatic status transitionsβ
The server handles status transitions without intervention:
PLANIFIE β EN_COURS (on first visit-done)
EN_COURS β TERMINEE (when the last stop is visited or skipped)
You can also force manually:
- Pause:
POST /tournees/:id/pause(status βPAUSED, the mobile app can be closed and resumed later without losing progress). - Cancel:
DELETE /tournees/:id(soft-delete, status βARCHIVEE).
Visit tracking in the contact profileβ
Each visit done via a plan creates a standard entry in the Activities tab of the contact, identical to a visit entered from the profile. Specific metadata:
type: "Visite"tourneeId(link to the plan)actions[](selected chips)commentaire(text note + transcription)audioUrl(if there is a voice note)auteur(logged-in user)
Consequence: a contact visited via a plan sees their last-visit chip on the map update automatically (turns green).
Typical use casesβ
Prepare the weekly tourβ
- Open the map on Thursday morning.
- Last visit coloring mode: spot the red and purple contacts around you.
- New plan β select 8-10 contacts on foot.
- Optimize β the order is reorganized in a minimal loop.
- Save as
Thursday tour week 21. - On tour morning, open the mobile app β My plans β tap the plan β Start.
Collective neighborhood visit for a holidayβ
- Filter the map by category
Passover_Family. - If fewer than 50 contacts: Create an optimized plan with these contacts β.
- Profile: By car for a distribution.
- Save, duplicate for each day of the holiday, start.
Prepare an unplanned visitβ
- On the map, tap a pin.
- Navigate button in the popup β opens Google Maps directly (no need to create a plan).
Permissions and multi-tenantβ
- Access to the map and plans requires the
contact.readpermission. - Creating / modifying / deleting a plan requires
contact.write. - Plans are strictly isolated per organization: the backend always
filters on
organisation = res.org.idand returns 404 (not 403) if you try to access a plan from another organization, to avoid leaking the existence of an id.
Limitationsβ
- Max 50 stops per plan (free OpenRouteService quota limit).
- Daily optimization quota: 2,500 calls / day, across all users. Beyond that, the Optimize button returns an error until the next day.
- Voice note: maximum 15 seconds; the format depends on the browser (usually WebM/Opus, M4A on Safari iOS).
- Whisper transcription: the service is self-hosted; in case of unavailability, the audio is kept and a Retry button appears.
- Offline: field mode requires a connection (status updates and audio uploads are synchronous). An offline mode with a sync queue is planned for a future version.
- No drag&drop in the RoutePanel on mobile (desktop only): reorganizing a plan is done from the web version.