Add template preview functionality: Introduced TemplatePreviewController and TemplatePreviewService for rendering dynamic previews of header, footer, and section templates. Updated form schemas to include preview components and enhanced real-time content updates. Added configuration for template assets and integrated preview functionality into the admin panel.

This commit is contained in:
Ümit Tunç
2025-10-31 16:48:36 -03:00
parent 67d8b4c781
commit 52c4ba1b19
8 changed files with 578 additions and 9 deletions
@@ -6,6 +6,7 @@ use Filament\Forms\Components\CodeEditor;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\View;
use Filament\Schemas\Schema;
class FooterTemplateForm
@@ -22,19 +23,31 @@ class FooterTemplateForm
->maxLength(255)
->columnSpanFull(),
// Preview Component
View::make('components.template-preview')
->extraAttributes([
'data-type' => 'footer',
'data-field-name' => 'html_content',
])
->columnSpanFull(),
CodeEditor::make('html_content')
->label(__('footer-templates.field_html_content'))
->required()
//->lineNumbers()
->live(onBlur: false) // Real-time updates
->columnSpanFull()
->helperText(__('footer-templates.field_html_content_help')),
->helperText(__('footer-templates.field_html_content_help'))
->extraAttributes([
'style' => 'min-height: 400px;',
'data-field-name' => 'html_content',
]),
Toggle::make('is_active')
->label(__('footer-templates.field_is_active'))
->default(true)
->inline(false),
])
->columns(2),
->columnSpanFull(),
]);
}
}
@@ -6,6 +6,7 @@ use Filament\Forms\Components\CodeEditor;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\View;
use Filament\Schemas\Schema;
class HeaderTemplateForm
@@ -22,19 +23,31 @@ class HeaderTemplateForm
->maxLength(255)
->columnSpanFull(),
// Preview Component
View::make('components.template-preview')
->extraAttributes([
'data-type' => 'header',
'data-field-name' => 'html_content',
])
->columnSpanFull(),
CodeEditor::make('html_content')
->label(__('header-templates.field_html_content'))
->required()
//->lineNumbers()
->live(onBlur: false) // Real-time updates
->columnSpanFull()
->helperText(__('header-templates.field_html_content_help')),
->helperText(__('header-templates.field_html_content_help'))
->extraAttributes([
'style' => 'min-height: 400px;',
'data-field-name' => 'html_content',
]),
Toggle::make('is_active')
->label(__('header-templates.field_is_active'))
->default(true)
->inline(false),
])
->columns(2),
->columnSpanFull(),
]);
}
}
@@ -6,6 +6,7 @@ use Filament\Forms\Components\CodeEditor;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\View;
use Filament\Schemas\Schema;
class SectionTemplateForm
@@ -22,19 +23,31 @@ class SectionTemplateForm
->maxLength(255)
->columnSpanFull(),
// Preview Component
View::make('components.template-preview')
->extraAttributes([
'data-type' => 'section',
'data-field-name' => 'html_content',
])
->columnSpanFull(),
CodeEditor::make('html_content')
->label(__('section-templates.field_html_content'))
->required()
//->lineNumbers()
->live(onBlur: false) // Real-time updates
->columnSpanFull()
->helperText(__('section-templates.field_html_content_help')),
->helperText(__('section-templates.field_html_content_help'))
->extraAttributes([
'style' => 'min-height: 400px;',
'data-field-name' => 'html_content',
]),
Toggle::make('is_active')
->label(__('section-templates.field_is_active'))
->default(true)
->inline(false),
])
->columns(2),
->columnSpanFull(),
]);
}
}
@@ -0,0 +1,32 @@
<?php
namespace App\Http\Controllers;
use App\Services\TemplatePreviewService;
use Illuminate\Http\Request;
class TemplatePreviewController extends Controller
{
/**
* Render template preview
*
* @param Request $request
* @return \Illuminate\View\View
*/
public function preview(Request $request)
{
$content = $request->input('content', '');
$type = $request->input('type', 'section'); // header, footer, section
// Validate type
if (!in_array($type, ['header', 'footer', 'section'])) {
$type = 'section';
}
$html = TemplatePreviewService::getPreviewHtml($content, $type);
return response($html)
->header('Content-Type', 'text/html; charset=utf-8');
}
}
+150
View File
@@ -0,0 +1,150 @@
<?php
namespace App\Services;
use App\Models\Setting;
class TemplatePreviewService
{
/**
* Get all CSS assets for preview
*
* @return array
*/
public static function getCssAssets(): array
{
$config = config('template-preview.css', []);
if (config('template-preview.use_settings_override', false)) {
$settingsKeys = config('template-preview.settings_keys', []);
foreach ($settingsKeys as $assetKey => $settingKey) {
if (str_starts_with($assetKey, 'css_')) {
$setting = Setting::get($settingKey);
if ($setting) {
$configKey = str_replace('css_', '', $assetKey);
$config[$configKey] = $setting;
}
}
}
}
return $config;
}
/**
* Get all JavaScript assets for preview
*
* @return array
*/
public static function getJsAssets(): array
{
$config = config('template-preview.js', []);
if (config('template-preview.use_settings_override', false)) {
$settingsKeys = config('template-preview.settings_keys', []);
foreach ($settingsKeys as $assetKey => $settingKey) {
if (str_starts_with($assetKey, 'js_')) {
$setting = Setting::get($settingKey);
if ($setting) {
$configKey = str_replace('js_', '', $assetKey);
$config[$configKey] = $setting;
}
}
}
}
return $config;
}
/**
* Get formatted CSS link tags
*
* @return string
*/
public static function getCssLinks(): string
{
$assets = self::getCssAssets();
$html = '';
foreach ($assets as $key => $path) {
if ($key === 'fonts') {
// Preload for fonts
$html .= sprintf(
'<link rel="preload" href="%s" as="style" onload="this.rel=\'stylesheet\'">' . "\n",
asset($path)
);
} else {
$html .= sprintf(
'<link rel="stylesheet" href="%s">' . "\n",
asset($path)
);
}
}
return $html;
}
/**
* Get formatted JavaScript script tags
*
* @return string
*/
public static function getJsScripts(): string
{
$assets = self::getJsAssets();
$html = '';
foreach ($assets as $path) {
$html .= sprintf(
'<script src="%s"></script>' . "\n",
asset($path)
);
}
return $html;
}
/**
* Get full HTML structure for preview
*
* @param string $content The template HTML content
* @param string $type The template type: 'header', 'footer', or 'section'
* @return string
*/
public static function getPreviewHtml(string $content, string $type = 'section'): string
{
$cssLinks = self::getCssLinks();
$jsScripts = self::getJsScripts();
$meta = config('template-preview.meta', []);
$viewport = $meta['viewport'] ?? 'width=device-width, initial-scale=1';
$charset = $meta['charset'] ?? 'utf-8';
// Wrap content based on type
$bodyContent = match($type) {
'header' => $content,
'footer' => $content,
'section' => '<div class="container py-8">' . $content . '</div>',
default => $content,
};
return <<<HTML
<!doctype html>
<html lang="tr">
<head>
<meta charset="{$charset}">
<meta name="viewport" content="{$viewport}">
<title>Template Preview</title>
{$cssLinks}
</head>
<body style="margin: 0; padding: 0;">
{$bodyContent}
{$jsScripts}
</body>
</html>
HTML;
}
}