feat: enhance RepresentationsPage with searchable city filter and form improvements

- Updated RepresentationsPage to use SearchableSelect for city filtering.
- Modified form handling to use Controller from react-hook-form for city selection.
- Improved city filter handling to support null values.
- Added city_id to Representation interface in types.
- Implemented uploadLogo endpoint in CategoryController for logo uploads.
- Added logo_url field to Category entity and updated related services.
- Created SearchableSelect component for better user experience in selecting options.
- Added migration to include logo_url in categories table.
This commit is contained in:
hamed
2026-06-10 13:02:47 +03:30
parent 942634c98e
commit 06ab875693
10 changed files with 931 additions and 158 deletions
@@ -7,16 +7,20 @@ use App\Category\Repository\CategoryRepository;
use App\Category\Service\CategoryService;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Controller\BaseController;
use App\Shared\Service\FileValidatorService;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Uid\Uuid;
class CategoryController extends BaseController
{
public function __construct(
private readonly CategoryRepository $repository,
private readonly CategoryService $service,
private readonly FileValidatorService $fileValidator,
private readonly string $projectDir,
) {}
#[Route('/api/v1/categorys/tag', methods: ['GET'])]
@@ -120,4 +124,42 @@ class CategoryController extends BaseController
return $this->success(['message' => 'دسته‌بندی با موفقیت حذف شد']);
}
#[Route('/api/v1/admin/category/upload-logo', methods: ['POST'])]
#[IsGranted('ROLE_ADMIN')]
public function uploadLogo(Request $request): JsonResponse
{
$content = $request->getContent();
$disposition = $request->headers->get('Content-Disposition', '');
preg_match('/filename=["\']?([^"\';\s]+)["\']?/i', $disposition, $m);
$filename = $m[1] ?? 'logo.jpg';
$tmpPath = sys_get_temp_dir() . '/' . uniqid('upload_', true);
file_put_contents($tmpPath, $content);
try {
$safeFilename = $this->fileValidator->sanitizeFilename($filename);
$mime = $this->fileValidator->detectMimeType($tmpPath);
$year = date('Y'); $month = date('m');
$dir = $this->projectDir . '/public/uploads/categories/logo/' . $year . '-' . $month;
if (!is_dir($dir)) mkdir($dir, 0755, true);
$storedName = uniqid('', true) . '_' . $safeFilename;
rename($tmpPath, $dir . '/' . $storedName);
$url = '/uploads/categories/logo/' . $year . '-' . $month . '/' . $storedName;
return $this->success([
'url' => $url,
'uuid' => Uuid::v4()->toRfc4122(),
'filename' => $safeFilename,
'filemime' => $mime,
'filesize' => strlen($content),
]);
} catch (\Throwable $e) {
if (file_exists($tmpPath)) unlink($tmpPath);
return $this->error(ErrorCodes::ERR_VALIDATION_001, $e->getMessage(), 422);
}
}
}