nimuli/app/Livewire/Modals/LinkVariants.php

134 lines
3.9 KiB
PHP

<?php
namespace App\Livewire\Modals;
use App\Domains\Link\Models\Link;
use App\Domains\Link\Models\LinkVariant;
use Illuminate\View\View;
use LivewireUI\Modal\ModalComponent;
class LinkVariants extends ModalComponent
{
public string $linkUlid = '';
public int $linkId = 0;
public string $linkTarget = '';
/** @var array<int, array{id: int|null, label: string, target_url: string, weight: int}> */
public array $variants = [];
public function mount(string $linkUlid): void
{
$link = Link::where('ulid', $linkUlid)->firstOrFail();
$this->linkUlid = $linkUlid;
$this->linkId = $link->id;
$this->linkTarget = $link->target_url;
$existing = $link->variants()->orderBy('id')->get();
if ($existing->isEmpty()) {
$this->variants = [
['id' => null, 'label' => 'Variant A', 'target_url' => $link->target_url, 'weight' => 50],
['id' => null, 'label' => 'Variant B', 'target_url' => '', 'weight' => 50],
];
} else {
$this->variants = $existing->map(fn ($v) => [
'id' => $v->id,
'label' => $v->label,
'target_url' => $v->target_url,
'weight' => $v->weight,
])->toArray();
}
}
public function addVariant(): void
{
if (count($this->variants) >= 5) {
return;
}
$this->variants[] = [
'id' => null,
'label' => 'Variant ' . chr(64 + count($this->variants) + 1),
'target_url' => '',
'weight' => 0,
];
$this->rebalanceWeights();
}
public function removeVariant(int $index): void
{
if (count($this->variants) <= 2) {
return;
}
array_splice($this->variants, $index, 1);
$this->rebalanceWeights();
}
public function rebalanceWeights(): void
{
$count = count($this->variants);
if ($count === 0) {
return;
}
$base = intdiv(100, $count);
$remainder = 100 - ($base * $count);
foreach ($this->variants as $i => &$variant) {
$variant['weight'] = $base + ($i === 0 ? $remainder : 0);
}
unset($variant);
}
public function save(): void
{
$this->validate([
'variants' => 'required|array|min:2|max:5',
'variants.*.label' => 'required|string|max:64',
'variants.*.target_url' => 'required|url|max:2048',
'variants.*.weight' => 'required|integer|min:1|max:99',
]);
$totalWeight = array_sum(array_column($this->variants, 'weight'));
if ($totalWeight !== 100) {
$this->addError('variants', 'Weights must sum to 100%.');
return;
}
$existingIds = collect($this->variants)->pluck('id')->filter()->values()->all();
LinkVariant::where('link_id', $this->linkId)
->whereNotIn('id', $existingIds)
->delete();
foreach ($this->variants as $v) {
if ($v['id']) {
LinkVariant::where('id', $v['id'])->update([
'label' => $v['label'],
'target_url' => $v['target_url'],
'weight' => $v['weight'],
]);
} else {
LinkVariant::create([
'link_id' => $this->linkId,
'label' => $v['label'],
'target_url' => $v['target_url'],
'weight' => $v['weight'],
]);
}
}
$this->dispatch('toast', message: 'A/B variants saved', type: 'success');
$this->closeModal();
}
public static function modalMaxWidth(): string
{
return 'xl';
}
public function render(): View
{
return view('livewire.modals.link-variants');
}
}