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:
@@ -340,7 +340,7 @@ class AdminApiController extends BaseController
|
||||
$cityId = $request->query->get('city_id');
|
||||
|
||||
$qb = $this->em->createQueryBuilder()
|
||||
->select('r.uuid, r.fullName, r.mobileNumber, r.cityId, r.commissionPercent, r.active, r.createdAt, c.label as city_name')
|
||||
->select('r.id, r.uuid, r.fullName, r.mobileNumber, r.cityId, r.commissionPercent, r.active, r.createdAt, c.label as city_name')
|
||||
->from(Representation::class, 'r')
|
||||
->leftJoin(Category::class, 'c', 'WITH', 'c.id = r.cityId')
|
||||
->orderBy('r.createdAt', 'DESC');
|
||||
@@ -361,6 +361,7 @@ class AdminApiController extends BaseController
|
||||
->getQuery()->getArrayResult();
|
||||
|
||||
$items = array_map(fn(array $r) => [
|
||||
'id' => (int) $r['id'],
|
||||
'uuid' => $r['uuid'],
|
||||
'domain' => $r['fullName'],
|
||||
'full_name' => $r['fullName'],
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +77,9 @@ class Category
|
||||
#[ORM\Column(name: 'social_media', type: 'json', nullable: true)]
|
||||
private ?array $socialMedia = null;
|
||||
|
||||
#[ORM\Column(name: 'logo_url', type: 'string', length: 500, nullable: true)]
|
||||
private ?string $logoUrl = null;
|
||||
|
||||
public function __construct(string $bundle, string $label)
|
||||
{
|
||||
$this->uuid = Uuid::v4()->toRfc4122();
|
||||
@@ -103,6 +106,7 @@ class Category
|
||||
public function getFooterDescription(): ?string { return $this->footerDescription; }
|
||||
public function getFooterDisclaimer(): ?string { return $this->footerDisclaimer; }
|
||||
public function getSocialMedia(): ?array { return $this->socialMedia; }
|
||||
public function getLogoUrl(): ?string { return $this->logoUrl; }
|
||||
|
||||
public function setBundle(string $bundle): self { $this->bundle = $bundle; return $this; }
|
||||
public function setLabel(?string $label): self { $this->label = $label; return $this; }
|
||||
@@ -121,6 +125,7 @@ class Category
|
||||
public function setFooterDescription(?string $v): self { $this->footerDescription = $v; return $this; }
|
||||
public function setFooterDisclaimer(?string $v): self { $this->footerDisclaimer = $v; return $this; }
|
||||
public function setSocialMedia(?array $v): self { $this->socialMedia = $v; return $this; }
|
||||
public function setLogoUrl(?string $v): self { $this->logoUrl = $v; return $this; }
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
@@ -139,7 +144,12 @@ class Category
|
||||
if ($this->title !== null) {
|
||||
$data['title'] = $this->title;
|
||||
}
|
||||
if (in_array($this->bundle, ['insurance_type', 'supplementary_insurance'], true)) {
|
||||
$data['logo_url'] = $this->logoUrl;
|
||||
}
|
||||
|
||||
if ($this->bundle === 'city') {
|
||||
$data['representation_id'] = $this->representationId;
|
||||
$data['contact_phone'] = $this->contactPhone;
|
||||
$data['email'] = $this->email;
|
||||
$data['description'] = $this->description;
|
||||
|
||||
@@ -84,6 +84,8 @@ class CategoryService
|
||||
if (array_key_exists('footer_description', $data)) $category->setFooterDescription($data['footer_description']);
|
||||
if (array_key_exists('footer_disclaimer', $data)) $category->setFooterDisclaimer($data['footer_disclaimer']);
|
||||
if (array_key_exists('social_media', $data)) $category->setSocialMedia($data['social_media']);
|
||||
if (array_key_exists('representation_id', $data)) $category->setRepresentationId($data['representation_id'] !== null ? (int) $data['representation_id'] : null);
|
||||
if (array_key_exists('logo_url', $data)) $category->setLogoUrl($data['logo_url']);
|
||||
}
|
||||
|
||||
private function invalidate(string $bundle): void
|
||||
|
||||
Reference in New Issue
Block a user