feat: add RichTextEditor component for rich text editing in articles

feat: create SanitizeBlogBodiesCommand to clean existing blog bodies according to current HTML sanitization policies

test: add AppointmentTreatmentSessionLinkTest to ensure appointment booking functionality works correctly with treatment session links
This commit is contained in:
hamed
2026-08-08 11:40:17 +03:30
parent 934405c42d
commit 47323daa27
36 changed files with 3461 additions and 12686 deletions
@@ -29,6 +29,8 @@ use Symfony\Component\Security\Http\Attribute\IsGranted;
#[OA\Tag(name: 'Appointments')]
class AppointmentController extends BaseController
{
use \App\Shared\Controller\PermissionGateTrait;
public function __construct(
private readonly AppointmentRepository $appointmentRepo,
private readonly DoctorRepository $doctorRepo,
@@ -1042,6 +1044,11 @@ class AppointmentController extends BaseController
#[Route('/api/v1/appointment/{uuid}/status', methods: ['PATCH'])]
public function updateStatus(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
{
// پیش‌چکِ منشی، پیش از واکشیِ رکورد. چکِ اصلی به خودِ شیء نیاز دارد و بالا
// نمی‌رود؛ سهمِ منشی از آن اما همیشه همین توگل است، پس این خط هیچ مسیرِ
// مجازی نمی‌بندد و فقط ۴۰۴ را به ۴۰۳ تبدیل می‌کند. یافتهٔ ۱۰ آدیت ۲۰۲۶-۰۸-۰۷.
$this->denySecretaryWithout($user, 'appointments', 'update_status');
$appointment = $this->appointmentRepo->findByUuid($uuid);
if ($appointment === null) {
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'نوبت یافت نشد', 404);
@@ -1131,6 +1138,11 @@ class AppointmentController extends BaseController
#[IsGranted('IS_AUTHENTICATED_FULLY')]
public function confirm(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
{
// پیش‌چکِ منشی، پیش از واکشیِ رکورد. چکِ اصلی به خودِ شیء نیاز دارد و بالا
// نمی‌رود؛ سهمِ منشی از آن اما همیشه همین توگل است، پس این خط هیچ مسیرِ
// مجازی نمی‌بندد و فقط ۴۰۴ را به ۴۰۳ تبدیل می‌کند. یافتهٔ ۱۰ آدیت ۲۰۲۶-۰۸-۰۷.
$this->denySecretaryWithout($user, 'appointments', 'update_status');
$appointment = $this->appointmentRepo->findByUuid($uuid);
if ($appointment === null) {
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'نوبت یافت نشد', 404);
@@ -1214,6 +1226,11 @@ class AppointmentController extends BaseController
#[IsGranted('IS_AUTHENTICATED_FULLY')]
public function update(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
{
// پیش‌چکِ منشی، پیش از واکشیِ رکورد. چکِ اصلی به خودِ شیء نیاز دارد و بالا
// نمی‌رود؛ سهمِ منشی از آن اما همیشه همین توگل است، پس این خط هیچ مسیرِ
// مجازی نمی‌بندد و فقط ۴۰۴ را به ۴۰۳ تبدیل می‌کند. یافتهٔ ۱۰ آدیت ۲۰۲۶-۰۸-۰۷.
$this->denySecretaryWithout($user, 'appointments', 'update_status');
$appointment = $this->appointmentRepo->findByUuid($uuid);
if ($appointment === null) {
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'نوبت یافت نشد', 404);
@@ -1396,6 +1413,11 @@ class AppointmentController extends BaseController
#[IsGranted('IS_AUTHENTICATED_FULLY')]
public function serviceReschedule(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
{
// پیش‌چکِ منشی، پیش از واکشیِ رکورد. چکِ اصلی به خودِ شیء نیاز دارد و بالا
// نمی‌رود؛ سهمِ منشی از آن اما همیشه همین توگل است، پس این خط هیچ مسیرِ
// مجازی نمی‌بندد و فقط ۴۰۴ را به ۴۰۳ تبدیل می‌کند. یافتهٔ ۱۰ آدیت ۲۰۲۶-۰۸-۰۷.
$this->denySecretaryWithout($user, 'appointments', 'update_status');
$appointment = $this->appointmentRepo->findByUuid($uuid);
if ($appointment === null) {
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'نوبت یافت نشد', 404);