• 17 Apr, 2025

Building an Admin-Controlled, Customizable Pop-up Box in Laravel

Building an Admin-Controlled, Customizable Pop-up Box in Laravel

Learn how to implement a versatile pop-up box in Laravel, designed to showcase custom images and text content on your website's frontend. A dedicated admin panel empowers administrators to easily modify the pop-up's appearance, including colors, size, content, and visibility status. This provides a code-free way for non-developers to manage website announcements or promotions directly.

This guide details how to build a dynamic pop-up box feature for a Laravel website. The pop-up is designed to display both images and text content to engage visitors upon landing on pages like the homepage. A key aspect is the deep customization managed through an easy-to-use admin panel. Administrators can modify the pop-up's image, text, colors (background and text), dimensions (width/height), and toggle its visibility without touching code. This allows non-developers to easily update website announcements, promotions, or alerts directly via the backend interface.

Customizable Pop-up Box in Laravel

Create a customizable pop-up image box feature for your Laravel application.

This will involve:

  1. Backend Setup (Laravel):
    • A Model and Migration for storing pop-up settings.
    • A Controller for managing these settings in the admin panel.
    • Routes for the admin panel and potentially fetching data for the frontend.
    • Admin Views for the form.
  2. Frontend Setup:
    • A Blade component or partial for the pop-up structure.
    • CSS for styling (including dynamic styles).
    • JavaScript for controlling the pop-up visibility and behavior.

Step 1: Backend Setup (Laravel)

1.1. Create Model and Migration:

Open your terminal in your Laravel project directory and run:

Bash
php artisan make:model PopupSetting -m

This creates:

  • app/Models/PopupSetting.php (Model)
  • database/migrations/YYYY_MM_DD_HHMMSS_create_popup_settings_table.php (Migration)

Now, edit the generated migration file (database/migrations/YYYY_MM_DD_HHMMSS_create_popup_settings_table.php):

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('popup_settings', function (Blueprint $table) {
            $table->id();
            $table->string('image_path')->nullable(); // Path to the image
            $table->text('text')->nullable();          // Pop-up text/content
            $table->string('background_color')->default('#FFFFFF'); // Background color
            $table->string('text_color')->default('#000000');       // Text color
            $table->string('width')->default('500px'); // e.g., '500px', '80%'
            $table->string('height')->default('auto'); // e.g., '300px', 'auto'
            $table->boolean('is_active')->default(false); // Controls if the pop-up is shown
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('popup_settings');
    }
};

Edit the Model (app/Models/PopupSetting.php) to allow mass assignment:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class PopupSetting extends Model
{
    use HasFactory;

    // Allow these fields to be mass-assigned
    protected $fillable = [
        'image_path',
        'text',
        'background_color',
        'text_color',
        'width',
        'height',
        'is_active',
    ];

    // Cast boolean value correctly
    protected $casts = [
        'is_active' => 'boolean',
    ];
}

Run the migration:

Bash
php artisan migrate

1.2. Create Admin Controller:

Bash
php artisan make:controller Admin/PopupSettingController --resource

(Note: We'll mainly use edit and update for a single settings record).

Edit app/Http/Controllers/Admin/PopupSettingController.php:

<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\PopupSetting;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage; // <-- Import Storage facade
use Illuminate\Validation\Rule;       // <-- Import Rule for validation

class PopupSettingController extends Controller
{
    // We assume there's only one pop-up setting record needed for the site.
    // We'll use ID 1, or create it if it doesn't exist.

    /**
     * Show the form for editing the specified resource.
     */
    public function edit() // Simplified: Always edit the first record
    {
        // Find the first setting, or create a default one if none exists
        $setting = PopupSetting::firstOrCreate(['id' => 1]);
        return view('admin.popup.edit', compact('setting'));
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request) // Simplified: Always update the first record
    {
        $setting = PopupSetting::findOrFail(1); // Find setting with ID 1

        $validated = $request->validate([
            'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg,webp|max:2048', // Max 2MB
            'text' => 'nullable|string',
            'background_color' => 'required|string|regex:/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$|^rgba?\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*(,\s*(0|1|0?\.\d+))?\s*\)$/i', // Validate hex or rgba
            'text_color' => 'required|string|regex:/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$|^rgba?\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*(,\s*(0|1|0?\.\d+))?\s*\)$/i', // Validate hex or rgba
            'width' => 'required|string|max:20', // e.g., '500px', '80%'
            'height' => 'required|string|max:20', // e.g., '300px', 'auto'
            'is_active' => 'sometimes|boolean', // 'sometimes' because checkbox might not be sent if unchecked
        ]);

        // Handle image upload
        if ($request->hasFile('image')) {
            // Delete old image if it exists
            if ($setting->image_path && Storage::disk('public')->exists($setting->image_path)) {
                Storage::disk('public')->delete($setting->image_path);
            }
            // Store the new image in 'storage/app/public/popups'
            $path = $request->file('image')->store('popups', 'public');
            $validated['image_path'] = $path;
        }

        // Handle 'is_active' checkbox (if unchecked, it won't be in the request)
        $validated['is_active'] = $request->has('is_active');

        // Update the settings
        $setting->update($validated);

        return redirect()->route('admin.popup.edit')->with('success', 'Pop-up settings updated successfully!');
    }

     /**
     * Optional: Method to remove the image
     */
    public function removeImage()
    {
        $setting = PopupSetting::findOrFail(1);
        if ($setting->image_path && Storage::disk('public')->exists($setting->image_path)) {
            Storage::disk('public')->delete($setting->image_path);
            $setting->image_path = null;
            $setting->save();
            return redirect()->route('admin.popup.edit')->with('success', 'Pop-up image removed successfully!');
        }
        return redirect()->route('admin.popup.edit')->with('error', 'No image to remove.');
    }
}
18c6424f-766b-4fcf-8f7d-4aa354c1bf54.webp

Okay, let's create a customizable pop-up image box feature for your Laravel application.

This will involve:

  1. Backend Setup (Laravel):
    • A Model and Migration for storing pop-up settings.
    • A Controller for managing these settings in the admin panel.
    • Routes for the admin panel and potentially fetching data for the frontend.
    • Admin Views for the form.
  2. Frontend Setup:
    • A Blade component or partial for the pop-up structure.
    • CSS for styling (including dynamic styles).
    • JavaScript for controlling the pop-up visibility and behavior.

Step 1: Backend Setup (Laravel)

1.1. Create Model and Migration:

Open your terminal in your Laravel project directory and run:

Bash
php artisan make:model PopupSetting -m

This creates:

  • app/Models/PopupSetting.php (Model)
  • database/migrations/YYYY_MM_DD_HHMMSS_create_popup_settings_table.php (Migration)

Now, edit the generated migration file (database/migrations/YYYY_MM_DD_HHMMSS_create_popup_settings_table.php):

PHP
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('popup_settings', function (Blueprint $table) {
            $table->id();
            $table->string('image_path')->nullable(); // Path to the image
            $table->text('text')->nullable();          // Pop-up text/content
            $table->string('background_color')->default('#FFFFFF'); // Background color
            $table->string('text_color')->default('#000000');       // Text color
            $table->string('width')->default('500px'); // e.g., '500px', '80%'
            $table->string('height')->default('auto'); // e.g., '300px', 'auto'
            $table->boolean('is_active')->default(false); // Controls if the pop-up is shown
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('popup_settings');
    }
};

Edit the Model (app/Models/PopupSetting.php) to allow mass assignment:

PHP
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class PopupSetting extends Model
{
    use HasFactory;

    // Allow these fields to be mass-assigned
    protected $fillable = [
        'image_path',
        'text',
        'background_color',
        'text_color',
        'width',
        'height',
        'is_active',
    ];

    // Cast boolean value correctly
    protected $casts = [
        'is_active' => 'boolean',
    ];
}

Run the migration:

Bash
php artisan migrate

1.2. Create Admin Controller:

Bash
php artisan make:controller Admin/PopupSettingController --resource

(Note: We'll mainly use edit and update for a single settings record).

Edit app/Http/Controllers/Admin/PopupSettingController.php:

PHP
<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\PopupSetting;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage; // <-- Import Storage facade
use Illuminate\Validation\Rule;       // <-- Import Rule for validation

class PopupSettingController extends Controller
{
    // We assume there's only one pop-up setting record needed for the site.
    // We'll use ID 1, or create it if it doesn't exist.

    /**
     * Show the form for editing the specified resource.
     */
    public function edit() // Simplified: Always edit the first record
    {
        // Find the first setting, or create a default one if none exists
        $setting = PopupSetting::firstOrCreate(['id' => 1]);
        return view('admin.popup.edit', compact('setting'));
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request) // Simplified: Always update the first record
    {
        $setting = PopupSetting::findOrFail(1); // Find setting with ID 1

        $validated = $request->validate([
            'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg,webp|max:2048', // Max 2MB
            'text' => 'nullable|string',
            'background_color' => 'required|string|regex:/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$|^rgba?\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*(,\s*(0|1|0?\.\d+))?\s*\)$/i', // Validate hex or rgba
            'text_color' => 'required|string|regex:/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$|^rgba?\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*(,\s*(0|1|0?\.\d+))?\s*\)$/i', // Validate hex or rgba
            'width' => 'required|string|max:20', // e.g., '500px', '80%'
            'height' => 'required|string|max:20', // e.g., '300px', 'auto'
            'is_active' => 'sometimes|boolean', // 'sometimes' because checkbox might not be sent if unchecked
        ]);

        // Handle image upload
        if ($request->hasFile('image')) {
            // Delete old image if it exists
            if ($setting->image_path && Storage::disk('public')->exists($setting->image_path)) {
                Storage::disk('public')->delete($setting->image_path);
            }
            // Store the new image in 'storage/app/public/popups'
            $path = $request->file('image')->store('popups', 'public');
            $validated['image_path'] = $path;
        }

        // Handle 'is_active' checkbox (if unchecked, it won't be in the request)
        $validated['is_active'] = $request->has('is_active');

        // Update the settings
        $setting->update($validated);

        return redirect()->route('admin.popup.edit')->with('success', 'Pop-up settings updated successfully!');
    }

     /**
     * Optional: Method to remove the image
     */
    public function removeImage()
    {
        $setting = PopupSetting::findOrFail(1);
        if ($setting->image_path && Storage::disk('public')->exists($setting->image_path)) {
            Storage::disk('public')->delete($setting->image_path);
            $setting->image_path = null;
            $setting->save();
            return redirect()->route('admin.popup.edit')->with('success', 'Pop-up image removed successfully!');
        }
        return redirect()->route('admin.popup.edit')->with('error', 'No image to remove.');
    }
}

1.3. Define Admin Routes:

Add these routes to your routes/web.php file, preferably within an admin middleware group:

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Admin\PopupSettingController;
use App\Http\Controllers\HomeController; // Or your relevant controller

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
*/

// Homepage Route (Example) - Fetch popup settings here
Route::get('/', [HomeController::class, 'index'])->name('homepage');

// Admin Routes (Example - adjust middleware as needed)
Route::middleware(['auth', 'admin'])->prefix('admin')->name('admin.')->group(function () {
    // Other admin routes...

    // Pop-up Settings Routes
    Route::get('/popup-settings', [PopupSettingController::class, 'edit'])->name('popup.edit');
    Route::put('/popup-settings', [PopupSettingController::class, 'update'])->name('popup.update');
    Route::delete('/popup-settings/image', [PopupSettingController::class, 'removeImage'])->name('popup.removeImage'); // Route to remove image

});

// Add Auth routes if you don't have them
// Auth::routes(); // Example if using laravel/ui

Okay, let's create a customizable pop-up image box feature for your Laravel application.

This will involve:

  1. Backend Setup (Laravel):
    • A Model and Migration for storing pop-up settings.
    • A Controller for managing these settings in the admin panel.
    • Routes for the admin panel and potentially fetching data for the frontend.
    • Admin Views for the form.
  2. Frontend Setup:
    • A Blade component or partial for the pop-up structure.
    • CSS for styling (including dynamic styles).
    • JavaScript for controlling the pop-up visibility and behavior.

Step 1: Backend Setup (Laravel)

1.1. Create Model and Migration:

Open your terminal in your Laravel project directory and run:

Bash
php artisan make:model PopupSetting -m

This creates:

  • app/Models/PopupSetting.php (Model)
  • database/migrations/YYYY_MM_DD_HHMMSS_create_popup_settings_table.php (Migration)

Now, edit the generated migration file (database/migrations/YYYY_MM_DD_HHMMSS_create_popup_settings_table.php):

PHP
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('popup_settings', function (Blueprint $table) {
            $table->id();
            $table->string('image_path')->nullable(); // Path to the image
            $table->text('text')->nullable();          // Pop-up text/content
            $table->string('background_color')->default('#FFFFFF'); // Background color
            $table->string('text_color')->default('#000000');       // Text color
            $table->string('width')->default('500px'); // e.g., '500px', '80%'
            $table->string('height')->default('auto'); // e.g., '300px', 'auto'
            $table->boolean('is_active')->default(false); // Controls if the pop-up is shown
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('popup_settings');
    }
};

Edit the Model (app/Models/PopupSetting.php) to allow mass assignment:

PHP
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class PopupSetting extends Model
{
    use HasFactory;

    // Allow these fields to be mass-assigned
    protected $fillable = [
        'image_path',
        'text',
        'background_color',
        'text_color',
        'width',
        'height',
        'is_active',
    ];

    // Cast boolean value correctly
    protected $casts = [
        'is_active' => 'boolean',
    ];
}

Run the migration:

Bash
php artisan migrate

1.2. Create Admin Controller:

Bash
php artisan make:controller Admin/PopupSettingController --resource

(Note: We'll mainly use edit and update for a single settings record).

Edit app/Http/Controllers/Admin/PopupSettingController.php:

PHP
<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\PopupSetting;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage; // <-- Import Storage facade
use Illuminate\Validation\Rule;       // <-- Import Rule for validation

class PopupSettingController extends Controller
{
    // We assume there's only one pop-up setting record needed for the site.
    // We'll use ID 1, or create it if it doesn't exist.

    /**
     * Show the form for editing the specified resource.
     */
    public function edit() // Simplified: Always edit the first record
    {
        // Find the first setting, or create a default one if none exists
        $setting = PopupSetting::firstOrCreate(['id' => 1]);
        return view('admin.popup.edit', compact('setting'));
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request) // Simplified: Always update the first record
    {
        $setting = PopupSetting::findOrFail(1); // Find setting with ID 1

        $validated = $request->validate([
            'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg,webp|max:2048', // Max 2MB
            'text' => 'nullable|string',
            'background_color' => 'required|string|regex:/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$|^rgba?\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*(,\s*(0|1|0?\.\d+))?\s*\)$/i', // Validate hex or rgba
            'text_color' => 'required|string|regex:/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$|^rgba?\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*(,\s*(0|1|0?\.\d+))?\s*\)$/i', // Validate hex or rgba
            'width' => 'required|string|max:20', // e.g., '500px', '80%'
            'height' => 'required|string|max:20', // e.g., '300px', 'auto'
            'is_active' => 'sometimes|boolean', // 'sometimes' because checkbox might not be sent if unchecked
        ]);

        // Handle image upload
        if ($request->hasFile('image')) {
            // Delete old image if it exists
            if ($setting->image_path && Storage::disk('public')->exists($setting->image_path)) {
                Storage::disk('public')->delete($setting->image_path);
            }
            // Store the new image in 'storage/app/public/popups'
            $path = $request->file('image')->store('popups', 'public');
            $validated['image_path'] = $path;
        }

        // Handle 'is_active' checkbox (if unchecked, it won't be in the request)
        $validated['is_active'] = $request->has('is_active');

        // Update the settings
        $setting->update($validated);

        return redirect()->route('admin.popup.edit')->with('success', 'Pop-up settings updated successfully!');
    }

     /**
     * Optional: Method to remove the image
     */
    public function removeImage()
    {
        $setting = PopupSetting::findOrFail(1);
        if ($setting->image_path && Storage::disk('public')->exists($setting->image_path)) {
            Storage::disk('public')->delete($setting->image_path);
            $setting->image_path = null;
            $setting->save();
            return redirect()->route('admin.popup.edit')->with('success', 'Pop-up image removed successfully!');
        }
        return redirect()->route('admin.popup.edit')->with('error', 'No image to remove.');
    }
}

1.3. Define Admin Routes:

Add these routes to your routes/web.php file, preferably within an admin middleware group:

PHP
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Admin\PopupSettingController;
use App\Http\Controllers\HomeController; // Or your relevant controller

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
*/

// Homepage Route (Example) - Fetch popup settings here
Route::get('/', [HomeController::class, 'index'])->name('homepage');

// Admin Routes (Example - adjust middleware as needed)
Route::middleware(['auth', 'admin'])->prefix('admin')->name('admin.')->group(function () {
    // Other admin routes...

    // Pop-up Settings Routes
    Route::get('/popup-settings', [PopupSettingController::class, 'edit'])->name('popup.edit');
    Route::put('/popup-settings', [PopupSettingController::class, 'update'])->name('popup.update');
    Route::delete('/popup-settings/image', [PopupSettingController::class, 'removeImage'])->name('popup.removeImage'); // Route to remove image

});

// Add Auth routes if you don't have them
// Auth::routes(); // Example if using laravel/ui

1.4. Create Admin View:

Create the directory resources/views/admin/popup. Create the file resources/views/admin/popup/edit.blade.php.

{{-- Assuming you have an admin layout file like layouts.admin --}}
@extends('layouts.admin') {{-- Adjust this to your admin layout --}}

@section('content')
<div class="container">
    <h1>Edit Pop-up Settings</h1>

    @if (session('success'))
        <div class="alert alert-success">
            {{ session('success') }}
        </div>
    @endif
     @if (session('error'))
        <div class="alert alert-danger">
            {{ session('error') }}
        </div>
    @endif

    @if ($errors->any())
        <div class="alert alert-danger">
            <ul>
                @foreach ($errors->all() as $error)
                    <li>{{ $error }}</li>
                @endforeach
            </ul>
        </div>
    @endif

    <form action="{{ route('admin.popup.update') }}" method="POST" enctype="multipart/form-data">
        @csrf
        @method('PUT')

        <div class="mb-3">
            <label for="image" class="form-label">Pop-up Image</label>
            <input type="file" class="form-control @error('image') is-invalid @enderror" id="image" name="image">
            @error('image')
                <div class="invalid-feedback">{{ $message }}</div>
            @enderror
            @if ($setting->image_path)
                <div class="mt-2">
                    <img src="{{ Storage::url($setting->image_path) }}" alt="Current Pop-up Image" style="max-width: 200px; max-height: 150px;">
                    {{-- Add a button to remove image --}}
                    <button type="submit" form="removeImageForm" class="btn btn-sm btn-danger ms-2">Remove Image</button>
                </div>
            @endif
        </div>

        <div class="mb-3">
            <label for="text" class="form-label">Pop-up Text</label>
            <textarea class="form-control @error('text') is-invalid @enderror" id="text" name="text" rows="5">{{ old('text', $setting->text) }}</textarea>
            @error('text')
                <div class="invalid-feedback">{{ $message }}</div>
            @enderror
        </div>

        <div class="row">
             <div class="col-md-6 mb-3">
                <label for="background_color" class="form-label">Background Color</label>
                <input type="color" class="form-control form-control-color @error('background_color') is-invalid @enderror" id="background_color_picker" value="{{ old('background_color', $setting->background_color) }}">
                <input type="text" class="form-control mt-1 @error('background_color') is-invalid @enderror" id="background_color" name="background_color" value="{{ old('background_color', $setting->background_color) }}" placeholder="#ffffff or rgba(255,255,255,0.9)">
                 @error('background_color')
                    <div class="invalid-feedback">{{ $message }}</div>
                @enderror
            </div>
             <div class="col-md-6 mb-3">
                <label for="text_color" class="form-label">Text Color</label>
                 <input type="color" class="form-control form-control-color @error('text_color') is-invalid @enderror" id="text_color_picker" value="{{ old('text_color', $setting->text_color) }}">
                <input type="text" class="form-control mt-1 @error('text_color') is-invalid @enderror" id="text_color" name="text_color" value="{{ old('text_color', $setting->text_color) }}" placeholder="#000000 or rgba(0,0,0,1)">
                 @error('text_color')
                    <div class="invalid-feedback">{{ $message }}</div>
                @enderror
            </div>
        </div>


        <div class="row">
            <div class="col-md-6 mb-3">
                <label for="width" class="form-label">Width (e.g., 500px, 80%)</label>
                <input type="text" class="form-control @error('width') is-invalid @enderror" id="width" name="width" value="{{ old('width', $setting->width) }}" required>
                 @error('width')
                    <div class="invalid-feedback">{{ $message }}</div>
                @enderror
            </div>
            <div class="col-md-6 mb-3">
                <label for="height" class="form-label">Height (e.g., 300px, auto)</label>
                <input type="text" class="form-control @error('height') is-invalid @enderror" id="height" name="height" value="{{ old('height', $setting->height) }}" required>
                 @error('height')
                    <div class="invalid-feedback">{{ $message }}</div>
                @enderror
            </div>
        </div>

        <div class="mb-3 form-check">
            <input type="checkbox" class="form-check-input" id="is_active" name="is_active" value="1" {{ old('is_active', $setting->is_active) ? 'checked' : '' }}>
            <label class="form-check-label" for="is_active">Show Pop-up on Homepage?</label>
        </div>


        <button type="submit" class="btn btn-primary">Save Settings</button>
    </form>

    {{-- Separate form for removing the image --}}
     @if ($setting->image_path)
    <form id="removeImageForm" action="{{ route('admin.popup.removeImage') }}" method="POST" style="display: none;">
        @csrf
        @method('DELETE')
    </form>
     @endif

</div>

{{-- Simple script to sync color picker and text input --}}
<script>
    document.getElementById('background_color_picker').addEventListener('input', (event) => {
        document.getElementById('background_color').value = event.target.value;
    });
     document.getElementById('text_color_picker').addEventListener('input', (event) => {
        document.getElementById('text_color').value = event.target.value;
    });
     document.getElementById('background_color').addEventListener('input', (event) => {
        document.getElementById('background_color_picker').value = event.target.value;
    });
     document.getElementById('text_color').addEventListener('input', (event) => {
        document.getElementById('text_color_picker').value = event.target.value;
    });
</script>

@endsection

.5. Filesystem Symlink:

Make sure your storage is linked so public files are accessible via URL. If you haven't done this before, run:

Bash
php artisan storage:link

This creates a symbolic link from public/storage to storage/app/public.


Step 2: Frontend Setup

2.1. Fetch Data in Homepage Controller:

Modify your HomeController (or wherever your homepage logic resides) to fetch the active pop-up setting.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\PopupSetting; // <-- Import the model

class HomeController extends Controller
{
    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Contracts\Support\Renderable
     */
    public function index()
    {
        // Fetch the *first* active pop-up setting
        $popupSetting = PopupSetting::where('is_active', true)->first();

        // Pass the setting (or null) to the view
        return view('welcome', compact('popupSetting')); // Adjust 'welcome' to your homepage view name
    }
}

2.2. Create Pop-up Blade View/Component:

Create a file like resources/views/components/popup.blade.php (or resources/views/partials/popup.blade.php).

@props(['setting']) {{-- Accept the setting object --}}

@if($setting)
<div id="customPopupOverlay" class="popup-overlay hidden">
    <div id="customPopupBox" class="popup-box" style="
        background-color: {{ $setting->background_color ?? '#fff' }};
        color: {{ $setting->text_color ?? '#000' }};
        width: {{ $setting->width ?? '500px' }};
        height: {{ $setting->height ?? 'auto' }};
        max-width: 95%; /* Prevent overflow on small screens */
        max-height: 90vh; /* Prevent overflow vertically */
        overflow-y: {{ $setting->height !== 'auto' ? 'auto' : 'visible' }}; /* Add scroll if height is fixed */
        ">
        <button id="customPopupClose" class="popup-close" aria-label="Close pop-up">&times;</button>

        <div class="popup-content">
            @if($setting->image_path)
            <div class="popup-image-container">
                 <img src="{{ Storage::url($setting->image_path) }}" alt="Pop-up Image" class="popup-image">
            </div>
            @endif

            @if($setting->text)
            <div class="popup-text">
                {!! nl2br(e($setting->text)) !!} {{-- nl2br to respect newlines, e() for security --}}
            </div>
            @endif
        </div>
    </div>
</div>
@endif

2.3. Include Pop-up in Homepage View:

In your main homepage Blade file (e.g., resources/views/welcome.blade.php), include the component/partial, passing the data from the controller:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>My Website</title>
    <link rel="preconnect" href="https://fonts.bunny.net">
    <link href="https://fonts.bunny.net/css?family=figtree:400,600&display=swap" rel="stylesheet" />
    <link rel="stylesheet" href="{{ asset('css/popup.css') }}">
    {{-- Your other CSS --}}
    @vite(['resources/css/app.css', 'resources/js/app.js']) {{-- Example if using Vite --}}
</head>
<body class="antialiased">

    {{-- Your main homepage content here --}}
    <h1>Welcome to the Homepage!</h1>
    <p>This is the main content of the page.</p>


    {{-- Include the Pop-up Component --}}
    {{-- Pass the $popupSetting variable fetched in the controller --}}
    <x-popup :setting="$popupSetting" />
    {{-- OR if using a partial: @include('partials.popup', ['setting' => $popupSetting]) --}}


    {{-- Include Pop-up JS (before closing body tag is best) --}}
    <script src="{{ asset('js/popup.js') }}"></script>
    {{-- Your other JS --}}
</body>
</html>

2.4. Create CSS File:

Create public/css/popup.css:

/* Pop-up Overlay */
.popup-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.6); /* Semi-transparent black background */
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 1000; /* Make sure it's on top */
    opacity: 1;
    visibility: visible;
    transition: opacity 0.3s ease, visibility 0s linear 0s; /* Smooth fade in */
}

.popup-overlay.hidden {
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.3s ease, visibility 0s linear 0.3s; /* Smooth fade out */
}

/* Pop-up Box */
.popup-box {
    position: relative; /* Needed for absolute positioning of the close button */
    padding: 35px 25px 25px 25px; /* Add padding, more top padding for close button */
    border-radius: 8px;
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
    transform: scale(1);
    transition: transform 0.3s ease;
    /* Dynamic styles (width, height, background-color, color) are applied inline */
    display: flex; /* Use flexbox for layout */
    flex-direction: column; /* Stack content vertically */
}

/* Animate pop-up box appearance */
.popup-overlay.hidden .popup-box {
    transform: scale(0.9);
}

/* Close Button */
.popup-close {
    position: absolute;
    top: 8px;
    right: 10px;
    background: none;
    border: none;
    font-size: 28px; /* Make it larger */
    font-weight: bold;
    color: inherit; /* Inherit color from popup-box text_color */
    opacity: 0.7;
    cursor: pointer;
    line-height: 1;
    padding: 0;
}

.popup-close:hover {
    opacity: 1;
}

/* Content Area */
.popup-content {
   /* Allow content to grow if needed */
   flex-grow: 1;
   /* For potential scrolling within content if needed, often handled by popup-box */
   /* overflow-y: auto; */
}


.popup-image-container {
    text-align: center; /* Center image if it's smaller than container */
    margin-bottom: 15px;
}

.popup-image {
    max-width: 100%; /* Ensure image is responsive */
    height: auto;   /* Maintain aspect ratio */
    max-height: 60vh; /* Limit image height */
    display: block;  /* Prevents bottom space */
    margin: 0 auto; /* Center block image */
}

.popup-text {
    line-height: 1.6;
    font-size: 1rem;
    /* Text color is inherited from .popup-box */
}

/* Add some basic responsive adjustments */
@media (max-width: 600px) {
    .popup-box {
        padding: 30px 15px 15px 15px;
        /* Width/height handled by inline styles and max-width/max-height */
    }
    .popup-close {
        font-size: 24px;
        top: 5px;
        right: 8px;
    }
}

2.5. Create JavaScript File:

Create public/js/popup.js:

document.addEventListener('DOMContentLoaded', function() {
    const popupOverlay = document.getElementById('customPopupOverlay');
    const closeButton = document.getElementById('customPopupClose');
    const popupBox = document.getElementById('customPopupBox');

    // Check if the popup elements exist on the page
    if (!popupOverlay || !closeButton || !popupBox) {
        // console.log('Popup elements not found.');
        return; // Exit if popup isn't rendered
    }

    // Function to close the popup
    function closePopup() {
        popupOverlay.classList.add('hidden');
        // Optional: Store in sessionStorage to prevent showing again in the same session
        try {
            sessionStorage.setItem('popupShown', 'true');
        } catch (e) {
            console.error('Session storage is unavailable.', e);
        }
    }

    // Function to open the popup
    function openPopup() {
         // Optional: Check if popup was already shown in this session
        try {
            if (sessionStorage.getItem('popupShown') === 'true') {
                console.log('Popup already shown this session.');
                popupOverlay.classList.add('hidden'); // Ensure it's hidden if already shown
                popupOverlay.style.display = 'none'; // Prevent flash of content
                return;
            }
        } catch (e) {
            console.error('Session storage is unavailable.', e);
        }

        popupOverlay.classList.remove('hidden');
    }

    // Event listener for the close button
    closeButton.addEventListener('click', closePopup);

    // Event listener to close when clicking the overlay (outside the box)
    popupOverlay.addEventListener('click', function(event) {
        // Check if the click was directly on the overlay, not on the box itself
        if (event.target === popupOverlay) {
            closePopup();
        }
    });

    // Event listener for the 'Escape' key
     document.addEventListener('keydown', function(event) {
        if (event.key === 'Escape' && !popupOverlay.classList.contains('hidden')) {
             closePopup();
        }
    });

    // --- Initially open the popup (after checking session storage) ---
    openPopup();

});

How to Use:

  1. Run php artisan migrate.
  2. Run php artisan storage:link (if you haven't already).
  3. Log in to your admin panel.
  4. Navigate to /admin/popup-settings (or the route you defined).
  5. Fill in the form:
    • Upload an image (optional).
    • Add text (optional).
    • Choose background and text colors.
    • Set the desired width and height (use CSS units like px or %). auto is valid for height.
    • Check the "Show Pop-up on Homepage?" box to activate it.
    • Save settings.
  6. Visit your homepage (/). The pop-up should appear if it's active and configured.
  7. The pop-up should only appear once per browser session due to the sessionStorage implementation in the JavaScript. Clear your session storage or close/reopen the browser tab/window to see it again for testing.

This provides a robust and customizable pop-up solution integrated into your Laravel admin panel. Remember to adjust layout names (layouts.admin, welcome.blade.php), route middleware ('auth', 'admin'), and potentially CSS/JS paths based on your specific project setup (e.g., if using Vite, ensure CSS/JS are correctly bundled and referenced).