HEX
Server: LiteSpeed
System: Linux s1049.use1.mysecurecloudhost.com 4.18.0-477.27.2.lve.el8.x86_64 #1 SMP Wed Oct 11 12:32:56 UTC 2023 x86_64
User: xedaptot (3356)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: /home/xedaptot/be.naniguide.com/app/Http/Controllers/Refactor/Admin/PageTemplateController.php
<?php

namespace App\Http\Controllers\Refactor\Admin;

use App\Dto\Refactor\PageTemplate\SaveContentDto;
use App\Http\Controllers\Controller;
use App\Http\Requests\Refactor\Admin\PageTemplate\CopyPageTemplateRequest;
use App\Http\Requests\Refactor\Admin\PageTemplate\CreatePageTemplateRequest;
use App\Http\Requests\Refactor\Admin\PageTemplate\DeletePageTemplatesRequest;
use App\Http\Requests\Refactor\Admin\PageTemplate\RenamePageTemplateRequest;
use App\Model\PageTemplate;
use App\Model\Template;
use App\Model\TemplateCategory;
use App\Services\PageTemplateService;
use App\Services\TemplateService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

/**
 * Thin orchestration: FormRequest (auth + validate) → DTO (typed transfer) →
 * Service (business logic) → Response. No validation, no queries, no writes
 * directly in this controller.
 */
class PageTemplateController extends Controller
{
    public function __construct(
        private readonly PageTemplateService $service,
    ) {}

    // ── READ ───────────────────────────────────────────────────────────────────

    public function index(Request $request)
    {
        if (\Gate::denies('read', new PageTemplate())) {
            return $this->notAuthorized();
        }

        $baseCategory = TemplateCategory::whereName('Base')->first();
        $extendedCategory = TemplateCategory::whereName('Extended')->first();

        $stats = [
            'total' => PageTemplate::count(),
            'base' => $baseCategory ? PageTemplate::categoryUid($baseCategory->uid)->count() : 0,
            'extended' => $extendedCategory ? PageTemplate::categoryUid($extendedCategory->uid)->count() : 0,
        ];

        return view('refactor.pages.admin.page-templates.index', compact('stats'));
    }

    public function listing(Request $request)
    {
        if (\Gate::denies('read', new PageTemplate())) {
            return $this->notAuthorized();
        }

        $query = PageTemplate::query()->search($request->keyword);

        if (!empty($request->category_uid)) {
            $query = $query->categoryUid($request->category_uid);
        }

        $tab = $request->tab;
        if (in_array($tab, ['base', 'extended'], true)) {
            $category = TemplateCategory::whereName($tab === 'base' ? 'Base' : 'Extended')->first();
            if ($category) {
                $query = $query->categoryUid($category->uid);
            }
        }

        $query->orderBy('created_at', 'desc');

        // Grid view always 16/page (McList default 10 breaks 4-col layout).
        $perPage = $request->view === 'grid' ? 16 : ($request->per_page ?? 15);
        $items = $query->paginate($perPage);

        $view = $request->view === 'grid' ? '_grid' : '_list';

        return view('refactor.pages.admin.page-templates.' . $view, compact('items'));
    }

    public function gallery(Request $request)
    {
        if (\Gate::denies('create', new PageTemplate())) {
            return $this->notAuthorized();
        }

        $tab = in_array($request->tab, ['base', 'extended'], true) ? $request->tab : 'extended';
        $category = TemplateCategory::whereName($tab === 'base' ? 'Base' : 'Extended')->first();

        $query = PageTemplate::query()->orderBy('created_at', 'desc');
        if (!empty($request->keyword)) {
            $query->search($request->keyword);
        }
        if ($category) {
            $query->categoryUid($category->uid);
        }

        $items = $query->paginate(16)->withQueryString();

        return view('refactor.pages.admin.page-templates._gallery', compact('items', 'tab'));
    }

    public function preview(Request $request, string $uid)
    {
        $item = PageTemplate::findByUid($uid);

        if (!$item) {
            return redirect()->back();
        }

        if (\Gate::denies('read', $item)) {
            return $this->notAuthorized();
        }

        return $item->template->getPreviewContent();
    }

    // ── WRITE (FormRequest → DTO → Service) ────────────────────────────────────

    public function add(Request $request)
    {
        if (\Gate::denies('create', new PageTemplate())) {
            return $this->notAuthorized();
        }

        $template = PageTemplate::newDefault();
        $template->name = trans('messages.untitled_template');

        return view('refactor.pages.admin.page-templates.create', compact('template'));
    }

    public function store(CreatePageTemplateRequest $request): JsonResponse
    {
        $pageTemplate = $this->service->createFromBase($request->toDto());

        if (!$pageTemplate) {
            return response()->json([
                'status' => 'error',
                'errors' => ['template' => [trans('refactor/admin_page_templates.errors.base_not_found')]],
            ], 422);
        }

        return response()->json([
            'status' => 'success',
            'message' => trans('refactor/admin_page_templates.flash.created'),
            'redirect' => route('refactor.admin.page-templates.builder', $pageTemplate->uid),
        ]);
    }

    public function copy(Request $request, string $uid)
    {
        $item = PageTemplate::findByUid($uid);

        if (!$item) {
            return response()->json(['status' => 'error', 'message' => 'Not found'], 404);
        }

        if (\Gate::denies('read', $item)) {
            return $this->notAuthorized();
        }

        return view('refactor.pages.admin.page-templates.copy', compact('item'));
    }

    public function storeCopy(CopyPageTemplateRequest $request, string $uid): JsonResponse
    {
        $newItem = $this->service->copyFrom($request->toDto($uid));

        if (!$newItem) {
            return response()->json(['status' => 'error', 'message' => 'Not found'], 404);
        }

        return response()->json([
            'status' => 'success',
            'message' => trans('refactor/admin_page_templates.flash.copied'),
        ]);
    }

    public function changeName(Request $request, string $uid)
    {
        $item = PageTemplate::findByUid($uid);

        if (!$item) {
            return response()->json(['status' => 'error', 'message' => 'Not found'], 404);
        }

        if (\Gate::denies('read', $item)) {
            return $this->notAuthorized();
        }

        return view('refactor.pages.admin.page-templates.rename', compact('item'));
    }

    public function updateName(RenamePageTemplateRequest $request, string $uid): JsonResponse
    {
        $ok = $this->service->rename($request->toDto($uid));

        if (!$ok) {
            return response()->json(['status' => 'error', 'message' => 'Not found'], 404);
        }

        return response()->json([
            'status' => 'success',
            'message' => trans('refactor/admin_page_templates.flash.renamed'),
        ]);
    }

    public function delete(DeletePageTemplatesRequest $request): JsonResponse
    {
        $this->service->bulkDelete($request->toDto());

        return response()->json([
            'status' => 'success',
            'message' => trans('refactor/admin_page_templates.flash.deleted'),
        ]);
    }

    public function builder(Request $request, string $uid)
    {
        $pageTemplate = PageTemplate::findByUid($uid);

        if (!$pageTemplate || !$request->user()->admin->can('update', $pageTemplate)) {
            return $this->notAuthorized();
        }

        // Templates flipped to custom HTML are locked out of the builder — re-edit happens
        // through customHtml. Mirrors campaign behavior (source='uploaded' is one-way).
        if ($request->isMethod('get') && $pageTemplate->template?->isUploaded()) {
            return redirect()->route('refactor.admin.page-templates.custom_html', $pageTemplate->uid);
        }

        if ($request->isMethod('post')) {
            $result = $this->service->saveContent(SaveContentDto::fromRequest($uid, $request->all()));

            if (!$result['ok']) {
                return response()->json($result['errors'], 400);
            }

            return response()->json(['status' => 'success']);
        }

        return view('refactor.pages.admin.page-templates.builder', compact('pageTemplate'));
    }

    public function customHtml(Request $request, string $uid)
    {
        $pageTemplate = PageTemplate::findByUid($uid);

        if (!$pageTemplate || !$request->user()->admin->can('update', $pageTemplate)) {
            return $this->notAuthorized();
        }

        if ($request->isMethod('post')) {
            $this->validate($request, ['html' => 'required|string']);

            // First switch from builder → uploaded: swap the template (source='uploaded' is
            // a one-way flag, matches campaign's setCustomHtml flow). Subsequent saves only
            // update content of the already-uploaded template.
            if ($pageTemplate->template?->isUploaded()) {
                $validator = $pageTemplate->template->updateContent(
                    Template::DEFAULT_BUILDER_JSON,
                    $request->html
                );

                if ($validator->fails()) {
                    if ($request->ajax()) {
                        return response()->json($validator->errors(), 400);
                    }
                    return back()->withErrors($validator)->withInput();
                }
            } else {
                TemplateService::for($pageTemplate)
                    ->setCustomHtml($request->html, $pageTemplate->name);
            }

            if ($request->ajax()) {
                return response()->json([
                    'status'   => 'success',
                    'message'  => trans('refactor/admin_page_templates.flash.custom_html_saved'),
                    'redirect' => route('refactor.admin.page-templates.index'),
                ]);
            }

            return redirect()->route('refactor.admin.page-templates.index');
        }

        // Pre-fill only when already uploaded — match campaign UX: a brand-new switch starts
        // from an empty editor so the admin pastes their own HTML rather than wrestling with
        // rendered builder output.
        $currentHtml = $pageTemplate->template?->isUploaded()
            ? ($pageTemplate->template->content ?? '')
            : '';

        return view('refactor.pages.admin.page-templates.custom_html', [
            'pageTemplate' => $pageTemplate,
            'currentHtml'  => $currentHtml,
        ]);
    }
}