feat: port tauri create-service page as 3-step new-session wizard

Backend:
- Add session_at and inventory_package_id to patient_sessions, new
  session_consumables table (migration Version20260716102537)
- New SessionConsumable entity/repository mirroring SessionService;
  price snapshot, quantity >= 1, tenant-scoped silent skip
- PatientService::createSession accepts session_at, consumables[] and
  inventory_package_uuid; consumables are fully patient-paid (no
  insurance coverage) and added to final_price_rials
- Functional tests: success, foreign-tenant/unknown skip, empty and
  zero-quantity edges (tests/Patient/SessionConsumableTest.php)
- docs/api/patient.md updated for the new Create Session fields

Frontend (admin):
- NewSessionPage rewritten as the tauri /files/create-service 3-step
  wizard (ایجاد سرویس ← پرداخت ← جزییات) using SessionStepper
- New CreateStep: acceptance date/time (Jalali), section/service/staff,
  consumables with counters, package select, conditional insurance
  block (insured service or insured patient profile), price summary
- PaymentStep/DetailsStep extracted from SessionPaymentPage and shared
  between both pages (behavior unchanged, tests still green)
- UserTick and FilesServiceAddCard icons ported verbatim from tauri
- Vitest coverage for the wizard incl. empty-data states

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
hamed
2026-07-16 14:20:32 +03:30
co-authored by Claude Opus 4.8
parent d7b06108f0
commit 07222826c3
14 changed files with 1462 additions and 550 deletions
@@ -358,3 +358,29 @@ export function CloseModalD() {
</svg>
);
}
/** «پذیرش کننده» — tauri UserTick, verbatim. */
export function UserTick({ color = '#0D9F43', size = 24 }: { color?: string; size?: number }) {
return (
<svg xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 24 24" fill="none">
<path d="M12 12C14.7614 12 17 9.76142 17 7C17 4.23858 14.7614 2 12 2C9.23858 2 7 4.23858 7 7C7 9.76142 9.23858 12 12 12Z" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M3.41016 22C3.41016 18.13 7.26015 15 12.0002 15C12.9602 15 13.8902 15.13 14.7602 15.37" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M22 18C22 18.75 21.79 19.46 21.42 20.06C21.21 20.42 20.94 20.74 20.63 21C19.93 21.63 19.01 22 18 22C16.54 22 15.27 21.22 14.58 20.06C14.21 19.46 14 18.75 14 18C14 16.74 14.58 15.61 15.5 14.88C16.19 14.33 17.06 14 18 14C20.21 14 22 15.79 22 18Z" stroke={color} strokeWidth="1.5" strokeMiterlimit="10" strokeLinecap="round" strokeLinejoin="round" />
<path d="M16.4395 18L17.4294 18.99L19.5594 17.02" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
);
}
/** آیکون «قیمت» کنار سرویس/کالا — tauri FilesServiceAddCard, verbatim. */
export function FilesServiceAddCard({ color = '#616161' }: { color?: string }) {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<path d="M1.66699 7.0835H11.2503" stroke={color} strokeWidth="1.5" strokeMiterlimit="10" strokeLinecap="round" strokeLinejoin="round" />
<path d="M5 13.75H6.66667" stroke={color} strokeWidth="1.5" strokeMiterlimit="10" strokeLinecap="round" strokeLinejoin="round" />
<path d="M8.75 13.75H12.0833" stroke={color} strokeWidth="1.5" strokeMiterlimit="10" strokeLinecap="round" strokeLinejoin="round" />
<path d="M18.3337 10.0248V13.4248C18.3337 16.3498 17.592 17.0832 14.6337 17.0832H5.36699C2.40866 17.0832 1.66699 16.3498 1.66699 13.4248V6.57484C1.66699 3.64984 2.40866 2.9165 5.36699 2.9165H11.2503" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M13.75 5.2085H18.3333" stroke={color} strokeWidth="1.5" strokeLinecap="round" />
<path d="M16.042 7.49984V2.9165" stroke={color} strokeWidth="1.5" strokeLinecap="round" />
</svg>
);
}