192 lines
12 KiB
PHP
192 lines
12 KiB
PHP
<?php use Illuminate\Support\Number; ?>
|
|
{{-- R24: the longest modal in the console — every plan, every add-on, a note
|
|
field. It was well past the bottom of the window and took its own grant
|
|
button with it. --}}
|
|
<x-ui.modal>
|
|
<x-slot:header>
|
|
{{-- Its own header: whose account this is belongs beside the title, and
|
|
the plain `title` prop cannot say two things. --}}
|
|
<div class="flex items-center justify-between gap-3">
|
|
<h3 class="text-base font-semibold text-ink">{{ __('admin.grant.title') }}</h3>
|
|
<span class="rounded bg-surface-2 px-2 py-0.5 text-xs text-muted">{{ $customerName }}</span>
|
|
</div>
|
|
</x-slot:header>
|
|
|
|
@if (count($grants) > 0)
|
|
<div class="mt-4 rounded-lg border border-line bg-surface-2 p-3">
|
|
<p class="text-xs font-semibold uppercase tracking-wider text-muted">{{ __('admin.grant.existing_title') }}</p>
|
|
<ul class="mt-2 space-y-2">
|
|
@foreach ($grants as $g)
|
|
@php
|
|
$lapsed = $g['granted_until'] && $g['granted_until']->isPast();
|
|
$nearingEnd = ! $lapsed && $g['granted_until'] && now()->diffInDays($g['granted_until'], false) <= 30;
|
|
@endphp
|
|
<li class="rounded-md bg-surface px-3 py-2 text-sm">
|
|
<div class="flex items-center justify-between gap-2">
|
|
<span class="inline-flex items-center gap-1.5 font-medium text-ink">
|
|
<x-ui.icon :name="$g['kind'] === 'package' ? 'box' : 'plug'" class="size-3.5 text-muted" />
|
|
{{ $g['label'] }}
|
|
</span>
|
|
<span class="font-mono text-xs text-muted">
|
|
{{ $g['price_cents'] === 0 ? __('admin.grant.free') : Number::currency($g['price_cents'] / 100, in: 'EUR', locale: app()->getLocale()) }}
|
|
</span>
|
|
</div>
|
|
<p class="mt-0.5 text-xs text-muted">
|
|
{{ __('admin.grant.by_on', ['who' => $g['granted_by'], 'when' => $g['granted_at']->local()->isoFormat('D. MMM YYYY')]) }}
|
|
</p>
|
|
@if ($g['note'])
|
|
<p class="mt-0.5 text-xs italic text-muted">{{ $g['note'] }}</p>
|
|
@endif
|
|
@if ($g['granted_until'])
|
|
<p @class([
|
|
'mt-1.5 inline-flex items-center gap-1 rounded-pill px-2 py-0.5 text-xs font-medium',
|
|
'bg-danger-bg text-danger' => $lapsed,
|
|
'bg-warning-bg text-warning' => $nearingEnd,
|
|
'bg-surface-2 text-muted' => ! $lapsed && ! $nearingEnd,
|
|
])>
|
|
<x-ui.icon name="calendar" class="size-3.5" />
|
|
{{ __('admin.grant.until', ['date' => $g['granted_until']->local()->isoFormat('D. MMM YYYY')]) }}
|
|
@if ($lapsed)
|
|
· {{ __('admin.grant.lapsed') }}
|
|
@elseif ($nearingEnd)
|
|
· {{ __('admin.grant.ending_soon') }}
|
|
@endif
|
|
</p>
|
|
@else
|
|
<p class="mt-1.5 text-xs text-muted">{{ __('admin.grant.unlimited') }}</p>
|
|
@endif
|
|
</li>
|
|
@endforeach
|
|
</ul>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="mt-5">
|
|
@if ($hasSubscription)
|
|
<div class="flex rounded-md border border-line-strong p-1 text-sm">
|
|
<button type="button" wire:click="$set('kind', 'package')"
|
|
class="flex-1 rounded px-3 py-1.5 font-medium {{ $kind === 'package' ? 'bg-accent text-white' : 'text-muted' }}">
|
|
{{ __('admin.grant.kind_package') }}
|
|
</button>
|
|
<button type="button" wire:click="$set('kind', 'addon')"
|
|
class="flex-1 rounded px-3 py-1.5 font-medium {{ $kind === 'addon' ? 'bg-accent text-white' : 'text-muted' }}">
|
|
{{ __('admin.grant.kind_addon') }}
|
|
</button>
|
|
</div>
|
|
@endif
|
|
|
|
@if ($kind === 'package')
|
|
<div class="mt-4 space-y-4">
|
|
<div>
|
|
<label class="text-sm font-medium text-body" for="grant-plan">{{ __('admin.grant.plan') }}</label>
|
|
<select id="grant-plan" wire:model.live="plan" class="mt-1.5 w-full rounded-md border border-line-strong bg-surface px-3 py-2 text-sm text-body">
|
|
<option value="">{{ __('admin.grant.choose') }}</option>
|
|
@foreach ($plans as $key => $p)
|
|
<option value="{{ $key }}">{{ $p['name'] }}</option>
|
|
@endforeach
|
|
</select>
|
|
@error('plan')<p class="mt-1 text-xs text-danger">{{ $message }}</p>@enderror
|
|
</div>
|
|
<div class="grid grid-cols-2 gap-3">
|
|
<div>
|
|
<label class="text-sm font-medium text-body" for="grant-term">{{ __('admin.grant.term') }}</label>
|
|
<select id="grant-term" wire:model.live="term" class="mt-1.5 w-full rounded-md border border-line-strong bg-surface px-3 py-2 text-sm text-body">
|
|
<option value="monthly">{{ __('admin.grant.monthly') }}</option>
|
|
<option value="yearly">{{ __('admin.grant.yearly') }}</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="text-sm font-medium text-body" for="grant-datacenter">{{ __('admin.grant.datacenter') }}</label>
|
|
<select id="grant-datacenter" wire:model="datacenter" class="mt-1.5 w-full rounded-md border border-line-strong bg-surface px-3 py-2 text-sm text-body">
|
|
@foreach ($datacenters as $code => $name)
|
|
<option value="{{ $code }}">{{ $name }} ({{ $code }})</option>
|
|
@endforeach
|
|
</select>
|
|
@error('datacenter')<p class="mt-1 text-xs text-danger">{{ $message }}</p>@enderror
|
|
</div>
|
|
</div>
|
|
|
|
<div class="rounded-lg border border-line bg-surface-2 p-3">
|
|
<p class="text-xs font-semibold uppercase tracking-wider text-muted">{{ __('admin.grant.bonus_title') }}</p>
|
|
<p class="mt-1 text-xs text-muted">{{ __('admin.grant.bonus_hint') }}</p>
|
|
<div class="mt-2 grid grid-cols-3 gap-3">
|
|
<div>
|
|
<label class="text-xs text-muted" for="grant-bonus-quota">{{ __('admin.grant.bonus_quota') }}</label>
|
|
<input id="grant-bonus-quota" type="number" min="0" wire:model="bonusQuotaGb" placeholder="0"
|
|
class="mt-1 w-full rounded-md border border-line-strong bg-surface px-2 py-1.5 text-sm text-ink" />
|
|
</div>
|
|
<div>
|
|
<label class="text-xs text-muted" for="grant-bonus-seats">{{ __('admin.grant.bonus_seats') }}</label>
|
|
<input id="grant-bonus-seats" type="number" min="0" wire:model="bonusSeats" placeholder="0"
|
|
class="mt-1 w-full rounded-md border border-line-strong bg-surface px-2 py-1.5 text-sm text-ink" />
|
|
</div>
|
|
<div>
|
|
<label class="text-xs text-muted" for="grant-bonus-traffic">{{ __('admin.grant.bonus_traffic') }}</label>
|
|
<input id="grant-bonus-traffic" type="number" min="0" wire:model="bonusTrafficGb" placeholder="0"
|
|
class="mt-1 w-full rounded-md border border-line-strong bg-surface px-2 py-1.5 text-sm text-ink" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@else
|
|
<div class="mt-4 space-y-4">
|
|
<div>
|
|
<label class="text-sm font-medium text-body" for="grant-addon">{{ __('admin.grant.addon') }}</label>
|
|
<select id="grant-addon" wire:model.live="addonKey" class="mt-1.5 w-full rounded-md border border-line-strong bg-surface px-3 py-2 text-sm text-body">
|
|
<option value="">{{ __('admin.grant.choose') }}</option>
|
|
@foreach (array_keys($addons) as $key)
|
|
<option value="{{ $key }}">{{ __('billing.addon.'.$key.'.name') }}</option>
|
|
@endforeach
|
|
</select>
|
|
@error('addonKey')<p class="mt-1 text-xs text-danger">{{ $message }}</p>@enderror
|
|
</div>
|
|
<div>
|
|
<label class="text-sm font-medium text-body" for="grant-quantity">{{ __('admin.grant.quantity') }}</label>
|
|
<input id="grant-quantity" type="number" min="1" wire:model="quantity"
|
|
class="mt-1.5 w-full rounded-md border border-line-strong bg-surface px-3 py-2 text-sm text-ink" />
|
|
@error('quantity')<p class="mt-1 text-xs text-danger">{{ $message }}</p>@enderror
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="mt-4 grid grid-cols-2 gap-3">
|
|
<div>
|
|
<label class="text-sm font-medium text-body" for="grant-price">{{ __('admin.grant.price') }}</label>
|
|
<div class="mt-1.5 flex items-center gap-2">
|
|
<input id="grant-price" type="text" inputmode="decimal" wire:model="priceEuros"
|
|
class="w-full rounded-md border border-line-strong bg-surface px-3 py-2 text-sm text-ink" />
|
|
<span class="text-sm text-muted">€</span>
|
|
</div>
|
|
@if ($cataloguePriceCents !== null)
|
|
<p class="mt-1 text-xs text-muted">
|
|
{{ __('admin.grant.catalogue_price', ['price' => Number::currency($cataloguePriceCents / 100, in: 'EUR', locale: app()->getLocale())]) }}
|
|
</p>
|
|
@endif
|
|
@error('priceEuros')<p class="mt-1 text-xs text-danger">{{ $message }}</p>@enderror
|
|
</div>
|
|
<div>
|
|
<label class="text-sm font-medium text-body" for="grant-until">{{ __('admin.grant.until_label') }}</label>
|
|
<input id="grant-until" type="date" wire:model="until"
|
|
class="mt-1.5 w-full rounded-md border border-line-strong bg-surface px-3 py-2 text-sm text-ink" />
|
|
<p class="mt-1 text-xs text-muted">{{ __('admin.grant.until_hint') }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-4">
|
|
<label class="text-sm font-medium text-body" for="grant-note">{{ __('admin.grant.note') }}</label>
|
|
<textarea id="grant-note" rows="2" wire:model="note"
|
|
class="mt-1.5 w-full rounded-md border border-line-strong bg-surface px-3 py-2 text-sm text-ink"></textarea>
|
|
@error('note')<p class="mt-1 text-xs text-danger">{{ $message }}</p>@enderror
|
|
</div>
|
|
</div>
|
|
|
|
<x-slot:footer>
|
|
<div class="flex justify-end gap-3">
|
|
<x-ui.button variant="secondary" x-on:click="Livewire.dispatch('closeModal')">{{ __('admin.grant.cancel') }}</x-ui.button>
|
|
<x-ui.button variant="primary" wire:click="grant" wire:loading.attr="disabled">
|
|
<x-ui.icon name="tag" class="size-4" />{{ __('admin.grant.submit') }}
|
|
</x-ui.button>
|
|
</div>
|
|
</x-slot:footer>
|
|
</x-ui.modal>
|