feat(settings): complete remaining settings tabs (account, tags, turns)

Fill the three previously-placeholder settings sections so every menu item
is now a real page inside the settings shell:

- حساب کاربری: new authenticated POST /api/v1/user/change-password
  (verifies current password, ≥8 chars, must differ) + account page with a
  profile summary and change-password form.
- برچسب‌ها: new per-tenant TenantTag domain (entity/repo/controller +
  migration) with tenant-scoped CRUD at /api/v1/tenant-tag(s), plus a tags
  management page (list + color + add/edit/delete).
- مدیریت نوبت دهی: export the existing WeeklyScheduleTab from
  DoctorDetailPage and reuse it in a standalone AppointmentSettingsPage
  (current doctor's uuid + addresses).

Wire all three menu entries to their routes. Backend covered by PHPUnit
(change-password, tenant-tag CRUD + ownership); FE covered by Vitest.
API docs updated (auth.md, tag.md).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
hamed
2026-07-13 14:16:21 +03:30
co-authored by Claude Opus 4.8
parent 19d8560c9e
commit 76f9fbe88f
20 changed files with 1004 additions and 14 deletions
+29
View File
@@ -430,6 +430,35 @@ class AuthController extends BaseController
return $this->success(['message' => 'رمز عبور با موفقیت تغییر یافت']);
}
/**
* Change the password of the authenticated user. Requires the current
* password (verified against the stored hash); the new one must be ≥ 8
* chars and different from the current.
*/
#[Route('/api/v1/user/change-password', methods: ['POST'])]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
public function changePassword(Request $request, #[CurrentUser] User $user): JsonResponse
{
$data = json_decode($request->getContent(), true) ?? [];
$current = trim($data['current_password'] ?? '');
$new = trim($data['new_password'] ?? '');
if (mb_strlen($new) < 8) {
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'رمز عبور جدید باید حداقل ۸ کاراکتر باشد', 422, 'new_password');
}
if ($current === '' || !$this->hasher->isPasswordValid($user, $current)) {
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'رمز عبور فعلی نادرست است', 422, 'current_password');
}
if ($this->hasher->isPasswordValid($user, $new)) {
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'رمز جدید نباید با رمز فعلی یکسان باشد', 422, 'new_password');
}
$user->setPasswordHash($this->hasher->hashPassword($user, $new));
$this->em->flush();
return $this->success(['message' => 'رمز عبور با موفقیت تغییر یافت']);
}
#[OA\Post(
path: '/oauth/token/refresh',
summary: 'Refresh access token using a refresh token',