720 lines
38 KiB
PHP
720 lines
38 KiB
PHP
<?php
|
|
|
|
use App\Models\CmsProject;
|
|
use Flux\Flux;
|
|
use Illuminate\Support\Str;
|
|
use function Livewire\Volt\{layout, title, state, computed, on};
|
|
|
|
layout('components.layouts.app');
|
|
title('Projekte verwalten');
|
|
|
|
state([
|
|
'search' => '',
|
|
'showForm' => false,
|
|
'editingId' => null,
|
|
'editLocale' => 'de',
|
|
'activeTab' => 'basic',
|
|
|
|
'slug' => '',
|
|
'projectTitle' => '',
|
|
'location' => '',
|
|
'status' => '',
|
|
'launch_date' => '',
|
|
'price_from_aed' => '',
|
|
'currency' => 'AED',
|
|
'image' => '',
|
|
'is_published' => true,
|
|
'order' => 0,
|
|
|
|
'highlights' => [''],
|
|
'quick_facts' => [['icon' => 'home-modern', 'label' => '', 'value' => '']],
|
|
'investTitle' => '',
|
|
'investText' => '',
|
|
'investViews' => [''],
|
|
'galleryItems' => [''],
|
|
'locTitle' => '',
|
|
'locMapUrl' => '',
|
|
'locPoints' => [''],
|
|
'contactTitle' => '',
|
|
'contactSubtitle' => '',
|
|
'contactOptions' => [['key' => '', 'value' => '']],
|
|
|
|
'trustTitle' => '',
|
|
'trustIntro' => '',
|
|
'trustColumns' => [
|
|
['icon' => 'heroicon-o-lock-closed', 'title' => '', 'text' => ''],
|
|
['icon' => 'heroicon-o-building-library', 'title' => '', 'text' => ''],
|
|
['icon' => 'heroicon-o-chart-bar', 'title' => '', 'text' => ''],
|
|
],
|
|
'trustCtaUrl' => '',
|
|
'trustCtaLabel' => '',
|
|
'furnitureTitle' => '',
|
|
'furnitureText' => '',
|
|
'furnitureButtonText' => '',
|
|
'furnitureButtonLink' => '',
|
|
]);
|
|
|
|
on(['media-selected' => function ($mediaId, $url, $field) {
|
|
$media = $mediaId ? \FluxCms\Core\Models\CmsMedia::find($mediaId) : null;
|
|
if (! $media) {
|
|
return;
|
|
}
|
|
if ($field === 'project_image') {
|
|
$this->image = $media->filename;
|
|
}
|
|
if (str_starts_with($field, 'gallery_')) {
|
|
$idx = (int) str_replace('gallery_', '', $field);
|
|
if (isset($this->galleryItems[$idx])) {
|
|
$this->galleryItems[$idx] = $media->filename;
|
|
}
|
|
}
|
|
}]);
|
|
|
|
$projects = computed(
|
|
fn () => CmsProject::query()
|
|
->when($this->search, fn ($q) => $q->where('slug', 'like', "%{$this->search}%"))
|
|
->ordered()
|
|
->get(),
|
|
);
|
|
|
|
$resetForm = function () {
|
|
$this->editingId = null;
|
|
$this->activeTab = 'basic';
|
|
$this->slug = '';
|
|
$this->projectTitle = '';
|
|
$this->location = '';
|
|
$this->status = '';
|
|
$this->launch_date = '';
|
|
$this->price_from_aed = '';
|
|
$this->currency = 'AED';
|
|
$this->image = '';
|
|
$this->is_published = true;
|
|
$this->order = 0;
|
|
$this->highlights = [''];
|
|
$this->quick_facts = [['icon' => 'home-modern', 'label' => '', 'value' => '']];
|
|
$this->investTitle = '';
|
|
$this->investText = '';
|
|
$this->investViews = [''];
|
|
$this->galleryItems = [''];
|
|
$this->locTitle = '';
|
|
$this->locMapUrl = '';
|
|
$this->locPoints = [''];
|
|
$this->contactTitle = '';
|
|
$this->contactSubtitle = '';
|
|
$this->contactOptions = [['key' => '', 'value' => '']];
|
|
$this->trustTitle = '';
|
|
$this->trustIntro = '';
|
|
$this->trustColumns = [
|
|
['icon' => 'heroicon-o-lock-closed', 'title' => '', 'text' => ''],
|
|
['icon' => 'heroicon-o-building-library', 'title' => '', 'text' => ''],
|
|
['icon' => 'heroicon-o-chart-bar', 'title' => '', 'text' => ''],
|
|
];
|
|
$this->trustCtaUrl = '';
|
|
$this->trustCtaLabel = '';
|
|
$this->furnitureTitle = '';
|
|
$this->furnitureText = '';
|
|
$this->furnitureButtonText = '';
|
|
$this->furnitureButtonLink = '';
|
|
};
|
|
|
|
$loadProjectIntoForm = function (CmsProject $project, string $locale) {
|
|
$this->slug = $project->slug;
|
|
$this->image = $project->image ?? '';
|
|
$this->status = $project->status ?? '';
|
|
$this->launch_date = $project->launch_date?->format('Y-m-d') ?? '';
|
|
$this->price_from_aed = $project->price_from_aed ?? '';
|
|
$this->currency = $project->currency ?? 'AED';
|
|
$this->is_published = $project->is_published;
|
|
$this->order = $project->order ?? 0;
|
|
|
|
$this->projectTitle = $project->getTranslation('title', $locale) ?? '';
|
|
$this->location = $project->getTranslation('location', $locale) ?? '';
|
|
|
|
$hl = $project->getTranslation('highlights', $locale);
|
|
$this->highlights = is_array($hl) && count($hl) > 0 ? $hl : [''];
|
|
|
|
$this->quick_facts = is_array($project->quick_facts) && count($project->quick_facts) > 0
|
|
? $project->quick_facts
|
|
: [['icon' => 'home-modern', 'label' => '', 'value' => '']];
|
|
|
|
$ic = $project->getTranslation('investment_case', $locale);
|
|
$this->investTitle = $ic['title'] ?? '';
|
|
$this->investText = $ic['text'] ?? '';
|
|
$this->investViews = is_array($ic['views'] ?? null) && count($ic['views']) > 0 ? $ic['views'] : [''];
|
|
|
|
$this->galleryItems = is_array($project->gallery) && count($project->gallery) > 0
|
|
? $project->gallery
|
|
: [''];
|
|
|
|
$li = $project->getTranslation('location_info', $locale);
|
|
$this->locTitle = $li['title'] ?? '';
|
|
$this->locMapUrl = $li['map_url'] ?? '';
|
|
$this->locPoints = is_array($li['points'] ?? null) && count($li['points']) > 0 ? $li['points'] : [''];
|
|
|
|
$ct = $project->getTranslation('contact', $locale);
|
|
$this->contactTitle = $ct['title'] ?? '';
|
|
$this->contactSubtitle = $ct['subtitle'] ?? '';
|
|
$opts = $ct['options'] ?? [];
|
|
$this->contactOptions = count($opts) > 0
|
|
? collect($opts)->map(fn ($v, $k) => ['key' => $k, 'value' => $v])->values()->toArray()
|
|
: [['key' => '', 'value' => '']];
|
|
|
|
$it = $project->getTranslation('investor_trust', $locale) ?? [];
|
|
$this->trustTitle = $it['title'] ?? '';
|
|
$this->trustIntro = $it['intro'] ?? '';
|
|
$trustCols = $it['columns'] ?? [];
|
|
$this->trustColumns = is_array($trustCols) && count($trustCols) > 0
|
|
? $trustCols
|
|
: [
|
|
['icon' => 'heroicon-o-lock-closed', 'title' => '', 'text' => ''],
|
|
['icon' => 'heroicon-o-building-library', 'title' => '', 'text' => ''],
|
|
['icon' => 'heroicon-o-chart-bar', 'title' => '', 'text' => ''],
|
|
];
|
|
$this->trustCtaUrl = $it['cta_url'] ?? '';
|
|
$this->trustCtaLabel = $it['cta_label'] ?? '';
|
|
|
|
$fb = $project->getTranslation('furniture_benefit', $locale) ?? [];
|
|
$this->furnitureTitle = $fb['title'] ?? '';
|
|
$this->furnitureText = $fb['text'] ?? '';
|
|
$this->furnitureButtonText = $fb['button_text'] ?? '';
|
|
$this->furnitureButtonLink = $fb['button_link'] ?? '';
|
|
};
|
|
|
|
$openCreate = function () {
|
|
$this->resetForm();
|
|
$this->showForm = true;
|
|
};
|
|
|
|
$openEdit = function (int $id) {
|
|
$project = CmsProject::find($id);
|
|
if (! $project) {
|
|
return;
|
|
}
|
|
$this->editingId = $id;
|
|
$this->activeTab = 'basic';
|
|
$this->loadProjectIntoForm($project, $this->editLocale);
|
|
$this->showForm = true;
|
|
};
|
|
|
|
$duplicateProject = function (int $id) {
|
|
$project = CmsProject::find($id);
|
|
if (! $project) {
|
|
return;
|
|
}
|
|
$this->editingId = null;
|
|
$this->activeTab = 'basic';
|
|
$this->loadProjectIntoForm($project, $this->editLocale);
|
|
$this->slug = $project->slug . '-kopie-' . Str::random(4);
|
|
$this->showForm = true;
|
|
Flux::toast(heading: 'Dupliziert', text: 'Projekt wurde als Kopie geladen. Bitte Slug anpassen und speichern.');
|
|
};
|
|
|
|
$switchLocale = function (string $locale) {
|
|
$this->editLocale = $locale;
|
|
if ($this->editingId) {
|
|
$project = CmsProject::find($this->editingId);
|
|
if ($project) {
|
|
$this->projectTitle = $project->getTranslation('title', $locale) ?? '';
|
|
$this->location = $project->getTranslation('location', $locale) ?? '';
|
|
|
|
$hl = $project->getTranslation('highlights', $locale);
|
|
$this->highlights = is_array($hl) && count($hl) > 0 ? $hl : [''];
|
|
|
|
$ic = $project->getTranslation('investment_case', $locale);
|
|
$this->investTitle = $ic['title'] ?? '';
|
|
$this->investText = $ic['text'] ?? '';
|
|
$this->investViews = is_array($ic['views'] ?? null) && count($ic['views']) > 0 ? $ic['views'] : [''];
|
|
|
|
$li = $project->getTranslation('location_info', $locale);
|
|
$this->locTitle = $li['title'] ?? '';
|
|
$this->locMapUrl = $li['map_url'] ?? '';
|
|
$this->locPoints = is_array($li['points'] ?? null) && count($li['points']) > 0 ? $li['points'] : [''];
|
|
|
|
$ct = $project->getTranslation('contact', $locale);
|
|
$this->contactTitle = $ct['title'] ?? '';
|
|
$this->contactSubtitle = $ct['subtitle'] ?? '';
|
|
$opts = $ct['options'] ?? [];
|
|
$this->contactOptions = count($opts) > 0
|
|
? collect($opts)->map(fn ($v, $k) => ['key' => $k, 'value' => $v])->values()->toArray()
|
|
: [['key' => '', 'value' => '']];
|
|
|
|
$it = $project->getTranslation('investor_trust', $locale) ?? [];
|
|
$this->trustTitle = $it['title'] ?? '';
|
|
$this->trustIntro = $it['intro'] ?? '';
|
|
$trustCols = $it['columns'] ?? [];
|
|
$this->trustColumns = is_array($trustCols) && count($trustCols) > 0
|
|
? $trustCols
|
|
: [
|
|
['icon' => 'heroicon-o-lock-closed', 'title' => '', 'text' => ''],
|
|
['icon' => 'heroicon-o-building-library', 'title' => '', 'text' => ''],
|
|
['icon' => 'heroicon-o-chart-bar', 'title' => '', 'text' => ''],
|
|
];
|
|
$this->trustCtaUrl = $it['cta_url'] ?? '';
|
|
$this->trustCtaLabel = $it['cta_label'] ?? '';
|
|
|
|
$fb = $project->getTranslation('furniture_benefit', $locale) ?? [];
|
|
$this->furnitureTitle = $fb['title'] ?? '';
|
|
$this->furnitureText = $fb['text'] ?? '';
|
|
$this->furnitureButtonText = $fb['button_text'] ?? '';
|
|
$this->furnitureButtonLink = $fb['button_link'] ?? '';
|
|
}
|
|
}
|
|
};
|
|
|
|
$addHighlight = fn () => $this->highlights[] = '';
|
|
$removeHighlight = function (int $i) { unset($this->highlights[$i]); $this->highlights = array_values($this->highlights); };
|
|
|
|
$addQuickFact = fn () => $this->quick_facts[] = ['icon' => 'home-modern', 'label' => '', 'value' => ''];
|
|
$removeQuickFact = function (int $i) { unset($this->quick_facts[$i]); $this->quick_facts = array_values($this->quick_facts); };
|
|
|
|
$addInvestView = fn () => $this->investViews[] = '';
|
|
$removeInvestView = function (int $i) { unset($this->investViews[$i]); $this->investViews = array_values($this->investViews); };
|
|
|
|
$addGalleryItem = fn () => $this->galleryItems[] = '';
|
|
$removeGalleryItem = function (int $i) { unset($this->galleryItems[$i]); $this->galleryItems = array_values($this->galleryItems); };
|
|
|
|
$addLocPoint = fn () => $this->locPoints[] = '';
|
|
$removeLocPoint = function (int $i) { unset($this->locPoints[$i]); $this->locPoints = array_values($this->locPoints); };
|
|
|
|
$addContactOption = fn () => $this->contactOptions[] = ['key' => '', 'value' => ''];
|
|
$removeContactOption = function (int $i) { unset($this->contactOptions[$i]); $this->contactOptions = array_values($this->contactOptions); };
|
|
|
|
$addTrustColumn = fn () => $this->trustColumns[] = ['icon' => 'heroicon-o-lock-closed', 'title' => '', 'text' => ''];
|
|
$removeTrustColumn = function (int $i) { unset($this->trustColumns[$i]); $this->trustColumns = array_values($this->trustColumns); };
|
|
|
|
$save = function () {
|
|
$validated = validator([
|
|
'slug' => $this->slug,
|
|
'projectTitle' => $this->projectTitle,
|
|
'location' => $this->location,
|
|
'status' => $this->status,
|
|
'launch_date' => $this->launch_date,
|
|
'price_from_aed' => $this->price_from_aed,
|
|
'currency' => $this->currency,
|
|
'image' => $this->image,
|
|
'is_published' => $this->is_published,
|
|
'order' => $this->order,
|
|
], [
|
|
'slug' => 'required|string|max:255',
|
|
'projectTitle' => 'required|string|max:500',
|
|
'location' => 'nullable|string|max:500',
|
|
'status' => 'nullable|string|max:100',
|
|
'launch_date' => 'nullable|date',
|
|
'price_from_aed' => 'nullable|integer|min:0',
|
|
'currency' => 'nullable|string|max:10',
|
|
'image' => 'nullable|string|max:500',
|
|
'is_published' => 'boolean',
|
|
'order' => 'integer|min:0',
|
|
])->validate();
|
|
|
|
$project = $this->editingId
|
|
? CmsProject::findOrFail($this->editingId)
|
|
: CmsProject::query()->make();
|
|
|
|
$project->slug = $validated['slug'];
|
|
$project->setTranslation('title', $this->editLocale, $validated['projectTitle']);
|
|
$project->setTranslation('location', $this->editLocale, $validated['location'] ?? '');
|
|
$project->status = $validated['status'] ?? null;
|
|
$project->launch_date = $validated['launch_date'] ?: null;
|
|
$project->price_from_aed = $validated['price_from_aed'] ?: null;
|
|
$project->currency = $validated['currency'] ?? 'AED';
|
|
$project->image = $validated['image'] ?? null;
|
|
$project->is_published = $validated['is_published'];
|
|
$project->order = $validated['order'];
|
|
|
|
$project->setTranslation('highlights', $this->editLocale, array_values(array_filter($this->highlights, fn ($h) => trim($h) !== '')));
|
|
|
|
$project->quick_facts = collect($this->quick_facts)
|
|
->filter(fn ($f) => ! empty($f['label']) || ! empty($f['value']))
|
|
->values()
|
|
->toArray();
|
|
|
|
$project->setTranslation('investment_case', $this->editLocale, [
|
|
'title' => $this->investTitle,
|
|
'text' => $this->investText,
|
|
'views' => array_values(array_filter($this->investViews, fn ($v) => trim($v) !== '')),
|
|
]);
|
|
|
|
$project->gallery = array_values(array_filter($this->galleryItems, fn ($g) => trim($g) !== ''));
|
|
|
|
$project->setTranslation('location_info', $this->editLocale, [
|
|
'title' => $this->locTitle,
|
|
'map_url' => $this->locMapUrl,
|
|
'points' => array_values(array_filter($this->locPoints, fn ($p) => trim($p) !== '')),
|
|
]);
|
|
|
|
$opts = [];
|
|
foreach ($this->contactOptions as $opt) {
|
|
if ($opt['value'] !== '') {
|
|
$opts[$opt['key']] = $opt['value'];
|
|
}
|
|
}
|
|
$project->setTranslation('contact', $this->editLocale, [
|
|
'title' => $this->contactTitle,
|
|
'subtitle' => $this->contactSubtitle,
|
|
'options' => $opts,
|
|
]);
|
|
|
|
$trustCols = collect($this->trustColumns)
|
|
->filter(fn ($c) => ! empty(trim($c['title'] ?? '')) || ! empty(trim($c['text'] ?? '')))
|
|
->values()
|
|
->toArray();
|
|
|
|
$project->setTranslation('investor_trust', $this->editLocale, [
|
|
'title' => $this->trustTitle,
|
|
'intro' => $this->trustIntro,
|
|
'columns' => $trustCols,
|
|
'cta_url' => $this->trustCtaUrl,
|
|
'cta_label' => $this->trustCtaLabel,
|
|
]);
|
|
|
|
$project->setTranslation('furniture_benefit', $this->editLocale, [
|
|
'title' => $this->furnitureTitle,
|
|
'text' => $this->furnitureText,
|
|
'button_text' => $this->furnitureButtonText,
|
|
'button_link' => $this->furnitureButtonLink,
|
|
]);
|
|
|
|
$project->save();
|
|
|
|
$this->showForm = false;
|
|
$this->editingId = null;
|
|
|
|
Flux::toast(variant: 'success', heading: 'Gespeichert', text: $project->slug . ' wurde erfolgreich gespeichert.');
|
|
};
|
|
|
|
$deleteProject = function (int $id) {
|
|
$project = CmsProject::find($id);
|
|
if (! $project) {
|
|
return;
|
|
}
|
|
$slug = $project->slug;
|
|
$project->delete();
|
|
Flux::toast(variant: 'success', heading: 'Gelöscht', text: "{$slug} wurde entfernt.");
|
|
};
|
|
|
|
$cancelForm = function () {
|
|
$this->showForm = false;
|
|
$this->editingId = null;
|
|
};
|
|
|
|
$setActiveTab = function (string $tab): void {
|
|
$this->activeTab = $tab;
|
|
};
|
|
|
|
?>
|
|
|
|
<div>
|
|
<div class="mb-6 flex items-center justify-between">
|
|
<flux:heading size="xl">Projekte (Immobilien)</flux:heading>
|
|
<flux:button variant="primary" icon="plus" wire:click="openCreate">Neues Projekt</flux:button>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<flux:input wire:model.live.debounce.300ms="search" placeholder="Projekt suchen..." icon="magnifying-glass"
|
|
size="sm" class="w-64" />
|
|
</div>
|
|
|
|
@if ($showForm)
|
|
<flux:card class="mb-6">
|
|
<div class="mb-4 flex items-center justify-between">
|
|
<flux:heading size="lg">{{ $editingId ? 'Projekt bearbeiten' : 'Neues Projekt' }}</flux:heading>
|
|
<div class="flex gap-1">
|
|
@foreach (config('flux-cms.locales', ['de' => 'DE', 'en' => 'EN']) as $code => $label)
|
|
<flux:button size="xs" :variant="$editLocale === $code ? 'primary' : 'ghost'"
|
|
wire:click="switchLocale('{{ $code }}')">{{ strtoupper($code) }}</flux:button>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Explizite Tab-Steuerung: zuverlässiger als nur wire:model auf ui-tabs (Livewire-Sync) --}}
|
|
<div class="mb-4 flex flex-wrap gap-1 rounded-lg bg-zinc-800/5 p-1 dark:bg-white/10">
|
|
<flux:button size="sm" type="button" :variant="$activeTab === 'basic' ? 'primary' : 'ghost'" icon="information-circle" wire:click="setActiveTab('basic')">Grunddaten</flux:button>
|
|
<flux:button size="sm" type="button" :variant="$activeTab === 'facts' ? 'primary' : 'ghost'" icon="list-bullet" wire:click="setActiveTab('facts')">Quick Facts</flux:button>
|
|
<flux:button size="sm" type="button" :variant="$activeTab === 'invest' ? 'primary' : 'ghost'" icon="chart-bar" wire:click="setActiveTab('invest')">Investment Case</flux:button>
|
|
<flux:button size="sm" type="button" :variant="$activeTab === 'gallery' ? 'primary' : 'ghost'" icon="photo" wire:click="setActiveTab('gallery')">Galerie</flux:button>
|
|
<flux:button size="sm" type="button" :variant="$activeTab === 'location' ? 'primary' : 'ghost'" icon="map-pin" wire:click="setActiveTab('location')">Location</flux:button>
|
|
<flux:button size="sm" type="button" :variant="$activeTab === 'contact' ? 'primary' : 'ghost'" icon="envelope" wire:click="setActiveTab('contact')">Kontakt</flux:button>
|
|
<flux:button size="sm" type="button" :variant="$activeTab === 'trust_synergy' ? 'primary' : 'ghost'" icon="shield-check" wire:click="setActiveTab('trust_synergy')">Trust & Möbel</flux:button>
|
|
</div>
|
|
|
|
<div class="mt-4" wire:key="project-form-panel-{{ $activeTab }}">
|
|
{{-- TAB: Grunddaten --}}
|
|
@if ($activeTab === 'basic')
|
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
|
<flux:input wire:model="slug" label="Slug" placeholder="azizi-creek-views-4" />
|
|
<flux:input wire:model="projectTitle" label="Titel ({{ strtoupper($editLocale) }})" placeholder="Projektname" />
|
|
<flux:input wire:model="location" label="Standort ({{ strtoupper($editLocale) }})" placeholder="Dubai, UAE" />
|
|
<flux:input wire:model="status" label="Status" placeholder="z.B. NEW LAUNCH" />
|
|
<flux:input wire:model="launch_date" label="Launch-Datum" type="date" />
|
|
<flux:input wire:model="price_from_aed" label="Preis ab (AED)" type="number" placeholder="0" />
|
|
<flux:input wire:model="currency" label="Währung" placeholder="AED" />
|
|
<flux:input wire:model="order" label="Sortierung" type="number" />
|
|
|
|
<div class="md:col-span-2">
|
|
<label class="mb-1 block text-sm font-medium text-zinc-700 dark:text-zinc-300">Hauptbild</label>
|
|
<div class="flex items-center gap-3">
|
|
@if ($image)
|
|
<div class="h-16 w-24 shrink-0 overflow-hidden rounded-lg border border-zinc-200 bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-800">
|
|
<img src="{{ media_url($image) }}" class="h-full w-full object-cover" />
|
|
</div>
|
|
@endif
|
|
<div class="flex-1">
|
|
<livewire:admin.cms.media-picker :value="null" field="project_image" type="image" profile="card" label="Projektbild wählen" :key="'proj-img-' . ($editingId ?? 'new')" />
|
|
</div>
|
|
</div>
|
|
<flux:input wire:model="image" size="sm" class="mt-2" placeholder="Oder Dateiname manuell..." />
|
|
</div>
|
|
|
|
<div class="md:col-span-2">
|
|
<flux:heading size="sm" class="mb-2">Highlights ({{ strtoupper($editLocale) }})</flux:heading>
|
|
<div class="space-y-2">
|
|
@foreach ($highlights as $i => $hl)
|
|
<div wire:key="hl-{{ $i }}" class="flex items-center gap-2">
|
|
<flux:input wire:model="highlights.{{ $i }}" placeholder="Highlight-Text" class="flex-1" size="sm" />
|
|
@if (count($highlights) > 1)
|
|
<flux:button size="xs" variant="ghost" icon="x-mark" wire:click="removeHighlight({{ $i }})" />
|
|
@endif
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
<flux:button size="xs" variant="ghost" icon="plus" wire:click="addHighlight" class="mt-2">Highlight</flux:button>
|
|
</div>
|
|
|
|
<div class="md:col-span-2">
|
|
<flux:switch wire:model="is_published" label="Veröffentlicht" />
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- TAB: Quick Facts --}}
|
|
@if ($activeTab === 'facts')
|
|
<div class="space-y-3">
|
|
@foreach ($quick_facts as $i => $fact)
|
|
<div wire:key="qf-{{ $i }}" class="flex items-end gap-2 rounded-lg border border-zinc-200 p-3 dark:border-zinc-700">
|
|
<flux:select wire:model="quick_facts.{{ $i }}.icon" label="Icon" class="w-44">
|
|
<flux:select.option value="home-modern">Haus</flux:select.option>
|
|
<flux:select.option value="squares-2x2">Fläche</flux:select.option>
|
|
<flux:select.option value="building-office-2">Gebäude</flux:select.option>
|
|
<flux:select.option value="user">Person</flux:select.option>
|
|
<flux:select.option value="currency-dollar">Preis</flux:select.option>
|
|
<flux:select.option value="calendar">Kalender</flux:select.option>
|
|
<flux:select.option value="map-pin">Standort</flux:select.option>
|
|
</flux:select>
|
|
<flux:input wire:model="quick_facts.{{ $i }}.label" label="Label" placeholder="Typen" class="flex-1" />
|
|
<flux:input wire:model="quick_facts.{{ $i }}.value" label="Wert" placeholder="1BR & 3BR" class="flex-1" />
|
|
@if (count($quick_facts) > 1)
|
|
<flux:button size="sm" variant="ghost" icon="trash" wire:click="removeQuickFact({{ $i }})" />
|
|
@endif
|
|
</div>
|
|
@endforeach
|
|
<flux:button size="xs" variant="ghost" icon="plus" wire:click="addQuickFact">Quick Fact</flux:button>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- TAB: Investment Case --}}
|
|
@if ($activeTab === 'invest')
|
|
<div class="space-y-4">
|
|
<flux:input wire:model="investTitle" label="Überschrift ({{ strtoupper($editLocale) }})" placeholder="Starkes Investment, hohe Nachfrage." />
|
|
<flux:textarea wire:model="investText" label="Text ({{ strtoupper($editLocale) }})" rows="4" placeholder="Beschreibung des Investment Case..." />
|
|
|
|
<div>
|
|
<label class="mb-1 block text-sm font-medium text-zinc-700 dark:text-zinc-300">Verfügbare Views</label>
|
|
<div class="space-y-2">
|
|
@foreach ($investViews as $i => $view)
|
|
<div wire:key="iv-{{ $i }}" class="flex items-center gap-2">
|
|
<flux:input wire:model="investViews.{{ $i }}" placeholder="z.B. Road View" class="flex-1" size="sm" />
|
|
@if (count($investViews) > 1)
|
|
<flux:button size="xs" variant="ghost" icon="x-mark" wire:click="removeInvestView({{ $i }})" />
|
|
@endif
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
<flux:button size="xs" variant="ghost" icon="plus" wire:click="addInvestView" class="mt-2">View</flux:button>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- TAB: Galerie --}}
|
|
@if ($activeTab === 'gallery')
|
|
<div class="space-y-3">
|
|
@foreach ($galleryItems as $i => $img)
|
|
<div wire:key="gal-{{ $i }}" class="flex items-center gap-3 rounded-lg border border-zinc-200 p-3 dark:border-zinc-700">
|
|
@if ($img)
|
|
<div class="h-14 w-20 shrink-0 overflow-hidden rounded-lg bg-zinc-100 dark:bg-zinc-700">
|
|
<img src="{{ media_url($img) }}" class="h-full w-full object-cover" />
|
|
</div>
|
|
@endif
|
|
<div class="flex-1">
|
|
<livewire:admin.cms.media-picker :value="null" :field="'gallery_' . $i" type="image" profile="gallery" label="Bild wählen" :key="'gal-picker-' . $i . '-' . ($editingId ?? 'new')" />
|
|
<flux:input wire:model="galleryItems.{{ $i }}" size="sm" class="mt-1" placeholder="Oder Pfad manuell..." />
|
|
</div>
|
|
@if (count($galleryItems) > 1)
|
|
<flux:button size="sm" variant="ghost" icon="trash" wire:click="removeGalleryItem({{ $i }})" />
|
|
@endif
|
|
</div>
|
|
@endforeach
|
|
<flux:button size="xs" variant="ghost" icon="plus" wire:click="addGalleryItem">Bild hinzufügen</flux:button>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- TAB: Location --}}
|
|
@if ($activeTab === 'location')
|
|
<div class="space-y-4">
|
|
<flux:input wire:model="locTitle" label="Überschrift ({{ strtoupper($editLocale) }})" placeholder="Strategische Location: Al Jaddaf" />
|
|
<flux:input wire:model="locMapUrl" label="Google Maps URL" placeholder="https://maps.google.com/?q=..." />
|
|
|
|
<div>
|
|
<label class="mb-1 block text-sm font-medium text-zinc-700 dark:text-zinc-300">Location-Punkte ({{ strtoupper($editLocale) }})</label>
|
|
<div class="space-y-2">
|
|
@foreach ($locPoints as $i => $point)
|
|
<div wire:key="lp-{{ $i }}" class="flex items-center gap-2">
|
|
<flux:input wire:model="locPoints.{{ $i }}" placeholder="Beschreibung des Standort-Vorteils" class="flex-1" size="sm" />
|
|
@if (count($locPoints) > 1)
|
|
<flux:button size="xs" variant="ghost" icon="x-mark" wire:click="removeLocPoint({{ $i }})" />
|
|
@endif
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
<flux:button size="xs" variant="ghost" icon="plus" wire:click="addLocPoint" class="mt-2">Punkt</flux:button>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- TAB: Kontakt --}}
|
|
@if ($activeTab === 'contact')
|
|
<div class="space-y-4">
|
|
<flux:input wire:model="contactTitle" label="Überschrift ({{ strtoupper($editLocale) }})" placeholder="Sichern Sie sich eine der Einheiten." />
|
|
<flux:input wire:model="contactSubtitle" label="Untertitel ({{ strtoupper($editLocale) }})" placeholder="Ihr Ansprechpartner: Marcel Scheibe" />
|
|
|
|
<div>
|
|
<label class="mb-1 block text-sm font-medium text-zinc-700 dark:text-zinc-300">Auswahloptionen (Key → Anzeige)</label>
|
|
<div class="space-y-2">
|
|
@foreach ($contactOptions as $i => $opt)
|
|
<div wire:key="co-{{ $i }}" class="flex items-center gap-2">
|
|
<flux:input wire:model="contactOptions.{{ $i }}.key" placeholder="Key (leer=Default)" class="w-40" size="sm" />
|
|
<flux:input wire:model="contactOptions.{{ $i }}.value" placeholder="Anzeige-Text" class="flex-1" size="sm" />
|
|
@if (count($contactOptions) > 1)
|
|
<flux:button size="xs" variant="ghost" icon="x-mark" wire:click="removeContactOption({{ $i }})" />
|
|
@endif
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
<flux:button size="xs" variant="ghost" icon="plus" wire:click="addContactOption" class="mt-2">Option</flux:button>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- TAB: Trust-Block & Möbel-Vorteil (Detailseite) --}}
|
|
@if ($activeTab === 'trust_synergy')
|
|
<div class="space-y-8">
|
|
<div>
|
|
<flux:heading size="sm" class="mb-3">Trust-Block: Investorenschutz ({{ strtoupper($editLocale) }})</flux:heading>
|
|
<div class="space-y-4">
|
|
<flux:input wire:model="trustTitle" label="Überschrift" placeholder="Maximale Sicherheit für Ihr Investment" />
|
|
<flux:textarea wire:model="trustIntro" label="Einleitung" rows="2" placeholder="Kurzer Intro-Text unter der Überschrift" />
|
|
<flux:input wire:model="trustCtaUrl" label="CTA-Link (Magazin o. ä.)" placeholder="/magazin/1" />
|
|
<flux:input wire:model="trustCtaLabel" label="CTA-Button-Text" placeholder="Deep Dive: …" />
|
|
|
|
<div>
|
|
<label class="mb-2 block text-sm font-medium text-zinc-700 dark:text-zinc-300">Drei Spalten (Icon = Blade-Name, z. B. heroicon-o-lock-closed)</label>
|
|
<div class="space-y-4">
|
|
@foreach ($trustColumns as $ti => $tc)
|
|
<div wire:key="trust-col-{{ $ti }}" class="rounded-lg border border-zinc-200 p-4 dark:border-zinc-700">
|
|
<div class="mb-3 flex items-end gap-2">
|
|
<flux:select wire:model="trustColumns.{{ $ti }}.icon" label="Icon" class="min-w-[14rem]">
|
|
<flux:select.option value="heroicon-o-lock-closed">Schloss (Escrow)</flux:select.option>
|
|
<flux:select.option value="heroicon-o-building-library">Gebäude / DLD</flux:select.option>
|
|
<flux:select.option value="heroicon-o-chart-bar">Diagramm</flux:select.option>
|
|
<flux:select.option value="heroicon-o-shield-check">Schild</flux:select.option>
|
|
<flux:select.option value="heroicon-o-sparkles">Sparkles</flux:select.option>
|
|
</flux:select>
|
|
@if (count($trustColumns) > 1)
|
|
<flux:button size="sm" variant="ghost" icon="trash" wire:click="removeTrustColumn({{ $ti }})" />
|
|
@endif
|
|
</div>
|
|
<flux:input wire:model="trustColumns.{{ $ti }}.title" label="Spaltenüberschrift" class="mb-2" />
|
|
<flux:textarea wire:model="trustColumns.{{ $ti }}.text" label="Text" rows="3" />
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
<flux:button size="xs" variant="ghost" icon="plus" wire:click="addTrustColumn" class="mt-2">Spalte hinzufügen</flux:button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<flux:separator />
|
|
|
|
<div>
|
|
<flux:heading size="sm" class="mb-3">Möbel-Vorteil / Synergie ({{ strtoupper($editLocale) }})</flux:heading>
|
|
<flux:textarea wire:model="furnitureTitle" label="Überschrift (HTML erlaubt, z. B. <span class="text-secondary">)" rows="2" />
|
|
<flux:textarea wire:model="furnitureText" label="Fließtext" rows="4" class="mt-3" />
|
|
<div class="mt-4 grid grid-cols-1 gap-4 md:grid-cols-2">
|
|
<flux:input wire:model="furnitureButtonText" label="Button-Text" placeholder="Mehr zum B2in-Netzwerk" />
|
|
<flux:input wire:model="furnitureButtonLink" label="Button-Link" placeholder="/netzwerk" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
<div class="mt-6 flex gap-2 border-t border-zinc-200 pt-4 dark:border-zinc-700">
|
|
<flux:button variant="primary" wire:click="save">Speichern</flux:button>
|
|
<flux:button variant="ghost" wire:click="cancelForm">Abbrechen</flux:button>
|
|
</div>
|
|
</flux:card>
|
|
@endif
|
|
|
|
{{-- Projektliste --}}
|
|
<flux:card>
|
|
<div class="divide-y dark:divide-zinc-700">
|
|
@forelse ($this->projects as $project)
|
|
<div wire:key="project-{{ $project->id }}" class="flex items-center justify-between gap-4 py-3">
|
|
<div class="flex items-center gap-4">
|
|
@if ($project->image)
|
|
<div class="h-12 w-20 shrink-0 overflow-hidden rounded-lg border border-zinc-200 bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-800">
|
|
<img src="{{ media_url($project->image) }}" class="h-full w-full object-cover" loading="lazy" />
|
|
</div>
|
|
@else
|
|
<div class="flex h-12 w-20 shrink-0 items-center justify-center rounded-lg border border-zinc-200 bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-800">
|
|
<x-heroicon-o-building-office class="h-6 w-6 text-zinc-300" />
|
|
</div>
|
|
@endif
|
|
<div>
|
|
<div class="flex items-center gap-2">
|
|
<span class="font-medium text-zinc-800 dark:text-zinc-200">{{ $project->title }}</span>
|
|
@if ($project->is_published)
|
|
<flux:badge size="sm" color="green">Live</flux:badge>
|
|
@else
|
|
<flux:badge size="sm" color="zinc">Entwurf</flux:badge>
|
|
@endif
|
|
</div>
|
|
<div class="flex items-center gap-3 text-sm text-zinc-500">
|
|
<span>{{ $project->location }}</span>
|
|
@if ($project->status)
|
|
<span>· {{ $project->status }}</span>
|
|
@endif
|
|
@if ($project->getFormattedPrice())
|
|
<span>· {{ $project->getFormattedPrice() }}</span>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center gap-1">
|
|
<flux:button size="sm" variant="ghost" icon="pencil" wire:click="openEdit({{ $project->id }})" />
|
|
<flux:button size="sm" variant="ghost" icon="document-duplicate" wire:click="duplicateProject({{ $project->id }})" title="Projekt duplizieren" />
|
|
<flux:button size="sm" variant="ghost" icon="trash"
|
|
wire:click="deleteProject({{ $project->id }})"
|
|
wire:confirm="'{{ $project->title }}' wirklich löschen?" />
|
|
</div>
|
|
</div>
|
|
@empty
|
|
<div class="py-12 text-center">
|
|
<x-heroicon-o-building-office class="mx-auto mb-4 h-12 w-12 text-zinc-400" />
|
|
<flux:heading>Keine Projekte</flux:heading>
|
|
<flux:text>Erstelle das erste Projekt mit dem Button oben.</flux:text>
|
|
</div>
|
|
@endforelse
|
|
</div>
|
|
</flux:card>
|
|
</div>
|