@php // Client-side "Add new" seed: opening the modal only sets Livewire props // with deferred $set (third arg false) so no /livewire/update fires — the // heavy withCount() render never runs just to show an empty form. $blank = ['editingId' => null, 'name' => '', 'url' => '', 'api_key' => '', 'driver' => 'standard', 'status' => '1', 'scrape_enabled' => false, 'public_services_url' => '', 'showModal' => true]; @endphp
@forelse ($providers as $p) @php $breaker = ['closed' => ['success', __('Healthy')], 'half_open' => ['warning', __('Recovering')], 'open' => ['danger', __('Tripped')]][$p->breaker_state] ?? ['neutral', $p->breaker_state]; // Edit seed built from the row already on screen (the api_key is // never surfaced, so it stays blank — exactly as the old edit() did). $edit = ['editingId' => $p->id, 'name' => $p->name, 'url' => $p->url, 'api_key' => '', 'driver' => $p->driver, 'status' => $p->status ? '1' : '0', 'scrape_enabled' => (bool) $p->scrape_enabled, 'public_services_url' => (string) $p->public_services_url, 'showModal' => true]; // Sync-modal seed: prefill the markup + last chosen toggles for THIS // provider so opening the modal never round-trips (deferred $set). $so = $p->sync_options ?? []; $syncSeed = [ 'syncProviderId' => $p->id, 'syncProviderName' => $p->name, 'syncMarkup' => rtrim(rtrim(number_format((float) $p->default_markup_percent, 2, '.', ''), '0'), '.') ?: '0', 'syncScope' => ($so['scope'] ?? 'current') === 'all' ? 'all' : 'current', 'syncOriginalPrice' => (bool) ($so['sync_original_price'] ?? true), 'syncNewPrice' => (bool) ($so['sync_new_price'] ?? true), 'syncStatus' => (bool) ($so['sync_status'] ?? true), 'syncName' => (bool) ($so['sync_name'] ?? false), 'syncDescription' => (bool) ($so['sync_description'] ?? false), 'syncMin' => (bool) ($so['sync_min'] ?? true), 'syncMax' => (bool) ($so['sync_max'] ?? true), 'syncDripfeed' => (bool) ($so['sync_dripfeed'] ?? false), 'syncCancel' => (bool) ($so['sync_cancel'] ?? false), 'convertCurrency' => (bool) ($so['convert_currency'] ?? false), 'showSyncModal' => true, ]; @endphp @empty @endforelse
{{ __('Provider') }} {{ __('Balance') }} {{ __('Connected') }} {{ __('Breaker') }} {{ __('Status') }} {{ __('Action') }}
{{ $p->name }}
{{ $p->url }}
@if ($p->balance !== null) {{ money($p->balance, $p->currency_code ?: 'USD') }} @else @endif {{ number_format($p->services_count) }} {{ $breaker[1] }} {{-- Route binds by ULID (HasUlid::getRouteKeyName) — pass the model, not ->id. --}} {{ __('Manage catalogue') }} @if ($p->scrape_enabled && $p->public_services_url) {{-- Scrape the public /services page NOW (CSRF-safe wire:click, SSRF-guarded backend). --}} @endif
@error('name')

{{ $message }}

@enderror
@error('url')

{{ $message }}

@enderror
@error('api_key')

{{ $message }}

@enderror

{{ __('Stored encrypted at rest.') }}

{{-- ══ Metadata scraping ══ URL field is always visible (optional). The "enable scraping" toggle only appears after a URL is entered — $wire.public_services_url is reactive in Alpine because wire:model.live keeps it in sync. --}}
@error('public_services_url')

{{ $message }}

@enderror

{{ __('Public page listing this provider\'s services (no login required). Used to fetch descriptions and average delivery times.') }}

{{-- Toggle visible only when a URL exists. --}}
{{-- ══ Sync-services configuration modal (reference-panel parity) ══ --}}

{{ __('Configure the synchronisation for') }}

{{-- Markup % + scope --}}
%

{{ __('Added over the provider cost to set the sale price. Auto-rounded to 2 decimals.') }}

{{ __('“Current” only updates imported services. “All” also imports upstream services not yet in your catalogue.') }}

{{-- Per-field toggles --}}

{{ __('Description sync only applies to providers whose API returns one. Currency conversion uses the rate configured in Settings → Currency.') }}

{{-- Explanatory note (Current vs All) --}}

{{ __('Your saved choices are remembered per provider. New price recalculates the sale price from the current cost and the percentage above.') }}