Commit Graph
399 Commits
Author SHA1 Message Date
hamed 3c103bc51e feat: update SMS settings review functionality
- Changed the tab name from 'post-visit-review' to 'post-visit-approved' in SmsPage.
- Introduced a new query for fetching approved post-visit texts.
- Updated the UI to display approved post-visit texts and their details.
- Enhanced the API endpoint to filter SMS settings based on status (pending/approved).
- Added entity name resolution for doctors and clinics in the SMS settings.
- Updated the SmsWalletPage to include new post-visit text variables for SMS templates.
- Improved type definitions in index.ts for better clarity and consistency.
2026-06-22 10:30:46 +03:30
hamed 8ed119e12a fix: update active doctor appointment logic to use online_booking_enabled field 2026-06-22 09:47:06 +03:30
hamed 447ffc8862 fix: adjust active doctor appointment filter to default to true unless specified 2026-06-21 18:02:03 +03:30
hamed 21b6c583c0 feat: add tag filtering to blog posts and implement JSON_CONTAINS DQL function 2026-06-21 17:34:07 +03:30
hamed 91cfb64217 fix: improve doctor search performance by optimizing query filters in findWithFilters method 2026-06-21 16:41:59 +03:30
hamed 8b9130011a feat: add export functionality for JSON data in CategoriesPage and update TabActions component 2026-06-21 15:37:24 +03:30
hamed 1f9b69c121 fix: correct refresh token handling in TokenService to return submitted token 2026-06-21 13:41:53 +03:30
hamed 57965f184d feat: add social media fields to Clinic entity and update API documentation 2026-06-21 13:26:47 +03:30
hamed 3f1b2de971 feat: add social media links field to Doctor entity and update API documentation 2026-06-21 12:35:22 +03:30
hamed e2636ce743 feat: implement short-lived grant system for OTP verification and enhance rate limiting across authentication endpoints 2026-06-20 12:51:10 +03:30
hamed f9678026a8 feat: implement server-side validation for mobile numbers and national codes across multiple endpoints 2026-06-20 12:28:36 +03:30
hamed b686cd1e7d feat: implement template editing functionality and revert approved templates to draft status 2026-06-19 20:48:44 +03:30
hamed fa332f7fa1 feat: Add tagging system for SMS logs and templates
- Introduced a `tag` field in the `SmsLog` entity to categorize SMS messages.
- Updated the `SmsService` to handle the new `tag` parameter during SMS dispatch.
- Implemented a `SmsTextResolver` service to resolve SMS message templates based on tags.
- Created a new `SmsMessageTemplate` entity for editable SMS templates with placeholders.
- Added endpoints for managing SMS message templates in the admin panel.
- Enhanced existing SMS dispatching methods across various controllers to utilize the tagging system.
- Migrated the database to include the new `tag` field and created a seeding command for default SMS templates.
- Updated admin API to filter SMS logs by tag and include tag information in responses.
2026-06-19 20:42:04 +03:30
hamed d910a30a7e feat: fix access scoping for clinic member doctors and add representation_id handling for clinics 2026-06-19 13:42:24 +03:30
hamed fe73fa1a05 feat: add ROLE_REPRESENTATION access to admin panel for managing doctors and clinics
- Updated authStore to include 'representation' role.
- Modified DoctorFormPage and DoctorsPage to handle different endpoints based on user role.
- Created new RepresentationActionController for handling doctor and clinic creation by representatives.
- Added new API endpoints for representatives to manage doctors, clinics, and view appointments.
- Updated documentation to reflect new role and API changes.
2026-06-19 13:20:40 +03:30
hamed a8d36d7455 feat(user-profile): add avatar upload functionality and update UserProfile entity 2026-06-19 10:19:47 +03:30
hamed e7be54b84e feat(clinic): enforce maximum gallery image limit of 5 and update validation logic 2026-06-19 01:15:27 +03:30
hamed dd1ca98cb4 feat(clinic): implement doctor detachment functionality with API endpoint and confirmation dialog 2026-06-19 01:10:06 +03:30
hamed ccfb3391f2 Refactor code structure for improved readability and maintainability 2026-06-19 00:54:34 +03:30
hamed 2943e29663 feat(representation): update representation deactivation logic and enhance API documentation 2026-06-19 00:46:34 +03:30
hamed 0f3bf68fdd feat(blog): refactor author data retrieval to handle potential missing authors 2026-06-19 00:35:07 +03:30
hamed 1e45b12aec feat(doctor): add filtering logic for clinic doctor list with pagination and sorting 2026-06-18 19:16:04 +03:30
hamed 89a8622e6a feat(clinic): enhance clinic detail response with location map and additional fields 2026-06-18 15:21:24 +03:30
hamed 4bb65c7502 feat(clinic): enhance clinic listing with additional details and address information
- Updated the clinic API response to include new fields: title, phone, logo, images_clinic, doctors_count, city, state, 24_7, and field_working_days.
- Modified the ClinicController to fetch and include city and state information based on the clinic's address.
- Refactored the toListArray method in the Clinic entity to accept city and state parameters.
- Added a new method in the ClinicRepository to retrieve city and province names for each clinic based on their address.
2026-06-18 15:03:36 +03:30
hamed fa41c15bb6 feat(clinic): update filtering logic to use clinic address for city/state 2026-06-18 09:06:36 +03:30
hamedandClaude Opus 4.8 404dd03247 feat(specialty): public doctor-counts endpoint (per city)
Add GET /api/v1/specialties/doctor-counts?city_id= returning every active
specialty with number_of_doctors (distinct doctors via doctor_specialties,
scoped by doctor_cities when city_id is given). Make /api/v1/specialties GET
public. Powers the /specialties page count. Docs updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 22:27:27 +03:30
hamedandClaude Opus 4.8 28011160a2 feat(appointment): include doctor specialties and clinic address in toArray
The appointment detail view needs the doctor's specialty and the clinic
address/phone/map, which toArray didn't return. Add doctor.specialties[] and
a top-level `address` (the doctor's first address via DoctorAddress::toArray —
address, telephone, map). Additive only; existing keys unchanged. Docs updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 12:01:32 +03:30
hamedandClaude Opus 4.8 b60dbdadba fix(profile): persist & expose birthday as a single Unix field
Birthday was never saved (hydrate forced null) and not returned in a form the
clients use. Now:
- hydrate stores the incoming `birthday` (Unix) into date_of_birth; `birthday`
  takes priority over a stray `date_of_birth: null` in the same payload so it
  can't be wiped.
- toArray exposes a single `birthday` (Unix) key — drop the duplicate
  `date_of_birth` output to avoid overlap.
- admin UserDetailPage reads profile.birthday (formatDate renders Jalali).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 11:42:31 +03:30
hamedandClaude Opus 4.8 800f9ad8cd fix(profile): sync User.real_name from profile name on create/update
When a user completes their profile (POST/PATCH /api/v1/user-profile), the
name was only stored on the profile (label/family) and User.real_name stayed
empty — so users who registered via booking showed with no name in the admin
user list. hydrate() now mirrors the profile's full name (label + family)
onto the owning User.realName, persisted in the same flush.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 10:45:29 +03:30
hamedandClaude Opus 4.8 492a7df989 feat(payment): canceled status, manageable origin allowlist, CORS subdomains
Unify and harden the payment flow (same API for the main site and all
consumer sites; per-client difference is only frontend_address).

- Payment gains STATUS_CANCELED. Gateways distinguish user-cancel from
  failure (Mellat ResCode=17, SEP CanceledByUser, mock cancel=1) via a new
  PaymentVerifyResult::canceled flag; callback sets canceled vs failed and
  skips the circuit-breaker on cancel.
- Expiry job now cancels the pending payment when a booking lapses
  (AppointmentExpiryService + PaymentRepository::findPendingByAppointment).
- frontend_address allowlist is read from the payment_allowed_frontend_hosts
  site setting (manageable via PATCH /api/v1/admin/settings), falling back to
  the ALLOWED_FRONTEND_HOSTS env var — so a new consumer site needs no code
  change.
- .env: broaden CORS_ALLOW_ORIGIN to city subdomains (*.localhost /
  *.clinic-pro.ddev.site) and add yazd-nobat.localhost to ALLOWED_FRONTEND_HOSTS.
- Update docs/api/payment.md and docs/api/admin.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 10:06:43 +03:30
hamedandClaude Opus 4.8 45242a3128 feat(rating): multi-dimensional ratings, rich comments, eligibility guard
Rebuild the doctor rating/review system to power the public site's rich
review UI, and restrict who may submit.

Ratings:
- Rate entity holds five 0–100 dimensions (waiting time, diagnosis
  accuracy, behaviour, cleanliness, expertise) instead of a single score.
- GET /rate/{uuid} returns aggregate {point, satisfaction, averages[]}.
- POST /rate upserts all five dimensions and returns the new aggregate.

Comments:
- Comment gains parent/replies (threaded) and a rich toArray with author,
  like_status (like/dislike counts + current user's vote) and nested
  approved replies. POST /comment accepts {comment, parent}.
- Likes are directional (value 1=like, -1=dislike) with toggle/replace;
  POST /like/{uuid} returns like_count/dislike_count/current_user_like.

Eligibility:
- Only a user with a confirmed appointment in the last 30 days may rate or
  comment (AppointmentRepository::hasRecentConfirmed); otherwise
  403 ERR_RATING_NOT_ELIGIBLE. New GET /rate/{uuid}/eligibility for the UI.
- security.yaml: narrow the public rate pattern so /eligibility stays auth'd.

Also updates admin rates listing to the new dimensions and the rating/admin
API docs. Includes migration for the new columns.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 00:25:59 +03:30
hamedandClaude Opus 4.8 cc182e48f9 refactor(scheduler): use the recipe's default stateful schedule
Consolidate onto the symfony/scheduler recipe's App\Schedule (stateful +
processOnlyLastMissedRun, so missed runs after downtime still execute)
instead of a separate provider. Add the every-1-minute
ExpireAppointmentsMessage there, point scheduler_default at
schedule://default, and drop the redundant ExpireAppointmentsSchedule.
debug:scheduler shows the trigger registered.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 22:47:38 +03:30
hamedandClaude Opus 4.8 71ab8892c9 feat(appointment): auto-expire unpaid bookings via Symfony Scheduler
Run the 15-min payment-expiry every minute through Symfony Scheduler so
unpaid pending bookings flip to expired without a system crontab. Install
symfony/scheduler; extract the expiry logic into AppointmentExpiryService
(reused by the existing command); add ExpireAppointmentsMessage + handler
and an #[AsSchedule] provider (RecurringMessage::every 1 minute); wire a
scheduler_default transport in messenger.yaml. Slots already free
just-in-time via isSlotTaken, so this only syncs the DB status.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 22:43:50 +03:30
hamedandClaude Opus 4.8 94d9f88cb2 fix(user-profile): don't lazy-create when an admin views another user
resolveProfile created an empty profile for any caller with create-intent,
including an admin merely viewing someone else's profile. Restrict
lazy-create to the user's own profile; an admin reading another user's
missing profile now gets 404 with no side-effect record. Doc updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 22:04:19 +03:30
hamedandClaude Opus 4.8 038cb73ea7 feat(payment): add GET /api/v1/my/payments for the logged-in user
Add PaymentRepository::findByUser/countByUser and a paginated
my/payments endpoint that returns the authenticated user's own payments
(derived from the token, never a userId in the URL). Public dashboard's
transactions tab can now list payments instead of hitting a nonexistent
route.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 19:21:31 +03:30
hamedandClaude Opus 4.8 da57ac9c5b fix(user-profile): resolve profile by user uuid and auto-create when missing
GET/PATCH /api/v1/user-profile/{uuid} treated {uuid} as the profile's own
uuid, but clients pass the user's uuid — and a freshly OTP-registered user
has no profile row, so the call always 404'd. Add resolveProfile(): try
profile uuid, then user uuid → that user's profile, and (for the current
user or an admin) lazy-create an empty profile so the client always gets
an editable one. Foreign/unknown uuids still 404 with no leak.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 19:01:27 +03:30
hamed daf2bd4b91 Merge branch 'feat/appointment-lock-patient'
# Conflicts:
#	src/Appointment/Controller/AppointmentController.php
2026-06-15 18:29:25 +03:30
hamed 6e7101f113 Merge branch 'fix/past-slots-unavailable'
# Conflicts:
#	docs/api/appointment.md
2026-06-15 18:28:06 +03:30
hamedandClaude Opus 4.8 a52f412cfc feat(payment): confirm appointment and SMS the patient on success
The payment callback handled subscription and sms-wallet types but left
appointment payments pending. Add a TYPE_APPOINTMENT branch that
transitions the booking pending → confirmed (guarded by canTransitionTo,
so an already-expired booking is skipped) and dispatches a confirmation
SMS to the patient's mobile.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:00:13 +03:30
hamedandClaude Opus 4.8 089badbb5a feat(appointment): expire pending bookings past their payment window
Add findPaymentExpired (pending with expires_at < now) and have the
cancel-expired command flip both payment-expired and slot-time-passed
pendings to expired (deduped). Run it on a schedule (e.g. every minute:
* * * * * php bin/console app:cancel-expired-appointments) to free locks
held by unpaid bookings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 17:58:28 +03:30
hamedandClaude Opus 4.8 aa2e46dfdb feat(appointment): book with patient data, TTL lock, atomic conflict
book() now accepts for_self plus patient_* fields: for_self fills the
patient from the paying user's profile, otherwise patient_name/mobile are
required (422 if missing) and the rest are stored. Every booking starts
pending with a 15-minute expires_at. Persisting goes through
bookAtomically (re-check inside a transaction) so two concurrent requests
for the same slot can't both win — the loser gets 409.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 17:56:56 +03:30
hamedandClaude Opus 4.8 14ac01d597 feat(appointment): treat expired pending bookings as free in isSlotTaken
A slot is taken only if confirmed, or pending with a still-valid lock
(expires_at null or in the future). An expired pending booking no longer
blocks the slot even before the cron flips it to expired.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 17:54:18 +03:30
hamedandClaude Opus 4.8 bff7001f49 feat(appointment): add expires_at and patient fields to Appointment
Add a 15-minute payment TTL (expires_at) plus patient_name/mobile/
national_code/gender/reason columns so a booking can hold a slot
temporarily and record a patient distinct from the paying user. New
markPendingWithTtl() sets the lock; transitioning out of pending clears
expires_at. All columns nullable (migration added).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 17:53:27 +03:30
hamedandClaude Opus 4.8 4216e67d68 fix(appointment): mark past-time slots as unavailable
is_available only checked whether a slot was booked, so on today's date
slots whose start time had already passed (e.g. 10:00 when it is 12:00)
still showed as bookable until the POST /appointment 422. Treat a slot
with start < now as unavailable in getAllSlotsWithAvailability, and drop
past slots in filterBookedSlots so getAvailableSlots agrees. Future days
are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 16:48:50 +03:30
hamedandClaude Opus 4.8 f443beb615 feat(appointment): public month-availability endpoint
Add GET /api/v1/appointment-settings/month-availability/{doctorUuid}
?year=&month= (Gregorian) returning disabled_dates / enabled_dates for
the month plus the doctor's online_booking flag and window. Lives in
AppointmentController (per-method guards) so it is genuinely public —
the class-level IsGranted on AppointmentSettingsController would have
forced auth. Whitelisted in security.yaml (firewall + access_control).
Uses SlotCalculator::hasAnyAvailability per day, so holidays, closed
overrides, non-working days and out-of-window dates all come back
disabled.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 16:23:12 +03:30
hamedandClaude Opus 4.8 f82d92b3ed feat(appointment): enforce booking window in slot calculation
buildAllSessions now rejects dates that are in the past, beyond the
doctor's booking window (today + value week|month), or when online
booking is disabled — gating overrides and weekly schedule alike. Add a
public hasAnyAvailability() for the month-availability endpoint. Window
config is read from the schedule meta, falling back to defaults.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 16:18:00 +03:30
hamedandClaude Opus 4.8 09aba878f8 feat(appointment): store online-booking window in WeeklySchedule meta
Add a reserved meta key inside WeeklySchedule.setting (no migration) holding
online_booking_enabled and a booking_window value+unit (week|month), with
sane defaults. Expose meta separately in toArray() and keep it out of the
day schedule map. setSetting now preserves meta when the day schedule is
replaced; the weekly-schedule POST/PATCH endpoints accept an optional meta.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 16:16:30 +03:30
hamed c662eb9b4f feat(doctor): update schedule fields in doctor API responses to reflect actual availability 2026-06-15 15:11:29 +03:30
hamed ec36eefeb8 feat: add OpenAPI tags to various controllers for improved API documentation 2026-06-15 14:54:10 +03:30
hamed df7a784701 feat: implement realistic data seeding for doctors, clinics, and secretaries
- Added seed_realistic_data.php to clean existing data and populate the database with realistic entries for doctors, clinics, and secretaries.
- Created a structured approach to generate 100 doctors per city with diverse specialties and services.
- Implemented database cleanup routines to ensure a fresh start for data seeding.
- Enhanced the DoctorSecretaryRepository with improved comments for clarity.
2026-06-15 14:18:25 +03:30