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,
]);
}
}