Compare commits
6 Commits
6858b387c3
...
4a51f3afde
| Author | SHA1 | Date | |
|---|---|---|---|
| 4a51f3afde | |||
| f1df6a5918 | |||
| dbeaf19513 | |||
| dd76d5d951 | |||
| f603a82b5e | |||
| b478c23f3b |
26
app/Domains/Auth/Controllers/AdminAppMeController.php
Normal file
26
app/Domains/Auth/Controllers/AdminAppMeController.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Auth\Controllers;
|
||||
|
||||
use App\Domains\Auth\Models\User;
|
||||
use App\Domains\Auth\Resources\AdminAppMeResource;
|
||||
use App\Domains\Auth\Services\AdminAppContextService;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class AdminAppMeController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly AdminAppContextService $adminAppContextService,
|
||||
) {}
|
||||
|
||||
public function __invoke(Request $request): AdminAppMeResource
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $request->user();
|
||||
|
||||
return AdminAppMeResource::make(
|
||||
$this->adminAppContextService->load($user)
|
||||
);
|
||||
}
|
||||
}
|
||||
25
app/Domains/Auth/Resources/AdminAppMeResource.php
Normal file
25
app/Domains/Auth/Resources/AdminAppMeResource.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Auth\Resources;
|
||||
|
||||
use App\Domains\Auth\Models\User;
|
||||
use App\Domains\Tenant\Resources\TenantResource;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/**
|
||||
* @mixin User
|
||||
*/
|
||||
class AdminAppMeResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'user' => UserResource::make($this->resource),
|
||||
'tenant' => TenantResource::make($this->tenant),
|
||||
];
|
||||
}
|
||||
}
|
||||
26
app/Domains/Auth/Services/AdminAppContextService.php
Normal file
26
app/Domains/Auth/Services/AdminAppContextService.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Auth\Services;
|
||||
|
||||
use App\Domains\Auth\Models\User;
|
||||
use App\Domains\Authorization\Enums\RoleCode;
|
||||
|
||||
class AdminAppContextService
|
||||
{
|
||||
public function load(User $user): User
|
||||
{
|
||||
$tenant = $user->tenant()
|
||||
->with([
|
||||
'menues' => fn ($query) => $query
|
||||
->whereHas(
|
||||
'roles',
|
||||
fn ($query) => $query->where('codigo', RoleCode::AdminApp->value)
|
||||
),
|
||||
])
|
||||
->firstOrFail();
|
||||
|
||||
$user->setRelation('tenant', $tenant);
|
||||
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
<?php
|
||||
|
||||
use App\Domains\Auth\Controllers\AdminAppLoginController;
|
||||
use App\Domains\Auth\Controllers\AdminAppMeController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::prefix('v1/adminapp')->group(function (): void {
|
||||
Route::post('login', AdminAppLoginController::class)->middleware('throttle:login');
|
||||
Route::middleware(['auth:sanctum', 'adminapp.tenant'])
|
||||
->get('me', AdminAppMeController::class);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Tenant\Controllers\AdminApp;
|
||||
|
||||
use App\Domains\Tenant\Models\WebsiteType;
|
||||
use App\Domains\Tenant\Requests\BootstrapAdminAppRequest;
|
||||
use App\Domains\Tenant\Resources\AdminApp\BootstrapAdminAppResource;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class BootstrapAdminAppController extends Controller
|
||||
{
|
||||
public function __invoke(
|
||||
BootstrapAdminAppRequest $request
|
||||
): BootstrapAdminAppResource {
|
||||
/** @var string $domain */
|
||||
$domain = $request->validated('dominio');
|
||||
|
||||
return BootstrapAdminAppResource::make(
|
||||
WebsiteType::query()
|
||||
->with(['siteLogo', 'footerLogo'])
|
||||
->where('dominio', $domain)
|
||||
->firstOrFail()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Tenant\Controllers\AdminApp;
|
||||
|
||||
use App\Domains\Auth\Models\User;
|
||||
use App\Domains\Tenant\Resources\TenantResource;
|
||||
use App\Domains\Tenant\Services\TenantInformationService;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class BootstrapTenantController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
protected TenantInformationService $tenantInformationService
|
||||
) {}
|
||||
|
||||
public function __invoke(Request $request): TenantResource
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $request->user();
|
||||
$tenant = $user->tenant()->firstOrFail();
|
||||
|
||||
return TenantResource::make(
|
||||
$this->tenantInformationService->load($tenant, [
|
||||
'menues' => fn ($query) => $query
|
||||
->where('code', 'like', 'admin.%')
|
||||
->whereHas(
|
||||
'roles',
|
||||
fn ($query) => $query->where('codigo', $user->rol_codigo)
|
||||
),
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,10 @@ class WebsiteExtraController extends Controller
|
||||
|
||||
return WebsiteExtrasResource::make(
|
||||
$this->loadTenant($request->user())
|
||||
);
|
||||
)->additional([
|
||||
'code' => 'tenant.website_extra_toggled',
|
||||
'message' => __('api.tenant.website_extra_toggled'),
|
||||
]);
|
||||
}
|
||||
|
||||
private function loadTenant(User $user): Tenant
|
||||
|
||||
@@ -17,8 +17,15 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
'secondary_color',
|
||||
'danger_color',
|
||||
'success_color',
|
||||
'warning_color',
|
||||
'body_color',
|
||||
'darker_body_color',
|
||||
'surface_color',
|
||||
'background_color',
|
||||
'border_color',
|
||||
'login_header_footer_color',
|
||||
'site_logo',
|
||||
'footer_logo',
|
||||
])]
|
||||
class WebsiteType extends Model
|
||||
{
|
||||
@@ -34,6 +41,14 @@ class WebsiteType extends Model
|
||||
return $this->belongsTo(Attachment::class, 'site_logo');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Attachment, $this>
|
||||
*/
|
||||
public function footerLogo(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Attachment::class, 'footer_logo');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<WebsiteTypeExtra, $this>
|
||||
*/
|
||||
|
||||
5
app/Domains/Tenant/Requests/BootstrapAdminAppRequest.php
Normal file
5
app/Domains/Tenant/Requests/BootstrapAdminAppRequest.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Tenant\Requests;
|
||||
|
||||
class BootstrapAdminAppRequest extends BootstrapTenantRequest {}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Tenant\Resources\AdminApp;
|
||||
|
||||
use App\Domains\Tenant\Models\WebsiteType;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/**
|
||||
* @mixin WebsiteType
|
||||
*/
|
||||
class BootstrapAdminAppResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'website_type_code' => $this->codigo,
|
||||
'primary_color' => $this->primary_color,
|
||||
'secondary_color' => $this->secondary_color,
|
||||
'danger_color' => $this->danger_color,
|
||||
'success_color' => $this->success_color,
|
||||
'warning_color' => $this->warning_color,
|
||||
'body_color' => $this->body_color,
|
||||
'darker_body_color' => $this->darker_body_color,
|
||||
'surface_color' => $this->surface_color,
|
||||
'background_color' => $this->background_color,
|
||||
'border_color' => $this->border_color,
|
||||
'login_header_footer_color' => $this->login_header_footer_color,
|
||||
'site_logo' => $this->siteLogo?->getTemporaryUrl(1440),
|
||||
'footer_logo' => $this->footerLogo?->getTemporaryUrl(1440),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -174,7 +174,15 @@ class WebsiteExtraService
|
||||
$websiteExtra = $tenant->websiteExtras()
|
||||
->where('website_type_extra_id', $definition->id)
|
||||
->lockForUpdate()
|
||||
->firstOrFail();
|
||||
->first();
|
||||
|
||||
if (! $websiteExtra) {
|
||||
return $tenant->websiteExtras()->create([
|
||||
'website_type_extra_id' => $definition->id,
|
||||
'config' => [],
|
||||
'is_enabled' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
$websiteExtra->update([
|
||||
'is_enabled' => ! $websiteExtra->is_enabled,
|
||||
|
||||
@@ -15,33 +15,71 @@ class WebsiteTypeService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Create a website type and store its site logo.
|
||||
* Create a website type and store its logos.
|
||||
*
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
public function create(array $data): WebsiteType
|
||||
{
|
||||
return DB::transaction(function () use ($data): WebsiteType {
|
||||
$siteLogo = $data['site_logo'] ?? null;
|
||||
return $this->save(new WebsiteType, $data);
|
||||
}
|
||||
|
||||
unset($data['site_logo']);
|
||||
/**
|
||||
* Create or update a website type and replace its logos when provided.
|
||||
*
|
||||
* @param array<string, mixed> $attributes
|
||||
* @param array<string, mixed> $values
|
||||
*/
|
||||
public function updateOrCreate(array $attributes, array $values = []): WebsiteType
|
||||
{
|
||||
/** @var WebsiteType $websiteType */
|
||||
$websiteType = WebsiteType::query()->firstOrNew($attributes);
|
||||
|
||||
$siteLogoAttachmentId = null;
|
||||
return $this->save($websiteType, [...$attributes, ...$values]);
|
||||
}
|
||||
|
||||
if ($siteLogo) {
|
||||
$attachment = Str::isUuid($siteLogo)
|
||||
? Attachment::query()->where('key', $siteLogo)->first()
|
||||
: $this->attachmentService->store($siteLogo, 'website-types');
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
private function save(WebsiteType $websiteType, array $data): WebsiteType
|
||||
{
|
||||
return DB::transaction(function () use ($websiteType, $data): WebsiteType {
|
||||
$previousLogos = [];
|
||||
|
||||
if ($attachment) {
|
||||
$siteLogoAttachmentId = $attachment->id;
|
||||
foreach (['site_logo' => 'siteLogo', 'footer_logo' => 'footerLogo'] as $field => $relation) {
|
||||
if (! array_key_exists($field, $data)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$logo = $data[$field];
|
||||
$previousLogos[$field] = $websiteType->exists
|
||||
? $websiteType->{$relation}()->first()
|
||||
: null;
|
||||
unset($data[$field]);
|
||||
|
||||
$attachment = null;
|
||||
|
||||
if ($logo) {
|
||||
$attachment = is_string($logo) && Str::isUuid($logo)
|
||||
? Attachment::query()->where('key', $logo)->first()
|
||||
: $this->attachmentService->store($logo, 'website-types');
|
||||
}
|
||||
|
||||
$data[$field] = $attachment?->id;
|
||||
}
|
||||
|
||||
$data['site_logo'] = $siteLogoAttachmentId;
|
||||
$websiteType->fill($data)->save();
|
||||
|
||||
/** @var WebsiteType $websiteType */
|
||||
$websiteType = WebsiteType::query()->create($data);
|
||||
foreach ($previousLogos as $field => $previousLogo) {
|
||||
if (
|
||||
$previousLogo instanceof Attachment
|
||||
&& $previousLogo->id !== $websiteType->{$field}
|
||||
&& $previousLogo->id !== $websiteType->site_logo
|
||||
&& $previousLogo->id !== $websiteType->footer_logo
|
||||
) {
|
||||
$this->attachmentService->delete($previousLogo);
|
||||
}
|
||||
}
|
||||
|
||||
return $websiteType;
|
||||
});
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
<?php
|
||||
|
||||
use App\Domains\Tenant\Controllers\AdminApp\BootstrapTenantController;
|
||||
use App\Domains\Tenant\Controllers\AdminApp\BootstrapAdminAppController;
|
||||
use App\Domains\Tenant\Controllers\AdminApp\WebsiteExtraController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::get(
|
||||
'v1/adminapp/bootstrap/{dominio}',
|
||||
BootstrapAdminAppController::class
|
||||
);
|
||||
|
||||
Route::prefix('v1/adminapp/tenant')
|
||||
->middleware(['auth:sanctum', 'adminapp.tenant'])
|
||||
->group(function (): void {
|
||||
Route::get('bootstrap', BootstrapTenantController::class);
|
||||
Route::get('website-extras', [WebsiteExtraController::class, 'show']);
|
||||
Route::get('website-extras/{websiteExtraCode}', [WebsiteExtraController::class, 'showExtra']);
|
||||
Route::put('website-extras/{websiteExtraCode}', [WebsiteExtraController::class, 'update']);
|
||||
|
||||
@@ -14,7 +14,13 @@ return new class extends Migration
|
||||
$table->string('secondary_color')->nullable()->after('primary_color');
|
||||
$table->string('danger_color')->nullable()->after('secondary_color');
|
||||
$table->string('success_color')->nullable()->after('danger_color');
|
||||
$table->string('login_header_footer_color')->nullable()->after('success_color');
|
||||
$table->string('warning_color')->nullable()->after('success_color');
|
||||
$table->string('body_color')->nullable()->after('warning_color');
|
||||
$table->string('darker_body_color')->nullable()->after('body_color');
|
||||
$table->string('surface_color')->nullable()->after('darker_body_color');
|
||||
$table->string('background_color')->nullable()->after('surface_color');
|
||||
$table->string('border_color')->nullable()->after('background_color');
|
||||
$table->string('login_header_footer_color')->nullable()->after('border_color');
|
||||
$table->unsignedBigInteger('site_logo')->nullable()->after('login_header_footer_color');
|
||||
|
||||
$table->foreign('site_logo')
|
||||
@@ -37,6 +43,12 @@ return new class extends Migration
|
||||
'secondary_color',
|
||||
'danger_color',
|
||||
'success_color',
|
||||
'warning_color',
|
||||
'body_color',
|
||||
'darker_body_color',
|
||||
'surface_color',
|
||||
'background_color',
|
||||
'border_color',
|
||||
'login_header_footer_color',
|
||||
'site_logo',
|
||||
]);
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('website_type', function (Blueprint $table): void {
|
||||
$table->unsignedBigInteger('footer_logo')->nullable()->after('site_logo');
|
||||
|
||||
$table->foreign('footer_logo')
|
||||
->references('id')
|
||||
->on('attachments')
|
||||
->nullOnDelete();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('website_type', function (Blueprint $table): void {
|
||||
if (Schema::getConnection()->getDriverName() !== 'sqlite') {
|
||||
$table->dropForeign(['footer_logo']);
|
||||
}
|
||||
|
||||
$table->dropColumn('footer_logo');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -23,16 +23,54 @@ class MenuSeeder extends Seeder
|
||||
'route' => '/product/:id',
|
||||
],
|
||||
['code' => 'checkout', 'label' => 'Finalizar compra', 'route' => '/checkout'],
|
||||
['code' => 'admin.event', 'label' => 'Eventos', 'route' => '/admin/event'],
|
||||
['code' => 'admin.catalog', 'label' => 'Catálogo', 'route' => '/admin/catalog'],
|
||||
['code' => 'admin.combos', 'label' => 'Combos', 'route' => '/admin/combos'],
|
||||
[
|
||||
'code' => 'admin.categories',
|
||||
'code' => 'main.adminapp',
|
||||
'label' => 'Administración',
|
||||
'content_type' => Menu::CONTENT_TYPE_DYNAMIC,
|
||||
'route' => '/',
|
||||
],
|
||||
[
|
||||
'code' => 'adminapp.event',
|
||||
'label' => 'Eventos',
|
||||
'parent_menu_code' => 'main.adminapp',
|
||||
'route' => '/admin/event',
|
||||
],
|
||||
[
|
||||
'code' => 'adminapp.inicio',
|
||||
'label' => 'Inicio',
|
||||
'parent_menu_code' => 'main.adminapp',
|
||||
'route' => '/admin/inicio',
|
||||
],
|
||||
[
|
||||
'code' => 'adminapp.catalog',
|
||||
'label' => 'Catálogo',
|
||||
'parent_menu_code' => 'main.adminapp',
|
||||
'route' => '/admin/catalog',
|
||||
],
|
||||
[
|
||||
'code' => 'adminapp.combos',
|
||||
'label' => 'Combos',
|
||||
'parent_menu_code' => 'main.adminapp',
|
||||
'route' => '/admin/combos',
|
||||
],
|
||||
[
|
||||
'code' => 'adminapp.categories',
|
||||
'label' => 'Categorías',
|
||||
'parent_menu_code' => 'main.adminapp',
|
||||
'route' => '/admin/categories',
|
||||
],
|
||||
['code' => 'admin.ventas', 'label' => 'Ventas', 'route' => '/admin/ventas'],
|
||||
['code' => 'admin.staff', 'label' => 'Staff', 'route' => '/admin/staff'],
|
||||
[
|
||||
'code' => 'adminapp.ventas',
|
||||
'label' => 'Ventas',
|
||||
'parent_menu_code' => 'main.adminapp',
|
||||
'route' => '/admin/ventas',
|
||||
],
|
||||
[
|
||||
'code' => 'adminapp.staff',
|
||||
'label' => 'Staff',
|
||||
'parent_menu_code' => 'main.adminapp',
|
||||
'route' => '/admin/staff',
|
||||
],
|
||||
[
|
||||
'code' => 'account',
|
||||
'label' => 'Mi cuenta',
|
||||
@@ -135,9 +173,30 @@ class MenuSeeder extends Seeder
|
||||
->whereIn('code', ['profile', 'purchases', 'tickets'])
|
||||
->delete();
|
||||
|
||||
Menu::query()
|
||||
->whereIn('code', [
|
||||
'admin.event',
|
||||
'admin.catalog',
|
||||
'admin.combos',
|
||||
'admin.categories',
|
||||
'admin.ventas',
|
||||
'admin.staff',
|
||||
'event',
|
||||
'catalog',
|
||||
'combos',
|
||||
'categories',
|
||||
'ventas',
|
||||
'staff',
|
||||
])
|
||||
->delete();
|
||||
|
||||
$allRoleMenuCodes = Menu::query()->pluck('code');
|
||||
$adminAppMenuCodes = Menu::query()
|
||||
->where('code', 'main.adminapp')
|
||||
->orWhere('parent_menu_code', 'main.adminapp')
|
||||
->pluck('code');
|
||||
$userMenuCodes = Menu::query()
|
||||
->where('code', 'not like', 'admin.%')
|
||||
->whereNotIn('code', $adminAppMenuCodes)
|
||||
->pluck('code');
|
||||
|
||||
Role::query()
|
||||
|
||||
@@ -120,7 +120,6 @@ class TenantSeeder extends Seeder
|
||||
'extras' => [
|
||||
'heroConfig' => [
|
||||
'title_html' => '<h1>Fiesta Fútbol Infantil</h1>',
|
||||
'description_html' => '<p>Viví una jornada inolvidable de fútbol infantil.</p>',
|
||||
'button_text' => 'Comprar entradas',
|
||||
'button_href' => '/tickets',
|
||||
'background_image_id' => $this->uploadedImage(
|
||||
@@ -133,10 +132,21 @@ class TenantSeeder extends Seeder
|
||||
'location' => 'Rosario, Santa Fe',
|
||||
'dates_text' => '9, 10, 11 y 12 de Octubre 2026',
|
||||
'dates' => [
|
||||
'2026-12-05',
|
||||
'2026-12-06',
|
||||
[
|
||||
'date' => '2026-12-05',
|
||||
'start_time' => '09:00',
|
||||
'end_time' => '18:00',
|
||||
],
|
||||
[
|
||||
'date' => '2026-12-06',
|
||||
'start_time' => '09:00',
|
||||
'end_time' => '18:00',
|
||||
],
|
||||
],
|
||||
],
|
||||
'additionalInfoConfig' => [
|
||||
'description' => '<p>Viví una jornada inolvidable de fútbol infantil.</p>',
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -2,16 +2,39 @@
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Domains\Tenant\Models\WebsiteType;
|
||||
use App\Domains\Tenant\Services\WebsiteTypeService;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use RuntimeException;
|
||||
|
||||
class WebsiteTypeSeeder extends Seeder
|
||||
{
|
||||
private const PRESENTATION = [
|
||||
'primary_color' => '#FF7006',
|
||||
'secondary_color' => '#777777',
|
||||
'danger_color' => '#E04A4A',
|
||||
'success_color' => '#81BC73',
|
||||
'warning_color' => '#81BC73',
|
||||
'body_color' => '#666666',
|
||||
'darker_body_color' => '#333333',
|
||||
'surface_color' => '#ffffff',
|
||||
'background_color' => '#f8f8f8',
|
||||
'border_color' => '#EAEAEA',
|
||||
];
|
||||
|
||||
public function __construct(private readonly WebsiteTypeService $websiteTypeService) {}
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
$shopIt = WebsiteType::query()->updateOrCreate(
|
||||
$shopIt = $this->websiteTypeService->updateOrCreate(
|
||||
['codigo' => 'shopit'],
|
||||
['nombre' => 'ShopIt'],
|
||||
[
|
||||
'nombre' => 'ShopIt',
|
||||
'dominio' => 'localhost',
|
||||
...self::PRESENTATION,
|
||||
'site_logo' => $this->onTicketLogo(),
|
||||
'footer_logo' => $this->onTicketFooterLogo(),
|
||||
],
|
||||
);
|
||||
|
||||
$shopIt->extras()->updateOrCreate(
|
||||
@@ -35,9 +58,15 @@ class WebsiteTypeSeeder extends Seeder
|
||||
],
|
||||
);
|
||||
|
||||
$onTicket = WebsiteType::query()->updateOrCreate(
|
||||
$onTicket = $this->websiteTypeService->updateOrCreate(
|
||||
['codigo' => 'onticket'],
|
||||
['nombre' => 'OnTicket'],
|
||||
[
|
||||
'nombre' => 'OnTicket',
|
||||
'dominio' => 'onticket.localhost',
|
||||
...self::PRESENTATION,
|
||||
'site_logo' => $this->onTicketLogo(),
|
||||
'footer_logo' => $this->onTicketFooterLogo(),
|
||||
],
|
||||
);
|
||||
|
||||
$onTicket->extras()->updateOrCreate(
|
||||
@@ -78,11 +107,68 @@ class WebsiteTypeSeeder extends Seeder
|
||||
'location' => 'nullable|string',
|
||||
'dates_text' => 'nullable|string|max:255',
|
||||
'dates' => 'nullable|array',
|
||||
'dates.*' => 'required|date_format:Y-m-d|distinct',
|
||||
'dates.*' => 'required|array:date,start_time,end_time',
|
||||
'dates.*.date' => 'required|date_format:Y-m-d|distinct',
|
||||
'dates.*.start_time' => 'required|date_format:H:i',
|
||||
'dates.*.end_time' => 'required|date_format:H:i',
|
||||
'contact' => 'nullable|array:whatsapp_url,instagram_url,facebook_url',
|
||||
'contact.whatsapp_url' => 'nullable|url|max:2048',
|
||||
'contact.instagram_url' => 'nullable|url|max:2048',
|
||||
'contact.facebook_url' => 'nullable|url|max:2048',
|
||||
],
|
||||
'transforms' => [],
|
||||
],
|
||||
],
|
||||
);
|
||||
|
||||
$onTicket->extras()->updateOrCreate(
|
||||
['codigo' => 'additionalInfoConfig'],
|
||||
[
|
||||
'nombre' => 'Información adicional',
|
||||
'descripcion' => 'Información adicional visible en la página del evento.',
|
||||
'is_required' => false,
|
||||
'config_schema' => [
|
||||
'request_rules' => [
|
||||
'$' => 'required|array',
|
||||
'description' => 'nullable|string',
|
||||
],
|
||||
'transforms' => [],
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
private function onTicketLogo(): UploadedFile
|
||||
{
|
||||
$path = public_path('images/website_types/onticket_logo.png');
|
||||
|
||||
if (! file_exists($path)) {
|
||||
throw new RuntimeException("OnTicket logo not found at path: {$path}");
|
||||
}
|
||||
|
||||
return new UploadedFile(
|
||||
$path,
|
||||
'onticket_logo.png',
|
||||
'image/png',
|
||||
null,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
private function onTicketFooterLogo(): UploadedFile
|
||||
{
|
||||
$path = public_path('images/website_types/onticket_footer_logo.png');
|
||||
|
||||
if (! file_exists($path)) {
|
||||
throw new RuntimeException("OnTicket footer logo not found at path: {$path}");
|
||||
}
|
||||
|
||||
return new UploadedFile(
|
||||
$path,
|
||||
'onticket_footer_logo.png',
|
||||
'image/png',
|
||||
null,
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,9 @@ return [
|
||||
'schema_required' => 'The schema is required for static menus.',
|
||||
'not_found' => 'The specified menu does not exist.',
|
||||
],
|
||||
'tenant' => [
|
||||
'website_extra_toggled' => 'The section status was updated successfully.',
|
||||
],
|
||||
'errors' => [
|
||||
'forbidden' => 'You do not have permission to perform this action.',
|
||||
'not_found' => 'The requested resource was not found.',
|
||||
|
||||
@@ -94,6 +94,9 @@ return [
|
||||
'schema_required' => 'El schema es obligatorio para los menús estáticos.',
|
||||
'not_found' => 'El menú indicado no existe.',
|
||||
],
|
||||
'tenant' => [
|
||||
'website_extra_toggled' => 'El estado de la sección se actualizó correctamente.',
|
||||
],
|
||||
'errors' => [
|
||||
'forbidden' => 'No tienes permiso para realizar esta acción.',
|
||||
'not_found' => 'El recurso solicitado no fue encontrado.',
|
||||
|
||||
BIN
public/images/website_types/onticket_footer_logo.png
Normal file
BIN
public/images/website_types/onticket_footer_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Tenant;
|
||||
namespace Tests\Feature\Auth;
|
||||
|
||||
use App\Domains\Auth\Models\User;
|
||||
use App\Domains\Authorization\Enums\RoleCode;
|
||||
@@ -11,31 +11,28 @@ use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Laravel\Sanctum\Sanctum;
|
||||
use Tests\TestCase;
|
||||
|
||||
class AdminAppBootstrapTenantControllerTest extends TestCase
|
||||
class AdminAppMeControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_authentication_is_required(): void
|
||||
{
|
||||
$this->getJson('/api/v1/adminapp/tenant/bootstrap')
|
||||
->assertUnauthorized();
|
||||
$this->getJson('/api/v1/adminapp/me')->assertUnauthorized();
|
||||
}
|
||||
|
||||
public function test_a_customer_cannot_bootstrap_adminapp(): void
|
||||
public function test_a_customer_cannot_access_the_adminapp_context(): void
|
||||
{
|
||||
$userRole = $this->createRole(RoleCode::User);
|
||||
$customer = User::factory()->create([
|
||||
'rol_codigo' => $userRole->codigo,
|
||||
'rol_codigo' => $this->createRole(RoleCode::User)->codigo,
|
||||
'tenant_codigo' => null,
|
||||
]);
|
||||
|
||||
Sanctum::actingAs($customer);
|
||||
|
||||
$this->getJson('/api/v1/adminapp/tenant/bootstrap')
|
||||
->assertForbidden();
|
||||
$this->getJson('/api/v1/adminapp/me')->assertForbidden();
|
||||
}
|
||||
|
||||
public function test_it_returns_the_authenticated_users_tenant_and_admin_menus(): void
|
||||
public function test_it_returns_the_user_tenant_and_authorized_admin_menus(): void
|
||||
{
|
||||
$adminAppRole = $this->createRole(RoleCode::AdminApp);
|
||||
$tenant = $this->createTenant('acme');
|
||||
@@ -49,18 +46,23 @@ class AdminAppBootstrapTenantControllerTest extends TestCase
|
||||
$tenant->menues()->sync([$catalog->code, $staff->code, $storefront->code]);
|
||||
$otherTenant->menues()->sync([$staff->code]);
|
||||
|
||||
Sanctum::actingAs(User::factory()->create([
|
||||
$user = User::factory()->create([
|
||||
'nombre_apellido' => 'Admin Acme',
|
||||
'email' => 'admin@acme.test',
|
||||
'rol_codigo' => $adminAppRole->codigo,
|
||||
'tenant_codigo' => $tenant->codigo,
|
||||
]));
|
||||
]);
|
||||
Sanctum::actingAs($user);
|
||||
|
||||
$this->getJson('/api/v1/adminapp/tenant/bootstrap')
|
||||
$this->getJson('/api/v1/adminapp/me')
|
||||
->assertOk()
|
||||
->assertJsonPath('data.codigo', 'acme')
|
||||
->assertJsonCount(1, 'data.menues')
|
||||
->assertJsonPath('data.menues.0.code', 'admin.catalog')
|
||||
->assertJsonPath('data.menues.0.label', 'Catálogo')
|
||||
->assertJsonPath('data.menues.0.route', '/admin/catalog')
|
||||
->assertJsonPath('data.user.id', $user->id)
|
||||
->assertJsonPath('data.user.email', 'admin@acme.test')
|
||||
->assertJsonPath('data.tenant.codigo', 'acme')
|
||||
->assertJsonCount(1, 'data.tenant.menues')
|
||||
->assertJsonPath('data.tenant.menues.0.code', 'admin.catalog')
|
||||
->assertJsonPath('data.tenant.menues.0.label', 'Catálogo')
|
||||
->assertJsonPath('data.tenant.menues.0.route', '/admin/catalog')
|
||||
->assertJsonMissing(['code' => 'admin.staff'])
|
||||
->assertJsonMissing(['code' => 'index']);
|
||||
}
|
||||
@@ -24,14 +24,30 @@ class MenuSeederTest extends TestCase
|
||||
$this->seed(MenuSeeder::class);
|
||||
|
||||
$expectedMenus = [
|
||||
'admin.catalog' => ['Catálogo', '/admin/catalog'],
|
||||
'admin.categories' => ['Categorías', '/admin/categories'],
|
||||
'admin.combos' => ['Combos', '/admin/combos'],
|
||||
'admin.event' => ['Eventos', '/admin/event'],
|
||||
'admin.staff' => ['Staff', '/admin/staff'],
|
||||
'admin.ventas' => ['Ventas', '/admin/ventas'],
|
||||
'adminapp.catalog' => ['Catálogo', '/admin/catalog'],
|
||||
'adminapp.categories' => ['Categorías', '/admin/categories'],
|
||||
'adminapp.combos' => ['Combos', '/admin/combos'],
|
||||
'adminapp.event' => ['Eventos', '/admin/event'],
|
||||
'adminapp.staff' => ['Staff', '/admin/staff'],
|
||||
'adminapp.ventas' => ['Ventas', '/admin/ventas'],
|
||||
];
|
||||
|
||||
$adminApp = Menu::query()
|
||||
->with('children')
|
||||
->where('code', 'main.adminapp')
|
||||
->firstOrFail();
|
||||
|
||||
$this->assertSame('Administración', $adminApp->label);
|
||||
$this->assertSame(Menu::CONTENT_TYPE_DYNAMIC, $adminApp->content_type);
|
||||
$this->assertSame('/', $adminApp->route);
|
||||
$this->assertSame(
|
||||
array_keys($expectedMenus),
|
||||
$adminApp->children->pluck('code')->sort()->values()->all()
|
||||
);
|
||||
$this->assertTrue(
|
||||
$tenant->menues()->where('menues.code', 'main.adminapp')->exists()
|
||||
);
|
||||
|
||||
$adminMenus = Menu::query()
|
||||
->whereIn('code', array_keys($expectedMenus))
|
||||
->get()
|
||||
@@ -42,10 +58,28 @@ class MenuSeederTest extends TestCase
|
||||
foreach ($expectedMenus as $code => [$label, $route]) {
|
||||
$this->assertSame($label, $adminMenus->get($code)?->label);
|
||||
$this->assertSame($route, $adminMenus->get($code)?->route);
|
||||
$this->assertSame('main.adminapp', $adminMenus->get($code)?->parent_menu_code);
|
||||
$this->assertTrue(
|
||||
$tenant->menues()->where('menues.code', $code)->exists()
|
||||
);
|
||||
}
|
||||
|
||||
$this->assertFalse(
|
||||
Menu::query()->whereIn('code', [
|
||||
'admin.event',
|
||||
'admin.catalog',
|
||||
'admin.combos',
|
||||
'admin.categories',
|
||||
'admin.ventas',
|
||||
'admin.staff',
|
||||
'event',
|
||||
'catalog',
|
||||
'combos',
|
||||
'categories',
|
||||
'ventas',
|
||||
'staff',
|
||||
])->exists()
|
||||
);
|
||||
}
|
||||
|
||||
public function test_it_assigns_menus_to_the_expected_roles(): void
|
||||
@@ -57,8 +91,12 @@ class MenuSeederTest extends TestCase
|
||||
->orderBy('code')
|
||||
->pluck('code')
|
||||
->all();
|
||||
$adminAppMenuCodes = Menu::query()
|
||||
->where('code', 'main.adminapp')
|
||||
->orWhere('parent_menu_code', 'main.adminapp')
|
||||
->pluck('code');
|
||||
$userMenuCodes = Menu::query()
|
||||
->where('code', 'not like', 'admin.%')
|
||||
->whereNotIn('code', $adminAppMenuCodes)
|
||||
->orderBy('code')
|
||||
->pluck('code')
|
||||
->all();
|
||||
@@ -84,6 +122,8 @@ class MenuSeederTest extends TestCase
|
||||
->all();
|
||||
|
||||
$this->assertSame($userMenuCodes, $userRoleMenuCodes);
|
||||
$this->assertNotContains('main.adminapp', $userRoleMenuCodes);
|
||||
$this->assertNotContains('adminapp.catalog', $userRoleMenuCodes);
|
||||
$this->assertDatabaseCount(
|
||||
'roles_menues',
|
||||
(count($allMenuCodes) * 2) + count($userMenuCodes)
|
||||
|
||||
@@ -84,12 +84,13 @@ class TenantSeederTest extends TestCase
|
||||
|
||||
$extras = $fiesta->websiteExtras->keyBy('websiteTypeExtra.codigo');
|
||||
$this->assertEqualsCanonicalizing(
|
||||
['heroConfig', 'eventConfig'],
|
||||
['heroConfig', 'eventConfig', 'additionalInfoConfig'],
|
||||
$extras->keys()->all(),
|
||||
);
|
||||
|
||||
$heroConfig = $extras->get('heroConfig')->config;
|
||||
$this->assertSame('<h1>Fiesta Fútbol Infantil</h1>', $heroConfig['title_html']);
|
||||
$this->assertArrayNotHasKey('description_html', $heroConfig);
|
||||
$this->assertIsInt($heroConfig['background_image_id']);
|
||||
$this->assertDatabaseHas('attachments', [
|
||||
'id' => $heroConfig['background_image_id'],
|
||||
@@ -101,9 +102,21 @@ class TenantSeederTest extends TestCase
|
||||
'location' => 'Rosario, Santa Fe',
|
||||
'dates_text' => '9, 10, 11 y 12 de Octubre 2026',
|
||||
'dates' => [
|
||||
'2026-12-05',
|
||||
'2026-12-06',
|
||||
[
|
||||
'date' => '2026-12-05',
|
||||
'start_time' => '09:00',
|
||||
'end_time' => '18:00',
|
||||
],
|
||||
[
|
||||
'date' => '2026-12-06',
|
||||
'start_time' => '09:00',
|
||||
'end_time' => '18:00',
|
||||
],
|
||||
],
|
||||
], $extras->get('eventConfig')->config);
|
||||
|
||||
$this->assertSame([
|
||||
'description' => '<p>Viví una jornada inolvidable de fútbol infantil.</p>',
|
||||
], $extras->get('additionalInfoConfig')->config);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
namespace Tests\Feature\Seeders;
|
||||
|
||||
use App\Domains\Attachable\Models\Attachment;
|
||||
use App\Domains\Tenant\Models\WebsiteType;
|
||||
use Database\Seeders\WebsiteTypeSeeder;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Tests\TestCase;
|
||||
|
||||
class WebsiteTypeSeederTest extends TestCase
|
||||
@@ -13,10 +15,26 @@ class WebsiteTypeSeederTest extends TestCase
|
||||
|
||||
public function test_it_seeds_website_types_and_their_config_schemas_idempotently(): void
|
||||
{
|
||||
Storage::fake('s3');
|
||||
|
||||
$this->seed(WebsiteTypeSeeder::class);
|
||||
$this->seed(WebsiteTypeSeeder::class);
|
||||
|
||||
$this->assertSame(2, WebsiteType::query()->count());
|
||||
$this->assertSame(4, Attachment::query()->count());
|
||||
|
||||
$expectedPresentation = [
|
||||
'primary_color' => '#FF7006',
|
||||
'secondary_color' => '#777777',
|
||||
'danger_color' => '#E04A4A',
|
||||
'success_color' => '#81BC73',
|
||||
'warning_color' => '#81BC73',
|
||||
'body_color' => '#666666',
|
||||
'darker_body_color' => '#333333',
|
||||
'surface_color' => '#ffffff',
|
||||
'background_color' => '#f8f8f8',
|
||||
'border_color' => '#EAEAEA',
|
||||
];
|
||||
|
||||
$shopIt = WebsiteType::query()
|
||||
->where('codigo', 'shopit')
|
||||
@@ -24,6 +42,12 @@ class WebsiteTypeSeederTest extends TestCase
|
||||
->sole();
|
||||
|
||||
$this->assertSame('ShopIt', $shopIt->nombre);
|
||||
$this->assertSame('localhost', $shopIt->dominio);
|
||||
$this->assertSame($expectedPresentation, $shopIt->only(array_keys($expectedPresentation)));
|
||||
$this->assertSame('onticket_logo.png', $shopIt->siteLogo->filename);
|
||||
Storage::disk('s3')->assertExists($shopIt->siteLogo->path);
|
||||
$this->assertSame('onticket_footer_logo.png', $shopIt->footerLogo->filename);
|
||||
Storage::disk('s3')->assertExists($shopIt->footerLogo->path);
|
||||
$this->assertSame(['carousel'], $shopIt->extras->pluck('codigo')->all());
|
||||
$this->assertSame('Carrusel principal', $shopIt->extras->sole()->nombre);
|
||||
$this->assertSame([
|
||||
@@ -45,8 +69,16 @@ class WebsiteTypeSeederTest extends TestCase
|
||||
->sole();
|
||||
|
||||
$this->assertSame('OnTicket', $onTicket->nombre);
|
||||
$this->assertSame('onticket.localhost', $onTicket->dominio);
|
||||
$this->assertSame($expectedPresentation, $onTicket->only(array_keys($expectedPresentation)));
|
||||
$this->assertSame('onticket_logo.png', $onTicket->siteLogo->filename);
|
||||
Storage::disk('s3')->assertExists($onTicket->siteLogo->path);
|
||||
$this->assertSame('onticket_footer_logo.png', $onTicket->footerLogo->filename);
|
||||
Storage::disk('s3')->assertExists($onTicket->footerLogo->path);
|
||||
$this->assertNotSame($shopIt->site_logo, $onTicket->site_logo);
|
||||
$this->assertNotSame($shopIt->footer_logo, $onTicket->footer_logo);
|
||||
$this->assertEqualsCanonicalizing(
|
||||
['heroConfig', 'eventConfig'],
|
||||
['heroConfig', 'eventConfig', 'additionalInfoConfig'],
|
||||
$onTicket->extras->pluck('codigo')->all(),
|
||||
);
|
||||
|
||||
@@ -76,9 +108,27 @@ class WebsiteTypeSeederTest extends TestCase
|
||||
'location' => 'nullable|string',
|
||||
'dates_text' => 'nullable|string|max:255',
|
||||
'dates' => 'nullable|array',
|
||||
'dates.*' => 'required|date_format:Y-m-d|distinct',
|
||||
'dates.*' => 'required|array:date,start_time,end_time',
|
||||
'dates.*.date' => 'required|date_format:Y-m-d|distinct',
|
||||
'dates.*.start_time' => 'required|date_format:H:i',
|
||||
'dates.*.end_time' => 'required|date_format:H:i',
|
||||
'contact' => 'nullable|array:whatsapp_url,instagram_url,facebook_url',
|
||||
'contact.whatsapp_url' => 'nullable|url|max:2048',
|
||||
'contact.instagram_url' => 'nullable|url|max:2048',
|
||||
'contact.facebook_url' => 'nullable|url|max:2048',
|
||||
],
|
||||
'transforms' => [],
|
||||
], $eventSchema);
|
||||
|
||||
$additionalInfoSchema = $onTicket->extras
|
||||
->firstWhere('codigo', 'additionalInfoConfig')
|
||||
->config_schema;
|
||||
$this->assertSame([
|
||||
'request_rules' => [
|
||||
'$' => 'required|array',
|
||||
'description' => 'nullable|string',
|
||||
],
|
||||
'transforms' => [],
|
||||
], $additionalInfoSchema);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,6 +195,8 @@ class AdminAppWebsiteExtraControllerTest extends TestCase
|
||||
|
||||
$this->patchJson('/api/v1/adminapp/tenant/website-extras/contactConfig/toggle')
|
||||
->assertOk()
|
||||
->assertJsonPath('code', 'tenant.website_extra_toggled')
|
||||
->assertJsonPath('message', 'El estado de la sección se actualizó correctamente.')
|
||||
->assertJsonPath('data.definitions.contactConfig.is_enabled', true);
|
||||
|
||||
$this->assertTrue($websiteExtra->refresh()->is_enabled);
|
||||
@@ -206,13 +208,21 @@ class AdminAppWebsiteExtraControllerTest extends TestCase
|
||||
$this->assertFalse($websiteExtra->refresh()->is_enabled);
|
||||
}
|
||||
|
||||
public function test_toggle_returns_not_found_for_an_extra_without_a_tenant_value(): void
|
||||
public function test_toggle_enables_an_extra_without_a_tenant_value(): void
|
||||
{
|
||||
$tenant = $this->createTenant('acme');
|
||||
Sanctum::actingAs($this->createAdminAppUser($tenant));
|
||||
|
||||
$this->patchJson('/api/v1/adminapp/tenant/website-extras/contactConfig/toggle')
|
||||
->assertNotFound();
|
||||
->assertOk()
|
||||
->assertJsonPath('data.definitions.contactConfig.is_enabled', true)
|
||||
->assertJsonPath('data.extras.contactConfig', []);
|
||||
|
||||
$this->assertDatabaseHas('websites_extras', [
|
||||
'website_code' => $tenant->codigo,
|
||||
'website_type_extra_id' => $this->websiteType->extras()->firstOrFail()->id,
|
||||
'is_enabled' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
private function createTenant(string $code): Tenant
|
||||
|
||||
63
tests/Feature/Tenant/BootstrapAdminAppControllerTest.php
Normal file
63
tests/Feature/Tenant/BootstrapAdminAppControllerTest.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Tenant;
|
||||
|
||||
use App\Domains\Attachable\Models\Attachment;
|
||||
use App\Domains\Tenant\Models\WebsiteType;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class BootstrapAdminAppControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_it_publicly_bootstraps_the_admin_app_by_domain(): void
|
||||
{
|
||||
$footerLogo = Attachment::factory()->create();
|
||||
|
||||
WebsiteType::query()->create([
|
||||
'codigo' => 'shopit',
|
||||
'nombre' => 'ShopIt',
|
||||
'dominio' => 'admin.shopit.test',
|
||||
'primary_color' => '#112233',
|
||||
'secondary_color' => '#445566',
|
||||
'danger_color' => '#aa0000',
|
||||
'success_color' => '#00aa00',
|
||||
'warning_color' => '#ffaa00',
|
||||
'body_color' => '#666666',
|
||||
'darker_body_color' => '#333333',
|
||||
'surface_color' => '#ffffff',
|
||||
'background_color' => '#f8f8f8',
|
||||
'border_color' => '#eaeaea',
|
||||
'login_header_footer_color' => '#313131',
|
||||
'footer_logo' => $footerLogo->id,
|
||||
]);
|
||||
|
||||
$this->getJson('/api/v1/adminapp/bootstrap/ADMIN.SHOPIT.TEST')
|
||||
->assertOk()
|
||||
->assertJsonPath('data.website_type_code', 'shopit')
|
||||
->assertJsonPath('data.primary_color', '#112233')
|
||||
->assertJsonPath('data.warning_color', '#ffaa00')
|
||||
->assertJsonPath('data.login_header_footer_color', '#313131')
|
||||
->assertJsonPath('data.site_logo', null)
|
||||
->assertJsonPath('data.footer_logo', $footerLogo->getTemporaryUrl(1440))
|
||||
->assertJsonMissingPath('data.codigo')
|
||||
->assertJsonMissingPath('data.nombre')
|
||||
->assertJsonMissingPath('data.dominio');
|
||||
}
|
||||
|
||||
public function test_it_returns_not_found_for_an_unknown_domain(): void
|
||||
{
|
||||
$this->getJson('/api/v1/adminapp/bootstrap/unknown.test')
|
||||
->assertNotFound();
|
||||
}
|
||||
|
||||
public function test_it_rejects_an_invalid_domain(): void
|
||||
{
|
||||
$invalidDomain = str_repeat('a', 256);
|
||||
|
||||
$this->getJson("/api/v1/adminapp/bootstrap/{$invalidDomain}")
|
||||
->assertUnprocessable()
|
||||
->assertJsonValidationErrors(['dominio']);
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,18 @@ class StoreTenantWithExtrasTest extends TestCase
|
||||
'title' => 'Festival',
|
||||
'location' => 'Buenos Aires',
|
||||
'dates_text' => '10 y 11 de octubre de 2026',
|
||||
'dates' => ['2026-10-10', '2026-10-11'],
|
||||
'dates' => [
|
||||
[
|
||||
'date' => '2026-10-10',
|
||||
'start_time' => '09:00',
|
||||
'end_time' => '18:00',
|
||||
],
|
||||
[
|
||||
'date' => '2026-10-11',
|
||||
'start_time' => '10:00',
|
||||
'end_time' => '17:00',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
]));
|
||||
@@ -52,7 +63,18 @@ class StoreTenantWithExtrasTest extends TestCase
|
||||
'title' => 'Festival',
|
||||
'location' => 'Buenos Aires',
|
||||
'dates_text' => '10 y 11 de octubre de 2026',
|
||||
'dates' => ['2026-10-10', '2026-10-11'],
|
||||
'dates' => [
|
||||
[
|
||||
'date' => '2026-10-10',
|
||||
'start_time' => '09:00',
|
||||
'end_time' => '18:00',
|
||||
],
|
||||
[
|
||||
'date' => '2026-10-11',
|
||||
'start_time' => '10:00',
|
||||
'end_time' => '17:00',
|
||||
],
|
||||
],
|
||||
],
|
||||
$tenant->websiteExtras()->sole()->config
|
||||
);
|
||||
@@ -80,12 +102,16 @@ class StoreTenantWithExtrasTest extends TestCase
|
||||
'website_type_code' => 'onticket',
|
||||
'extras' => [
|
||||
'eventConfig' => [
|
||||
'dates' => ['not-a-date'],
|
||||
'dates' => [[
|
||||
'date' => 'not-a-date',
|
||||
'start_time' => '09:00',
|
||||
'end_time' => '18:00',
|
||||
]],
|
||||
],
|
||||
],
|
||||
]))
|
||||
->assertUnprocessable()
|
||||
->assertJsonValidationErrors(['extras.eventConfig.dates.0']);
|
||||
->assertJsonValidationErrors(['extras.eventConfig.dates.0.date']);
|
||||
|
||||
$this->assertDatabaseMissing('tenants', ['codigo' => 'festival']);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,12 @@ class WebsiteExtrasTest extends TestCase
|
||||
'secondary_color',
|
||||
'danger_color',
|
||||
'success_color',
|
||||
'warning_color',
|
||||
'body_color',
|
||||
'darker_body_color',
|
||||
'surface_color',
|
||||
'background_color',
|
||||
'border_color',
|
||||
'login_header_footer_color',
|
||||
'site_logo',
|
||||
'created_at',
|
||||
@@ -73,6 +79,12 @@ class WebsiteExtrasTest extends TestCase
|
||||
'secondary_color' => '#222222',
|
||||
'danger_color' => '#ff0000',
|
||||
'success_color' => '#00aa55',
|
||||
'warning_color' => '#ffaa00',
|
||||
'body_color' => '#666666',
|
||||
'darker_body_color' => '#333333',
|
||||
'surface_color' => '#ffffff',
|
||||
'background_color' => '#f8f8f8',
|
||||
'border_color' => '#dddddd',
|
||||
'login_header_footer_color' => '#333333',
|
||||
'site_logo' => $siteLogo->id,
|
||||
]);
|
||||
@@ -100,6 +112,12 @@ class WebsiteExtrasTest extends TestCase
|
||||
$this->assertSame('#222222', $type->secondary_color);
|
||||
$this->assertSame('#ff0000', $type->danger_color);
|
||||
$this->assertSame('#00aa55', $type->success_color);
|
||||
$this->assertSame('#ffaa00', $type->warning_color);
|
||||
$this->assertSame('#666666', $type->body_color);
|
||||
$this->assertSame('#333333', $type->darker_body_color);
|
||||
$this->assertSame('#ffffff', $type->surface_color);
|
||||
$this->assertSame('#f8f8f8', $type->background_color);
|
||||
$this->assertSame('#dddddd', $type->border_color);
|
||||
$this->assertSame('#333333', $type->login_header_footer_color);
|
||||
$this->assertTrue($type->siteLogo()->firstOrFail()->is($siteLogo));
|
||||
$this->assertSame($type->codigo, $typeExtra->website_type_code);
|
||||
|
||||
@@ -12,7 +12,7 @@ class WebsiteTypeServiceTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_it_creates_a_website_type_and_uploads_its_logo_to_s3(): void
|
||||
public function test_it_creates_a_website_type_and_uploads_its_logos_to_s3(): void
|
||||
{
|
||||
Storage::fake('s3');
|
||||
|
||||
@@ -24,21 +24,39 @@ class WebsiteTypeServiceTest extends TestCase
|
||||
'secondary_color' => '#222222',
|
||||
'danger_color' => '#ff0000',
|
||||
'success_color' => '#00aa55',
|
||||
'warning_color' => '#ffaa00',
|
||||
'body_color' => '#666666',
|
||||
'darker_body_color' => '#333333',
|
||||
'surface_color' => '#ffffff',
|
||||
'background_color' => '#f8f8f8',
|
||||
'border_color' => '#dddddd',
|
||||
'login_header_footer_color' => '#333333',
|
||||
'site_logo' => UploadedFile::fake()->image('site-logo.png'),
|
||||
'footer_logo' => UploadedFile::fake()->image('footer-logo.png'),
|
||||
]);
|
||||
|
||||
$siteLogo = $websiteType->siteLogo()->firstOrFail();
|
||||
$footerLogo = $websiteType->footerLogo()->firstOrFail();
|
||||
|
||||
$this->assertSame('marketplace', $websiteType->codigo);
|
||||
$this->assertSame($siteLogo->id, $websiteType->site_logo);
|
||||
$this->assertSame($footerLogo->id, $websiteType->footer_logo);
|
||||
$this->assertStringStartsWith('website-types/', $siteLogo->path);
|
||||
$this->assertStringStartsWith('website-types/', $footerLogo->path);
|
||||
Storage::disk('s3')->assertExists($siteLogo->path);
|
||||
Storage::disk('s3')->assertExists($footerLogo->path);
|
||||
$this->assertDatabaseHas('website_type', [
|
||||
'codigo' => 'marketplace',
|
||||
'dominio' => 'marketplace.test',
|
||||
'warning_color' => '#ffaa00',
|
||||
'body_color' => '#666666',
|
||||
'darker_body_color' => '#333333',
|
||||
'surface_color' => '#ffffff',
|
||||
'background_color' => '#f8f8f8',
|
||||
'border_color' => '#dddddd',
|
||||
'login_header_footer_color' => '#333333',
|
||||
'site_logo' => $siteLogo->id,
|
||||
'footer_logo' => $footerLogo->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
25
tests/Unit/Tenant/BootstrapAdminAppResourceTest.php
Normal file
25
tests/Unit/Tenant/BootstrapAdminAppResourceTest.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Tenant;
|
||||
|
||||
use App\Domains\Tenant\Models\WebsiteType;
|
||||
use App\Domains\Tenant\Resources\AdminApp\BootstrapAdminAppResource;
|
||||
use Tests\TestCase;
|
||||
|
||||
class BootstrapAdminAppResourceTest extends TestCase
|
||||
{
|
||||
public function test_it_exposes_the_website_type_code(): void
|
||||
{
|
||||
$websiteType = new WebsiteType([
|
||||
'codigo' => 'shopit',
|
||||
'nombre' => 'ShopIt',
|
||||
'dominio' => 'admin.shopit.test',
|
||||
]);
|
||||
$websiteType->setRelation('siteLogo', null);
|
||||
|
||||
$data = BootstrapAdminAppResource::make($websiteType)->resolve(request());
|
||||
|
||||
$this->assertSame('shopit', $data['codigo']);
|
||||
$this->assertSame('shopit', $data['website_type_code']);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user