First commit

This commit is contained in:
Kevin Adametz 2025-10-20 17:50:35 +02:00
commit 7cf3558ba7
12933 changed files with 1180047 additions and 0 deletions

View file

@ -0,0 +1,40 @@
<?php
namespace App\Actions\Fortify;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Laravel\Fortify\Contracts\CreatesNewUsers;
class CreateNewUser implements CreatesNewUsers
{
use PasswordValidationRules;
/**
* Validate and create a newly registered user.
*
* @param array<string, string> $input
*/
public function create(array $input): User
{
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
'email' => [
'required',
'string',
'email',
'max:255',
Rule::unique(User::class),
],
'password' => $this->passwordRules(),
])->validate();
return User::create([
'name' => $input['name'],
'email' => $input['email'],
'password' => Hash::make($input['password']),
]);
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace App\Actions\Fortify;
use Illuminate\Validation\Rules\Password;
trait PasswordValidationRules
{
/**
* Get the validation rules used to validate passwords.
*
* @return array<int, \Illuminate\Contracts\Validation\Rule|array<mixed>|string>
*/
protected function passwordRules(): array
{
return ['required', 'string', Password::default(), 'confirmed'];
}
}

View file

@ -0,0 +1,29 @@
<?php
namespace App\Actions\Fortify;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Laravel\Fortify\Contracts\ResetsUserPasswords;
class ResetUserPassword implements ResetsUserPasswords
{
use PasswordValidationRules;
/**
* Validate and reset the user's forgotten password.
*
* @param array<string, string> $input
*/
public function reset(User $user, array $input): void
{
Validator::make($input, [
'password' => $this->passwordRules(),
])->validate();
$user->forceFill([
'password' => Hash::make($input['password']),
])->save();
}
}

View file

@ -0,0 +1,32 @@
<?php
namespace App\Actions\Fortify;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Laravel\Fortify\Contracts\UpdatesUserPasswords;
class UpdateUserPassword implements UpdatesUserPasswords
{
use PasswordValidationRules;
/**
* Validate and update the user's password.
*
* @param array<string, string> $input
*/
public function update(User $user, array $input): void
{
Validator::make($input, [
'current_password' => ['required', 'string', 'current_password:web'],
'password' => $this->passwordRules(),
], [
'current_password.current_password' => __('The provided password does not match your current password.'),
])->validateWithBag('updatePassword');
$user->forceFill([
'password' => Hash::make($input['password']),
])->save();
}
}

View file

@ -0,0 +1,58 @@
<?php
namespace App\Actions\Fortify;
use App\Models\User;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Laravel\Fortify\Contracts\UpdatesUserProfileInformation;
class UpdateUserProfileInformation implements UpdatesUserProfileInformation
{
/**
* Validate and update the given user's profile information.
*
* @param array<string, string> $input
*/
public function update(User $user, array $input): void
{
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
'email' => [
'required',
'string',
'email',
'max:255',
Rule::unique('users')->ignore($user->id),
],
])->validateWithBag('updateProfileInformation');
if ($input['email'] !== $user->email &&
$user instanceof MustVerifyEmail) {
$this->updateVerifiedUser($user, $input);
} else {
$user->forceFill([
'name' => $input['name'],
'email' => $input['email'],
])->save();
}
}
/**
* Update the given verified user's profile information.
*
* @param array<string, string> $input
*/
protected function updateVerifiedUser(User $user, array $input): void
{
$user->forceFill([
'name' => $input['name'],
'email' => $input['email'],
'email_verified_at' => null,
])->save();
$user->sendEmailVerificationNotification();
}
}

View file

@ -0,0 +1,73 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
class GenerateDomainFavicons extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'domains:generate-favicons';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Generiere Favicons für alle konfigurierten Domains';
/**
* Execute the console command.
*/
public function handle()
{
$domains = config('domains');
$faviconDir = public_path('img/favicons');
// Erstelle das Favicon-Verzeichnis, wenn es nicht existiert
if (! File::exists($faviconDir)) {
File::makeDirectory($faviconDir, 0755, true);
$this->info("Verzeichnis {$faviconDir} erstellt.");
}
// Erstelle eine Liste der Themes, für die wir Favicons erstellen müssen
$themes = [];
foreach ($domains as $domain => $config) {
if (is_array($config) && isset($config['theme'])) {
$themes[$config['theme']] = [
'domain' => $domain,
'name' => $config['name'] ?? 'Website',
'color' => $config['color_scheme']['primary'] ?? '#000000',
];
}
}
// Erstelle die Favicons
foreach ($themes as $theme => $data) {
$faviconPath = "{$faviconDir}/{$theme}-favicon.ico";
// Wenn die Datei bereits existiert, frage, ob sie überschrieben werden soll
if (File::exists($faviconPath) && ! $this->confirm("Favicon für '{$theme}' existiert bereits. Überschreiben?")) {
$this->info("Favicon für '{$theme}' übersprungen.");
continue;
}
// Hier könntest du mit einer externen Bibliothek einen Favicon erstellen
// Da dies aber komplex sein kann, erstellen wir erstmal nur Platzhalter-Dateien
File::put($faviconPath, '');
$this->info("Platzhalter-Favicon für '{$theme}' erstellt: {$faviconPath}");
$this->comment("Ersetze diese Datei mit einem echten Favicon für {$data['domain']} ({$data['name']})");
}
$this->info('Favicons wurden erfolgreich erstellt!');
$this->line('Denke daran, die Platzhalter-Dateien mit echten Favicons zu ersetzen.');
}
}

36
app/Console/Kernel.php Normal file
View file

@ -0,0 +1,36 @@
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
\App\Console\Commands\GenerateDomainFavicons::class,
];
/**
* Define the application's command schedule.
*/
protected function schedule(Schedule $schedule): void
{
// $schedule->command('inspire')->hourly();
}
/**
* Register the commands for the application.
*/
protected function commands(): void
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
}

113
app/Helpers/ThemeHelper.php Normal file
View file

@ -0,0 +1,113 @@
<?php
namespace App\Helpers;
class ThemeHelper
{
/**
* Get the logo path based on the current theme
*/
public static function getLogoPath(string $type = 'positive'): string
{
$theme = config('app.theme', 'b2in');
$logoMap = [
'b2in' => [
'positive' => 'img/logos/b2in-logo-positive.svg',
'negative' => 'img/logos/b2in-logo-negative.svg'
],
'b2a' => [
'positive' => 'img/logos/b2a-logo-positiv.svg',
'negative' => 'img/logos/b2a-logo-negativ.svg'
],
'stileigentum' => [
'positive' => 'img/logos/stileigentum-logo-positiv.svg',
'negative' => 'img/logos/stileigentum-logo-negativ.svg'
],
'style2own' => [
'positive' => 'img/logos/style2own-logo-positiv.svg',
'negative' => 'img/logos/style2own-logo-negativ.svg'
]
];
return $logoMap[$theme][$type] ?? $logoMap['b2in'][$type];
}
/**
* Get the favicon path based on the current theme
*/
public static function getFaviconPath(): string
{
$theme = config('app.theme', 'b2in');
return "img/favicons/{$theme}-favicon.ico";
}
/**
* Get the CSS theme file based on the current theme
*/
public static function getThemeCssPath(): string
{
$theme = config('app.theme', 'b2in');
return "resources/css/web/theme-{$theme}.css";
}
/**
* Get domain-specific configuration
*/
public static function getDomainConfig(): array
{
$theme = config('app.theme', 'b2in');
return config("domains.domains.{$theme}", []);
}
/**
* Get primary color for the current theme
*/
public static function getPrimaryColor(): string
{
$config = self::getDomainConfig();
return $config['color_scheme']['primary'] ?? '#2b3f51';
}
/**
* Get secondary color for the current theme
*/
public static function getSecondaryColor(): string
{
$config = self::getDomainConfig();
return $config['color_scheme']['secondary'] ?? '#20a0da';
}
/**
* Get primary font family for the current theme
*/
public static function getPrimaryFont(): string
{
$config = self::getDomainConfig();
return $config['font_family']['primary'] ?? 'Inter';
}
/**
* Get secondary font family for the current theme
*/
public static function getSecondaryFont(): string
{
$config = self::getDomainConfig();
return $config['font_family']['secondary'] ?? 'IBM Plex Sans';
}
/**
* Get domain name for the current theme
*/
public static function getDomainName(): string
{
$config = self::getDomainConfig();
return $config['domain_name'] ?? 'B2IN';
}
public static function getDomainUrl(): string
{
$config = self::getDomainConfig();
return $config['url'] ?? config('app.url');
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Auth\Events\Verified;
use Illuminate\Foundation\Auth\EmailVerificationRequest;
use Illuminate\Http\RedirectResponse;
class VerifyEmailController extends Controller
{
/**
* Mark the authenticated user's email address as verified.
*/
public function __invoke(EmailVerificationRequest $request): RedirectResponse
{
if ($request->user()->hasVerifiedEmail()) {
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
}
if ($request->user()->markEmailAsVerified()) {
/** @var \Illuminate\Contracts\Auth\MustVerifyEmail $user */
$user = $request->user();
event(new Verified($user));
}
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
}
}

View file

@ -0,0 +1,8 @@
<?php
namespace App\Http\Controllers;
abstract class Controller
{
//
}

View file

@ -0,0 +1,28 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class BasicAuthMiddleware
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
// Credentials from .env file
$user = config('auth.basic.user');
$pass = config('auth.basic.password');
if ($request->getUser() != $user || $request->getPassword() != $pass) {
return response('Unauthorized.', 401, ['WWW-Authenticate' => 'Basic']);
}
return $next($request);
}
}

View file

@ -0,0 +1,57 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class ThemeMiddleware
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
$host = $request->getHost();
$path = $request->path();
// Theme-Switching über Subdomains
if (str_contains($host, 'b2in.test')) {
config(['app.theme' => 'b2in']);
} elseif (str_contains($host, 'b2a.test')) {
config(['app.theme' => 'b2a']);
} elseif (str_contains($host, 'stileigentum.test')) {
config(['app.theme' => 'stileigentum']);
} elseif (str_contains($host, 'style2own.test')) {
config(['app.theme' => 'style2own']);
}
// Theme-Switching über URL-Parameter (für Testing)
if ($request->has('theme')) {
$theme = $request->get('theme');
if (in_array($theme, ['b2in', 'b2a', 'stileigentum', 'style2own'])) {
config(['app.theme' => $theme]);
}
}
// Theme-Switching über Pfade (für lokale Entwicklung ohne Domain-Setup)
if (str_starts_with($path, 'b2in/')) {
config(['app.theme' => 'b2in']);
$request->server->set('REQUEST_URI', '/' . substr($path, 5)); // Entferne 'b2in/' vom Pfad
} elseif (str_starts_with($path, 'b2a/')) {
config(['app.theme' => 'b2a']);
$request->server->set('REQUEST_URI', '/' . substr($path, 4)); // Entferne 'b2a/' vom Pfad
} elseif (str_starts_with($path, 'stileigentum/')) {
config(['app.theme' => 'stileigentum']);
$request->server->set('REQUEST_URI', '/' . substr($path, 13)); // Entferne 'stileigentum/' vom Pfad
} elseif (str_starts_with($path, 'style2own/')) {
config(['app.theme' => 'style2own']);
$request->server->set('REQUEST_URI', '/' . substr($path, 10)); // Entferne 'style2own/' vom Pfad
}
return $next($request);
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace App\Livewire\Actions;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
class Logout
{
/**
* Log the current user out of the application.
*/
public function __invoke()
{
Auth::guard('web')->logout();
Session::invalidate();
Session::regenerateToken();
return redirect('/');
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace App\Livewire\Web\Components;
use Livewire\Component;
class AboutHero extends Component
{
public function render()
{
return view('livewire.web.components.sections.about-hero');
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace App\Livewire\Web\Components\Demo;
use Livewire\Component;
class ButtonDemo extends Component
{
public function render()
{
return view('livewire.web.components.demo.button-demo');
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace App\Livewire\Web\Components\Demo;
use Livewire\Component;
class ColorDemo extends Component
{
public function render()
{
return view('livewire.web.components.demo.color-demo');
}
}

View file

@ -0,0 +1,24 @@
<?php
namespace App\Livewire\Web\Components\Demo;
use Livewire\Component;
class DemoSection extends Component
{
public $title;
public $description;
public $component;
public function mount($title, $description = null, $component = null)
{
$this->title = $title;
$this->description = $description;
$this->component = $component;
}
public function render()
{
return view('livewire.web.components.demo.demo-section');
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace App\Livewire\Web\Components\Demo;
use Livewire\Component;
class LogoDemo extends Component
{
public function render()
{
return view('livewire.web.components.demo.logo-demo');
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace App\Livewire\Web\Components\Demo;
use Livewire\Component;
class ThemeInfo extends Component
{
public function render()
{
return view('livewire.web.components.demo.theme-info');
}
}

View file

@ -0,0 +1,25 @@
<?php
namespace App\Livewire\Web\Components\Demo;
use Livewire\Component;
class ThemeSwitcher extends Component
{
public $domains = [];
public function mount()
{
$this->domains = config('domains.domains');
foreach ($this->domains as $key => $domain) {
if ($domain['domain_name'] == config('domains.domain_portal')) {
unset($this->domains[$key]);
}
}
}
public function render()
{
return view('livewire.web.components.demo.theme-switcher');
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class AboutHero extends Component
{
public $content = [];
public function mount()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.about_hero", []);
}
public function render()
{
return view('livewire.web.components.sections.about-hero');
}
}

View file

@ -0,0 +1,29 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class BrandWorlds extends Component
{
public $title;
public $subtitle;
public $worlds = [];
public $bg = 'bg-background';
public function mount($bg = 'bg-background')
{
$theme = config('app.theme', 'b2in');
$content = config("content.themes.{$theme}.brand_worlds");
$this->title = $content['title'] ?? '';
$this->subtitle = $content['subtitle'] ?? '';
$this->worlds = $content['worlds'] ?? [];
$this->bg = $bg;
}
public function render()
{
return view('livewire.web.components.sections.brand-worlds');
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class BrokerSection extends Component
{
public $content = [];
public function mount()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.broker_section", []);
}
public function render()
{
return view('livewire.web.components.sections.broker-section');
}
}

View file

@ -0,0 +1,25 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class CTASection extends Component
{
public $content = [];
public $bg = '';
public $section = '';
public function mount($bg = 'bg-secondary', $section = 'cta_section')
{
$this->section = $section;
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.{$this->section}", []);
$this->bg = $bg;
}
public function render()
{
return view('livewire.web.components.sections.c-t-a-section');
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class CommitmentSection extends Component
{
public $content = [];
public function mount()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.commitment_section", []);
}
public function render()
{
return view('livewire.web.components.sections.commitment-section');
}
}

View file

@ -0,0 +1,27 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class ContentSection extends Component
{
public $content = [];
public $layout = 'left'; // Standard-Layout
public $bg = 'bg-background';
public $section = 'content_section_left';
public function mount($layout = 'left', $bg = 'bg-background', $section = 'content_section')
{
$this->layout = $layout;
$this->bg = $bg;
$this->section = $section;
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.{$this->section}", []);
}
public function render()
{
return view('livewire.web.components.sections.content-section');
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class DarkStatsSection extends Component
{
public $content = [];
public function mount()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.dark_stats_section", []);
}
public function render()
{
return view('livewire.web.components.sections.dark-stats-section');
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class DigitalCore extends Component
{
public $content = [];
public function mount()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.digital_core", []);
}
public function render()
{
return view('livewire.web.components.sections.digital-core');
}
}

View file

@ -0,0 +1,26 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class EcosystemCore extends Component
{
public $content = [];
public $bg = 'bg-background';
public $section = 'ecosystem_core';
public function mount($bg = 'bg-background', $section = 'ecosystem_core')
{
$this->bg = $bg;
$this->section = $section;
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.{$this->section}", []);
}
public function render()
{
return view('livewire.web.components.sections.ecosystem-core');
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class EcosystemHero extends Component
{
public $content = [];
public function mount()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.ecosystem_hero", []);
}
public function render()
{
return view('livewire.web.components.sections.ecosystem-hero');
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class EcosystemStats extends Component
{
public $content = [];
public function mount()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.ecosystem_stats", []);
}
public function render()
{
return view('livewire.web.components.sections.ecosystem-stats');
}
}

View file

@ -0,0 +1,25 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class EndCustomerSection extends Component
{
public $content = [];
public $bg = '';
public $section = '';
public function mount($bg = 'bg-accent', $section = 'end_customer_section')
{
$theme = config('app.theme', 'b2in');
$this->section = $section;
$this->bg = $bg;
$this->content = config("content.themes.{$theme}.end_customer_section", []);
}
public function render()
{
return view('livewire.web.components.sections.end-customer-section');
}
}

View file

@ -0,0 +1,26 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class FAQ extends Component
{
public $content = [];
public $bg = '';
public $section = '';
public function mount($bg = 'bg-background', $section = 'faq')
{
$this->bg = $bg;
$this->section = $section;
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.{$this->section}", []);
}
public function render()
{
return view('livewire.web.components.sections.f-a-q');
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class FinalCommitment extends Component
{
public $content = [];
public function mount()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.final_commitment", []);
}
public function render()
{
return view('livewire.web.components.sections.final-commitment');
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class Hero extends Component
{
public $content = [];
public function mount()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.hero", []);
}
public function render()
{
return view('livewire.web.components.sections.hero');
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class HeroImage extends Component
{
public $content = [];
public function mount()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.hero_image", []);
}
public function render()
{
return view('livewire.web.components.sections.hero-image');
}
}

View file

@ -0,0 +1,44 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class HeroSlider extends Component
{
public $content = [];
public $currentSlide = 0;
public function mount()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.hero_slider", []);
}
public function nextSlide()
{
if (!empty($this->content['slides'])) {
$this->currentSlide = ($this->currentSlide + 1) % count($this->content['slides']);
}
}
public function previousSlide()
{
if (!empty($this->content['slides'])) {
$totalSlides = count($this->content['slides']);
$this->currentSlide = ($this->currentSlide - 1 + $totalSlides) % $totalSlides;
}
}
public function setSlide($index)
{
if (!empty($this->content['slides']) && $index >= 0 && $index < count($this->content['slides'])) {
$this->currentSlide = $index;
}
}
public function render()
{
return view('livewire.web.components.sections.hero-slider');
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class HeroTiles extends Component
{
public $content = [];
public function mount()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.hero_tiles", []);
}
public function render()
{
return view('livewire.web.components.sections.hero-tiles');
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class LeadershipTeam extends Component
{
public $content = [];
public function mount()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.leadership_team", []);
}
public function render()
{
return view('livewire.web.components.sections.leadership-team');
}
}

View file

@ -0,0 +1,56 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class MagazinDetail extends Component
{
public $articleId;
public $article;
public $relatedArticles;
public $content;
public function mount($id = 1)
{
$this->articleId = (int) $id;
$this->loadArticle();
$this->loadRelatedArticles();
$this->loadThemeContent();
}
private function loadArticle()
{
$articles = $this->getArticlesData();
$this->article = $articles[$this->articleId] ?? $articles[1];
}
private function loadRelatedArticles()
{
$articles = $this->getArticlesData();
$this->relatedArticles = collect($articles)
->filter(fn($article, $key) => $key != $this->articleId)
->take(2)
->values()
->toArray();
}
private function loadThemeContent()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.magazin_detail", []);
}
private function getArticlesData()
{
return config('content.articles', []);
}
public function render()
{
return view('livewire.web.components.sections.magazin-detail', [
'article' => $this->article,
'relatedArticles' => $this->relatedArticles
]);
}
}

View file

@ -0,0 +1,43 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class MagazinList extends Component
{
public $posts = [];
public $content = [];
public function mount()
{
$this->loadPosts();
$this->loadThemeContent();
}
private function loadPosts()
{
$articles = config('content.articles', []);
$this->posts = collect($articles)->map(function ($article) {
return [
'id' => $article['id'],
'title' => $article['title'],
'excerpt' => $article['content']['intro'], // Using intro as excerpt
'image' => $article['image'],
'date' => $article['date'],
'readTime' => $article['readTime'],
];
})->all();
}
private function loadThemeContent()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.magazin_list", []);
}
public function render()
{
return view('livewire.web.components.sections.magazin-list');
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class OurStory extends Component
{
public $content = [];
public function mount()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.our_story", []);
}
public function render()
{
return view('livewire.web.components.sections.our-story');
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class OurValues extends Component
{
public $content = [];
public function mount()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.our_values", []);
}
public function render()
{
return view('livewire.web.components.sections.our-values');
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class PartnerBenefits extends Component
{
public $content = [];
public function mount()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.partner_benefits", []);
}
public function render()
{
return view('livewire.web.components.sections.partner-benefits');
}
}

View file

@ -0,0 +1,25 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class PartnerCTA extends Component
{
public $content = [];
public $bg = '';
public $section = '';
public function mount($bg = 'bg-secondary', $section = 'partner_cta')
{
$this->section = $section;
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.{$this->section}", []);
$this->bg = $bg;
}
public function render()
{
return view('livewire.web.components.sections.partner-c-t-a');
}
}

View file

@ -0,0 +1,45 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class PartnerHero extends Component
{
public $partnerTypes = [
[
'title' => 'Makler',
'description' => 'Lifetime-Vergütung',
'icon' => 'trending-up',
],
[
'title' => 'Lieferanten',
'description' => 'Globale Märkte',
'icon' => 'globe',
],
[
'title' => 'Partnership',
'description' => 'Faire Konditionen',
'icon' => 'handshake',
],
[
'title' => 'Erfolg',
'description' => 'Nachhaltiges Wachstum',
'icon' => 'award',
],
];
public $content = [];
public function mount()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.partner_hero", []);
}
public function render()
{
return view('livewire.web.components.sections.partner-hero', [
'partnerTypes' => $this->partnerTypes
]);
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class PartnerProcess extends Component
{
public $content = [];
public function mount()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.partner_process", []);
}
public function render()
{
return view('livewire.web.components.sections.partner-process');
}
}

View file

@ -0,0 +1,66 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class Portfolio extends Component
{
public string $activeFilter = 'alle';
public ?array $selectedProject = null;
public bool $showModal = false;
public $content = [];
public $theme = '';
public $filters = [];
public function mount()
{
$filters = [
'alle' => 'Alle',
'villen' => 'Villen',
'penthouse' => 'Penthouse',
'loft' => 'Loft'
];
$this->filters = $filters;
$this->activeFilter = 'alle';
$this->theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$this->theme}.portfolio", []);
$this->filters = config("content.themes.{$this->theme}.portfolio.filters", $filters);
}
public function filterBy(string $category): void
{
$this->activeFilter = $category;
}
public function openModal(array $project): void
{
$this->selectedProject = $project;
$this->showModal = true;
}
public function closeModal(): void
{
$this->showModal = false;
$this->selectedProject = null;
}
public function getFilteredProjects(): array
{
$projects = config("content.themes.{$this->theme}.portfolio.projects", []);
if ($this->activeFilter === 'alle') {
return $projects;
}
return array_filter($projects, function ($project) {
return strtolower($project['category']) === strtolower($this->activeFilter);
});
}
public function render()
{
return view('livewire.web.components.sections.portfolio');
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class SpotlightsSection extends Component
{
public $content = [];
public function mount()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.spotlights_section", []);
}
public function render()
{
return view('livewire.web.components.sections.spotlights-section');
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class SupplierSection extends Component
{
public $content = [];
public function mount()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.supplier_section", []);
}
public function render()
{
return view('livewire.web.components.sections.supplier-section');
}
}

View file

@ -0,0 +1,23 @@
<?php
namespace App\Livewire\Web\Components\Sections;
use Livewire\Component;
class VisionSection extends Component
{
public $content = [];
public $bg = 'bg-background';
public function mount($bg = 'bg-background')
{
$this->bg = $bg;
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.vision_section", []);
}
public function render()
{
return view('livewire.web.components.sections.vision-section');
}
}

View file

@ -0,0 +1,65 @@
<?php
namespace App\Livewire\Web\Components\Ui;
use Livewire\Component;
class ContactForm extends Component
{
public $firstName = '';
public $lastName = '';
public $company = '';
public $email = '';
public $phone = '';
public $subject = '';
public $message = '';
public $content = [];
public function mount()
{
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.contact_form", []);
}
public function getSubjectsProperty()
{
return $this->content['form']['subjects'] ?? [];
}
public function getContactInfoProperty()
{
return $this->content['contact_info'] ?? [];
}
public function getSocialMediaProperty()
{
return $this->content['social_media']['platforms'] ?? [];
}
public function submit()
{
$this->validate([
'firstName' => 'required|string|max:255',
'lastName' => 'required|string|max:255',
'email' => 'required|email|max:255',
'subject' => 'required|string',
'message' => 'required|string|max:2000',
'company' => 'nullable|string|max:255',
'phone' => 'nullable|string|max:255',
]);
// Here you would typically save to database or send email
// For now, we'll just show a success message
$successMessage = $this->content['form']['success_message'] ?? 'Vielen Dank für Ihre Nachricht! Wir werden uns schnellstmöglich bei Ihnen melden.';
session()->flash('message', $successMessage);
$this->reset(['firstName', 'lastName', 'company', 'email', 'phone', 'subject', 'message']);
}
public function render()
{
return view('livewire.web.components.ui.contact-form');
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace App\Livewire\Web\Components\Ui;
use Livewire\Component;
class Footer extends Component
{
public function render()
{
return view('livewire.web.components.ui.footer');
}
}

View file

@ -0,0 +1,57 @@
<?php
namespace App\Livewire\Web\Components\Ui;
use Livewire\Component;
class Header extends Component
{
public $isMobileMenuOpen = false;
public $domainName;
public $domainUrl;
public $content = [];
public function mount()
{
$this->domainName = \App\Helpers\ThemeHelper::getDomainName();
$this->domainUrl = \App\Helpers\ThemeHelper::getDomainUrl();
$theme = config('app.theme', 'b2in');
$this->content = config("content.themes.{$theme}.header", []);
}
public function toggleMobileMenu()
{
$this->isMobileMenuOpen = ! $this->isMobileMenuOpen;
}
public function closeMobileMenu()
{
$this->isMobileMenuOpen = false;
}
public function isActiveRoute($url)
{
$currentPath = request()->path();
$currentPath = '/' . ltrim($currentPath, '/');
// Exact match
if ($currentPath === $url) {
return true;
}
// Special handling for home route
if ($url === '/' && ($currentPath === '/' || $currentPath === '')) {
return true;
}
// Check if current path starts with the navigation URL (for sub-pages)
if ($url !== '/' && str_starts_with($currentPath, rtrim($url, '/'))) {
return true;
}
return false;
}
public function render()
{
return view('livewire.web.components.ui.header');
}
}

View file

@ -0,0 +1,62 @@
<?php
namespace App\Livewire\Web\Components\Ui;
use Livewire\Component;
use Illuminate\Support\Facades\Log;
class TopBar extends Component
{
public $currentLocale;
public $availableLocales = [
'de' => 'Deutsch',
'en' => 'English',
//'fr' => 'Français',
//'es' => 'Español',
];
public $localeFlags = [
'de' => '🇩🇪',
'en' => '🇬🇧',
];
public $domainName;
public function mount()
{
Log::info('Mounting TopBar');
$sessionLocale = session('locale');
if ($sessionLocale && array_key_exists($sessionLocale, $this->availableLocales)) {
app()->setLocale($sessionLocale);
$this->currentLocale = $sessionLocale;
} else {
$this->currentLocale = app()->getLocale();
}
$this->domainName = \App\Helpers\ThemeHelper::getDomainName();
}
public function switchLanguage($locale)
{
Log::info('Switching language to: ' . $locale);
if (array_key_exists($locale, $this->availableLocales)) {
app()->setLocale($locale);
session(['locale' => $locale]);
$this->currentLocale = $locale;
Log::info('Language switched successfully to: ' . $locale);
// Wichtig: In Livewire zeigt request()->url() auf /livewire/update.
// Für einen Seiten-Reload müssen wir den Browser-Referer nutzen.
$referer = request()->header('Referer') ?? '/';
return redirect()->to($referer);
}
Log::warning('Invalid locale attempted: ' . $locale);
}
public function render()
{
return view('livewire.web.components.ui.top-bar');
}
}

63
app/Models/User.php Normal file
View file

@ -0,0 +1,63 @@
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Str;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Laravel\Sanctum\HasApiTokens;
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable
{
/** @use HasFactory<\Database\Factories\UserFactory> */
use HasApiTokens, HasFactory, HasRoles, Notifiable, TwoFactorAuthenticatable;
/**
* The attributes that are mass assignable.
*
* @var list<string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var list<string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
/**
* Get the user's initials
*/
public function initials(): string
{
return Str::of($this->name)
->explode(' ')
->map(fn (string $name) => Str::of($name)->substr(0, 1))
->implode('');
}
}

View file

@ -0,0 +1,24 @@
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
//
}
}

View file

@ -0,0 +1,52 @@
<?php
namespace App\Providers;
use App\Actions\Fortify\CreateNewUser;
use App\Actions\Fortify\ResetUserPassword;
use App\Actions\Fortify\UpdateUserPassword;
use App\Actions\Fortify\UpdateUserProfileInformation;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
use Laravel\Fortify\Actions\RedirectIfTwoFactorAuthenticatable;
use Laravel\Fortify\Fortify;
use Livewire\Volt\Volt;
class FortifyServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
// Nur die notwendigen Fortify-Aktionen behalten
Fortify::createUsersUsing(CreateNewUser::class);
Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class);
Fortify::updateUserPasswordsUsing(UpdateUserPassword::class);
Fortify::resetUserPasswordsUsing(ResetUserPassword::class);
Fortify::redirectUserForTwoFactorAuthenticationUsing(RedirectIfTwoFactorAuthenticatable::class);
// Keine View-Definitionen, da wir Volt verwenden
RateLimiter::for('login', function (Request $request) {
$throttleKey = Str::transliterate(Str::lower($request->input(Fortify::username())).'|'.$request->ip());
return Limit::perMinute(5)->by($throttleKey);
});
RateLimiter::for('two-factor', function (Request $request) {
return Limit::perMinute(5)->by($request->session()->get('login.id'));
});
}
}

View file

@ -0,0 +1,77 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Vite;
use Illuminate\Support\ServiceProvider;
class ThemeServiceProvider extends ServiceProvider
{
/**
* Register services.
*/
public function register(): void
{
// Registriere die domains.php Konfigurationsdatei
$this->mergeConfigFrom(
base_path('config/domains.php'),
'domains'
);
}
/**
* Bootstrap services.
*/
public function boot(): void
{
$host = Request::getHost(); // is domain_name
$themeOverride = Request::get('theme'); // Allow theme override via URL parameter
// Standard-Werte für Domain, die nicht in der Konfiguration sind
$domainConfig = [
'name' => config('app.name'),
'theme' => 'b2in',
'view_prefix' => 'b2in',
'assets_dir' => 'build/b2in',
'url' => config('app.url'),
'domain_name' => config('app.domain_name'),
];
// Lade die Domain-Konfiguration
$confiDomains = config('domains.domains');
// Suche nach der aktuellen Domain in der Konfiguration
foreach ($confiDomains as $name => $config) {
if (is_array($config) && isset($config['domain_name']) && $config['domain_name'] === $host) {
$domainConfig = array_merge($domainConfig, $config);
break;
}
}
// Allow theme override via URL parameter (for testing)
if ($themeOverride && isset($confiDomains[$themeOverride])) {
$domainConfig = array_merge($domainConfig, $confiDomains[$themeOverride]);
}
// Grundlegende Konfiguration im Anwendungskontext verfügbar machen
config([
'app.theme' => $domainConfig['theme'],
'app.view_prefix' => $domainConfig['view_prefix'],
'app.domain_name' => $domainConfig['domain_name'],
'app.url' => $domainConfig['url'],
]);
// Spezifischere Daten für die Views verfügbar machen
View::share('theme', $domainConfig['theme']);
View::share('viewPrefix', $domainConfig['view_prefix']);
View::share('domainName', $domainConfig['domain_name']);
View::share('domainConfig', $domainConfig);
View::share('domainUrl', $domainConfig['url']);
// Vite-Assets-Konfiguration für die aktuelle Domain
if (! app()->runningInConsole() && isset($domainConfig['assets_dir'])) {
Vite::useBuildDirectory($domainConfig['assets_dir']);
}
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Livewire\Volt\Volt;
class VoltServiceProvider extends ServiceProvider
{
/**
* Register services.
*/
public function register(): void
{
//
}
/**
* Bootstrap services.
*/
public function boot(): void
{
Volt::mount([
config('livewire.view_path', resource_path('views/livewire')),
resource_path('views/pages'),
]);
}
}