here is my json code

--- File: D:\projects\digitalvocano\Modules\WebPilotAI\composer.json ---
{
    "name": "nwidart/webpilotai",
    "description": "",
    "authors": [
        {
            "name": "Nicolas Widart",
            "email": "n.widart@gmail.com"
        }
    ],
    "extra": {
        "laravel": {
            "providers": [],
            "aliases": {

            }
        }
    },
    "autoload": {
        "psr-4": {
            "Modules\\WebPilotAI\\": "app/",
            "Modules\\WebPilotAI\\Database\\Factories\\": "database/factories/",
            "Modules\\WebPilotAI\\Database\\Seeders\\": "database/seeders/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Modules\\WebPilotAI\\Tests\\": "tests/"
        }
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\module.json ---
{
    "name": "WebPilotAI",
    "alias": "webpilotai",
    "display_name": "WebPilot AI Website Builder",
    "description": "A revolutionary AI-powered website builder. Create, customize, and deploy stunning websites with ease. Supports various AI models, ZIP downloads, and direct hosting deployment.",
    "keywords": [
        "ai website builder",
        "ai web design",
        "no-code website",
        "cpanel deploy",
        "ftp deploy",
        "automated web development",
        "webpilot",
        "ai",
        "website generator",
        "saas"
    ],
    "priority": 0,
    "category": "ai_tool",
    "requires_subscription": true,
    "providers": [
        "Modules\\WebPilotAI\\Providers\\WebPilotAIServiceProvider"
    ],
    "files": [],
    "requires": [],
    "admin_menu": {
        "title": "WebPilot AI Builder",
        "route_name": "admin.webpilotai.dashboard",
        "icon_svg_path": "M17.25 9.75A2.25 2.25 0 0115 12H9a2.25 2.25 0 01-2.25-2.25V9.75m0 0A2.25 2.25 0 009 7.5h6a2.25 2.25 0 002.25 2.25M9 12v6.75a2.25 2.25 0 002.25 2.25h1.5A2.25 2.25 0 0015 18.75V12M9 7.5V3.75A2.25 2.25 0 0111.25 1.5h1.5A2.25 2.25 0 0115 3.75V7.5",
        "children": [
            {
                "title": "Dashboard",
                "route_name": "admin.webpilotai.dashboard"
            },
            {
                "title": "AI Model Configuration",
                "route_name": "admin.webpilotai.models.index"
            },
            {
                "title": "User Websites",
                "route_name": "admin.webpilotai.user-sites.index"
            },
            {
                "title": "AI Templates",
                "route_name": "admin.webpilotai.ai-templates.index"
            },
            {
                "title": "AI Style Presets",
                "route_name": "admin.webpilotai.ai-style-presets.index"
            },
            {
                "title": "Module Settings",
                "route_name": "admin.webpilotai.settings.index"
            }
        ]
    },
    "user_menu": [
        {
            "title": "My AI Websites",
            "route_name": "frontend.webpilotai.websites.index",
            "icon_svg_path": "M9 17.25v1.007a3 3 0 01-.879 2.122L7.5 21h9l-.621-.621A3 3 0 0115 18.257V17.25m6-12V15a2.25 2.25 0 01-2.25 2.25H5.25A2.25 2.25 0 013 15V5.25A2.25 2.25 0 015.25 3h13.5A2.25 2.25 0 0121 5.25z",
            "target_role_hint": "general_user",
            "associated_feature_key": "webpilotai_generate_website"
        }
    ],
    "features": [
        {
            "key": "webpilotai_generate_website",
            "name": "AI Website Generation",
            "description": "Create complete websites by describing your needs, business type, and desired style. Our AI will craft a unique design and initial content.",
            "requires_subscription": true,
            "limitable": true,
            "limit_label": "Max AI Websites (per plan/month)",
            "target_role_hint": "general_user",
            "user_route_name": "admin.webpilotai.user-sites.create"
        },
        {
            "key": "webpilotai_download_zip",
            "name": "Download Website as ZIP",
            "description": "Download the complete source code of your generated website as a ZIP file for manual hosting or backup.",
            "requires_subscription": true,
            "limitable": false,
            "target_role_hint": "general_user"
        },
        {
            "key": "webpilotai_deploy_hosting",
            "name": "Direct Hosting Deployment (cPanel/FTP/SFTP)",
            "description": "Automatically deploy your generated website to your cPanel, FTP, or SFTP hosting account with a few clicks.",
            "requires_subscription": true,
            "limitable": true,
            "limit_label": "Max Deployments (per plan/month)",
            "target_role_hint": "general_user"
        },
        {
            "key": "webpilotai_select_ai_model",
            "name": "Choose AI Generation Engine",
            "description": "Select from various AI models (e.g., OpenAI GPT series, Anthropic Claude series, Google Gemini) for website generation. Access to premium models may vary by plan.",
            "requires_subscription": true,
            "limitable": true,
            "limit_label": "Available AI Models",
            "target_role_hint": "general_user"
        },
        {
            "key": "webpilotai_template_library",
            "name": "AI Template & Style Library",
            "description": "Access a rich library of AI-generated design templates, color palettes, and font pairings to kickstart your website.",
            "requires_subscription": true,
            "limitable": true,
            "limit_label": "Access to Premium Templates/Styles",
            "target_role_hint": "general_user"
        },
        {
            "key": "webpilotai_advanced_customization",
            "name": "AI-Assisted Visual Editor",
            "description": "Fine-tune your website's design, layout, and elements with an intuitive visual editor, enhanced by AI suggestions.",
            "requires_subscription": true,
            "limitable": false,
            "target_role_hint": "general_user"
        },
        {
            "key": "webpilotai_ai_content_writer",
            "name": "AI Content Writer for Sections",
            "description": "Generate engaging text content (headlines, paragraphs, CTAs, product descriptions) for various sections of your website using AI.",
            "requires_subscription": true,
            "limitable": true,
            "limit_label": "AI Content Generation Credits (per plan/month)",
            "target_role_hint": "general_user"
        },
        {
            "key": "webpilotai_ai_image_studio",
            "name": "AI Image Studio & Stock Library",
            "description": "Generate unique images with AI or choose from a vast library of high-quality stock photos to enhance your website's visuals.",
            "requires_subscription": true,
            "limitable": true,
            "limit_label": "AI Image Credits / Premium Stock Access",
            "target_role_hint": "general_user"
        },
        {
            "key": "webpilotai_seo_assistant",
            "name": "AI SEO Assistant",
            "description": "Receive AI-driven recommendations for meta tags, keywords, content structure, and image alt-text to improve your website's search engine visibility.",
            "requires_subscription": true,
            "limitable": false,
            "target_role_hint": "general_user"
        },
        {
            "key": "webpilotai_version_history",
            "name": "Website Version History & Rollback",
            "description": "Keep track of changes to your AI-generated websites and easily revert to previous versions if needed.",
            "requires_subscription": true,
            "limitable": true,
            "limit_label": "Stored Versions / History Depth",
            "target_role_hint": "general_user"
        }
    ],
    "settings": [
        {
            "key": "enable_openai_models", "label": "Enable OpenAI Models", "description": "Allow users to select OpenAI models.", "type": "boolean", "default": true, "group": "AI Model Providers"
        },
        {
            "key": "openai_api_key", "label": "OpenAI API Key", "description": "API key for OpenAI models.", "type": "password", "group": "AI Model Providers", "depends_on": {"enable_openai_models": true}
        },
        {
            "key": "enable_anthropic_models", "label": "Enable Anthropic Models", "description": "Allow users to select Anthropic Claude models.", "type": "boolean", "default": false, "group": "AI Model Providers"
        },
        {
            "key": "anthropic_api_key", "label": "Anthropic API Key", "description": "API key for Anthropic models.", "type": "password", "group": "AI Model Providers", "depends_on": {"enable_anthropic_models": true}
        },
        {
            "key": "enable_google_models", "label": "Enable Google AI Models", "description": "Allow users to select Google Gemini models.", "type": "boolean", "default": false, "group": "AI Model Providers"
        },
        {
            "key": "google_ai_api_key", "label": "Google AI (Gemini) API Key", "description": "API key for Google AI models.", "type": "password", "group": "AI Model Providers", "depends_on": {"enable_google_models": true}
        },
        {
            "key": "enable_free_tier_model", "label": "Enable Free Tier AI Model", "description": "Offer a basic free AI model.", "type": "boolean", "default": true, "group": "AI Model Providers"
        },
        {
            "key": "free_tier_model_name", "label": "Free Tier Model Identifier", "description": "Identifier for the free tier AI model.", "type": "text", "group": "AI Model Providers", "depends_on": {"enable_free_tier_model": true}
        },
        {
            "key": "default_deployment_path_cpanel", "label": "Default cPanel Deployment Path", "description": "E.g., public_html.", "type": "text", "default": "public_html", "group": "Deployment Settings"
        },
        {
            "key": "max_website_revisions_default", "label": "Default Max Website Revisions", "description": "Default number of revisions stored per website.", "type": "number", "default": 10, "group": "Feature Defaults"
        }
    ]
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\package.json ---
{
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build"
  },
  "devDependencies": {
    "axios": "^1.1.2",
    "laravel-vite-plugin": "^0.7.5",
    "sass": "^1.69.5",
    "postcss": "^8.3.7",
    "vite": "^4.0.0"
  }
}

here is my php code

--- File: D:\projects\digitalvocano\Modules\WebPilotAI\app\Http\Controllers\Admin\AIModelController.php ---
<?php

namespace Modules\WebPilotAI\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Modules\WebPilotAI\Models\AIModel; // Import the AIModel

class AIModelController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        // Fetch models from the database, ordered by name
        $models = AIModel::orderBy('name')->get();

        return view('webpilotai::admin.aimodels.index', compact('models'));
    }

    /**
     * Show the form for creating a new resource.
     */
    public function create()
    {
        return view('webpilotai::admin.aimodels.create');
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request) {
        $validatedData = $request->validate([
            'name' => 'required|string|max:255',
            // The 'unique' rule will now work as the table and model are set up.
            'identifier' => 'required|string|max:255|unique:ai_models,identifier',
            'provider' => 'required|string|max:100',
            'api_key_setting_name' => 'nullable|string|max:255',
            'description' => 'nullable|string',
            'is_active' => 'nullable|boolean',
            'is_premium' => 'nullable|boolean',
        ]);

        // Adjust 'is_active' and 'is_premium' as checkboxes send '1' or nothing
        $validatedData['is_active'] = $request->has('is_active');
        $validatedData['is_premium'] = $request->has('is_premium');

        // Create the AIModel record in the database
        AIModel::create($validatedData);

        return redirect()->route('admin.webpilotai.models.index')->with('success', 'AI Model added successfully.');
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  \Modules\WebPilotAI\Models\AIModel  $model  // Route model binding
     * @return \Illuminate\Http\Response
     */
    public function edit(AIModel $model) // Using Route Model Binding
    {
        return view('webpilotai::admin.aimodels.edit', compact('model'));
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Modules\WebPilotAI\Models\AIModel  $model // Route model binding
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, AIModel $model) // Using Route Model Binding
    {
        $validatedData = $request->validate([
            'name' => 'required|string|max:255',
            // Ensure identifier is unique, ignoring the current model's identifier
            'identifier' => 'required|string|max:255|unique:ai_models,identifier,' . $model->id,
            'provider' => 'required|string|max:100',
            'api_key_setting_name' => 'nullable|string|max:255',
            'description' => 'nullable|string',
            'is_active' => 'nullable|boolean',
            'is_premium' => 'nullable|boolean',
        ]);

        $validatedData['is_active'] = $request->has('is_active');
        $validatedData['is_premium'] = $request->has('is_premium');

        $model->update($validatedData);

        return redirect()->route('admin.webpilotai.models.index')->with('success', 'AI Model updated successfully.');
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  \Modules\WebPilotAI\Models\AIModel  $model // Route model binding
     * @return \Illuminate\Http\Response
     */
    public function destroy(AIModel $model) // Using Route Model Binding
    {
        $model->delete();
        return redirect()->route('admin.webpilotai.models.index')->with('success', 'AI Model deleted successfully.');
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\app\Http\Controllers\Admin\DashboardController.php ---
<?php

namespace Modules\WebPilotAI\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Modules\WebPilotAI\Models\AIModel;
use Modules\WebPilotAI\Models\Website;

class DashboardController extends Controller
{
    /**
     * Display the admin dashboard for WebPilotAI.
     */
    public function index()
    {
        $totalAIModels = AIModel::count();
        $activeAIModels = AIModel::where('is_active', true)->count();
        $totalWebsites = Website::count();
        $completedWebsites = Website::where('status', 'completed')->count();
        $pendingWebsites = Website::whereIn('status', ['pending_generation', 'generating', 'pending_regeneration'])->count();

        return view('webpilotai::admin.dashboard', compact(
            'totalAIModels', 'activeAIModels', 'totalWebsites', 'completedWebsites', 'pendingWebsites'
        ));
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\app\Http\Controllers\Admin\ModuleSettingsController.php ---
<?php

namespace Modules\WebPilotAI\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Rule;

class ModuleSettingsController extends Controller
{
    private string $moduleName = 'WebPilotAI';
    private string $moduleAlias = 'webpilotai';

    /**
     * Display the module settings page.
     */
    public function index()
    {
        $settingsConfig = $this->getSettingsConfig();
        $currentSettings = [];

        foreach ($settingsConfig as $setting) {
            // Fetch current value from the published config file
            $currentSettings[$setting['key']] = config($this->moduleAlias . '.' . $setting['key'], $setting['default'] ?? null);
        }

        return view('webpilotai::admin.modulesettings.index', compact('settingsConfig', 'currentSettings'));
    }

    /**
     * Store the updated module settings.
     */
    public function store(Request $request): RedirectResponse
    {
        $settingsConfig = $this->getSettingsConfig();
        $rules = [];
        $newData = [];

        foreach ($settingsConfig as $setting) {
            $key = $setting['key'];
            // Basic validation based on type
            switch ($setting['type']) {
                case 'boolean':
                    $rules[$key] = 'nullable|boolean';
                    $newData[$key] = $request->has($key); // Checkbox handling
                    break;
                case 'number':
                    $rules[$key] = 'nullable|numeric';
                    $newData[$key] = $request->input($key);
                    break;
                case 'text':
                case 'password':
                case 'textarea':
                case 'select':
                default:
                    $rules[$key] = 'nullable|string';
                    $newData[$key] = $request->input($key);
                    break;
            }
        }

        $request->validate($rules); // Use validated data if further processing needed

        // Path to the published config file
        $publishedConfigPath = config_path($this->moduleAlias . '.php');

        if (!File::exists($publishedConfigPath)) {
            // Attempt to publish if not exists (might require user to run command manually first)
            // Artisan::call('vendor:publish', ['--provider' => "Modules\\{$this->moduleName}\\Providers\\{$this->moduleName}ServiceProvider", '--tag' => "config"]);
            return redirect()->back()->withErrors(['msg' => 'Module configuration file not found. Please publish the module assets.']);
        }

        // Read existing published config
        $existingConfig = require $publishedConfigPath;

        // Merge new data with existing config, preserving other keys
        $updatedConfig = array_merge($existingConfig, $newData);

        // Write back to the published config file
        $phpCode = "<?php\n\nreturn " . var_export($updatedConfig, true) . ";\n";

        try {
            File::put($publishedConfigPath, $phpCode);
            // Clear config cache to apply changes
            Artisan::call('config:clear');
            // Optionally, re-cache if in production: Artisan::call('config:cache');
        } catch (\Exception $e) {
            Log::error("Failed to write WebPilotAI settings to config file: " . $e->getMessage());
            return redirect()->back()->withErrors(['msg' => 'Failed to save settings. Check file permissions or logs.']);
        }

        return redirect()->route('admin.webpilotai.settings.index')->with('success', $this->moduleName . ' settings updated successfully!');
    }

    private function getSettingsConfig(): array
    {
        $moduleJsonPath = module_path($this->moduleName, 'module.json');
        if (File::exists($moduleJsonPath)) {
            $moduleConfig = json_decode(File::get($moduleJsonPath), true);
            return $moduleConfig['settings'] ?? [];
        }
        return [];
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\app\Http\Controllers\Admin\UserSiteController.php ---
<?php

namespace Modules\WebPilotAI\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Modules\WebPilotAI\Models\Website;
use App\Models\User; // Assuming your User model is in App\Models
use Modules\WebPilotAI\Models\AIModel;

class UserSiteController extends Controller
{
    /**
     * Display a listing of all user websites.
     */
    public function index()
    {
        $websites = Website::with(['user', 'aiModel']) // Eager load relationships
                            ->orderBy('created_at', 'desc')
                            ->paginate(15);
        return view('webpilotai::admin.usersites.index', compact('websites'));
    }

    /**
     * Show the form for creating a new website (by admin).
     */
    public function create()
    {
        $users = User::orderBy('name')->get();
        $aiModels = AIModel::where('is_active', true)->orderBy('name')->get();
        return view('webpilotai::admin.usersites.create', compact('users', 'aiModels'));
    }

    /**
     * Store a newly created website in storage (by admin).
     */
    public function store(Request $request): RedirectResponse
    {
        $validatedData = $request->validate([
            'user_id' => 'required|exists:users,id',
            'name' => 'nullable|string|max:255',
            'description_prompt' => 'required|string|min:10',
            'ai_model_id' => 'required|exists:ai_models,id',
            'status' => 'required|string', // Admin can set status directly
        ]);

        Website::create($validatedData);

        return redirect()->route('admin.webpilotai.user-sites.index')->with('success', 'User website created successfully.');
    }

    /**
     * Display the specified website.
     */
    public function show(Website $user_site) // Route model binding (Laravel 7+ uses singular for resource param)
    {
        $user_site->load(['user', 'aiModel']);
        return view('webpilotai::admin.usersites.show', ['website' => $user_site]);
    }

    /**
     * Show the form for editing the specified website.
     */
    public function edit(Website $user_site)
    {
        $users = User::orderBy('name')->get();
        $aiModels = AIModel::orderBy('name')->get(); // Get all models, admin might assign inactive one
        return view('webpilotai::admin.usersites.edit', ['website' => $user_site, 'users' => $users, 'aiModels' => $aiModels]);
    }

    /**
     * Update the specified website in storage.
     */
    public function update(Request $request, Website $user_site): RedirectResponse
    {
        $validatedData = $request->validate([
            'user_id' => 'required|exists:users,id',
            'name' => 'nullable|string|max:255',
            'description_prompt' => 'required|string|min:10',
            'ai_model_id' => 'required|exists:ai_models,id',
            'status' => 'required|string',
            'generated_content_path' => 'nullable|string',
            'generation_error' => 'nullable|string',
        ]);

        $user_site->update($validatedData);

        return redirect()->route('admin.webpilotai.user-sites.index')->with('success', 'User website updated successfully.');
    }

    // destroy method can be added if admin should be able to delete sites
    // public function destroy(Website $user_site) { ... }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\app\Http\Controllers\User\WebsiteController.php ---
<?php

namespace Modules\WebPilotAI\Http\Controllers\User;

use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Crypt; // Import Crypt facade
use Illuminate\Validation\Rule; // Import Rule
use Modules\WebPilotAI\Models\AIModel;
use Illuminate\Support\Facades\Storage;
use Modules\WebPilotAI\Models\Website;
use Modules\WebPilotAI\Jobs\ProcessWebsiteGeneration; // Import the Job
use Modules\WebPilotAI\Jobs\DeployWebsiteToHosting; // Placeholder for new Job

class WebsiteController extends Controller
{
    /**
     * Display a listing of the user's websites.
     */
    public function index()
    {
        $websites = Website::where('user_id', Auth::id())
                            ->with('aiModel') // Eager load the AI model relationship
                            ->orderBy('created_at', 'desc')
                            ->paginate(10); // Paginate results

        return view('webpilotai::user.websites.index', compact('websites'));
    }

    /**
     * Show the form for creating a new website.
     */
    public function create()
    {
        // Fetch active AI models for the user to choose from
        $aiModels = AIModel::where('is_active', true)->orderBy('name')->get();
        return view('webpilotai::user.websites.create', compact('aiModels'));
    }

    /**
     * Store a newly created website in storage.
     */
    public function store(Request $request): RedirectResponse
    {
        $validatedData = $request->validate([
            'name' => 'nullable|string|max:255',
            'description_prompt' => 'required|string|min:20',
            'ai_model_id' => [
                'required',
                Rule::exists('ai_models', 'id')->where(function ($query) {
                    $query->where('is_active', true);
                }),
            ],
        ]);

        $website = Website::create([
            'user_id' => Auth::id(),
            'name' => $validatedData['name'] ?? 'My AI Website - ' . now()->format('Y-m-d H:i'),
            'description_prompt' => $validatedData['description_prompt'],
            'ai_model_id' => $validatedData['ai_model_id'],
            'status' => 'pending_generation', // Initial status
        ]);

        // Dispatch the job to handle AI generation
        ProcessWebsiteGeneration::dispatch($website);

        return redirect()->route('frontend.webpilotai.websites.index')->with('success', 'Your website request has been submitted! Generation will begin shortly.');
    }

    /**
     * Display the specified website.
     */
    public function show(Website $website) // Route model binding
    {
        // Ensure the authenticated user owns this website
        $this->authorize('view', $website); // Requires a WebsitePolicy

        return view('webpilotai::user.websites.show', compact('website'));
    }

    /**
     * Show the form for editing the specified website.
     */
    public function edit(Website $website) // Route model binding
    {
        $this->authorize('update', $website); // Requires a WebsitePolicy
        $aiModels = AIModel::where('is_active', true)->orderBy('name')->get();

        return view('webpilotai::user.websites.edit', compact('website', 'aiModels'));
    }

    /**
     * Update the specified website in storage.
     */
    public function update(Request $request, Website $website): RedirectResponse
    {
        $this->authorize('update', $website);

        $validatedData = $request->validate([
            'name' => 'nullable|string|max:255',
            'description_prompt' => 'required|string|min:20',
            'ai_model_id' => [
                'required',
                Rule::exists('ai_models', 'id')->where(function ($query) {
                    $query->where('is_active', true);
                }),
            ],
            'regenerate' => 'nullable|boolean', // Checkbox to trigger regeneration
        ]);

        $originalPrompt = $website->description_prompt;
        $originalModelId = $website->ai_model_id;

        $website->update([
            'name' => $validatedData['name'] ?? $website->name,
            'description_prompt' => $validatedData['description_prompt'],
            'ai_model_id' => $validatedData['ai_model_id'],
        ]);

        // Re-dispatch job if prompt changed, model changed, or user explicitly requests regeneration
        if ($request->has('regenerate') || $validatedData['description_prompt'] !== $originalPrompt || $validatedData['ai_model_id'] != $originalModelId) {
            $website->update(['status' => 'pending_regeneration', 'generated_content_path' => null, 'generation_error' => null]); // Reset path and error
            ProcessWebsiteGeneration::dispatch($website);
            return redirect()->route('frontend.webpilotai.websites.show', $website->id)->with('success', 'Website details updated. Regeneration has started!');
        }

        return redirect()->route('frontend.webpilotai.websites.show', $website->id)->with('success', 'Website details updated successfully.');
    }

    /**
     * Remove the specified website from storage.
     */
    public function destroy(Website $website): RedirectResponse
    {
        $this->authorize('delete', $website);

        // Delete the generated file/directory from storage
        if ($website->generated_content_path) {
            // The path stored is relative to public/storage, e.g., "storage/webpilotai_sites_zip/1/file.zip"
            // To use Storage facade, we need path relative to disk's root, e.g., "public/webpilotai_sites_zip/1/file.zip" for the 'public' disk
            $storagePath = str_replace('storage/', 'public/', $website->generated_content_path);
            
            if (Storage::exists($storagePath)) {
                Storage::delete($storagePath); // Deletes the ZIP file
                
                // Attempt to delete the parent directory if it's empty
                $directory = dirname($storagePath);
                if (empty(Storage::files($directory)) && empty(Storage::directories($directory))) {
                    Storage::deleteDirectory($directory);
                }
            }
        }
        $website->delete();
        return redirect()->route('frontend.webpilotai.websites.index')->with('success', 'Website deleted successfully.');
    }

    /**
     * Show the form for configuring deployment for a specific website.
     */
    public function showDeployForm(Website $website)
    {
        $this->authorize('update', $website); // Users can only deploy sites they own/can update

        if ($website->status !== 'completed' || !$website->generated_content_path) {
            return redirect()->route('frontend.webpilotai.websites.show', $website->id)
                             ->withErrors(['msg' => 'Website must be successfully generated before it can be deployed.']);
        }

        // Retrieve last used deployment details if available (e.g., from $website->deployment_details)
        // For now, we'll pass an empty array or defaults.
        $lastDeploymentDetails = $website->deployment_details ?? [];

        return view('webpilotai::user.websites.deploy', compact('website', 'lastDeploymentDetails'));
    }

    /**
     * Handle the submission of deployment details and initiate deployment.
     */
    public function handleDeployment(Request $request, Website $website): RedirectResponse
    {
        $this->authorize('update', $website);

        $validatedData = $request->validate([
            'deployment_type' => ['required', Rule::in(['ftp', 'sftp', 'cpanel'])],
            'host' => 'required|string|max:255',
            'username' => 'required|string|max:255',
            'password' => 'required|string', // Consider not validating length too strictly here
            'port' => 'nullable|numeric',
            'remote_path' => 'nullable|string|max:255', // e.g., public_html/my-site
            'cpanel_domain' => 'nullable|required_if:deployment_type,cpanel|string|max:255',
        ]);

        // Encrypt the password before dispatching the job
        $deploymentPayload = $validatedData;
        if (isset($deploymentPayload['password'])) {
            $deploymentPayload['password'] = Crypt::encryptString($deploymentPayload['password']);
        }

        DeployWebsiteToHosting::dispatch($website, $deploymentPayload);

        return redirect()->route('frontend.webpilotai.websites.show', $website->id)->with('success', 'Deployment process initiated. You will be notified upon completion.');
    }

    /**
     * Get the current status of a website for AJAX polling.
     */
    public function getDeploymentStatus(Website $website): JsonResponse
    {
        $this->authorize('view', $website);

        return response()->json([
            'name' => $website->name ?: 'Untitled Website',
            'status' => $website->status,
            'status_friendly' => Str::title(str_replace('_', ' ', $website->status)),
            'generation_error' => $website->generation_error,
            'deployment_details' => $website->deployment_details,
            'last_generated_at' => $website->last_generated_at ? $website->last_generated_at->format('M d, Y H:i A') : 'Never',
            'generated_content_path' => $website->generated_content_path ? asset($website->generated_content_path) : null,
            'can_download' => $website->status === Website::STATUS_COMPLETED && $website->generated_content_path,
            'can_deploy_form' => $website->status === Website::STATUS_COMPLETED && $website->generated_content_path,
        ]);
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\app\Http\Controllers\WebPilotAIController.php ---
<?php

namespace Modules\WebPilotAI\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class WebPilotAIController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        return view('webpilotai::index');
    }

    /**
     * Show the form for creating a new resource.
     */
    public function create()
    {
        return view('webpilotai::create');
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request) {}

    /**
     * Show the specified resource.
     */
    public function show($id)
    {
        return view('webpilotai::show');
    }

    /**
     * Show the form for editing the specified resource.
     */
    public function edit($id)
    {
        return view('webpilotai::edit');
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, $id) {}

    /**
     * Remove the specified resource from storage.
     */
    public function destroy($id) {}
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\app\Jobs\ProcessWebsiteGeneration.php ---
<?php

namespace Modules\WebPilotAI\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;

class ProcessWebsiteGeneration implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    /**
     * Create a new job instance.
     */
    public function __construct() {}

    /**
     * Execute the job.
     */
    public function handle(): void {}
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\app\Policies\WebsitePolicy.php ---
<?php

namespace Modules\WebPilotAI\Policies; // Adjust if needed

use App\Models\User;
use Modules\WebPilotAI\Models\Website;
use Illuminate\Auth\Access\HandlesAuthorization;

class WebsitePolicy
{
    use HandlesAuthorization;

    /**
     * Determine whether the user can view any models.
     */
    public function viewAny(User $user): bool
    {
        return true; // Or based on permissions
    }

    /**
     * Determine whether the user can view the model.
     */
    public function view(User $user, Website $website): bool
    {
        return $user->id === $website->user_id;
    }

    /**
     * Determine whether the user can create models.
     */
    public function create(User $user): bool
    {
        return true; // Or based on subscription/permissions
    }

    /**
     * Determine whether the user can update the model.
     */
    public function update(User $user, Website $website): bool
    {
        return $user->id === $website->user_id;
    }

    /**
     * Determine whether the user can delete the model.
     */
    public function delete(User $user, Website $website): bool
    {
        return $user->id === $website->user_id;
    }

    // Add other policy methods as needed (restore, forceDelete)
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\app\Providers\EventServiceProvider.php ---
<?php

namespace Modules\WebPilotAI\Providers;

use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
{
    /**
     * The event handler mappings for the application.
     *
     * @var array<string, array<int, string>>
     */
    protected $listen = [];

    /**
     * Indicates if events should be discovered.
     *
     * @var bool
     */
    protected static $shouldDiscoverEvents = true;

    /**
     * Configure the proper event listeners for email verification.
     */
    protected function configureEmailVerification(): void {}
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\app\Providers\RouteServiceProvider.php ---
<?php

namespace Modules\WebPilotAI\Providers;

use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route;

class RouteServiceProvider extends ServiceProvider
{
    protected string $name = 'WebPilotAI';

    /**
     * Called before routes are registered.
     *
     * Register any model bindings or pattern based filters.
     */
    public function boot(): void
    {
        parent::boot();
    }

    /**
     * Define the routes for the application.
     */
    public function map(): void
    {
        $this->mapApiRoutes();
        $this->mapWebRoutes();
    }

    /**
     * Define the "web" routes for the application.
     *
     * These routes all receive session state, CSRF protection, etc.
     */
    protected function mapWebRoutes(): void
    {
        Route::middleware('web')->group(module_path($this->name, '/routes/web.php'));
    }

    /**
     * Define the "api" routes for the application.
     *
     * These routes are typically stateless.
     */
    protected function mapApiRoutes(): void
    {
        Route::middleware('api')->prefix('api')->name('api.')->group(module_path($this->name, '/routes/api.php'));
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\app\Providers\WebPilotAIServiceProvider.php ---
<?php

namespace Modules\WebPilotAI\Providers;

use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
use Nwidart\Modules\Traits\PathNamespace;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;

class WebPilotAIServiceProvider extends ServiceProvider
{
    use PathNamespace;

    protected string $name = 'WebPilotAI';

    protected string $nameLower = 'webpilotai';

    /**
     * Boot the application events.
     */
    public function boot(): void
    {
        $this->registerCommands();
        $this->registerCommandSchedules();
        $this->registerTranslations();
        $this->registerConfig();
        $this->registerViews();
        $this->loadMigrationsFrom(module_path($this->name, 'database/migrations'));
    }

    /**
     * Register the service provider.
     */
    public function register(): void
    {
        $this->app->register(EventServiceProvider::class);
        $this->app->register(RouteServiceProvider::class);
    }

    /**
     * Register commands in the format of Command::class
     */
    protected function registerCommands(): void
    {
        // $this->commands([]);
    }

    /**
     * Register command Schedules.
     */
    protected function registerCommandSchedules(): void
    {
        // $this->app->booted(function () {
        //     $schedule = $this->app->make(Schedule::class);
        //     $schedule->command('inspire')->hourly();
        // });
    }

    /**
     * Register translations.
     */
    public function registerTranslations(): void
    {
        $langPath = resource_path('lang/modules/'.$this->nameLower);

        if (is_dir($langPath)) {
            $this->loadTranslationsFrom($langPath, $this->nameLower);
            $this->loadJsonTranslationsFrom($langPath);
        } else {
            $this->loadTranslationsFrom(module_path($this->name, 'lang'), $this->nameLower);
            $this->loadJsonTranslationsFrom(module_path($this->name, 'lang'));
        }
    }

    /**
     * Register config.
     */
    protected function registerConfig(): void
    {
        $configPath = module_path($this->name, config('modules.paths.generator.config.path'));

        if (is_dir($configPath)) {
            $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($configPath));

            foreach ($iterator as $file) {
                if ($file->isFile() && $file->getExtension() === 'php') {
                    $config = str_replace($configPath.DIRECTORY_SEPARATOR, '', $file->getPathname());
                    $config_key = str_replace([DIRECTORY_SEPARATOR, '.php'], ['.', ''], $config);
                    $segments = explode('.', $this->nameLower.'.'.$config_key);

                    // Remove duplicated adjacent segments
                    $normalized = [];
                    foreach ($segments as $segment) {
                        if (end($normalized) !== $segment) {
                            $normalized[] = $segment;
                        }
                    }

                    $key = ($config === 'config.php') ? $this->nameLower : implode('.', $normalized);

                    $this->publishes([$file->getPathname() => config_path($config)], 'config');
                    $this->merge_config_from($file->getPathname(), $key);
                }
            }
        }
    }

    /**
     * Merge config from the given path recursively.
     */
    protected function merge_config_from(string $path, string $key): void
    {
        $existing = config($key, []);
        $module_config = require $path;

        config([$key => array_replace_recursive($existing, $module_config)]);
    }

    /**
     * Register views.
     */
    public function registerViews(): void
    {
        $viewPath = resource_path('views/modules/'.$this->nameLower);
        $sourcePath = module_path($this->name, 'resources/views');

        $this->publishes([$sourcePath => $viewPath], ['views', $this->nameLower.'-module-views']);

        $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->nameLower);

        Blade::componentNamespace(config('modules.namespace').'\\' . $this->name . '\\View\\Components', $this->nameLower);
    }

    /**
     * Get the services provided by the provider.
     */
    public function provides(): array
    {
        return [];
    }

    private function getPublishableViewPaths(): array
    {
        $paths = [];
        foreach (config('view.paths') as $path) {
            if (is_dir($path.'/modules/'.$this->nameLower)) {
                $paths[] = $path.'/modules/'.$this->nameLower;
            }
        }

        return $paths;
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\config\config.php ---
<?php

return [
    'name' => 'WebPilotAI',
];


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\database\seeders\WebPilotAIDatabaseSeeder.php ---
<?php

namespace Modules\WebPilotAI\Database\Seeders;

use Illuminate\Database\Seeder;

class WebPilotAIDatabaseSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        // $this->call([]);
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Events\WebsiteDeploymentFailed.php ---
<?php

namespace Modules\WebPilotAI\Events;

use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
use Modules\WebPilotAI\Models\Website;

class WebsiteDeploymentFailed
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public Website $website;
    public string $errorMessage;
    public bool $jobFailedPermanently;

    /**
     * Create a new event instance.
     *
     * @param Website $website
     * @param string $errorMessage
     * @param bool $jobFailedPermanently
     */
    public function __construct(Website $website, string $errorMessage, bool $jobFailedPermanently = false)
    {
        $this->website = $website;
        $this->errorMessage = $errorMessage;
        $this->jobFailedPermanently = $jobFailedPermanently;
    }

    // public function broadcastOn() { return []; }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Events\WebsiteDeploymentSucceeded.php ---
<?php

namespace Modules\WebPilotAI\Events;

use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
use Modules\WebPilotAI\Models\Website;

class WebsiteDeploymentSucceeded
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public Website $website;

    /**
     * Create a new event instance.
     *
     * @param Website $website
     */
    public function __construct(Website $website)
    {
        $this->website = $website;
    }

    // public function broadcastOn() { return []; }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Events\WebsiteGenerationFailed.php ---
<?php

namespace Modules\WebPilotAI\Events;

use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
use Modules\WebPilotAI\Models\Website;

class WebsiteGenerationFailed
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public Website $website;
    public string $errorMessage;
    public bool $jobFailedPermanently;

    /**
     * Create a new event instance.
     *
     * @param Website $website
     * @param string $errorMessage
     * @param bool $jobFailedPermanently
     */
    public function __construct(Website $website, string $errorMessage, bool $jobFailedPermanently = false)
    {
        $this->website = $website;
        $this->errorMessage = $errorMessage;
        $this->jobFailedPermanently = $jobFailedPermanently;
    }

    // public function broadcastOn() { return []; }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Events\WebsiteGenerationSucceeded.php ---
<?php

namespace Modules\WebPilotAI\Events;

use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
use Modules\WebPilotAI\Models\Website;

class WebsiteGenerationSucceeded
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public Website $website;

    /**
     * Create a new event instance.
     *
     * @param Website $website
     */
    public function __construct(Website $website)
    {
        $this->website = $website;
    }

    // If you need to broadcast this event, uncomment the following:
    // public function broadcastOn() { return []; }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Http\Controllers\Admin\AIModelController.php ---
<?php

namespace Modules\WebPilotAI\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Modules\WebPilotAI\Models\AIModel; // Import the AIModel
use Illuminate\Http\RedirectResponse; // Added for type hinting

class AIModelController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        // Fetch models from the database with pagination, ordered by name
        $aiModels = AIModel::orderBy('name')->paginate(15); // Use paginate for pagination

        return view('webpilotai::admin.aimodels.index', compact('aiModels')); // Pass $aiModels
    }

    /**
     * Show the form for creating a new resource.
     */
    public function create()
    {
        return view('webpilotai::admin.aimodels.create');
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request): RedirectResponse
    {
        $validatedData = $request->validate([
            'name' => 'required|string|max:255',
            'identifier' => 'required|string|max:255|unique:ai_models,identifier',
            'provider' => 'required|string|max:100', // Consider using an ENUM or a predefined list for validation
            'api_key_setting_name' => 'nullable|string|max:255',
            'description' => 'nullable|string',
            'is_active' => 'nullable|boolean',
            'is_premium' => 'nullable|boolean',
        ]);

        // Adjust 'is_active' and 'is_premium' as checkboxes send '1' or nothing
        $validatedData['is_active'] = $request->has('is_active');
        $validatedData['is_premium'] = $request->has('is_premium');

        AIModel::create($validatedData);

        return redirect()->route('admin.webpilotai.models.index')->with('success', 'AI Model added successfully.');
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  \Modules\WebPilotAI\Models\AIModel  $model
     * @return \Illuminate\View\View
     */
    public function edit(AIModel $model) // Using Route Model Binding
    {
        return view('webpilotai::admin.aimodels.edit', compact('model'));
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Modules\WebPilotAI\Models\AIModel  $model
     * @return \Illuminate\Http\RedirectResponse
     */
    public function update(Request $request, AIModel $model): RedirectResponse // Using Route Model Binding
    {
        $validatedData = $request->validate([
            'name' => 'required|string|max:255',
            'identifier' => 'required|string|max:255|unique:ai_models,identifier,' . $model->id,
            'provider' => 'required|string|max:100', // Consider using an ENUM or a predefined list for validation
            'api_key_setting_name' => 'nullable|string|max:255',
            'description' => 'nullable|string',
            'is_active' => 'nullable|boolean',
            'is_premium' => 'nullable|boolean',
        ]);

        $validatedData['is_active'] = $request->has('is_active');
        $validatedData['is_premium'] = $request->has('is_premium');

        $model->update($validatedData);

        return redirect()->route('admin.webpilotai.models.index')->with('success', 'AI Model updated successfully.');
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  \Modules\WebPilotAI\Models\AIModel  $model
     * @return \Illuminate\Http\RedirectResponse
     */
    public function destroy(AIModel $model): RedirectResponse // Using Route Model Binding
    {
        $model->delete();
        return redirect()->route('admin.webpilotai.models.index')->with('success', 'AI Model deleted successfully.');
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Http\Controllers\Admin\AIStylePresetController.php ---
<?php

namespace Modules\WebPilotAI\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
// use Illuminate\Http\Response; // Not strictly needed if not returning raw Response objects
use Modules\WebPilotAI\Models\AIStylePreset;
use Illuminate\Support\Str; // For slug generation
use Illuminate\Support\Facades\Storage; // For file storage

class AIStylePresetController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        $aiStylePresets = AIStylePreset::orderBy('name')->paginate(15); // Changed variable name and order_by
        return view('webpilotai::admin.ai_style_presets.index', compact('aiStylePresets'));
    }

    /**
     * Show the form for creating a new resource.
     */
    public function create()
    {
        $types = [
            'color_palette' => 'Color Palette',
            'font_scheme' => 'Font Scheme',
            'visual_theme' => 'Visual Theme',
            'element_specific_styles' => 'Element Specific Styles',
            'direct_instruction' => 'Direct Instruction'
        ]; // Define available types
        return view('webpilotai::admin.ai_style_presets.create', compact('types'));
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request): RedirectResponse
    {
        $validatedData = $request->validate([
            'name' => 'required|string|max:255|unique:ai_style_presets,name',
            'description' => 'nullable|string',
            'type' => 'required|string|in:color_palette,font_scheme,visual_theme,element_specific_styles,direct_instruction',
            'configuration' => ['required', 'string', function ($attribute, $value, $fail) {
                json_decode($value);
                if (json_last_error() !== JSON_ERROR_NONE) {
                    $fail('The ' . $attribute . ' must be a valid JSON string.');
                }
            }],
            'preview_image_path' => 'nullable|image|mimes:jpg,jpeg,png,gif,webp|max:2048',
            'is_active' => 'nullable|boolean', // Changed to nullable for checkbox
            'is_premium' => 'nullable|boolean', // Changed to nullable for checkbox
            // 'sort_order' => 'nullable|integer', // Defaulted below
        ]);

        $data = $validatedData;
        $data['is_active'] = $request->has('is_active');
        $data['is_premium'] = $request->has('is_premium');
        $data['slug'] = Str::slug($validatedData['name']);

        if ($request->hasFile('preview_image_path')) {
            $data['preview_image_path'] = $request->file('preview_image_path')->store('webpilotai/style_previews', 'public');
        }

        $data['configuration'] = json_decode($validatedData['configuration'], true); // Store as array
        $data['sort_order'] = $request->input('sort_order', 0); // Default sort_order if not provided

        AIStylePreset::create($data);

        return redirect()->route('admin.webpilotai.ai-style-presets.index')->with('success', 'AI Style Preset created successfully.');
    }

    /**
     * Show the specified resource.
     */
    public function show(AIStylePreset $aiStylePreset) // Using Route Model Binding
    {
        return redirect()->route('admin.webpilotai.ai-style-presets.edit', $aiStylePreset->id);
    }

    /**
     * Show the form for editing the specified resource.
     */
    public function edit(AIStylePreset $aiStylePreset) // Using Route Model Binding
    {
        $types = [
            'color_palette' => 'Color Palette',
            'font_scheme' => 'Font Scheme',
            'visual_theme' => 'Visual Theme',
            'element_specific_styles' => 'Element Specific Styles',
            'direct_instruction' => 'Direct Instruction'
        ];
        // Pass the model instance as 'preset' if your view expects that name,
        // or 'aiStylePreset' if it expects that.
        // For consistency with Route Model Binding, using 'aiStylePreset' is good,
        // but if the view uses 'preset', we map it.
        return view('webpilotai::admin.ai_style_presets.edit', ['preset' => $aiStylePreset, 'types' => $types]);
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, AIStylePreset $aiStylePreset): RedirectResponse // Using Route Model Binding
    {
        $validatedData = $request->validate([
            'name' => 'required|string|max:255|unique:ai_style_presets,name,' . $aiStylePreset->id,
            'description' => 'nullable|string',
            'type' => 'required|string|in:color_palette,font_scheme,visual_theme,element_specific_styles,direct_instruction',
            'configuration' => ['required', 'string', function ($attribute, $value, $fail) {
                json_decode($value);
                if (json_last_error() !== JSON_ERROR_NONE) {
                    $fail('The ' . $attribute . ' must be a valid JSON string.');
                }
            }],
            'preview_image_path' => 'nullable|image|mimes:jpg,jpeg,png,gif,webp|max:2048',
            'is_active' => 'nullable|boolean',
            'is_premium' => 'nullable|boolean',
            // 'sort_order' => 'nullable|integer', // Handled below
        ]);

        $data = $validatedData;
        $data['is_active'] = $request->has('is_active');
        $data['is_premium'] = $request->has('is_premium');
        $data['slug'] = Str::slug($validatedData['name']);

        if ($request->hasFile('preview_image_path')) {
            // Delete old image if it exists
            if ($aiStylePreset->preview_image_path) {
                Storage::disk('public')->delete($aiStylePreset->preview_image_path);
            }
            $data['preview_image_path'] = $request->file('preview_image_path')->store('webpilotai/style_previews', 'public');
        } elseif ($request->has('remove_preview_image') && $aiStylePreset->preview_image_path) {
            // Handle removal of preview image
            Storage::disk('public')->delete($aiStylePreset->preview_image_path);
            $data['preview_image_path'] = null;
        }

        $data['configuration'] = json_decode($validatedData['configuration'], true);
        $data['sort_order'] = $request->input('sort_order', $aiStylePreset->sort_order); // Keep existing or update

        $aiStylePreset->update($data);

        return redirect()->route('admin.webpilotai.ai-style-presets.index')->with('success', 'AI Style Preset updated successfully.');
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(AIStylePreset $aiStylePreset): RedirectResponse // Using Route Model Binding
    {
        if ($aiStylePreset->preview_image_path) {
            Storage::disk('public')->delete($aiStylePreset->preview_image_path);
        }
        $aiStylePreset->delete();

        return redirect()->route('admin.webpilotai.ai-style-presets.index')->with('success', 'AI Style Preset deleted successfully.');
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Http\Controllers\Admin\AITemplateController.php ---
<?php

namespace Modules\WebPilotAI\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
// Illuminate\Http\Response; // Not strictly needed if not returning raw Response objects
use Modules\WebPilotAI\Models\AITemplate;
use Illuminate\Support\Str; // For slug generation
use Illuminate\Support\Facades\Storage; // For file storage

class AITemplateController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        $aiTemplates = AITemplate::orderBy('name')->paginate(15);
        return view('webpilotai::admin.ai_templates.index', compact('aiTemplates'));
    }

    /**
     * Show the form for creating a new resource.
     */
    public function create()
    {
        return view('webpilotai::admin.ai_templates.create');
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request): RedirectResponse
    {
        $validatedData = $request->validate([
            'name' => 'required|string|max:255|unique:ai_templates,name',
            'description' => 'nullable|string',
            'preview_image_path' => 'nullable|image|mimes:jpg,jpeg,png,gif,webp|max:2048', // Max 2MB
            'prompt_structure' => ['required', 'string', function ($attribute, $value, $fail) {
                if (!empty($value)) {
                    json_decode($value);
                    if (json_last_error() !== JSON_ERROR_NONE) {
                        $fail('The ' . $attribute . ' must be a valid JSON string.');
                    }
                }
            }],
            'is_active' => 'nullable|boolean',
            'is_premium' => 'nullable|boolean',
            // 'sort_order' => 'nullable|integer', // Defaulted below
        ]);

        $data = $validatedData;
        $data['slug'] = Str::slug($validatedData['name']);
        $data['is_active'] = $request->has('is_active');
        $data['is_premium'] = $request->has('is_premium');

        if ($request->hasFile('preview_image_path')) {
            $data['preview_image_path'] = $request->file('preview_image_path')->store('webpilotai/template_previews', 'public');
        }

        $data['prompt_structure'] = !empty($validatedData['prompt_structure']) ? json_decode($validatedData['prompt_structure'], true) : null;
        $data['sort_order'] = $request->input('sort_order', 0); // Default sort_order if not provided

        AITemplate::create($data);

        return redirect()->route('admin.webpilotai.ai-templates.index')->with('success', 'AI Template created successfully.');
    }

    /**
     * Show the specified resource.
     */
    public function show(AITemplate $aiTemplate) // Using Route Model Binding
    {
        // Typically not needed for admin CRUD, redirect to edit or index.
        return redirect()->route('admin.webpilotai.ai-templates.edit', $aiTemplate->id);
    }

    /**
     * Show the form for editing the specified resource.
     */
    public function edit(AITemplate $aiTemplate) // Using Route Model Binding
    {
        return view('webpilotai::admin.ai_templates.edit', compact('aiTemplate'));
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, AITemplate $aiTemplate): RedirectResponse // Using Route Model Binding
    {
        $validatedData = $request->validate([
            'name' => 'required|string|max:255|unique:ai_templates,name,' . $aiTemplate->id,
            'description' => 'nullable|string',
            'preview_image_path' => 'nullable|image|mimes:jpg,jpeg,png,gif,webp|max:2048',
            'prompt_structure' => ['required', 'string', function ($attribute, $value, $fail) {
                if (!empty($value)) {
                    json_decode($value);
                    if (json_last_error() !== JSON_ERROR_NONE) {
                        $fail('The ' . $attribute . ' must be a valid JSON string.');
                    }
                }
            }],
            'is_active' => 'nullable|boolean',
            'is_premium' => 'nullable|boolean',
            // 'sort_order' => 'nullable|integer', // Handled below
        ]);

        $data = $validatedData;
        $data['slug'] = Str::slug($validatedData['name']);
        $data['is_active'] = $request->has('is_active');
        $data['is_premium'] = $request->has('is_premium');

        if ($request->hasFile('preview_image_path')) {
            // Delete old image if it exists
            if ($aiTemplate->preview_image_path) {
                Storage::disk('public')->delete($aiTemplate->preview_image_path);
            }
            $data['preview_image_path'] = $request->file('preview_image_path')->store('webpilotai/template_previews', 'public');
        } elseif ($request->has('remove_preview_image') && $aiTemplate->preview_image_path) {
            // Handle removal of preview image
            Storage::disk('public')->delete($aiTemplate->preview_image_path);
            $data['preview_image_path'] = null;
        }

        $data['prompt_structure'] = !empty($validatedData['prompt_structure']) ? json_decode($validatedData['prompt_structure'], true) : null;
        $data['sort_order'] = $request->input('sort_order', $aiTemplate->sort_order); // Keep existing or update

        $aiTemplate->update($data);

        return redirect()->route('admin.webpilotai.ai-templates.index')->with('success', 'AI Template updated successfully.');
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(AITemplate $aiTemplate): RedirectResponse // Using Route Model Binding
    {
        if ($aiTemplate->preview_image_path) {
            Storage::disk('public')->delete($aiTemplate->preview_image_path);
        }
        $aiTemplate->delete();

        return redirect()->route('admin.webpilotai.ai-templates.index')->with('success', 'AI Template deleted successfully.');
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Http\Controllers\Admin\DashboardController.php ---
<?php

namespace Modules\WebPilotAI\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class DashboardController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        return view('webpilotai::admin.dashboard');
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Http\Controllers\Admin\ModuleSettingsController.php ---
<?php

namespace Modules\WebPilotAI\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use App\Models\Setting; // Assuming your Setting model is in App\Models
use Illuminate\Validation\Rule;

class ModuleSettingsController extends Controller
{
    private string $moduleName = 'WebPilotAI';
    private string $moduleAlias = 'webpilotai';

    /**
     * Display the module settings page.
     */
    public function index()
    {
        $settingsConfig = $this->getSettingsConfig();
        $currentSettings = [];

        foreach ($settingsConfig as $setting) {
            // Fetch current value from the database settings table
            $dbSetting = Setting::where('key', $this->moduleAlias . '.' . $setting['key'])->first();
            if ($dbSetting) {
                $currentSettings[$setting['key']] = $this->castSettingValue($dbSetting->value, $setting['type']);
            } else {
                $currentSettings[$setting['key']] = $setting['default'] ?? null;
            }
        }

        return view('webpilotai::admin.modulesettings.index', compact('settingsConfig', 'currentSettings'));
    }

    /**
     * Store the updated module settings.
     */
    public function store(Request $request): RedirectResponse
    {
        $settingsConfig = $this->getSettingsConfig();
        $rules = [];
        $newData = [];
        $originalValues = []; // To store original values for password fields

        foreach ($settingsConfig as $setting) {
            $key = $setting['key'];
            $fullKey = $this->moduleAlias . '.' . $key;

            switch ($setting['type']) {
                case 'boolean':
                    $rules[$key] = 'nullable|boolean';
                    $newData[$fullKey] = $request->has($key); // Checkbox handling
                    break;
                case 'number':
                    $rules[$key] = 'nullable|numeric';
                    $newData[$fullKey] = $request->input($key);
                    break;
                case 'text':
                case 'password':
                case 'textarea':
                case 'select':
                default:
                    $rules[$key] = 'nullable|string';
                    $inputValue = $request->input($key);

                    // For password fields, if they are submitted empty, keep the existing value
                    if ($setting['type'] === 'password' && empty($inputValue)) {
                        $dbSetting = Setting::where('key', $fullKey)->first();
                        $newData[$fullKey] = $dbSetting ? $dbSetting->value : null; // Keep existing encrypted or plain value
                    } else {
                        $newData[$fullKey] = $inputValue;
                    }
                    break;
            }
        }

        $request->validate($rules);

        try {
            foreach ($settingsConfig as $setting) {
                $fullKey = $this->moduleAlias . '.' . $setting['key'];
                $valueToStore = $this->prepareValueForStorage($newData[$fullKey] ?? null, $setting['type']);

                Setting::updateOrCreate(
                    ['key' => $fullKey],
                    [
                        'name' => $setting['label'], // Use 'label' from module.json for the 'name' column
                        'value' => $valueToStore,
                        'type' => $setting['type'],
                        'group' => $setting['group'] ?? $this->moduleAlias,
                    ]
                );
            }

            // Clear application cache if your Setting model or a service uses caching for settings.
            // Artisan::call('cache:clear'); // Or a more specific cache tag if you use one.
            
            // If your application loads settings from the DB into the config() array at boot,
            // then clearing config cache might be necessary.
            // Artisan::call('config:clear'); // Consider if this is needed for your setup.

        } catch (\Exception $e) {
            Log::error("Failed to write WebPilotAI settings to config file: " . $e->getMessage());
            return redirect()->back()->withErrors(['msg' => 'Failed to save settings. Check file permissions or logs.']);
        }

        return redirect()->route('admin.webpilotai.settings.index')->with('success', $this->moduleName . ' settings updated successfully!');
    }

    private function getSettingsConfig(): array
    {
        $moduleJsonPath = module_path($this->moduleName, 'module.json');
        if (File::exists($moduleJsonPath)) {
            $moduleConfig = json_decode(File::get($moduleJsonPath), true);
            return $moduleConfig['settings'] ?? [];
        }
        return [];
    }

    /**
     * Casts the setting value from string (database) to its correct type.
     */
    private function castSettingValue(?string $value, string $type)
    {
        if ($value === null) {
            return null;
        }
        switch ($type) {
            case 'boolean':
                return filter_var($value, FILTER_VALIDATE_BOOLEAN);
            case 'number':
                return is_numeric($value) ? ($value + 0) : $value; // Converts to int/float
            // case 'array': // If you store JSON strings for arrays
            //     return json_decode($value, true);
            default:
                return $value;
        }
    }

    private function prepareValueForStorage($value, string $type): ?string
    {
        if ($type === 'boolean') {
            return $value ? '1' : '0';
        }
        return $value === null ? null : (string) $value;
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Http\Controllers\Admin\UserSiteController.php ---
<?php

namespace Modules\WebPilotAI\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Modules\WebPilotAI\Models\Website;
use App\Models\User; // Assuming your User model is in App\Models
use Modules\WebPilotAI\Models\AIModel;

class UserSiteController extends Controller
{
    /**
     * Display a listing of all user websites.
     */
    public function index()
    {
        $websites = Website::with(['user', 'aiModel']) // Eager load relationships
                            ->orderBy('created_at', 'desc')
                            ->paginate(15);
        return view('webpilotai::admin.usersites.index', compact('websites'));
    }

    /**
     * Show the form for creating a new website (by admin).
     */
    public function create()
    {
        $users = User::orderBy('name')->get();
        $aiModels = AIModel::where('is_active', true)->orderBy('name')->get();
        return view('webpilotai::admin.usersites.create', compact('users', 'aiModels'));
    }

    /**
     * Store a newly created website in storage (by admin).
     */
    public function store(Request $request): RedirectResponse
    {
        $validatedData = $request->validate([
            'user_id' => 'required|exists:users,id',
            'name' => 'nullable|string|max:255',
            'description_prompt' => 'required|string|min:10',
            'ai_model_id' => 'required|exists:ai_models,id',
            'status' => 'required|string', // Admin can set status directly
        ]);

        Website::create($validatedData);

        return redirect()->route('admin.webpilotai.user-sites.index')->with('success', 'User website created successfully.');
    }

    /**
     * Display the specified website.
     */
    public function show(Website $user_site) // Route model binding (Laravel 7+ uses singular for resource param)
    {
        $user_site->load(['user', 'aiModel']);
        return view('webpilotai::admin.usersites.show', ['website' => $user_site]);
    }

    /**
     * Show the form for editing the specified website.
     */
    public function edit(Website $user_site)
    {
        $users = User::orderBy('name')->get();
        $aiModels = AIModel::orderBy('name')->get(); // Get all models, admin might assign inactive one
        return view('webpilotai::admin.usersites.edit', ['website' => $user_site, 'users' => $users, 'aiModels' => $aiModels]);
    }

    /**
     * Update the specified website in storage.
     */
    public function update(Request $request, Website $user_site): RedirectResponse
    {
        $validatedData = $request->validate([
            'user_id' => 'required|exists:users,id',
            'name' => 'nullable|string|max:255',
            'description_prompt' => 'required|string|min:10',
            'ai_model_id' => 'required|exists:ai_models,id',
            'status' => 'required|string',
            'generated_content_path' => 'nullable|string',
            'generation_error' => 'nullable|string',
        ]);

        $user_site->update($validatedData);

        return redirect()->route('admin.webpilotai.user-sites.index')->with('success', 'User website updated successfully.');
    }

    // destroy method can be added if admin should be able to delete sites
    // public function destroy(Website $user_site) { ... }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Http\Controllers\User\WebsiteController.php ---
<?php

namespace Modules\WebPilotAI\Http\Controllers\User;

use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\Rule;
use Modules\WebPilotAI\Models\AIModel;
use Modules\WebPilotAI\Models\AITemplate; // Import AITemplate model
use Modules\WebPilotAI\Models\AIStylePreset; // Import AIStylePreset model
use Modules\WebPilotAI\Models\Website;
use Modules\WebPilotAI\Jobs\ProcessWebsiteGeneration;
use Illuminate\Support\Facades\Storage; // For deleting generated content

class WebsiteController extends Controller
{
    /**
     * Display a listing of the user's websites.
     */
    public function index()
    {
        $websites = Website::where('user_id', Auth::id())
                            ->with('aiModel') // Eager load the AI model relationship
                            ->orderBy('created_at', 'desc')
                            ->paginate(10); // Paginate results

        return view('webpilotai::user.websites.index', compact('websites'));
    }

    /**
     * Show the form for creating a new website.
     */
    public function create()
    {
        $aiModels = AIModel::where('is_active', true)->orderBy('provider')->orderBy('name')->get();
        $aiTemplates = AITemplate::where('is_active', true)
            ->orderBy('sort_order')
            ->orderBy('name')
            ->get();
        $aiStylePresets = AIStylePreset::where('is_active', true)
            ->orderBy('type') // Group by type visually if needed in view, or sort by name
            ->orderBy('sort_order')
            ->orderBy('name')
            ->get();

        return view('webpilotai::user.websites.create', compact('aiModels', 'aiTemplates', 'aiStylePresets'));
    }

    /**
     * Store a newly created website in storage.
     */
    public function store(Request $request): RedirectResponse
    {
        $validatedData = $request->validate([
            'name' => 'nullable|string|max:255',
            'description_prompt' => 'required|string|min:50', // Min length for a decent prompt
            'ai_model_id' => [
                'required',
                Rule::exists('ai_models', 'id')->where(function ($query) {
                    $query->where('is_active', true);
                }),
            ],
            'ai_template_id' => 'nullable|exists:ai_templates,id', // Validate template ID
            'ai_style_preset_ids' => 'nullable|array',
            'ai_style_preset_ids.*' => 'nullable|exists:ai_style_presets,id', // Validate each style preset ID
        ]);

        $website = Website::create([
            'user_id' => Auth::id(),
            'name' => $validatedData['name'] ?? 'My AI Website - ' . now()->format('Y-m-d H:i'),
            'description_prompt' => $validatedData['description_prompt'],
            'ai_model_id' => $validatedData['ai_model_id'],
            'ai_template_id' => $validatedData['ai_template_id'] ?? null,
            'status' => 'pending_generation', // Initial status
        ]);


        // Sync AI Style Presets
        if ($request->has('ai_style_preset_ids')) {
            $website->aiStylePresets()->sync($validatedData['ai_style_preset_ids']);
        }

        // Dispatch the job to handle AI generation
        ProcessWebsiteGeneration::dispatch($website);

        return redirect()->route('frontend.webpilotai.websites.index')->with('success', 'Your website request has been submitted! Generation will begin shortly.');
    }

    /**
     * Display the specified website.
     */
    public function show(Website $website) // Route model binding
    {
        // Ensure the authenticated user owns this website
        $this->authorize('view', $website); // Requires a WebsitePolicy

        return view('webpilotai::user.websites.show', compact('website'));
    }

    /**
     * Show the form for editing the specified website.
     */
    public function edit(Website $website) // Route model binding
    {
        $this->authorize('update', $website); // Requires a WebsitePolicy

        $aiModels = AIModel::where('is_active', true)->orderBy('provider')->orderBy('name')->get();
        $aiTemplates = AITemplate::where('is_active', true)
            ->orderBy('sort_order')
            ->orderBy('name')
            ->get();
        $aiStylePresets = AIStylePreset::where('is_active', true)
            ->orderBy('type')
            ->orderBy('sort_order')
            ->orderBy('name')
            ->get();

        return view('webpilotai::user.websites.edit', compact('website', 'aiModels', 'aiTemplates', 'aiStylePresets'));
    }

    /**
     * Update the specified website in storage.
     */
    public function update(Request $request, Website $website): RedirectResponse
    {
        $this->authorize('update', $website);

        $validatedData = $request->validate([
            'name' => 'nullable|string|max:255',
            'description_prompt' => 'required|string|min:50',
            'ai_model_id' => [
                'required',
                Rule::exists('ai_models', 'id')->where(function ($query) {
                    $query->where('is_active', true);
                }),
            ],
            'ai_template_id' => 'nullable|exists:ai_templates,id',
            'ai_style_preset_ids' => 'nullable|array',
            'ai_style_preset_ids.*' => 'nullable|exists:ai_style_presets,id',
        ]);

        $website->name = $validatedData['name'] ?? $website->name; // Keep old name if new one is not provided
        $website->description_prompt = $validatedData['description_prompt'];
        $website->ai_model_id = $validatedData['ai_model_id'];
        $website->ai_template_id = $validatedData['ai_template_id'] ?? null;
        $website->status = Website::STATUS_PENDING_REGENERATION; // Or PENDING_GENERATION
        $website->generation_error = null; // Clear previous errors
        $website->save();

        // Sync AI Style Presets
        // If ai_style_preset_ids is not present in the request (e.g., all checkboxes unchecked),
        // sync with an empty array to remove all existing associations.
        $website->aiStylePresets()->sync($request->input('ai_style_preset_ids', []));

        ProcessWebsiteGeneration::dispatch($website);

        return redirect()->route('frontend.webpilotai.websites.show', $website->id)->with('success', 'Website details updated. Regeneration process has started.');
    }

    /**
     * Remove the specified website from storage.
     */
    public function destroy(Website $website): RedirectResponse
    {
        $this->authorize('delete', $website);
        // Optionally: Delete generated_content_path file from storage
        // if ($website->generated_content_path && Storage::disk('public')->exists(str_replace('storage/', '', $website->generated_content_path))) {
        //     Storage::disk('public')->delete(str_replace('storage/', '', $website->generated_content_path));
        // }
        $website->delete();
        return redirect()->route('frontend.webpilotai.websites.index')->with('success', 'Website deleted successfully.');
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Http\Controllers\WebPilotAIController.php ---
<?php

namespace Modules\WebPilotAI\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class WebPilotAIController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        return view('webpilotai::index');
    }

    /**
     * Show the form for creating a new resource.
     */
    public function create()
    {
        return view('webpilotai::create');
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request) {}

    /**
     * Show the specified resource.
     */
    public function show($id)
    {
        return view('webpilotai::show');
    }

    /**
     * Show the form for editing the specified resource.
     */
    public function edit($id)
    {
        return view('webpilotai::edit');
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, $id) {}

    /**
     * Remove the specified resource from storage.
     */
    public function destroy($id) {}
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Jobs\DeployWebsiteToHosting.php ---
<?php

namespace Modules\WebPilotAI\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Modules\WebPilotAI\Models\Website;
use Throwable;
use ZipArchive; // For unzipping
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Crypt; // For password encryption/decryption
use League\Flysystem\Filesystem;
use League\Flysystem\Ftp\FtpAdapter as FlysystemFtpAdapter; // Renamed to avoid conflict
use Illuminate\Support\Facades\Http; // For cPanel API calls
use League\Flysystem\PhpseclibV3\SftpAdapter;
use League\Flysystem\PhpseclibV3\SftpConnectionProvider;

use Modules\WebPilotAI\Events\WebsiteDeploymentSucceeded; // Import Success Event
use Modules\WebPilotAI\Events\WebsiteDeploymentFailed; // Import Failure Event

class DeployWebsiteToHosting implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public Website $website;
    public array $deploymentConfig;

    /**
     * Create a new job instance.
     */
    public function __construct(Website $website, array $deploymentConfig)
    {
        $this->website = $website;
        // Password is now encrypted by the controller before dispatching.
        $this->deploymentConfig = $deploymentConfig;
    }

    /**
     * Execute the job.
     */
    public function handle(): void
    {
        $hostDisplay = $this->deploymentConfig['host'] ?? 'N/A';
        Log::info("Starting deployment for website ID: {$this->website->id} to host: {$hostDisplay}");
        $this->website->update(['status' => Website::STATUS_DEPLOYING, 'generation_error' => null]); // Clear previous errors

        $password = null;
        if (isset($this->deploymentConfig['password'])) {
            $password = Crypt::decryptString($this->deploymentConfig['password']);
        }
        $tempDir = null;

        try {
            $zipStoragePath = str_replace('storage/', 'public/', $this->website->generated_content_path);
            if (!$this->website->generated_content_path || !Storage::disk('local')->exists($zipStoragePath)) {
                throw new \Exception("Generated website ZIP file not found at '{$zipStoragePath}' for website ID: {$this->website->id}.");
            }

            $absoluteZipPath = Storage::disk('local')->path($zipStoragePath);

            // 1. Create a temporary directory to extract ZIP contents
            $tempDir = storage_path('app/temp_deploy_' . $this->website->id . '_' . time());
            File::ensureDirectoryExists($tempDir);

            // 2. Unzip the contents
            $zip = new ZipArchive;
            if ($zip->open($absoluteZipPath) === TRUE) {
                $zip->extractTo($tempDir);
                $zip->close();
                Log::info("Successfully unzipped {$absoluteZipPath} to {$tempDir} for website ID: {$this->website->id}");
            } else {
                throw new \Exception("Failed to unzip archive for website ID: {$this->website->id}");
            }

            // 3. Implement deployment logic based on type
            $deploymentType = $this->deploymentConfig['deployment_type'];

            if ($deploymentType === 'sftp') {
                Log::info("Attempting SFTP deployment for website ID: {$this->website->id}");

                // Check for password or private key (assuming you might add private_key_path to deploymentConfig)
                if (empty($password) && empty($this->deploymentConfig['private_key_path'])) {
                    throw new \InvalidArgumentException("SFTP deployment requires either a password or a private key.");
                }

                $sftpConfig = [
                    'host' => $this->deploymentConfig['host'],
                    'username' => $this->deploymentConfig['username'],
                    'password' => $password, // Use the (potentially decrypted) password
                    'port' => (int) ($this->deploymentConfig['port'] ?? 22),
                    'root' => $this->deploymentConfig['remote_path'] ?? '/', // Remote base path
                    // 'privateKey' => $this->deploymentConfig['private_key_path'] ?? null, // Path to private key
                    // 'passphrase' => $this->deploymentConfig['private_key_passphrase'] ?? null, // Passphrase for private key
                    'timeout' => 30, // Connection timeout
                ];

                $provider = new SftpConnectionProvider(
                    $sftpConfig['host'],
                    $sftpConfig['username'],
                    $sftpConfig['password'],
                    $this->deploymentConfig['private_key_path'] ?? null, // private key path
                    $this->deploymentConfig['private_key_passphrase'] ?? null, // passphrase for private key
                    $sftpConfig['port'],
                    false, // use agent
                    $sftpConfig['timeout']
                );

                $adapter = new SftpAdapter($provider, $sftpConfig['root']);
                $filesystem = new Filesystem($adapter);

                // Upload files from $tempDir
                $localFiles = File::allFiles($tempDir);

                foreach ($localFiles as $localFile) {
                    $relativePath = $localFile->getRelativePathname();
                    $remoteFilePath = $relativePath; // Path relative to the SFTP root

                    Log::debug("Uploading {$localFile->getPathname()} to SFTP path: {$remoteFilePath}");
                    $stream = fopen($localFile->getPathname(), 'r+');
                    $filesystem->writeStream($remoteFilePath, $stream);
                    if (is_resource($stream)) {
                        fclose($stream);
                    }
                    Log::info("Uploaded {$relativePath} to SFTP for website ID: {$this->website->id}");
                }
                Log::info("SFTP deployment completed for website ID: {$this->website->id}");

            } elseif ($deploymentType === 'ftp') {
                Log::info("Attempting FTP deployment for website ID: {$this->website->id}");

                if (empty($password)) {
                    throw new \InvalidArgumentException("FTP deployment requires a password.");
                }

                $ftpConfig = [
                    'host' => $this->deploymentConfig['host'],
                    'username' => $this->deploymentConfig['username'],
                    'password' => $password,
                    'port' => (int) ($this->deploymentConfig['port'] ?? 21),
                    'root' => $this->deploymentConfig['remote_path'] ?? '/',
                    'passive' => true, // Often required
                    'ssl' => false, // Set to true for FTPS
                    'timeout' => 30,
                ];

                $adapter = new FlysystemFtpAdapter($ftpConfig);
                $filesystem = new Filesystem($adapter);

                $localFiles = File::allFiles($tempDir);
                foreach ($localFiles as $localFile) {
                    $relativePath = $localFile->getRelativePathname();
                    $remoteFilePath = $relativePath;

                    Log::debug("Uploading {$localFile->getPathname()} to FTP path: {$remoteFilePath}");
                    $stream = fopen($localFile->getPathname(), 'r+');
                    $filesystem->writeStream($remoteFilePath, $stream);
                    if (is_resource($stream)) {
                        fclose($stream);
                    }
                    Log::info("Uploaded {$relativePath} to FTP for website ID: {$this->website->id}");
                }
                Log::info("FTP deployment completed for website ID: {$this->website->id}");

            } elseif ($deploymentType === 'cpanel') {
                Log::info("Attempting cPanel API deployment for website ID: {$this->website->id}");

                $cpanelHost = $this->deploymentConfig['host'];
                $cpanelUser = $this->deploymentConfig['username'];
                $remotePath = $this->deploymentConfig['remote_path'] ?? 'public_html'; // Default to public_html
                $cpanelPort = (int) ($this->deploymentConfig['port'] ?? 2083); // Default cPanel SSL port

                if (empty($password)) {
                    throw new \InvalidArgumentException("cPanel deployment requires a password (or API token if supported).");
                }

                // 1. Create a new temporary ZIP of the extracted contents
                $tempPackageName = 'webpilotai_deploy_package_' . time() . '.zip';
                $tempPackagePath = storage_path('app/' . $tempPackageName);
                $packageZip = new ZipArchive();

                if ($packageZip->open($tempPackagePath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
                    throw new \Exception("Could not create temporary package for cPanel deployment.");
                }
                $filesToZip = new \RecursiveIteratorIterator(
                    new \RecursiveDirectoryIterator($tempDir),
                    \RecursiveIteratorIterator::LEAVES_ONLY
                );
                foreach ($filesToZip as $name => $file) {
                    if (!$file->isDir()) {
                        $filePath = $file->getRealPath();
                        $relativePath = substr($filePath, strlen($tempDir) + 1);
                        $packageZip->addFile($filePath, $relativePath);
                    }
                }
                $packageZip->close();
                Log::info("Created temporary cPanel deployment package: {$tempPackagePath}");

                // 2. Upload the temporary package
                $uploadApiUrl = "https://{$cpanelHost}:{$cpanelPort}/execute/Fileman/upload_files";
                $responseUpload = Http::withBasicAuth($cpanelUser, $password)
                    ->attach('file-1', file_get_contents($tempPackagePath), $tempPackageName)
                    ->post($uploadApiUrl, ['dir' => $remotePath]);

                File::delete($tempPackagePath); // Clean up local temp package

                if (!$responseUpload->successful() || !empty($responseUpload->json()['errors'])) {
                    Log::error("cPanel API upload_files failed: " . $responseUpload->body());
                    throw new \Exception("cPanel API: Failed to upload package. Errors: " . implode(', ', $responseUpload->json()['errors'] ?? ['Unknown error']));
                }
                Log::info("cPanel API: Successfully uploaded {$tempPackageName} to {$remotePath}");

                // 3. Extract the uploaded package
                $extractApiUrl = "https://{$cpanelHost}:{$cpanelPort}/execute/Fileman/extract_files";
                $responseExtract = Http::withBasicAuth($cpanelUser, $password)
                    ->post($extractApiUrl, ['dir' => $remotePath, 'file' => $tempPackageName]);

                if (!$responseExtract->successful() || !empty($responseExtract->json()['errors'])) {
                    Log::error("cPanel API extract_files failed: " . $responseExtract->body());
                    Http::withBasicAuth($cpanelUser, $password)->post("https://{$cpanelHost}:{$cpanelPort}/execute/Fileman/unlink_files", ['dir' => $remotePath, 'files' => $tempPackageName]);
                    throw new \Exception("cPanel API: Failed to extract package. Errors: " . implode(', ', $responseExtract->json()['errors'] ?? ['Unknown error']));
                }
                Log::info("cPanel API: Successfully extracted {$tempPackageName} in {$remotePath}");

                // 4. Delete the uploaded package from the server
                $deleteApiUrl = "https://{$cpanelHost}:{$cpanelPort}/execute/Fileman/unlink_files";
                $responseDelete = Http::withBasicAuth($cpanelUser, $password)
                    ->post($deleteApiUrl, ['dir' => $remotePath, 'files' => $tempPackageName]);

                if (!$responseDelete->successful() || !empty($responseDelete->json()['errors'])) {
                    Log::warning("cPanel API: Failed to delete uploaded package {$tempPackageName} from server. Manual cleanup may be required. Errors: " . implode(', ', $responseDelete->json()['errors'] ?? ['Unknown error']));
                } else {
                    Log::info("cPanel API: Successfully deleted {$tempPackageName} from server path {$remotePath}");
                }
                Log::info("cPanel API deployment completed for website ID: {$this->website->id}");

            } else {
                Log::warning("Unsupported deployment type '{$deploymentType}' for website ID: {$this->website->id}.");
                throw new \Exception("Unsupported deployment type: {$deploymentType}");
            }

            // If successful, update status and store deployment details
            $this->website->update([
                'status' => Website::STATUS_DEPLOYED,
                'deployment_details' => [
                    'type' => $deploymentType,
                    'host' => $this->deploymentConfig['host'],
                    'username' => $this->deploymentConfig['username'], // Store username for reference
                    'remote_path' => $this->deploymentConfig['remote_path'] ?? ($deploymentType === 'cpanel' ? 'public_html' : '/'),
                    'port' => $this->deploymentConfig['port'] ?? ($deploymentType === 'sftp' ? 22 : ($deploymentType === 'ftp' ? 21 : ($deploymentType === 'cpanel' ? 2083 : null))),
                    'cpanel_domain' => $this->deploymentConfig['cpanel_domain'] ?? null,
                    'deployed_at' => now()->toDateTimeString(),
                ],
                'generation_error' => null, // Clear any previous errors
            ]);
            Log::info("Website ID: {$this->website->id} marked as deployed.");
            WebsiteDeploymentSucceeded::dispatch($this->website);

        } catch (Throwable $e) {
            Log::error("Error deploying website ID {$this->website->id}: {$e->getMessage()} \n" . $e->getTraceAsString());
            $this->website->update([
                'status' => Website::STATUS_DEPLOYMENT_FAILED,
                'generation_error' => "Deployment Error: " . $e->getMessage()
            ]);
            WebsiteDeploymentFailed::dispatch($this->website, "Deployment Error: " . $e->getMessage());
        }
        finally {
            // Clean up the temporary directory
            if ($tempDir && File::isDirectory($tempDir)) {
                File::deleteDirectory($tempDir);
                Log::info("Cleaned up temporary directory: {$tempDir}");
            }
        }
    }

    /**
     * Handle a job failure.
     */
    public function failed(Throwable $exception): void
    {
        Log::critical("Deployment job permanently failed for website ID {$this->website->id}: {$exception->getMessage()}");
        // Ensure status is updated if not already set by the handle() method's catch block,
        // which can happen for unhandled exceptions or job timeouts.
        if ($this->website->status !== Website::STATUS_DEPLOYMENT_FAILED) {
            $this->website->update([
                'status' => Website::STATUS_DEPLOYMENT_FAILED,
                'generation_error' => "Deployment Job Failed: " . $exception->getMessage()
            ]);
        }
        WebsiteDeploymentFailed::dispatch($this->website, "Deployment Job Failed: " . $exception->getMessage(), true);
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Jobs\ProcessWebsiteGeneration.php ---
<?php

namespace Modules\WebPilotAI\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http; // Import Http facade
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Modules\WebPilotAI\Models\Website;
use App\Models\Setting; // Import Setting model
use Modules\WebPilotAI\Models\AITemplate; // Import AITemplate model
use Modules\WebPilotAI\Models\AIStylePreset; // Import AIStylePreset model
use Modules\WebPilotAI\Events\WebsiteGenerationSucceeded; // Import Success Event
use Modules\WebPilotAI\Events\WebsiteGenerationFailed; // Import Failure Event
use Throwable;

class ProcessWebsiteGeneration implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public Website $website;

    /**
     * Create a new job instance.
     */
    public function __construct(Website $website)
    {
        $this->website = $website;
    }

    /**
     * Execute the job.
     */
    public function handle(): void
    {
        Log::info("Starting AI website generation for website ID: {$this->website->id}");
        $this->website->update(['status' => 'generating']);

        try {
            // Eager load aiModel, aiTemplate, and aiStylePresets relationships
            $website = $this->website->load(['aiModel', 'aiTemplate', 'aiStylePresets']);
            $aiModel = $website->aiModel;

            if (!$aiModel || !$aiModel->identifier) {
                throw new \Exception("AI Model not found, not associated, or identifier missing for website ID: {$this->website->id}");
            }

            $apiKey = null;
            if ($aiModel->api_key_setting_name) {
                // Fetch API key from the database settings table
                $settingKey = 'webpilotai.' . $aiModel->api_key_setting_name;
                $apiKey = Setting::getValue($settingKey); // Use the static getValue method
            }

            if ($aiModel->provider === 'OpenAI' && !$apiKey) {
                throw new \Exception("OpenAI API key for model '{$aiModel->name}' (setting: '{$aiModel->api_key_setting_name}') is not configured in module settings.");
            }

            // Construct a detailed prompt for the AI
            $userPrompt = $this->website->description_prompt;
            $websiteName = $this->website->name ?: "AI Generated Website";

            $systemPrompt = "You are an expert web developer AI. Your task is to generate a complete, single-page HTML website. The website should be modern, responsive, and visually appealing. All CSS styles MUST be embedded within `<style>` tags in the `<head>` section. All JavaScript for basic interactivity MUST be embedded within `<script>` tags before the closing `</body>` tag. Do NOT use external CSS or JavaScript files. The output should be ONLY the raw HTML code for the complete page, starting with `<!DOCTYPE html>` and ending with `</html>`. Do not include any explanations, comments, or markdown formatting around the HTML code itself.";

            $fullUserPrompt = "Generate an HTML page for a website named '{$websiteName}'.\n";
            $fullUserPrompt .= "User Description: \"{$userPrompt}\"\n";
            $fullUserPrompt .= "Based on the description, include relevant sections such as a Header (with the website name), a Hero Section, an About Us section, a Services/Products section (if applicable), a Gallery (if applicable), a simple Contact Form (HTML structure only, no backend processing), and a Footer.\n";
            $fullUserPrompt .= "Ensure the design is coherent and professional. Use appropriate HTML5 semantic tags.";

            // Incorporate AI Template if selected
            $aiTemplate = $this->website->aiTemplate;
            $finalConstructedPrompt = $fullUserPrompt; // Default to the detailed user prompt

            if ($aiTemplate && !empty($aiTemplate->prompt_structure)) {
                $structure = $aiTemplate->prompt_structure; // This is an array

                Log::info("Website ID {$this->website->id}: Using AI Template '{$aiTemplate->name}' (ID: {$aiTemplate->id}). Structure: " . json_encode($structure));

                if (isset($structure['full_prompt_template']) && is_string($structure['full_prompt_template'])) {
                    // Use the full template, replacing [USER_PROMPT] with the original user's description_prompt
                    // and [DETAILED_USER_PROMPT] with the more structured $fullUserPrompt we built.
                    // Or, if the template is simpler, it might just expect [USER_PROMPT].
                    // For now, let's assume [USER_PROMPT] refers to the base description.
                    // And [WEBSITE_NAME] for the website name.
                    $tempPrompt = str_replace('[USER_PROMPT]', $userPrompt, $structure['full_prompt_template']);
                    $finalConstructedPrompt = str_replace('[WEBSITE_NAME]', $websiteName, $tempPrompt);
                } else {
                    // Fallback to prefix/suffix if full_prompt_template is not defined
                    // Apply prefix/suffix to the $fullUserPrompt
                    if (isset($structure['prefix']) && is_string($structure['prefix'])) {
                        $finalConstructedPrompt = rtrim($structure['prefix']) . "\n\n" . ltrim($finalConstructedPrompt);
                    }
                    if (isset($structure['suffix']) && is_string($structure['suffix'])) {
                        $finalConstructedPrompt = rtrim($finalConstructedPrompt) . "\n\n" . ltrim($structure['suffix']);
                    }
                }
            }

            // Incorporate AI Style Presets
            $styleContext = [
                'colors' => [], // e.g., 'primary' => '#RRGGBB', 'background' => '#RRGGBB'
                'fonts' => [],  // e.g., 'headings' => 'Arial, sans-serif', 'body' => 'Roboto, sans-serif'
                'theme_keywords' => [],
                'layout_hints' => [], // e.g., 'spacing' => 'generous', 'corners' => 'rounded'
                'element_specific' => [], // e.g., 'buttons' => ['shape' => 'pill', 'hover' => 'shadow']
                'raw_instructions' => [], // For direct pass-through instructions
            ];

            if ($this->website->aiStylePresets->isNotEmpty()) {
                Log::info("Website ID {$this->website->id}: Applying AI Style Presets.");
                foreach ($this->website->aiStylePresets as $stylePreset) {
                    Log::info("Website ID {$this->website->id}: Processing Style Preset '{$stylePreset->name}' (Type: {$stylePreset->type}). Configuration: " . json_encode($stylePreset->configuration));
                    if ($stylePreset->configuration && (is_object($stylePreset->configuration) || is_array($stylePreset->configuration))) {
                        $config = (array) $stylePreset->configuration; // Cast to array for easier handling

                        switch ($stylePreset->type) {
                            case 'color_palette':
                                foreach (['primary', 'secondary', 'accent', 'background', 'text', 'text_on_primary', 'text_on_secondary'] as $colorKey) {
                                    if (isset($config[$colorKey])) {
                                        if (is_string($config[$colorKey])) {
                                            $styleContext['colors'][$colorKey] = $config[$colorKey];
                                        } elseif (is_array($config[$colorKey]) && isset($config[$colorKey]['value'])) {
                                            // Handle more complex color definitions if they have a 'value'
                                            $styleContext['colors'][$colorKey] = $config[$colorKey]['value'];
                                            if (isset($config[$colorKey]['description'])) {
                                                $styleContext['raw_instructions'][] = "The {$colorKey} color ({$config[$colorKey]['value']}) is intended for: {$config[$colorKey]['description']}.";
                                            }
                                        }
                                    }
                                }
                                break;
                            case 'font_scheme':
                                foreach (['headings', 'body', 'captions', 'buttons'] as $fontTarget) {
                                    if (isset($config[$fontTarget])) {
                                        if (is_string($config[$fontTarget])) { // Simple font family string
                                            $styleContext['fonts'][$fontTarget] = $config[$fontTarget];
                                        } elseif (is_array($config[$fontTarget]) && isset($config[$fontTarget]['family'])) { // Complex font object
                                            $fontDetails = "font-family: '{$config[$fontTarget]['family']}'";
                                            if (isset($config[$fontTarget]['weight'])) $fontDetails .= "; font-weight: {$config[$fontTarget]['weight']}";
                                            if (isset($config[$fontTarget]['size'])) $fontDetails .= "; font-size: {$config[$fontTarget]['size']}";
                                            if (isset($config[$fontTarget]['style'])) $fontDetails .= "; font-style: {$config[$fontTarget]['style']}";
                                            $styleContext['fonts'][$fontTarget] = $fontDetails;
                                        }
                                    }
                                }
                                break;
                            case 'visual_theme':
                                if (isset($config['keywords']) && is_array($config['keywords'])) {
                                    $styleContext['theme_keywords'] = array_merge($styleContext['theme_keywords'], $config['keywords']);
                                }
                                if (isset($config['mood']) && is_string($config['mood'])) {
                                    $styleContext['raw_instructions'][] = "The overall mood should be: {$config['mood']}.";
                                }
                                foreach (['spacing', 'corner_style', 'shadow_intensity'] as $layoutKey) {
                                    if (isset($config[$layoutKey]) && is_string($config[$layoutKey])) {
                                        $styleContext['layout_hints'][$layoutKey] = $config[$layoutKey];
                                    }
                                }
                                break;
                            case 'element_specific_styles': // New type for granular control
                                if (is_array($config)) {
                                    foreach ($config as $elementKey => $elementStyles) {
                                        $styleContext['element_specific'][$elementKey] = array_merge($styleContext['element_specific'][$elementKey] ?? [], (array)$elementStyles);
                                    }
                                }
                                break;
                            case 'direct_instruction': // For presets that just contain raw instructions
                                if (isset($config['instruction']) && is_string($config['instruction'])) {
                                    $styleContext['raw_instructions'][] = $config['instruction'];
                                } elseif (isset($config['instructions']) && is_array($config['instructions'])) {
                                    $styleContext['raw_instructions'] = array_merge($styleContext['raw_instructions'], $config['instructions']);
                                }
                                break;
                        }
                    }
                }
            }

            // Consolidate unique keywords
            $styleContext['theme_keywords'] = array_unique($styleContext['theme_keywords']);
            $styleContext['raw_instructions'] = array_unique($styleContext['raw_instructions']);

            // Build the final style instructions for the prompt
            $finalStyleInstructions = [];

            if (!empty($styleContext['colors'])) {
                $colorDetails = [];
                foreach ($styleContext['colors'] as $key => $value) {
                    $colorDetails[] = Str::title(str_replace('_', ' ', $key)) . " color: {$value}";
                }
                if ($colorDetails) $finalStyleInstructions[] = "Color Palette: " . implode(', ', $colorDetails) . ".";
            }

            if (!empty($styleContext['fonts'])) {
                $fontDetails = [];
                foreach ($styleContext['fonts'] as $key => $value) {
                    $fontDetails[] = Str::title(str_replace('_', ' ', $key)) . " should use font(s) '{$value}'";
                }
                if ($fontDetails) $finalStyleInstructions[] = "Typography: " . implode('. ', $fontDetails) . ".";
            }

            if (!empty($styleContext['theme_keywords'])) {
                $finalStyleInstructions[] = "Overall Visual Theme Keywords: " . implode(', ', $styleContext['theme_keywords']) . ".";
            }

            if (!empty($styleContext['layout_hints'])) {
                $layoutDetails = [];
                foreach ($styleContext['layout_hints'] as $key => $value) {
                    $layoutDetails[] = Str::title(str_replace('_', ' ', $key)) . ": {$value}";
                }
                if ($layoutDetails) $finalStyleInstructions[] = "Layout & Feel: " . implode(', ', $layoutDetails) . ".";
            }

            if (!empty($styleContext['element_specific'])) {
                foreach ($styleContext['element_specific'] as $element => $styles) {
                    $elementStyleDetails = [];
                    foreach ($styles as $prop => $val) {
                        $elementStyleDetails[] = Str::title(str_replace('_', ' ', $prop)) . ": {$val}";
                    }
                    if ($elementStyleDetails) $finalStyleInstructions[] = "For {$element}: " . implode(', ', $elementStyleDetails) . ".";
                }
            }

            if (!empty($styleContext['raw_instructions'])) {
                $finalStyleInstructions[] = "Additional Styling Guidelines:\n- " . implode("\n- ", $styleContext['raw_instructions']);
            }

            if (!empty($finalStyleInstructions)) {
                $finalConstructedPrompt .= "\n\n--- STYLING GUIDELINES START ---\n" . implode("\n", $finalStyleInstructions) . "\n--- STYLING GUIDELINES END ---";
            }

            Log::debug("Final AI Prompt with Styles for website ID {$this->website->id}: {$finalConstructedPrompt}");

            $generatedHtmlContent = "";

            if ($aiModel->provider === 'OpenAI') {
                $client = \OpenAI::client($apiKey);
                $response = $client->chat()->create([
                    'model' => $aiModel->identifier, // e.g., 'gpt-3.5-turbo' or 'gpt-4-turbo-preview'
                    'messages' => [
                        ['role' => 'system', 'content' => $systemPrompt],
                        ['role' => 'user', 'content' => $finalConstructedPrompt], // Use the final constructed prompt
                    ],
                    'temperature' => 0.7, // Adjust for creativity
                    // 'max_tokens' => 3000, // Adjust based on expected output length and model limits
                ]);
                $generatedHtmlContent = $response->choices[0]->message->content;

                // Clean the output: sometimes AI wraps code in markdown
                if (Str::startsWith($generatedHtmlContent, '```html')) {
                    $generatedHtmlContent = Str::after($generatedHtmlContent, '```html');
                    $generatedHtmlContent = Str::beforeLast($generatedHtmlContent, '```');
                }
                $generatedHtmlContent = trim($generatedHtmlContent);

            } elseif ($aiModel->provider === 'Anthropic') {
                if (!$apiKey) {
                    throw new \Exception("Anthropic API key for model '{$aiModel->name}' (setting: '{$aiModel->api_key_setting_name}') is not configured in module settings.");
                }
                Log::info("Anthropic provider selected for website ID: {$this->website->id}. Attempting API call.");

                // Anthropic API endpoint for messages
                $anthropicApiUrl = 'https://api.anthropic.com/v1/messages';

                // Note: Anthropic's API is slightly different. It doesn't use a 'system' role in the same way.
                // We can prepend system-like instructions to the user prompt or use the 'system' parameter if available for the model.
                // For Messages API, the 'system' parameter is preferred.
                $response = Http::withHeaders([
                    'x-api-key' => $apiKey,
                    'anthropic-version' => '2023-06-01', // Required header
                    'content-type' => 'application/json',
                ])->timeout(120)->post($anthropicApiUrl, [ // Increased timeout for potentially longer generations
                    'model' => $aiModel->identifier,
                    'system' => $systemPrompt, // Pass the system prompt here
                    'messages' => [
                        ['role' => 'user', 'content' => $finalConstructedPrompt], // Use the final constructed prompt
                    ],
                    'max_tokens' => 3500, // Adjust as needed, Claude models can handle large contexts
                    'temperature' => 0.7,
                ]);

                if ($response->failed()) {
                    Log::error("Anthropic API call failed for website ID {$this->website->id}: " . $response->body());
                    throw new \Exception("Anthropic API call failed: " . $response->status() . " - " . $response->body());
                }

                $responseData = $response->json();
                // The main content is usually in responseData['content'][0]['text']
                $generatedHtmlContent = $responseData['content'][0]['text'] ?? '';
                $generatedHtmlContent = trim($generatedHtmlContent);

                sleep(2); // Simulate some processing
            } else {
                // Placeholder for other AI providers or if no provider matches
                Log::warning("AI Provider '{$aiModel->provider}' not implemented or no API key for website ID: {$this->website->id}. Using placeholder content.");
                $generatedHtmlContent = "<!DOCTYPE html><html><head><title>".$websiteName." (Placeholder)</title><style>body{font-family: sans-serif; margin: 20px; background-color: #f0f0f0;} h1{color: #333;}</style></head><body><h1>Welcome to ".$websiteName."</h1><p>AI Provider '{$aiModel->provider}' not fully implemented yet.</p><p>Original Prompt: {$userPrompt}</p>";
                if ($aiTemplate) $generatedHtmlContent .= "<p>Template '{$aiTemplate->name}' was considered.</p>";
                if ($this->website->aiStylePresets->isNotEmpty()) $generatedHtmlContent .= "<p>Style presets were considered: " . $this->website->aiStylePresets->pluck('name')->implode(', ') . ".</p>";
                $generatedHtmlContent .= "<p>This site was generated by WebPilotAI!</p></body></html>";
                sleep(2); // Simulate some processing
            }

            if (empty($generatedHtmlContent)) {
                throw new \Exception("AI returned empty content for website ID: {$this->website->id}");
            }

            // 4. Save the generated content
            $safeWebsiteName = Str::slug($websiteName ?: 'untitled-website');
            $htmlFilename = "index.html"; // Standard name inside the zip
            $zipFilename = "{$safeWebsiteName}_{$this->website->id}_" . time() . ".zip";
            
            // Define paths for storing the ZIP
            $zipDirectoryPath = "webpilotai_sites_zip/{$this->website->id}"; // Directory for this website's zips
            $fullZipStoragePath = "public/{$zipDirectoryPath}/{$zipFilename}"; // Full path for Storage facade
            $absoluteZipDiskPath = Storage::disk('local')->path($fullZipStoragePath); // Absolute path on disk for ZipArchive

            // Ensure the target directory for the ZIP exists
            File::ensureDirectoryExists(dirname($absoluteZipDiskPath));

            $zip = new \ZipArchive();
            if ($zip->open($absoluteZipDiskPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) === TRUE) {
                $zip->addFromString($htmlFilename, $generatedHtmlContent);
                $zip->close();
            } else {
                throw new \Exception("Could not create ZIP archive for website ID: {$this->website->id} at {$absoluteZipDiskPath}");
            }
            
            $this->website->update([
                'status' => 'completed',
                // Path relative to public/storage link for asset() helper
                'generated_content_path' => "storage/{$zipDirectoryPath}/{$zipFilename}", 
                'last_generated_at' => now(),
                'generation_error' => null,
            ]);
            Log::info("Successfully generated website ID: {$this->website->id}. ZIP archive saved to: {$fullZipStoragePath}");
            WebsiteGenerationSucceeded::dispatch($this->website);

        } catch (Throwable $e) {
            Log::error("Error generating website ID {$this->website->id}: {$e->getMessage()} \n" . $e->getTraceAsString());
            $this->website->update([
                'status' => 'failed',
                'generation_error' => $e->getMessage(),
            ]);
            WebsiteGenerationFailed::dispatch($this->website, $e->getMessage());
        }
    }

    /**
     * Handle a job failure.
    */
    public function failed(Throwable $exception): void
    {
        Log::error("Job failed for website ID {$this->website->id}: {$exception->getMessage()}");
        $this->website->update([
            'status' => 'failed',
            'generation_error' => "Job failed: " . $exception->getMessage(),
        ]);
        // Dispatch event for permanent job failure
        // The third parameter indicates the job failed permanently (not just an error within the handle method)
        WebsiteGenerationFailed::dispatch($this->website, "Job failed: " . $exception->getMessage(), true);
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Listeners\SendWebsiteDeploymentFailureNotification.php ---
<?php

namespace Modules\WebPilotAI\Listeners;

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Modules\WebPilotAI\Events\WebsiteDeploymentFailed;
use Modules\WebPilotAI\Mail\WebsiteDeploymentHasFailed;

class SendWebsiteDeploymentFailureNotification implements ShouldQueue
{
    use InteractsWithQueue;

    /**
     * Create the event listener.
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     */
    public function handle(WebsiteDeploymentFailed $event): void
    {
        if ($event->website->user && $event->website->user->email) {
            Mail::to($event->website->user->email)
                ->send(new WebsiteDeploymentHasFailed($event->website, $event->errorMessage));
            Log::info("Sent WebsiteDeploymentFailed email to {$event->website->user->email} for website ID: {$event->website->id}");
        } else {
            Log::warning("Could not send WebsiteDeploymentFailed email: User or user email missing for website ID: {$event->website->id}");
        }
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Listeners\SendWebsiteDeploymentSuccessNotification.php ---
<?php

namespace Modules\WebPilotAI\Listeners;

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Modules\WebPilotAI\Events\WebsiteDeploymentSucceeded;
use Modules\WebPilotAI\Mail\WebsiteDeployedSuccessfully;

class SendWebsiteDeploymentSuccessNotification implements ShouldQueue
{
    use InteractsWithQueue;

    /**
     * Create the event listener.
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     */
    public function handle(WebsiteDeploymentSucceeded $event): void
    {
        if ($event->website->user && $event->website->user->email) {
            Mail::to($event->website->user->email)
                ->send(new WebsiteDeployedSuccessfully($event->website));
            Log::info("Sent WebsiteDeploymentSucceeded email to {$event->website->user->email} for website ID: {$event->website->id}");
        } else {
            Log::warning("Could not send WebsiteDeploymentSucceeded email: User or user email missing for website ID: {$event->website->id}");
        }
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Listeners\SendWebsiteGenerationFailureNotification.php ---
<?php

namespace Modules\WebPilotAI\Listeners;

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Modules\WebPilotAI\Events\WebsiteGenerationFailed;
use Modules\WebPilotAI\Mail\WebsiteGenerationHasFailed;

class SendWebsiteGenerationFailureNotification implements ShouldQueue
{
    use InteractsWithQueue;

    /**
     * Create the event listener.
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     */
    public function handle(WebsiteGenerationFailed $event): void
    {
        if ($event->website->user && $event->website->user->email) {
            Mail::to($event->website->user->email)
                ->send(new WebsiteGenerationHasFailed($event->website, $event->errorMessage));
            Log::info("Sent WebsiteGenerationFailed email to {$event->website->user->email} for website ID: {$event->website->id}");
        } else {
            Log::warning("Could not send WebsiteGenerationFailed email: User or user email missing for website ID: {$event->website->id}");
        }
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Listeners\SendWebsiteGenerationSuccessNotification.php ---
<?php

namespace Modules\WebPilotAI\Listeners;

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Modules\WebPilotAI\Events\WebsiteGenerationSucceeded;
use Modules\WebPilotAI\Mail\WebsiteGeneratedSuccessfully;

class SendWebsiteGenerationSuccessNotification implements ShouldQueue
{
    use InteractsWithQueue;

    /**
     * Create the event listener.
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     */
    public function handle(WebsiteGenerationSucceeded $event): void
    {
        if ($event->website->user && $event->website->user->email) {
            Mail::to($event->website->user->email)
                ->send(new WebsiteGeneratedSuccessfully($event->website));
            Log::info("Sent WebsiteGenerationSucceeded email to {$event->website->user->email} for website ID: {$event->website->id}");
        } else {
            Log::warning("Could not send WebsiteGenerationSucceeded email: User or user email missing for website ID: {$event->website->id}");
        }
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Mail\WebsiteDeployedSuccessfully.php ---
<?php

namespace Modules\WebPilotAI\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
use Modules\WebPilotAI\Models\Website;

class WebsiteDeployedSuccessfully extends Mailable
{
    use Queueable, SerializesModels;

    public Website $website;

    /**
     * Create a new message instance.
     */
    public function __construct(Website $website)
    {
        $this->website = $website;
    }

    /**
     * Get the message envelope.
     */
    public function envelope(): Envelope
    {
        return new Envelope(
            subject: 'Deployment Successful for AI Website: "' . ($this->website->name ?: 'Untitled') . '"',
        );
    }

    /**
     * Get the message content definition.
     */
    public function content(): Content
    {
        return new Content(
            view: 'webpilotai::emails.deployment.success', // We'll create this Blade view next
        );
    }

    /**
     * Get the attachments for the message.
     *
     * @return array<int, \Illuminate\Mail\Mailables\Attachment>
     */
    public function attachments(): array
    {
        return [];
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Mail\WebsiteDeploymentHasFailed.php ---
<?php

namespace Modules\WebPilotAI\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
use Modules\WebPilotAI\Models\Website;

class WebsiteDeploymentHasFailed extends Mailable
{
    use Queueable, SerializesModels;

    public Website $website;
    public string $errorMessage;

    /**
     * Create a new message instance.
     */
    public function __construct(Website $website, string $errorMessage)
    {
        $this->website = $website;
        $this->errorMessage = $errorMessage;
    }

    /**
     * Get the message envelope.
     */
    public function envelope(): Envelope
    {
        return new Envelope(
            subject: 'Problem Deploying Your AI Website: "' . ($this->website->name ?: 'Untitled') . '"',
        );
    }

    /**
     * Get the message content definition.
     */
    public function content(): Content
    {
        return new Content(
            view: 'webpilotai::emails.deployment.failed', // We'll create this Blade view next
        );
    }

    /**
     * Get the attachments for the message.
     *
     * @return array<int, \Illuminate\Mail\Mailables\Attachment>
     */
    public function attachments(): array
    {
        return [];
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Mail\WebsiteGeneratedSuccessfully.php ---
<?php

namespace Modules\WebPilotAI\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
use Modules\WebPilotAI\Models\Website;

class WebsiteGeneratedSuccessfully extends Mailable
{
    use Queueable, SerializesModels;

    public Website $website;

    /**
     * Create a new message instance.
     */
    public function __construct(Website $website)
    {
        $this->website = $website;
    }

    /**
     * Get the message envelope.
     */
    public function envelope(): Envelope
    {
        return new Envelope(
            subject: 'Your AI Website "' . ($this->website->name ?: 'Untitled') . '" is Ready!',
        );
    }

    /**
     * Get the message content definition.
     */
    public function content(): Content
    {
        return new Content(
            view: 'webpilotai::emails.generation.success', // We'll create this Blade view next
        );
    }

    /**
     * Get the attachments for the message.
     *
     * @return array<int, \Illuminate\Mail\Mailables\Attachment>
     */
    public function attachments(): array
    {
        return [];
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Mail\WebsiteGenerationHasFailed.php ---
<?php

namespace Modules\WebPilotAI\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
use Modules\WebPilotAI\Models\Website;

class WebsiteGenerationHasFailed extends Mailable
{
    use Queueable, SerializesModels;

    public Website $website;
    public string $errorMessage;

    /**
     * Create a new message instance.
     */
    public function __construct(Website $website, string $errorMessage)
    {
        $this->website = $website;
        $this->errorMessage = $errorMessage;
    }

    /**
     * Get the message envelope.
     */
    public function envelope(): Envelope
    {
        return new Envelope(
            subject: 'Problem Generating Your AI Website: "' . ($this->website->name ?: 'Untitled') . '"',
        );
    }

    /**
     * Get the message content definition.
     */
    public function content(): Content
    {
        return new Content(
            view: 'webpilotai::emails.generation.failed', // We'll create this Blade view next
        );
    }

    /**
     * Get the attachments for the message.
     *
     * @return array<int, \Illuminate\Mail\Mailables\Attachment>
     */
    public function attachments(): array
    {
        return [];
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Models\AIModel.php ---
<?php

namespace Modules\WebPilotAI\Models;

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

class AIModel extends Model
{
    use HasFactory;

    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'ai_models';

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'identifier',
        'provider',
        'api_key_setting_name',
        'description',
        'is_active',
        'is_premium',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'is_active' => 'boolean',
        'is_premium' => 'boolean',
    ];

    // If you plan to use factories for testing or seeding:
    // protected static function newFactory()
    // {
    //     return \Modules\WebPilotAI\Database\factories\AIModelFactory::new();
    // }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Models\AIStylePreset.php ---
<?php

namespace Modules\WebPilotAI\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Support\Str;
use Illuminate\Database\Eloquent\Casts\AsArrayObject; // Keep this if you were using it
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class AIStylePreset extends Model
{
    use HasFactory;

    protected $table = 'ai_style_presets';

    protected $fillable = [
        'name',
        'slug',
        'description',
        'type',
        'configuration',
        'preview_image_path',
        'is_premium',
        'is_active',
        'sort_order',
    ];

    protected $casts = [
        'configuration' => AsArrayObject::class, // Or 'array' if you prefer plain arrays
        'is_premium' => 'boolean',
        'is_active' => 'boolean',
    ];

    /**
     * The websites that belong to the AI style preset.
     */
    public function websites(): BelongsToMany
    {
        return $this->belongsToMany(Website::class, 'ai_style_preset_website', 'ai_style_preset_id', 'website_id')
                    ->withTimestamps(); // Assuming your pivot table has timestamps (if not, remove this line)
    }

    // If you create a factory:
    // protected static function newFactory()
    // {
    //     return \Modules\WebPilotAI\Database\factories\AIStylePresetFactory::new();
    // }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Models\AITemplate.php ---
<?php

namespace Modules\WebPilotAI\Models;

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

class AITemplate extends Model
{
    use HasFactory;

    protected $table = 'ai_templates';

    protected $fillable = [
        'name',
        'slug',
        'description',
        'preview_image_path',
        'category',
        'tags',
        'prompt_modifications',
        'is_premium',
        'is_active',
        'sort_order',
    ];

    protected $casts = [
        'tags' => 'array',
        'prompt_modifications' => 'array',
        'is_premium' => 'boolean',
        'is_active' => 'boolean',
    ];

    // Accessor for slug generation if needed, or handle in observer/controller
    // public function setNameAttribute($value) {
    //     $this->attributes['name'] = $value;
    //     if (!isset($this->attributes['slug']) || empty($this->attributes['slug'])) {
    //         $this->attributes['slug'] = Str::slug($value);
    //     }
    // }

    // If you create a factory:
    // protected static function newFactory()
    // {
    //     return \Modules\WebPilotAI\Database\factories\AITemplateFactory::new();
    // }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Models\Website.php ---
<?php

namespace Modules\WebPilotAI\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use App\Models\User; // Assuming User model is in App\Models
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Modules\WebPilotAI\Models\AITemplate; // Import AITemplate model

class Website extends Model
{
    use HasFactory;

    // Status Constants
    public const STATUS_PENDING_GENERATION = 'pending_generation';
    public const STATUS_GENERATING = 'generating';
    public const STATUS_COMPLETED = 'completed';
    public const STATUS_FAILED = 'failed';
    public const STATUS_PENDING_REGENERATION = 'pending_regeneration';
    public const STATUS_DEPLOYING = 'deploying';
    public const STATUS_DEPLOYED = 'deployed';
    public const STATUS_DEPLOYMENT_FAILED = 'deployment_failed';


    protected $table = 'websites';

    protected $fillable = [
        'user_id',
        'name',
        'description_prompt',
        'ai_model_id',
        'ai_template_id', // Add ai_template_id here
        'status',
        'generated_content_path',
        'last_generated_at',
        'generation_error',
        'deployment_details',
    ];

    protected $casts = [
        'last_generated_at' => 'datetime',
        'deployment_details' => 'array',
    ];

    /**
     * Get the user that owns the website.
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }

    /**
     * Get the AI model used for generating this website.
     */
    public function aiModel(): BelongsTo
    {
        return $this->belongsTo(AIModel::class, 'ai_model_id');
    }

    /**
     * Get the AI template associated with the website.
     */
    public function aiTemplate(): BelongsTo
    {
        return $this->belongsTo(AITemplate::class, 'ai_template_id');
    }

    /**
     * The AI style presets that belong to the website.
     */
    public function aiStylePresets(): BelongsToMany
    {
        return $this->belongsToMany(AIStylePreset::class, 'ai_style_preset_website', 'website_id', 'ai_style_preset_id')
                    ->withTimestamps(); // Assuming your pivot table has timestamps (if not, remove this line)
    }

    // TODO: Add factory if needed: protected static function newFactory() { return \Modules\WebPilotAI\Database\factories\WebsiteFactory::new(); }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Policies\WebsitePolicy.php ---
<?php

namespace Modules\WebPilotAI\Policies; // Adjust if needed

use App\Models\User;
use Modules\WebPilotAI\Models\Website;
use Illuminate\Auth\Access\HandlesAuthorization;

class WebsitePolicy
{
    use HandlesAuthorization;

    /**
     * Determine whether the user can view any models.
     */
    public function viewAny(User $user): bool
    {
        return true; // Or based on permissions
    }

    /**
     * Determine whether the user can view the model.
     */
    public function view(User $user, Website $website): bool
    {
        return $user->id === $website->user_id;
    }

    /**
     * Determine whether the user can create models.
     */
    public function create(User $user): bool
    {
        return true; // Or based on subscription/permissions
    }

    /**
     * Determine whether the user can update the model.
     */
    public function update(User $user, Website $website): bool
    {
        return $user->id === $website->user_id;
    }

    /**
     * Determine whether the user can delete the model.
     */
    public function delete(User $user, Website $website): bool
    {
        return $user->id === $website->user_id;
    }

    // Add other policy methods as needed (restore, forceDelete)
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Providers\EventServiceProvider.php ---
<?php

namespace Modules\WebPilotAI\Providers;

use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Modules\WebPilotAI\Events\WebsiteGenerationSucceeded;
use Modules\WebPilotAI\Listeners\SendWebsiteGenerationSuccessNotification;
use Modules\WebPilotAI\Events\WebsiteGenerationFailed;
use Modules\WebPilotAI\Listeners\SendWebsiteGenerationFailureNotification;
use Modules\WebPilotAI\Events\WebsiteDeploymentSucceeded;
use Modules\WebPilotAI\Listeners\SendWebsiteDeploymentSuccessNotification;
use Modules\WebPilotAI\Events\WebsiteDeploymentFailed;
use Modules\WebPilotAI\Listeners\SendWebsiteDeploymentFailureNotification;

class EventServiceProvider extends ServiceProvider
{
    /**
     * The event to listener mappings for the application.
     *
     * @var array<class-string, array<int, class-string>>
     */
    protected $listen = [
        WebsiteGenerationSucceeded::class => [
            SendWebsiteGenerationSuccessNotification::class,
        ],
        WebsiteGenerationFailed::class => [
            SendWebsiteGenerationFailureNotification::class,
        ],
        WebsiteDeploymentSucceeded::class => [
            SendWebsiteDeploymentSuccessNotification::class,
        ],
        WebsiteDeploymentFailed::class => [
            SendWebsiteDeploymentFailureNotification::class,
        ],
    ];

    /**
     * Register any events for your application.
     */
    public function boot(): void
    {
        parent::boot();
    }

    // You can add shouldDiscoverEvents() method if you prefer event discovery
    // public function shouldDiscoverEvents(): bool { return true; }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Providers\RouteServiceProvider.php ---
<?php

namespace Modules\WebPilotAI\Providers;

use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route;

class RouteServiceProvider extends ServiceProvider
{
    protected string $name = 'WebPilotAI';

    /**
     * Called before routes are registered.
     *
     * Register any model bindings or pattern based filters.
     */
    public function boot(): void
    {
        parent::boot();
    }

    /**
     * Define the routes for the application.
     */
    public function map(): void
    {
        $this->mapApiRoutes();
        $this->mapWebRoutes();
    }

    /**
     * Define the "web" routes for the application.
     *
     * These routes all receive session state, CSRF protection, etc.
     */
    protected function mapWebRoutes(): void
    {
        Route::middleware('web')->group(module_path($this->name, '/routes/web.php'));
    }

    /**
     * Define the "api" routes for the application.
     *
     * These routes are typically stateless.
     */
    protected function mapApiRoutes(): void
    {
        Route::middleware('api')->prefix('api')->name('api.')->group(module_path($this->name, '/routes/api.php'));
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\Providers\WebPilotAIServiceProvider.php ---
<?php

namespace Modules\WebPilotAI\Providers;

use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
use Nwidart\Modules\Traits\PathNamespace;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;

class WebPilotAIServiceProvider extends ServiceProvider
{
    use PathNamespace;

    protected string $name = 'WebPilotAI';

    protected string $nameLower = 'webpilotai';

    /**
     * Boot the application events.
     */
    public function boot(): void
    {
        $this->registerCommands();
        $this->registerCommandSchedules();
        $this->registerTranslations();
        $this->registerConfig();
        $this->registerViews();
        $this->loadMigrationsFrom(module_path($this->name, 'database/migrations'));
    }

    /**
     * Register the service provider.
     */
    public function register(): void
    {
        $this->app->register(EventServiceProvider::class);
        $this->app->register(RouteServiceProvider::class);
    }

    /**
     * Register commands in the format of Command::class
     */
    protected function registerCommands(): void
    {
        // $this->commands([]);
    }

    /**
     * Register command Schedules.
     */
    protected function registerCommandSchedules(): void
    {
        // $this->app->booted(function () {
        //     $schedule = $this->app->make(Schedule::class);
        //     $schedule->command('inspire')->hourly();
        // });
    }

    /**
     * Register translations.
     */
    public function registerTranslations(): void
    {
        $langPath = resource_path('lang/modules/'.$this->nameLower);

        if (is_dir($langPath)) {
            $this->loadTranslationsFrom($langPath, $this->nameLower);
            $this->loadJsonTranslationsFrom($langPath);
        } else {
            $this->loadTranslationsFrom(module_path($this->name, 'lang'), $this->nameLower);
            $this->loadJsonTranslationsFrom(module_path($this->name, 'lang'));
        }
    }

    /**
     * Register config.
     */
    protected function registerConfig(): void
    {
        $configPath = module_path($this->name, config('modules.paths.generator.config.path'));

        if (is_dir($configPath)) {
            $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($configPath));

            foreach ($iterator as $file) {
                if ($file->isFile() && $file->getExtension() === 'php') {
                    $config = str_replace($configPath.DIRECTORY_SEPARATOR, '', $file->getPathname());
                    $config_key = str_replace([DIRECTORY_SEPARATOR, '.php'], ['.', ''], $config);
                    $segments = explode('.', $this->nameLower.'.'.$config_key);

                    // Remove duplicated adjacent segments
                    $normalized = [];
                    foreach ($segments as $segment) {
                        if (end($normalized) !== $segment) {
                            $normalized[] = $segment;
                        }
                    }

                    $key = ($config === 'config.php') ? $this->nameLower : implode('.', $normalized);

                    $this->publishes([$file->getPathname() => config_path($config)], 'config');
                    $this->merge_config_from($file->getPathname(), $key);
                }
            }
        }
    }

    /**
     * Merge config from the given path recursively.
     */
    protected function merge_config_from(string $path, string $key): void
    {
        $existing = config($key, []);
        $module_config = require $path;

        config([$key => array_replace_recursive($existing, $module_config)]);
    }

    /**
     * Register views.
     */
    public function registerViews(): void
    {
        $viewPath = resource_path('views/modules/'.$this->nameLower);
        $sourcePath = module_path($this->name, 'resources/views');

        $this->publishes([$sourcePath => $viewPath], ['views', $this->nameLower.'-module-views']);

        $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->nameLower);

        Blade::componentNamespace(config('modules.namespace').'\\' . $this->name . '\\View\\Components', $this->nameLower);
    }

    /**
     * Get the services provided by the provider.
     */
    public function provides(): array
    {
        return [];
    }

    private function getPublishableViewPaths(): array
    {
        $paths = [];
        foreach (config('view.paths') as $path) {
            if (is_dir($path.'/modules/'.$this->nameLower)) {
                $paths[] = $path.'/modules/'.$this->nameLower;
            }
        }

        return $paths;
    }
}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\admin\aimodels\create.blade.php ---
@extends('layouts.admin') {{-- Or your main admin layout --}}

@section('title', 'Create AI Model')
@section('header_title', 'Create New AI Model')

@section('content')
<div class="p-6">
    <div class="bg-white dark:bg-gray-800 shadow-md rounded-lg p-6">
        <h2 class="text-2xl font-semibold text-gray-700 dark:text-gray-200 mb-6">New AI Model Details</h2>

        @include('webpilotai::admin.partials._validation_errors')

        <form action="{{ route('admin.webpilotai.models.store') }}" method="POST">
            @csrf

            <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                {{-- Name --}}
                <div>
                    <label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Model Name <span class="text-red-500">*</span></label>
                    <input type="text" name="name" id="name" value="{{ old('name') }}" required
                           class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200"
                           placeholder="e.g., GPT-4 Turbo">
                </div>

                {{-- Provider --}}
                <div>
                    <label for="provider" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Provider <span class="text-red-500">*</span></label>
                    <select name="provider" id="provider" required
                            class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">
                        <option value="">-- Select Provider --</option>
                        <option value="OpenAI" {{ old('provider') == 'OpenAI' ? 'selected' : '' }}>OpenAI</option>
                        <option value="Anthropic" {{ old('provider') == 'Anthropic' ? 'selected' : '' }}>Anthropic</option>
                        <option value="GoogleAI" {{ old('provider') == 'GoogleAI' ? 'selected' : '' }}>GoogleAI (Gemini)</option>
                        {{-- Add other providers as they become available/supported --}}
                    </select>
                </div>
            </div>

            {{-- Identifier --}}
            <div class="mt-6">
                <label for="identifier" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Model Identifier <span class="text-red-500">*</span></label>
                <input type="text" name="identifier" id="identifier" value="{{ old('identifier') }}" required
                       class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200"
                       placeholder="e.g., gpt-4-turbo-preview or claude-3-opus-20240229">
                <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">The exact model ID used by the provider's API.</p>
            </div>

            {{-- API Key Setting Name --}}
            <div class="mt-6">
                <label for="api_key_setting_name" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">API Key Setting Name <span class="text-red-500">*</span></label>
                <input type="text" name="api_key_setting_name" id="api_key_setting_name" value="{{ old('api_key_setting_name') }}" required
                       class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200"
                       placeholder="e.g., openai_api_key or anthropic_api_key">
                <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">The key name (without 'webpilotai.' prefix) used in the main application's settings table to store the API key for this provider. E.g., if the setting is `webpilotai.openai_api_key`, enter `openai_api_key`.</p>
            </div>

            {{-- Description --}}
            <div class="mt-6">
                <label for="description" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Description (Optional)</label>
                <textarea name="description" id="description" rows="3"
                          class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200"
                          placeholder="Briefly describe this model's capabilities or intended use.">{{ old('description') }}</textarea>
            </div>


            {{-- Flags --}}
            <div class="mt-6 grid grid-cols-1 md:grid-cols-2 gap-6">
                <div>
                    <label for="is_active" class="flex items-center">
                        <input type="checkbox" name="is_active" id="is_active" value="1" {{ old('is_active', true) ? 'checked' : '' }}
                               class="h-4 w-4 text-indigo-600 border-gray-300 dark:border-gray-600 rounded focus:ring-indigo-500 dark:bg-gray-700 dark:focus:ring-offset-gray-800">
                        <span class="ml-2 text-sm text-gray-700 dark:text-gray-300">Active (Available for users)</span>
                    </label>
                </div>
                <div>
                    <label for="is_premium" class="flex items-center">
                        <input type="checkbox" name="is_premium" id="is_premium" value="1" {{ old('is_premium') ? 'checked' : '' }}
                               class="h-4 w-4 text-indigo-600 border-gray-300 dark:border-gray-600 rounded focus:ring-indigo-500 dark:bg-gray-700 dark:focus:ring-offset-gray-800">
                        <span class="ml-2 text-sm text-gray-700 dark:text-gray-300">Premium (Requires subscription/permission)</span>
                    </label>
                </div>
            </div>

            {{-- Actions --}}
            <div class="mt-8 pt-5 border-t border-gray-200 dark:border-gray-700">
                <div class="flex justify-end">
                    <a href="{{ route('admin.webpilotai.models.index') }}" class="bg-gray-200 hover:bg-gray-300 text-gray-700 font-bold py-2 px-4 rounded-lg mr-3 transition duration-150 ease-in-out dark:bg-gray-600 dark:text-gray-200 dark:hover:bg-gray-500">
                        Cancel
                    </a>
                    <button type="submit" class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded-lg shadow-md transition duration-150 ease-in-out">
                        <i class="fas fa-save mr-2"></i> Save Model
                    </button>
                </div>
            </div>
        </form>
    </div>
</div>
@endsection

@push('styles')
{{-- Add any specific styles for this page here if needed --}}
@endpush

@push('scripts')
{{-- Add any specific scripts for this page here if needed --}}
@endpush


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\admin\aimodels\edit.blade.php ---
@extends('layouts.admin') {{-- Or your main admin layout --}}

@section('title', 'Edit AI Model')
@section('header_title', 'Edit AI Model: ' . $aiModel->name)

@section('content')
<div class="p-6">
    <div class="bg-white dark:bg-gray-800 shadow-md rounded-lg p-6">
        <h2 class="text-2xl font-semibold text-gray-700 dark:text-gray-200 mb-6">Update AI Model Details</h2>

        @include('webpilotai::admin.partials._validation_errors')

        <form action="{{ route('admin.webpilotai.models.update', $aiModel->id) }}" method="POST">
            @csrf
            @method('PUT')

            <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                {{-- Name --}}
                <div>
                    <label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Model Name <span class="text-red-500">*</span></label>
                    <input type="text" name="name" id="name" value="{{ old('name', $aiModel->name) }}" required
                           class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200"
                           placeholder="e.g., GPT-4 Turbo">
                </div>

                {{-- Provider --}}
                <div>
                    <label for="provider" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Provider <span class="text-red-500">*</span></label>
                    <select name="provider" id="provider" required
                            class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">
                        <option value="">-- Select Provider --</option>
                        <option value="OpenAI" {{ old('provider', $aiModel->provider) == 'OpenAI' ? 'selected' : '' }}>OpenAI</option>
                        <option value="Anthropic" {{ old('provider', $aiModel->provider) == 'Anthropic' ? 'selected' : '' }}>Anthropic</option>
                        <option value="GoogleAI" {{ old('provider', $aiModel->provider) == 'GoogleAI' ? 'selected' : '' }}>GoogleAI (Gemini)</option>
                        {{-- Add other providers as they become available/supported --}}
                    </select>
                </div>
            </div>

            {{-- Identifier --}}
            <div class="mt-6">
                <label for="identifier" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Model Identifier <span class="text-red-500">*</span></label>
                <input type="text" name="identifier" id="identifier" value="{{ old('identifier', $aiModel->identifier) }}" required
                       class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200"
                       placeholder="e.g., gpt-4-turbo-preview or claude-3-opus-20240229">
                <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">The exact model ID used by the provider's API.</p>
            </div>

            {{-- API Key Setting Name --}}
            <div class="mt-6">
                <label for="api_key_setting_name" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">API Key Setting Name <span class="text-red-500">*</span></label>
                <input type="text" name="api_key_setting_name" id="api_key_setting_name" value="{{ old('api_key_setting_name', $aiModel->api_key_setting_name) }}" required
                       class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200"
                       placeholder="e.g., openai_api_key or anthropic_api_key">
                <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">The key name (without 'webpilotai.' prefix) used in the main application's settings table to store the API key for this provider. E.g., if the setting is `webpilotai.openai_api_key`, enter `openai_api_key`.</p>
            </div>

            {{-- Description --}}
            <div class="mt-6">
                <label for="description" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Description (Optional)</label>
                <textarea name="description" id="description" rows="3"
                          class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200"
                          placeholder="Briefly describe this model's capabilities or intended use.">{{ old('description', $aiModel->description) }}</textarea>
            </div>

            {{-- Flags --}}
            <div class="mt-6 grid grid-cols-1 md:grid-cols-2 gap-6">
                <div>
                    <label for="is_active" class="flex items-center">
                        <input type="checkbox" name="is_active" id="is_active" value="1" {{ old('is_active', $aiModel->is_active) ? 'checked' : '' }}
                               class="h-4 w-4 text-indigo-600 border-gray-300 dark:border-gray-600 rounded focus:ring-indigo-500 dark:bg-gray-700 dark:focus:ring-offset-gray-800">
                        <span class="ml-2 text-sm text-gray-700 dark:text-gray-300">Active (Available for users)</span>
                    </label>
                </div>
                <div>
                    <label for="is_premium" class="flex items-center">
                        <input type="checkbox" name="is_premium" id="is_premium" value="1" {{ old('is_premium', $aiModel->is_premium) ? 'checked' : '' }}
                               class="h-4 w-4 text-indigo-600 border-gray-300 dark:border-gray-600 rounded focus:ring-indigo-500 dark:bg-gray-700 dark:focus:ring-offset-gray-800">
                        <span class="ml-2 text-sm text-gray-700 dark:text-gray-300">Premium (Requires subscription/permission)</span>
                    </label>
                </div>
            </div>

            {{-- Actions --}}
            <div class="mt-8 pt-5 border-t border-gray-200 dark:border-gray-700">
                <div class="flex justify-end">
                    <a href="{{ route('admin.webpilotai.models.index') }}" class="bg-gray-200 hover:bg-gray-300 text-gray-700 font-bold py-2 px-4 rounded-lg mr-3 transition duration-150 ease-in-out dark:bg-gray-600 dark:text-gray-200 dark:hover:bg-gray-500">
                        Cancel
                    </a>
                    <button type="submit" class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded-lg shadow-md transition duration-150 ease-in-out">
                        <i class="fas fa-save mr-2"></i> Update Model
                    </button>
                </div>
            </div>
        </form>
    </div>
</div>
@endsection

@push('styles')
{{-- Add any specific styles for this page here if needed --}}
@endpush

@push('scripts')
{{-- Add any specific scripts for this page here if needed --}}
@endpush


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\admin\aimodels\index.blade.php ---
@extends('layouts.admin') {{-- Or your main admin layout --}}

@section('title', 'AI Models')
@section('header_title', 'Manage AI Models')

@section('content')
<div class="p-6">
    <div class="bg-white dark:bg-gray-800 shadow-md rounded-lg p-6">
        <div class="flex justify-between items-center mb-6">
            <h2 class="text-2xl font-semibold text-gray-700 dark:text-gray-200">AI Models List</h2>
            <a href="{{ route('admin.webpilotai.models.create') }}" class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-lg shadow-md transition duration-150 ease-in-out">
                <i class="fas fa-plus mr-2"></i> Add New Model
            </a>
        </div>

        @include('webpilotai::admin.partials._session_messages')

        @if($aiModels->isEmpty())
            <div class="text-center py-10">
                <p class="text-gray-500 dark:text-gray-400 text-lg">No AI Models configured.</p>
                <p class="mt-2 text-gray-400 dark:text-gray-500">Get started by adding a new AI model!</p>
            </div>
        @else
            <div class="overflow-x-auto">
                <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
                    <thead class="bg-gray-50 dark:bg-gray-700">
                        <tr>
                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Name</th>
                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Provider</th>
                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Identifier</th>
                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Status</th>
                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Premium</th>
                            <th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Actions</th>
                        </tr>
                    </thead>
                    <tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
                        @foreach ($aiModels as $model)
                        <tr>
                            <td class="px-6 py-4 whitespace-nowrap">
                                <div class="text-sm font-medium text-gray-900 dark:text-white">{{ $model->name }}</div>
                                @if($model->description)
                                <div class="text-xs text-gray-500 dark:text-gray-400 truncate" style="max-width: 200px;" title="{{ $model->description }}">{{ Str::limit($model->description, 40) }}</div>
                                @endif
                            </td>
                            <td class="px-6 py-4 whitespace-nowrap">
                                <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full 
                                    @if($model->provider === 'OpenAI') bg-sky-100 text-sky-800 dark:bg-sky-700 dark:text-sky-100
                                    @elseif($model->provider === 'Anthropic') bg-orange-100 text-orange-800 dark:bg-orange-700 dark:text-orange-100
                                    @elseif($model->provider === 'GoogleAI') bg-emerald-100 text-emerald-800 dark:bg-emerald-700 dark:text-emerald-100
                                    @else bg-gray-100 text-gray-800 dark:bg-gray-600 dark:text-gray-200 @endif">
                                    {{ $model->provider }}
                                </span>
                            </td>
                            <td class="px-6 py-4 whitespace-nowrap">
                                <div class="text-sm text-gray-500 dark:text-gray-400">{{ $model->identifier }}</div>
                            </td>
                            <td class="px-6 py-4 whitespace-nowrap">
                                @if ($model->is_active)
                                    <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800 dark:bg-green-700 dark:text-green-100">
                                        Active
                                    </span>
                                @else
                                    <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800 dark:bg-red-700 dark:text-red-100">
                                        Inactive
                                    </span>
                                @endif
                            </td>
                            <td class="px-6 py-4 whitespace-nowrap">
                                @if ($model->is_premium)
                                    <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800 dark:bg-yellow-600 dark:text-yellow-100">
                                        Yes
                                    </span>
                                @else
                                    <span class="text-xs text-gray-500 dark:text-gray-400">No</span>
                                @endif
                            </td>
                            <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
                                <a href="{{ route('admin.webpilotai.models.edit', $model->id) }}" class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-200 mr-3 transition duration-150 ease-in-out">
                                    <i class="fas fa-edit"></i> Edit
                                </a>
                                <form action="{{ route('admin.webpilotai.models.destroy', $model->id) }}" method="POST" class="inline-block" onsubmit="return confirm('Are you sure you want to delete this AI Model? This action cannot be undone.');">
                                    @csrf
                                    @method('DELETE')
                                    <button type="submit" class="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-200 transition duration-150 ease-in-out">
                                        <i class="fas fa-trash-alt"></i> Delete
                                    </button>
                                </form>
                            </td>
                        </tr>
                        @endforeach
                    </tbody>
                </table>
            </div>

            @if ($aiModels->hasPages())
                <div class="mt-6">
                    {{ $aiModels->links() }} {{-- Ensure your app is configured for Tailwind pagination views --}}
                </div>
            @endif
        @endif
    </div>
</div>
@endsection

@push('scripts')
{{-- Add any specific scripts for this page here if needed --}}
@endpush

@push('styles')
<style>
    /* You can add specific styles here if absolutely necessary, but prefer Tailwind classes */
</style>
@endpush


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\admin\ai_style_presets\create.blade.php ---
@extends('layouts.admin') {{-- Or your main admin layout --}}

@section('title', 'Create AI Style Preset')
@section('header_title', 'Create New AI Style Preset')

@section('content')
<div class="p-6">
    <div class="bg-white dark:bg-gray-800 shadow-md rounded-lg p-6">
        <h2 class="text-2xl font-semibold text-gray-700 dark:text-gray-200 mb-6">New AI Style Preset Details</h2>

        @include('webpilotai::admin.partials._validation_errors')

        <form action="{{ route('admin.webpilotai.ai-style-presets.store') }}" method="POST" enctype="multipart/form-data">
            @csrf

            <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                {{-- Name --}}
                <div>
                    <label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Preset Name <span class="text-red-500">*</span></label>
                    <input type="text" name="name" id="name" value="{{ old('name') }}" required
                           class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">
                </div>

                {{-- Type --}}
                <div>
                    <label for="type" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Preset Type <span class="text-red-500">*</span></label>
                    <select name="type" id="type" required
                            class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">
                        <option value="">-- Select Type --</option>
                        <option value="color_palette" {{ old('type') == 'color_palette' ? 'selected' : '' }}>Color Palette</option>
                        <option value="font_scheme" {{ old('type') == 'font_scheme' ? 'selected' : '' }}>Font Scheme</option>
                        <option value="visual_theme" {{ old('type') == 'visual_theme' ? 'selected' : '' }}>Visual Theme</option>
                        <option value="element_specific_styles" {{ old('type') == 'element_specific_styles' ? 'selected' : '' }}>Element Specific Styles</option>
                        <option value="direct_instruction" {{ old('type') == 'direct_instruction' ? 'selected' : '' }}>Direct Instruction</option>
                        {{-- Add other types as needed --}}
                    </select>
                </div>
            </div>

            {{-- Description --}}
            <div class="mt-6">
                <label for="description" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Description</label>
                <textarea name="description" id="description" rows="3"
                          class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">{{ old('description') }}</textarea>
                <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">A brief explanation of what this style preset does.</p>
            </div>

            {{-- Configuration (JSON) --}}
            <div class="mt-6">
                <label for="configuration" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Configuration (JSON) <span class="text-red-500">*</span></label>
                <textarea name="configuration" id="configuration" rows="10" required
                          class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm font-mono dark:text-gray-200"
                          placeholder='e.g., {"primary_color": "#aabbcc", "font_family": "Arial"}'
                          >{{ old('configuration') }}</textarea>
                <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
                    Enter valid JSON. Refer to documentation for structure based on 'Preset Type'.
                    <br>Example for Color Palette: <code>{"primary": "#3498db", "secondary": "#2ecc71"}</code>
                    <br>Example for Font Scheme: <code>{"headings": {"family": "Montserrat", "weight": "700"}, "body": "Lato"}</code>
                </p>
            </div>

            {{-- Preview Image --}}
            <div class="mt-6">
                <label for="preview_image" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Preview Image (Optional)</label>
                <input type="file" name="preview_image" id="preview_image"
                       class="mt-1 block w-full text-sm text-gray-900 dark:text-gray-200 border border-gray-300 dark:border-gray-600 rounded-lg cursor-pointer bg-gray-50 dark:bg-gray-700 focus:outline-none
                              file:mr-4 file:py-2 file:px-4
                              file:rounded-l-lg file:border-0
                              file:text-sm file:font-semibold
                              file:bg-indigo-50 dark:file:bg-indigo-800 file:text-indigo-700 dark:file:text-indigo-300
                              hover:file:bg-indigo-100 dark:hover:file:bg-indigo-700">
                <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Recommended size: 300x200px. Max 2MB.</p>
            </div>

            {{-- Flags --}}
            <div class="mt-6 grid grid-cols-1 md:grid-cols-2 gap-6">
                <div>
                    <label for="is_active" class="flex items-center">
                        <input type="checkbox" name="is_active" id="is_active" value="1" {{ old('is_active', true) ? 'checked' : '' }}
                               class="h-4 w-4 text-indigo-600 border-gray-300 dark:border-gray-600 rounded focus:ring-indigo-500 dark:bg-gray-700 dark:focus:ring-offset-gray-800">
                        <span class="ml-2 text-sm text-gray-700 dark:text-gray-300">Active (Available for users)</span>
                    </label>
                </div>
                <div>
                    <label for="is_premium" class="flex items-center">
                        <input type="checkbox" name="is_premium" id="is_premium" value="1" {{ old('is_premium') ? 'checked' : '' }}
                               class="h-4 w-4 text-indigo-600 border-gray-300 dark:border-gray-600 rounded focus:ring-indigo-500 dark:bg-gray-700 dark:focus:ring-offset-gray-800">
                        <span class="ml-2 text-sm text-gray-700 dark:text-gray-300">Premium (Requires subscription/permission)</span>
                    </label>
                </div>
            </div>

            {{-- Actions --}}
            <div class="mt-8 pt-5 border-t border-gray-200 dark:border-gray-700">
                <div class="flex justify-end">
                    <a href="{{ route('admin.webpilotai.ai-style-presets.index') }}" class="bg-gray-200 hover:bg-gray-300 text-gray-700 font-bold py-2 px-4 rounded-lg mr-3 transition duration-150 ease-in-out dark:bg-gray-600 dark:text-gray-200 dark:hover:bg-gray-500">
                        Cancel
                    </a>
                    <button type="submit" class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded-lg shadow-md transition duration-150 ease-in-out">
                        <i class="fas fa-save mr-2"></i> Save Preset
                    </button>
                </div>
            </div>
        </form>
    </div>
</div>
@endsection

@push('styles')
{{-- For a better JSON editing experience, consider CodeMirror or a similar library --}}
{{-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/codemirror.min.css"> --}}
{{-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/theme/material-darker.min.css"> --}}
@endpush

@push('scripts')
{{-- <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/codemirror.min.js"></script> --}}
{{-- <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/mode/javascript/javascript.min.js"></script> --}}
{{-- <script>
    // document.addEventListener('DOMContentLoaded', function () {
    //     var configTextArea = document.getElementById('configuration');
    //     if (configTextArea) {
    //         CodeMirror.fromTextArea(configTextArea, {
    //             lineNumbers: true,
    //             mode: { name: "javascript", json: true },
    //             theme: "material-darker", // or any other theme you prefer
    //             matchBrackets: true,
    //             autoCloseBrackets: true,
    //         });
    //     }
    // });
</script> --}}
@endpush

{{-- Create a partial for validation errors if you don't have one --}}
{{-- Example: resources/views/admin/partials/_validation_errors.blade.php --}}
{{--
@if ($errors->any())
    <div class="bg-red-100 dark:bg-red-700 border-l-4 border-red-500 dark:border-red-300 text-red-700 dark:text-red-200 p-4 mb-6" role="alert">
        <p class="font-bold">Please correct the following errors:</p>
        <ul class="mt-2 list-disc list-inside text-sm">
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif
--}}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\admin\ai_style_presets\edit.blade.php ---
@extends('layouts.admin') {{-- Or your main admin layout --}}

@section('title', 'Edit AI Style Preset')
@section('header_title', 'Edit AI Style Preset: ' . $aiStylePreset->name)

@section('content')
<div class="p-6">
    <div class="bg-white dark:bg-gray-800 shadow-md rounded-lg p-6">
        <h2 class="text-2xl font-semibold text-gray-700 dark:text-gray-200 mb-6">Update AI Style Preset Details</h2>

        @include('webpilotai::admin.partials._validation_errors')

        <form action="{{ route('admin.webpilotai.ai-style-presets.update', $aiStylePreset->id) }}" method="POST" enctype="multipart/form-data">
            @csrf
            @method('PUT')

            <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                {{-- Name --}}
                <div>
                    <label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Preset Name <span class="text-red-500">*</span></label>
                    <input type="text" name="name" id="name" value="{{ old('name', $aiStylePreset->name) }}" required
                           class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">
                </div>

                {{-- Type --}}
                <div>
                    <label for="type" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Preset Type <span class="text-red-500">*</span></label>
                    <select name="type" id="type" required
                            class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">
                        <option value="">-- Select Type --</option>
                        <option value="color_palette" {{ old('type', $aiStylePreset->type) == 'color_palette' ? 'selected' : '' }}>Color Palette</option>
                        <option value="font_scheme" {{ old('type', $aiStylePreset->type) == 'font_scheme' ? 'selected' : '' }}>Font Scheme</option>
                        <option value="visual_theme" {{ old('type', $aiStylePreset->type) == 'visual_theme' ? 'selected' : '' }}>Visual Theme</option>
                        <option value="element_specific_styles" {{ old('type', $aiStylePreset->type) == 'element_specific_styles' ? 'selected' : '' }}>Element Specific Styles</option>
                        <option value="direct_instruction" {{ old('type', $aiStylePreset->type) == 'direct_instruction' ? 'selected' : '' }}>Direct Instruction</option>
                        {{-- Add other types as needed --}}
                    </select>
                </div>
            </div>

            {{-- Description --}}
            <div class="mt-6">
                <label for="description" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Description</label>
                <textarea name="description" id="description" rows="3"
                          class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">{{ old('description', $aiStylePreset->description) }}</textarea>
                <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">A brief explanation of what this style preset does.</p>
            </div>

            {{-- Configuration (JSON) --}}
            <div class="mt-6">
                <label for="configuration" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Configuration (JSON) <span class="text-red-500">*</span></label>
                <textarea name="configuration" id="configuration" rows="10" required
                          class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm font-mono dark:text-gray-200"
                          placeholder='e.g., {"primary_color": "#aabbcc", "font_family": "Arial"}'
                          >{{ old('configuration', json_encode($aiStylePreset->configuration, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)) }}</textarea>
                <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
                    Enter valid JSON. Refer to documentation for structure based on 'Preset Type'.
                    <br>Example for Color Palette: <code>{"primary": "#3498db", "secondary": "#2ecc71"}</code>
                    <br>Example for Font Scheme: <code>{"headings": {"family": "Montserrat", "weight": "700"}, "body": "Lato"}</code>
                </p>
            </div>

            {{-- Preview Image --}}
            <div class="mt-6">
                <label for="preview_image" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Preview Image (Optional)</label>
                @if($aiStylePreset->preview_image_path)
                    <div class="mb-2">
                        <img src="{{ asset('storage/' . $aiStylePreset->preview_image_path) }}" alt="Current Preview" class="h-20 w-auto rounded border border-gray-300 dark:border-gray-600">
                        <label for="remove_preview_image" class="mt-1 text-xs flex items-center text-red-600 dark:text-red-400">
                            <input type="checkbox" name="remove_preview_image" id="remove_preview_image" value="1" class="mr-1 h-3 w-3"> Remove current image
                        </label>
                    </div>
                @endif
                <input type="file" name="preview_image" id="preview_image"
                       class="mt-1 block w-full text-sm text-gray-900 dark:text-gray-200 border border-gray-300 dark:border-gray-600 rounded-lg cursor-pointer bg-gray-50 dark:bg-gray-700 focus:outline-none
                              file:mr-4 file:py-2 file:px-4
                              file:rounded-l-lg file:border-0
                              file:text-sm file:font-semibold
                              file:bg-indigo-50 dark:file:bg-indigo-800 file:text-indigo-700 dark:file:text-indigo-300
                              hover:file:bg-indigo-100 dark:hover:file:bg-indigo-700">
                <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Recommended size: 300x200px. Max 2MB. Uploading a new image will replace the current one.</p>
            </div>

            {{-- Flags --}}
            <div class="mt-6 grid grid-cols-1 md:grid-cols-2 gap-6">
                <div>
                    <label for="is_active" class="flex items-center">
                        <input type="checkbox" name="is_active" id="is_active" value="1" {{ old('is_active', $aiStylePreset->is_active) ? 'checked' : '' }}
                               class="h-4 w-4 text-indigo-600 border-gray-300 dark:border-gray-600 rounded focus:ring-indigo-500 dark:bg-gray-700 dark:focus:ring-offset-gray-800">
                        <span class="ml-2 text-sm text-gray-700 dark:text-gray-300">Active (Available for users)</span>
                    </label>
                </div>
                <div>
                    <label for="is_premium" class="flex items-center">
                        <input type="checkbox" name="is_premium" id="is_premium" value="1" {{ old('is_premium', $aiStylePreset->is_premium) ? 'checked' : '' }}
                               class="h-4 w-4 text-indigo-600 border-gray-300 dark:border-gray-600 rounded focus:ring-indigo-500 dark:bg-gray-700 dark:focus:ring-offset-gray-800">
                        <span class="ml-2 text-sm text-gray-700 dark:text-gray-300">Premium (Requires subscription/permission)</span>
                    </label>
                </div>
            </div>

            {{-- Actions --}}
            <div class="mt-8 pt-5 border-t border-gray-200 dark:border-gray-700">
                <div class="flex justify-end">
                    <a href="{{ route('admin.webpilotai.ai-style-presets.index') }}" class="bg-gray-200 hover:bg-gray-300 text-gray-700 font-bold py-2 px-4 rounded-lg mr-3 transition duration-150 ease-in-out dark:bg-gray-600 dark:text-gray-200 dark:hover:bg-gray-500">
                        Cancel
                    </a>
                    <button type="submit" class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded-lg shadow-md transition duration-150 ease-in-out">
                        <i class="fas fa-save mr-2"></i> Update Preset
                    </button>
                </div>
            </div>
        </form>
    </div>
</div>
@endsection

@push('styles')
{{-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/codemirror.min.css"> --}}
{{-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/theme/material-darker.min.css"> --}}
@endpush

@push('scripts')
{{-- <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/codemirror.min.js"></script> --}}
{{-- <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/mode/javascript/javascript.min.js"></script> --}}
{{-- <script>
    // document.addEventListener('DOMContentLoaded', function () {
    //     var configTextArea = document.getElementById('configuration');
    //     if (configTextArea) {
    //         CodeMirror.fromTextArea(configTextArea, {
    //             lineNumbers: true,
    //             mode: { name: "javascript", json: true },
    //             theme: "material-darker", // or any other theme you prefer
    //             matchBrackets: true,
    //             autoCloseBrackets: true,
    //         });
    //     }
    // });
</script> --}}
@endpush


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\admin\ai_style_presets\index.blade.php ---
@extends('layouts.admin') {{-- Or your main admin layout --}}

@section('title', 'AI Style Presets')
@section('header_title', 'Manage AI Style Presets')

@section('content')
<div class="p-6">
    <div class="bg-white dark:bg-gray-800 shadow-md rounded-lg p-6">
        <div class="flex justify-between items-center mb-6">
            <h2 class="text-2xl font-semibold text-gray-700 dark:text-gray-200">AI Style Presets List</h2>
            <a href="{{ route('admin.webpilotai.ai-style-presets.create') }}" class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-lg shadow-md transition duration-150 ease-in-out">
                <i class="fas fa-plus mr-2"></i> Create New Preset
            </a>
        </div>

        @include('webpilotai::admin.partials._session_messages')

        @if($aiStylePresets->isEmpty())
            <div class="text-center py-10">
                <p class="text-gray-500 dark:text-gray-400 text-lg">No AI Style Presets found.</p>
                <p class="mt-2 text-gray-400 dark:text-gray-500">Get started by creating a new preset!</p>
            </div>
        @else
            <div class="overflow-x-auto">
                <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
                    <thead class="bg-gray-50 dark:bg-gray-700">
                        <tr>
                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Preview</th>
                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Name</th>
                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Type</th>
                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Description</th>
                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Status</th>
                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Premium</th>
                            <th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Actions</th>
                        </tr>
                    </thead>
                    <tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
                        @foreach ($aiStylePresets as $preset)
                        <tr>
                            <td class="px-6 py-4 whitespace-nowrap">
                                @if($preset->preview_image_path)
                                    <img src="{{ asset('storage/' . $preset->preview_image_path) }}" alt="{{ $preset->name }} preview" class="h-10 w-16 object-cover rounded border border-gray-300 dark:border-gray-600">
                                @else
                                    <div class="h-10 w-16 flex items-center justify-center bg-gray-100 dark:bg-gray-700 text-gray-400 dark:text-gray-500 rounded border border-gray-300 dark:border-gray-600 text-xs">
                                        No Preview
                                    </div>
                                @endif
                            </td>
                            <td class="px-6 py-4 whitespace-nowrap">
                                <div class="text-sm font-medium text-gray-900 dark:text-white">{{ $preset->name }}</div>
                            </td>
                            <td class="px-6 py-4 whitespace-nowrap">
                                <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-100 text-blue-800 dark:bg-blue-700 dark:text-blue-100">
                                    {{ Str::title(str_replace('_', ' ', $preset->type)) }}
                                </span>
                            </td>
                            <td class="px-6 py-4">
                                <div class="text-sm text-gray-500 dark:text-gray-400 truncate" style="max-width: 200px;" title="{{ $preset->description }}">
                                    {{ Str::limit($preset->description, 50) }}
                                </div>
                            </td>
                            <td class="px-6 py-4 whitespace-nowrap">
                                @if ($preset->is_active)
                                    <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800 dark:bg-green-700 dark:text-green-100">
                                        Active
                                    </span>
                                @else
                                    <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800 dark:bg-red-700 dark:text-red-100">
                                        Inactive
                                    </span>
                                @endif
                            </td>
                            <td class="px-6 py-4 whitespace-nowrap">
                                @if ($preset->is_premium)
                                    <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800 dark:bg-yellow-600 dark:text-yellow-100">
                                        Yes
                                    </span>
                                @else
                                    <span class="text-xs text-gray-500 dark:text-gray-400">No</span>
                                @endif
                            </td>
                            <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
                                <a href="{{ route('admin.webpilotai.ai-style-presets.edit', $preset->id) }}" class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-200 mr-3 transition duration-150 ease-in-out">
                                    <i class="fas fa-edit"></i> Edit
                                </a>
                                <form action="{{ route('admin.webpilotai.ai-style-presets.destroy', $preset->id) }}" method="POST" class="inline-block" onsubmit="return confirm('Are you sure you want to delete this AI Style Preset? This action cannot be undone.');">
                                    @csrf
                                    @method('DELETE')
                                    <button type="submit" class="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-200 transition duration-150 ease-in-out">
                                        <i class="fas fa-trash-alt"></i> Delete
                                    </button>
                                </form>
                            </td>
                        </tr>
                        @endforeach
                    </tbody>
                </table>
            </div>

            @if ($aiStylePresets->hasPages())
                <div class="mt-6">
                    {{ $aiStylePresets->links() }}
                </div>
            @endif
        @endif
    </div>
</div>
@endsection

@push('scripts')
{{-- Add any specific scripts for this page here if needed --}}
@endpush

@push('styles')
<style>
    /* You can add specific styles here if absolutely necessary, but prefer Tailwind classes */
</style>
@endpush

{{-- Create a partial for session messages if you don't have one --}}
{{-- Example: resources/views/admin/partials/_session_messages.blade.php --}}
{{--
@if (session('success'))
    <div class="bg-green-100 dark:bg-green-700 border border-green-400 dark:border-green-600 text-green-700 dark:text-green-100 px-4 py-3 rounded relative mb-4" role="alert">
        <strong class="font-bold">Success!</strong>
        <span class="block sm:inline">{{ session('success') }}</span>
    </div>
@endif

@if (session('error'))
    <div class="bg-red-100 dark:bg-red-700 border border-red-400 dark:border-red-600 text-red-700 dark:text-red-100 px-4 py-3 rounded relative mb-4" role="alert">
        <strong class="font-bold">Error!</strong>
        <span class="block sm:inline">{{ session('error') }}</span>
    </div>
@endif
--}}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\admin\ai_templates\create.blade.php ---
@extends('layouts.admin') {{-- Or your main admin layout --}}

@section('title', 'Create AI Template')
@section('header_title', 'Create New AI Template')

@section('content')
<div class="p-6">
    <div class="bg-white dark:bg-gray-800 shadow-md rounded-lg p-6">
        <h2 class="text-2xl font-semibold text-gray-700 dark:text-gray-200 mb-6">New AI Template Details</h2>

        @include('webpilotai::admin.partials._validation_errors')

        <form action="{{ route('admin.webpilotai.ai-templates.store') }}" method="POST" enctype="multipart/form-data">
            @csrf

            {{-- Name --}}
            <div class="mb-6">
                <label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Template Name <span class="text-red-500">*</span></label>
                <input type="text" name="name" id="name" value="{{ old('name') }}" required
                       class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">
            </div>

            {{-- Description --}}
            <div class="mb-6">
                <label for="description" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Description</label>
                <textarea name="description" id="description" rows="3"
                          class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">{{ old('description') }}</textarea>
                <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">A brief explanation of what this template is for.</p>
            </div>

            {{-- Prompt Structure (JSON) --}}
            <div class="mb-6">
                <label for="prompt_structure" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Prompt Structure (JSON) <span class="text-red-500">*</span></label>
                <textarea name="prompt_structure" id="prompt_structure" rows="10" required
                          class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm font-mono dark:text-gray-200"
                          placeholder='e.g., {"prefix": "Generate a website about...", "suffix": "Ensure it is responsive."}'
                          >{{ old('prompt_structure') }}</textarea>
                <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
                    Enter valid JSON. Can include 'prefix', 'suffix', or 'full_prompt_template'.
                    <br>Example: <code>{"full_prompt_template": "Create a landing page for [WEBSITE_NAME] about [USER_PROMPT]. Focus on a modern look."}</code>
                    <br>Placeholders: <code>[USER_PROMPT]</code>, <code>[WEBSITE_NAME]</code>
                </p>
            </div>

            {{-- Preview Image --}}
            <div class="mb-6">
                <label for="preview_image" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Preview Image (Optional)</label>
                <input type="file" name="preview_image" id="preview_image"
                       class="mt-1 block w-full text-sm text-gray-900 dark:text-gray-200 border border-gray-300 dark:border-gray-600 rounded-lg cursor-pointer bg-gray-50 dark:bg-gray-700 focus:outline-none
                              file:mr-4 file:py-2 file:px-4
                              file:rounded-l-lg file:border-0
                              file:text-sm file:font-semibold
                              file:bg-indigo-50 dark:file:bg-indigo-800 file:text-indigo-700 dark:file:text-indigo-300
                              hover:file:bg-indigo-100 dark:hover:file:bg-indigo-700">
                <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Recommended size: 300x200px. Max 2MB.</p>
            </div>

            {{-- Flags --}}
            <div class="mb-6 grid grid-cols-1 md:grid-cols-2 gap-6">
                <div>
                    <label for="is_active" class="flex items-center">
                        <input type="checkbox" name="is_active" id="is_active" value="1" {{ old('is_active', true) ? 'checked' : '' }}
                               class="h-4 w-4 text-indigo-600 border-gray-300 dark:border-gray-600 rounded focus:ring-indigo-500 dark:bg-gray-700 dark:focus:ring-offset-gray-800">
                        <span class="ml-2 text-sm text-gray-700 dark:text-gray-300">Active (Available for users)</span>
                    </label>
                </div>
                <div>
                    <label for="is_premium" class="flex items-center">
                        <input type="checkbox" name="is_premium" id="is_premium" value="1" {{ old('is_premium') ? 'checked' : '' }}
                               class="h-4 w-4 text-indigo-600 border-gray-300 dark:border-gray-600 rounded focus:ring-indigo-500 dark:bg-gray-700 dark:focus:ring-offset-gray-800">
                        <span class="ml-2 text-sm text-gray-700 dark:text-gray-300">Premium (Requires subscription/permission)</span>
                    </label>
                </div>
            </div>

            {{-- Actions --}}
            <div class="mt-8 pt-5 border-t border-gray-200 dark:border-gray-700">
                <div class="flex justify-end">
                    <a href="{{ route('admin.webpilotai.ai-templates.index') }}" class="bg-gray-200 hover:bg-gray-300 text-gray-700 font-bold py-2 px-4 rounded-lg mr-3 transition duration-150 ease-in-out dark:bg-gray-600 dark:text-gray-200 dark:hover:bg-gray-500">
                        Cancel
                    </a>
                    <button type="submit" class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded-lg shadow-md transition duration-150 ease-in-out">
                        <i class="fas fa-save mr-2"></i> Save Template
                    </button>
                </div>
            </div>
        </form>
    </div>
</div>
@endsection

@push('styles')
{{-- For a better JSON editing experience, consider CodeMirror or a similar library --}}
{{-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/codemirror.min.css"> --}}
{{-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/theme/material-darker.min.css"> --}}
@endpush

@push('scripts')
{{-- <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/codemirror.min.js"></script> --}}
{{-- <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/mode/javascript/javascript.min.js"></script> --}}
{{-- <script>
    // document.addEventListener('DOMContentLoaded', function () {
    //     var promptStructureTextArea = document.getElementById('prompt_structure');
    //     if (promptStructureTextArea) {
    //         CodeMirror.fromTextArea(promptStructureTextArea, {
    //             lineNumbers: true,
    //             mode: { name: "javascript", json: true },
    //             theme: "material-darker", // or any other theme you prefer
    //             matchBrackets: true,
    //             autoCloseBrackets: true,
    //         });
    //     }
    // });
</script> --}}
@endpush


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\admin\ai_templates\edit.blade.php ---
@extends('layouts.admin') {{-- Or your main admin layout --}}

@section('title', 'Edit AI Template')
@section('header_title', 'Edit AI Template: ' . $aiTemplate->name)

@section('content')
<div class="p-6">
    <div class="bg-white dark:bg-gray-800 shadow-md rounded-lg p-6">
        <h2 class="text-2xl font-semibold text-gray-700 dark:text-gray-200 mb-6">Update AI Template Details</h2>

        @include('webpilotai::admin.partials._validation_errors')

        <form action="{{ route('admin.webpilotai.ai-templates.update', $aiTemplate->id) }}" method="POST" enctype="multipart/form-data">
            @csrf
            @method('PUT')

            {{-- Name --}}
            <div class="mb-6">
                <label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Template Name <span class="text-red-500">*</span></label>
                <input type="text" name="name" id="name" value="{{ old('name', $aiTemplate->name) }}" required
                       class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">
            </div>

            {{-- Description --}}
            <div class="mb-6">
                <label for="description" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Description</label>
                <textarea name="description" id="description" rows="3"
                          class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">{{ old('description', $aiTemplate->description) }}</textarea>
                <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">A brief explanation of what this template is for.</p>
            </div>

            {{-- Prompt Structure (JSON) --}}
            <div class="mb-6">
                <label for="prompt_structure" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Prompt Structure (JSON) <span class="text-red-500">*</span></label>
                <textarea name="prompt_structure" id="prompt_structure" rows="10" required
                          class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm font-mono dark:text-gray-200"
                          placeholder='e.g., {"prefix": "Generate a website about...", "suffix": "Ensure it is responsive."}'
                          >{{ old('prompt_structure', json_encode($aiTemplate->prompt_structure, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)) }}</textarea>
                <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
                    Enter valid JSON. Can include 'prefix', 'suffix', or 'full_prompt_template'.
                    <br>Example: <code>{"full_prompt_template": "Create a landing page for [WEBSITE_NAME] about [USER_PROMPT]. Focus on a modern look."}</code>
                    <br>Placeholders: <code>[USER_PROMPT]</code>, <code>[WEBSITE_NAME]</code>
                </p>
            </div>

            {{-- Preview Image --}}
            <div class="mb-6">
                <label for="preview_image" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Preview Image (Optional)</label>
                @if($aiTemplate->preview_image_path)
                    <div class="mb-2">
                        <img src="{{ asset('storage/' . $aiTemplate->preview_image_path) }}" alt="Current Preview" class="h-20 w-auto rounded border border-gray-300 dark:border-gray-600">
                        <label for="remove_preview_image" class="mt-1 text-xs flex items-center text-red-600 dark:text-red-400">
                            <input type="checkbox" name="remove_preview_image" id="remove_preview_image" value="1" class="mr-1 h-3 w-3"> Remove current image
                        </label>
                    </div>
                @endif
                <input type="file" name="preview_image" id="preview_image"
                       class="mt-1 block w-full text-sm text-gray-900 dark:text-gray-200 border border-gray-300 dark:border-gray-600 rounded-lg cursor-pointer bg-gray-50 dark:bg-gray-700 focus:outline-none
                              file:mr-4 file:py-2 file:px-4
                              file:rounded-l-lg file:border-0
                              file:text-sm file:font-semibold
                              file:bg-indigo-50 dark:file:bg-indigo-800 file:text-indigo-700 dark:file:text-indigo-300
                              hover:file:bg-indigo-100 dark:hover:file:bg-indigo-700">
                <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Recommended size: 300x200px. Max 2MB. Uploading a new image will replace the current one.</p>
            </div>

            {{-- Flags --}}
            <div class="mb-6 grid grid-cols-1 md:grid-cols-2 gap-6">
                <div>
                    <label for="is_active" class="flex items-center">
                        <input type="checkbox" name="is_active" id="is_active" value="1" {{ old('is_active', $aiTemplate->is_active) ? 'checked' : '' }}
                               class="h-4 w-4 text-indigo-600 border-gray-300 dark:border-gray-600 rounded focus:ring-indigo-500 dark:bg-gray-700 dark:focus:ring-offset-gray-800">
                        <span class="ml-2 text-sm text-gray-700 dark:text-gray-300">Active (Available for users)</span>
                    </label>
                </div>
                <div>
                    <label for="is_premium" class="flex items-center">
                        <input type="checkbox" name="is_premium" id="is_premium" value="1" {{ old('is_premium', $aiTemplate->is_premium) ? 'checked' : '' }}
                               class="h-4 w-4 text-indigo-600 border-gray-300 dark:border-gray-600 rounded focus:ring-indigo-500 dark:bg-gray-700 dark:focus:ring-offset-gray-800">
                        <span class="ml-2 text-sm text-gray-700 dark:text-gray-300">Premium (Requires subscription/permission)</span>
                    </label>
                </div>
            </div>

            {{-- Actions --}}
            <div class="mt-8 pt-5 border-t border-gray-200 dark:border-gray-700">
                <div class="flex justify-end">
                    <a href="{{ route('admin.webpilotai.ai-templates.index') }}" class="bg-gray-200 hover:bg-gray-300 text-gray-700 font-bold py-2 px-4 rounded-lg mr-3 transition duration-150 ease-in-out dark:bg-gray-600 dark:text-gray-200 dark:hover:bg-gray-500">
                        Cancel
                    </a>
                    <button type="submit" class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded-lg shadow-md transition duration-150 ease-in-out">
                        <i class="fas fa-save mr-2"></i> Update Template
                    </button>
                </div>
            </div>
        </form>
    </div>
</div>
@endsection

@push('styles')
{{-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/codemirror.min.css"> --}}
{{-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/theme/material-darker.min.css"> --}}
@endpush

@push('scripts')
{{-- <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/codemirror.min.js"></script> --}}
{{-- <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/mode/javascript/javascript.min.js"></script> --}}
{{-- <script>
    // document.addEventListener('DOMContentLoaded', function () {
    //     var promptStructureTextArea = document.getElementById('prompt_structure');
    //     if (promptStructureTextArea) {
    //         CodeMirror.fromTextArea(promptStructureTextArea, {
    //             lineNumbers: true,
    //             mode: { name: "javascript", json: true },
    //             theme: "material-darker", // or any other theme you prefer
    //             matchBrackets: true,
    //             autoCloseBrackets: true,
    //         });
    //     }
    // });
</script> --}}
@endpush


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\admin\ai_templates\index.blade.php ---
@extends('layouts.admin') {{-- Or your main admin layout --}}

@section('title', 'AI Templates')
@section('header_title', 'Manage AI Templates')

@section('content')
<div class="p-6">
    <div class="bg-white dark:bg-gray-800 shadow-md rounded-lg p-6">
        <div class="flex justify-between items-center mb-6">
            <h2 class="text-2xl font-semibold text-gray-700 dark:text-gray-200">AI Templates List</h2>
            <a href="{{ route('admin.webpilotai.ai-templates.create') }}" class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-lg shadow-md transition duration-150 ease-in-out">
                <i class="fas fa-plus mr-2"></i> Create New Template
            </a>
        </div>

        @include('webpilotai::admin.partials._session_messages')

        @if($aiTemplates->isEmpty())
            <div class="text-center py-10">
                <p class="text-gray-500 dark:text-gray-400 text-lg">No AI Templates found.</p>
                <p class="mt-2 text-gray-400 dark:text-gray-500">Get started by creating a new template!</p>
            </div>
        @else
            <div class="overflow-x-auto">
                <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
                    <thead class="bg-gray-50 dark:bg-gray-700">
                        <tr>
                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Preview</th>
                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Name</th>
                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Description</th>
                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Status</th>
                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Premium</th>
                            <th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Actions</th>
                        </tr>
                    </thead>
                    <tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
                        @foreach ($aiTemplates as $template)
                        <tr>
                            <td class="px-6 py-4 whitespace-nowrap">
                                @if($template->preview_image_path)
                                    <img src="{{ asset('storage/' . $template->preview_image_path) }}" alt="{{ $template->name }} preview" class="h-10 w-16 object-cover rounded border border-gray-300 dark:border-gray-600">
                                @else
                                    <div class="h-10 w-16 flex items-center justify-center bg-gray-100 dark:bg-gray-700 text-gray-400 dark:text-gray-500 rounded border border-gray-300 dark:border-gray-600 text-xs">
                                        No Preview
                                    </div>
                                @endif
                            </td>
                            <td class="px-6 py-4 whitespace-nowrap">
                                <div class="text-sm font-medium text-gray-900 dark:text-white">{{ $template->name }}</div>
                            </td>
                            <td class="px-6 py-4">
                                <div class="text-sm text-gray-500 dark:text-gray-400 truncate" style="max-width: 250px;" title="{{ $template->description }}">
                                    {{ Str::limit($template->description, 60) }}
                                </div>
                            </td>
                            <td class="px-6 py-4 whitespace-nowrap">
                                @if ($template->is_active)
                                    <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800 dark:bg-green-700 dark:text-green-100">
                                        Active
                                    </span>
                                @else
                                    <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800 dark:bg-red-700 dark:text-red-100">
                                        Inactive
                                    </span>
                                @endif
                            </td>
                            <td class="px-6 py-4 whitespace-nowrap">
                                @if ($template->is_premium)
                                    <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800 dark:bg-yellow-600 dark:text-yellow-100">
                                        Yes
                                    </span>
                                @else
                                    <span class="text-xs text-gray-500 dark:text-gray-400">No</span>
                                @endif
                            </td>
                            <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
                                <a href="{{ route('admin.webpilotai.ai-templates.edit', $template->id) }}" class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-200 mr-3 transition duration-150 ease-in-out">
                                    <i class="fas fa-edit"></i> Edit
                                </a>
                                <form action="{{ route('admin.webpilotai.ai-templates.destroy', $template->id) }}" method="POST" class="inline-block" onsubmit="return confirm('Are you sure you want to delete this AI Template? This action cannot be undone.');">
                                    @csrf
                                    @method('DELETE')
                                    <button type="submit" class="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-200 transition duration-150 ease-in-out">
                                        <i class="fas fa-trash-alt"></i> Delete
                                    </button>
                                </form>
                            </td>
                        </tr>
                        @endforeach
                    </tbody>
                </table>
            </div>

            @if ($aiTemplates->hasPages())
                <div class="mt-6">
                    {{ $aiTemplates->links() }} {{-- Ensure your app is configured for Tailwind pagination views --}}
                </div>
            @endif
        @endif
    </div>
</div>
@endsection

@push('scripts')
{{-- Add any specific scripts for this page here if needed --}}
@endpush

@push('styles')
<style>
    /* You can add specific styles here if absolutely necessary, but prefer Tailwind classes */
</style>
@endpush

{{--
Ensure you have these partials or adapt to your existing ones:
Modules/WebPilotAI/resources/views/admin/partials/_session_messages.blade.php
Modules/WebPilotAI/resources/views/admin/partials/_validation_errors.blade.php
--}}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\admin\modulesettings\index.blade.php ---
@extends('layouts.admin') {{-- Or your main admin layout --}}

@section('title', 'WebPilotAI Module Settings')
@section('header_title', 'WebPilotAI Module Settings')

@section('content')
<div class="p-6">
    <form action="{{ route('admin.webpilotai.settings.update') }}" method="POST">
        @csrf
        @method('PUT')

        <div class="bg-white dark:bg-gray-800 shadow-md rounded-lg p-6">
            <h2 class="text-2xl font-semibold text-gray-700 dark:text-gray-200 mb-1">Module Configuration</h2>
            <p class="text-sm text-gray-500 dark:text-gray-400 mb-6">Manage settings for the WebPilotAI module. API keys and other sensitive information are stored securely.</p>

            @include('webpilotai::admin.partials._session_messages')
            @include('webpilotai::admin.partials._validation_errors')

            @if(empty($settingsSchema))
                <div class="bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700 p-4" role="alert">
                    <p class="font-bold">Warning</p>
                    <p>No settings schema found. Please ensure your module.json is correctly configured.</p>
                </div>
            @else
                @php
                    $groupedSettings = collect($settingsSchema)->groupBy('group');
                @endphp

                @foreach($groupedSettings as $groupName => $settingsInGroup)
                    <div class="mb-8 p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
                        <h3 class="text-xl font-semibold text-gray-600 dark:text-gray-300 mb-4">{{ $groupName }}</h3>
                        @foreach($settingsInGroup as $setting)
                            @php
                                $settingKey = 'webpilotai.' . $setting['key'];
                                $currentValue = $settingsData[$settingKey] ?? $setting['default'] ?? null;
                                $inputId = 'setting_' . Str::slug($setting['key'], '_');
                                $isDisabled = false;
                                if (isset($setting['depends_on'])) {
                                    foreach ($setting['depends_on'] as $depKey => $depValue) {
                                        $dependencyFullKey = 'webpilotai.' . $depKey;
                                        $dependencyCurrentValue = $settingsData[$dependencyFullKey] ?? $setting['default'] ?? null;
                                        if ($dependencyCurrentValue != $depValue) {
                                            $isDisabled = true;
                                            break;
                                        }
                                    }
                                }
                            @endphp
                            <div class="mb-6 {{ $isDisabled ? 'opacity-50' : '' }}">
                                <label for="{{ $inputId }}" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
                                    {{ $setting['label'] ?? Str::title(str_replace('_', ' ', $setting['key'])) }}
                                    @if(Str::contains(json_encode($setting['rules'] ?? []), 'required')) <span class="text-red-500">*</span> @endif
                                </label>

                                @if($setting['type'] === 'text' || $setting['type'] === 'number' || $setting['type'] === 'password')
                                    <input type="{{ $setting['type'] }}" name="{{ $settingKey }}" id="{{ $inputId }}"
                                           value="{{ old($settingKey, $currentValue) }}"
                                           {{ $isDisabled ? 'disabled' : '' }}
                                           class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">
                                @elseif($setting['type'] === 'textarea')
                                    <textarea name="{{ $settingKey }}" id="{{ $inputId }}" rows="3"
                                              {{ $isDisabled ? 'disabled' : '' }}
                                              class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">{{ old($settingKey, $currentValue) }}</textarea>
                                @elseif($setting['type'] === 'boolean' || $setting['type'] === 'checkbox')
                                    <label for="{{ $inputId }}" class="flex items-center mt-1">
                                        <input type="hidden" name="{{ $settingKey }}" value="0"> {{-- Hidden input for unchecked value --}}
                                        <input type="checkbox" name="{{ $settingKey }}" id="{{ $inputId }}" value="1"
                                               {{ old($settingKey, $currentValue) == '1' ? 'checked' : '' }}
                                               {{ $isDisabled ? 'disabled' : '' }}
                                               class="h-4 w-4 text-indigo-600 border-gray-300 dark:border-gray-600 rounded focus:ring-indigo-500 dark:bg-gray-700 dark:focus:ring-offset-gray-800">
                                        <span class="ml-2 text-sm text-gray-600 dark:text-gray-300">Enable</span>
                                    </label>
                                @elseif($setting['type'] === 'select' && isset($setting['options']))
                                     <select name="{{ $settingKey }}" id="{{ $inputId }}" {{ $isDisabled ? 'disabled' : '' }}
                                            class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">
                                        @foreach($setting['options'] as $value => $label)
                                            <option value="{{ $value }}" {{ old($settingKey, $currentValue) == $value ? 'selected' : '' }}>{{ $label }}</option>
                                        @endforeach
                                    </select>
                                @endif

                                @if(isset($setting['description']))
                                    <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">{{ $setting['description'] }}</p>
                                @endif
                                @error($settingKey)
                                    <p class="mt-1 text-xs text-red-500">{{ $message }}</p>
                                @enderror
                            </div>
                        @endforeach
                    </div>
                @endforeach
            @endif

            <div class="mt-8 pt-5 border-t border-gray-200 dark:border-gray-700">
                <div class="flex justify-end">
                    <button type="submit" class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded-lg shadow-md transition duration-150 ease-in-out">
                        <i class="fas fa-save mr-2"></i> Save Settings
                    </button>
                </div>
            </div>
        </div>
    </form>
</div>
@endsection

@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function () {
    // Logic to enable/disable dependent fields based on checkbox/select changes
    const settingsSchema = @json($settingsSchema ?? []);
    settingsSchema.forEach(setting => {
        if (setting.depends_on) {
            Object.keys(setting.depends_on).forEach(dependencyKey => {
                const dependencyFullKey = 'webpilotai.' + dependencyKey;
                const dependentFieldInputId = 'setting_' + setting.key.replace(/\./g, '_');
                const dependentField = document.getElementById(dependentFieldInputId);
                const controllerFieldId = 'setting_' + dependencyKey.replace(/\./g, '_');
                const controllerField = document.getElementById(controllerFieldId);

                if (controllerField && dependentField) {
                    const updateDependentState = () => {
                        let enable = true;
                        Object.entries(setting.depends_on).forEach(([depKey, depValue]) => {
                            const currentControllerField = document.getElementById('setting_' + depKey.replace(/\./g, '_'));
                            if (currentControllerField) {
                                const currentValue = currentControllerField.type === 'checkbox' ? (currentControllerField.checked ? '1' : '0') : currentControllerField.value;
                                if (String(currentValue) != String(depValue)) { // Ensure type comparison is consistent
                                    enable = false;
                                }
                            }
                        });
                        dependentField.disabled = !enable;
                        dependentField.closest('.mb-6').style.opacity = enable ? '1' : '0.5';
                    };

                    controllerField.addEventListener('change', updateDependentState);
                    // Initial state
                    // updateDependentState(); // Already handled by PHP, but good for dynamic scenarios
                }
            });
        }
    });
});
</script>
@endpush


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\admin\partials\_session_messages.blade.php ---
@if (session('success'))
    <div class="bg-green-100 dark:bg-green-700 border border-green-400 dark:border-green-600 text-green-700 dark:text-green-100 px-4 py-3 rounded-lg relative mb-4 shadow" role="alert">
        <strong class="font-bold">Success!</strong>
        <span class="block sm:inline">{{ session('success') }}</span>
    </div>
@endif

@if (session('error'))
    <div class="bg-red-100 dark:bg-red-700 border border-red-400 dark:border-red-600 text-red-700 dark:text-red-100 px-4 py-3 rounded-lg relative mb-4 shadow" role="alert">
        <strong class="font-bold">Error!</strong>
        <span class="block sm:inline">{{ session('error') }}</span>
    </div>
@endif

{{-- You can also add warnings or info messages here if needed --}}


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\admin\partials\_validation_errors.blade.php ---
@if ($errors->any())
    <div class="bg-red-100 dark:bg-red-700 border-l-4 border-red-500 dark:border-red-300 text-red-700 dark:text-red-200 p-4 mb-6 shadow" role="alert">
        <div class="flex">
            <div class="py-1">
                <svg class="fill-current h-6 w-6 text-red-500 dark:text-red-300 mr-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm12.73-1.41A8 8 0 1 0 4.34 4.34a8 8 0 0 0 11.32 11.32zM9 11V9h2v6H9v-4zm0-6h2v2H9V5z"/></svg>
            </div>
            <div>
                <p class="font-bold">Please correct the following errors:</p>
                <ul class="mt-2 list-disc list-inside text-sm">
                    @foreach ($errors->all() as $error)
                        <li>{{ $error }}</li>
                    @endforeach
                </ul>
            </div>
        </div>
    </div>
@endif


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\admin\usersites\create.blade.php ---
@extends('layouts.admin') {{-- Or your main admin layout --}}

@section('title', 'Create User Website (Admin)')
@section('header_title', 'Create New User Website')

@section('content')
<div class="p-6">
    <div class="bg-white dark:bg-gray-800 shadow-md rounded-lg p-6">
        <h2 class="text-2xl font-semibold text-gray-700 dark:text-gray-200 mb-6">New Website Details (Admin Creation)</h2>

        @include('webpilotai::admin.partials._validation_errors')
        @include('webpilotai::admin.partials._session_messages')

        <form action="{{ route('admin.webpilotai.user-sites.store') }}" method="POST">
            @csrf

            {{-- User Selection --}}
            <div class="mb-6">
                <label for="user_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Assign to User <span class="text-red-500">*</span></label>
                <select name="user_id" id="user_id" required
                        class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">
                    <option value="">-- Select User --</option>
                    @foreach($users as $user)
                        <option value="{{ $user->id }}" {{ old('user_id') == $user->id ? 'selected' : '' }}>{{ $user->name }} ({{ $user->email }})</option>
                    @endforeach
                </select>
            </div>

            {{-- Website Name --}}
            <div class="mb-6">
                <label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Website Name (Optional)</label>
                <input type="text" id="name" name="name" value="{{ old('name') }}"
                       class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">
            </div>

            {{-- Description Prompt --}}
            <div class="mb-6">
                <label for="description_prompt" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Description Prompt <span class="text-red-500">*</span></label>
                <textarea name="description_prompt" id="description_prompt" rows="8" required
                          class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">{{ old('description_prompt') }}</textarea>
            </div>

            {{-- AI Template Selection --}}
            <div class="mb-6 p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
                <label class="block text-lg font-semibold text-gray-700 dark:text-gray-300 mb-3">Choose an AI Template (Optional)</label>
                @if($aiTemplates->isEmpty())
                    <p class="text-gray-500 dark:text-gray-400">No AI templates are currently available.</p>
                @else
                    <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 template-selection-container-admin">
                        <label class="template-card-admin p-4 border rounded-lg cursor-pointer hover:border-indigo-500 dark:border-gray-600 dark:hover:border-indigo-400 transition-colors">
                            <input type="radio" name="ai_template_id" value="" {{ old('ai_template_id') == '' ? 'checked' : '' }} class="mr-2">
                            <strong class="block text-md font-medium text-gray-800 dark:text-gray-100 mb-1">No Specific Template</strong>
                            <p class="text-xs text-gray-600 dark:text-gray-400">Use default AI behavior.</p>
                        </label>
                        @foreach($aiTemplates as $template)
                        <label class="template-card-admin p-4 border rounded-lg cursor-pointer hover:border-indigo-500 dark:border-gray-600 dark:hover:border-indigo-400 transition-colors">
                            <input type="radio" name="ai_template_id" value="{{ $template->id }}" {{ old('ai_template_id') == $template->id ? 'checked' : '' }} class="mr-2">
                            @if($template->preview_image_path)
                                <img src="{{ asset('storage/' . $template->preview_image_path) }}" alt="{{ $template->name }} preview" class="w-full h-24 object-cover rounded mb-2 border dark:border-gray-600">
                            @else
                                <div class="w-full h-24 flex items-center justify-center bg-gray-100 dark:bg-gray-700 text-gray-400 dark:text-gray-500 rounded mb-2 text-xs">No Preview</div>
                            @endif
                            <strong class="block text-md font-medium text-gray-800 dark:text-gray-100 mb-1">
                                {{ $template->name }}
                                @if($template->is_premium)<span class="text-xs text-yellow-500 ml-1">[Premium]</span>@endif
                            </strong>
                            <p class="text-xs text-gray-600 dark:text-gray-400 truncate" title="{{ $template->description }}">{{ Str::limit($template->description, 50) }}</p>
                        </label>
                        @endforeach
                    </div>
                @endif
            </div>

            {{-- AI Style Presets Selection --}}
            <div class="mb-6 p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
                <label class="block text-lg font-semibold text-gray-700 dark:text-gray-300 mb-3">Choose AI Style Presets (Optional)</label>
                @if($aiStylePresets->isEmpty())
                    <p class="text-gray-500 dark:text-gray-400">No AI style presets are currently available.</p>
                @else
                    @php $groupedStylePresets = $aiStylePresets->groupBy('type'); @endphp
                    @foreach($groupedStylePresets as $type => $presets)
                        <fieldset class="mb-4">
                            <legend class="text-md font-semibold text-gray-700 dark:text-gray-300 mb-2">{{ Str::title(str_replace('_', ' ', $type)) }}</legend>
                            <div class="space-y-2">
                                @foreach($presets as $preset)
                                <label for="ai_style_preset_{{ $preset->id }}" class="flex items-center text-sm text-gray-700 dark:text-gray-300">
                                    <input type="checkbox" name="ai_style_preset_ids[]" id="ai_style_preset_{{ $preset->id }}" value="{{ $preset->id }}"
                                           {{ (is_array(old('ai_style_preset_ids')) && in_array($preset->id, old('ai_style_preset_ids'))) ? 'checked' : '' }}
                                           class="h-4 w-4 text-indigo-600 border-gray-300 dark:border-gray-600 rounded focus:ring-indigo-500 mr-2">
                                    <span title="{{ $preset->description }}">{{ $preset->name }}</span>
                                    @if($preset->is_premium)<span class="text-xs text-yellow-500 ml-1">[Premium]</span>@endif
                                    @if($preset->preview_image_path)
                                        <img src="{{ asset('storage/' . $preset->preview_image_path) }}" alt="{{ $preset->name }} preview" class="h-5 w-8 object-cover ml-2 border dark:border-gray-600 rounded-sm">
                                    @endif
                                </label>
                                @endforeach
                            </div>
                        </fieldset>
                    @endforeach
                @endif
            </div>

            {{-- AI Model Selection --}}
            <div class="mb-6">
                <label for="ai_model_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">AI Generation Model <span class="text-red-500">*</span></label>
                @if($aiModels->isEmpty())
                    <p class="text-orange-500">No AI models are currently available. Please configure them in settings.</p>
                @else
                    <select name="ai_model_id" id="ai_model_id" required
                            class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">
                        <option value="">-- Select AI Model --</option>
                        @foreach($aiModels as $model)
                            <option value="{{ $model->id }}" {{ old('ai_model_id') == $model->id ? 'selected' : '' }}>
                                {{ $model->name }} ({{ $model->provider }}) {{ $model->is_premium ? '[Premium]' : '' }}
                            </option>
                        @endforeach
                    </select>
                @endif
            </div>

            {{-- Status Selection (Admin only) --}}
            <div class="mb-6">
                <label for="status" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Initial Status <span class="text-red-500">*</span></label>
                <select name="status" id="status" required
                        class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">
                    <option value="{{ \Modules\WebPilotAI\Models\Website::STATUS_PENDING_GENERATION }}" {{ old('status', \Modules\WebPilotAI\Models\Website::STATUS_PENDING_GENERATION) == \Modules\WebPilotAI\Models\Website::STATUS_PENDING_GENERATION ? 'selected' : '' }}>Pending Generation</option>
                    <option value="{{ \Modules\WebPilotAI\Models\Website::STATUS_NEEDS_CONFIG }}" {{ old('status') == \Modules\WebPilotAI\Models\Website::STATUS_NEEDS_CONFIG ? 'selected' : '' }}>Needs Configuration (Manual)</option>
                    {{-- Add other relevant statuses if admin should be able to set them directly on creation --}}
                </select>
                <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">'Pending Generation' will automatically queue the site for AI processing.</p>
            </div>

            {{-- Actions --}}
            <div class="mt-8 pt-5 border-t border-gray-200 dark:border-gray-700">
                <div class="flex justify-end">
                    <a href="{{ route('admin.webpilotai.user-sites.index') }}" class="bg-gray-200 hover:bg-gray-300 text-gray-700 font-bold py-2 px-4 rounded-lg mr-3 transition duration-150 ease-in-out dark:bg-gray-600 dark:text-gray-200 dark:hover:bg-gray-500">
                        Cancel
                    </a>
                    <button type="submit" class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded-lg shadow-md transition duration-150 ease-in-out" {{ $aiModels->isEmpty() || $users->isEmpty() ? 'disabled' : '' }}>
                        <i class="fas fa-plus mr-2"></i> Create Website
                    </button>
                </div>
            </div>
        </form>
    </div>
</div>
@endsection

@push('styles')
<style>
    .template-card-admin input[type="radio"]:checked + img + strong,
    .template-card-admin input[type="radio"]:checked + div + strong, /* For No Preview */
    .template-card-admin input[type="radio"]:checked + strong /* For No Specific Template */ {
        color: #4f46e5; /* indigo-600 */
    }
    .template-card-admin:has(input[type="radio"]:checked) {
        border-color: #4f46e5; /* indigo-600 */
        box-shadow: 0 0 0 2px rgba(79, 70, 229, .5);
    }
</style>
@endpush


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\admin\usersites\edit.blade.php ---
@extends('layouts.admin') {{-- Or your main admin layout --}}

@section('title', 'Edit User Website: ' . ($website->name ?: 'Untitled Website'))
@section('header_title', 'Edit User Website: ' . ($website->name ?: 'Untitled Website'))

@section('content')
<div class="p-6">
    <div class="bg-white dark:bg-gray-800 shadow-md rounded-lg p-6">
        <h2 class="text-2xl font-semibold text-gray-700 dark:text-gray-200 mb-6">Update Website Details (Admin)</h2>

        @include('webpilotai::admin.partials._validation_errors')
        @include('webpilotai::admin.partials._session_messages')

        <form action="{{ route('admin.webpilotai.user-sites.update', $website->id) }}" method="POST">
            @csrf
            @method('PUT')

            {{-- Website Name --}}
            <div class="mb-6">
                <label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Website Name (Optional)</label>
                <input type="text" id="name" name="name" value="{{ old('name', $website->name) }}"
                       class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">
            </div>

            {{-- Description Prompt --}}
            <div class="mb-6">
                <label for="description_prompt" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Description Prompt <span class="text-red-500">*</span></label>
                <textarea name="description_prompt" id="description_prompt" rows="8" required
                          class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">{{ old('description_prompt', $website->description_prompt) }}</textarea>
            </div>

            {{-- AI Template Selection --}}
            <div class="mb-6 p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
                <label class="block text-lg font-semibold text-gray-700 dark:text-gray-300 mb-3">Choose an AI Template (Optional)</label>
                @if($aiTemplates->isEmpty())
                    <p class="text-gray-500 dark:text-gray-400">No AI templates are currently available.</p>
                @else
                    <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 template-selection-container-admin">
                        <label class="template-card-admin p-4 border rounded-lg cursor-pointer hover:border-indigo-500 dark:border-gray-600 dark:hover:border-indigo-400 transition-colors">
                            <input type="radio" name="ai_template_id" value="" {{ old('ai_template_id', $website->ai_template_id) == '' ? 'checked' : '' }} class="mr-2">
                            <strong class="block text-md font-medium text-gray-800 dark:text-gray-100 mb-1">No Specific Template</strong>
                            <p class="text-xs text-gray-600 dark:text-gray-400">Use default AI behavior.</p>
                        </label>
                        @foreach($aiTemplates as $template)
                        <label class="template-card-admin p-4 border rounded-lg cursor-pointer hover:border-indigo-500 dark:border-gray-600 dark:hover:border-indigo-400 transition-colors">
                            <input type="radio" name="ai_template_id" value="{{ $template->id }}" {{ old('ai_template_id', $website->ai_template_id) == $template->id ? 'checked' : '' }} class="mr-2">
                            @if($template->preview_image_path)
                                <img src="{{ asset('storage/' . $template->preview_image_path) }}" alt="{{ $template->name }} preview" class="w-full h-24 object-cover rounded mb-2 border dark:border-gray-600">
                            @else
                                <div class="w-full h-24 flex items-center justify-center bg-gray-100 dark:bg-gray-700 text-gray-400 dark:text-gray-500 rounded mb-2 text-xs">No Preview</div>
                            @endif
                            <strong class="block text-md font-medium text-gray-800 dark:text-gray-100 mb-1">
                                {{ $template->name }}
                                @if($template->is_premium)<span class="text-xs text-yellow-500 ml-1">[Premium]</span>@endif
                            </strong>
                            <p class="text-xs text-gray-600 dark:text-gray-400 truncate" title="{{ $template->description }}">{{ Str::limit($template->description, 50) }}</p>
                        </label>
                        @endforeach
                    </div>
                    <small class="block mt-2 text-xs text-gray-500 dark:text-gray-400">This will be applied if you choose to re-generate.</small>
                @endif
            </div>

            {{-- AI Style Presets Selection --}}
            <div class="mb-6 p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
                <label class="block text-lg font-semibold text-gray-700 dark:text-gray-300 mb-3">Choose AI Style Presets (Optional)</label>
                @if($aiStylePresets->isEmpty())
                    <p class="text-gray-500 dark:text-gray-400">No AI style presets are currently available.</p>
                @else
                    @php
                        $groupedStylePresets = $aiStylePresets->groupBy('type');
                        $currentPresetIds = old('ai_style_preset_ids', $website->aiStylePresets->pluck('id')->toArray());
                    @endphp
                    @foreach($groupedStylePresets as $type => $presets)
                        <fieldset class="mb-4">
                            <legend class="text-md font-semibold text-gray-700 dark:text-gray-300 mb-2">{{ Str::title(str_replace('_', ' ', $type)) }}</legend>
                            <div class="space-y-2">
                                @foreach($presets as $preset)
                                <label for="ai_style_preset_{{ $preset->id }}" class="flex items-center text-sm text-gray-700 dark:text-gray-300">
                                    <input type="checkbox" name="ai_style_preset_ids[]" id="ai_style_preset_{{ $preset->id }}" value="{{ $preset->id }}"
                                           {{ (is_array($currentPresetIds) && in_array($preset->id, $currentPresetIds)) ? 'checked' : '' }}
                                           class="h-4 w-4 text-indigo-600 border-gray-300 dark:border-gray-600 rounded focus:ring-indigo-500 mr-2">
                                    <span title="{{ $preset->description }}">{{ $preset->name }}</span>
                                    @if($preset->is_premium)<span class="text-xs text-yellow-500 ml-1">[Premium]</span>@endif
                                    @if($preset->preview_image_path)
                                        <img src="{{ asset('storage/' . $preset->preview_image_path) }}" alt="{{ $preset->name }} preview" class="h-5 w-8 object-cover ml-2 border dark:border-gray-600 rounded-sm">
                                    @endif
                                </label>
                                @endforeach
                            </div>
                        </fieldset>
                    @endforeach
                    <small class="block mt-2 text-xs text-gray-500 dark:text-gray-400">Changes will be applied if you choose to re-generate.</small>
                @endif
            </div>

            {{-- AI Model Selection --}}
            <div class="mb-6">
                <label for="ai_model_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">AI Generation Model <span class="text-red-500">*</span></label>
                @if($aiModels->isEmpty())
                    <p class="text-orange-500">No AI models are currently available. Please configure them in settings.</p>
                @else
                    <select name="ai_model_id" id="ai_model_id" required
                            class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">
                        @foreach($aiModels as $model)
                            <option value="{{ $model->id }}" {{ old('ai_model_id', $website->ai_model_id) == $model->id ? 'selected' : '' }}>
                                {{ $model->name }} ({{ $model->provider }}) {{ $model->is_premium ? '[Premium]' : '' }}
                            </option>
                        @endforeach
                    </select>
                @endif
            </div>

            {{-- Re-generate Option --}}
            <div class="mb-6">
                <label for="regenerate" class="flex items-center">
                    <input type="checkbox" id="regenerate" name="regenerate" value="1" {{ old('regenerate') ? 'checked' : '' }}
                           class="h-4 w-4 text-indigo-600 border-gray-300 dark:border-gray-600 rounded focus:ring-indigo-500 mr-2">
                    <span class="text-sm text-gray-700 dark:text-gray-300">Re-generate website content with these new details</span>
                </label>
                <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">If checked, the existing generated site will be replaced. If unchecked, only the associations will be updated.</p>
            </div>

            {{-- Actions --}}
            <div class="mt-8 pt-5 border-t border-gray-200 dark:border-gray-700">
                <div class="flex justify-end">
                    <a href="{{ route('admin.webpilotai.user-sites.show', $website->id) }}" class="bg-gray-200 hover:bg-gray-300 text-gray-700 font-bold py-2 px-4 rounded-lg mr-3 transition duration-150 ease-in-out dark:bg-gray-600 dark:text-gray-200 dark:hover:bg-gray-500">
                        Cancel
                    </a>
                    <button type="submit" class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded-lg shadow-md transition duration-150 ease-in-out" {{ $aiModels->isEmpty() ? 'disabled' : '' }}>
                        <i class="fas fa-save mr-2"></i> Update Website
                    </button>
                </div>
            </div>
        </form>
    </div>
</div>
@endsection

@push('styles')
<style>
    .template-card-admin input[type="radio"]:checked + img + strong,
    .template-card-admin input[type="radio"]:checked + div + strong, /* For No Preview */
    .template-card-admin input[type="radio"]:checked + strong /* For No Specific Template */ {
        color: #4f46e5; /* indigo-600 */
    }
    .template-card-admin input[type="radio"]:checked {
        /* You might want to hide the radio button itself and style the label */
    }
    .template-card-admin {
        /* Add styles to indicate selection, e.g., border color */
    }
    .template-card-admin:has(input[type="radio"]:checked) {
        border-color: #4f46e5; /* indigo-600 */
        box-shadow: 0 0 0 2px rgba(79, 70, 229, .5);
    }
</style>
@endpush


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\admin\usersites\index.blade.php ---
@extends('layouts.admin') {{-- Or your main user-facing layout --}}

@section('title', 'My AI Websites')
@section('header_title', 'My AI Websites') {{-- Or your layout's equivalent section for page titles --}}

@section('content')
<div class="container mx-auto px-4 py-8">
    <div class="flex justify-between items-center mb-6">
        <h1 class="text-3xl font-bold text-gray-800 dark:text-white">My AI Websites</h1>
        <a href="{{ route('admin.webpilotai.user-sites.create') }}" class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-lg shadow-md transition duration-150 ease-in-out">
            <i class="fas fa-plus mr-2"></i> Create New AI Website
        </a>
    </div>

    @include('webpilotai::admin.partials._session_messages') {{-- Assuming partials are in user/partials --}}

    <div class="bg-white dark:bg-gray-800 shadow-xl rounded-lg overflow-hidden">
        @if($websites->isEmpty())
            <div class="text-center py-12 px-6">
                <svg class="mx-auto h-12 w-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
                    <path vector-effect="non-scaling-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 13h6m-3-3v6m-9 1V7a2 2 0 012-2h6l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z" />
                </svg>
                <h3 class="mt-2 text-lg font-medium text-gray-900 dark:text-white">No websites yet</h3>
                <p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Get started by creating your first AI-powered website.</p>
                <div class="mt-6">
                    <a href="{{ route('admin.webpilotai.user-sites.create') }}" class="inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
                        <i class="fas fa-plus -ml-1 mr-2 h-5 w-5"></i>
                        Create New Website
                    </a>
                </div>
            </div>
        @else
            <div class="overflow-x-auto">
                <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
                    <thead class="bg-gray-50 dark:bg-gray-700">
                        <tr>
                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Name</th>
                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Status</th>
                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">AI Model</th>
                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Last Generated</th>
                            <th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Actions</th>
                        </tr>
                    </thead>
                    <tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
                        @foreach ($websites as $website)
                        <tr>
                            <td class="px-6 py-4 whitespace-nowrap">
                                <div class="text-sm font-semibold text-gray-900 dark:text-white">{{ $website->name ?: 'Untitled Website' }}</div>
                                <div class="text-xs text-gray-500 dark:text-gray-400">ID: {{ $website->id }}</div>
                            </td>
                            <td class="px-6 py-4 whitespace-nowrap">
                                <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full
                                    @if($website->status === \Modules\WebPilotAI\Models\Website::STATUS_COMPLETED) bg-green-100 text-green-800 dark:bg-green-700 dark:text-green-100
                                    @elseif(in_array($website->status, [\Modules\WebPilotAI\Models\Website::STATUS_PENDING_GENERATION, \Modules\WebPilotAI\Models\Website::STATUS_GENERATING, \Modules\WebPilotAI\Models\Website::STATUS_PENDING_REGENERATION])) bg-yellow-100 text-yellow-800 dark:bg-yellow-700 dark:text-yellow-100
                                    @elseif($website->status === \Modules\WebPilotAI\Models\Website::STATUS_FAILED) bg-red-100 text-red-800 dark:bg-red-700 dark:text-red-100
                                    @else bg-gray-100 text-gray-800 dark:bg-gray-600 dark:text-gray-200 @endif">
                                    {{ Str::title(str_replace('_', ' ', $website->status)) }}
                                </span>
                            </td>
                            <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
                                {{ $website->aiModel->name ?? 'N/A' }}
                            </td>
                            <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
                                {{ $website->last_generated_at ? $website->last_generated_at->format('M d, Y H:i') : 'Never' }}
                            </td>
                            <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
                                <a href="{{ route('frontend.webpilotai.websites.show', $website->id) }}" class="text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 mr-3 transition duration-150 ease-in-out" title="View">
                                    <i class="fas fa-eye"></i> <span class="sr-only">View</span>
                                </a>
                                <a href="{{ route('frontend.webpilotai.websites.edit', $website->id) }}" class="text-indigo-600 hover:text-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300 mr-3 transition duration-150 ease-in-out" title="Edit">
                                    <i class="fas fa-edit"></i> <span class="sr-only">Edit</span>
                                </a>
                                @if($website->status === \Modules\WebPilotAI\Models\Website::STATUS_COMPLETED && $website->generated_content_path)
                                    <a href="{{ asset($website->generated_content_path) }}" download class="text-green-600 hover:text-green-800 dark:text-green-400 dark:hover:text-green-300 mr-3 transition duration-150 ease-in-out" title="Download ZIP">
                                        <i class="fas fa-download"></i> <span class="sr-only">Download</span>
                                    </a>
                                @endif
                                <form action="{{ route('frontend.webpilotai.websites.destroy', $website->id) }}" method="POST" class="inline-block" onsubmit="return confirm('Are you sure you want to delete this website? This action cannot be undone.');">
                                    @csrf
                                    @method('DELETE')
                                    <button type="submit" class="text-red-600 hover:text-red-800 dark:text-red-400 dark:hover:text-red-300 transition duration-150 ease-in-out" title="Delete">
                                        <i class="fas fa-trash-alt"></i> <span class="sr-only">Delete</span>
                                    </button>
                                </form>
                            </td>
                        </tr>
                        @endforeach
                    </tbody>
                </table>
            </div>

            @if ($websites->hasPages())
                <div class="bg-white dark:bg-gray-800 px-4 py-3 border-t border-gray-200 dark:border-gray-700 sm:px-6">
                    {{ $websites->links() }} {{-- Ensure Tailwind pagination views are configured --}}
                </div>
            @endif
        @endif
    </div>
</div>
@endsection


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\admin\usersites\show.blade.php ---
@extends('layouts.admin') {{-- Or your main user-facing layout --}}

@section('title', 'Website Details: ' . ($website->name ?: 'Untitled Website'))
@section('header_title', $website->name ?: 'Untitled Website') {{-- Or your layout's equivalent section --}}

@section('content')
<div class="container mx-auto px-4 py-8">
    <div class="max-w-4xl mx-auto bg-white dark:bg-gray-800 shadow-xl rounded-lg">
        <div class="p-6 md:p-8">
            <div class="flex flex-col md:flex-row justify-between items-start md:items-center mb-6">
                <h1 class="text-3xl font-bold text-gray-800 dark:text-white mb-3 md:mb-0">
                    {{ $website->name ?: 'Untitled Website' }}
                </h1>
                <div class="flex space-x-3">
                    <a href="{{ route('frontend.webpilotai.websites.edit', $website->id) }}" class="inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
                        <i class="fas fa-edit mr-2"></i>Edit
                    </a>
                    @if($website->status === \Modules\WebPilotAI\Models\Website::STATUS_COMPLETED && $website->generated_content_path)
                        <a href="{{ asset($website->generated_content_path) }}" download class="inline-flex items-center px-4 py-2 border border-green-500 shadow-sm text-sm font-medium rounded-md text-green-700 bg-green-100 hover:bg-green-200 dark:text-green-300 dark:bg-green-700 dark:hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500">
                            <i class="fas fa-download mr-2"></i>Download ZIP
                        </a>
                    @endif
                </div>
            </div>

            @include('webpilotai::admin.partials._session_messages')

            <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
                <div class="md:col-span-2 space-y-4">
                    <div>
                        <h2 class="text-sm font-medium text-gray-500 dark:text-gray-400">Status</h2>
                        <p class="mt-1 text-lg text-gray-900 dark:text-white">
                            <span class="px-3 py-1 inline-flex text-sm leading-5 font-semibold rounded-full
                                @if($website->status === \Modules\WebPilotAI\Models\Website::STATUS_COMPLETED) bg-green-100 text-green-800 dark:bg-green-700 dark:text-green-100
                                @elseif(in_array($website->status, [\Modules\WebPilotAI\Models\Website::STATUS_PENDING_GENERATION, \Modules\WebPilotAI\Models\Website::STATUS_GENERATING, \Modules\WebPilotAI\Models\Website::STATUS_PENDING_REGENERATION])) bg-yellow-100 text-yellow-800 dark:bg-yellow-700 dark:text-yellow-100
                                @elseif($website->status === \Modules\WebPilotAI\Models\Website::STATUS_FAILED) bg-red-100 text-red-800 dark:bg-red-700 dark:text-red-100
                                @else bg-gray-100 text-gray-800 dark:bg-gray-600 dark:text-gray-200 @endif">
                                {{ Str::title(str_replace('_', ' ', $website->status)) }}
                            </span>
                        </p>
                    </div>
                     <div>
                        <h2 class="text-sm font-medium text-gray-500 dark:text-gray-400">AI Model Used</h2>
                        <p class="mt-1 text-md text-gray-900 dark:text-white">{{ $website->aiModel->name ?? 'N/A' }} ({{ $website->aiModel->provider ?? 'N/A' }})</p>
                    </div>
                    <div>
                        <h2 class="text-sm font-medium text-gray-500 dark:text-gray-400">AI Template Used</h2>
                        <p class="mt-1 text-md text-gray-900 dark:text-white">{{ $website->aiTemplate->name ?? 'None Selected' }}</p>
                    </div>
                    <div>
                        <h2 class="text-sm font-medium text-gray-500 dark:text-gray-400">AI Style Presets Used</h2>
                        <div class="mt-1">
                            @forelse($website->aiStylePresets as $preset)
                                <span class="inline-block bg-gray-200 dark:bg-gray-700 rounded-full px-3 py-1 text-xs font-semibold text-gray-700 dark:text-gray-200 mr-2 mb-2">{{ $preset->name }}</span>
                            @empty
                                <span class="text-sm text-gray-900 dark:text-white">None Selected</span>
                            @endforelse
                        </div>
                    </div>
                </div>
                <div class="space-y-4">
                    <div>
                        <h2 class="text-sm font-medium text-gray-500 dark:text-gray-400">Created</h2>
                        <p class="mt-1 text-md text-gray-900 dark:text-white">{{ $website->created_at->format('M d, Y H:i A') }}</p>
                    </div>
                    <div>
                        <h2 class="text-sm font-medium text-gray-500 dark:text-gray-400">Last Generated</h2>
                        <p class="mt-1 text-md text-gray-900 dark:text-white">{{ $website->last_generated_at ? $website->last_generated_at->format('M d, Y H:i A') : 'Never' }}</p>
                    </div>
                </div>
            </div>

            @if($website->status === \Modules\WebPilotAI\Models\Website::STATUS_FAILED && $website->generation_error)
            <div class="mb-8">
                <h3 class="text-lg font-medium text-red-600 dark:text-red-400 mb-1">Last Generation Error</h3>
                <div class="p-4 bg-red-50 dark:bg-red-900 border border-red-200 dark:border-red-700 rounded-md text-sm text-red-700 dark:text-red-200 whitespace-pre-wrap">
                    {{ $website->generation_error }}
                </div>
            </div>
            @endif

            <div class="mb-8">
                <h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">Description Prompt</h3>
                <div class="p-4 bg-gray-50 dark:bg-gray-700 rounded-md text-sm text-gray-700 dark:text-gray-300 whitespace-pre-wrap leading-relaxed">
                    {{ $website->description_prompt }}
                </div>
            </div>

            @if($website->status === \Modules\WebPilotAI\Models\Website::STATUS_COMPLETED && $website->generated_content_path)
                <div class="mb-8 p-4 border-t border-b border-gray-200 dark:border-gray-700">
                    <h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">Website Ready!</h3>
                    <p class="text-sm text-gray-600 dark:text-gray-300 mb-3">Your website has been generated. Download the ZIP file to host it or make further manual edits.</p>
                    <a href="{{ asset($website->generated_content_path) }}" download class="inline-flex items-center px-6 py-3 border border-transparent shadow-sm text-base font-medium rounded-md text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500">
                        <i class="fas fa-cloud-download-alt mr-2"></i> Download Website ZIP
                    </a>
                    <p class="mt-3 text-xs text-gray-500 dark:text-gray-400">
                        To preview, download the ZIP, extract it, and open the <code>index.html</code> file in your browser.
                    </p>
                </div>
            @elseif(in_array($website->status, [\Modules\WebPilotAI\Models\Website::STATUS_PENDING_GENERATION, \Modules\WebPilotAI\Models\Website::STATUS_GENERATING, \Modules\WebPilotAI\Models\Website::STATUS_PENDING_REGENERATION]))
                <div class="mb-8 p-4 bg-yellow-50 dark:bg-yellow-900 border-l-4 border-yellow-400 dark:border-yellow-600">
                    <div class="flex">
                        <div class="flex-shrink-0">
                            <svg class="h-5 w-5 text-yellow-400 dark:text-yellow-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
                                <path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd" />
                            </svg>
                        </div>
                        <div class="ml-3">
                            <p class="text-sm text-yellow-700 dark:text-yellow-200">
                                Your website is currently <strong>{{ Str::lower(str_replace('_', ' ', $website->status)) }}</strong>. Please check back shortly.
                                {{-- Consider adding auto-refresh JS if status is 'generating' --}}
                            </p>
                        </div>
                    </div>
                </div>
            @endif

            <div class="mt-8 pt-6 border-t border-gray-200 dark:border-gray-700">
                <a href="{{ route('frontend.webpilotai.websites.index') }}" class="text-indigo-600 hover:text-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300 font-medium">
                    &larr; Back to My Websites
                </a>
            </div>
        </div>
    </div>
</div>
@endsection

@push('scripts')
@if(in_array($website->status, [\Modules\WebPilotAI\Models\Website::STATUS_GENERATING, \Modules\WebPilotAI\Models\Website::STATUS_PENDING_REGENERATION, \Modules\WebPilotAI\Models\Website::STATUS_DEPLOYING]))
<script>
    // Optional: Auto-refresh the page if the status is 'generating', 'pending_regeneration', or 'deploying'
    // This is a simple refresh. For a better UX, consider AJAX polling to update only parts of the page.
    // setTimeout(function(){
    //    window.location.reload(1);
    // }, 15000); // Refresh every 15 seconds

    // More advanced AJAX polling (example)
    // function checkStatus() {
    //     fetch("{{ route('frontend.webpilotai.websites.status', $website->id) }}") // You'd need a status check route
    //         .then(response => response.json())
    //         .then(data => {
    //             if (data.status !== '{{ $website->status }}') {
    //                 window.location.reload();
    //             } else if (['generating', 'pending_regeneration', 'deploying'].includes(data.status)) {
    //                 setTimeout(checkStatus, 15000);
    //             }
    //         });
    // }
    // if (['generating', 'pending_regeneration', 'deploying'].includes('{{ $website->status }}')) {
    //     setTimeout(checkStatus, 15000);
    // }
</script>
@endif
@endpush


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\admin\dashboard.blade.php ---
@extends('layouts.admin') {{-- Or your main admin layout if different, e.g., layouts.app if that's your admin base --}}

@section('title', 'WebPilotAI Dashboard')
@section('header_title', 'WebPilotAI Dashboard')

@section('content')
<div class="p-6">
    <p class="mb-6 text-gray-700 dark:text-gray-300">Overview of the WebPilotAI module activity.</p>

    <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6 mb-6">
        {{-- Stat Card: AI Models --}}
        <div class="bg-white dark:bg-gray-800 overflow-hidden shadow-lg rounded-lg">
            <div class="p-5">
                <div class="flex items-center">
                    <div class="flex-shrink-0 bg-blue-500 dark:bg-blue-700 rounded-md p-3">
                        {{-- Heroicon: cpu-chip --}}
                        <svg class="h-6 w-6 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                            <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 3v1.5M4.5 8.25H3m18 0h-1.5M4.5 12H3m18 0h-1.5m-15 3.75H3m18 0h-1.5M8.25 19.5V21M12 3v1.5m0 15V21m3.75-18v1.5m0 15V21m-9-7.5h10.5a2.25 2.25 0 0 0 2.25-2.25V8.25a2.25 2.25 0 0 0-2.25-2.25H6.75A2.25 2.25 0 0 0 4.5 8.25v3.5A2.25 2.25 0 0 0 6.75 14.25h10.5" />
                        </svg>
                    </div>
                    <div class="ml-5 w-0 flex-1">
                        <dl>
                            <dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">AI Models</dt>
                            <dd class="text-lg font-semibold text-gray-900 dark:text-white">Total: {{ $totalAIModels ?? 0 }}</dd>
                            <dd class="text-sm font-medium text-green-500 dark:text-green-400">Active: {{ $activeAIModels ?? 0 }}</dd>
                        </dl>
                    </div>
                </div>
            </div>
            <div class="bg-gray-50 dark:bg-gray-700 px-5 py-3">
                <div class="text-sm">
                    <a href="{{ route('admin.webpilotai.models.index') }}" class="font-medium text-blue-600 dark:text-blue-400 hover:text-blue-500">Manage AI Models</a>
                </div>
            </div>
        </div>

        {{-- Stat Card: User Websites --}}
        <div class="bg-white dark:bg-gray-800 overflow-hidden shadow-lg rounded-lg">
            <div class="p-5">
                <div class="flex items-center">
                    <div class="flex-shrink-0 bg-indigo-500 dark:bg-indigo-700 rounded-md p-3">
                        {{-- Heroicon: globe-alt --}}
                        <svg class="h-6 w-6 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                            <path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253m0 0A11.978 11.978 0 0 1 12 16.5c-3.162 0-6.024-1.205-8.242-3.243m16.484 0L12 16.5" />
                        </svg>
                    </div>
                    <div class="ml-5 w-0 flex-1">
                        <dl>
                            <dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">User Websites</dt>
                            <dd class="text-lg font-semibold text-gray-900 dark:text-white">Total: {{ $totalWebsites ?? 0 }}</dd>
                            <dd class="text-sm font-medium text-green-500 dark:text-green-400">Completed: {{ $completedWebsites ?? 0 }}</dd>
                            <dd class="text-sm font-medium text-yellow-500 dark:text-yellow-400">Pending: {{ $pendingWebsites ?? 0 }}</dd>
                        </dl>
                    </div>
                </div>
            </div>
            <div class="bg-gray-50 dark:bg-gray-700 px-5 py-3">
                <div class="text-sm">
                    <a href="{{ route('admin.webpilotai.user-sites.index') }}" class="font-medium text-indigo-600 dark:text-indigo-400 hover:text-indigo-500">Manage User Websites</a>
                </div>
            </div>
        </div>

        {{-- Stat Card: AI Templates --}}
        <div class="bg-white dark:bg-gray-800 overflow-hidden shadow-lg rounded-lg">
            <div class="p-5">
                <div class="flex items-center">
                    <div class="flex-shrink-0 bg-purple-500 dark:bg-purple-700 rounded-md p-3">
                        {{-- Heroicon: document-duplicate --}}
                        <svg class="h-6 w-6 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                            <path stroke-linecap="round" stroke-linejoin="round" d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 0 0-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 0 1-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H9.75" />
                        </svg>
                    </div>
                    <div class="ml-5 w-0 flex-1">
                        <dl>
                            <dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">AI Templates</dt>
                            <dd class="text-lg font-semibold text-gray-900 dark:text-white">Total: {{ $totalAITemplates ?? 0 }}</dd>
                            <dd class="text-sm font-medium text-green-500 dark:text-green-400">Active: {{ $activeAITemplates ?? 0 }}</dd>
                        </dl>
                    </div>
                </div>
            </div>
            <div class="bg-gray-50 dark:bg-gray-700 px-5 py-3">
                <div class="text-sm">
                    <a href="{{ route('admin.webpilotai.ai-templates.index') }}" class="font-medium text-purple-600 dark:text-purple-400 hover:text-purple-500">Manage AI Templates</a>
                </div>
            </div>
        </div>

        {{-- Stat Card: AI Style Presets --}}
        <div class="bg-white dark:bg-gray-800 overflow-hidden shadow-lg rounded-lg">
            <div class="p-5">
                <div class="flex items-center">
                    <div class="flex-shrink-0 bg-pink-500 dark:bg-pink-700 rounded-md p-3">
                        {{-- Heroicon: paint-brush --}}
                        <svg class="h-6 w-6 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                            <path stroke-linecap="round" stroke-linejoin="round" d="M9.53 16.122a3 3 0 0 0-5.78 1.128 2.25 2.25 0 0 1-2.4 2.245 4.5 4.5 0 0 0 8.4-2.245c0-.399-.078-.78-.22-1.128Zm0 0a15.998 15.998 0 0 0 3.388-1.62m-5.043-.025a15.994 15.994 0 0 1 1.622-3.395m3.42 3.42a15.995 15.995 0 0 0 4.764-4.648l3.876-5.814a1.151 1.151 0 0 0-1.597-1.597L14.146 6.32a15.996 15.996 0 0 0-4.649 4.763m3.42 3.42a6.776 6.776 0 0 0-3.42-3.42" />
                        </svg>
                    </div>
                    <div class="ml-5 w-0 flex-1">
                        <dl>
                            <dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">AI Style Presets</dt>
                            <dd class="text-lg font-semibold text-gray-900 dark:text-white">Total: {{ $totalAIStylePresets ?? 0 }}</dd>
                            <dd class="text-sm font-medium text-green-500 dark:text-green-400">Active: {{ $activeAIStylePresets ?? 0 }}</dd>
                        </dl>
                    </div>
                </div>
            </div>
            <div class="bg-gray-50 dark:bg-gray-700 px-5 py-3">
                <div class="text-sm">
                    <a href="{{ route('admin.webpilotai.ai-style-presets.index') }}" class="font-medium text-pink-600 dark:text-pink-400 hover:text-pink-500">Manage AI Style Presets</a>
                </div>
            </div>
        </div>

        {{-- Link to Module Settings --}}
        <div class="bg-white dark:bg-gray-800 overflow-hidden shadow-lg rounded-lg col-span-1 md:col-span-2 lg:col-span-1">
            <div class="p-5">
                <div class="flex items-center">
                    <div class="flex-shrink-0 bg-gray-500 dark:bg-gray-700 rounded-md p-3">
                        {{-- Heroicon: cog-6-tooth --}}
                        <svg class="h-6 w-6 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                            <path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.646.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 0 1 0 1.905c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 0 1-.22.128c-.333.183-.582.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.759 6.759 0 0 1 0-1.905c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281Z" />
                            <path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
                        </svg>
                    </div>
                    <div class="ml-5 w-0 flex-1">
                        <dl>
                            <dt class="text-lg font-semibold text-gray-900 dark:text-white">Module Settings</dt>
                            <dd class="text-sm font-medium text-gray-500 dark:text-gray-400">API keys, defaults, etc.</dd>
                        </dl>
                    </div>
                </div>
            </div>
            <div class="bg-gray-50 dark:bg-gray-700 px-5 py-3">
                <div class="text-sm">
                    <a href="{{ route('admin.webpilotai.settings.index') }}" class="font-medium text-gray-600 dark:text-gray-400 hover:text-gray-500">Go to Settings</a>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\components\layouts\master.blade.php ---
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">

    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="csrf-token" content="{{ csrf_token() }}">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">

        <title>WebPilotAI Module - {{ config('app.name', 'Laravel') }}</title>

        <meta name="description" content="{{ $description ?? '' }}">
        <meta name="keywords" content="{{ $keywords ?? '' }}">
        <meta name="author" content="{{ $author ?? '' }}">

        <!-- Fonts -->
        <link rel="preconnect" href="https://fonts.bunny.net">
        <link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
        @stack('head-extras')

        {{-- Vite CSS --}}
        {{-- {{ module_vite('build-webpilotai', 'resources/assets/sass/app.scss') }} --}}
    </head>

    <body>
        {{ $slot }}

        {{-- Vite JS --}}
        {{-- {{ module_vite('build-webpilotai', 'resources/assets/js/app.js') }} --}}
    </body>


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\emails\deployment\failed.blade.php ---
@component('mail::message')
# Uh Oh! Problem Deploying Your AI Website

Hello {{ $website->user->name ?? 'User' }},

We encountered an issue while trying to deploy your website, **"{{ $website->name ?: 'Untitled Website' }}"**.

**Deployment Target:**
- **Type:** {{ strtoupper($website->deployment_details['type'] ?? ($website->deploymentConfig['deployment_type'] ?? 'N/A')) }}
- **Host:** {{ $website->deployment_details['host'] ?? ($website->deploymentConfig['host'] ?? 'N/A') }}

**Error Details:**
```
{{ $errorMessage }}
```

Please check your deployment settings and try again.

@component('mail::button', ['url' => route('frontend.webpilotai.websites.deploy.form', $website->id)])
Retry Deployment
@endcomponent

If the problem persists, please contact support.

Thanks,<br>
{{ config('app.name') }}
@endcomponent


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\emails\deployment\success.blade.php ---
@component('mail::message')
# Deployment Successful!

Hello {{ $website->user->name ?? 'User' }},

Your AI website, **"{{ $website->name ?: 'Untitled Website' }}"**, has been successfully deployed!

**Deployment Details:**
- **Type:** {{ strtoupper($website->deployment_details['type'] ?? 'N/A') }}
- **Host:** {{ $website->deployment_details['host'] ?? 'N/A' }}
@if(isset($website->deployment_details['remote_path']))
- **Remote Path:** {{ $website->deployment_details['remote_path'] }}
@endif
@if(isset($website->deployment_details['cpanel_domain']) && $website->deployment_details['type'] === 'cpanel')
- **cPanel Domain:** {{ $website->deployment_details['cpanel_domain'] }}
@endif

You can view the status and further details from your dashboard.

@component('mail::button', ['url' => route('frontend.webpilotai.websites.show', $website->id)])
View Website Details
@endcomponent

Thanks,<br>
{{ config('app.name') }}
@endcomponent


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\emails\generation\failed.blade.php ---
@component('mail::message')
# Uh Oh! Problem Generating Your AI Website

Hello {{ $website->user->name ?? 'User' }},

We encountered an issue while trying to generate your website, **"{{ $website->name ?: 'Untitled Website' }}"**.

**Error Details:**
```
{{ $errorMessage }}
```

You can try editing the prompt or selecting a different AI model and attempting to regenerate the website.

@component('mail::button', ['url' => route('frontend.webpilotai.websites.edit', $website->id)])
Edit and Regenerate Website
@endcomponent

Prompt used:
> {{ $website->description_prompt }}

If the problem persists, please contact support.

Thanks,<br>
{{ config('app.name') }}
@endcomponent


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\emails\generation\success.blade.php ---
@component('mail::message')
# Great News! Your AI Website is Ready!

Hello {{ $website->user->name ?? 'User' }},

We're excited to let you know that the AI generation for your website, **"{{ $website->name ?: 'Untitled Website' }}"**, has completed successfully.

You can now view, download, or deploy your new website.

@component('mail::button', ['url' => route('frontend.webpilotai.websites.show', $website->id)])
View Your Website
@endcomponent

Prompt used:
> {{ $website->description_prompt }}

Thanks,<br>
{{ config('app.name') }}
@endcomponent


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\user\websites\create.blade.php ---
@extends('layouts.app') {{-- Or your main user-facing layout --}}

@section('title', 'Create New AI Website')
@section('header_title', 'Create Your AI Website') {{-- Or your layout's equivalent section --}}

@section('content')
<div class="container mx-auto px-4 py-8">
    <div class="max-w-3xl mx-auto bg-white dark:bg-gray-800 shadow-xl rounded-lg p-6 md:p-8">
        <h1 class="text-3xl font-bold text-gray-800 dark:text-white mb-2">Let's Build Your Website!</h1>
        <p class="text-gray-600 dark:text-gray-300 mb-6">Describe your vision, and our AI will bring it to life.</p>

        @include('webpilotai::user.partials._validation_errors')
        @include('webpilotai::admin.partials._session_messages')

        <form action="{{ route('frontend.webpilotai.websites.store') }}" method="POST">
            @csrf
            <div class="space-y-6">
                <div>
                    <label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Website Name (Optional)</label>
                    <input type="text" id="name" name="name" value="{{ old('name') }}"
                           class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">
                    <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">If left blank, a name will be suggested by the AI.</p>
                </div>

                <div>
                    <label for="description_prompt" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Describe your website needs (The Prompt) <span class="text-red-500">*</span></label>
                    <textarea name="description_prompt" id="description_prompt" rows="8" required
                              class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200"
                              placeholder="e.g., I need a modern website for my bakery called 'Sweet Treats'. It should showcase our cakes, cookies, and custom order form. The style should be elegant and inviting, using pastel colors. Include an 'About Us' page, a gallery, and a contact page with a map.">{{ old('description_prompt') }}</textarea>
                </div>

                {{-- AI Template Selection --}}
                <div class="p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
                    <label class="block text-lg font-semibold text-gray-700 dark:text-gray-300 mb-3">Choose an AI Template (Optional)</label>
                    @if($aiTemplates->isEmpty())
                        <p class="text-gray-500 dark:text-gray-400">No AI templates are currently available.</p>
                    @else
                        <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 template-selection-container-user">
                            <label class="template-card-user p-4 border rounded-lg cursor-pointer hover:border-indigo-500 dark:border-gray-600 dark:hover:border-indigo-400 transition-colors">
                                <input type="radio" name="ai_template_id" value="" {{ old('ai_template_id') == '' ? 'checked' : '' }} class="mr-2">
                                <strong class="block text-md font-medium text-gray-800 dark:text-gray-100 mb-1">No Specific Template</strong>
                                <p class="text-xs text-gray-600 dark:text-gray-400">Use default AI behavior.</p>
                            </label>
                            @foreach($aiTemplates as $template)
                            <label class="template-card-user p-4 border rounded-lg cursor-pointer hover:border-indigo-500 dark:border-gray-600 dark:hover:border-indigo-400 transition-colors">
                                <input type="radio" name="ai_template_id" value="{{ $template->id }}" {{ old('ai_template_id') == $template->id ? 'checked' : '' }} class="mr-2">
                                @if($template->preview_image_path)
                                    <img src="{{ asset('storage/' . $template->preview_image_path) }}" alt="{{ $template->name }} preview" class="w-full h-24 object-cover rounded mb-2 border dark:border-gray-600">
                                @else
                                    <div class="w-full h-24 flex items-center justify-center bg-gray-100 dark:bg-gray-700 text-gray-400 dark:text-gray-500 rounded mb-2 text-xs">No Preview</div>
                                @endif
                                <strong class="block text-md font-medium text-gray-800 dark:text-gray-100 mb-1">
                                    {{ $template->name }}
                                    @if($template->is_premium)<span class="text-xs text-yellow-500 ml-1">[Premium]</span>@endif
                                </strong>
                                <p class="text-xs text-gray-600 dark:text-gray-400 truncate" title="{{ $template->description }}">{{ Str::limit($template->description, 50) }}</p>
                            </label>
                            @endforeach
                        </div>
                        <small class="block mt-2 text-xs text-gray-500 dark:text-gray-400">Selecting a template can help guide the AI in structuring your website.</small>
                    @endif
                </div>

                {{-- AI Style Presets Selection --}}
                <div class="p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
                    <label class="block text-lg font-semibold text-gray-700 dark:text-gray-300 mb-3">Choose AI Style Presets (Optional)</label>
                    @if($aiStylePresets->isEmpty())
                        <p class="text-gray-500 dark:text-gray-400">No AI style presets are currently available.</p>
                    @else
                        @php $groupedStylePresets = $aiStylePresets->groupBy('type'); @endphp
                        @foreach($groupedStylePresets as $type => $presets)
                            <fieldset class="mb-4">
                                <legend class="text-md font-semibold text-gray-700 dark:text-gray-300 mb-2">{{ Str::title(str_replace('_', ' ', $type)) }}</legend>
                                <div class="space-y-2">
                                    @foreach($presets as $preset)
                                    <label for="ai_style_preset_{{ $preset->id }}" class="flex items-center text-sm text-gray-700 dark:text-gray-300">
                                        <input type="checkbox" name="ai_style_preset_ids[]" id="ai_style_preset_{{ $preset->id }}" value="{{ $preset->id }}"
                                               {{ (is_array(old('ai_style_preset_ids')) && in_array($preset->id, old('ai_style_preset_ids'))) ? 'checked' : '' }}
                                               class="h-4 w-4 text-indigo-600 border-gray-300 dark:border-gray-600 rounded focus:ring-indigo-500 mr-2">
                                        <span title="{{ $preset->description }}">{{ $preset->name }}</span>
                                        @if($preset->is_premium)<span class="text-xs text-yellow-500 ml-1">[Premium]</span>@endif
                                        @if($preset->preview_image_path)
                                            <img src="{{ asset('storage/' . $preset->preview_image_path) }}" alt="{{ $preset->name }} preview" class="h-5 w-8 object-cover ml-2 border dark:border-gray-600 rounded-sm">
                                        @endif
                                    </label>
                                    @endforeach
                                </div>
                            </fieldset>
                        @endforeach
                        <small class="block mt-2 text-xs text-gray-500 dark:text-gray-400">Selecting style presets can further customize the look and feel.</small>
                    @endif
                </div>

                <div>
                    <label for="ai_model_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Choose AI Generation Model <span class="text-red-500">*</span></label>
                    @if($aiModels->isEmpty())
                        <p class="mt-1 text-orange-500">No AI models are currently available. Please contact support.</p>
                    @else
                        <select name="ai_model_id" id="ai_model_id" required
                                class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">
                            @foreach($aiModels as $model)
                                <option value="{{ $model->id }}" {{ old('ai_model_id') == $model->id ? 'selected' : '' }}>
                                    {{ $model->name }} ({{ $model->provider }}) {{ $model->is_premium ? '[Premium]' : '' }}
                                </option>
                            @endforeach
                        </select>
                    @endif
                </div>

                <div class="pt-5">
                    <div class="flex justify-end">
                        <a href="{{ route('frontend.webpilotai.websites.index') }}" class="bg-gray-200 hover:bg-gray-300 text-gray-700 font-bold py-2 px-4 rounded-lg mr-3 transition duration-150 ease-in-out dark:bg-gray-600 dark:text-gray-200 dark:hover:bg-gray-500">
                            Cancel
                        </a>
                        <button type="submit"
                                class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded-lg shadow-md transition duration-150 ease-in-out {{ $aiModels->isEmpty() ? 'opacity-50 cursor-not-allowed' : '' }}"
                                {{ $aiModels->isEmpty() ? 'disabled' : '' }}>
                            <i class="fas fa-rocket mr-2"></i> Start AI Website Generation
                        </button>
                    </div>
                </div>
            </div>
        </form>
    </div>
</div>
@endsection

@push('styles')
<style>
    .template-card-user:has(input[type="radio"]:checked) {
        border-color: #4f46e5; /* indigo-600 */
        box-shadow: 0 0 0 2px rgba(79, 70, 229, .5); /* focus:ring-indigo-500 with opacity */
    }
    .template-card-user input[type="radio"]:checked + img + strong,
    .template-card-user input[type="radio"]:checked + div + strong, /* For No Preview */
    .template-card-user input[type="radio"]:checked + strong /* For No Specific Template */ {
        color: #4f46e5; /* indigo-600 */
    }
</style>
@endpush


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\user\websites\deploy.blade.php ---
<x-webpilotai::layouts.master>
    <h1>Deploy Website: {{ $website->name ?: 'Untitled Website' }}</h1>
    <p>Enter your hosting details to deploy your website. Ensure the generated ZIP file is ready.</p>

    @if ($errors->any())
        <div style="color: red; margin-bottom: 15px; padding: 10px; border: 1px solid red; background-color: #ffe6e6;">
            <strong>Whoops! Something went wrong.</strong>
            <ul style="margin-top: 5px; margin-left: 20px;">
                @foreach ($errors->all() as $error)
                    <li>{{ $error }}</li>
                @endforeach
            </ul>
        </div>
    @endif

    <form action="{{ route('frontend.webpilotai.websites.deploy.submit', $website->id) }}" method="POST">
        @csrf

        <div style="margin-bottom: 15px;">
            <label for="deployment_type" style="display: block; margin-bottom: 5px;">Deployment Type:</label>
            <select name="deployment_type" id="deployment_type" required style="width: 100%; padding: 8px; box-sizing: border-box; border: 1px solid #ccc; border-radius: 4px;">
                <option value="ftp" {{ (old('deployment_type', $lastDeploymentDetails['type'] ?? 'ftp') == 'ftp') ? 'selected' : '' }}>FTP</option>
                <option value="sftp" {{ (old('deployment_type', $lastDeploymentDetails['type'] ?? '') == 'sftp') ? 'selected' : '' }}>SFTP (Recommended)</option>
                <option value="cpanel" {{ (old('deployment_type', $lastDeploymentDetails['type'] ?? '') == 'cpanel') ? 'selected' : '' }}>cPanel API</option>
            </select>
            @error('deployment_type') <span style="color: red; font-size: 0.9em;">{{ $message }}</span> @enderror
        </div>

        <div style="margin-bottom: 15px;">
            <label for="host" style="display: block; margin-bottom: 5px;">Host / Server Address:</label>
            <input type="text" id="host" name="host" value="{{ old('host', $lastDeploymentDetails['host'] ?? '') }}" required style="width: 100%; padding: 8px; box-sizing: border-box; border: 1px solid #ccc; border-radius: 4px;" placeholder="e.g., ftp.yourdomain.com or your_server_ip">
            @error('host') <span style="color: red; font-size: 0.9em;">{{ $message }}</span> @enderror
        </div>

        <div style="margin-bottom: 15px;">
            <label for="username" style="display: block; margin-bottom: 5px;">Username:</label>
            <input type="text" id="username" name="username" value="{{ old('username', $lastDeploymentDetails['username'] ?? '') }}" required style="width: 100%; padding: 8px; box-sizing: border-box; border: 1px solid #ccc; border-radius: 4px;">
            @error('username') <span style="color: red; font-size: 0.9em;">{{ $message }}</span> @enderror
        </div>

        <div style="margin-bottom: 15px;">
            <label for="password" style="display: block; margin-bottom: 5px;">Password:</label>
            <input type="password" id="password" name="password" required style="width: 100%; padding: 8px; box-sizing: border-box; border: 1px solid #ccc; border-radius: 4px;" autocomplete="new-password">
            @error('password') <span style="color: red; font-size: 0.9em;">{{ $message }}</span> @enderror
        </div>

        <div style="margin-bottom: 15px;">
            <label for="port" style="display: block; margin-bottom: 5px;">Port (Optional):</label>
            <input type="number" id="port" name="port" value="{{ old('port', $lastDeploymentDetails['port'] ?? '') }}" style="width: 100%; padding: 8px; box-sizing: border-box; border: 1px solid #ccc; border-radius: 4px;" placeholder="e.g., 21 for FTP, 22 for SFTP">
            <small>Leave blank for default (FTP: 21, SFTP: 22).</small>
            @error('port') <span style="color: red; font-size: 0.9em;">{{ $message }}</span> @enderror
        </div>

        <div style="margin-bottom: 20px;">
            <label for="remote_path" style="display: block; margin-bottom: 5px;">Remote Path (Optional):</label>
            <input type="text" id="remote_path" name="remote_path" value="{{ old('remote_path', $lastDeploymentDetails['remote_path'] ?? config('webpilotai.default_deployment_path_cpanel', 'public_html')) }}" style="width: 100%; padding: 8px; box-sizing: border-box; border: 1px solid #ccc; border-radius: 4px;" placeholder="e.g., public_html or www or public_html/my-subdirectory">
            <small>The directory on your server where website files should be uploaded. Defaults to 'public_html' or similar.</small>
            @error('remote_path') <span style="color: red; font-size: 0.9em;">{{ $message }}</span> @enderror
        </div>

        {{-- cPanel specific fields - initially hidden, shown by JS if cPanel is selected --}}
        <div id="cpanel_fields" style="display: {{ old('deployment_type', $lastDeploymentDetails['type'] ?? 'ftp') == 'cpanel' ? 'block' : 'none' }}; border: 1px dashed #007bff; padding: 10px; margin-bottom:15px; background-color: #f0f8ff;">
            <h4 style="margin-top:0; color: #0056b3;">cPanel Specific Settings</h4>
            <div style="margin-bottom: 15px;">
                <label for="cpanel_domain" style="display: block; margin-bottom: 5px;">cPanel Domain:</label>
                <input type="text" id="cpanel_domain" name="cpanel_domain" value="{{ old('cpanel_domain', $lastDeploymentDetails['cpanel_domain'] ?? '') }}" style="width: 100%; padding: 8px; box-sizing: border-box; border: 1px solid #ccc; border-radius: 4px;" placeholder="e.g., yourdomain.com (associated with the cPanel account)">
                @error('cpanel_domain') <span style="color: red; font-size: 0.9em;">{{ $message }}</span> @enderror
                <small>The primary domain of your cPanel account, or the specific domain if deploying to an addon domain's root. The 'Host / Server Address' above should be your cPanel server's hostname (e.g., server.hosting.com).</small>
            </div>
        </div>

        <button type="submit" style="padding: 10px 20px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 1em;">Start Deployment</button>
        <a href="{{ route('frontend.webpilotai.websites.show', $website->id) }}" style="margin-left: 10px; padding: 10px 15px; background-color: #6c757d; color: white; text-decoration: none; border-radius: 4px;">Cancel</a>
    </form>

    <script>
        document.addEventListener('DOMContentLoaded', function () {
            const deploymentTypeSelect = document.getElementById('deployment_type');
            const cpanelFieldsDiv = document.getElementById('cpanel_fields');
            const cpanelDomainInput = document.getElementById('cpanel_domain');

            function toggleCpanelFields() {
                const isCpanel = deploymentTypeSelect.value === 'cpanel';
                cpanelFieldsDiv.style.display = isCpanel ? 'block' : 'none';
                cpanelDomainInput.required = isCpanel; // Make cpanel_domain required only if cPanel is selected
            }
            deploymentTypeSelect.addEventListener('change', toggleCpanelFields);
            toggleCpanelFields(); // Initial check
        });
    </script>
</x-webpilotai::layouts.master>


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\user\websites\edit.blade.php ---
@extends('layouts.app') {{-- Or your main user-facing layout --}}

@section('title', 'Edit My Website: ' . ($website->name ?: 'Untitled Website'))
@section('header_title', 'Edit My Website') {{-- Or your layout's equivalent section --}}

@section('content')
<div class="container mx-auto px-4 py-8">
    <div class="max-w-3xl mx-auto bg-white dark:bg-gray-800 shadow-xl rounded-lg p-6 md:p-8">
        <h1 class="text-3xl font-bold text-gray-800 dark:text-white mb-2">Edit: {{ $website->name ?: 'Untitled Website' }}</h1>
        <p class="text-gray-600 dark:text-gray-300 mb-6">Modify your website details below. Checking "Re-generate" will create a new version.</p>

        @include('webpilotai::user.partials._validation_errors')
        @include('webpilotai::admin.partials._session_messages')

        <form action="{{ route('frontend.webpilotai.websites.update', $website->id) }}" method="POST">
            @csrf
            @method('PUT')
            <div class="space-y-6">
                <div>
                    <label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Website Name (Optional)</label>
                    <input type="text" id="name" name="name" value="{{ old('name', $website->name) }}"
                           class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">
                </div>

                <div>
                    <label for="description_prompt" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Describe your website needs (The Prompt) <span class="text-red-500">*</span></label>
                    <textarea name="description_prompt" id="description_prompt" rows="8" required
                              class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">{{ old('description_prompt', $website->description_prompt) }}</textarea>
                </div>

                {{-- AI Template Selection --}}
                <div class="p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
                    <label class="block text-lg font-semibold text-gray-700 dark:text-gray-300 mb-3">Choose an AI Template (Optional)</label>
                    @if($aiTemplates->isEmpty())
                        <p class="text-gray-500 dark:text-gray-400">No AI templates are currently available.</p>
                    @else
                        <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 template-selection-container-user">
                            <label class="template-card-user p-4 border rounded-lg cursor-pointer hover:border-indigo-500 dark:border-gray-600 dark:hover:border-indigo-400 transition-colors">
                                <input type="radio" name="ai_template_id" value="" {{ old('ai_template_id', $website->ai_template_id) == '' ? 'checked' : '' }} class="mr-2">
                                <strong class="block text-md font-medium text-gray-800 dark:text-gray-100 mb-1">No Specific Template</strong>
                                <p class="text-xs text-gray-600 dark:text-gray-400">Use default AI behavior.</p>
                            </label>
                            @foreach($aiTemplates as $template)
                            <label class="template-card-user p-4 border rounded-lg cursor-pointer hover:border-indigo-500 dark:border-gray-600 dark:hover:border-indigo-400 transition-colors">
                                <input type="radio" name="ai_template_id" value="{{ $template->id }}" {{ old('ai_template_id', $website->ai_template_id) == $template->id ? 'checked' : '' }} class="mr-2">
                                @if($template->preview_image_path)
                                    <img src="{{ asset('storage/' . $template->preview_image_path) }}" alt="{{ $template->name }} preview" class="w-full h-24 object-cover rounded mb-2 border dark:border-gray-600">
                                @else
                                    <div class="w-full h-24 flex items-center justify-center bg-gray-100 dark:bg-gray-700 text-gray-400 dark:text-gray-500 rounded mb-2 text-xs">No Preview</div>
                                @endif
                                <strong class="block text-md font-medium text-gray-800 dark:text-gray-100 mb-1">
                                    {{ $template->name }}
                                    @if($template->is_premium)<span class="text-xs text-yellow-500 ml-1">[Premium]</span>@endif
                                </strong>
                                <p class="text-xs text-gray-600 dark:text-gray-400 truncate" title="{{ $template->description }}">{{ Str::limit($template->description, 50) }}</p>
                            </label>
                            @endforeach
                        </div>
                        <small class="block mt-2 text-xs text-gray-500 dark:text-gray-400">This will be applied if you choose to re-generate.</small>
                    @endif
                </div>

                {{-- AI Style Presets Selection --}}
                <div class="p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
                    <label class="block text-lg font-semibold text-gray-700 dark:text-gray-300 mb-3">Choose AI Style Presets (Optional)</label>
                    @if($aiStylePresets->isEmpty())
                        <p class="text-gray-500 dark:text-gray-400">No AI style presets are currently available.</p>
                    @else
                        @php
                            $groupedStylePresets = $aiStylePresets->groupBy('type');
                            $currentPresetIds = old('ai_style_preset_ids', $website->aiStylePresets->pluck('id')->toArray());
                        @endphp
                        @foreach($groupedStylePresets as $type => $presets)
                            <fieldset class="mb-4">
                                <legend class="text-md font-semibold text-gray-700 dark:text-gray-300 mb-2">{{ Str::title(str_replace('_', ' ', $type)) }}</legend>
                                <div class="space-y-2">
                                    @foreach($presets as $preset)
                                    <label for="ai_style_preset_{{ $preset->id }}" class="flex items-center text-sm text-gray-700 dark:text-gray-300">
                                        <input type="checkbox" name="ai_style_preset_ids[]" id="ai_style_preset_{{ $preset->id }}" value="{{ $preset->id }}"
                                               {{ (is_array($currentPresetIds) && in_array($preset->id, $currentPresetIds)) ? 'checked' : '' }}
                                               class="h-4 w-4 text-indigo-600 border-gray-300 dark:border-gray-600 rounded focus:ring-indigo-500 mr-2">
                                        <span title="{{ $preset->description }}">{{ $preset->name }}</span>
                                        @if($preset->is_premium)<span class="text-xs text-yellow-500 ml-1">[Premium]</span>@endif
                                        @if($preset->preview_image_path)
                                            <img src="{{ asset('storage/' . $preset->preview_image_path) }}" alt="{{ $preset->name }} preview" class="h-5 w-8 object-cover ml-2 border dark:border-gray-600 rounded-sm">
                                        @endif
                                    </label>
                                    @endforeach
                                </div>
                            </fieldset>
                        @endforeach
                        <small class="block mt-2 text-xs text-gray-500 dark:text-gray-400">Changes will be applied if you choose to re-generate.</small>
                    @endif
                </div>

                <div>
                    <label for="ai_model_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Choose AI Generation Model <span class="text-red-500">*</span></label>
                    @if($aiModels->isEmpty())
                        <p class="mt-1 text-orange-500">No AI models are currently available. Please contact support.</p>
                    @else
                        <select name="ai_model_id" id="ai_model_id" required
                                class="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:text-gray-200">
                            @foreach($aiModels as $model)
                                <option value="{{ $model->id }}" {{ old('ai_model_id', $website->ai_model_id) == $model->id ? 'selected' : '' }}>
                                    {{ $model->name }} ({{ $model->provider }}) {{ $model->is_premium ? '[Premium]' : '' }}
                                </option>
                            @endforeach
                        </select>
                    @endif
                </div>

                <div>
                    <label for="regenerate" class="flex items-center">
                        <input type="checkbox" id="regenerate" name="regenerate" value="1" {{ old('regenerate') ? 'checked' : '' }}
                               class="h-4 w-4 text-indigo-600 border-gray-300 dark:border-gray-600 rounded focus:ring-indigo-500 mr-2">
                        <span class="text-sm text-gray-700 dark:text-gray-300">Re-generate website content with these new details</span>
                    </label>
                    <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">If checked, the existing generated site will be replaced. If unchecked, only the associations will be updated.</p>
                </div>

                <div class="pt-5">
                    <div class="flex justify-end">
                        <a href="{{ route('frontend.webpilotai.websites.show', $website->id) }}" class="bg-gray-200 hover:bg-gray-300 text-gray-700 font-bold py-2 px-4 rounded-lg mr-3 transition duration-150 ease-in-out dark:bg-gray-600 dark:text-gray-200 dark:hover:bg-gray-500">
                            Cancel
                        </a>
                        <button type="submit"
                                class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded-lg shadow-md transition duration-150 ease-in-out {{ $aiModels->isEmpty() ? 'opacity-50 cursor-not-allowed' : '' }}"
                                {{ $aiModels->isEmpty() ? 'disabled' : '' }}>
                            <i class="fas fa-save mr-2"></i> Update Website
                        </button>
                    </div>
                </div>
            </div>
        </form>
    </div>
</div>
@endsection

@push('styles')
<style>
    .template-card-user:has(input[type="radio"]:checked) {
        border-color: #4f46e5; /* indigo-600 */
        box-shadow: 0 0 0 2px rgba(79, 70, 229, .5);
    }
    .template-card-user input[type="radio"]:checked + img + strong,
    .template-card-user input[type="radio"]:checked + div + strong, /* For No Preview */
    .template-card-user input[type="radio"]:checked + strong /* For No Specific Template */ {
        color: #4f46e5; /* indigo-600 */
    }
</style>
@endpush


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\user\websites\index.blade.php ---
<x-webpilotai::layouts.master>
    <h1>My AI Websites</h1>
    <div style="margin-bottom: 20px;">
        <a href="{{ route('admin.webpilotai.user-sites.create') }}" style="padding: 10px 15px; background-color: #007bff; color: white; text-decoration: none; border-radius: 4px;">Create New AI Website</a>
    </div>

    @if(session('success'))
        <div style="color: green; margin-bottom: 15px; padding: 10px; border: 1px solid green; background-color: #e6ffed;">{{ session('success') }}</div>
    @endif

    @if($websites->isEmpty())
        <p>You haven't created any AI websites yet. <a href="{{ route('admin.webpilotai.user-sites.create') }}">Get started now!</a></p>
    @else
        <table style="width: 100%; border-collapse: collapse; border: 1px solid #ddd;">
            <thead style="background-color: #f0f0f0;">
                <tr>
                    <th style="padding: 10px; border: 1px solid #ddd; text-align: left;">Name</th>
                    <th style="padding: 10px; border: 1px solid #ddd; text-align: left;">AI Model</th>
                    <th style="padding: 10px; border: 1px solid #ddd; text-align: left;">Status</th>
                    <th style="padding: 10px; border: 1px solid #ddd; text-align: left;">Created At</th>
                    <th style="padding: 10px; border: 1px solid #ddd; text-align: left;">Actions</th>
                </tr>
            </thead>
            <tbody>
                @foreach($websites as $website)
                    <tr>
                        <td style="padding: 10px; border: 1px solid #ddd;">{{ $website->name ?: 'Untitled Website' }}</td>
                        <td style="padding: 10px; border: 1px solid #ddd;">{{ $website->aiModel?->name ?: 'N/A' }}</td>
                        <td style="padding: 10px; border: 1px solid #ddd;">
                            <span style="padding: 3px 7px; border-radius: 3px; color: white; background-color: {{ $website->status === 'completed' ? 'green' : ($website->status === 'failed' ? 'red' : 'orange') }};">
                                {{ ucfirst(str_replace('_', ' ', $website->status)) }}
                            </span>
                        </td>
                        <td style="padding: 10px; border: 1px solid #ddd;">{{ $website->created_at->format('M d, Y H:i') }}</td>
                        <td style="padding: 10px; border: 1px solid #ddd;">
                            <a href="{{ route('frontend.webpilotai.websites.show', $website->id) }}" style="margin-right: 8px; color: #007bff; text-decoration: none;">View</a>
                            <a href="{{ route('frontend.webpilotai.websites.edit', $website->id) }}" style="margin-right: 8px; color: #ffc107; text-decoration: none;">Edit</a>
                            <form action="{{ route('frontend.webpilotai.websites.destroy', $website->id) }}" method="POST" style="display: inline;" onsubmit="return confirm('Are you sure you want to delete this website and its generated files? This action cannot be undone.');">
                                @csrf
                                @method('DELETE')
                                <button type="submit" style="background: none; border: none; color: red; cursor: pointer; padding: 0; font-size: inherit; text-decoration: underline;">Delete</button>
                            </form>
                        </td>
                    </tr>
                @endforeach
            </tbody>
        </table>
        <div style="margin-top: 20px;">
            {{ $websites->links() }}
        </div>
    @endif
</x-webpilotai::layouts.master>


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\user\websites\show.blade.php ---
<x-webpilotai::layouts.master>
    <style>
        .status-badge {
            padding: 5px 10px;
            border-radius: 4px;
            color: white;
            font-weight: bold;
        }
        .status-pending_generation { background-color: #ffc107; color: #333; }
        .status-generating { background-color: #007bff; }
        .status-completed { background-color: #28a745; }
        .status-failed { background-color: #dc3545; }
        .status-pending_regeneration { background-color: #fd7e14; }
        .status-deploying { background-color: #17a2b8; }
        .status-deployed { background-color: #20c997; }
        .status-deployment_failed { background-color: #dc3545; }

        .action-buttons a, .action-buttons button {
            margin-right: 5px;
            margin-bottom: 5px;
            padding: 8px 12px;
            text-decoration: none;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        .btn-primary { background-color: #007bff; color: white;}
        .btn-secondary { background-color: #6c757d; color: white;}
        .btn-info { background-color: #17a2b8; color: white;}
        .btn-danger { background-color: #dc3545; color: white;}
        .btn-warning { background-color: #ffc107; color: #212529;}

        .card {
            border: 1px solid #e0e0e0;
            border-radius: 5px;
            padding: 20px;
            margin-bottom: 20px;
            background-color: #fff;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        .card-header {
            font-size: 1.2em;
            font-weight: bold;
            margin-bottom: 15px;
            padding-bottom: 10px;
            border-bottom: 1px solid #eee;
        }
        .status-section p { margin: 8px 0; }
        .error-message { color: #dc3545; font-weight: bold; margin-top: 5px; }
        .deployment-details { background-color: #e9f7fd; border-left: 3px solid #17a2b8; padding: 10px; margin-top:10px; font-size:0.9em;}
        .deployment-details strong { color: #0056b3; }

    </style>

    <h1>Website: {{ $website->name ?: 'Untitled Website' }}</h1>

    @if(session('success'))
        <div style="padding: 10px; margin-bottom: 15px; background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; border-radius: 4px;">
            {{ session('success') }}
        </div>
    @endif

    @if($errors->any())
        <div style="color: red; margin-bottom: 15px; padding: 10px; border: 1px solid red; background-color: #ffe6e6;">
            <strong>Whoops! Something went wrong.</strong>
            <ul style="margin-top: 5px; margin-left: 20px;">
                @foreach ($errors->all() as $error)
                    <li>{{ $error }}</li>
                @endforeach
            </ul>
        </div>
    @endif

    {{-- Dedicated Status Container --}}
    <div id="website-status-container" class="card">
        <div class="card-header">Website Status & Details</div>
        <div class="status-section">
            <p><strong>Prompt:</strong> <span id="prompt-display">{{ $website->description_prompt }}</span></p>
            <p><strong>AI Model:</strong> <span id="ai-model-display">{{ $website->aiModel?->name ?? 'N/A' }}</span></p>
            <p><strong>Overall Status:</strong>
                <span id="status-badge-display" class="status-badge status-{{ $website->status }}">
                    {{ Str::title(str_replace('_', ' ', $website->status)) }}
                </span>
            </p>
            <div id="error-message-display" class="error-message" style="{{ ($website->status === \Modules\WebPilotAI\Models\Website::STATUS_FAILED || $website->status === \Modules\WebPilotAI\Models\Website::STATUS_DEPLOYMENT_FAILED) && $website->generation_error ? '' : 'display:none;' }}">
                <strong>Error:</strong> {{ $website->generation_error }}
            </div>
            <p><strong>Last Generated:</strong> <span id="last-generated-display">{{ $website->last_generated_at ? $website->last_generated_at->format('M d, Y H:i A') : 'Never' }}</span></p>

            @if($website->status === \Modules\WebPilotAI\Models\Website::STATUS_DEPLOYED && $website->deployment_details)
            <div id="deployment-details-display" class="deployment-details">
                <p><strong>Last Deployed To:</strong> {{ $website->deployment_details['host'] ?? 'N/A' }}
                    @if(isset($website->deployment_details['type'])) (Type: {{ strtoupper($website->deployment_details['type']) }}) @endif
                </p>
                @if(isset($website->deployment_details['remote_path']))
                    <p><strong>Remote Path:</strong> {{ $website->deployment_details['remote_path'] }}</p>
                @endif
                 @if(isset($website->deployment_details['deployed_at']))
                    <p><strong>Deployment Time:</strong> {{ \Carbon\Carbon::parse($website->deployment_details['deployed_at'])->format('M d, Y H:i A') }}</p>
                @endif
            </div>
            @else
            <div id="deployment-details-display" class="deployment-details" style="display:none;">
                </div>
            @endif
        </div>
    </div>
    {{-- End Dedicated Status Container --}}

    <div class="action-buttons">
        <a href="{{ route('frontend.webpilotai.websites.edit', $website->id) }}" class="btn-secondary">Edit & Regenerate</a>
        <a id="download-zip-button" href="{{ $website->generated_content_path ? asset($website->generated_content_path) : '#' }}" download class="btn-primary" style="{{ ($website->status === \Modules\WebPilotAI\Models\Website::STATUS_COMPLETED && $website->generated_content_path) ? '' : 'display:none;' }}">Download ZIP</a>
        <a id="deploy-hosting-button" href="{{ route('frontend.webpilotai.websites.deploy.form', $website->id) }}" class="btn-info" style="{{ ($website->status === \Modules\WebPilotAI\Models\Website::STATUS_COMPLETED && $website->generated_content_path) ? '' : 'display:none;' }}">Deploy to Hosting</a>
        <form action="{{ route('frontend.webpilotai.websites.destroy', $website->id) }}" method="POST" style="display: inline;" onsubmit="return confirm('Are you sure you want to delete this website? This action cannot be undone.');">
            @csrf
            @method('DELETE')
            <button type="submit" class="btn-danger">Delete Website</button>
        </form>
    </div>

    <a href="{{ route('frontend.webpilotai.websites.index') }}" style="display: inline-block; margin-top: 20px;">&laquo; Back to My Websites</a>

    {{-- JavaScript will always be present to handle initial state and potential polling --}}
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            let refreshInterval = 5000; // 5 seconds
            let attempts = 0;
            let maxAttempts = 24; // Refresh for up to 2 minutes (24 * 5s = 120s)
            let initialStatus = "{{ $website->status }}";

            const statusBadgeDisplay = document.getElementById('status-badge-display');
            const errorMessageDisplay = document.getElementById('error-message-display');
            const lastGeneratedDisplay = document.getElementById('last-generated-display');
            const deploymentDetailsDisplay = document.getElementById('deployment-details-display');
            const downloadZipButton = document.getElementById('download-zip-button');
            const deployHostingButton = document.getElementById('deploy-hosting-button');

            function requestNotificationPermission() {
                if (!("Notification" in window)) {
                    console.log("This browser does not support desktop notification");
                } else if (Notification.permission === "granted") {
                    // If permission is already granted, we're good.
                    return;
                } else if (Notification.permission !== "denied") {
                    Notification.requestPermission().then(function (permission) {
                        if (permission === "granted") {
                            console.log("Notification permission granted.");
                        }
                    });
                }
            }

            function updateUI(data) {
                // Update status badge
                statusBadgeDisplay.className = 'status-badge status-' + data.status;
                statusBadgeDisplay.textContent = data.status_friendly;

                // Update error message
                if ((data.status === '{{ \Modules\WebPilotAI\Models\Website::STATUS_FAILED }}' || data.status === '{{ \Modules\WebPilotAI\Models\Website::STATUS_DEPLOYMENT_FAILED }}') && data.generation_error) {
                    errorMessageDisplay.innerHTML = '<strong>Error:</strong> ' + data.generation_error;
                    errorMessageDisplay.style.display = 'block';
                } else {
                    errorMessageDisplay.style.display = 'none';
                }

                // Update last generated
                lastGeneratedDisplay.textContent = data.last_generated_at;

                // Update deployment details
                if (data.status === '{{ \Modules\WebPilotAI\Models\Website::STATUS_DEPLOYED }}' && data.deployment_details) {
                    let detailsHtml = '<p><strong>Last Deployed To:</strong> ' + (data.deployment_details.host || 'N/A');
                    if (data.deployment_details.type) {
                        detailsHtml += ' (Type: ' + data.deployment_details.type.toUpperCase() + ')';
                    }
                    detailsHtml += '</p>';
                    if (data.deployment_details.remote_path) {
                        detailsHtml += '<p><strong>Remote Path:</strong> ' + data.deployment_details.remote_path + '</p>';
                    }
                    if (data.deployment_details.deployed_at) {
                        // Assuming deployed_at is already formatted by the server or format here
                        let deployedDate = new Date(data.deployment_details.deployed_at).toLocaleString('en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit', hour12: true });
                        detailsHtml += '<p><strong>Deployment Time:</strong> ' + deployedDate + '</p>';
                    }
                    deploymentDetailsDisplay.innerHTML = detailsHtml;
                    deploymentDetailsDisplay.style.display = 'block';
                } else {
                    deploymentDetailsDisplay.style.display = 'none';
                }

                // Update action buttons visibility
                downloadZipButton.style.display = data.can_download ? 'inline-block' : 'none';
                if(data.can_download) downloadZipButton.href = data.generated_content_path;

                deployHostingButton.style.display = data.can_deploy_form ? 'inline-block' : 'none';

                // Return current status for polling decision
                return data.status;
            }

            function checkStatus() {
                attempts++;
                if (attempts > maxAttempts) {
                    console.log('Max refresh attempts reached. Stopping auto-refresh.');
                    // TODO: Optionally notify user that auto-refresh has stopped.
                    return;
                }
                fetch("{{ route('frontend.webpilotai.websites.status', $website->id) }}", {
                    headers: {
                        'X-Requested-With': 'XMLHttpRequest',
                        'Accept': 'application/json'
                    }
                })
                    .then(response => response.json())
                    .then(data => {
                        const currentStatus = updateUI(data);

                        if (currentStatus === '{{ \Modules\WebPilotAI\Models\Website::STATUS_GENERATING }}' || currentStatus === '{{ \Modules\WebPilotAI\Models\Website::STATUS_DEPLOYING }}') {
                            setTimeout(checkStatus, refreshInterval);
                        } else {
                            console.log('Process finished. Status: ' + currentStatus);
                            // Trigger browser notification if permission granted
                            if (Notification.permission === "granted") {
                                new Notification("WebPilotAI: " + data.name, { body: "Status updated to: " + data.status_friendly });
                            }
                        }
                    })
                    .catch(error => console.error('Error fetching website status:', error));
            }

            // Start polling if initial status is generating or deploying
            if (initialStatus === '{{ \Modules\WebPilotAI\Models\Website::STATUS_GENERATING }}' || initialStatus === '{{ \Modules\WebPilotAI\Models\Website::STATUS_DEPLOYING }}') {
                setTimeout(checkStatus, refreshInterval);
            }
            // Request notification permission on load
            requestNotificationPermission();
            }
        });
    </script>

</x-webpilotai::layouts.master>


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\resources\views\index.blade.php ---
<x-webpilotai::layouts.master>
    <h1>Hello World</h1>

    <p>Module: {!! config('webpilotai.name') !!}</p>
</x-webpilotai::layouts.master>


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\routes\admin.php ---
<?php

use Illuminate\Support\Facades\Route;
use Modules\WebPilotAI\Http\Controllers\Admin\DashboardController;
use Modules\WebPilotAI\Http\Controllers\Admin\AIModelController;
use Modules\WebPilotAI\Http\Controllers\Admin\UserSiteController;
use Modules\WebPilotAI\Http\Controllers\Admin\AITemplateController;
use Modules\WebPilotAI\Http\Controllers\Admin\AIStylePresetController;
use Modules\WebPilotAI\Http\Controllers\Admin\ModuleSettingsController;

/*
|--------------------------------------------------------------------------
| WebPilotAI Admin Routes
|--------------------------------------------------------------------------
|
| Here is where you can register admin routes for your WebPilotAI module.
| These routes are loaded by the WebPilotAIServiceProvider within a group which
| is assigned the "web" middleware group, a '/admin/webpilotai' prefix,
| and the 'admin.webpilotai.' route name prefix.
|
*/

// Dashboard
Route::get('dashboard', [DashboardController::class, 'index'])->name('dashboard');

// AI Model Configuration
Route::resource('models', AIModelController::class)->except(['show']); // Assuming no show view for admin models list

// User Websites
Route::resource('user-sites', UserSiteController::class);

// AI Templates
Route::resource('ai-templates', AITemplateController::class);

// AI Style Presets
Route::resource('ai-style-presets', AIStylePresetController::class);

// Module Settings
Route::get('settings', [ModuleSettingsController::class, 'index'])->name('settings.index');
Route::put('settings', [ModuleSettingsController::class, 'update'])->name('settings.update');

// You can add more admin-specific routes here if needed


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\routes\api.php ---
<?php

use Illuminate\Support\Facades\Route;
use Modules\WebPilotAI\Http\Controllers\User\WebsiteController; // Updated path

Route::middleware(['auth:sanctum'])->prefix('v1')->group(function () {
    Route::apiResource('websites', WebsiteController::class)->names('api.webpilotai.websites'); // Changed resource name and route name for clarity
});


--- File: D:\projects\digitalvocano\Modules\WebPilotAI\routes\web.php ---
<?php

use Illuminate\Support\Facades\Route;
use Modules\WebPilotAI\Http\Controllers\User\WebsiteController;

/*
|--------------------------------------------------------------------------
| WebPilotAI Frontend Routes
|--------------------------------------------------------------------------
|
| Here is where you can register frontend routes for your WebPilotAI module.
| These routes are loaded by the WebPilotAIServiceProvider within a group which
| is assigned the "web" middleware group and the 'frontend.webpilotai.' route name prefix.
| You might also want to apply auth middleware here or in the controller.
|
*/

Route::middleware(['auth', 'verified'])->group(function () { // Assuming user routes require authentication
    // User Websites CRUD
    Route::resource('websites', WebsiteController::class)->names([
        'index' => 'websites.index',
        'create' => 'websites.create',
        'store' => 'websites.store',
        'show' => 'websites.show',
        'edit' => 'websites.edit',
        'update' => 'websites.update',
        'destroy' => 'websites.destroy',
    ]);

    // Deployment Routes
    Route::get('websites/{website}/deploy', [WebsiteController::class, 'showDeployForm'])->name('websites.deploy.form');
    Route::post('websites/{website}/deploy', [WebsiteController::class, 'deploy'])->name('websites.deploy.submit');
    Route::get('websites/{website}/status', [WebsiteController::class, 'checkStatus'])->name('websites.status'); // For AJAX status checks
});



