10-04-2026

This commit is contained in:
Kevin Adametz 2026-04-10 17:18:17 +02:00
parent 4d6b4930b2
commit 4bb89aad8c
836 changed files with 52961 additions and 5950 deletions

View file

@ -2,10 +2,10 @@
namespace FluxCms\Components;
use Illuminate\Support\ServiceProvider;
use Livewire\Livewire;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
use Livewire\Livewire;
use ReflectionClass;
class FluxCmsComponentsServiceProvider extends ServiceProvider
@ -33,7 +33,7 @@ class FluxCmsComponentsServiceProvider extends ServiceProvider
*/
protected function bootViews(): void
{
$this->loadViewsFrom(__DIR__ . '/../resources/views', 'flux-cms-components');
$this->loadViewsFrom(__DIR__.'/../resources/views', 'flux-cms-components');
}
/**
@ -44,12 +44,12 @@ class FluxCmsComponentsServiceProvider extends ServiceProvider
if ($this->app->runningInConsole()) {
// Publish views
$this->publishes([
__DIR__ . '/../resources/views' => resource_path('views/vendor/flux-cms-components'),
__DIR__.'/../resources/views' => resource_path('views/vendor/flux-cms-components'),
], 'flux-cms-components-views');
// Publish assets
$this->publishes([
__DIR__ . '/../resources/assets' => public_path('vendor/flux-cms-components'),
__DIR__.'/../resources/assets' => public_path('vendor/flux-cms-components'),
], 'flux-cms-components-assets');
}
}
@ -59,22 +59,22 @@ class FluxCmsComponentsServiceProvider extends ServiceProvider
*/
protected function bootLivewireComponents(): void
{
$this->registerLivewireComponentsFrom(__DIR__ . '/Livewire/Backend', 'FluxCms\\Components\\Livewire\\Backend', 'flux-cms::');
$this->registerLivewireComponentsFrom(__DIR__ . '/Livewire/Frontend', 'FluxCms\\Components\\Livewire\\Frontend', 'flux-cms::');
$this->registerLivewireComponentsFrom(__DIR__.'/Livewire/Backend', 'FluxCms\\Components\\Livewire\\Backend', 'flux-cms::');
$this->registerLivewireComponentsFrom(__DIR__.'/Livewire/Frontend', 'FluxCms\\Components\\Livewire\\Frontend', 'flux-cms::');
}
protected function registerLivewireComponentsFrom(string $path, string $namespace, string $aliasPrefix = ''): void
{
$filesystem = new Filesystem();
if (!$filesystem->isDirectory($path)) {
$filesystem = new Filesystem;
if (! $filesystem->isDirectory($path)) {
return;
}
foreach ($filesystem->allFiles($path) as $file) {
$class = $namespace . '\\' . str_replace(['/', '.php'], ['\\', ''], $file->getRelativePathname());
$class = $namespace.'\\'.str_replace(['/', '.php'], ['\\', ''], $file->getRelativePathname());
if (class_exists($class) && is_subclass_of($class, \Livewire\Component::class) && !(new ReflectionClass($class))->isAbstract()) {
$alias = $aliasPrefix . Str::kebab(class_basename($class));
if (class_exists($class) && is_subclass_of($class, \Livewire\Component::class) && ! (new ReflectionClass($class))->isAbstract()) {
$alias = $aliasPrefix.Str::kebab(class_basename($class));
Livewire::component($alias, $class);
}
}

View file

@ -2,13 +2,13 @@
namespace FluxCms\Components\Livewire\Backend;
use Livewire\Component;
use FluxCms\Core\Models\BlogPost;
use Spatie\Tags\Tag;
use Livewire\Component;
class BlogEditor extends Component
{
public BlogPost $post;
public string $tags = '';
public function mount(BlogPost $post)

View file

@ -2,21 +2,26 @@
namespace FluxCms\Components\Livewire\Backend;
use FluxCms\Core\Models\BlogPost;
use Livewire\Component;
use Livewire\WithPagination;
use FluxCms\Core\Models\BlogPost;
use Illuminate\Support\Collection;
class BlogManager extends Component
{
use WithPagination;
public string $domainKey;
public array $availableLanguages = [];
public string $currentLocale = 'de';
public string $search = '';
public string $filterStatus = 'all';
public bool $showCreateModal = false;
public ?BlogPost $editingPost = null;
// Form data
@ -48,12 +53,12 @@ class BlogManager extends Component
$query = BlogPost::forDomain($this->domainKey);
// Search filter
if (!empty($this->search)) {
if (! empty($this->search)) {
$query->where(function ($q) {
$q->where('title->de', 'like', '%' . $this->search . '%')
->orWhere('title->en', 'like', '%' . $this->search . '%')
->orWhere('content->de', 'like', '%' . $this->search . '%')
->orWhere('content->en', 'like', '%' . $this->search . '%');
$q->where('title->de', 'like', '%'.$this->search.'%')
->orWhere('title->en', 'like', '%'.$this->search.'%')
->orWhere('content->de', 'like', '%'.$this->search.'%')
->orWhere('content->en', 'like', '%'.$this->search.'%');
});
}
@ -175,7 +180,7 @@ class BlogManager extends Component
session()->flash('success', $message);
} catch (\Exception $e) {
session()->flash('error', 'Error saving blog post: ' . $e->getMessage());
session()->flash('error', 'Error saving blog post: '.$e->getMessage());
}
}
@ -191,7 +196,7 @@ class BlogManager extends Component
session()->flash('success', 'Blog post deleted successfully.');
}
} catch (\Exception $e) {
session()->flash('error', 'Error deleting blog post: ' . $e->getMessage());
session()->flash('error', 'Error deleting blog post: '.$e->getMessage());
}
}
@ -213,7 +218,7 @@ class BlogManager extends Component
session()->flash('success', $message);
}
} catch (\Exception $e) {
session()->flash('error', 'Error updating publish status: ' . $e->getMessage());
session()->flash('error', 'Error updating publish status: '.$e->getMessage());
}
}
@ -225,12 +230,12 @@ class BlogManager extends Component
try {
$post = BlogPost::find($postId);
if ($post) {
$post->update(['is_featured' => !$post->is_featured]);
$post->update(['is_featured' => ! $post->is_featured]);
$message = $post->is_featured ? 'Post marked as featured.' : 'Post removed from featured.';
session()->flash('success', $message);
}
} catch (\Exception $e) {
session()->flash('error', 'Error updating featured status: ' . $e->getMessage());
session()->flash('error', 'Error updating featured status: '.$e->getMessage());
}
}
@ -249,14 +254,14 @@ class BlogManager extends Component
// Update title to indicate it's a copy
$titles = $duplicate->getTranslations('title');
foreach ($titles as $locale => $title) {
$titles[$locale] = $title . ' (Copy)';
$titles[$locale] = $title.' (Copy)';
}
$duplicate->title = $titles;
// Update slugs to avoid conflicts
$slugs = $duplicate->getTranslations('slug');
foreach ($slugs as $locale => $slug) {
$slugs[$locale] = $slug . '-copy-' . time();
$slugs[$locale] = $slug.'-copy-'.time();
}
$duplicate->slug = $slugs;
@ -264,7 +269,7 @@ class BlogManager extends Component
session()->flash('success', 'Blog post duplicated successfully.');
}
} catch (\Exception $e) {
session()->flash('error', 'Error duplicating blog post: ' . $e->getMessage());
session()->flash('error', 'Error duplicating blog post: '.$e->getMessage());
}
}
@ -276,7 +281,7 @@ class BlogManager extends Component
$title = $this->postData['title'][$locale] ?? '';
if ($title) {
$slug = \Illuminate\Support\Str::slug($title);
$this->postData['slug'][$locale] = '/' . $slug;
$this->postData['slug'][$locale] = '/'.$slug;
}
}
@ -315,4 +320,4 @@ class BlogManager extends Component
'featured' => BlogPost::forDomain($this->domainKey)->featured()->count(),
];
}
}
}

View file

@ -2,17 +2,22 @@
namespace FluxCms\Components\Livewire\Backend;
use Livewire\Component;
use FluxCms\Core\Models\PageComponent;
use FluxCms\Core\Services\ComponentRegistry;
use Livewire\Component;
class ComponentEditor extends Component
{
public PageComponent $component;
public array $content = [];
public array $availableLanguages = [];
public string $currentLocale = 'de';
public bool $expanded = false;
public array $validationErrors = [];
protected ComponentRegistry $componentRegistry;
@ -56,7 +61,7 @@ class ComponentEditor extends Component
*/
public function toggleExpanded()
{
$this->expanded = !$this->expanded;
$this->expanded = ! $this->expanded;
}
/**
@ -85,21 +90,22 @@ class ComponentEditor extends Component
{
$this->validateContent();
if (!empty($this->validationErrors)) {
if (! empty($this->validationErrors)) {
session()->flash('error', 'Please correct validation errors.');
return;
}
try {
$this->component->update([
'content' => $this->content
'content' => $this->content,
]);
session()->flash('success', 'Component saved successfully.');
$this->dispatch('component-saved', componentId: $this->component->id);
} catch (\Exception $e) {
session()->flash('error', 'Error saving component: ' . $e->getMessage());
session()->flash('error', 'Error saving component: '.$e->getMessage());
}
}
@ -111,7 +117,7 @@ class ComponentEditor extends Component
if (empty($this->validationErrors)) {
try {
$this->component->update([
'content' => $this->content
'content' => $this->content,
]);
} catch (\Exception $e) {
// Silent fail for auto-save
@ -189,6 +195,7 @@ class ComponentEditor extends Component
public function hasFieldError(string $fieldKey, ?string $locale = null): bool
{
$errorKey = $locale ? "{$fieldKey}.{$locale}" : $fieldKey;
return isset($this->validationErrors[$errorKey]);
}
@ -198,6 +205,7 @@ class ComponentEditor extends Component
public function getFieldErrors(string $fieldKey, ?string $locale = null): array
{
$errorKey = $locale ? "{$fieldKey}.{$locale}" : $fieldKey;
return $this->validationErrors[$errorKey] ?? [];
}
@ -225,4 +233,4 @@ class ComponentEditor extends Component
$this->content = $this->component->getTranslations('content');
$this->validationErrors = [];
}
}
}

View file

@ -2,24 +2,32 @@
namespace FluxCms\Components\Livewire\Backend;
use Illuminate\Http\UploadedFile;
use Livewire\Component;
use Livewire\WithFileUploads;
use Livewire\WithPagination;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Illuminate\Http\UploadedFile;
class MediaManager extends Component
{
use WithFileUploads, WithPagination;
public bool $showModal = false;
public ?string $targetComponentId = null;
public ?string $targetFieldKey = null;
public ?string $targetLocale = null;
public array $uploadingFiles = [];
public string $searchTerm = '';
public string $filterType = 'all';
public array $selectedMedia = [];
public bool $multiSelect = false;
protected $paginationTheme = 'simple-bootstrap';
@ -71,7 +79,7 @@ class MediaManager extends Component
public function uploadFiles()
{
$this->validate([
'uploadingFiles.*' => 'file|max:' . config('flux-cms.media.max_file_size', 10240),
'uploadingFiles.*' => 'file|max:'.config('flux-cms.media.max_file_size', 10240),
]);
try {
@ -83,7 +91,7 @@ class MediaManager extends Component
session()->flash('success', 'Files uploaded successfully.');
} catch (\Exception $e) {
session()->flash('error', 'Error uploading files: ' . $e->getMessage());
session()->flash('error', 'Error uploading files: '.$e->getMessage());
}
}
@ -94,8 +102,10 @@ class MediaManager extends Component
{
// Create a temporary model for media library
// In real implementation, you'd use a dedicated media model
$mediaModel = new class extends \Illuminate\Database\Eloquent\Model implements \Spatie\MediaLibrary\HasMedia {
$mediaModel = new class extends \Illuminate\Database\Eloquent\Model implements \Spatie\MediaLibrary\HasMedia
{
use \Spatie\MediaLibrary\InteractsWithMedia;
protected $table = 'flux_cms_media'; // Would exist in real implementation
};
@ -114,7 +124,7 @@ class MediaManager extends Component
{
if ($this->multiSelect) {
if (in_array($mediaId, $this->selectedMedia)) {
$this->selectedMedia = array_filter($this->selectedMedia, fn($id) => $id !== $mediaId);
$this->selectedMedia = array_filter($this->selectedMedia, fn ($id) => $id !== $mediaId);
} else {
$this->selectedMedia[] = $mediaId;
}
@ -165,7 +175,7 @@ class MediaManager extends Component
session()->flash('success', 'Media deleted successfully.');
}
} catch (\Exception $e) {
session()->flash('error', 'Error deleting media: ' . $e->getMessage());
session()->flash('error', 'Error deleting media: '.$e->getMessage());
}
}
@ -177,13 +187,13 @@ class MediaManager extends Component
$query = Media::query()->orderBy('created_at', 'desc');
// Search filter
if (!empty($this->searchTerm)) {
$query->where('name', 'like', '%' . $this->searchTerm . '%');
if (! empty($this->searchTerm)) {
$query->where('name', 'like', '%'.$this->searchTerm.'%');
}
// Type filter
if ($this->filterType !== 'all') {
$query->where('mime_type', 'like', $this->filterType . '%');
$query->where('mime_type', 'like', $this->filterType.'%');
}
return $query->paginate(20);
@ -271,6 +281,6 @@ class MediaManager extends Component
$bytes /= (1 << (10 * $pow));
return round($bytes, 2) . ' ' . $units[$pow];
return round($bytes, 2).' '.$units[$pow];
}
}
}

View file

@ -2,26 +2,35 @@
namespace FluxCms\Components\Livewire\Backend;
use Livewire\Component;
use FluxCms\Core\Models\Navigation;
use FluxCms\Core\Models\NavigationItem;
use FluxCms\Core\Models\Page;
use Illuminate\Support\Collection;
use Livewire\Component;
class NavigationManager extends Component
{
public string $domainKey;
public Collection $navigations;
public ?Navigation $selectedNavigation = null;
public Collection $navigationItems;
public array $availableLanguages = [];
public string $currentLocale = 'de';
public bool $showCreateModal = false;
public bool $showItemModal = false;
public ?NavigationItem $editingItem = null;
// Form data
public array $navigationData = [];
public array $itemData = [];
public function mount(string $domainKey)
@ -58,8 +67,9 @@ class NavigationManager extends Component
*/
public function loadNavigationItems()
{
if (!$this->selectedNavigation) {
if (! $this->selectedNavigation) {
$this->navigationItems = collect();
return;
}
@ -124,7 +134,7 @@ class NavigationManager extends Component
session()->flash('success', 'Navigation created successfully.');
} catch (\Exception $e) {
session()->flash('error', 'Error creating navigation: ' . $e->getMessage());
session()->flash('error', 'Error creating navigation: '.$e->getMessage());
}
}
@ -172,6 +182,7 @@ class NavigationManager extends Component
// Validate that either page or external URL is provided
if (empty($this->itemData['page_id']) && empty($this->itemData['external_url'])) {
$this->addError('itemData.page_id', 'Either select a page or provide an external URL.');
return;
}
@ -205,7 +216,7 @@ class NavigationManager extends Component
session()->flash('success', $message);
} catch (\Exception $e) {
session()->flash('error', 'Error saving navigation item: ' . $e->getMessage());
session()->flash('error', 'Error saving navigation item: '.$e->getMessage());
}
}
@ -222,7 +233,7 @@ class NavigationManager extends Component
session()->flash('success', 'Navigation item deleted successfully.');
}
} catch (\Exception $e) {
session()->flash('error', 'Error deleting navigation item: ' . $e->getMessage());
session()->flash('error', 'Error deleting navigation item: '.$e->getMessage());
}
}
@ -234,11 +245,11 @@ class NavigationManager extends Component
try {
$item = NavigationItem::find($itemId);
if ($item) {
$item->update(['is_active' => !$item->is_active]);
$item->update(['is_active' => ! $item->is_active]);
$this->loadNavigationItems();
}
} catch (\Exception $e) {
session()->flash('error', 'Error toggling navigation item: ' . $e->getMessage());
session()->flash('error', 'Error toggling navigation item: '.$e->getMessage());
}
}
@ -256,7 +267,7 @@ class NavigationManager extends Component
session()->flash('success', 'Navigation order updated successfully.');
} catch (\Exception $e) {
session()->flash('error', 'Error updating order: ' . $e->getMessage());
session()->flash('error', 'Error updating order: '.$e->getMessage());
}
}
@ -279,7 +290,7 @@ class NavigationManager extends Component
session()->flash('success', 'Navigation deleted successfully.');
}
} catch (\Exception $e) {
session()->flash('error', 'Error deleting navigation: ' . $e->getMessage());
session()->flash('error', 'Error deleting navigation: '.$e->getMessage());
}
}
@ -288,7 +299,7 @@ class NavigationManager extends Component
*/
public function getAvailableParentsProperty(): Collection
{
if (!$this->selectedNavigation) {
if (! $this->selectedNavigation) {
return collect();
}
@ -327,4 +338,4 @@ class NavigationManager extends Component
$this->navigationData = [];
$this->itemData = [];
}
}
}

View file

@ -2,22 +2,29 @@
namespace FluxCms\Components\Livewire\Backend;
use Livewire\Component;
use Livewire\Attributes\On;
use FluxCms\Core\Models\Page;
use FluxCms\Core\Models\PageComponent;
use FluxCms\Core\Services\ComponentRegistry;
use Illuminate\Support\Collection;
use Livewire\Attributes\On;
use Livewire\Component;
class PageEditor extends Component
{
public Page $page;
public Collection $components;
public array $availableLanguages = [];
public string $currentLocale = 'de';
public bool $showComponentModal = false;
public array $availableComponents = [];
public string $selectedCategory = 'all';
public bool $isLoading = false;
protected ComponentRegistry $componentRegistry;
@ -39,7 +46,7 @@ class PageEditor extends Component
public function render()
{
return view('flux-cms-components::livewire.backend.page-editor')
->layout('flux-cms-components::layouts.admin');
->layout('flux-cms-components::layouts.admin');
}
/**
@ -99,8 +106,9 @@ class PageEditor extends Component
*/
public function addComponent(string $componentClass)
{
if (!$this->componentRegistry->isValidComponent($componentClass)) {
if (! $this->componentRegistry->isValidComponent($componentClass)) {
$this->addError('component', 'Invalid component selected.');
return;
}
@ -121,7 +129,7 @@ class PageEditor extends Component
session()->flash('success', 'Component added successfully.');
} catch (\Exception $e) {
$this->addError('component', 'Error adding component: ' . $e->getMessage());
$this->addError('component', 'Error adding component: '.$e->getMessage());
}
}
@ -133,8 +141,9 @@ class PageEditor extends Component
try {
$component = $this->components->firstWhere('id', $componentId);
if (!$component) {
if (! $component) {
$this->addError('component', 'Component not found.');
return;
}
@ -145,7 +154,7 @@ class PageEditor extends Component
session()->flash('success', 'Component deleted successfully.');
} catch (\Exception $e) {
$this->addError('component', 'Error deleting component: ' . $e->getMessage());
$this->addError('component', 'Error deleting component: '.$e->getMessage());
}
}
@ -157,8 +166,9 @@ class PageEditor extends Component
try {
$component = $this->components->firstWhere('id', $componentId);
if (!$component) {
if (! $component) {
$this->addError('component', 'Component not found.');
return;
}
@ -169,7 +179,7 @@ class PageEditor extends Component
session()->flash('success', 'Component duplicated successfully.');
} catch (\Exception $e) {
$this->addError('component', 'Error duplicating component: ' . $e->getMessage());
$this->addError('component', 'Error duplicating component: '.$e->getMessage());
}
}
@ -181,15 +191,15 @@ class PageEditor extends Component
try {
$component = $this->components->firstWhere('id', $componentId);
if (!$component) {
if (! $component) {
return;
}
$component->update(['is_active' => !$component->is_active]);
$component->update(['is_active' => ! $component->is_active]);
$this->loadComponents();
} catch (\Exception $e) {
$this->addError('component', 'Error toggling component: ' . $e->getMessage());
$this->addError('component', 'Error toggling component: '.$e->getMessage());
}
}
@ -208,7 +218,7 @@ class PageEditor extends Component
session()->flash('success', 'Component order updated.');
} catch (\Exception $e) {
$this->addError('order', 'Error updating order: ' . $e->getMessage());
$this->addError('order', 'Error updating order: '.$e->getMessage());
}
}
@ -237,7 +247,7 @@ class PageEditor extends Component
}
foreach ($config['fields'] as $field) {
if (!$field instanceof \FluxCms\Core\FieldTypes\BaseField) {
if (! $field instanceof \FluxCms\Core\FieldTypes\BaseField) {
continue;
}
@ -271,7 +281,7 @@ class PageEditor extends Component
session()->flash('success', 'Page data saved successfully.');
} catch (\Exception $e) {
$this->addError('page', 'Error saving page: ' . $e->getMessage());
$this->addError('page', 'Error saving page: '.$e->getMessage());
}
}
@ -292,21 +302,21 @@ class PageEditor extends Component
session()->flash('success', $message);
} catch (\Exception $e) {
$this->addError('publish', 'Error updating publish status: ' . $e->getMessage());
$this->addError('publish', 'Error updating publish status: '.$e->getMessage());
}
}
/**
* Create version
*/
public function createVersion(string $description = null)
public function createVersion(?string $description = null)
{
try {
$this->page->createVersion($description, auth()->id());
session()->flash('success', 'Version created successfully.');
} catch (\Exception $e) {
$this->addError('version', 'Error creating version: ' . $e->getMessage());
$this->addError('version', 'Error creating version: '.$e->getMessage());
}
}
@ -320,10 +330,11 @@ class PageEditor extends Component
if (empty($slug)) {
$this->addError('preview', 'No slug available for current language.');
return;
}
$url = $this->page->getUrl($locale) . '?preview=1';
$url = $this->page->getUrl($locale).'?preview=1';
$this->dispatch('open-preview', url: $url);
}
@ -351,6 +362,7 @@ class PageEditor extends Component
foreach ($this->availableComponents as $category => $categoryComponents) {
$components = array_merge($components, $categoryComponents);
}
return $components;
}
@ -362,7 +374,7 @@ class PageEditor extends Component
*/
public function getPageStatusProperty(): string
{
if (!$this->page->is_published) {
if (! $this->page->is_published) {
return 'draft';
}
@ -372,4 +384,4 @@ class PageEditor extends Component
return 'published';
}
}
}

View file

@ -2,26 +2,34 @@
namespace FluxCms\Components\Livewire\Frontend;
use Livewire\Component;
use Livewire\WithPagination;
use FluxCms\Core\Models\BlogPost;
use Illuminate\Support\Collection;
use Livewire\Component;
use Livewire\WithPagination;
class BlogList extends Component
{
use WithPagination;
public string $domainKey;
public int $perPage = 12;
public bool $showFeatured = true;
public bool $showPagination = true;
public string $orderBy = 'published_at';
public string $orderDirection = 'desc';
public array $classes = [];
// Filtering
public string $search = '';
public array $tags = [];
public ?string $category = null;
protected $paginationTheme = 'simple-bootstrap';
@ -63,12 +71,12 @@ class BlogList extends Component
$query = BlogPost::forDomain($this->domainKey)->published();
// Search filter
if (!empty($this->search)) {
if (! empty($this->search)) {
$locale = app()->getLocale();
$query->where(function ($q) use ($locale) {
$q->where("title->{$locale}", 'like', '%' . $this->search . '%')
->orWhere("excerpt->{$locale}", 'like', '%' . $this->search . '%')
->orWhere("content->{$locale}", 'like', '%' . $this->search . '%');
$q->where("title->{$locale}", 'like', '%'.$this->search.'%')
->orWhere("excerpt->{$locale}", 'like', '%'.$this->search.'%')
->orWhere("content->{$locale}", 'like', '%'.$this->search.'%');
});
}
@ -119,6 +127,7 @@ class BlogList extends Component
public function getPostTitle(BlogPost $post): string
{
$locale = app()->getLocale();
return $post->getTranslation('title', $locale);
}
@ -185,6 +194,7 @@ class BlogList extends Component
{
$defaultClasses = ['flux-cms-blog-list', 'blog-list'];
$allClasses = array_merge($defaultClasses, $this->classes);
return implode(' ', $allClasses);
}
@ -228,6 +238,7 @@ class BlogList extends Component
public function getSearchPlaceholder(): string
{
$locale = app()->getLocale();
return $locale === 'de' ? 'Blog durchsuchen...' : 'Search blog...';
}
@ -238,10 +249,10 @@ class BlogList extends Component
{
$locale = app()->getLocale();
if (!empty($this->search)) {
if (! empty($this->search)) {
return $locale === 'de'
? 'Keine Artikel für "' . $this->search . '" gefunden.'
: 'No posts found for "' . $this->search . '".';
? 'Keine Artikel für "'.$this->search.'" gefunden.'
: 'No posts found for "'.$this->search.'".';
}
return $locale === 'de'
@ -262,4 +273,4 @@ class BlogList extends Component
return $minutes === 1 ? '1 min read' : "{$minutes} min read";
}
}
}

View file

@ -2,18 +2,24 @@
namespace FluxCms\Components\Livewire\Frontend;
use Livewire\Component;
use FluxCms\Core\Models\BlogPost as BlogPostModel;
use Illuminate\Support\Collection;
use Livewire\Component;
class BlogPost extends Component
{
public BlogPostModel $post;
public string $domainKey;
public bool $showRelated = true;
public bool $showAuthor = true;
public bool $showMeta = true;
public bool $showSocial = true;
public array $classes = [];
public function mount(
@ -65,7 +71,7 @@ class BlogPost extends Component
$locale = app()->getLocale();
return [
'title' => $this->getTitle() . ' - Blog',
'title' => $this->getTitle().' - Blog',
'description' => $this->getExcerpt(160),
'keywords' => $this->post->getTranslation('meta_keywords', $locale),
'og_title' => $this->getTitle(),
@ -85,6 +91,7 @@ class BlogPost extends Component
public function getTitle(): string
{
$locale = app()->getLocale();
return $this->post->getTranslation('title', $locale);
}
@ -94,6 +101,7 @@ class BlogPost extends Component
public function getContent(): string
{
$locale = app()->getLocale();
return $this->post->getTranslation('content', $locale);
}
@ -228,6 +236,7 @@ class BlogPost extends Component
public function getRelatedPostTitle(BlogPostModel $relatedPost): string
{
$locale = app()->getLocale();
return $relatedPost->getTranslation('title', $locale);
}
@ -311,4 +320,4 @@ class BlogPost extends Component
'next' => $nextPost,
];
}
}
}

View file

@ -2,18 +2,24 @@
namespace FluxCms\Components\Livewire\Frontend;
use Livewire\Component;
use FluxCms\Core\Models\Navigation;
use Illuminate\Support\Collection;
use Livewire\Component;
class NavigationRenderer extends Component
{
public string $domainKey;
public string $navigationName;
public ?Navigation $navigation = null;
public Collection $navigationItems;
public string $currentUrl = '';
public array $classes = [];
public bool $showInactive = false;
public function mount(
@ -49,7 +55,7 @@ class NavigationRenderer extends Component
$this->navigationItems = $this->navigation->getHierarchicalItems();
// Filter inactive items if needed
if (!$this->showInactive) {
if (! $this->showInactive) {
$this->navigationItems = $this->navigationItems->where('is_active', true);
}
} else {
@ -79,6 +85,7 @@ class NavigationRenderer extends Component
public function getItemLabel($item): string
{
$locale = app()->getLocale();
return $item->getTranslation('label', $locale);
}
@ -95,9 +102,10 @@ class NavigationRenderer extends Component
*/
public function getChildren($item): Collection
{
if (!$this->showInactive) {
if (! $this->showInactive) {
return $item->children->where('is_active', true);
}
return $item->children;
}
@ -114,8 +122,9 @@ class NavigationRenderer extends Component
*/
public function getNavigationClasses(): string
{
$defaultClasses = ['flux-cms-navigation', 'navigation-' . $this->navigationName];
$defaultClasses = ['flux-cms-navigation', 'navigation-'.$this->navigationName];
$allClasses = array_merge($defaultClasses, $this->classes);
return implode(' ', $allClasses);
}
@ -138,7 +147,7 @@ class NavigationRenderer extends Component
$classes[] = 'nav-item--has-children';
}
if (!$item->is_active) {
if (! $item->is_active) {
$classes[] = 'nav-item--inactive';
}
@ -177,7 +186,7 @@ class NavigationRenderer extends Component
$attributeStrings = [];
foreach ($attributes as $key => $value) {
$attributeStrings[] = $key . '="' . htmlspecialchars($value) . '"';
$attributeStrings[] = $key.'="'.htmlspecialchars($value).'"';
}
return implode(' ', $attributeStrings);
@ -239,11 +248,12 @@ class NavigationRenderer extends Component
*/
public function getNavigationDisplayName(): string
{
if (!$this->navigation) {
if (! $this->navigation) {
return '';
}
$locale = app()->getLocale();
return $this->navigation->getTranslation('display_name', $locale);
}
}
}

View file

@ -2,15 +2,18 @@
namespace FluxCms\Components\Livewire\Frontend;
use Livewire\Component;
use FluxCms\Core\Models\Page;
use Illuminate\Support\Collection;
use Livewire\Component;
class PageRenderer extends Component
{
public Page $page;
public Collection $components;
public bool $isPreview = false;
public array $seoData = [];
public function mount(Page $page, bool $isPreview = false)
@ -24,10 +27,10 @@ class PageRenderer extends Component
public function render()
{
return view('flux-cms-components::livewire.frontend.page-renderer')
->layout('flux-cms-components::layouts.frontend', [
'seoData' => $this->seoData,
'page' => $this->page,
]);
->layout('flux-cms-components::layouts.frontend', [
'seoData' => $this->seoData,
'page' => $this->page,
]);
}
/**
@ -84,6 +87,7 @@ class PageRenderer extends Component
public function getComponentContent(PageComponent $component): array
{
$locale = app()->getLocale();
return $component->getTranslatedContent($locale);
}
@ -93,23 +97,24 @@ class PageRenderer extends Component
public function renderComponent(PageComponent $component): string
{
try {
if (!$this->canRenderComponent($component)) {
if (! $this->canRenderComponent($component)) {
return '';
}
$content = $this->getComponentContent($component);
// Check if component class exists
if (!class_exists($component->component_class)) {
if (! class_exists($component->component_class)) {
if ($this->isPreview) {
return $this->renderComponentError($component, 'Component class not found');
}
return '';
}
// Render component
$componentHtml = \Livewire\Livewire::mount($component->component_class, [
'content' => $component->getTranslations('content')
'content' => $component->getTranslations('content'),
])->html();
// Wrap component if enabled
@ -123,7 +128,7 @@ class PageRenderer extends Component
\Log::error('Error rendering component', [
'component_id' => $component->id,
'component_class' => $component->component_class,
'error' => $e->getMessage()
'error' => $e->getMessage(),
]);
if ($this->isPreview) {
@ -141,10 +146,10 @@ class PageRenderer extends Component
{
$classes = [
'flux-cms-component',
'flux-cms-component--' . class_basename($component->component_class),
'flux-cms-component--'.class_basename($component->component_class),
];
if (!$component->is_active) {
if (! $component->is_active) {
$classes[] = 'flux-cms-component--inactive';
}
@ -157,7 +162,7 @@ class PageRenderer extends Component
}
$attributeString = collect($attributes)
->map(fn($value, $key) => "{$key}=\"{$value}\"")
->map(fn ($value, $key) => "{$key}=\"{$value}\"")
->implode(' ');
$classString = implode(' ', $classes);
@ -196,9 +201,9 @@ class PageRenderer extends Component
public function getRelatedPages(): Collection
{
return Page::forDomain($this->page->domain_key)
->published()
->where('id', '!=', $this->page->id)
->limit(3)
->get();
->published()
->where('id', '!=', $this->page->id)
->limit(3)
->get();
}
}
}