libra/resources/views/livewire/admin/pages/edit.blade.php

273 lines
9.6 KiB
PHP

<?php
use App\Models\AdminLog;
use App\Models\Page;
use Livewire\Volt\Component;
new class extends Component
{
public Page $page;
public string $activeTab = 'ar';
public string $content_ar = '';
public string $content_en = '';
public bool $showPreview = false;
public function mount(string $slug): void
{
$this->page = Page::query()->where('slug', $slug)->firstOrFail();
$this->content_ar = $this->page->content_ar ?? '';
$this->content_en = $this->page->content_en ?? '';
}
public function setTab(string $tab): void
{
$this->activeTab = $tab;
}
public function save(): void
{
$oldValues = $this->page->toArray();
$this->page->update([
'content_ar' => clean($this->content_ar),
'content_en' => clean($this->content_en),
]);
AdminLog::create([
'admin_id' => auth()->id(),
'action' => 'update',
'target_type' => 'page',
'target_id' => $this->page->id,
'old_values' => $oldValues,
'new_values' => $this->page->fresh()->toArray(),
'ip_address' => request()->ip(),
'created_at' => now(),
]);
session()->flash('success', __('pages.page_saved'));
}
public function togglePreview(): void
{
$this->showPreview = ! $this->showPreview;
}
public function closePreview(): void
{
$this->showPreview = false;
}
}; ?>
<div>
<div class="mb-6">
<flux:button variant="ghost" :href="route('admin.pages.index')" wire:navigate icon="arrow-left">
{{ __('pages.back_to_pages') }}
</flux:button>
</div>
<div class="mb-6 flex flex-col items-start justify-between gap-4 sm:flex-row sm:items-center">
<div>
<flux:heading size="xl">{{ __('pages.edit_page') }}: {{ $page->title_en }}</flux:heading>
<p class="mt-1 text-sm text-zinc-500 dark:text-zinc-400">
{{ __('pages.last_updated') }}: {{ $page->updated_at->diffForHumans() }}
</p>
</div>
</div>
@if (session('success'))
<flux:callout variant="success" class="mb-6">
{{ session('success') }}
</flux:callout>
@endif
<div class="rounded-lg border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
<!-- Language Tabs -->
<div class="mb-6 border-b border-zinc-200 dark:border-zinc-700">
<nav class="-mb-px flex gap-4">
<button
wire:click="setTab('ar')"
class="{{ $activeTab === 'ar' ? 'border-blue-500 text-blue-600 dark:text-blue-400' : 'border-transparent text-zinc-500 hover:border-zinc-300 hover:text-zinc-700 dark:text-zinc-400' }} border-b-2 px-1 pb-3 text-sm font-medium transition-colors"
>
{{ __('pages.arabic') }}
</button>
<button
wire:click="setTab('en')"
class="{{ $activeTab === 'en' ? 'border-blue-500 text-blue-600 dark:text-blue-400' : 'border-transparent text-zinc-500 hover:border-zinc-300 hover:text-zinc-700 dark:text-zinc-400' }} border-b-2 px-1 pb-3 text-sm font-medium transition-colors"
>
{{ __('pages.english') }}
</button>
</nav>
</div>
<form wire:submit="save" class="space-y-6">
<!-- Arabic Content -->
<div x-show="$wire.activeTab === 'ar'" x-cloak>
<flux:field>
<flux:label>{{ __('pages.content') }} ({{ __('pages.arabic') }})</flux:label>
<div wire:ignore>
<div
id="editor-ar"
class="min-h-[300px] rounded-lg border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900"
dir="rtl"
></div>
<textarea id="content_ar" wire:model="content_ar" class="hidden">{{ $content_ar }}</textarea>
</div>
</flux:field>
</div>
<!-- English Content -->
<div x-show="$wire.activeTab === 'en'" x-cloak>
<flux:field>
<flux:label>{{ __('pages.content') }} ({{ __('pages.english') }})</flux:label>
<div wire:ignore>
<div
id="editor-en"
class="min-h-[300px] rounded-lg border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900"
></div>
<textarea id="content_en" wire:model="content_en" class="hidden">{{ $content_en }}</textarea>
</div>
</flux:field>
</div>
<div class="flex items-center justify-end gap-4 border-t border-zinc-200 pt-6 dark:border-zinc-700">
<flux:button variant="ghost" :href="route('admin.pages.index')" wire:navigate>
{{ __('common.cancel') }}
</flux:button>
<flux:button type="button" wire:click="togglePreview">
{{ __('pages.preview') }}
</flux:button>
<flux:button variant="primary" type="submit">
{{ __('pages.save_publish') }}
</flux:button>
</div>
</form>
</div>
{{-- Preview Modal --}}
<flux:modal wire:model="showPreview" class="max-w-4xl">
<div class="p-6">
<flux:heading size="lg" class="mb-4">{{ __('pages.preview') }}</flux:heading>
<div class="space-y-6">
<div class="border-b border-zinc-200 pb-4 dark:border-zinc-700">
<h3 class="mb-2 text-lg font-semibold text-zinc-600 dark:text-zinc-400">{{ __('pages.arabic') }}</h3>
<h2 class="text-xl font-bold text-zinc-900 dark:text-zinc-100" dir="rtl">{{ $page->title_ar }}</h2>
<div class="prose prose-sm mt-2 max-w-none dark:prose-invert" dir="rtl">{!! clean($content_ar) !!}</div>
</div>
<div>
<h3 class="mb-2 text-lg font-semibold text-zinc-600 dark:text-zinc-400">{{ __('pages.english') }}</h3>
<h2 class="text-xl font-bold text-zinc-900 dark:text-zinc-100">{{ $page->title_en }}</h2>
<div class="prose prose-sm mt-2 max-w-none dark:prose-invert">{!! clean($content_en) !!}</div>
</div>
</div>
<div class="mt-6 flex justify-end">
<flux:button wire:click="closePreview">{{ __('common.close') }}</flux:button>
</div>
</div>
</flux:modal>
</div>
@push('styles')
<link href="https://cdn.jsdelivr.net/npm/quill@2.0.3/dist/quill.snow.css" rel="stylesheet" />
<style>
.ql-editor {
min-height: 250px;
font-size: 16px;
}
.ql-container {
font-family: inherit;
}
.dark .ql-toolbar {
border-color: rgb(63 63 70);
background: rgb(24 24 27);
}
.dark .ql-container {
border-color: rgb(63 63 70);
background: rgb(24 24 27);
}
.dark .ql-editor {
color: rgb(244 244 245);
}
.dark .ql-stroke {
stroke: rgb(161 161 170);
}
.dark .ql-fill {
fill: rgb(161 161 170);
}
.dark .ql-picker-label {
color: rgb(161 161 170);
}
.dark .ql-picker-options {
background: rgb(39 39 42);
border-color: rgb(63 63 70);
}
.dark .ql-picker-item {
color: rgb(244 244 245);
}
.dark .ql-picker-item:hover {
color: rgb(59 130 246);
}
</style>
@endpush
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/quill@2.0.3/dist/quill.js"></script>
<script>
document.addEventListener('livewire:navigated', initQuillEditors)
document.addEventListener('DOMContentLoaded', initQuillEditors)
function initQuillEditors() {
if (document.querySelector('#editor-ar') && !document.querySelector('#editor-ar .ql-editor')) {
const toolbarOptions = [
[{ header: [1, 2, 3, false] }],
['bold', 'italic', 'underline'],
[{ list: 'ordered' }, { list: 'bullet' }],
['link'],
['clean'],
]
// Arabic Editor
const quillAr = new Quill('#editor-ar', {
theme: 'snow',
modules: {
toolbar: toolbarOptions,
},
placeholder: '{{ __('pages.enter_content_ar') }}',
})
quillAr.root.innerHTML = document.getElementById('content_ar').value
quillAr.on('text-change', function () {
@this.set('content_ar', quillAr.root.innerHTML)
})
// English Editor
const quillEn = new Quill('#editor-en', {
theme: 'snow',
modules: {
toolbar: toolbarOptions,
},
placeholder: '{{ __('pages.enter_content_en') }}',
})
quillEn.root.innerHTML = document.getElementById('content_en').value
quillEn.on('text-change', function () {
@this.set('content_en', quillEn.root.innerHTML)
})
}
}
</script>
@endpush