Add template save event handling and update preview functionality: Implemented afterSave method in Footer, Header, Menu, and Section templates to dispatch a 'template-saved' event for Livewire. Updated template preview components to listen for this event, ensuring previews are refreshed after template modifications. Enhanced viewData method in template forms for better record ID handling.
This commit is contained in:
@@ -52,4 +52,11 @@ class EditFooterTemplate extends EditRecord
|
|||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function afterSave(): void
|
||||||
|
{
|
||||||
|
// Kaydetme sonrası preview'ı güncelle
|
||||||
|
// Livewire event dispatch et
|
||||||
|
$this->dispatch('template-saved');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,13 +50,11 @@ class FooterTemplateForm
|
|||||||
|
|
||||||
// Preview Component - placed after editor
|
// Preview Component - placed after editor
|
||||||
View::make('components.template-preview')
|
View::make('components.template-preview')
|
||||||
->viewData(function ($record) {
|
->viewData([
|
||||||
return [
|
'type' => 'footer',
|
||||||
'type' => 'footer',
|
'fieldName' => 'html_content',
|
||||||
'fieldName' => 'html_content',
|
'recordId' => null, // Will be extracted from URL by JavaScript
|
||||||
'recordId' => $record?->id,
|
])
|
||||||
];
|
|
||||||
})
|
|
||||||
->columnSpanFull(),
|
->columnSpanFull(),
|
||||||
|
|
||||||
Toggle::make('is_active')
|
Toggle::make('is_active')
|
||||||
|
|||||||
@@ -52,4 +52,11 @@ class EditHeaderTemplate extends EditRecord
|
|||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function afterSave(): void
|
||||||
|
{
|
||||||
|
// Kaydetme sonrası preview'ı güncelle
|
||||||
|
// Livewire event dispatch et (JavaScript tarafında Livewire.on() ile dinlenecek)
|
||||||
|
$this->dispatch('template-saved');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,13 +49,11 @@ class HeaderTemplateForm
|
|||||||
|
|
||||||
// Preview Component - placed after editor
|
// Preview Component - placed after editor
|
||||||
View::make('components.template-preview')
|
View::make('components.template-preview')
|
||||||
->viewData(function ($record) {
|
->viewData([
|
||||||
return [
|
'type' => 'header',
|
||||||
'type' => 'header',
|
'fieldName' => 'html_content',
|
||||||
'fieldName' => 'html_content',
|
'recordId' => null, // Will be extracted from URL by JavaScript
|
||||||
'recordId' => $record?->id,
|
])
|
||||||
];
|
|
||||||
})
|
|
||||||
->columnSpanFull(),
|
->columnSpanFull(),
|
||||||
|
|
||||||
Toggle::make('is_active')
|
Toggle::make('is_active')
|
||||||
|
|||||||
@@ -28,4 +28,11 @@ class EditMenuTemplate extends EditRecord
|
|||||||
RestoreAction::make(),
|
RestoreAction::make(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function afterSave(): void
|
||||||
|
{
|
||||||
|
// Kaydetme sonrası preview'ı güncelle
|
||||||
|
// Livewire event dispatch et
|
||||||
|
$this->dispatch('template-saved');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,13 +46,11 @@ class MenuTemplateForm
|
|||||||
|
|
||||||
// Preview Component
|
// Preview Component
|
||||||
View::make('components.menu-template-preview')
|
View::make('components.menu-template-preview')
|
||||||
->viewData(function ($record) {
|
->viewData([
|
||||||
return [
|
'type' => 'menu',
|
||||||
'type' => 'menu',
|
'fieldName' => 'html_content',
|
||||||
'fieldName' => 'html_content',
|
'recordId' => null, // Will be extracted from URL by JavaScript
|
||||||
'recordId' => $record?->id,
|
])
|
||||||
];
|
|
||||||
})
|
|
||||||
->columnSpanFull(),
|
->columnSpanFull(),
|
||||||
|
|
||||||
Toggle::make('is_active')
|
Toggle::make('is_active')
|
||||||
|
|||||||
@@ -52,4 +52,11 @@ class EditSectionTemplate extends EditRecord
|
|||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function afterSave(): void
|
||||||
|
{
|
||||||
|
// Kaydetme sonrası preview'ı güncelle
|
||||||
|
// Livewire event dispatch et
|
||||||
|
$this->dispatch('template-saved');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,13 +49,11 @@ class SectionTemplateForm
|
|||||||
|
|
||||||
// Preview Component - placed after editor
|
// Preview Component - placed after editor
|
||||||
View::make('components.template-preview')
|
View::make('components.template-preview')
|
||||||
->viewData(function ($record) {
|
->viewData([
|
||||||
return [
|
'type' => 'section',
|
||||||
'type' => 'section',
|
'fieldName' => 'html_content',
|
||||||
'fieldName' => 'html_content',
|
'recordId' => null, // Will be extracted from URL by JavaScript
|
||||||
'recordId' => $record?->id,
|
])
|
||||||
];
|
|
||||||
})
|
|
||||||
->columnSpanFull(),
|
->columnSpanFull(),
|
||||||
|
|
||||||
Toggle::make('is_active')
|
Toggle::make('is_active')
|
||||||
|
|||||||
@@ -116,6 +116,27 @@ function templatePreview(previewUrl, type, fieldName, recordIdFromView = null) {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('[Preview] Could not get ID from Livewire');
|
console.log('[Preview] Could not get ID from Livewire');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Livewire event listener ekle (Livewire 3.x için)
|
||||||
|
if (window.Livewire) {
|
||||||
|
document.addEventListener('livewire:init', () => {
|
||||||
|
Livewire.on('template-saved', () => {
|
||||||
|
this.updatePreview();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Eğer Livewire zaten yüklendiyse direkt dinle
|
||||||
|
if (window.Livewire.hook) {
|
||||||
|
Livewire.on('template-saved', () => {
|
||||||
|
this.updatePreview();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Window event listener ekle (alternatif - Livewire event'lerini de yakalar)
|
||||||
|
window.addEventListener('template-saved', () => {
|
||||||
|
this.updatePreview();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async updatePreview() {
|
async updatePreview() {
|
||||||
|
|||||||
@@ -85,7 +85,7 @@
|
|||||||
size="sm"
|
size="sm"
|
||||||
color="gray"
|
color="gray"
|
||||||
outlined
|
outlined
|
||||||
@click="insertPlaceholder('{{ $fieldName }}', type + '.' + example)"
|
x-on:click="window.insertPlaceholder('{{ $fieldName }}', type + '.' + example)"
|
||||||
x-bind:tooltip="'{{ __('placeholder-picker.click_to_insert') }}'"
|
x-bind:tooltip="'{{ __('placeholder-picker.click_to_insert') }}'"
|
||||||
>
|
>
|
||||||
<code class="text-xs font-mono" x-text="'{' + type + '.' + example + '}'"></code>
|
<code class="text-xs font-mono" x-text="'{' + type + '.' + example + '}'"></code>
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
size="sm"
|
size="sm"
|
||||||
color="primary"
|
color="primary"
|
||||||
outlined
|
outlined
|
||||||
@click="insertPlaceholder('{{ $fieldName }}', 'menu')"
|
x-on:click="window.insertPlaceholder('{{ $fieldName }}', 'menu')"
|
||||||
>
|
>
|
||||||
<code class="text-xs font-mono">{menu}</code>
|
<code class="text-xs font-mono">{menu}</code>
|
||||||
</x-filament::button>
|
</x-filament::button>
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
size="sm"
|
size="sm"
|
||||||
color="primary"
|
color="primary"
|
||||||
outlined
|
outlined
|
||||||
@click="insertPlaceholder('{{ $fieldName }}', 'staticMenu')"
|
x-on:click="window.insertPlaceholder('{{ $fieldName }}', 'staticMenu')"
|
||||||
>
|
>
|
||||||
<code class="text-xs font-mono">{staticMenu}</code>
|
<code class="text-xs font-mono">{staticMenu}</code>
|
||||||
</x-filament::button>
|
</x-filament::button>
|
||||||
@@ -179,25 +179,37 @@
|
|||||||
// Monaco Editor instance'ını bul
|
// Monaco Editor instance'ını bul
|
||||||
const editorContainer = document.querySelector('[data-field-name=\'' + fieldName + '\']');
|
const editorContainer = document.querySelector('[data-field-name=\'' + fieldName + '\']');
|
||||||
let monacoEditorInstance = null;
|
let monacoEditorInstance = null;
|
||||||
let currentValue = '';
|
|
||||||
let cursorPosition = null;
|
|
||||||
|
|
||||||
// Monaco Editor'dan cursor pozisyonunu al
|
// Monaco Editor instance'ını bul
|
||||||
if (editorContainer && window.monaco) {
|
if (editorContainer && window.monaco && window.monaco.editor) {
|
||||||
|
// Tüm Monaco Editor instance'larını kontrol et
|
||||||
const editorInstances = window.monaco.editor.getEditors();
|
const editorInstances = window.monaco.editor.getEditors();
|
||||||
for (let editor of editorInstances) {
|
for (let editor of editorInstances) {
|
||||||
const container = editor.getContainerDomNode();
|
try {
|
||||||
if (container && container.closest && container.closest('[data-field-name=\'' + fieldName + '\']')) {
|
const container = editor.getContainerDomNode();
|
||||||
monacoEditorInstance = editor;
|
if (container) {
|
||||||
const model = editor.getModel();
|
// Container'ın parent'larını kontrol et
|
||||||
currentValue = model.getValue();
|
let parent = container;
|
||||||
const selection = editor.getSelection();
|
while (parent && parent !== document.body) {
|
||||||
if (selection) {
|
if (parent.hasAttribute && parent.hasAttribute('data-field-name') &&
|
||||||
cursorPosition = model.getOffsetAt(selection.getStartPosition());
|
parent.getAttribute('data-field-name') === fieldName) {
|
||||||
} else {
|
monacoEditorInstance = editor;
|
||||||
cursorPosition = currentValue.length;
|
break;
|
||||||
|
}
|
||||||
|
parent = parent.parentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Veya closest metodunu kullan
|
||||||
|
if (!monacoEditorInstance && container.closest) {
|
||||||
|
const closest = container.closest('[data-field-name=\'' + fieldName + '\']');
|
||||||
|
if (closest) {
|
||||||
|
monacoEditorInstance = editor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
} catch (e) {
|
||||||
|
console.debug('Monaco Editor container check failed:', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -205,93 +217,90 @@
|
|||||||
// Monaco Editor varsa, doğrudan onu kullan
|
// Monaco Editor varsa, doğrudan onu kullan
|
||||||
if (monacoEditorInstance) {
|
if (monacoEditorInstance) {
|
||||||
const model = monacoEditorInstance.getModel();
|
const model = monacoEditorInstance.getModel();
|
||||||
const newValue = currentValue.slice(0, cursorPosition) + placeholderText + currentValue.slice(cursorPosition);
|
const currentValue = model.getValue();
|
||||||
|
|
||||||
// Monaco Editor'da değeri güncelle
|
// Monaco Editor'ı focus et (önce focus et ki cursor pozisyonu doğru alınsın)
|
||||||
monacoEditorInstance.executeEdits('placeholder-insert', [{
|
|
||||||
range: new window.monaco.Range(
|
|
||||||
model.getPositionAt(cursorPosition).lineNumber,
|
|
||||||
model.getPositionAt(cursorPosition).column,
|
|
||||||
model.getPositionAt(cursorPosition).lineNumber,
|
|
||||||
model.getPositionAt(cursorPosition).column
|
|
||||||
),
|
|
||||||
text: placeholderText
|
|
||||||
}]);
|
|
||||||
|
|
||||||
// Cursor pozisyonunu güncelle
|
|
||||||
const newPosition = model.getPositionAt(cursorPosition + placeholderText.length);
|
|
||||||
monacoEditorInstance.setPosition(newPosition);
|
|
||||||
monacoEditorInstance.focus();
|
monacoEditorInstance.focus();
|
||||||
|
|
||||||
// Filament form state'ini güncellemek için Monaco Editor'ın change event'ini tetikle
|
// Kısa bir delay ile cursor pozisyonunu al (focus işleminin tamamlanması için)
|
||||||
// Filament CodeEditor otomatik olarak değişiklikleri algılar
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// Monaco Editor model değişikliğini tetikle
|
// Cursor pozisyonunu al (selection varsa onu kullan, yoksa cursor pozisyonunu)
|
||||||
// Bu, Filament'in CodeEditor component'inin otomatik olarak algılamasını sağlar
|
let selection = monacoEditorInstance.getSelection();
|
||||||
if (monacoEditorInstance) {
|
let cursorPosition;
|
||||||
// Model'in değerini tekrar set et (zaten set ettik ama event'i tetiklemek için)
|
|
||||||
const model = monacoEditorInstance.getModel();
|
if (selection && !selection.isEmpty()) {
|
||||||
|
// Seçili metin varsa, başlangıç pozisyonunu kullan
|
||||||
// Monaco Editor'ın onDidChangeContent event'ini manuel tetikle
|
cursorPosition = model.getOffsetAt(selection.getStartPosition());
|
||||||
// Filament CodeEditor bu event'i dinliyor ve form state'ini güncelliyor
|
} else {
|
||||||
model.onDidChangeContent(() => {
|
// Seçim yoksa cursor pozisyonunu kullan
|
||||||
// Bu event zaten tetiklenmiş olacak, sadece emin olmak için
|
const position = monacoEditorInstance.getPosition();
|
||||||
});
|
if (position) {
|
||||||
|
cursorPosition = model.getOffsetAt(position);
|
||||||
|
} else {
|
||||||
|
// Cursor pozisyonu alınamazsa sona ekle
|
||||||
|
cursorPosition = currentValue.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Placeholder'ı cursor pozisyonuna ekle
|
||||||
|
const position = model.getPositionAt(cursorPosition);
|
||||||
|
const range = new window.monaco.Range(
|
||||||
|
position.lineNumber,
|
||||||
|
position.column,
|
||||||
|
position.lineNumber,
|
||||||
|
position.column
|
||||||
|
);
|
||||||
|
|
||||||
|
// Edit işlemini yap
|
||||||
|
monacoEditorInstance.executeEdits('placeholder-insert', [{
|
||||||
|
range: range,
|
||||||
|
text: placeholderText
|
||||||
|
}]);
|
||||||
|
|
||||||
|
// Cursor pozisyonunu güncelle (placeholder'ın sonuna)
|
||||||
|
const newCursorPosition = model.getPositionAt(cursorPosition + placeholderText.length);
|
||||||
|
monacoEditorInstance.setPosition(newCursorPosition);
|
||||||
|
|
||||||
|
// Focus'u koru
|
||||||
|
monacoEditorInstance.focus();
|
||||||
|
|
||||||
|
// Filament form state'ini güncellemek için güncel değeri al
|
||||||
|
const updatedValue = model.getValue();
|
||||||
|
|
||||||
|
// Filament form state'ini güncelle
|
||||||
|
setTimeout(() => {
|
||||||
// Hidden input'u bul ve güncelle (eğer varsa)
|
// Hidden input'u bul ve güncelle (eğer varsa)
|
||||||
const hiddenInput = editorContainer.querySelector('input[type=\'hidden\']');
|
const hiddenInput = editorContainer.querySelector('input[type=\'hidden\']');
|
||||||
if (hiddenInput) {
|
if (hiddenInput) {
|
||||||
hiddenInput.value = newValue;
|
hiddenInput.value = updatedValue;
|
||||||
hiddenInput.dispatchEvent(new Event('input', { bubbles: true }));
|
hiddenInput.dispatchEvent(new Event('input', { bubbles: true }));
|
||||||
hiddenInput.dispatchEvent(new Event('change', { bubbles: true }));
|
hiddenInput.dispatchEvent(new Event('change', { bubbles: true }));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filament form'unu bul ve güncelle
|
// Filament form'unu bul ve güncelle
|
||||||
// Form component'ini bulmak için editor container'ın parent'larını kontrol et
|
|
||||||
let formComponent = null;
|
|
||||||
const formWireElement = editorContainer.closest('form [wire\\:id], [wire\\:id][wire\\:key*="form"]');
|
const formWireElement = editorContainer.closest('form [wire\\:id], [wire\\:id][wire\\:key*="form"]');
|
||||||
|
|
||||||
if (formWireElement && window.Livewire) {
|
if (formWireElement && window.Livewire) {
|
||||||
const wireId = formWireElement.getAttribute('wire:id');
|
const wireId = formWireElement.getAttribute('wire:id');
|
||||||
const component = window.Livewire.find(wireId);
|
const component = window.Livewire.find(wireId);
|
||||||
|
|
||||||
// Component'in form component'i olup olmadığını kontrol et
|
|
||||||
// Sadece field path'lerini kontrol et, 'data' property'sine direkt erişme
|
|
||||||
if (component && component.get) {
|
if (component && component.get) {
|
||||||
const fieldPath = 'data.' + fieldName;
|
const fieldPath = 'data.' + fieldName;
|
||||||
try {
|
try {
|
||||||
// Field path'i kontrol et
|
|
||||||
if (component.get(fieldPath) !== undefined) {
|
if (component.get(fieldPath) !== undefined) {
|
||||||
formComponent = component;
|
component.set(fieldPath, updatedValue);
|
||||||
} else if (component.get('data.html_content') !== undefined) {
|
} else if (component.get('data.html_content') !== undefined) {
|
||||||
formComponent = component;
|
component.set('data.html_content', updatedValue);
|
||||||
} else if (component.get('html_content') !== undefined) {
|
} else if (component.get('html_content') !== undefined) {
|
||||||
formComponent = component;
|
component.set('html_content', updatedValue);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Property kontrolü sırasında hata alırsak, component'i kullanma
|
console.debug('Livewire form state update skipped:', e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}, 100);
|
||||||
// Sadece form component'i bulunduysa güncelle
|
}, 50);
|
||||||
if (formComponent) {
|
|
||||||
try {
|
|
||||||
const fieldPath = 'data.' + fieldName;
|
|
||||||
if (formComponent.get(fieldPath) !== undefined) {
|
|
||||||
formComponent.set(fieldPath, newValue);
|
|
||||||
} else if (formComponent.get('data.html_content') !== undefined) {
|
|
||||||
formComponent.set('data.html_content', newValue);
|
|
||||||
} else if (formComponent.get('html_content') !== undefined) {
|
|
||||||
formComponent.set('html_content', newValue);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// Hata durumunda sadece console'a yaz, Monaco Editor zaten güncellenmiş
|
|
||||||
console.debug('Livewire form state update skipped:', e.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 150);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,6 +116,27 @@ function templatePreview(previewUrl, type, fieldName, recordIdFromView = null) {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('[Preview] Could not get ID from Livewire');
|
console.log('[Preview] Could not get ID from Livewire');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Livewire event listener ekle (Livewire 3.x için)
|
||||||
|
if (window.Livewire) {
|
||||||
|
document.addEventListener('livewire:init', () => {
|
||||||
|
Livewire.on('template-saved', () => {
|
||||||
|
this.updatePreview();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Eğer Livewire zaten yüklendiyse direkt dinle
|
||||||
|
if (window.Livewire.hook) {
|
||||||
|
Livewire.on('template-saved', () => {
|
||||||
|
this.updatePreview();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Window event listener ekle (alternatif - Livewire event'lerini de yakalar)
|
||||||
|
window.addEventListener('template-saved', () => {
|
||||||
|
this.updatePreview();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async updatePreview() {
|
async updatePreview() {
|
||||||
|
|||||||
Reference in New Issue
Block a user