Commit Graph
36 Commits
Author SHA1 Message Date
hamed 1a8b3bfa53 feat(representation): expose doctor representation details and add counts in admin views 2026-07-12 13:00:49 +03:30
hamed 476d5bb80e Refactor code structure for improved readability and maintainability 2026-07-12 09:54:44 +03:30
hamed 23590370b2 feat(doctor): prevent duplicate doctor imports based on medical system code 2026-07-11 19:56:58 +03:30
hamed 6c67d522ba feat(doctor): ensure default office address creation during doctor import 2026-07-11 19:21:24 +03:30
hamedandClaude Opus 4.8 2f0131171d feat(doctor): claim captcha+mobile, owner profile delete, admin map zoom fix
- DoctorClaimController: ALTCHA CaptchaGuard on /claim (dev no-op via
  ALTCHA_ENABLED=false); optional `mobile` field must match the logged-in
  user's number (422 ERR_CONFLICT_001 on mismatch)
- DoctorController::delete: now IS_AUTHENTICATED_FULLY — admin (any) or the
  owner of a claimed profile (IDOR-guarded); FK appointment guard kept
- DoctorDetailPage address map: MapController calls map.invalidateSize()
  before flyTo (fixes needing to pick a city twice on a freshly-mounted map);
  geocode retries once (nominatim empty/429 on first hit)
- tests: mobile mismatch, owner-delete allowed + others 403, unclaimed not
  deletable by random user
- docs: doctor-claim.md (mobile+captcha), doctor.md (delete permission)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 14:57:41 +03:30
hamedandClaude Opus 4.8 3dcd4f3b26 feat(doctor): imported IRIMC doctors are active by default
Per request, doctors created via import now set active_doctor_appointment
= true (was false). DoctorImportTest updated to assert active.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 14:20:42 +03:30
hamedandClaude Opus 4.8 51432c7bb9 fix(doctor): strip «دکتر» prefix on IRIMC import + name-fix & purge commands
Root cause of "دکتر دکتر …" (and ellipsis-truncated "…نی") in admin: IRIMC
names already contain the «دکتر» title, while the panel renders «دکتر {name}».
Convention is to store the bare name.

- DoctorImportService: normalize name via PersianText::stripDoctorTitle
  (also fixes ي/ی, ك/ک, half-space)
- PersianText::stripDoctorTitle now strips consecutive «دکتر دکتر …» prefixes
- app:doctors:fix-irimc-names: one-off backfill for existing source='irimc'
  rows (dry-run supported) — fixed 340 rows
- app:doctors:purge: FK-safe full wipe of doctors + all dependent tables +
  orphan surrogate users, for a clean test DB (dry-run default, --force to
  apply, prod-guarded)
- tests: PersianTextTest cases for the title stripping; DoctorImportTest
  asserts stored name has no «دکتر» prefix
- docs/api/doctor-import.md: name convention + the two new commands

Verified: import "دکتر صفورا حجازی نیا" → stored "صفورا حجازی نیا" → panel
shows single «دکتر صفورا حجازی نیا».

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 14:07:18 +03:30
hamedandClaude Opus 4.8 af125572c9 feat(doctor): complete IRIMC import feature — claim flow, least-privilege importer, unique import key
- Extract import logic from AdminApiController into DoctorImportService
  (thin DoctorImportController keeps the same route/contract)
- Surrogate users get marker role ROLE_UNCLAIMED_DOCTOR (+ backfill command
  app:doctors:backfill-surrogate-role) enabling safe deletion after claim
- DB-level UNIQUE (source, medical_system_code) + concurrent-import retry
- Doctor profile claim flow (climed.md): shahkar + PersonInfo identity checks
  via existing ApiIrService, Persian name normalization (PersianText),
  pessimistic-lock race protection, DoctorClaimRequest audit table
  (national code hashed, mobile masked), doctor_claim rate limiter,
  public claim-info endpoint, welcome SMS
- Admin support tools: manual transfer endpoint + paginated doctor-claims
  audit list + owner_status filter/fields in admin doctors list
- Least privilege: system owner now gets ROLE_IMPORTER (ROLE_ADMIN stripped),
  import endpoint accepts ADMIN|IMPORTER, isStaff includes IMPORTER
- Headless crawler login: X-Service-Token header bypasses captcha only
  (rate limit + password checks intact; empty env = no bypass)
- docs: doctor-claim.md (new), doctor-import.md, admin.md, doctor.md
- tests: DoctorImportTest (6), DoctorClaimTest (11), PersianTextTest (5)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 11:39:15 +03:30
hamed 9f56f4aa08 feat(migrations): add ownership fields to doctors table for IRIMC import
- Introduced new columns: owner_status, source, source_ref, managed_by, and claimed_at to the doctors table.
- Created indexes for owner_status and source to optimize queries related to unclaimed doctors.

feat(auth): implement SystemOwnerCommand for managing system-owner user

- Added command to create, activate, and deactivate a system-owner user for IRIMC crawler.
- Ensured the user has ROLE_ADMIN to access import endpoints.
- Handled password setting and user status management within the command.
2026-07-11 08:59:36 +03:30
hamed 0750bc9812 feat: add multi-city representation support and domain context resolution
- Created migration to add representation_cities table and domain, is_global fields to representations.
- Implemented SiteContextController to resolve domain to site context (city | representation | unknown).
- Developed DomainContext and DomainContextResolver services for domain mapping.
- Added tests for DomainContextResolver and commission logic based on domain ownership.
2026-07-09 07:26:58 +03:30
hamed 0a1e03d0ce feat(doctors): add activity start date field with Persian calendar in admin panel 2026-07-02 11:35:59 +03:30
hamed 4565f521c4 fix(doctors): correct city/state filters in doctor listing API 2026-07-02 11:13:16 +03:30
hamed cf38faff88 feat: add SmokeEndpointsCommand for endpoint health checks and logging
- Introduced SmokeEndpointsCommand to send HTTP requests to all /api and /oauth endpoints on the live site, ensuring logs are generated in the app_log table.
- Implemented non-destructive testing by using real IDs for GET requests and non-existent IDs for write operations.
- Added functionality to authenticate as an admin using a fixed OTP for development.
- Created tests for endpoint sweeps to verify that no 5xx errors occur during various conditions, including no-auth and wrong-role scenarios.
- Established RouteManifest for public success paths that should return 200 without requiring fixtures.
2026-07-01 22:37:53 +03:30
hamedandClaude Opus 4.8 fe6383314e fix(security): enforce ownership on 4 IDOR read/bind endpoints (M2-M5)
- M2 GET /insurance/{id}: was unguarded; now owner-or-admin (403 otherwise) —
  stops reading another doctor's negotiated price by id enumeration.
- M3 GET /clinic-pro/doctor-address/{id}: add the same owner/admin check the
  sibling PATCH/DELETE already had.
- M4 POST/PATCH /service-item: staff_uuid must belong to the caller's tenant
  (entity_type/entity_id) → 422; stops binding another tenant's staff.
- M5 appointment-settings list endpoints (date-override/holidays/
  available-locations): add the per-doctor ownership check the sibling
  single-record endpoints already enforce.

Regressions (6 negative cases fail without the fixes):
DoctorInsuranceOwnershipTest, DoctorAddressOwnershipTest,
ServiceItemStaffOwnershipTest, AppointmentSettingsListOwnershipTest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 20:17:52 +03:30
hamedandClaude Opus 4.8 6b12f3ddb9 perf(doctor): fetch-join specialties in clinic doctor list (H6)
findByClinicWithFilters left-joined specialties only for filtering, so
toListArray() lazy-loaded them per doctor (N+1). addSelect them and switch the
result fetch to Paginator(fetchJoinCollection: true) so LIMIT still paginates by
doctor.

Test infra: ApiTestCase::countQueries() (via doctrine.debug_data_holder).
Regression: tests/Doctor/ClinicDoctorListNPlusOneTest asserts the query count
does not grow with doctor count (4→10 without the fix).

Also relaxed AppointmentExpiryServiceTest's exact-count assertion (it counts all
stale pendings in the shared db_test, which accumulates) — logged test-isolation
debt as E6.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 19:24:49 +03:30
hamedandClaude Opus 4.8 4cf6873900 fix(db): purge insurance config when a doctor/clinic is deleted (H5)
tenant_insurances, entity_insurance_pricing and tenant_service_coverages
reference their owner through a polymorphic (entity_type, entity_id) pair, so no
database FK can cascade their cleanup. Hard-deleting a doctor (DoctorController)
or clinic (AdminApiController) left these rows orphaned.

Add TenantInsuranceCleanupService::purgeForEntity() and call it from both delete
paths — removes coverage (via owning tenant_insurance ids), then tenant
insurances, then pricing.

Residual (separate, lower-freq paths): deleting an insurance category or a
service_item still orphans rows that reference them by id — tracked under the
medium-tier soft-ref findings.

Regression: tests/Insurance/TenantInsuranceCleanupTest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 19:16:10 +03:30
hamed 36b87e9817 fix(db): declare onDelete on required FKs that had none
Eight required (nullable:false) FKs had no referential action. Set per the
codebase's existing pattern: CASCADE for owned relations (doctor_insurances,
doctor_secretaries), RESTRICT for owner/reference FKs (Doctor/Clinic.user_id,
invited_by_id, subscription plan/period). Only the two CASCADE FKs need DDL —
RESTRICT is the MySQL default.
2026-06-28 17:04:23 +03:30
hamedandClaude Opus 4.8 05fde81320 fix(orm): declare repositoryClass on all entities with a custom repository
On prod (opcache.preload + prod container), getRepository(Entity::class)
returned Doctrine's default repository instead of the custom one when the
entity's #[ORM\Entity] had no repositoryClass — so custom finders like
DoctorSecretaryRepository::findAllActiveBySecretary threw BadMethodCallException,
making /oauth/userinfo return 500 after login. Declare repositoryClass explicitly
on all 25 affected entities.

Also add app:create-admin command (create/promote a ROLE_ADMIN user by mobile).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 16:19:07 +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 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 3f1b2de971 feat: add social media links field to Doctor entity and update API documentation 2026-06-21 12:35:22 +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 c662eb9b4f feat(doctor): update schedule fields in doctor API responses to reflect actual availability 2026-06-15 15:11:29 +03:30
hamed d05c070661 feat: enhance doctor address management by including clinic addresses and enforcing required fields 2026-06-13 18:30:06 +03:30
hamed c1c57df4ff feat: add clinic role support to doctor profile access and update API endpoint for clinic owners 2026-06-13 18:10:44 +03:30
hamed 0333b24071 feat: enhance DoctorAddress entity to support clinic addresses and types
- Added `clinic_id` and `type` fields to `DoctorAddress` entity to differentiate between personal and clinic addresses.
- Updated constructor to support creation of addresses for both doctors and clinics.
- Modified repository methods to handle new address types and added methods for counting and finding addresses by clinic.
- Implemented migration to update the database schema accordingly.
- Removed deprecated endpoint for creating addresses from clinics and updated related controller methods.
- Added new endpoints for managing clinic addresses, including CRUD operations.
- Updated frontend components to handle new address types and display accordingly.
2026-06-12 13:39:37 +03:30
hamed e7b90a6399 feat(api): add dashboard endpoints for clinic, doctor, and secretary roles
- Implemented GET /api/v1/dashboard/clinic to return clinic stats and today's schedule for clinic owners.
- Implemented GET /api/v1/dashboard/doctor to return doctor's stats and today's schedule for doctors.
- Implemented GET /api/v1/dashboard/secretary to return stats and conditional appointments for secretaries.

feat(migrations): create user_active_context and mobile_verification_otp tables

- Added migration to create user_active_context table for tracking active user sessions.
- Added migration to create mobile_verification_otp table for handling mobile number verification.

feat(migrations): create site_config table for application settings

- Added migration to create site_config table to store various site configuration settings.

feat(appointments): create MyAppointmentsController for user-specific appointments

- Added MyAppointmentsController to handle fetching user-specific appointments with pagination and filtering.

feat(auth): implement NotificationMobileController for mobile number verification

- Added NotificationMobileController to handle OTP requests and verification for mobile number changes.

feat(auth): create MobileVerificationOtp entity for OTP management

- Created MobileVerificationOtp entity to manage OTP records for mobile verification.

feat(auth): create UserActiveContext entity for user session management

- Created UserActiveContext entity to manage user active sessions.

feat(config): implement SiteConfigController for managing site settings

- Added SiteConfigController to handle fetching and updating site configuration settings.

feat(config): create SiteConfig entity and repository for configuration management

- Created SiteConfig entity and repository to manage site configuration data.
2026-06-11 12:20:12 +03:30
hamed af0ae51987 feat: add clinic doctor invitation feature
- Implemented ClinicInvitationController to handle doctor invitations.
- Created ClinicDoctorInvitation entity and repository for managing invitations.
- Added ClinicInvitationService for business logic related to invitations.
- Introduced endpoints for inviting, listing, resending, changing status, and deleting invitations.
- Updated security configuration to allow public access to invitation endpoints.
- Added migration for clinic_doctor_invitations table.
- Enhanced DoctorRepository with a method to find doctors by mobile number.
- Updated ClinicDetailPage to include invitation management UI.
2026-06-10 22:13:39 +03:30
hamed 25701c09b7 feat(admin): add user and doctor management APIs and frontend form
- Updated security configuration to include new API route for file uploads.
- Added new endpoints in AdminApiController for user statistics, toggling user status, updating user roles, and managing user details.
- Implemented doctor statistics and management endpoints, including toggling doctor status and creating new doctors.
- Enhanced user listing with filtering options for roles and status.
- Introduced DoctorFormPage component for adding new doctors with specialties selection.
- Integrated react-leaflet for mapping functionalities and added necessary dependencies.
- Updated package.json and package-lock.json to include new dependencies.
2026-06-10 17:02:42 +03:30
hamed de702d78bd feat(doctor-address): add city and province associations to DoctorAddress entity 2026-06-10 14:45:01 +03:30
hamed 5066fcbd91 feat: add insurance and location management
- Introduced InsuranceType enum for insurance categorization.
- Created InsuranceRepository for managing insurance entities.
- Developed LocationController for handling provinces and cities, including CRUD operations.
- Implemented City and Province entities with necessary fields and relationships.
- Added CityRepository and ProvinceRepository for database interactions.
- Established Specialty management with SpecialtyController, including CRUD operations.
- Created Specialty and Tag entities with appropriate fields and relationships.
- Implemented TagController for managing tags, including CRUD operations.
- Added TagRepository for database interactions with tags.
2026-06-10 14:22:26 +03:30
hamed e9075e8c92 Add comprehensive project documentation for ClinicPro in CLAUDE.md and README.md
- Introduced CLAUDE.md for internal guidance on project structure, commands, and architecture.
- Created README.md with detailed project overview, technology stack, directory structure, setup instructions, API endpoints, authentication flow, and external services.
2026-06-10 11:12:15 +03:30
hamed de1a78a235 feat: Implement SMS sending functionality with KavehNegar and Rangineh providers
- Add SendSmsMessage class for encapsulating SMS message data.
- Create KavehNegarProvider and RanginehProvider classes implementing SmsProviderInterface for sending SMS.
- Implement SmsLogRepository and SmsTemplateRepository for managing SMS logs and templates.
- Develop SendSmsHandler for handling SMS sending messages.
- Create SmsService to manage SMS dispatching and logging.
- Add UserProfileController for managing user profiles with CRUD operations.
- Implement UserProfile entity and repository for user profile data management.
- Update symfony.lock and bootstrap.php for project dependencies and environment setup.
2026-06-09 22:00:34 +03:30