Commit Graph

4 Commits

Author SHA1 Message Date
kovakmedya 53e443b4f1 feat(jobs): clinic-side 'Düzeltme İste' (revision request) flow
Up to now the only thing a clinic could do on a prova was approve it.
If the casting didn't fit there was no way to bounce the case back to
the lab short of cancelling the whole thing. Real-world flow needs a
'try again, this is what's wrong' lever, so:

  - requestRevisionAction (clinic only): pre-conditions
    in_progress + at_clinic + currentStep set; flips location → at_lab
    while leaving currentStep untouched so the same prova stage repeats
    after the lab redoes the work. Requires a note (the lab can't fix
    what it doesn't know is broken) — appended to job_status_history
    with a '[Düzeltme talebi]' prefix and surfaced to the lab via
    notification.
  - JobActionsPanel: when the clinic side sees a prova (in_progress +
    at_clinic) it now shows two buttons — Onayla as before, plus
    Düzeltme İste (variant=destructive). The dialog requires a note
    before submit.
2026-05-22 16:03:36 +03:00
kovakmedya 479972e9a9 feat(workflow): split job step from location, model back-and-forth between lab and clinic
Real prosthetic production isn't a one-way pipeline — the work moves
between lab and clinic multiple times. After substructure is produced
the lab hands it to the clinic for a fitting, the clinic approves it
back to the lab, the lab builds the superstructure, hands it back for
a second fitting, the clinic approves again, the lab does cila/bitim,
and finally delivers it to the clinic for handover to the patient.

Previously we only had a single 'advance step' action callable by the
lab, which collapsed all of that into a linear forward push and didn't
capture who physically had the work at any given moment.

DB
  - New jobs.location enum (at_clinic | at_lab, default at_clinic).
  - Existing jobs keep working via a 'location ?? at_lab' fallback in
    code; no manual backfill required for the four test rows.

State machine
  - acceptJobAction (lab): pending → in_progress, currentStep=alt_yapi_prova,
    location=at_lab. Skips the implicit 'olcu' production step now that
    accepting the job means the lab has the impression in hand.
  - handToClinicAction (lab, NEW): at_lab → at_clinic, step stays the
    same. If step is cila_bitim, status becomes 'sent' (final delivery)
    and finance sync fires.
  - approveAtClinicAction (clinic, NEW): at_clinic → at_lab, step
    advances to the next stage so the lab knows what to produce next.
  - markDeliveredAction unchanged — clinic confirms the final handoff.
  - advanceStepAction removed; its single forward push doesn't fit the
    new bidirectional flow.

UI
  - JobActionsPanel now picks the right button from the role + status +
    location matrix:
      * Lab + pending          → 'İşleme Al'
      * Lab + in_progress + at_lab + cila_bitim → 'Cila Bitim — Nihai Teslime Gönder'
      * Lab + in_progress + at_lab + other      → '{stage} Provaya Gönder'
      * Clinic + in_progress + at_clinic → '{stage} Provası Tamam'
      * Clinic + sent          → 'Teslim Aldım'
      * Both + pending         → 'İptal Et'
  - Job detail surfaces a new 'Şu An' info row that resolves to a
    human-readable location ('Klinikte', 'Laboratuvarda', 'Hasta'ya
    teslim edildi', ...) so anyone glancing at the page can tell where
    the work physically is.
2026-05-22 01:31:49 +03:00
kovakmedya cdb2a15643 fix(ui): router.refresh after server actions so status updates show without reload
Lab side reported that after accepting a job / advancing a step the
button kept its 'Yükleniyor' state and the page didn't reflect the new
status until they hit refresh. Two issues stacked on top of each other:

1. The button forms were passing the action through an extra
   startTransition wrap — 'action={(fd) => startTransition(() => action(fd))}'.
   With React 19 + useActionState this is unnecessary; useActionState
   already manages its own transition. The double transition can leave
   the dispatch's pending flag wedged in some race orderings, which
   matches what the user saw.

2. revalidatePath() on the server invalidates the RSC cache but does not
   trigger client navigation. So even after the action returned, the
   page kept rendering the stale Job snapshot — and since the buttons
   are conditional on job.status, the now-stale 'pending' status meant
   the button stayed visible.

Fix in JobActionsPanel and the four sibling components (connections
delete row, pending inbound, pending outbound, file row delete):
  - Removed the startTransition wrap; forms point at 'action' directly.
  - Added useRouter() and call router.refresh() in the same useEffect
    branch where the success toast fires. This forces the Server
    Component tree to re-fetch, picks up the new job.status, and the
    actions panel rerenders into whatever button is next in the flow.
  - Cleaned the now-unused useTransition imports.

Net effect: tap 'İşleme Al' → spinner appears, ~400ms later the toast
hits and the row updates in place to 'Sonraki Aşama' without any
manual refresh.
2026-05-22 01:15:32 +03:00
kovakmedya 2c6c074a06 feat: job status/step flow, file upload, finance sync, notifications
Job lifecycle
  - acceptJobAction (lab): pending → in_progress + currentStep=olcu
  - advanceStepAction (lab): step ilerletir, son adım sonrası status=sent
  - markDeliveredAction (clinic): sent → delivered
  - cancelJobAction: pending iş iptali (her iki taraf)
  - job_status_history her step transition'da idempotent kayıt
  - Detay sayfası interactive panel + Aşama Geçmişi kartı

Job files (Appwrite Storage job-files bucket, 30MB/file)
  - uploadJobFilesAction: çoklu dosya, mimeType'tan kind sınıflandırma
    (scan/image/document), her iki team'e read permission, partial-fail
    rollback (storage + row temizliği)
  - deleteJobFileAction: yetkilendirilmiş silme, file + row birlikte
  - JobFilesPanel: client-side select + upload + liste + indir + sil
  - next.config bodySizeLimit 3mb → 100mb (toplu yükleme için)

Finance sync (idempotent)
  - syncFinanceForJob helper: sent/delivered transition'larında klinik
    payable + lab receivable rows (jobId+tenantId+type unique kontrolü,
    her tarafta tek satır garanti)
  - markFinancePaidAction / reopenFinanceAction: manuel ödendi/geri al
  - /finance sayfası: stat kartlar (bekleyen alacak/borç, aylık gelir/gider)
    + hareketler tablosu, role-aware kopyalar
  - Memory rule [[feedback_cross_entity_sync_helpers]]: best-effort, never
    re-throws

Notifications
  - createNotification helper, connection (request/approve) ve job
    (create/accept/sent/delivered) eventlerinde tetikleniyor
  - /notifications sayfası + tek tek / hepsi okundu işaretle
  - Header'a Bell ikonu + okunmamış count badge (layout SSR'de besler)
  - Middleware PROTECTED_PREFIXES'e /notifications ekli
2026-05-21 20:17:33 +03:00