134 lines
3.9 KiB
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');
|
|
}
|
|
}
|