frontend phase 4: catalog browse (Home A5) & nurse service builder (B7)
Light up the two faces of the configurable service catalog over a new cached services/catalog domain (consumes the b5 contract; unlocks f6 search). - services/catalog: types/keys/constants/apis(client+mock+seam)/hooks/index + names.ts. Categories & option groups are session-cached reference data (Infinity staleTime, like geography); variant mutations invalidate myVariantsLists() and setQueryData the edited row. Mock-primary (USE_CATALOG_MOCK), one-line swap; mock reproduces the 400 missing-required and (nurse,category,option-set) 409 duplicate rules. - Customer Home (A5): greeting+avatar, search bar (navigates toward f6), data-driven category grid (loading/empty/error), patient nudge from the cached f2 query. Deferred /search placeholder stub. - Nurse Services & prices (B7) at /nurse/services: offerings list (active vs deactivated, edit, soft-deactivate w/ confirm, reactivate, no delete) and a 3-step variant builder (category -> required/optional options -> price+unit+ duration). Required-group gate; Toman->IRR digit-string at the field boundary (no float); live unit-aware estimated total (never from price alone); editable auto display_name; inline 409 duplicate warning; locked category edit form. - Shared, tested components: CategoryTile, PriceDisplay, VariantCard. Money util: tomanToRial + multiplyIrr (integer-safe) + tests. - i18n: catalog/services/search namespaces + home additions + nav.services (both locales, in sync). Icons, routes (SEARCH, NURSE_SERVICES), nurse nav. Gate: npm run check green; npm run test:ci green (147 tests, +18 across 4 suites); npm run build green with NEXT_PUBLIC_API_URL set. Docs: client/CLAUDE.md (Project Structure, caching note, namespaces), STATUS, for-backend REQ-010 (pagination param casing), phase report, mocks registry. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+82
-2
@@ -7,6 +7,7 @@
|
||||
"profile": "Profile",
|
||||
"bank": "Bank account",
|
||||
"coverage": "Coverage",
|
||||
"services": "Services",
|
||||
"dashboard": "Dashboard",
|
||||
"verification": "Verification",
|
||||
"visits": "Visits",
|
||||
@@ -43,8 +44,14 @@
|
||||
"placeholder_body": "This area will be built in a later phase."
|
||||
},
|
||||
"home": {
|
||||
"greeting": "Welcome to Balinyaar",
|
||||
"greeting_named": "Hi, {name}",
|
||||
"greeting_plain": "Hi there",
|
||||
"subtitle": "Manage care for the people you love.",
|
||||
"search_placeholder": "Search a service or nurse…",
|
||||
"search_action": "Search",
|
||||
"categories_title": "Services",
|
||||
"categories_error": "Couldn't load services.",
|
||||
"categories_empty": "No services have been set up yet.",
|
||||
"nudge_patient_title": "Complete the patient record",
|
||||
"nudge_patient_body": "Add conditions, medications and routine so nurses arrive prepared.",
|
||||
"nudge_patient_cta": "Open patients",
|
||||
@@ -134,7 +141,7 @@
|
||||
"unverified_title": "Your profile isn't active yet",
|
||||
"unverified_body": "Complete identity verification to appear to families and receive bookings.",
|
||||
"unverified_cta": "Complete verification",
|
||||
"deferred_services": "You'll add your services & prices and available days in a later step."
|
||||
"deferred_services": "Manage your services & prices in the Services section; available days come in a later step."
|
||||
},
|
||||
"bank": {
|
||||
"title": "Bank account",
|
||||
@@ -235,6 +242,79 @@
|
||||
"remove_confirm": "Remove",
|
||||
"removed": "Coverage area removed"
|
||||
},
|
||||
"catalog": {
|
||||
"unit_per_hour": "hourly",
|
||||
"unit_per_session": "per session",
|
||||
"unit_per_half_day": "per half-day",
|
||||
"unit_per_day": "daily",
|
||||
"unit_per_24h": "24-hour (live-in)",
|
||||
"count_per_hour": "hours",
|
||||
"count_per_session": "sessions",
|
||||
"count_per_half_day": "half-days",
|
||||
"count_per_day": "days",
|
||||
"count_per_24h": "days (24h)",
|
||||
"estimate_label": "Estimated total",
|
||||
"estimate_for": "for {count} {unit}"
|
||||
},
|
||||
"services": {
|
||||
"title": "Services & prices",
|
||||
"subtitle": "The services you offer and what each costs.",
|
||||
"add": "Add service",
|
||||
"empty_title": "No services yet",
|
||||
"empty_body": "Add at least one active, priced service so families can find and book you.",
|
||||
"active_chip": "Active",
|
||||
"inactive_chip": "Deactivated",
|
||||
"inactive_hint": "This service is deactivated and can't be booked.",
|
||||
"edit": "Edit",
|
||||
"deactivate": "Deactivate",
|
||||
"activate": "Reactivate",
|
||||
"deactivate_title": "Deactivate this service?",
|
||||
"deactivate_body": "It becomes unbookable and drops out of search. You can reactivate it anytime — it is never deleted.",
|
||||
"deactivate_confirm": "Deactivate",
|
||||
"deactivated_toast": "Service deactivated",
|
||||
"activated_toast": "Service reactivated",
|
||||
"toggle_error": "That couldn't be done. Please try again.",
|
||||
"builder_add_title": "Add a new service",
|
||||
"builder_edit_title": "Edit service",
|
||||
"step_category": "Category",
|
||||
"step_options": "Options",
|
||||
"step_price": "Price",
|
||||
"category_title": "Choose the service category",
|
||||
"category_subtitle": "Every service belongs to one category.",
|
||||
"categories_error": "Couldn't load categories. Please try again.",
|
||||
"category_locked": "The category can't be changed after a service is created.",
|
||||
"options_title": "Set the service options",
|
||||
"options_subtitle": "Pick one option for each required item.",
|
||||
"options_none": "This category has no options to configure.",
|
||||
"required_badge": "Required",
|
||||
"optional_badge": "Optional",
|
||||
"options_incomplete": "Answer every required item to continue.",
|
||||
"price_title": "Set the price and unit",
|
||||
"price_label": "Price (Toman)",
|
||||
"price_hint": "Enter the price in Toman.",
|
||||
"price_required": "Enter a valid price greater than zero.",
|
||||
"unit_label": "Price unit",
|
||||
"duration_label": "Duration / count",
|
||||
"duration_hint": "Optional — add a duration to preview the estimated total.",
|
||||
"rate_note": "This is the base rate per unit, not the full total.",
|
||||
"display_name_label": "Display name",
|
||||
"display_name_hint": "Auto-generated from your options — you can edit it.",
|
||||
"duplicate_warning": "You already have a service with these exact details. Change an option or the category.",
|
||||
"summary_options": "Options",
|
||||
"summary_none": "No options",
|
||||
"next": "Next",
|
||||
"submit_create": "Add service",
|
||||
"submit_save": "Save changes",
|
||||
"create_error": "The service couldn't be saved. Check your inputs and try again.",
|
||||
"created_toast": "Service added",
|
||||
"saved_toast": "Changes saved"
|
||||
},
|
||||
"search": {
|
||||
"title": "Search",
|
||||
"deferred": "Search and results arrive in the next phase.",
|
||||
"query_echo": "You searched for “{query}”.",
|
||||
"category_echo": "Filtered to the selected service category."
|
||||
},
|
||||
"auth": {
|
||||
"customer_title": "Sign in to Balinyaar",
|
||||
"customer_subtitle": "Sign in with your mobile number",
|
||||
|
||||
+82
-2
@@ -7,6 +7,7 @@
|
||||
"profile": "پروفایل",
|
||||
"bank": "حساب بانکی",
|
||||
"coverage": "پوشش",
|
||||
"services": "خدمات",
|
||||
"dashboard": "داشبورد",
|
||||
"verification": "احراز هویت",
|
||||
"visits": "ویزیتها",
|
||||
@@ -43,8 +44,14 @@
|
||||
"placeholder_body": "این بخش در فازهای بعدی تکمیل میشود."
|
||||
},
|
||||
"home": {
|
||||
"greeting": "به بلینیار خوش آمدید",
|
||||
"greeting_named": "سلام، {name}",
|
||||
"greeting_plain": "سلام",
|
||||
"subtitle": "مراقبت از عزیزانتان را مدیریت کنید.",
|
||||
"search_placeholder": "جستجوی خدمت یا پرستار…",
|
||||
"search_action": "جستجو",
|
||||
"categories_title": "خدمات",
|
||||
"categories_error": "خدمات بارگذاری نشد.",
|
||||
"categories_empty": "هنوز خدمتی تعریف نشده است.",
|
||||
"nudge_patient_title": "تکمیل پروندهٔ بیمار",
|
||||
"nudge_patient_body": "وضعیتها، داروها و روتین را اضافه کنید تا پرستار آماده حاضر شود.",
|
||||
"nudge_patient_cta": "مشاهدهٔ بیماران",
|
||||
@@ -134,7 +141,7 @@
|
||||
"unverified_title": "پروفایل شما هنوز فعال نیست",
|
||||
"unverified_body": "برای نمایش به خانوادهها و دریافت رزرو، احراز هویت را تکمیل کنید.",
|
||||
"unverified_cta": "تکمیل احراز هویت",
|
||||
"deferred_services": "خدمات و قیمتها و روزهای کاری را در مرحله بعد اضافه میکنید."
|
||||
"deferred_services": "خدمات و قیمتهای خود را در بخش «خدمات و قیمتها» مدیریت کنید؛ روزهای کاری در مرحله بعد اضافه میشود."
|
||||
},
|
||||
"bank": {
|
||||
"title": "حساب بانکی",
|
||||
@@ -235,6 +242,79 @@
|
||||
"remove_confirm": "حذف",
|
||||
"removed": "منطقهٔ تحت پوشش حذف شد"
|
||||
},
|
||||
"catalog": {
|
||||
"unit_per_hour": "ساعتی",
|
||||
"unit_per_session": "هر جلسه",
|
||||
"unit_per_half_day": "نیمروزی",
|
||||
"unit_per_day": "روزانه",
|
||||
"unit_per_24h": "شبانهروزی",
|
||||
"count_per_hour": "ساعت",
|
||||
"count_per_session": "جلسه",
|
||||
"count_per_half_day": "نیمروز",
|
||||
"count_per_day": "روز",
|
||||
"count_per_24h": "شبانهروز",
|
||||
"estimate_label": "برآورد کل",
|
||||
"estimate_for": "برای {count} {unit}"
|
||||
},
|
||||
"services": {
|
||||
"title": "خدمات و قیمتها",
|
||||
"subtitle": "خدماتی که ارائه میدهید و قیمت هرکدام.",
|
||||
"add": "افزودن خدمت",
|
||||
"empty_title": "هنوز خدمتی اضافه نکردهاید",
|
||||
"empty_body": "برای دیدهشدن و دریافت رزرو، حداقل یک خدمت فعال و دارای قیمت اضافه کنید.",
|
||||
"active_chip": "فعال",
|
||||
"inactive_chip": "غیرفعال",
|
||||
"inactive_hint": "این خدمت غیرفعال است و قابل رزرو نیست.",
|
||||
"edit": "ویرایش",
|
||||
"deactivate": "غیرفعالسازی",
|
||||
"activate": "فعالسازی دوباره",
|
||||
"deactivate_title": "این خدمت غیرفعال شود؟",
|
||||
"deactivate_body": "این خدمت قابل رزرو نخواهد بود و از نتایج جستجو حذف میشود. هر زمان میتوانید دوباره فعالش کنید — هرگز حذف نمیشود.",
|
||||
"deactivate_confirm": "غیرفعال کن",
|
||||
"deactivated_toast": "خدمت غیرفعال شد",
|
||||
"activated_toast": "خدمت دوباره فعال شد",
|
||||
"toggle_error": "این عملیات انجام نشد. دوباره تلاش کنید.",
|
||||
"builder_add_title": "افزودن خدمت جدید",
|
||||
"builder_edit_title": "ویرایش خدمت",
|
||||
"step_category": "دسته",
|
||||
"step_options": "گزینهها",
|
||||
"step_price": "قیمت",
|
||||
"category_title": "دستهٔ خدمت را انتخاب کنید",
|
||||
"category_subtitle": "هر خدمت به یک دسته تعلق دارد.",
|
||||
"categories_error": "دستهها بارگذاری نشد. دوباره تلاش کنید.",
|
||||
"category_locked": "دسته پس از ایجاد خدمت قابل تغییر نیست.",
|
||||
"options_title": "گزینههای خدمت را مشخص کنید",
|
||||
"options_subtitle": "برای هر مورد الزامی یک گزینه انتخاب کنید.",
|
||||
"options_none": "این دسته گزینهای برای تنظیم ندارد.",
|
||||
"required_badge": "الزامی",
|
||||
"optional_badge": "اختیاری",
|
||||
"options_incomplete": "برای ادامه، همهٔ موارد الزامی را انتخاب کنید.",
|
||||
"price_title": "قیمت و واحد را تعیین کنید",
|
||||
"price_label": "قیمت (تومان)",
|
||||
"price_hint": "قیمت را به تومان وارد کنید.",
|
||||
"price_required": "قیمتی معتبر و بزرگتر از صفر وارد کنید.",
|
||||
"unit_label": "واحد قیمت",
|
||||
"duration_label": "مدت / تعداد",
|
||||
"duration_hint": "اختیاری — برای نمایش برآورد هزینهٔ کل، مدت را وارد کنید.",
|
||||
"rate_note": "این نرخ پایه به ازای هر واحد است، نه هزینهٔ کل.",
|
||||
"display_name_label": "نام نمایشی",
|
||||
"display_name_hint": "بهصورت خودکار از گزینهها ساخته میشود؛ میتوانید تغییرش دهید.",
|
||||
"duplicate_warning": "شما قبلاً خدمتی با همین مشخصات دارید. یک گزینه یا دسته را تغییر دهید.",
|
||||
"summary_options": "گزینهها",
|
||||
"summary_none": "بدون گزینه",
|
||||
"next": "بعدی",
|
||||
"submit_create": "ثبت خدمت",
|
||||
"submit_save": "ذخیره تغییرات",
|
||||
"create_error": "ثبت خدمت انجام نشد. ورودیها را بررسی کرده و دوباره تلاش کنید.",
|
||||
"created_toast": "خدمت اضافه شد",
|
||||
"saved_toast": "تغییرات ذخیره شد"
|
||||
},
|
||||
"search": {
|
||||
"title": "جستجو",
|
||||
"deferred": "جستجو و نتایج در فاز بعدی اضافه میشود.",
|
||||
"query_echo": "جستجوی شما: «{query}».",
|
||||
"category_echo": "محدود به دستهٔ خدمت انتخابشده."
|
||||
},
|
||||
"auth": {
|
||||
"customer_title": "ورود به بلینیار",
|
||||
"customer_subtitle": "با شماره موبایل خود وارد شوید",
|
||||
|
||||
Reference in New Issue
Block a user