Compare commits
2 Commits
5ada399adf
...
460eec3c78
| Author | SHA1 | Date | |
|---|---|---|---|
| 460eec3c78 | |||
| 2b1ef5f8a9 |
32
app/Domains/Menu/Controllers/TenantMenuController.php
Normal file
32
app/Domains/Menu/Controllers/TenantMenuController.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Menu\Controllers;
|
||||
|
||||
use App\Domains\Menu\Models\Menu;
|
||||
use App\Domains\Menu\Requests\StoreTenantMenuRequest;
|
||||
use App\Domains\Menu\Services\TenantMenuService;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class TenantMenuController extends Controller
|
||||
{
|
||||
public function __construct(private readonly TenantMenuService $tenantMenuService) {}
|
||||
|
||||
public function store(
|
||||
StoreTenantMenuRequest $request,
|
||||
string $tenantCode,
|
||||
string $menuCode,
|
||||
): JsonResponse {
|
||||
$tenant = Tenant::query()->where('codigo', $tenantCode)->firstOrFail();
|
||||
$menu = Menu::query()->where('code', $menuCode)->firstOrFail();
|
||||
|
||||
$tenantMenu = $this->tenantMenuService->configure(
|
||||
$tenant,
|
||||
$menu,
|
||||
$request->validated('static_content'),
|
||||
);
|
||||
|
||||
return response()->json($tenantMenu);
|
||||
}
|
||||
}
|
||||
49
app/Domains/Menu/Requests/StoreTenantMenuRequest.php
Normal file
49
app/Domains/Menu/Requests/StoreTenantMenuRequest.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Menu\Requests;
|
||||
|
||||
use App\Domains\Menu\Models\Menu;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class StoreTenantMenuRequest extends FormRequest
|
||||
{
|
||||
private ?Menu $menuModel = null;
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function prepareForValidation(): void
|
||||
{
|
||||
$this->menuModel = Menu::query()
|
||||
->where('code', $this->route('menu_code'))
|
||||
->first();
|
||||
|
||||
if (! $this->menuModel) {
|
||||
throw ValidationException::withMessages([
|
||||
'menu_code' => 'El menú indicado no existe.',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
if ($this->menuModel?->content_type !== Menu::CONTENT_TYPE_STATIC) {
|
||||
return [
|
||||
'static_content' => ['nullable', 'array'],
|
||||
];
|
||||
}
|
||||
|
||||
$rules = [
|
||||
'static_content' => ['required', 'array'],
|
||||
];
|
||||
|
||||
foreach ($this->menuModel->static_content_schema as $field => $rule) {
|
||||
$rules["static_content.{$field}"] = $rule;
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
}
|
||||
26
app/Domains/Menu/Services/TenantMenuService.php
Normal file
26
app/Domains/Menu/Services/TenantMenuService.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Menu\Services;
|
||||
|
||||
use App\Domains\Menu\Models\Menu;
|
||||
use App\Domains\Menu\Models\TenantMenu;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class TenantMenuService
|
||||
{
|
||||
public function configure(Tenant $tenant, Menu $menu, ?array $staticContent): TenantMenu
|
||||
{
|
||||
return DB::transaction(fn () => TenantMenu::query()->updateOrCreate(
|
||||
[
|
||||
'tenant_code' => $tenant->codigo,
|
||||
'menu_code' => $menu->code,
|
||||
],
|
||||
[
|
||||
'static_content' => $menu->content_type === Menu::CONTENT_TYPE_STATIC
|
||||
? $staticContent
|
||||
: null,
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,12 @@
|
||||
<?php
|
||||
|
||||
use App\Domains\Menu\Controllers\MenuController;
|
||||
use App\Domains\Menu\Controllers\TenantMenuController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::apiResource('menues', MenuController::class);
|
||||
|
||||
Route::post(
|
||||
'{tenant_code}/menues/{menu_code}',
|
||||
[TenantMenuController::class, 'store']
|
||||
);
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
namespace App\Domains\Tenant\Resources;
|
||||
|
||||
use App\Domains\Menu\Models\Menu;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* @mixin Tenant
|
||||
@@ -55,7 +57,49 @@ class TenantResource extends JsonResource
|
||||
])
|
||||
->values()
|
||||
),
|
||||
'menues' => $this->whenLoaded('menues'),
|
||||
'menues' => $this->whenLoaded(
|
||||
'menues',
|
||||
fn () => $this->menuTree($this->menues)
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection<int, Menu> $menus
|
||||
* @return Collection<int, array<string, mixed>>
|
||||
*/
|
||||
private function menuTree(Collection $menus): Collection
|
||||
{
|
||||
$menuCodes = $menus->pluck('code')->flip();
|
||||
$childrenByParent = $menus
|
||||
->filter(fn (Menu $menu) => $menu->parent_menu_code !== null)
|
||||
->groupBy('parent_menu_code');
|
||||
|
||||
$formatMenu = function (Menu $menu) use (&$formatMenu, $childrenByParent): array {
|
||||
$formatted = [
|
||||
'id' => $menu->id,
|
||||
'code' => $menu->code,
|
||||
'parent_menu_code' => $menu->parent_menu_code,
|
||||
'content_type' => $menu->content_type,
|
||||
'route' => $menu->route,
|
||||
];
|
||||
|
||||
if ($menu->pivot?->static_content !== null) {
|
||||
$formatted['static_content'] = $menu->pivot->static_content;
|
||||
}
|
||||
|
||||
$formatted['submenues'] = $childrenByParent
|
||||
->get($menu->code, collect())
|
||||
->map($formatMenu)
|
||||
->values();
|
||||
|
||||
return $formatted;
|
||||
};
|
||||
|
||||
return $menus
|
||||
->filter(fn (Menu $menu) => $menu->parent_menu_code === null
|
||||
|| ! $menuCodes->has($menu->parent_menu_code))
|
||||
->map($formatMenu)
|
||||
->values();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Domains\Menu\Models\Menu;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class MenuSeeder extends Seeder
|
||||
{
|
||||
@@ -20,15 +20,123 @@ class MenuSeeder extends Seeder
|
||||
['code' => 'profile', 'route' => '/profile'],
|
||||
['code' => 'purchases', 'route' => '/purchases'],
|
||||
['code' => 'tickets', 'route' => '/tickets'],
|
||||
[
|
||||
'code' => 'help',
|
||||
'content_type' => Menu::CONTENT_TYPE_DYNAMIC,
|
||||
'route' => '/ayuda',
|
||||
],
|
||||
[
|
||||
'code' => 'help.faq',
|
||||
'parent_menu_code' => 'help',
|
||||
'content_type' => Menu::CONTENT_TYPE_STATIC,
|
||||
'static_content_schema' => [
|
||||
'*.pregunta' => 'required|string',
|
||||
'*.respuesta' => 'required|string',
|
||||
'*.is_active' => 'sometimes|boolean',
|
||||
],
|
||||
'route' => '/ayuda/preguntas-frecuentes',
|
||||
],
|
||||
[
|
||||
'code' => 'help.contact',
|
||||
'parent_menu_code' => 'help',
|
||||
'content_type' => Menu::CONTENT_TYPE_STATIC,
|
||||
'static_content_schema' => [
|
||||
'whatsapp' => 'required|array',
|
||||
'whatsapp.whatsapp_url' => 'required|url',
|
||||
'whatsapp.whatsapp_label' => 'required|string',
|
||||
'phone' => 'required|string',
|
||||
'locations' => 'required|array|min:1',
|
||||
'locations.*.label' => 'required|string',
|
||||
'locations.*.addresses' => 'required|array|min:1',
|
||||
'locations.*.addresses.*' => 'required|string',
|
||||
'map_locations' => 'required|array',
|
||||
'map_locations.*' => 'required|array|size:2',
|
||||
'map_locations.*.*' => 'required|numeric',
|
||||
],
|
||||
'route' => '/ayuda/contacto',
|
||||
],
|
||||
[
|
||||
'code' => 'help.payment-methods',
|
||||
'parent_menu_code' => 'help',
|
||||
'content_type' => Menu::CONTENT_TYPE_DYNAMIC,
|
||||
'route' => '/ayuda/medios-de-pago',
|
||||
],
|
||||
[
|
||||
'code' => 'help.shipping',
|
||||
'parent_menu_code' => 'help',
|
||||
'content_type' => Menu::CONTENT_TYPE_DYNAMIC,
|
||||
'route' => '/ayuda/envios',
|
||||
],
|
||||
[
|
||||
'code' => 'help.terms-and-conditions',
|
||||
'parent_menu_code' => 'help',
|
||||
'content_type' => Menu::CONTENT_TYPE_DYNAMIC,
|
||||
'route' => '/ayuda/terminos-y-condiciones',
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($menus as $menuData) {
|
||||
Menu::firstOrCreate(['code' => $menuData['code']], $menuData);
|
||||
$menuData = array_merge([
|
||||
'parent_menu_code' => null,
|
||||
'content_type' => Menu::CONTENT_TYPE_DYNAMIC,
|
||||
'static_content_schema' => null,
|
||||
], $menuData);
|
||||
|
||||
Menu::updateOrCreate(['code' => $menuData['code']], $menuData);
|
||||
}
|
||||
|
||||
$tenants = Tenant::all();
|
||||
|
||||
$allMenus = Menu::pluck('code')->toArray();
|
||||
$helpMenuCodes = [
|
||||
'help',
|
||||
'help.faq',
|
||||
'help.contact',
|
||||
'help.payment-methods',
|
||||
'help.shipping',
|
||||
'help.terms-and-conditions',
|
||||
];
|
||||
$helpTenantCodes = [
|
||||
'sonder',
|
||||
'fiesta_futbol_infantil',
|
||||
];
|
||||
$frequentlyAskedQuestions = [
|
||||
[
|
||||
'pregunta' => '¿Hay algún límite de compra?',
|
||||
'respuesta' => 'No hay un límite general de compra. La cantidad disponible depende del stock de cada producto.',
|
||||
'is_active' => true,
|
||||
],
|
||||
[
|
||||
'pregunta' => '¿Cuáles son los medios de pago disponibles?',
|
||||
'respuesta' => 'Podés consultar y seleccionar los medios de pago habilitados al momento de finalizar tu compra.',
|
||||
'is_active' => false,
|
||||
],
|
||||
[
|
||||
'pregunta' => 'Necesito asesoramiento antes de comprar el producto, ¿cómo puedo hacer?',
|
||||
'respuesta' => 'Nuestro equipo comercial está preparado para asesorarte, no dudes en escribirnos por WhatsApp al (0341) 6658247 de lunes a viernes de 8.30 a 20 horas. Los sábados, domingos y feriados te contestaremos lo más rápido posible.',
|
||||
'is_active' => false,
|
||||
],
|
||||
];
|
||||
$contactContent = [
|
||||
'whatsapp' => [
|
||||
'whatsapp_url' => 'https://wa.me/543412602222',
|
||||
'whatsapp_label' => 'Chatea con nosotros',
|
||||
],
|
||||
'phone' => '+54 9 (0341) 6658247',
|
||||
'locations' => [
|
||||
'rosario' => [
|
||||
'label' => 'Rosario',
|
||||
'addresses' => [
|
||||
'Av. San Lorenzo 1542',
|
||||
],
|
||||
],
|
||||
],
|
||||
'map_locations' => [
|
||||
'gigante_de_arroyito' => [-32.913997, -60.674567],
|
||||
'punto_centro' => [-32.946820, -60.639320],
|
||||
'punto_sur' => [-32.971510, -60.650640],
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($tenants as $tenant) {
|
||||
$menuCodes = $allMenus;
|
||||
@@ -41,8 +149,21 @@ class MenuSeeder extends Seeder
|
||||
$menuCodes = array_diff($menuCodes, ['product.detail']);
|
||||
}
|
||||
|
||||
if (! in_array($tenant->codigo, $helpTenantCodes, true)) {
|
||||
$menuCodes = array_diff($menuCodes, $helpMenuCodes);
|
||||
}
|
||||
|
||||
// Usar sync para asociar los menues al tenant
|
||||
$tenant->menues()->sync($menuCodes);
|
||||
|
||||
if (in_array($tenant->codigo, $helpTenantCodes, true)) {
|
||||
$tenant->menues()->updateExistingPivot('help.faq', [
|
||||
'static_content' => $frequentlyAskedQuestions,
|
||||
]);
|
||||
$tenant->menues()->updateExistingPivot('help.contact', [
|
||||
'static_content' => $contactContent,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
130
tests/Feature/Menu/TenantMenuControllerTest.php
Normal file
130
tests/Feature/Menu/TenantMenuControllerTest.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Menu;
|
||||
|
||||
use App\Domains\Attachable\Enums\AttachmentType;
|
||||
use App\Domains\Attachable\Models\Attachment;
|
||||
use App\Domains\Menu\Models\Menu;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class TenantMenuControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_it_validates_static_content_with_the_menu_schema(): void
|
||||
{
|
||||
$tenant = $this->createTenant();
|
||||
$menu = Menu::query()->create([
|
||||
'code' => 'about',
|
||||
'content_type' => Menu::CONTENT_TYPE_STATIC,
|
||||
'static_content_schema' => [
|
||||
'title' => 'required|string|max:20',
|
||||
'sections' => 'required|array|min:1',
|
||||
'sections.*.body' => 'required|string',
|
||||
],
|
||||
'route' => '/about',
|
||||
]);
|
||||
|
||||
$this->postJson("/api/{$tenant->codigo}/menues/{$menu->code}", [
|
||||
'static_content' => [
|
||||
'title' => 123,
|
||||
'sections' => [],
|
||||
],
|
||||
])
|
||||
->assertUnprocessable()
|
||||
->assertJsonValidationErrors([
|
||||
'static_content.title',
|
||||
'static_content.sections',
|
||||
]);
|
||||
|
||||
$this->assertDatabaseMissing('tenants_menues', [
|
||||
'tenant_code' => $tenant->codigo,
|
||||
'menu_code' => $menu->code,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_it_configures_a_static_menu_when_content_matches_the_schema(): void
|
||||
{
|
||||
$tenant = $this->createTenant();
|
||||
$menu = Menu::query()->create([
|
||||
'code' => 'about',
|
||||
'content_type' => Menu::CONTENT_TYPE_STATIC,
|
||||
'static_content_schema' => [
|
||||
'title' => 'required|string|max:20',
|
||||
'sections' => 'required|array|min:1',
|
||||
'sections.*.body' => 'required|string',
|
||||
],
|
||||
'route' => '/about',
|
||||
]);
|
||||
|
||||
$content = [
|
||||
'title' => 'Nosotros',
|
||||
'sections' => [
|
||||
['body' => 'Nuestra historia'],
|
||||
],
|
||||
];
|
||||
|
||||
$this->postJson("/api/{$tenant->codigo}/menues/{$menu->code}", [
|
||||
'static_content' => $content,
|
||||
])
|
||||
->assertOk()
|
||||
->assertJsonPath('static_content.title', 'Nosotros')
|
||||
->assertJsonPath('static_content.sections.0.body', 'Nuestra historia');
|
||||
|
||||
$this->assertSame(
|
||||
$content,
|
||||
$tenant->menues()->where('menues.code', $menu->code)->firstOrFail()->pivot->static_content
|
||||
);
|
||||
}
|
||||
|
||||
public function test_dynamic_menus_do_not_require_static_content(): void
|
||||
{
|
||||
$tenant = $this->createTenant();
|
||||
$menu = Menu::query()->create([
|
||||
'code' => 'catalog',
|
||||
'route' => '/catalog',
|
||||
]);
|
||||
|
||||
$this->postJson("/api/{$tenant->codigo}/menues/{$menu->code}")
|
||||
->assertOk()
|
||||
->assertJsonPath('static_content', null);
|
||||
|
||||
$this->assertDatabaseHas('tenants_menues', [
|
||||
'tenant_code' => $tenant->codigo,
|
||||
'menu_code' => $menu->code,
|
||||
'static_content' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
private function createTenant(): Tenant
|
||||
{
|
||||
$headerLogo = $this->createAttachment('header.png');
|
||||
$footerLogo = $this->createAttachment('footer.png');
|
||||
|
||||
return Tenant::query()->create([
|
||||
'codigo' => 'acme',
|
||||
'nombre' => 'Acme',
|
||||
'dominio' => 'acme.com',
|
||||
'primary_color' => '#111111',
|
||||
'secondary_color' => '#222222',
|
||||
'danger_color' => '#333333',
|
||||
'success_color' => '#444444',
|
||||
'header_bg_color' => '#ffffff',
|
||||
'footer_bg_color' => '#ffffff',
|
||||
'header_logo_id' => $headerLogo->id,
|
||||
'footer_logo_id' => $footerLogo->id,
|
||||
]);
|
||||
}
|
||||
|
||||
private function createAttachment(string $filename): Attachment
|
||||
{
|
||||
return Attachment::query()->create([
|
||||
'path' => "test/{$filename}",
|
||||
'filename' => $filename,
|
||||
'type' => AttachmentType::Image,
|
||||
'mime_type' => 'image/png',
|
||||
]);
|
||||
}
|
||||
}
|
||||
155
tests/Feature/Seeders/MenuSeederTest.php
Normal file
155
tests/Feature/Seeders/MenuSeederTest.php
Normal file
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Seeders;
|
||||
|
||||
use App\Domains\Attachable\Enums\AttachmentType;
|
||||
use App\Domains\Attachable\Models\Attachment;
|
||||
use App\Domains\Menu\Models\Menu;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Database\Seeders\MenuSeeder;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class MenuSeederTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_it_seeds_the_help_menu_hierarchy_and_content_schemas(): void
|
||||
{
|
||||
$this->seed(MenuSeeder::class);
|
||||
|
||||
$help = Menu::query()
|
||||
->with('children')
|
||||
->where('code', 'help')
|
||||
->firstOrFail();
|
||||
|
||||
$this->assertSame(Menu::CONTENT_TYPE_DYNAMIC, $help->content_type);
|
||||
$this->assertSame([
|
||||
'help.contact',
|
||||
'help.faq',
|
||||
'help.payment-methods',
|
||||
'help.shipping',
|
||||
'help.terms-and-conditions',
|
||||
], $help->children->pluck('code')->sort()->values()->all());
|
||||
|
||||
$faq = Menu::query()->where('code', 'help.faq')->firstOrFail();
|
||||
$this->assertSame(Menu::CONTENT_TYPE_STATIC, $faq->content_type);
|
||||
$this->assertEquals([
|
||||
'*.pregunta' => 'required|string',
|
||||
'*.respuesta' => 'required|string',
|
||||
'*.is_active' => 'sometimes|boolean',
|
||||
], $faq->static_content_schema);
|
||||
|
||||
$contact = Menu::query()->where('code', 'help.contact')->firstOrFail();
|
||||
$this->assertSame(Menu::CONTENT_TYPE_STATIC, $contact->content_type);
|
||||
$this->assertSame('required|url', $contact->static_content_schema['whatsapp.whatsapp_url']);
|
||||
$this->assertSame('required|string', $contact->static_content_schema['phone']);
|
||||
$this->assertSame(
|
||||
'required|array|min:1',
|
||||
$contact->static_content_schema['locations.*.addresses']
|
||||
);
|
||||
$this->assertSame(
|
||||
'required|array|size:2',
|
||||
$contact->static_content_schema['map_locations.*']
|
||||
);
|
||||
}
|
||||
|
||||
public function test_it_assigns_help_to_the_supported_tenants_with_faq_content(): void
|
||||
{
|
||||
$sonder = $this->createTenant('sonder');
|
||||
$fiesta = $this->createTenant('fiesta_futbol_infantil');
|
||||
$otherTenant = $this->createTenant('other');
|
||||
|
||||
$this->seed(MenuSeeder::class);
|
||||
|
||||
$expectedQuestions = [
|
||||
[
|
||||
'pregunta' => '¿Hay algún límite de compra?',
|
||||
'respuesta' => 'No hay un límite general de compra. La cantidad disponible depende del stock de cada producto.',
|
||||
'is_active' => true,
|
||||
],
|
||||
[
|
||||
'pregunta' => '¿Cuáles son los medios de pago disponibles?',
|
||||
'respuesta' => 'Podés consultar y seleccionar los medios de pago habilitados al momento de finalizar tu compra.',
|
||||
'is_active' => false,
|
||||
],
|
||||
[
|
||||
'pregunta' => 'Necesito asesoramiento antes de comprar el producto, ¿cómo puedo hacer?',
|
||||
'respuesta' => 'Nuestro equipo comercial está preparado para asesorarte, no dudes en escribirnos por WhatsApp al (0341) 6658247 de lunes a viernes de 8.30 a 20 horas. Los sábados, domingos y feriados te contestaremos lo más rápido posible.',
|
||||
'is_active' => false,
|
||||
],
|
||||
];
|
||||
$expectedContact = [
|
||||
'whatsapp' => [
|
||||
'whatsapp_url' => 'https://wa.me/543412602222',
|
||||
'whatsapp_label' => 'Chatea con nosotros',
|
||||
],
|
||||
'phone' => '+54 9 (0341) 6658247',
|
||||
'locations' => [
|
||||
'rosario' => [
|
||||
'label' => 'Rosario',
|
||||
'addresses' => [
|
||||
'Av. San Lorenzo 1542',
|
||||
],
|
||||
],
|
||||
],
|
||||
'map_locations' => [
|
||||
'gigante_de_arroyito' => [-32.913997, -60.674567],
|
||||
'punto_centro' => [-32.946820, -60.639320],
|
||||
'punto_sur' => [-32.971510, -60.650640],
|
||||
],
|
||||
];
|
||||
|
||||
foreach ([$sonder, $fiesta] as $tenant) {
|
||||
$helpMenus = $tenant->menues()
|
||||
->where(fn ($query) => $query
|
||||
->where('menues.code', 'help')
|
||||
->orWhere('menues.parent_menu_code', 'help'))
|
||||
->get();
|
||||
|
||||
$this->assertCount(6, $helpMenus);
|
||||
$this->assertEquals(
|
||||
$expectedQuestions,
|
||||
$helpMenus->firstWhere('code', 'help.faq')->pivot->static_content
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expectedContact,
|
||||
$helpMenus->firstWhere('code', 'help.contact')->pivot->static_content
|
||||
);
|
||||
}
|
||||
|
||||
$this->assertFalse(
|
||||
$otherTenant->menues()->where('menues.code', 'help')->exists()
|
||||
);
|
||||
}
|
||||
|
||||
private function createTenant(string $code): Tenant
|
||||
{
|
||||
$headerLogo = $this->createAttachment("{$code}-header.png");
|
||||
$footerLogo = $this->createAttachment("{$code}-footer.png");
|
||||
|
||||
return Tenant::query()->create([
|
||||
'codigo' => $code,
|
||||
'nombre' => $code,
|
||||
'dominio' => "{$code}.example.com",
|
||||
'primary_color' => '#111111',
|
||||
'secondary_color' => '#222222',
|
||||
'danger_color' => '#333333',
|
||||
'success_color' => '#444444',
|
||||
'header_bg_color' => '#ffffff',
|
||||
'footer_bg_color' => '#ffffff',
|
||||
'header_logo_id' => $headerLogo->id,
|
||||
'footer_logo_id' => $footerLogo->id,
|
||||
]);
|
||||
}
|
||||
|
||||
private function createAttachment(string $filename): Attachment
|
||||
{
|
||||
return Attachment::query()->create([
|
||||
'path' => "test/{$filename}",
|
||||
'filename' => $filename,
|
||||
'type' => AttachmentType::Image,
|
||||
'mime_type' => 'image/png',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Tests\Feature\Tenant;
|
||||
|
||||
use App\Domains\Menu\Models\Menu;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
@@ -99,6 +100,73 @@ class BootstrapTenantControllerTest extends TestCase
|
||||
$response->assertNotFound();
|
||||
}
|
||||
|
||||
public function test_it_returns_tenant_menus_as_a_clean_hierarchy(): void
|
||||
{
|
||||
$tenant = $this->createTenant();
|
||||
$parent = Menu::query()->create([
|
||||
'code' => 'help',
|
||||
'content_type' => Menu::CONTENT_TYPE_DYNAMIC,
|
||||
'route' => '/ayuda',
|
||||
]);
|
||||
$staticChild = Menu::query()->create([
|
||||
'code' => 'help.faq',
|
||||
'parent_menu_code' => $parent->code,
|
||||
'content_type' => Menu::CONTENT_TYPE_STATIC,
|
||||
'static_content_schema' => [
|
||||
'*.question' => 'required|string',
|
||||
'*.answer' => 'required|string',
|
||||
],
|
||||
'route' => '/ayuda/preguntas-frecuentes',
|
||||
]);
|
||||
$dynamicChild = Menu::query()->create([
|
||||
'code' => 'help.shipping',
|
||||
'parent_menu_code' => $parent->code,
|
||||
'content_type' => Menu::CONTENT_TYPE_DYNAMIC,
|
||||
'route' => '/ayuda/envios',
|
||||
]);
|
||||
$staticContent = [
|
||||
[
|
||||
'question' => '¿Cómo compro?',
|
||||
'answer' => 'Desde el catálogo.',
|
||||
],
|
||||
];
|
||||
|
||||
$tenant->menues()->sync([
|
||||
$parent->code,
|
||||
$staticChild->code => ['static_content' => $staticContent],
|
||||
$dynamicChild->code,
|
||||
]);
|
||||
|
||||
$response = $this->getJson('/api/tenants/bootstrap/acme.com');
|
||||
|
||||
$response
|
||||
->assertOk()
|
||||
->assertJsonCount(1, 'data.menues')
|
||||
->assertJsonCount(2, 'data.menues.0.submenues')
|
||||
->assertJsonPath('data.menues.0.code', 'help')
|
||||
->assertJsonPath('data.menues.0.parent_menu_code', null)
|
||||
->assertJsonPath('data.menues.0.submenues.0.code', 'help.faq')
|
||||
->assertJsonPath('data.menues.0.submenues.0.parent_menu_code', 'help')
|
||||
->assertJsonPath('data.menues.0.submenues.1.code', 'help.shipping');
|
||||
|
||||
$assertCleanMenu = function (array $menu) use (&$assertCleanMenu): void {
|
||||
$this->assertArrayNotHasKey('created_at', $menu);
|
||||
$this->assertArrayNotHasKey('updated_at', $menu);
|
||||
$this->assertArrayNotHasKey('pivot', $menu);
|
||||
$this->assertArrayNotHasKey('static_content_schema', $menu);
|
||||
|
||||
foreach ($menu['submenues'] as $submenu) {
|
||||
$assertCleanMenu($submenu);
|
||||
}
|
||||
};
|
||||
|
||||
$menus = $response->json('data.menues');
|
||||
$this->assertEquals($staticContent, $menus[0]['submenues'][0]['static_content']);
|
||||
$assertCleanMenu($menus[0]);
|
||||
$this->assertArrayNotHasKey('static_content', $menus[0]);
|
||||
$this->assertArrayNotHasKey('static_content', $menus[0]['submenues'][1]);
|
||||
}
|
||||
|
||||
public function test_it_rejects_duplicate_domains_after_normalization_when_storing(): void
|
||||
{
|
||||
$base64Image = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
|
||||
|
||||
Reference in New Issue
Block a user