CluPilotCloud/app/Livewire/Settings.php

165 lines
5.4 KiB
PHP

<?php
namespace App\Livewire;
use App\Livewire\Concerns\ResolvesCustomer;
use Illuminate\Support\Facades\Storage;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Validate;
use Livewire\Component;
use Livewire\WithFileUploads;
class Settings extends Component
{
use \App\Livewire\Concerns\ChangesOwnPassword;
use ResolvesCustomer, WithFileUploads;
// Company / billing profile
#[Validate('required|string|max:255')]
public string $companyName = '';
#[Validate('nullable|string|max:255')]
public string $contactName = '';
#[Validate('nullable|string|max:64')]
public string $phone = '';
#[Validate('nullable|string|max:64')]
public string $vatId = '';
#[Validate('nullable|string|max:2000')]
public string $billingAddress = '';
// Branding
#[Validate('nullable|string|max:255')]
public string $brandDisplayName = '';
#[Validate('nullable|regex:/^#[0-9a-fA-F]{6}$/')]
public string $brandPrimary = '';
#[Validate('nullable|regex:/^#[0-9a-fA-F]{6}$/')]
public string $brandAccent = '';
/** New logo upload (validated on save). */
public $logo = null;
public ?string $brandLogoPath = null;
public function mount(): void
{
$c = $this->customer();
if ($c === null) {
return;
}
$this->companyName = $c->name ?? '';
$this->contactName = $c->contact_name ?? '';
$this->phone = $c->phone ?? '';
$this->vatId = $c->vat_id ?? '';
$this->billingAddress = $c->billing_address ?? '';
$this->brandDisplayName = $c->brand_display_name ?? '';
$this->brandPrimary = $c->brand_primary_color ?? '';
$this->brandAccent = $c->brand_accent_color ?? '';
$this->brandLogoPath = $c->brand_logo_path;
}
public function saveProfile(): void
{
$c = $this->requireCustomer();
if ($c === null) {
return;
}
$this->validateOnly('companyName');
$data = $this->validate([
'companyName' => 'required|string|max:255',
'contactName' => 'nullable|string|max:255',
'phone' => 'nullable|string|max:64',
'vatId' => 'nullable|string|max:64',
'billingAddress' => 'nullable|string|max:2000',
]);
$c->update([
'name' => $data['companyName'],
'contact_name' => $data['contactName'] ?: null,
'phone' => $data['phone'] ?: null,
'vat_id' => $data['vatId'] ?: null,
'billing_address' => $data['billingAddress'] ?: null,
]);
$this->dispatch('notify', message: __('settings.profile_saved'));
}
public function saveBranding(): void
{
$c = $this->requireCustomer();
if ($c === null) {
return;
}
$this->validate([
'brandDisplayName' => 'nullable|string|max:255',
'brandPrimary' => 'nullable|regex:/^#[0-9a-fA-F]{6}$/',
'brandAccent' => 'nullable|regex:/^#[0-9a-fA-F]{6}$/',
'logo' => 'nullable|image|mimes:png,webp|max:2048',
]);
// Store the new upload, but keep the old file until the DB row that
// references it is updated — delete the old one only after that commits,
// so a failed update never orphans a file or dangles a reference.
$oldToDelete = null;
if ($this->logo !== null) {
$oldToDelete = $this->brandLogoPath;
$this->brandLogoPath = $this->logo->store('branding', 'public');
$this->logo = null;
}
$c->update([
'brand_display_name' => $this->brandDisplayName ?: null,
'brand_primary_color' => $this->brandPrimary ?: null,
'brand_accent_color' => $this->brandAccent ?: null,
'brand_logo_path' => $this->brandLogoPath,
]);
if ($oldToDelete !== null && $oldToDelete !== $this->brandLogoPath) {
Storage::disk('public')->delete($oldToDelete);
}
$this->dispatch('notify', message: __('settings.branding_saved'));
}
public function removeLogo(): void
{
$c = $this->requireCustomer();
if ($c === null) {
return;
}
if ($this->brandLogoPath !== null) {
Storage::disk('public')->delete($this->brandLogoPath);
}
$this->brandLogoPath = null;
$c->update(['brand_logo_path' => null]);
$this->dispatch('notify', message: __('settings.branding_saved'));
}
#[Layout('layouts.portal-app')]
public function render()
{
$c = $this->customer();
// Prefer the active/cancelling instance for the package section so the
// controls line up with what cancellation actually targets.
$active = $c?->instances()->where('status', 'active')->latest('id')->first();
$scheduled = $c?->instances()->where('status', 'cancellation_scheduled')->latest('id')->first();
$instance = $active ?? $scheduled ?? $c?->instances()->latest('id')->first();
return view('livewire.settings', [
'customer' => $c,
'instance' => $instance,
'branding' => $c?->brandingResolved(),
'logoUrl' => $this->brandLogoPath ? Storage::disk('public')->url($this->brandLogoPath) : null,
'hasActivePackage' => $active !== null,
'cancellationScheduled' => $active === null && $scheduled !== null,
]);
}
}