Skip to main content
SYNTIweb websites are built with 11 sections - 2 fixed (hero, footer) and 9 dynamic reorderable sections. Section availability is determined by the tenant’s plan level.

Section Overview

Fixed Sections (Always Rendered)

These sections are hardcoded outside the dynamic loop:

Hero

Plan Required: 1 (OPORTUNIDAD)
Position: Before main content
Configurable: Layout, images, content

Footer

Plan Required: 1 (OPORTUNIDAD)
Position: After main content
Configurable: Variant, social links

Dynamic Sections (Reorderable)

These 9 sections can be reordered and toggled via TenantCustomization.visual_effects['sections_order']:

Plan 1 (OPORTUNIDAD) - 5 Sections

Products

Required Plan: 1
Icon: tabler:shopping-cart
Label: “Productos”
Display product catalog with multiple variants (grid3, carousel, etc.)

Services

Required Plan: 1
Icon: tabler:tool
Label: “Servicios”
Service offerings with cards or list layouts

Contact

Required Plan: 1
Icon: tabler:map-pin
Label: “Contacto”
Contact information with map integration

Payment Methods

Required Plan: 1
Icon: tabler:credit-card
Label: “Medios de Pago”
Accepted payment methods display

CTA (Call-to-Action)

Required Plan: 1
Icon: tabler:send
Label: “Llamado a Acción”
Promotional banner with customizable button

Plan 2 (CRECIMIENTO) - +2 Sections

About

Required Plan: 2
Icon: tabler:info-circle
Label: “Acerca de”
About section with image and rich text

Testimonials

Required Plan: 2
Icon: tabler:message-star
Label: “Testimonios”
Customer testimonials carousel or grid

Plan 3 (VISIÓN) - +2 Sections

FAQ

Required Plan: 3
Icon: tabler:help-circle
Label: “FAQ”
Frequently asked questions accordion

Branches

Required Plan: 3
Icon: tabler:building-bank
Label: “Sucursales”
Multiple business locations display

Section Ordering System

Default Order

When no custom order is set, sections follow this default sequence:
// TenantCustomization.php:86-96
$order = [
    ['name' => 'products',        'visible' => true, 'order' => 0],
    ['name' => 'services',        'visible' => true, 'order' => 1],
    ['name' => 'contact',         'visible' => true, 'order' => 2],
    ['name' => 'payment_methods', 'visible' => true, 'order' => 3],
    ['name' => 'cta',             'visible' => true, 'order' => 4],
    ['name' => 'about',           'visible' => true, 'order' => 5],
    ['name' => 'testimonials',    'visible' => true, 'order' => 6],
    ['name' => 'faq',             'visible' => true, 'order' => 7],
    ['name' => 'branches',        'visible' => true, 'order' => 8],
];

Custom Ordering

Sections are stored in tenant_customization.visual_effects['sections_order'] as JSON:
{
  "sections_order": [
    {"name": "services", "visible": true, "order": 0},
    {"name": "products", "visible": false, "order": 1},
    {"name": "about", "visible": true, "order": 2}
  ]
}

Getting Available Sections

The Tenant model provides getAvailableSections() which filters sections by plan access:
// Tenant.php:242-266
public function getAvailableSections(): array
{
    $allSections = [
        'products'        => ['label' => 'Productos',        'icon' => 'tabler:shopping-cart',      'plan' => 1],
        'services'        => ['label' => 'Servicios',        'icon' => 'tabler:tool',               'plan' => 1],
        'contact'         => ['label' => 'Contacto',         'icon' => 'tabler:map-pin',            'plan' => 1],
        'payment_methods' => ['label' => 'Medios de Pago',   'icon' => 'tabler:credit-card',        'plan' => 1],
        'cta'             => ['label' => 'Llamado a Acción', 'icon' => 'tabler:send',               'plan' => 1],
        'about'           => ['label' => 'Acerca de',        'icon' => 'tabler:info-circle',        'plan' => 2],
        'testimonials'    => ['label' => 'Testimonios',      'icon' => 'tabler:message-star',       'plan' => 2],
        'faq'             => ['label' => 'FAQ',              'icon' => 'tabler:help-circle',        'plan' => 3],
        'branches'        => ['label' => 'Sucursales',       'icon' => 'tabler:building-bank',      'plan' => 3],
    ];

    // Filter by plan access
    $available = [];
    foreach ($allSections as $key => $section) {
        if ($this->customization && $this->customization->canAccessSection($key, $this->plan_id)) {
            $available[$key] = $section;
        }
    }

    return $available;
}
Hero and footer are NOT included in getAvailableSections() because they are rendered outside the dynamic loop (hero before main, footer after main).

Section Visibility

Checking Visibility

// TenantCustomization.php:164-169
public function isSectionVisible(string $section): bool
{
    $order = $this->getSectionsOrder();
    $sectionData = collect($order)->firstWhere('name', $section);
    return $sectionData['visible'] ?? true;
}

Plan-Based Access Control

// TenantCustomization.php:178-196
public function canAccessSection(string $section, int $planId): bool
{
    $planRequirements = [
        'hero'            => 1,
        'products'        => 1,
        'services'        => 1,
        'contact'         => 1,
        'payment_methods' => 1,
        'cta'             => 1,
        'footer'          => 1,
        'about'           => 2,
        'testimonials'    => 2,
        'faq'             => 3,
        'branches'        => 3,
    ];

    $requiredPlan = $planRequirements[$section] ?? 1;
    return $planId >= $requiredPlan;
}
Even if a section is marked as visible: true, it won’t render if the tenant’s plan doesn’t meet the minimum requirement.

Section Configuration

Default Configurations

Each section has default settings stored in TenantCustomization:
// TenantCustomization.php:134-146
$defaults = [
    'hero'            => ['variant' => 'fullscreen',  'visible' => true],
    'products'        => ['variant' => 'grid3',       'visible' => true, 'border' => 'rounded', 'effect' => 'none',     'spacing' => 'normal'],
    'services'        => ['variant' => 'cards',       'visible' => true, 'border' => 'rounded', 'effect' => 'glow',     'spacing' => 'airy'],
    'about'           => ['variant' => 'split',       'visible' => true, 'border' => 'rounded', 'effect' => 'none',     'spacing' => 'normal'],
    'contact'         => ['variant' => 'map',         'visible' => true, 'border' => 'rounded', 'effect' => 'none',     'spacing' => 'normal'],
    'payment_methods' => ['variant' => 'grid',        'visible' => true, 'border' => 'rounded', 'effect' => 'none',     'spacing' => 'normal'],
    'testimonials'    => ['variant' => 'carousel',    'visible' => true, 'border' => 'rounded', 'effect' => 'none',     'spacing' => 'normal'],
    'faq'             => ['variant' => 'accordion',   'visible' => true, 'border' => 'pill',    'effect' => 'none',     'spacing' => 'normal'],
    'branches'        => ['variant' => 'cards',       'visible' => true, 'border' => 'rounded', 'effect' => 'none',     'spacing' => 'normal'],
    'cta'             => ['variant' => 'centered',    'visible' => true, 'border' => 'rounded', 'effect' => 'gradient', 'spacing' => 'airy'],
    'footer'          => ['variant' => 'simple',      'visible' => true],
];

Getting Section Config

// TenantCustomization.php:127-155
public function getSectionConfig(string $section): array
{
    $defaultConfig = $defaults[$section] ?? ['visible' => true];

    if (empty($this->visual_effects) || !isset($this->visual_effects['sections_config'][$section])) {
        return $defaultConfig;
    }

    return array_merge($defaultConfig, $this->visual_effects['sections_config'][$section]);
}

Updating Section Config

// TenantCustomization.php:205-220
public function updateSectionConfig(string $section, array $newConfig): void
{
    $visualEffects = $this->visual_effects ?? [
        'sections_order' => [],
        'sections_config' => [],
    ];

    if (!isset($visualEffects['sections_config'])) {
        $visualEffects['sections_config'] = [];
    }

    $currentConfig = $visualEffects['sections_config'][$section] ?? [];
    $visualEffects['sections_config'][$section] = array_merge($currentConfig, $newConfig);

    $this->update(['visual_effects' => $visualEffects]);
}

Configuration Options

Each section supports multiple configuration properties:
Section layout variant (e.g., grid3, carousel, cards, split, accordion)
Boolean - Controls whether the section renders on the page
Border style (e.g., rounded, pill, square)
Visual effects (e.g., none, glow, gradient, shadow)
Section spacing (e.g., normal, airy, compact)
Integer position in the sections sequence (0-indexed)

Architecture Notes

Why are hero and footer excluded from reordering?They serve as structural anchors:
  • Hero: Always first (before <main>)
  • Footer: Always last (after </main>)
This ensures consistent page structure and SEO best practices.
To check all available sections for a tenant:
$tenant = Tenant::find($id);
$sections = $tenant->getAvailableSections();
// Returns only sections accessible by the tenant's plan

Tenant Model

app/Models/Tenant.php:242-266
Contains getAvailableSections()

TenantCustomization Model

app/Models/TenantCustomization.php
Manages section visibility and config

Plan Constants

app/Models/Plan.php:15-22
OPORTUNIDAD = 1, CRECIMIENTO = 2, VISION = 3

Build docs developers (and LLMs) love