diff --git a/app/Livewire/Modals/AddDomain.php b/app/Livewire/Modals/AddDomain.php index 32831bb..3c3ab35 100644 --- a/app/Livewire/Modals/AddDomain.php +++ b/app/Livewire/Modals/AddDomain.php @@ -41,9 +41,8 @@ class AddDomain extends ModalComponent { $this->validate(); - $workspace = $this->workspaceId - ? Workspace::findOrFail($this->workspaceId) - : require_workspace(); + abort_if($this->workspaceId === 0, 404); + $workspace = Workspace::findOrFail($this->workspaceId); (new CreateDomain)->handle($workspace, $this->hostname); diff --git a/app/Livewire/Modals/CreateBioPage.php b/app/Livewire/Modals/CreateBioPage.php index eddffcd..be76c74 100644 --- a/app/Livewire/Modals/CreateBioPage.php +++ b/app/Livewire/Modals/CreateBioPage.php @@ -59,7 +59,7 @@ class CreateBioPage extends ModalComponent { $this->validate(); - $wsId = $this->workspaceId ?: require_workspace()->id; + abort_if($this->workspaceId === 0, 404); $wsId = $this->workspaceId; if (empty($this->slug)) { $this->slug = Str::slug($this->title).'-'.Str::random(4); diff --git a/app/Livewire/Modals/CreateWebhook.php b/app/Livewire/Modals/CreateWebhook.php index d78db7d..03e6ea2 100644 --- a/app/Livewire/Modals/CreateWebhook.php +++ b/app/Livewire/Modals/CreateWebhook.php @@ -58,7 +58,7 @@ class CreateWebhook extends ModalComponent { $this->validate(); - $wsId = $this->workspaceId ?: require_workspace()->id; + abort_if($this->workspaceId === 0, 404); $wsId = $this->workspaceId; Webhook::create([ 'workspace_id' => $wsId, diff --git a/app/Livewire/Modals/InviteMember.php b/app/Livewire/Modals/InviteMember.php index b02a91d..65f6b21 100644 --- a/app/Livewire/Modals/InviteMember.php +++ b/app/Livewire/Modals/InviteMember.php @@ -45,7 +45,7 @@ class InviteMember extends ModalComponent { $this->validate(); - $wsId = $this->workspaceId ?: require_workspace()->id; + abort_if($this->workspaceId === 0, 404); $wsId = $this->workspaceId; WorkspaceInvitation::create([ 'workspace_id' => $wsId, diff --git a/app/Livewire/Pages/QrCodes/Create.php b/app/Livewire/Pages/QrCodes/Create.php new file mode 100644 index 0000000..7b0db5d --- /dev/null +++ b/app/Livewire/Pages/QrCodes/Create.php @@ -0,0 +1,249 @@ +workspaceUlid = require_workspace()->ulid; + } + + public function updated(string $prop): void + { + $this->dispatch('qr-preview-update', + payload: $this->buildPayload(), + style: [ + 'fg' => $this->fgColor, + 'bg' => $this->bgColor, + 'dotStyle' => $this->dotStyle, + 'cornerSquare' => $this->cornerSquare, + 'cornerDot' => $this->cornerDot, + 'margin' => $this->margin, + 'ec' => $this->errorCorrection, + 'logoUrl' => $this->logoUrl ?: null, + 'useGradient' => $this->gradientEnabled, + 'gradStart' => $this->gradStart, + 'gradEnd' => $this->gradEnd, + 'logoSize' => $this->logoSize / 100, + 'logoMargin' => $this->logoMargin, + 'hideDots' => $this->hideDots, + ] + ); + } + + public function buildPayload(): string + { + return match ($this->type) { + 'email' => 'mailto:'.trim($this->emailTo).'?subject='.urlencode($this->emailSubject).'&body='.urlencode($this->emailBody), + 'phone' => 'tel:'.trim($this->url), + 'sms' => 'SMSTO:'.trim($this->smsTo).':'.$this->smsBody, + 'vcard' => $this->buildVcard(), + 'wifi' => $this->buildWifi(), + 'text' => $this->textContent, + default => $this->normalizeUrl(trim($this->url)), + }; + } + + private function buildVcard(): string + { + return implode("\n", array_filter([ + 'BEGIN:VCARD', + 'VERSION:3.0', + $this->vcardName ? "FN:{$this->vcardName}" : null, + $this->vcardPhone ? "TEL:{$this->vcardPhone}" : null, + $this->vcardEmail ? "EMAIL:{$this->vcardEmail}" : null, + $this->vcardCompany ? "ORG:{$this->vcardCompany}" : null, + $this->vcardWebsite ? "URL:{$this->vcardWebsite}" : null, + 'END:VCARD', + ])); + } + + private function buildWifi(): string + { + $enc = $this->wifiEncryption ?: 'nopass'; + $hidden = $this->wifiHidden ? 'true' : 'false'; + + return "WIFI:T:{$enc};S:{$this->wifiSsid};P:{$this->wifiPassword};H:{$hidden};;"; + } + + private function normalizeUrl(string $url): string + { + if ($url && !preg_match('/^https?:\/\//i', $url)) { + return 'https://' . $url; + } + + return $url; + } + + public function save(): void + { + $urlRule = match ($this->type) { + 'url' => 'required|url|max:2048', + 'phone' => 'required|max:50', + 'email' => 'nullable', + default => 'nullable', + }; + + $this->validate([ + 'type' => 'required|in:url,vcard,wifi,text,email,phone,sms', + 'label' => 'nullable|max:200', + 'url' => $urlRule, + 'emailTo' => $this->type === 'email' ? 'required|email|max:200' : 'nullable', + 'smsTo' => $this->type === 'sms' ? 'required|max:50' : 'nullable', + 'vcardName' => $this->type === 'vcard' ? 'required|max:200' : 'nullable', + 'wifiSsid' => $this->type === 'wifi' ? 'required|max:200' : 'nullable', + 'fgColor' => ['required', 'regex:/^#[0-9a-fA-F]{6}$/'], + 'bgColor' => ['required', 'regex:/^#[0-9a-fA-F]{6}$/'], + 'logoUrl' => 'nullable|url|max:2048', + 'errorCorrection' => 'in:L,M,Q,H', + 'frame' => 'in:none,label-bottom,label-top', + 'gradStart' => ['nullable', 'regex:/^#[0-9a-fA-F]{6}$/'], + 'gradEnd' => ['nullable', 'regex:/^#[0-9a-fA-F]{6}$/'], + ]); + + $workspace = Workspace::where('ulid', $this->workspaceUlid)->firstOrFail(); + + $dbType = match ($this->type) { + 'email', 'phone', 'sms' => 'url', + default => $this->type, + }; + + QrCode::create([ + 'workspace_id' => $workspace->id, + 'type' => $dbType, + 'payload' => [ + 'data' => $this->buildPayload(), + 'label' => $this->label ?: null, + 'display_type' => $this->type, + ], + 'style' => [ + 'fg_color' => $this->fgColor, + 'bg_color' => $this->bgColor, + 'logo_url' => $this->logoUrl ?: null, + 'dot_style' => $this->dotStyle, + 'corner_square' => $this->cornerSquare, + 'corner_dot' => $this->cornerDot, + 'size' => $this->size, + 'margin' => $this->margin, + 'error_correction' => $this->errorCorrection, + 'gradient_enabled' => $this->gradientEnabled, + 'grad_start' => $this->gradStart, + 'grad_end' => $this->gradEnd, + 'logo_size' => $this->logoSize, + 'logo_margin' => $this->logoMargin, + 'hide_dots' => $this->hideDots, + 'frame' => $this->frame, + 'frame_text' => $this->frameText ?: null, + 'frame_color' => $this->frameColor, + ], + 'is_dynamic' => $this->isDynamic, + 'scan_limit' => $this->scanLimit ?: null, + 'expires_at' => $this->expiresAt ?: null, + ]); + + $this->dispatch('toast', message: 'QR-Code erstellt', type: 'success'); + $this->redirect(route('w.qr.index', $workspace->ulid), navigate: true); + } + + public function render(): View + { + return view('livewire.pages.qr-codes.create') + ->layout('layouts.nimuli-app', ['title' => 'QR Code erstellen']); + } +} diff --git a/app/Livewire/Pages/Team/Members.php b/app/Livewire/Pages/Team/Members.php index c298928..1587ed5 100644 --- a/app/Livewire/Pages/Team/Members.php +++ b/app/Livewire/Pages/Team/Members.php @@ -2,18 +2,24 @@ namespace App\Livewire\Pages\Team; +use App\Domains\Workspace\Models\Workspace; use App\Domains\Workspace\Models\WorkspaceMember; use Illuminate\View\View; use Livewire\Component; class Members extends Component { + public int $workspaceId = 0; + + public function mount(): void + { + $this->workspaceId = require_workspace()->id; + } + public function render(): View { - $workspace = require_workspace(); - $members = WorkspaceMember::with('user') - ->where('workspace_id', $workspace->id) + ->where('workspace_id', $this->workspaceId) ->get(); return view('livewire.pages.team.members', compact('members'))