feat(ui): standard modal shell — fixed header/footer, scrollable body, 2-col
Issue 3: the New-Automation modal was taller than the screen and the whole dialog scrolled. New <x-modal> component: header + footer are fixed (shrink-0), only the body scrolls, capped to max-h-85dvh so the page never scrolls. Refactored create-automation (now a responsive 2-column grid, widened to 3xl), add-device, ring-connect and add-person onto it — the standard for every modal going forward. Verified: modal fits a 720px viewport with no page scroll; create flow still works. 12/12 tabs clean, suite green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>feat/phase-1-bootstrap
parent
2fa15decb4
commit
f028528f1f
|
|
@ -34,7 +34,7 @@ class CreateAutomation extends ModalComponent
|
|||
|
||||
public static function modalMaxWidth(): string
|
||||
{
|
||||
return 'lg';
|
||||
return '3xl';
|
||||
}
|
||||
|
||||
/** @return array<int, array{value:string,label:string,controllable:bool}> */
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
@props(['title' => null, 'icon' => null])
|
||||
|
||||
{{-- Standard modal frame: header + footer stay fixed, only the body scrolls, capped to the
|
||||
viewport so a tall form never scrolls the whole dialog. Body content controls its own
|
||||
padding/columns. Works on phone, tablet and desktop. --}}
|
||||
<div class="flex flex-col max-h-[85dvh] w-full">
|
||||
<header class="flex items-center gap-3 px-5 py-4 border-b border-line-soft shrink-0">
|
||||
@if ($icon)
|
||||
<span class="grid place-items-center w-9 h-9 rounded-lg bg-accent/10 text-accent shrink-0"><x-icon :name="$icon" :size="18" /></span>
|
||||
@endif
|
||||
<h2 class="text-[15px] font-bold text-ink truncate">{{ $title }}</h2>
|
||||
<button type="button" wire:click="closeModal"
|
||||
class="ml-auto grid place-items-center w-9 h-9 rounded-lg text-ink-3 hover:bg-raised hover:text-ink transition-colors shrink-0"
|
||||
aria-label="{{ __('common.close') }}">
|
||||
<x-icon name="close" :size="18" />
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<div class="flex-1 overflow-y-auto min-h-0">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
|
||||
@isset($footer)
|
||||
<footer class="flex items-center justify-end gap-2 px-5 py-3.5 border-t border-line-soft shrink-0">
|
||||
{{ $footer }}
|
||||
</footer>
|
||||
@endisset
|
||||
</div>
|
||||
|
|
@ -1,50 +1,44 @@
|
|||
<div class="flex flex-col">
|
||||
<header class="flex items-center gap-3 px-5 py-4 border-b border-line-soft">
|
||||
<span class="grid place-items-center w-9 h-9 rounded-lg bg-accent/10 text-accent shrink-0"><x-icon name="devices" :size="18" /></span>
|
||||
<h2 class="text-[15px] font-bold text-ink">{{ __('devices.add_title') }}</h2>
|
||||
<button type="button" wire:click="closeModal" class="ml-auto grid place-items-center w-9 h-9 rounded-lg text-ink-3 hover:bg-raised hover:text-ink transition-colors" aria-label="{{ __('common.close') }}">
|
||||
<x-icon name="close" :size="18" />
|
||||
</button>
|
||||
</header>
|
||||
<form wire:submit="save">
|
||||
<x-modal :title="__('devices.add_title')" icon="devices">
|
||||
<div class="p-5 flex flex-col gap-4">
|
||||
<p class="text-[12px] text-ink-3 leading-relaxed">{{ __('devices.add_intro') }}</p>
|
||||
|
||||
<form wire:submit="save" class="p-5 flex flex-col gap-4">
|
||||
<p class="text-[12px] text-ink-3 leading-relaxed">{{ __('devices.add_intro') }}</p>
|
||||
@if ($error)
|
||||
<p class="rounded-lg border border-offline/30 bg-offline/10 px-3 py-2 text-[12.5px] text-offline">{{ $error }}</p>
|
||||
@endif
|
||||
|
||||
@if ($error)
|
||||
<p class="rounded-lg border border-offline/30 bg-offline/10 px-3 py-2 text-[12.5px] text-offline">{{ $error }}</p>
|
||||
@endif
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="d-ip" class="text-[12px] font-semibold text-ink-2">{{ __('devices.add_ip') }}</label>
|
||||
<input wire:model="ip" id="d-ip" type="text" inputmode="decimal" placeholder="10.10.30.78" autofocus
|
||||
class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm font-mono text-ink outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||||
@error('ip') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="d-ip" class="text-[12px] font-semibold text-ink-2">{{ __('devices.add_ip') }}</label>
|
||||
<input wire:model="ip" id="d-ip" type="text" inputmode="decimal" placeholder="10.10.30.78" autofocus
|
||||
class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm font-mono text-ink outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||||
@error('ip') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="d-name" class="text-[12px] font-semibold text-ink-2">{{ __('devices.name') }} <span class="text-ink-3 font-normal">({{ __('devices.add_optional') }})</span></label>
|
||||
<input wire:model="name" id="d-name" type="text"
|
||||
class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||||
@error('name') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="d-room" class="text-[12px] font-semibold text-ink-2">{{ __('devices.room') }} <span class="text-ink-3 font-normal">({{ __('devices.add_optional') }})</span></label>
|
||||
<select wire:model="roomId" id="d-room" class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent">
|
||||
<option value="">{{ __('devices.no_room') }}</option>
|
||||
@foreach ($rooms as $room)
|
||||
<option value="{{ $room->id }}">{{ $room->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="d-name" class="text-[12px] font-semibold text-ink-2">{{ __('devices.name') }} <span class="text-ink-3 font-normal">({{ __('devices.add_optional') }})</span></label>
|
||||
<input wire:model="name" id="d-name" type="text"
|
||||
class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||||
@error('name') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="d-room" class="text-[12px] font-semibold text-ink-2">{{ __('devices.room') }} <span class="text-ink-3 font-normal">({{ __('devices.add_optional') }})</span></label>
|
||||
<select wire:model="roomId" id="d-room" class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent">
|
||||
<option value="">{{ __('devices.no_room') }}</option>
|
||||
@foreach ($rooms as $room)
|
||||
<option value="{{ $room->id }}">{{ $room->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-end gap-2 pt-1">
|
||||
<x-slot:footer>
|
||||
<button type="button" wire:click="closeModal" class="rounded-lg border border-line px-3.5 py-2 text-[13px] font-semibold text-ink-2 hover:text-ink hover:bg-raised transition-colors">{{ __('common.cancel') }}</button>
|
||||
<button type="submit" wire:loading.attr="disabled" wire:target="save"
|
||||
class="rounded-lg bg-accent px-3.5 py-2 text-[13px] font-bold text-base hover:brightness-110 transition-[filter] disabled:opacity-60">
|
||||
<span wire:loading.remove wire:target="save">{{ __('devices.add_submit') }}</span>
|
||||
<span wire:loading wire:target="save">{{ __('devices.add_probing') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-slot:footer>
|
||||
</x-modal>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -1,41 +1,35 @@
|
|||
<div class="flex flex-col">
|
||||
<header class="flex items-center gap-3 px-5 py-4 border-b border-line-soft">
|
||||
<span class="grid place-items-center w-9 h-9 rounded-lg bg-accent/10 text-accent shrink-0"><x-icon name="persons" :size="18" /></span>
|
||||
<h2 class="text-[15px] font-bold text-ink">{{ __('persons.add_title') }}</h2>
|
||||
<button type="button" wire:click="closeModal" class="ml-auto grid place-items-center w-9 h-9 rounded-lg text-ink-3 hover:bg-raised hover:text-ink transition-colors" aria-label="{{ __('common.close') }}">
|
||||
<x-icon name="close" :size="18" />
|
||||
</button>
|
||||
</header>
|
||||
<form wire:submit="save">
|
||||
<x-modal :title="__('persons.add_title')" icon="persons">
|
||||
<div class="p-5 flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="p-name" class="text-[12px] font-semibold text-ink-2">{{ __('devices.name') }}</label>
|
||||
<input wire:model="name" id="p-name" type="text" autofocus
|
||||
class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||||
@error('name') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<form wire:submit="save" class="p-5 flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="p-name" class="text-[12px] font-semibold text-ink-2">{{ __('devices.name') }}</label>
|
||||
<input wire:model="name" id="p-name" type="text" autofocus
|
||||
class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||||
@error('name') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="p-mac" class="text-[12px] font-semibold text-ink-2">{{ __('persons.device') }}</label>
|
||||
@if ($unifiError)
|
||||
<p class="text-[12px] text-warning">{{ __('persons.unifi_error') }}</p>
|
||||
<input wire:model="mac" id="p-mac" type="text" placeholder="aa:bb:cc:dd:ee:ff"
|
||||
class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm font-mono text-ink outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||||
@else
|
||||
<select wire:model="mac" id="p-mac" class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent">
|
||||
<option value="">{{ __('persons.pick_client') }}</option>
|
||||
@foreach ($clientOptions as $o)
|
||||
<option value="{{ $o['mac'] }}">{{ $o['label'] }} · {{ $o['mac'] }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="text-[11.5px] text-ink-3">{{ __('persons.client_hint') }}</p>
|
||||
@endif
|
||||
@error('mac') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="p-mac" class="text-[12px] font-semibold text-ink-2">{{ __('persons.device') }}</label>
|
||||
@if ($unifiError)
|
||||
<p class="text-[12px] text-warning">{{ __('persons.unifi_error') }}</p>
|
||||
<input wire:model="mac" id="p-mac" type="text" placeholder="aa:bb:cc:dd:ee:ff"
|
||||
class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm font-mono text-ink outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||||
@else
|
||||
<select wire:model="mac" id="p-mac" class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent">
|
||||
<option value="">{{ __('persons.pick_client') }}</option>
|
||||
@foreach ($clientOptions as $o)
|
||||
<option value="{{ $o['mac'] }}">{{ $o['label'] }} · {{ $o['mac'] }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="text-[11.5px] text-ink-3">{{ __('persons.client_hint') }}</p>
|
||||
@endif
|
||||
@error('mac') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-end gap-2 pt-1">
|
||||
<x-slot:footer>
|
||||
<button type="button" wire:click="closeModal" class="rounded-lg border border-line px-3.5 py-2 text-[13px] font-semibold text-ink-2 hover:text-ink hover:bg-raised transition-colors">{{ __('common.cancel') }}</button>
|
||||
<button type="submit" class="rounded-lg bg-accent px-3.5 py-2 text-[13px] font-bold text-base hover:brightness-110 transition-[filter]">{{ __('persons.add') }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-slot:footer>
|
||||
</x-modal>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -1,119 +1,109 @@
|
|||
<div class="flex flex-col">
|
||||
<header class="flex items-center gap-3 px-5 py-4 border-b border-line-soft">
|
||||
<span class="grid place-items-center w-9 h-9 rounded-lg bg-accent/10 text-accent shrink-0"><x-icon name="automation" :size="18" /></span>
|
||||
<h2 class="text-[15px] font-bold text-ink">{{ __('automations.add_title') }}</h2>
|
||||
<button type="button" wire:click="closeModal" class="ml-auto grid place-items-center w-9 h-9 rounded-lg text-ink-3 hover:bg-raised hover:text-ink transition-colors" aria-label="{{ __('common.close') }}">
|
||||
<x-icon name="close" :size="18" />
|
||||
</button>
|
||||
</header>
|
||||
<form wire:submit="save">
|
||||
<x-modal :title="__('automations.add_title')" icon="automation">
|
||||
<div class="p-5 grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div class="flex flex-col gap-1.5 sm:col-span-2">
|
||||
<label for="a-name" class="text-[12px] font-semibold text-ink-2">{{ __('automations.name') }}</label>
|
||||
<input wire:model="name" id="a-name" type="text" autofocus
|
||||
class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||||
@error('name') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<form wire:submit="save" class="p-5 flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="a-name" class="text-[12px] font-semibold text-ink-2">{{ __('automations.name') }}</label>
|
||||
<input wire:model="name" id="a-name" type="text" autofocus
|
||||
class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||||
@error('name') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
{{-- Trigger --}}
|
||||
<fieldset class="flex flex-col gap-3 rounded-xl border border-line-soft p-3.5">
|
||||
<legend class="px-1.5 text-[11px] font-bold uppercase tracking-wide text-ink-3">{{ __('automations.trigger') }}</legend>
|
||||
|
||||
{{-- Trigger --}}
|
||||
<fieldset class="flex flex-col gap-3 rounded-xl border border-line-soft p-3.5">
|
||||
<legend class="px-1.5 text-[11px] font-bold uppercase tracking-wide text-ink-3">{{ __('automations.trigger') }}</legend>
|
||||
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<select wire:model.live="triggerType" class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent">
|
||||
<option value="state_change">{{ __('automations.trigger_state_change') }}</option>
|
||||
<option value="time">{{ __('automations.trigger_time') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@if ($triggerType === 'state_change')
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="a-tent" class="text-[12px] font-semibold text-ink-2">{{ __('automations.trigger_entity') }}</label>
|
||||
<select wire:model="triggerEntity" id="a-tent" class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent">
|
||||
<option value="">—</option>
|
||||
@foreach ($this->entityOptions as $o)
|
||||
<option value="{{ $o['value'] }}">{{ $o['label'] }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('triggerEntity') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="a-teq" class="text-[12px] font-semibold text-ink-2">{{ __('automations.trigger_equals') }}</label>
|
||||
<select wire:model="triggerEquals" id="a-teq" class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent">
|
||||
<option value="1">{{ __('automations.state_on') }}</option>
|
||||
<option value="0">{{ __('automations.state_off') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
@else
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="a-at" class="text-[12px] font-semibold text-ink-2">{{ __('automations.trigger_at') }}</label>
|
||||
<input wire:model="triggerAt" id="a-at" type="time"
|
||||
class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||||
@error('triggerAt') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
@endif
|
||||
</fieldset>
|
||||
@if ($triggerType === 'state_change')
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="a-tent" class="text-[12px] font-semibold text-ink-2">{{ __('automations.trigger_entity') }}</label>
|
||||
<select wire:model="triggerEntity" id="a-tent" class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent">
|
||||
<option value="">—</option>
|
||||
@foreach ($this->entityOptions as $o)
|
||||
<option value="{{ $o['value'] }}">{{ $o['label'] }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('triggerEntity') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="a-teq" class="text-[12px] font-semibold text-ink-2">{{ __('automations.trigger_equals') }}</label>
|
||||
<select wire:model="triggerEquals" id="a-teq" class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent">
|
||||
<option value="1">{{ __('automations.state_on') }}</option>
|
||||
<option value="0">{{ __('automations.state_off') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
@else
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="a-at" class="text-[12px] font-semibold text-ink-2">{{ __('automations.trigger_at') }}</label>
|
||||
<input wire:model="triggerAt" id="a-at" type="time"
|
||||
class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||||
@error('triggerAt') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
@endif
|
||||
</fieldset>
|
||||
|
||||
{{-- Action --}}
|
||||
<fieldset class="flex flex-col gap-3 rounded-xl border border-line-soft p-3.5">
|
||||
<legend class="px-1.5 text-[11px] font-bold uppercase tracking-wide text-ink-3">{{ __('automations.action') }}</legend>
|
||||
{{-- Action --}}
|
||||
<fieldset class="flex flex-col gap-3 rounded-xl border border-line-soft p-3.5">
|
||||
<legend class="px-1.5 text-[11px] font-bold uppercase tracking-wide text-ink-3">{{ __('automations.action') }}</legend>
|
||||
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<select wire:model.live="actionType" class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent">
|
||||
<option value="switch">{{ __('automations.action_switch') }}</option>
|
||||
<option value="notify">{{ __('automations.action_notify') }}</option>
|
||||
</select>
|
||||
|
||||
@if ($actionType === 'switch')
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="a-aent" class="text-[12px] font-semibold text-ink-2">{{ __('automations.action_entity') }}</label>
|
||||
<select wire:model="actionEntity" id="a-aent" class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent">
|
||||
<option value="">—</option>
|
||||
@foreach ($this->entityOptions as $o)
|
||||
@if ($o['controllable'])
|
||||
<option value="{{ $o['value'] }}">{{ $o['label'] }}</option>
|
||||
@endif
|
||||
@endforeach
|
||||
</select>
|
||||
@error('actionEntity') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="a-aon" class="text-[12px] font-semibold text-ink-2">{{ __('automations.action_on') }}</label>
|
||||
<select wire:model="actionOn" id="a-aon" class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent">
|
||||
<option value="1">{{ __('automations.state_on') }}</option>
|
||||
<option value="0">{{ __('automations.state_off') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
@else
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="a-msg" class="text-[12px] font-semibold text-ink-2">{{ __('automations.message') }}</label>
|
||||
<input wire:model="actionMessage" id="a-msg" type="text"
|
||||
class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||||
@error('actionMessage') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
@endif
|
||||
</fieldset>
|
||||
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="a-cd" class="text-[12px] font-semibold text-ink-2">{{ __('automations.cooldown') }}</label>
|
||||
<input wire:model="cooldownSeconds" id="a-cd" type="number" min="0" max="86400"
|
||||
class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||||
<p class="text-[11.5px] text-ink-3">{{ __('automations.cooldown_hint') }}</p>
|
||||
@error('cooldownSeconds') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
@if ($actionType === 'switch')
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="a-aent" class="text-[12px] font-semibold text-ink-2">{{ __('automations.action_entity') }}</label>
|
||||
<select wire:model="actionEntity" id="a-aent" class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent">
|
||||
<option value="">—</option>
|
||||
@foreach ($this->entityOptions as $o)
|
||||
@if ($o['controllable'])
|
||||
<option value="{{ $o['value'] }}">{{ $o['label'] }}</option>
|
||||
@endif
|
||||
@endforeach
|
||||
</select>
|
||||
@error('actionEntity') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="a-aon" class="text-[12px] font-semibold text-ink-2">{{ __('automations.action_on') }}</label>
|
||||
<select wire:model="actionOn" id="a-aon" class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent">
|
||||
<option value="1">{{ __('automations.state_on') }}</option>
|
||||
<option value="0">{{ __('automations.state_off') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
@else
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="a-msg" class="text-[12px] font-semibold text-ink-2">{{ __('automations.message') }}</label>
|
||||
<input wire:model="actionMessage" id="a-msg" type="text"
|
||||
class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||||
@error('actionMessage') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
@endif
|
||||
</fieldset>
|
||||
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="a-cd" class="text-[12px] font-semibold text-ink-2">{{ __('automations.cooldown') }}</label>
|
||||
<input wire:model="cooldownSeconds" id="a-cd" type="number" min="0" max="86400"
|
||||
class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||||
<p class="text-[11.5px] text-ink-3">{{ __('automations.cooldown_hint') }}</p>
|
||||
@error('cooldownSeconds') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
<label class="flex items-start gap-3 cursor-pointer self-end pb-1">
|
||||
<input wire:model="dryRun" type="checkbox" class="mt-0.5 w-4 h-4 rounded border-line text-accent focus:ring-accent">
|
||||
<span class="flex flex-col">
|
||||
<span class="text-[13px] font-semibold text-ink">{{ __('automations.dry_run') }}</span>
|
||||
<span class="text-[11.5px] text-ink-3">{{ __('automations.dry_run_hint') }}</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<label class="flex items-start gap-3 cursor-pointer">
|
||||
<input wire:model="dryRun" type="checkbox" class="mt-0.5 w-4 h-4 rounded border-line text-accent focus:ring-accent">
|
||||
<span class="flex flex-col">
|
||||
<span class="text-[13px] font-semibold text-ink">{{ __('automations.dry_run') }}</span>
|
||||
<span class="text-[11.5px] text-ink-3">{{ __('automations.dry_run_hint') }}</span>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div class="flex items-center justify-end gap-2 pt-1">
|
||||
<x-slot:footer>
|
||||
<button type="button" wire:click="closeModal" class="rounded-lg border border-line px-3.5 py-2 text-[13px] font-semibold text-ink-2 hover:text-ink hover:bg-raised transition-colors">{{ __('common.cancel') }}</button>
|
||||
<button type="submit" class="rounded-lg bg-accent px-3.5 py-2 text-[13px] font-bold text-base hover:brightness-110 transition-[filter]">{{ __('automations.add') }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-slot:footer>
|
||||
</x-modal>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -1,45 +1,39 @@
|
|||
<div class="flex flex-col">
|
||||
<header class="flex items-center gap-3 px-5 py-4 border-b border-line-soft">
|
||||
<span class="grid place-items-center w-9 h-9 rounded-lg bg-accent/10 text-accent shrink-0"><x-icon name="doorbell" :size="18" /></span>
|
||||
<h2 class="text-[15px] font-bold text-ink">{{ __('addons.ring.connect_title') }}</h2>
|
||||
<button type="button" wire:click="closeModal" class="ml-auto grid place-items-center w-9 h-9 rounded-lg text-ink-3 hover:bg-raised hover:text-ink transition-colors" aria-label="{{ __('common.close') }}">
|
||||
<x-icon name="close" :size="18" />
|
||||
</button>
|
||||
</header>
|
||||
<form wire:submit="save">
|
||||
<x-modal :title="__('addons.ring.connect_title')" icon="doorbell">
|
||||
<div class="p-5 flex flex-col gap-4">
|
||||
<p class="text-[12px] text-ink-3 leading-relaxed flex items-start gap-2">
|
||||
<x-icon name="cloud" :size="15" class="mt-0.5 shrink-0" />
|
||||
<span>{{ __('addons.ring.backend_note') }}</span>
|
||||
</p>
|
||||
|
||||
<form wire:submit="save" class="p-5 flex flex-col gap-4">
|
||||
<p class="text-[12px] text-ink-3 leading-relaxed flex items-start gap-2">
|
||||
<x-icon name="cloud" :size="15" class="mt-0.5 shrink-0" />
|
||||
<span>{{ __('addons.ring.backend_note') }}</span>
|
||||
</p>
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="ring-email" class="text-[12px] font-semibold text-ink-2">{{ __('addons.ring.email') }}</label>
|
||||
<input wire:model="email" id="ring-email" type="email" autocomplete="off" autofocus
|
||||
class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||||
@error('email') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="ring-email" class="text-[12px] font-semibold text-ink-2">{{ __('addons.ring.email') }}</label>
|
||||
<input wire:model="email" id="ring-email" type="email" autocomplete="off" autofocus
|
||||
class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||||
@error('email') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="ring-password" class="text-[12px] font-semibold text-ink-2">{{ __('addons.ring.password') }}</label>
|
||||
<input wire:model="password" id="ring-password" type="password" autocomplete="new-password"
|
||||
class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||||
@if ($hasStoredPassword)
|
||||
<p class="text-[11.5px] text-ink-3">{{ __('addons.ring.password_stored') }}</p>
|
||||
@endif
|
||||
@error('password') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="ring-code" class="text-[12px] font-semibold text-ink-2">{{ __('addons.ring.code') }}</label>
|
||||
<input wire:model="code" id="ring-code" type="text" inputmode="numeric" autocomplete="one-time-code"
|
||||
class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm font-mono text-ink outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||||
@error('code') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="ring-password" class="text-[12px] font-semibold text-ink-2">{{ __('addons.ring.password') }}</label>
|
||||
<input wire:model="password" id="ring-password" type="password" autocomplete="new-password"
|
||||
class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm text-ink outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||||
@if ($hasStoredPassword)
|
||||
<p class="text-[11.5px] text-ink-3">{{ __('addons.ring.password_stored') }}</p>
|
||||
@endif
|
||||
@error('password') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<label for="ring-code" class="text-[12px] font-semibold text-ink-2">{{ __('addons.ring.code') }}</label>
|
||||
<input wire:model="code" id="ring-code" type="text" inputmode="numeric" autocomplete="one-time-code"
|
||||
class="w-full rounded-lg bg-raised border border-line px-3 py-2.5 text-sm font-mono text-ink outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||||
@error('code') <p class="text-[12px] text-offline">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-end gap-2 pt-1">
|
||||
<x-slot:footer>
|
||||
<button type="button" wire:click="closeModal" class="rounded-lg border border-line px-3.5 py-2 text-[13px] font-semibold text-ink-2 hover:text-ink hover:bg-raised transition-colors">{{ __('common.cancel') }}</button>
|
||||
<button type="submit" class="rounded-lg bg-accent px-3.5 py-2 text-[13px] font-bold text-base hover:brightness-110 transition-[filter]">{{ __('addons.ring.save') }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-slot:footer>
|
||||
</x-modal>
|
||||
</form>
|
||||
|
|
|
|||
Loading…
Reference in New Issue