{{-- The settings partial EVERY ConfigurableAdapter uses. Iterates the adapter's settingsSchema() and renders each declared field as either plain text or a password (with show/hide toggle) based on its `secret` flag. Everything ELSE on the form (URL, active toggle, heartbeat toggle, per-field mapping) lives in the shared form-shell component. Field names AND ids are prefixed with the instance slug so each adapter's form has fully unique DOM identifiers to avoid entered values to visually propagate across tabs. --}} @php $slug = $adapter->name(); $locked = config('app.lock_passwords') === true; @endphp {{ trans('admin/settings/sync_adapters.experimental_banner_body') }} @if ($adapter->supportsGroupScoping() && $adapter->isEnabled()) {{-- Refresh-groups action lives outside the config form because it POSTs to a different route (settings.adapters.refresh_groups) and HTML disallows nested form elements. Rendering it above the config form makes it visually close to the mapping table it populates. --}}
@csrf
@endif @if ($adapter->supportsVendorCustomFields() && $adapter->isEnabled()) {{-- Parallel to the refresh-groups action: pulls the vendor's current custom-field list so extraFields() can offer each as a mappable target. Same outside-the-config-form rationale. --}}
@csrf
@endif @php $settingsSections = $adapter->settingsSections(); // Track section transitions across the loop so we can open // and close
wrappers around each contiguous run // of same-sectioned schema entries. Un-fieldset-sectioned entries // (section => null) render outside any fieldset. // // Starts at baseUrlSection() so if the shell already opened a // fieldset around the Base URL row, the schema loop's first // same-sectioned entries render into that fieldset instead of // opening a duplicate. When the loop transitions to a // different section, the close-then-open handles the switch. $currentSection = $adapter->baseUrlSection(); @endphp @foreach ($adapter->settingsSchema() as $field) @php $fieldName = $slug.'_'.$field['key']; $fieldType = $field['type'] ?? 'text'; $isSecret = $field['secret'] ?? false; $isRequired = $field['required'] ?? true; $storedValue = $adapter->credentialForDisplay($field['key']); // Optional conditional visibility. Schema entries declare // visible_when: [source_key => value | [values]] and the // rendered field gets wrapped in a data-driven div. Inline // JS below the loop wires the show/hide logic. $visibleWhen = $field['visible_when'] ?? null; $section = $field['section'] ?? null; @endphp @if ($section !== $currentSection) @if ($currentSection !== null)
@endif @if ($section !== null && isset($settingsSections[$section])) @php // Hoist the section-help lookup out of the Blade attribute // to sidestep an operator-precedence trap: inside a // `help_text="{!! ... !!}"` string attribute Blade compiles // the `??` fallback as `'' . $arr['help'] ?? ''`, and the // outer concat binds tighter than `??`, so PHP evaluates // $arr['help'] unconditionally and warns on missing keys. // Regular PHP `??` on its own line does the right thing. $sectionHelp = $settingsSections[$section]['help'] ?? ''; @endphp
{{ $settingsSections[$section]['title'] }} @endif @php $currentSection = $section; @endphp @endif @if (is_array($visibleWhen)) @php $depKey = array_key_first($visibleWhen); $depValues = (array) $visibleWhen[$depKey]; @endphp
@endif @if ($fieldType === 'checkbox') @elseif ($fieldType === 'category') {{-- Per-record category selector. Empty means "fall back to the instance-wide default_category_id". Uses the same AJAX-backed select2 component as the shell's default category picker. --}} @elseif ($fieldType === 'field_map') {{-- field_map entries are rendered by the shell's own fieldset below the operational checkboxes, so they sit alongside the standard mapping controls instead of getting lost among the credential inputs. Nothing to render here. --}} @else @if ($locked) @elseif ($fieldType === 'select') @elseif ($fieldType === 'multiselect') @php $selectedValues = old( $fieldName, is_string($storedValue) && $storedValue !== '' ? (json_decode($storedValue, true) ?: []) : [] ); // First-render fallback: when nothing is // stored yet, adapters can declare a // `default` array to seed the selection so // admins see the intended baseline instead // of an empty widget. if (empty($selectedValues) && ! empty($field['default']) && is_array($field['default'])) { $selectedValues = $field['default']; } @endphp @elseif ($fieldType === 'textarea') @elseif ($isSecret) @elseif (! empty($field['url_prefix'])) {{-- Input-group that shows the current Base URL as a prefix addon so admins see the full URL that will be assembled at request time. Marker class on the addon lets the live-update JS at the bottom of this partial sync new Base URL keystrokes into this label without a page reload. --}}
{{ $adapter->getUrl() ?: trans('admin/settings/sync_adapters.custom_url_prefix_base_url_placeholder') }}
@else @endif
@endif @if (is_array($visibleWhen))
@endif @endforeach @if ($currentSection !== null) {{-- Close the last section's fieldset when the schema loop exits inside one. --}}
@endif
{{-- JS handlers for [data-visible-when-field] conditional-visibility and .js-adapter-url-prefix live-update live in resources/assets/js/snipeit.js. Both are data-attribute driven so nothing on this partial needs inline script anymore. --}}