From 78d3bce5846515b115075aca37fd1cd2a7aab24e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Cmit=20Tun=C3=A7?= Date: Sat, 1 Nov 2025 15:31:53 -0300 Subject: [PATCH] Refactor TemplatePreviewController to support template content retrieval by ID: Updated the preview method to accept a record ID for fetching template content from the database. Introduced a private method to handle content retrieval for header, footer, and section templates. Enhanced error handling for empty content scenarios, improving the overall robustness of the template preview functionality. --- .../Controllers/TemplatePreviewController.php | 37 +- .../components/template-preview.blade.php | 580 +----------------- 2 files changed, 46 insertions(+), 571 deletions(-) diff --git a/app/Http/Controllers/TemplatePreviewController.php b/app/Http/Controllers/TemplatePreviewController.php index 476303f..8c1af3d 100644 --- a/app/Http/Controllers/TemplatePreviewController.php +++ b/app/Http/Controllers/TemplatePreviewController.php @@ -2,6 +2,9 @@ namespace App\Http\Controllers; +use App\Models\HeaderTemplate; +use App\Models\FooterTemplate; +use App\Models\SectionTemplate; use App\Services\TemplatePreviewService; use Illuminate\Http\Request; @@ -11,11 +14,11 @@ class TemplatePreviewController extends Controller * Render template preview * * @param Request $request - * @return \Illuminate\View\View + * @return \Illuminate\Http\Response */ public function preview(Request $request) { - $content = $request->input('content', ''); + $recordId = $request->input('record_id'); $type = $request->input('type', 'section'); // header, footer, section // Validate type @@ -23,10 +26,40 @@ class TemplatePreviewController extends Controller $type = 'section'; } + // ID varsa veritabanından çek, yoksa eskisi gibi çalış + $content = ''; + if ($recordId) { + $content = $this->getTemplateContent($recordId, $type); + } else { + // Fallback: eski yöntem (content gönderimi) + $content = $request->input('content', ''); + } + + if (empty($content)) { + return response('No content to preview', 400); + } + $html = TemplatePreviewService::getPreviewHtml($content, $type); return response($html) ->header('Content-Type', 'text/html; charset=utf-8'); } + + /** + * Get template content from database by ID + * + * @param int $id + * @param string $type + * @return string + */ + private function getTemplateContent(int $id, string $type): string + { + return match($type) { + 'header' => HeaderTemplate::find($id)?->html_content ?? '', + 'footer' => FooterTemplate::find($id)?->html_content ?? '', + 'section' => SectionTemplate::find($id)?->html_content ?? '', + default => '', + }; + } } diff --git a/resources/views/components/template-preview.blade.php b/resources/views/components/template-preview.blade.php index 2dfbe52..095164f 100644 --- a/resources/views/components/template-preview.blade.php +++ b/resources/views/components/template-preview.blade.php @@ -51,7 +51,6 @@
- @@ -66,108 +65,6 @@ x-transition> - - - - @push('scripts') @@ -178,255 +75,21 @@ function templatePreview(previewUrl, type, fieldName) { type: type, fieldName: fieldName, iframeSrc: '', - fullscreenIframeSrc: '', isLoading: false, - content: '', - codeMirrorView: null, - isFullscreen: false, - editorCollapsed: false, - clonedEditor: null, - originalEditorWrapper: null, + recordId: null, init() { - console.log('[Preview] Init:', { previewUrl, type, fieldName }); - - // Wait for everything to load - this.$nextTick(() => { - setTimeout(() => { - this.findCodeMirror(); - }, 1500); - }); - - // ESC key to close fullscreen - document.addEventListener('keydown', (e) => { - if (e.key === 'Escape' && this.isFullscreen) { - this.closeFullscreenEditor(); - } - }); - }, - - findCodeMirror() { - console.log('[Preview] Looking for CodeMirror editor for field:', this.fieldName); - - // Try multiple wrapper selectors for Filament 4 compatibility - const wrapperSelectors = [ - '.fi-fo-field-wrp', - '.fi-input-wrp', - '[wire\\:id]', - ]; - - // Debug: List all field wrappers - let allWrappers = []; - wrapperSelectors.forEach(selector => { - const wrappers = document.querySelectorAll(selector); - if (wrappers.length > 0) { - console.log(`[Preview] Found ${wrappers.length} wrappers with selector: ${selector}`); - allWrappers = Array.from(wrappers); - } - }); - - // Remove duplicates - allWrappers = Array.from(new Set(allWrappers)); - console.log('[Preview] Total unique field wrappers found:', allWrappers.length); - - let targetWrapper = null; - - // Method 1: Find by data-field-name attribute (anywhere in wrapper) - const dataFieldElements = document.querySelectorAll(`[data-field-name="${this.fieldName}"]`); - if (dataFieldElements.length > 0) { - console.log('[Preview] Found', dataFieldElements.length, 'elements with data-field-name'); - // Try to find closest wrapper from any selector - for (const selector of wrapperSelectors) { - targetWrapper = dataFieldElements[0].closest(selector); - if (targetWrapper) break; - } - } - - // Method 2: Find by name attribute in textarea or input - if (!targetWrapper) { - const input = document.querySelector(`textarea[name="${this.fieldName}"], input[name="${this.fieldName}"]`); - if (input) { - console.log('[Preview] Found input/textarea with name:', this.fieldName); - for (const selector of wrapperSelectors) { - targetWrapper = input.closest(selector); - if (targetWrapper) break; - } - } - } - - // Method 3: Find all CodeMirror editors and check their wrappers - if (!targetWrapper) { - const cmEditors = document.querySelectorAll('.cm-editor'); - console.log('[Preview] Found', cmEditors.length, 'CodeMirror editors'); - cmEditors.forEach((cmEditor, idx) => { - for (const selector of wrapperSelectors) { - const wrapper = cmEditor.closest(selector); - if (wrapper) { - const label = wrapper.querySelector('label'); - console.log(`[Preview] CM Editor ${idx} wrapper label:`, label?.textContent?.substring(0, 30)); - - // If this is the only CodeMirror editor, use it - // Or if label contains HTML/Content - if (cmEditors.length === 1 || (label && (label.textContent.toLowerCase().includes('html') || label.textContent.toLowerCase().includes('content')))) { - if (!targetWrapper) { - targetWrapper = wrapper; - console.log('[Preview] Selected wrapper for CM Editor', idx); - } - } - break; - } - } - }); - } - - // Method 4: Find by label text (fallback) - if (!targetWrapper && allWrappers.length > 0) { - allWrappers.forEach((wrapper) => { - const label = wrapper.querySelector('label'); - if (label) { - const labelText = label.textContent.toLowerCase(); - if ((labelText.includes('html') || labelText.includes('content')) && !targetWrapper) { - const hasEditor = wrapper.querySelector('.cm-editor, textarea'); - if (hasEditor) { - targetWrapper = wrapper; - console.log('[Preview] Selected wrapper by label:', labelText.substring(0, 30)); - } - } - } - }); - } - - if (!targetWrapper) { - console.warn('[Preview] Field wrapper not found. Available wrappers:', allWrappers.length); - console.warn('[Preview] Trying to find any CodeMirror editor...'); - - // Last resort: Use first CodeMirror editor found - const firstCmEditor = document.querySelector('.cm-editor'); - if (firstCmEditor) { - for (const selector of wrapperSelectors) { - targetWrapper = firstCmEditor.closest(selector); - if (targetWrapper) break; - } - console.log('[Preview] Using first CodeMirror editor as fallback'); - } - } - - if (!targetWrapper) { - console.error('[Preview] Could not find field wrapper. Make sure the CodeEditor is rendered.'); - return; - } - - console.log('[Preview] Found field wrapper'); - - // Try to find CodeMirror editor - const cmEditor = targetWrapper.querySelector('.cm-editor'); - if (cmEditor) { - console.log('[Preview] Found .cm-editor element'); - - // Try to get from cm-content (CodeMirror 6) - const cmContent = cmEditor.querySelector('.cm-content'); - if (cmContent) { - console.log('[Preview] Found .cm-content element'); - - // Get initial content (no watcher, only on demand) - this.content = cmContent.textContent || ''; - console.log('[Preview] Initial content from .cm-content, length:', this.content.length); - - // Watch for content changes to update internal content variable - // but don't trigger preview update automatically - const observer = new MutationObserver(() => { - const newContent = cmContent.textContent || ''; - if (newContent !== this.content) { - this.content = newContent; - console.log('[Preview] Content changed, length:', this.content.length); - // Don't auto-update, wait for Run button - } - }); - observer.observe(cmContent, { - childList: true, - subtree: true, - characterData: true, - }); - - if (this.content) { - return; // Success! - } - } - - // Try CodeMirror 6 View - if (cmEditor.view || cmEditor._view) { - const view = cmEditor.view || cmEditor._view; - if (view.state && view.state.doc) { - this.content = view.state.doc.toString(); - console.log('[Preview] Found CodeMirror 6 View, content length:', this.content.length); - - if (view.dispatch) { - // Watch for changes to update internal content variable - // but don't trigger preview update automatically - const originalDispatch = view.dispatch; - view.dispatch = (tr) => { - originalDispatch.call(view, tr); - this.content = view.state.doc.toString(); - // Don't auto-update, wait for Run button - }; - } - - if (this.content) { - return; // Success! - } - } - } - - // Try CodeMirror 5 - const cmInstance = cmEditor.__cm || cmEditor.cm; - if (cmInstance) { - console.log('[Preview] Found CodeMirror 5 instance'); - if (cmInstance.getValue) { - this.content = cmInstance.getValue(); - cmInstance.on('change', () => { - this.content = cmInstance.getValue(); - // Don't auto-update, wait for Run button - }); - console.log('[Preview] CodeMirror 5 content length:', this.content.length); - if (this.content) { - return; // Success! - } - } - } - } else { - // Try textarea - const textarea = targetWrapper.querySelector('textarea'); - if (textarea) { - console.log('[Preview] Found textarea'); - this.content = textarea.value || ''; - textarea.addEventListener('input', () => { - this.content = textarea.value; - // Don't auto-update, wait for Run button - }); - console.log('[Preview] Textarea content length:', this.content.length); - } else { - console.warn('[Preview] No CodeMirror editor or textarea found in wrapper'); - } + // URL'den ID'yi al + const urlMatch = window.location.pathname.match(/\/(\d+)\/(edit|view)$/); + if (urlMatch && urlMatch[1]) { + this.recordId = urlMatch[1]; } }, async updatePreview() { - // Refresh content from editor before updating - this.findCodeMirror(); - - // Give a moment for content to be refreshed - await new Promise(resolve => setTimeout(resolve, 100)); - - if (!this.content || !this.content.trim()) { - console.log('[Preview] No content, skipping update'); - if (this.iframeSrc) { - URL.revokeObjectURL(this.iframeSrc); - this.iframeSrc = ''; - } - if (this.fullscreenIframeSrc) { - URL.revokeObjectURL(this.fullscreenIframeSrc); - this.fullscreenIframeSrc = ''; - } + // ID yoksa preview yapma + if (!this.recordId) { + console.log('[Preview] No record ID'); return; } @@ -434,7 +97,7 @@ function templatePreview(previewUrl, type, fieldName) { try { const formData = new FormData(); - formData.append('content', this.content); + formData.append('record_id', this.recordId); formData.append('type', this.type); formData.append('_token', document.querySelector('meta[name="csrf-token"]')?.content || ''); @@ -453,217 +116,15 @@ function templatePreview(previewUrl, type, fieldName) { const html = await response.text(); - // Update both iframes - if (this.iframeSrc) { - URL.revokeObjectURL(this.iframeSrc); - } - if (this.fullscreenIframeSrc) { - URL.revokeObjectURL(this.fullscreenIframeSrc); - } + if (this.iframeSrc) URL.revokeObjectURL(this.iframeSrc); const blob = new Blob([html], { type: 'text/html; charset=utf-8' }); this.iframeSrc = URL.createObjectURL(blob); - this.fullscreenIframeSrc = URL.createObjectURL(blob); this.isLoading = false; - - console.log('[Preview] Updated successfully, content length:', this.content.length); } catch (error) { console.error('[Preview] Error:', error); this.isLoading = false; } - }, - - openFullscreenEditor() { - this.isFullscreen = true; - this.editorCollapsed = false; - - this.$nextTick(() => { - setTimeout(() => { - this.cloneEditorToFullscreen(); - }, 100); - }); - }, - - closeFullscreenEditor() { - // Restore original editor if it was moved - if (this.clonedEditor && this.originalEditorWrapper) { - const editorContainer = this.clonedEditor.parentElement; - if (editorContainer && editorContainer.id === 'fullscreen-code-editor') { - // Move editor back to original location - const cmEditor = this.clonedEditor.querySelector('.cm-editor'); - if (cmEditor && this.originalEditorWrapper) { - // Restore to original wrapper - const originalContainer = this.originalEditorWrapper.querySelector('.cm-editor').parentElement; - if (originalContainer) { - // Keep the editor where it is, just clean up - this.clonedEditor = null; - this.originalEditorWrapper = null; - } - } - } - } - - this.isFullscreen = false; - this.editorCollapsed = false; - - // Clean up fullscreen iframe - if (this.fullscreenIframeSrc) { - URL.revokeObjectURL(this.fullscreenIframeSrc); - this.fullscreenIframeSrc = ''; - } - }, - - toggleEditorCollapse() { - this.editorCollapsed = !this.editorCollapsed; - }, - - cloneEditorToFullscreen() { - const fullscreenContainer = document.getElementById('fullscreen-code-editor'); - if (!fullscreenContainer) { - console.error('[Preview] Fullscreen container not found'); - return; - } - - // Find the original CodeMirror editor - const wrapperSelectors = ['.fi-fo-field-wrp', '.fi-input-wrp', '[wire\\:id]']; - let targetWrapper = null; - - const dataFieldElements = document.querySelectorAll(`[data-field-name="${this.fieldName}"]`); - if (dataFieldElements.length > 0) { - for (const selector of wrapperSelectors) { - targetWrapper = dataFieldElements[0].closest(selector); - if (targetWrapper) break; - } - } - - if (!targetWrapper) { - const input = document.querySelector(`textarea[name="${this.fieldName}"], input[name="${this.fieldName}"]`); - if (input) { - for (const selector of wrapperSelectors) { - targetWrapper = input.closest(selector); - if (targetWrapper) break; - } - } - } - - if (!targetWrapper) { - const cmEditor = document.querySelector('.cm-editor'); - if (cmEditor) { - for (const selector of wrapperSelectors) { - targetWrapper = cmEditor.closest(selector); - if (targetWrapper) break; - } - } - } - - if (!targetWrapper) { - console.error('[Preview] Could not find editor wrapper'); - return; - } - - this.originalEditorWrapper = targetWrapper; - - // Find the CodeMirror editor element - const cmEditor = targetWrapper.querySelector('.cm-editor'); - if (!cmEditor) { - console.error('[Preview] CodeMirror editor not found'); - return; - } - - // Clone the entire editor structure - const editorParent = cmEditor.parentElement; - if (!editorParent) { - console.error('[Preview] Editor parent not found'); - return; - } - - // Create a deep clone - const clone = editorParent.cloneNode(true); - clone.id = 'cloned-editor'; - - // Clear the container first - fullscreenContainer.innerHTML = ''; - - // Append clone - fullscreenContainer.appendChild(clone); - - this.clonedEditor = clone; - - // Adjust styles for fullscreen - const clonedCmEditor = clone.querySelector('.cm-editor'); - if (clonedCmEditor) { - clonedCmEditor.style.height = '100%'; - clonedCmEditor.style.minHeight = '100%'; - - // Make the cm-scroller take full height - const cmScroller = clonedCmEditor.querySelector('.cm-scroller'); - if (cmScroller) { - cmScroller.style.height = '100%'; - cmScroller.style.minHeight = '100%'; - } - - // Make the cm-content take full height - const cmContent = clonedCmEditor.querySelector('.cm-content'); - if (cmContent) { - cmContent.style.minHeight = '100%'; - } - } - - // Update content watcher for cloned editor - const clonedCmContent = clone.querySelector('.cm-content'); - if (clonedCmContent) { - // Watch for changes in cloned editor - const observer = new MutationObserver(() => { - const newContent = clonedCmContent.textContent || ''; - if (newContent !== this.content) { - this.content = newContent; - // Also update original editor if it exists - const originalCmContent = cmEditor.querySelector('.cm-content'); - if (originalCmContent) { - // Sync back to original if needed - } - } - }); - observer.observe(clonedCmContent, { - childList: true, - subtree: true, - characterData: true, - }); - - // Update content from cloned editor - this.content = clonedCmContent.textContent || ''; - } - - // Try to sync CodeMirror 6 view if available - if (clonedCmEditor) { - if (clonedCmEditor.view || clonedCmEditor._view) { - const view = clonedCmEditor.view || clonedCmEditor._view; - if (view.state && view.state.doc) { - // Watch for changes - if (view.dispatch) { - const originalDispatch = view.dispatch; - view.dispatch = (tr) => { - originalDispatch.call(view, tr); - this.content = view.state.doc.toString(); - }; - } - } - } - } - - console.log('[Preview] Editor cloned to fullscreen'); - }, - - destroy() { - if (this.iframeSrc) { - URL.revokeObjectURL(this.iframeSrc); - } - if (this.fullscreenIframeSrc) { - URL.revokeObjectURL(this.fullscreenIframeSrc); - } - if (this.isFullscreen) { - this.closeFullscreenEditor(); - } } }; } @@ -675,25 +136,6 @@ function templatePreview(previewUrl, type, fieldName) { [x-cloak] { display: none !important; } - - /* Fullscreen editor styles */ - #fullscreen-code-editor { - font-family: 'Fira Code', 'Consolas', 'Monaco', monospace; - } - - #fullscreen-code-editor .cm-editor { - font-size: 14px; - line-height: 1.6; - } - - #fullscreen-code-editor .cm-content { - padding: 1rem; - } - - /* Smooth transitions */ - .transition-all { - transition-property: all; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - } @endpush +