feat(tenant): add site title and favicon support for tenants and website types

This commit is contained in:
2026-08-14 08:55:59 -03:00
parent 4de08ff2f2
commit bfdaf1c38b
17 changed files with 355 additions and 6 deletions

View File

@@ -21,6 +21,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
'codigo',
'nombre',
'dominio',
'site_title',
'primary_color',
'secondary_color',
'danger_color',
@@ -29,6 +30,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
'footer_bg_color',
'header_logo_id',
'footer_logo_id',
'favicon_id',
'header_bg_image_id',
'footer_bg_image_id',
'website_type_code',
@@ -101,6 +103,14 @@ class Tenant extends Model
return $this->belongsTo(Attachment::class, 'footer_logo_id');
}
/**
* @return BelongsTo<Attachment, $this>
*/
public function favicon(): BelongsTo
{
return $this->belongsTo(Attachment::class, 'favicon_id');
}
/**
* @return BelongsTo<Attachment, $this>
*/

View File

@@ -14,6 +14,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
'nombre',
'dominio',
'scanner_domain',
'site_title',
'primary_color',
'secondary_color',
'danger_color',
@@ -27,6 +28,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
'login_header_footer_color',
'site_logo',
'footer_logo',
'favicon_id',
])]
class WebsiteType extends Model
{
@@ -50,6 +52,14 @@ class WebsiteType extends Model
return $this->belongsTo(Attachment::class, 'footer_logo');
}
/**
* @return BelongsTo<Attachment, $this>
*/
public function favicon(): BelongsTo
{
return $this->belongsTo(Attachment::class, 'favicon_id');
}
/**
* @return HasMany<WebsiteTypeExtra, $this>
*/

View File

@@ -55,6 +55,7 @@ class StoreTenantRequest extends FormRequest
'max:255',
Rule::unique('tenants', 'dominio'),
],
'site_title' => ['sometimes', 'nullable', 'string', 'max:255'],
'primary_color' => ['required', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'],
'secondary_color' => ['required', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'],
'danger_color' => ['required', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'],
@@ -63,6 +64,7 @@ class StoreTenantRequest extends FormRequest
'footer_bg_color' => ['required', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'],
'header_logo' => $logoRule,
'footer_logo' => $logoRule,
'favicon' => ['sometimes', 'nullable', new ImageOrBase64Rule],
'header_bg_image' => ['sometimes', 'nullable', new ImageOrBase64Rule],
'footer_bg_image' => ['sometimes', 'nullable', new ImageOrBase64Rule],
'social_media' => ['sometimes', 'array'],

View File

@@ -65,6 +65,7 @@ class UpdateTenantRequest extends FormRequest
'max:255',
Rule::unique('tenants', 'dominio')->ignore($tenant?->id),
],
'site_title' => ['sometimes', 'nullable', 'string', 'max:255'],
'primary_color' => ['nullable', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'],
'secondary_color' => ['nullable', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'],
'danger_color' => ['nullable', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'],
@@ -73,6 +74,7 @@ class UpdateTenantRequest extends FormRequest
'footer_bg_color' => ['nullable', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'],
'header_logo' => $logoRule,
'footer_logo' => $logoRule,
'favicon' => ['sometimes', 'nullable', new ImageOrBase64Rule],
'header_bg_image' => ['sometimes', 'nullable', new ImageOrBase64Rule],
'footer_bg_image' => ['sometimes', 'nullable', new ImageOrBase64Rule],
'social_media' => ['sometimes', 'array'],

View File

@@ -26,6 +26,11 @@ class TenantResource extends JsonResource
'codigo' => $this->codigo,
'nombre' => $this->nombre,
'dominio' => $this->dominio,
'site_title' => $this->site_title
?? $this->websiteType?->site_title
?? 'ShopitFront',
'favicon' => ($this->favicon ?? $this->websiteType?->favicon)
?->getTemporaryUrl(1440),
'primary_color' => $this->primary_color,
'secondary_color' => $this->secondary_color,
'danger_color' => $this->danger_color,

View File

@@ -13,6 +13,8 @@ class TenantInformationService
private const DEFAULT_RELATIONS = [
'headerLogo',
'footerLogo',
'favicon',
'websiteType.favicon',
'headerBackgroundImage',
'footerBackgroundImage',
'socialMedia',

View File

@@ -25,6 +25,7 @@ class TenantService
return DB::transaction(function () use ($data): Tenant {
$headerLogo = $data['header_logo'] ?? null;
$footerLogo = $data['footer_logo'] ?? null;
$favicon = $data['favicon'] ?? null;
$headerBackgroundImage = $data['header_bg_image'] ?? null;
$footerBackgroundImage = $data['footer_bg_image'] ?? null;
$socialMedia = $data['social_media'] ?? [];
@@ -33,6 +34,7 @@ class TenantService
unset(
$data['header_logo'],
$data['footer_logo'],
$data['favicon'],
$data['header_bg_image'],
$data['footer_bg_image'],
$data['social_media'],
@@ -63,6 +65,7 @@ class TenantService
$data['header_logo_id'] = $headerAttachmentId;
$data['footer_logo_id'] = $footerAttachmentId;
$data['favicon_id'] = $this->storeTenantImage($favicon);
$data['header_bg_image_id'] = $this->storeTenantImage($headerBackgroundImage);
$data['footer_bg_image_id'] = $this->storeTenantImage($footerBackgroundImage);
@@ -85,11 +88,13 @@ class TenantService
return DB::transaction(function () use ($tenant, $data): Tenant {
$hasHeaderLogoKey = array_key_exists('header_logo', $data);
$hasFooterLogoKey = array_key_exists('footer_logo', $data);
$hasFaviconKey = array_key_exists('favicon', $data);
$hasHeaderBackgroundImageKey = array_key_exists('header_bg_image', $data);
$hasFooterBackgroundImageKey = array_key_exists('footer_bg_image', $data);
$hasSocialMediaKey = array_key_exists('social_media', $data);
$headerLogo = $data['header_logo'] ?? null;
$footerLogo = $data['footer_logo'] ?? null;
$favicon = $data['favicon'] ?? null;
$headerBackgroundImage = $data['header_bg_image'] ?? null;
$footerBackgroundImage = $data['footer_bg_image'] ?? null;
$socialMedia = $data['social_media'] ?? [];
@@ -97,6 +102,7 @@ class TenantService
unset(
$data['header_logo'],
$data['footer_logo'],
$data['favicon'],
$data['header_bg_image'],
$data['footer_bg_image'],
$data['social_media']
@@ -136,6 +142,10 @@ class TenantService
}
}
if ($hasFaviconKey) {
$tenant->favicon_id = $this->storeTenantImage($favicon);
}
if ($hasHeaderBackgroundImageKey) {
$tenant->header_bg_image_id = $this->storeTenantImage($headerBackgroundImage);
}

View File

@@ -46,14 +46,20 @@ class WebsiteTypeService
return DB::transaction(function () use ($websiteType, $data): WebsiteType {
$previousLogos = [];
foreach (['site_logo' => 'siteLogo', 'footer_logo' => 'footerLogo'] as $field => $relation) {
$attachmentFields = [
'site_logo' => ['relation' => 'siteLogo', 'column' => 'site_logo'],
'footer_logo' => ['relation' => 'footerLogo', 'column' => 'footer_logo'],
'favicon' => ['relation' => 'favicon', 'column' => 'favicon_id'],
];
foreach ($attachmentFields as $field => $attachmentField) {
if (! array_key_exists($field, $data)) {
continue;
}
$logo = $data[$field];
$previousLogos[$field] = $websiteType->exists
? $websiteType->{$relation}()->first()
? $websiteType->{$attachmentField['relation']}()->first()
: null;
unset($data[$field]);
@@ -65,17 +71,17 @@ class WebsiteTypeService
: $this->attachmentService->store($logo, 'website-types');
}
$data[$field] = $attachment?->id;
$data[$attachmentField['column']] = $attachment?->id;
}
$websiteType->fill($data)->save();
foreach ($previousLogos as $field => $previousLogo) {
foreach ($previousLogos as $previousLogo) {
if (
$previousLogo instanceof Attachment
&& $previousLogo->id !== $websiteType->{$field}
&& $previousLogo->id !== $websiteType->site_logo
&& $previousLogo->id !== $websiteType->footer_logo
&& $previousLogo->id !== $websiteType->favicon_id
) {
$this->attachmentService->delete($previousLogo);
}