253 lines
No EOL
14 KiB
PHP
253 lines
No EOL
14 KiB
PHP
<div class="space-y-6">
|
||
{{-- Header --}}
|
||
<div class="flex items-center justify-between">
|
||
<div>
|
||
<h1 class="text-2xl font-semibold text-gray-900">
|
||
{{ $componentData ? 'Edit Component' : 'Add Component' }}
|
||
</h1>
|
||
@if($componentType)
|
||
<p class="text-sm text-gray-500 mt-1">
|
||
Component Type: {{ class_basename($componentType) }}
|
||
</p>
|
||
@endif
|
||
</div>
|
||
|
||
<div class="flex items-center space-x-3">
|
||
<flux:button wire:click="$dispatch('close-modal')" variant="ghost">
|
||
Cancel
|
||
</flux:button>
|
||
<flux:button wire:click="save" variant="primary">
|
||
{{ $componentData ? 'Update' : 'Add' }} Component
|
||
</flux:button>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- Component Settings --}}
|
||
@if($componentType && $fields)
|
||
<flux:card>
|
||
<flux:card.header>
|
||
<flux:heading size="lg">Component Settings</flux:heading>
|
||
</flux:card.header>
|
||
|
||
<div class="space-y-6">
|
||
{{-- Language Tabs for Translatable Fields --}}
|
||
@if($hasTranslatableFields)
|
||
<div class="border-b border-gray-200">
|
||
<nav class="-mb-px flex space-x-8">
|
||
@foreach($availableLocales as $locale)
|
||
<button wire:click="setActiveLocale('{{ $locale }}')"
|
||
class="py-2 px-1 border-b-2 font-medium text-sm {{ $activeLocale === $locale ? 'border-blue-500 text-blue-600' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300' }}">
|
||
{{ strtoupper($locale) }}
|
||
</button>
|
||
@endforeach
|
||
</nav>
|
||
</div>
|
||
@endif
|
||
|
||
{{-- Dynamic Fields --}}
|
||
@foreach($fields as $field)
|
||
<div class="space-y-2">
|
||
@if($field->isTranslatable() && $hasTranslatableFields)
|
||
{{-- Translatable Field --}}
|
||
<flux:field>
|
||
<flux:label>{{ $field->getLabel() }}</flux:label>
|
||
@if($field->getHelpText())
|
||
<flux:description>{{ $field->getHelpText() }}</flux:description>
|
||
@endif
|
||
|
||
@php
|
||
$fieldKey = $field->getKey();
|
||
$wireModel = "content.{$fieldKey}.{$activeLocale}";
|
||
@endphp
|
||
|
||
@switch($field->getType())
|
||
@case('text')
|
||
<flux:input wire:model.live="{{ $wireModel }}"
|
||
placeholder="{{ $field->getPlaceholder() }}" />
|
||
@break
|
||
|
||
@case('textarea')
|
||
<flux:textarea wire:model.live="{{ $wireModel }}"
|
||
placeholder="{{ $field->getPlaceholder() }}"
|
||
rows="4" />
|
||
@break
|
||
|
||
@case('wysiwyg')
|
||
<div wire:ignore>
|
||
<textarea wire:model.live="{{ $wireModel }}"
|
||
class="wysiwyg-editor"
|
||
rows="10"></textarea>
|
||
</div>
|
||
@break
|
||
|
||
@case('select')
|
||
<flux:select wire:model.live="{{ $wireModel }}">
|
||
@if($field->getEmptyOption())
|
||
<flux:option value="">{{ $field->getEmptyOption() }}</flux:option>
|
||
@endif
|
||
@foreach($field->getOptions() as $value => $label)
|
||
<flux:option value="{{ $value }}">{{ $label }}</flux:option>
|
||
@endforeach
|
||
</flux:select>
|
||
@break
|
||
@endswitch
|
||
|
||
<flux:error name="{{ $wireModel }}" />
|
||
</flux:field>
|
||
@else
|
||
{{-- Non-translatable Field --}}
|
||
<flux:field>
|
||
<flux:label>{{ $field->getLabel() }}</flux:label>
|
||
@if($field->getHelpText())
|
||
<flux:description>{{ $field->getHelpText() }}</flux:description>
|
||
@endif
|
||
|
||
@php
|
||
$fieldKey = $field->getKey();
|
||
$wireModel = "content.{$fieldKey}";
|
||
@endphp
|
||
|
||
@switch($field->getType())
|
||
@case('text')
|
||
<flux:input wire:model.live="{{ $wireModel }}"
|
||
placeholder="{{ $field->getPlaceholder() }}" />
|
||
@break
|
||
|
||
@case('number')
|
||
<flux:input wire:model.live="{{ $wireModel }}"
|
||
type="number"
|
||
step="{{ $field->getStep() }}"
|
||
@if($field->getMin() !== null) min="{{ $field->getMin() }}" @endif
|
||
@if($field->getMax() !== null) max="{{ $field->getMax() }}" @endif
|
||
placeholder="{{ $field->getPlaceholder() }}" />
|
||
@break
|
||
|
||
@case('boolean')
|
||
@if($field->getDisplayType() === 'toggle')
|
||
<flux:switch wire:model.live="{{ $wireModel }}">
|
||
{{ $field->getTrueLabel() }}
|
||
</flux:switch>
|
||
@else
|
||
<flux:checkbox wire:model.live="{{ $wireModel }}">
|
||
{{ $field->getTrueLabel() }}
|
||
</flux:checkbox>
|
||
@endif
|
||
@break
|
||
|
||
@case('select')
|
||
@if($field->isMultiple())
|
||
<flux:select wire:model.live="{{ $wireModel }}" multiple>
|
||
@foreach($field->getOptions() as $value => $label)
|
||
<flux:option value="{{ $value }}">{{ $label }}</flux:option>
|
||
@endforeach
|
||
</flux:select>
|
||
@else
|
||
<flux:select wire:model.live="{{ $wireModel }}">
|
||
@if($field->getEmptyOption())
|
||
<flux:option value="">{{ $field->getEmptyOption() }}</flux:option>
|
||
@endif
|
||
@foreach($field->getOptions() as $value => $label)
|
||
<flux:option value="{{ $value }}">{{ $label }}</flux:option>
|
||
@endforeach
|
||
</flux:select>
|
||
@endif
|
||
@break
|
||
|
||
@case('media')
|
||
<div class="space-y-3">
|
||
<flux:button wire:click="openMediaPicker('{{ $fieldKey }}')" size="sm">
|
||
Select {{ $field->acceptsImages() ? 'Image' : 'File' }}
|
||
</flux:button>
|
||
|
||
@if(!empty($content[$fieldKey]))
|
||
<div class="grid grid-cols-3 gap-3">
|
||
@if($field->isMultiple())
|
||
@foreach($content[$fieldKey] as $index => $mediaId)
|
||
<div class="relative">
|
||
<img src="{{ $this->getMediaUrl($mediaId) }}"
|
||
alt="Selected media"
|
||
class="w-full h-24 object-cover rounded">
|
||
<button wire:click="removeMedia('{{ $fieldKey }}', {{ $index }})"
|
||
class="absolute -top-2 -right-2 bg-red-500 text-white rounded-full w-6 h-6 flex items-center justify-center text-xs">
|
||
×
|
||
</button>
|
||
</div>
|
||
@endforeach
|
||
@else
|
||
<div class="relative">
|
||
<img src="{{ $this->getMediaUrl($content[$fieldKey]) }}"
|
||
alt="Selected media"
|
||
class="w-full h-24 object-cover rounded">
|
||
<button wire:click="removeMedia('{{ $fieldKey }}')"
|
||
class="absolute -top-2 -right-2 bg-red-500 text-white rounded-full w-6 h-6 flex items-center justify-center text-xs">
|
||
×
|
||
</button>
|
||
</div>
|
||
@endif
|
||
</div>
|
||
@endif
|
||
</div>
|
||
@break
|
||
@endswitch
|
||
|
||
<flux:error name="{{ $wireModel }}" />
|
||
</flux:field>
|
||
@endif
|
||
</div>
|
||
@endforeach
|
||
</div>
|
||
</flux:card>
|
||
|
||
{{-- Component Visibility Settings --}}
|
||
<flux:card>
|
||
<flux:card.header>
|
||
<flux:heading size="lg">Visibility Settings</flux:heading>
|
||
</flux:card.header>
|
||
|
||
<div class="space-y-4">
|
||
<flux:field>
|
||
<flux:checkbox wire:model.live="settings.is_active">
|
||
Component is active
|
||
</flux:checkbox>
|
||
<flux:description>Inactive components will not be displayed on the frontend</flux:description>
|
||
</flux:field>
|
||
|
||
<flux:field>
|
||
<flux:label>CSS Classes</flux:label>
|
||
<flux:input wire:model.live="settings.css_classes"
|
||
placeholder="Additional CSS classes" />
|
||
<flux:description>Custom CSS classes to apply to this component</flux:description>
|
||
</flux:field>
|
||
</div>
|
||
</flux:card>
|
||
@else
|
||
<div class="text-center py-8">
|
||
<flux:icon.exclamation-triangle class="w-12 h-12 mx-auto text-gray-400 mb-4" />
|
||
<p class="text-gray-500">No component type selected or fields defined.</p>
|
||
</div>
|
||
@endif
|
||
|
||
{{-- Media Picker Modal --}}
|
||
<flux:modal name="media-picker" class="md:w-4xl">
|
||
<div class="space-y-6">
|
||
<flux:heading size="lg">Select Media</flux:heading>
|
||
|
||
<div class="grid grid-cols-4 gap-4 max-h-96 overflow-y-auto">
|
||
{{-- Media items would be loaded here --}}
|
||
<div class="text-center py-8 col-span-4 text-gray-500">
|
||
Media picker implementation needed
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</flux:modal>
|
||
</div>
|
||
|
||
@push('scripts')
|
||
<script>
|
||
// Initialize WYSIWYG editors
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// Initialize your preferred WYSIWYG editor here
|
||
// Example: CKEditor, TinyMCE, Quill, etc.
|
||
});
|
||
</script>
|
||
@endpush |