Technician App Manual
User Manual

Blukaal Technician — Field Service Mobile App

Blukaal Technician (com.blukaal.technician) is the mobile companion to PLMP’s Service module. It lets a field technician see assigned Service Tickets, look up any ticket or asset by id, record arrivals/visits/resolutions/restorations, reserve and issue spare parts, attach photos, and keep working on the core recording actions without a data connection. It has its own bottom-tab navigation — a deliberately different, phone-first surface from the desktop apps/employee-web workspace.

⚠️ Not yet published to an app store. Today it only runs as a local dev build (npm run dev), an installed PWA, or an unsigned Android/iOS developer build. All screenshots below are real captures from the running dev build, taken against the live PLMP API at localhost:8100 — nothing here is mocked.

Login screen

Screen

First screen shown whenever the app has no active session — on launch, after sign-out, or once a session expires.

Technician app login screen with email and password fields

Real capture · signed-out state, PLMP Technician login

Purpose

Authenticate as a technician so the app can act on your behalf against the PLMP backend. Technicians sign in against the exact same account system as office employees in apps/employee-web — there is no separate technician-only login.

Fields & actions

  • Email and Password fields, plus a Sign in button.
  • A login failure shows inline red error text below the fields (e.g. “Invalid email or password.”).
  • If your account has no two-factor authentication, a correct password signs you in immediately.
  • If two-factor authentication is enabled, you are taken to the Two-Factor Authentication screen next — password alone is not enough.
Offline behavior: Login requires connectivity. There is no offline/cached login — you must be online for the first sign-in and any time a fresh session is required.

Related screens

Two-Factor Authentication

Screen

Shown automatically, immediately after a correct password, only when the account has 2FA confirmed/enabled.

Two-factor authentication screen, authenticator code mode

Real capture · authenticator-code mode (default)

Two-factor authentication screen, recovery code mode

Real capture · after tapping “Use a recovery code instead”

Purpose

Complete the second authentication factor. The session is not considered signed in until this succeeds — a correct password alone leaves you half-authenticated.

Fields & actions

  • A single input field whose label switches between Authenticator code and Recovery code, plus a Verify button.
  • Enter the 6-digit code from your authenticator app and tap Verify.
  • Tap Use a recovery code instead to switch modes (useful if you’ve lost your authenticator device), and back again with Use an authenticator code instead.
  • Errors appear inline (e.g. “Invalid two-factor code or recovery code.”, or “Two-factor challenge failed.” on a bad/already-used code).
Offline behavior: Requires connectivity, same as the login step.

Related screens

“Find a ticket” (Ticket Lookup)

Screen

Landing screen of the Tickets tab, before a specific ticket is open.

Find a ticket screen with scan button and manual ticket id field

Real capture · signed in as manual-demo@blukaal.test

Purpose

Open any Service Ticket by id — including one not assigned to you — typically because you’re working from a physical printed work order rather than your assigned-jobs list.

Fields & actions

  • A Scan ticket QR code button, opening the shared Code Scanner (see §6). The first code it decodes is used immediately.
  • A manual Ticket id field with an Open ticket button.
Offline behavior: Typing or scanning an id works offline, but opening the ticket requires connectivity to actually fetch its details.

Related screens

“My Jobs” (Assignments List)

Screen

Landing screen of the My Jobs tab.

My Jobs screen showing the empty state, no assignments right now

Real capture · genuine empty state (see note below)

Purpose

See every Service Ticket currently assigned to you.

What you see

A list of cards, one per assigned ticket: ticket number, description, a status pill showing the backend’s raw status code (no separate technician vocabulary), and a chevron. If nothing is assigned, it shows “No assignments right now.” A failed load shows an inline error with the underlying message.

Shown here with no assigned tickets. The manual-demo@blukaal.test account used to capture this manual has every permission, but the richer demo dataset with real technician assignments is being built separately and wasn’t in this local database at capture time. This is the app’s genuine, honestly-captured empty state — once demo data is seeded, this screen will show real assignment cards in the same layout.
Offline behavior: Loaded live from the backend (scoped to your technician id) every time you land here — there is no offline cache of your assignment list.

Related screens

Ticket Detail

Screen · most important

Reached from either Ticket Lookup or My Jobs by opening a specific ticket id.

Ticket detail screen showing header, status, priority, asset link and arrivals section

Real capture · header, status & Arrivals section

Ticket detail Visits section with Diagnosis and Work performed fields

Real capture · Visits section (Diagnosis / Work performed)

Ticket detail Restorations section with Restoration notes field

Real capture · Restorations section

Purpose

View everything recorded against one Service Ticket, and record new work: arrivals, visits, resolutions, restorations, spare-parts reservations/issues, and photos.

What you see

  • Header: ticket number, status pill, priority code, assigned technician (or “Unassigned”), description, and the Asset id as a tappable link.
  • Arrivals — prior arrivals plus a notes field and Record arrival button (disabled offline).
  • Visits — prior visits plus Diagnosis / Work performed fields and Record visit. A save shows a short idempotency-key fragment and the pending-sync count.
  • Resolutions — prior resolutions plus a notes field and Record resolution, same save confirmation pattern.
  • Restorations — prior restorations plus a notes field and Record restoration (disabled offline).
  • Parts — compatible spare SKUs for this asset’s product model, plus Reservations and Issues sub-sections (each disabled offline).
  • Photos & documents — attached files list plus Attach a photo (prefers the rear camera).

What each action actually does

ActionBehaviorConnectivity
Record arrivalReal backend command; server records you as the arriving technicianOnline only
Record visitCombines diagnosis + work performed into one command, queuedWorks offline
Record resolutionQueues a real resolution commandWorks offline
Record restorationReal backend command, directOnline only
Reserve a spareReal spare-reservation commandOnline only
Issue a spareReal spare-issue commandOnline only
Attach a photoReads the captured photo, queues a document-upload commandWorks offline
Tap Asset idOpens Asset Lookup for this ticket’s assetNeeds connectivity once there

All of these are real, persisted backend actions — nothing here is a demo/mock. Recording a restoration is distinct from resolving a ticket, and resolving does not automatically close it (a Visit ≠ a Restoration ≠ a Resolution ≠ a Ticket Closure).

Safe offline: Visits, Resolutions and Photos are queued locally (IndexedDB) and sent automatically once back online — each carries a client idempotency key so a retried send can never create a duplicate record.
Online-only: Arrivals, Restorations, and Parts reservations/issues are disabled while offline with an inline explanation — their backend commands don’t yet support an idempotency key, so queuing them risks a genuine duplicate (a second arrival, a double-issued part).

Related screens

Asset Lookup

Screen

Reached by tapping a ticket’s Asset id link, or in principle as a standalone lookup.

Asset lookup screen showing asset number, product model, SKU and lifecycle status

Real capture · opened from the demo ticket’s Asset id

Purpose

Look up the details of a physical Asset by its internal id.

Fields & actions

  • A Scan asset label button (same Code Scanner component, §6).
  • A manual Asset id field with a Look up button.
  • Once loaded: Asset number, Product model (id), SKU (or “—”), Lifecycle status, and Owner customer account (or “—”).
  • A Back button returns to wherever you came from.

An id with no match shows: “Asset '<id>' was not found.”

Offline behavior: Requires connectivity — a single live “fetch asset by id” call, with no offline cache or scan-to-search endpoint.
Scope note: This screen shows only the asset’s own record — no parts-used/parts-reserved history view exists yet for technicians in the current backend.

Related screens

Account

Screen

Landing screen of the Account tab.

Account screen showing identity card, connection status and sync now button

Real capture · identity + connection cards

Purpose

See your own identity/session info, check connection and offline-sync status, manually trigger a sync, and sign out.

What you see

  • Identity card: Name, Email, and whether Two-factor authentication is Enabled.
  • Connection card: live Online/Offline status, queued offline action count, a “(syncing…)” suffix mid-sync, and a Sync now button (disabled if already syncing or nothing is queued).
  • Session: a Sign out button (calls the real logout endpoint).
Stale in-app copy, confirmed live: the Connection card’s explanatory sentence still says syncing “only clears the local queue’s bookkeeping” because there’s “no real command backend yet” — visible verbatim in the screenshot above. That text is out of date: as documented in §7, a sync today genuinely sends queued Visits, Resolutions and Photos to the real backend. Trust the pending-count number and the Sync now button’s actual behavior over that leftover sentence.
Offline behavior: The identity card and connection status need no connectivity to view (loaded at sign-in / from device network state). Signing out requires connectivity.

Related screens

None — this is a leaf screen.

Code Scanner

Shared component

Not a bottom-nav destination — a shared camera-scanning overlay used from both Ticket Lookup and Asset Lookup.

Purpose

Scan a QR code or barcode printed on a work order (ticket id) or an asset’s physical label (asset id), instead of typing it by hand.

What you see & can do

  • A live rear-camera preview when supported, a manual-entry field (“Enter code manually”) with a Use this code button, and a Cancel link.
  • Recognizes QR codes, Code 128, EAN-13, and Code 39. The first code decoded is used immediately — no confirmation step.
  • If camera scanning isn’t supported or accessible, an inline message explains this and manual entry works exactly the same — a fully supported path, not a degraded fallback.
Platform note: relies on the browser’s BarcodeDetector API — supported in Chromium and Android WebView, not in Safari/iOS WebView. On iOS, manual entry is the only working path today; this is a disclosed limitation, not a bug.
There is no server-side “search/lookup by scanned code” capability — scanning only ever decodes a code into the same plain id string you could type by hand.
Offline behavior: The camera and scanning work entirely offline (on-device decoding). What you do with the resulting id is what needs connectivity.

Offline behavior — the full picture

Reference

What’s cached/queued locally

  • The app shell itself opens with no connection via a real PWA service worker precaching UI code — but no ticket/asset data is cached, so nothing fresh loads offline.
  • Queued field-work commands live in a durable on-device IndexedDB store, surviving an app restart or a dead phone mid-shift. Each queued action gets a unique client-generated idempotency key at the moment it’s recorded.

Safe offline — queued automatically

Recording a technician visit, recording a resolution, and attaching a photo. Their backend commands accept the client idempotency key, so a lost confirmation and retried send can never create a duplicate record. The screen shows a confirmation with a key fragment and pending-sync count immediately, offline or not.

Needs connectivity — disabled while offline

Recording an arrival, recording a restoration, reserving or issuing a spare part, opening/refreshing My Jobs / Find a ticket / Asset lookup, and signing in, the two-factor step, and signing out. Their backend commands don’t yet support an idempotency key — queuing them for later replay would risk a genuine duplicate business record, so the app disables them and explains why instead.

Syncing back up

  • The app automatically retries every queued action once connectivity returns, strictly in recorded order (first recorded, first sent).
  • You can also force this from the Account screen’s Sync now button.
  • If an item genuinely fails to send, the queue stops there (nothing jumps ahead) and retries from that point on the next attempt.
  • A successful sync is a real, completed backend action — not local-only bookkeeping (despite the stale in-app text noted in §5).

Typical technician workflow

Reference
  1. Sign in (with 2FA if enabled).
  2. Land on Tickets by default, or tap My Jobs for your assigned queue.
  3. Open a ticket from My Jobs, or scan/enter an id from a physical work order via Tickets.
  4. On site: Record an arrival (needs connectivity — do it before entering a signal-dead area).
  5. Tap the ticket’s Asset id to confirm you’re looking at the right equipment; Back returns to the ticket.
  6. Do the diagnostic/repair work, then record a visit — safe with no signal.
  7. If parts are needed, check Parts, then Reserve and later Issue the spare (both need connectivity).
  8. Attach photos of the work as you go — safe offline, queued automatically.
  9. If fixed, record a restoration (needs connectivity) once back in signal.
  10. Once the underlying issue is addressed, record a resolution — safe offline. This does not close the ticket by itself.
  11. Check Account any time to confirm the queued-action count is dropping to zero, or tap Sync now.
  12. Sign out at end of shift on a shared device.

Known limitations to disclose to technicians

  • Not available from an app store. No signed release build exists yet in either store — today the app only runs from a local/dev build or an installed PWA.
  • iOS has no camera code scanning — manual code entry is the only path on iOS today.
  • Arrivals, restorations, and spare-parts actions require connectivity — no offline queueing for these, by deliberate design, to avoid duplicate records.
  • No offline cache of ticket/asset data — only the app’s own UI shell is available offline; a ticket must already be loaded, or you must wait for connectivity, to see its data.
  • No parts-used history on Asset Lookup — that view doesn’t exist for technicians in the current backend.
  • The Account screen’s connection-status text about syncing is stale — it understates what actually happens; real sync behavior is as described in §7, not as that leftover sentence describes it. Confirmed live in the §5 screenshot above.