- Added initialTab prop to EditModal for setting the active tab on open. - Updated state management in ClinicDetailPage to handle initial tab for editing. - Refactored openEdit function to set the initial tab before opening the edit modal. - Combined specialties, insurances, and services sections in the sidebar for better organization. - Improved modal rendering using createPortal for better context handling. style: increase z-index for modal overlay - Updated the z-index of the overlay class in styles.css to ensure modals appear above other elements. feat: implement multi-role dashboard functionality - Created a new prompt for multi-role dashboard implementation. - Defined roles and their access levels in the admin panel. - Updated backend to support user role identification and context retrieval. - Enhanced frontend to dynamically render components based on user roles. - Added new routes and components for role-specific dashboards. chore: add skills for admin endpoint and page creation - Created SKILL.md files for adding admin endpoints and pages. - Provided templates and guidelines for implementing new admin features. chore: sync database after entity changes - Added a new skill for syncing the database after any entity modifications.
70 lines
2.9 KiB
Markdown
70 lines
2.9 KiB
Markdown
---
|
|
name: add-admin-endpoint
|
|
description: Add a new paginated admin API endpoint to AdminApiController. Use when the user wants to add a backend admin list, stats, or action endpoint — things like "add an endpoint for X", "create an admin API for Y", "I need a route that lists Z".
|
|
---
|
|
|
|
## Target file
|
|
`src/Admin/Controller/AdminApiController.php`
|
|
|
|
All admin endpoints live here. The class already has `#[IsGranted('ROLE_ADMIN')]` and injects `EntityManagerInterface $em`.
|
|
|
|
## Checklist
|
|
|
|
1. **Stats endpoint** (optional but standard): a separate `#[Route('/api/v1/admin/{entity}/stats')]` method that returns counts via raw SQL (`$this->em->getConnection()->fetchOne()`). Return with `$this->success([...])`.
|
|
|
|
2. **List endpoint**: use QueryBuilder with `->getArrayResult()` — never load full entities for list queries (entity getters may not exist for all fields). Pattern:
|
|
|
|
```php
|
|
#[Route('/api/v1/admin/{entities}', methods: ['GET'])]
|
|
public function list{Entity}(Request $request): JsonResponse
|
|
{
|
|
$page = max(1, (int) $request->query->get('page', 1));
|
|
$limit = min(100, max(1, (int) $request->query->get('limit', 15)));
|
|
$search = trim((string) $request->query->get('search', ''));
|
|
|
|
$qb = $this->em->createQueryBuilder()
|
|
->select('e.id, e.uuid, e.someField, e.createdAt')
|
|
->from(SomeEntity::class, 'e');
|
|
|
|
if ($search !== '') {
|
|
$qb->andWhere('e.name LIKE :s')->setParameter('s', "%$search%");
|
|
}
|
|
|
|
$total = (clone $qb)->select('COUNT(e.id)')->getQuery()->getSingleScalarResult();
|
|
|
|
$items = $qb
|
|
->orderBy('e.id', 'DESC')
|
|
->setFirstResult(($page - 1) * $limit)
|
|
->setMaxResults($limit)
|
|
->getQuery()
|
|
->getArrayResult();
|
|
|
|
return $this->paginated($items, (int) $total, $page, $limit);
|
|
}
|
|
```
|
|
|
|
3. **Action endpoints** (toggle status, etc.) follow this shape:
|
|
|
|
```php
|
|
#[Route('/api/v1/admin/{entities}/{uuid}/status', methods: ['POST'])]
|
|
public function toggle{Entity}Status(string $uuid): JsonResponse
|
|
{
|
|
$entity = $this->em->getRepository(SomeEntity::class)->findOneBy(['uuid' => $uuid]);
|
|
if (!$entity) return $this->error('NOT_FOUND', 'Entity not found', 404);
|
|
|
|
$entity->setIsActive(!$entity->getIsActive());
|
|
$this->em->flush();
|
|
|
|
return $this->success(['is_active' => $entity->getIsActive()]);
|
|
}
|
|
```
|
|
|
|
## Critical rules
|
|
|
|
- **Always use `getArrayResult()`** for list queries. Never call entity getters inside admin list methods.
|
|
- `createdAt` and `updatedAt` are Unix integer timestamps — do not format them in PHP, let the frontend handle it.
|
|
- For JOINs to categories (city, state, specialty), use LEFT JOIN in DQL and select the name field directly into the array result.
|
|
- Response shape for lists: `$this->paginated($items, $total, $page, $limit)` — frontend reads `data?.data` for items and `data?.meta?.totalRecords` for count.
|
|
- Add `use` imports for any new entity class at the top of the file.
|
|
- Run `/sync-db` only if a new entity or column was added as part of this change.
|