Snapshot of main at 9f39a6e1e (2026-11-02)

This commit is contained in:
Ronald Huynen committed 2026-11-02 10:41:47 +01:00
commit 6ccf1792d7
2580 files changed
+1040202

No files matched your search

@@ -0,0 +1,22 @@
{{-- Shown on the Post and Questions tabs while the poll's content is read-only (Poll::isContentLocked) --}}
@if ($contentLocked)
<div class="rounded-md bg-gray-50 border border-gray-300 p-4" role="status">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-5 w-5 text-gray-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 1a4.5 4.5 0 00-4.5 4.5V9H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-.5V5.5A4.5 4.5 0 0010 1zm3 8V5.5a3 3 0 10-6 0V9h6z" clip-rule="evenodd" />
</svg>
</div>
<div class="ml-3 text-sm text-gray-700">
<p class="font-medium text-gray-800">{{ __('This poll is read-only.') }}</p>
<p class="mt-1">
@if ($editingPoll?->hasResponses())
{{ __('People have already voted, so the post and the questions can no longer change: every voter must see the same poll. Settings and target audience can still be edited.') }}
@else
{{ __('The poll is no longer a draft, so the post and the questions cannot be changed. To change them, set the status back to Draft and save; that is only possible while nobody has voted.') }}
@endif
</p>
</div>
</div>
</div>
@endif
@@ -0,0 +1,103 @@
<div class="space-y-6">
@include('livewire.polls.partials.locked-warning')
<!-- Language of the texts being edited (also usable when read-only) -->
<div class="flex flex-wrap items-center gap-2">
<span class="text-sm text-gray-700">{{ __('Language') }}:</span>
@foreach ($locales as $code)
@php($hasTitle = trim((string) ($postFields[$code]['title'] ?? '')) !== '')
<button type="button" wire:click="$set('editLocale', '{{ $code }}')"
title="{{ $hasTitle ? __('Filled in') : '' }}"
class="rounded-md border px-2 py-1 text-xs uppercase {{ $editLocale === $code ? 'border-theme-brand bg-theme-brand text-white' : 'border-theme-border text-theme-primary' }} {{ $hasTitle ? 'font-semibold' : 'opacity-70' }}">
{{ $code }}
</button>
@endforeach
</div>
<fieldset @disabled($contentLocked) @class(["space-y-6", "opacity-60" => $contentLocked])>
<div wire:key="poll-post-title-{{ $editingPollId }}-{{ $editLocale }}">
<label class="block text-sm text-gray-700 mb-2">{{ __('Title') }} ({{ strtoupper($editLocale) }})</label>
<x-input errorless wire:model.blur="postFields.{{ $editLocale }}.title" type="text" maxlength="150" class="w-full" />
@include('polls.types._error', ['field' => "postFields.$editLocale.title"])
</div>
<div wire:key="poll-post-excerpt-{{ $editingPollId }}-{{ $editLocale }}">
<label class="block text-sm text-gray-700 mb-2">{{ __('Introduction') }} ({{ strtoupper($editLocale) }})</label>
<x-textarea errorless wire:model.blur="postFields.{{ $editLocale }}.excerpt" rows="3" maxlength="{{ timebank_config('posts.excerpt_max_input', 500) }}" class="w-full" />
@include('polls.types._error', ['field' => "postFields.$editLocale.excerpt"])
</div>
<!-- Content: WYSIWYG editor (Quill), as in Posts/Manage -->
<div wire:ignore wire:key="poll-post-quill-{{ $editingPollId }}-{{ $editLocale }}"
x-data="{
quill: null,
init() {
this.$nextTick(() => {
this.quill = new Quill(this.$refs.editor, {
modules: {
toolbar: [
[{ header: [1, 2, false] }],
['bold', 'italic', 'underline', 'strike'],
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
[{ 'align': [] }],
['link', 'image', 'video'],
['clean']
]
},
theme: 'snow',
readOnly: {{ $contentLocked ? 'true' : 'false' }},
});
this.quill.on('text-change', () => {
@this.set('postFields.{{ $editLocale }}.content', this.quill.root.innerHTML, false);
});
});
}
}">
<label class="block text-sm text-gray-700 mb-2">{{ __('Content') }} ({{ strtoupper($editLocale) }})</label>
<div x-ref="editor">{!! \App\Helpers\StringHelper::sanitizeHtml($postFields[$editLocale]['content'] ?? '') !!}</div>
</div>
@include('polls.types._error', ['field' => "postFields.$editLocale.content"])
<!-- Image -->
<div>
<label class="block text-sm text-gray-700 mb-2">{{ __('Image') }}</label>
@if ($image && !$errors->has('image'))
<img class="mb-2 w-64 rounded-md border border-gray-300" src="{{ $image->temporaryUrl() }}" alt="">
@elseif ($currentImage)
<img class="mb-2 w-64 rounded-md border border-gray-300" src="{{ $currentImage }}" alt="">
@else
<div class="mb-2 flex h-36 w-64 items-center justify-center border border-gray-300">
<span>{{ __('No image') }}</span>
</div>
@endif
<div @class(['flex flex-wrap items-center gap-3', 'hidden' => $contentLocked])>
<input accept="image/*" type="file" class="hidden" id="poll-image-upload" wire:model="image">
<x-jetstream.secondary-button type="button" onclick="document.getElementById('poll-image-upload').click()">
{{ __('Browse...') }}
</x-jetstream.secondary-button>
@if ($image || $currentImage)
<x-jetstream.danger-button type="button" wire:click="markImageForRemoval">
{{ __('Delete photo') }}
</x-jetstream.danger-button>
@endif
<span wire:loading wire:target="image" class="text-sm text-theme-secondary">{{ __('Uploading...') }}</span>
</div>
@if ($removeImage)
<p class="mt-1 text-sm text-theme-secondary">{{ __('The photo will be removed when you save the poll.') }}</p>
@endif
@include('polls.types._error', ['field' => 'image'])
</div>
<div>
<label class="block text-sm text-gray-700 mb-2">{{ __('Image ownership') }} ({{ __('all languages') }})</label>
<x-input errorless wire:model.blur="mediaOwner" type="text" maxlength="150" class="w-full" />
@include('polls.types._error', ['field' => 'mediaOwner'])
</div>
<div wire:key="poll-post-caption-{{ $editingPollId }}-{{ $editLocale }}">
<label class="block text-sm text-gray-700 mb-2">{{ __('Caption') }} ({{ strtoupper($editLocale) }})</label>
<x-textarea errorless wire:model.blur="mediaCaptions.{{ $editLocale }}" rows="2" maxlength="{{ timebank_config('posts.media_caption_max_input', 300) }}" class="w-full" />
@include('polls.types._error', ['field' => "mediaCaptions.$editLocale"])
</div>
</fieldset>
</div>
@@ -0,0 +1,92 @@
<div class="space-y-6">
@include('livewire.polls.partials.locked-warning')
<!-- Language of the texts being edited -->
<div class="flex flex-wrap items-center gap-2">
<span class="text-sm text-gray-700">{{ __('Language') }}:</span>
@foreach ($locales as $code)
<button type="button" wire:click="$set('editLocale', '{{ $code }}')"
class="rounded-md border px-2 py-1 text-xs uppercase {{ $editLocale === $code ? 'border-theme-brand bg-theme-brand text-white' : 'border-theme-border text-theme-primary' }}">
{{ $code }}
</button>
@endforeach
</div>
@forelse ($questions as $index => $question)
@php($type = $registry->has($question['type']) ? $registry->get($question['type']) : null)
<div class="p-4 bg-theme-surface rounded-md border border-theme-border" wire:key="poll-question-{{ $question['key'] }}">
<div class="flex flex-wrap items-center gap-2 mb-4">
<span class="text-sm font-semibold text-theme-primary mr-auto">{{ __('Question') }} {{ $index + 1 }}</span>
@unless ($contentLocked)
<x-jetstream.light-button wire:click="moveQuestion({{ $index }}, -1)" :disabled="$index === 0" title="{{ __('Move up') }}">
<x-icon class="h-4 w-4" name="chevron-up" />
</x-jetstream.light-button>
<x-jetstream.light-button wire:click="moveQuestion({{ $index }}, 1)" :disabled="$loop->last" title="{{ __('Move down') }}">
<x-icon class="h-4 w-4" name="chevron-down" />
</x-jetstream.light-button>
<x-jetstream.light-button wire:click="removeQuestion({{ $index }})" title="{{ __('Remove question') }}">
<x-icon class="h-4 w-4" name="trash" />
</x-jetstream.light-button>
@endunless
</div>
<fieldset @disabled($contentLocked) @class(["space-y-4", "opacity-60" => $contentLocked])>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-3">
<div class="sm:col-span-2">
<label class="block text-sm text-gray-700 mb-2">{{ __('Question') }} ({{ strtoupper($editLocale) }})</label>
<x-input wire:model="questions.{{ $index }}.label.{{ $editLocale }}" type="text" class="w-full" />
@include('polls.types._error', ['field' => "questions.$index.label"])
@php($filled = array_keys(array_filter($question['label'] ?? [], fn ($label) => trim((string) $label) !== '')))
@if ($filled)
<p class="mt-1 text-xs text-theme-secondary">{{ __('Filled in') }}: {{ strtoupper(implode(', ', $filled)) }}</p>
@endif
</div>
<div>
<label class="block text-sm text-gray-700 mb-2">{{ __('Type') }}</label>
<select wire:model.live="questions.{{ $index }}.type" class="w-full border border-primary-300 bg-theme-background rounded-md shadow-sm text-sm">
@foreach ($typeOptions as $key => $label)
<option value="{{ $key }}">{{ $label }}</option>
@endforeach
@unless ($type)
<option value="{{ $question['type'] }}">{{ $question['type'] }} ({{ __('disabled') }})</option>
@endunless
</select>
</div>
</div>
@include('polls.types._checkbox', ['model' => "questions.$index.required", 'label' => __('Answer required')])
@if ($type)
@include($type->editorView(), [
'type' => $type,
'settings' => $question['settings'] ?? [],
'prefix' => "questions.$index.settings",
'index' => $index,
'locale' => $editLocale,
])
@else
<p class="text-sm text-red-600">{{ __('This question type is disabled in the platform configuration.') }}</p>
@endif
</fieldset>
</div>
@empty
<p class="text-sm text-theme-secondary">{{ __('No questions yet.') }}</p>
@endforelse
@unless ($contentLocked)
<div class="flex flex-wrap items-end gap-3">
<div>
<label class="block text-sm text-gray-700 mb-2">{{ __('New question') }}</label>
<select wire:model="newQuestionType" class="border border-primary-300 bg-theme-background rounded-md shadow-sm text-sm">
@foreach ($typeOptions as $key => $label)
<option value="{{ $key }}">{{ $label }}</option>
@endforeach
</select>
</div>
<x-jetstream.secondary-button wire:click="addQuestion">
<x-icon class="mr-2 h-4 w-4" name="plus" />
{{ __('Add question') }}
</x-jetstream.secondary-button>
</div>
@endunless
</div>
@@ -0,0 +1,89 @@
<div class="space-y-6">
@if ($results['may_export'])
<div class="flex justify-end">
<a href="{{ route('polls.export', $editingPoll) }}"
class="inline-flex items-center rounded-md border border-theme-border bg-theme-background px-4 py-2 text-sm text-theme-primary shadow-sm hover:bg-theme-surface">
<x-icon class="mr-2 h-5 w-5" name="arrow-down-tray" />
{{ __('Download CSV') }}
</a>
</div>
@endif
<!-- Verification state -->
<div class="p-3 bg-theme-surface rounded-md border border-theme-border">
<p class="text-sm text-theme-primary">
<strong>{{ __('Ballot ledger') }}:</strong>
@if ($results['chain']['valid'])
{{ __('intact') }} ({{ trans_choice(':count entry|:count entries', $results['chain']['entries'], ['count' => $results['chain']['entries']]) }})
@else
<span class="text-red-600">{{ __('broken at entry :sequence', ['sequence' => $results['chain']['broken_at']]) }}</span>
@endif
</p>
<p class="text-sm text-theme-secondary mt-1">
{{ __('Votes recorded: :votes. Ballots counted in the ledger: :ballots.', ['votes' => $results['response_count'], 'ballots' => $results['active_ballots']]) }}
@if ($results['response_count'] !== $results['active_ballots'])
<span class="text-red-600">{{ __('These numbers should be equal.') }}</span>
@endif
</p>
</div>
<!-- Results per question -->
@foreach ($results['questions'] as $question)
@if ($registry->has($question->type))
@php($type = $registry->get($question->type))
<div wire:key="poll-admin-results-{{ $question->id }}">
<p class="mb-2 text-sm font-semibold text-theme-primary">{{ $question->labelFor($locale) }}</p>
@include($type->resultsView(), [
'type' => $type,
'question' => $question,
'settings' => $question->settings ?? [],
'summary' => $results['summaries'][$question->id],
'locale' => $locale,
])
</div>
@endif
@endforeach
<!-- Voters -->
<div>
<p class="mb-2 text-sm font-semibold text-theme-primary">{{ __('Voters') }}</p>
@if ($editingPoll->anonymous)
<p class="text-sm text-theme-secondary">{{ __('This poll is anonymous: voters are not shown.') }}</p>
@elseif (!$results['may_export'])
<p class="text-sm text-theme-secondary">{{ __('Who voted is shown to admins with the poll export permission.') }}</p>
@elseif ($results['voters']->isEmpty())
<p class="text-sm text-theme-secondary">{{ __('No votes yet.') }}</p>
@else
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200 text-sm">
<thead class="bg-gray-50">
<tr>
<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">{{ __('Profile') }}</th>
<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">{{ __('Submitted by') }}</th>
<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">{{ __('Voted') }}</th>
<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">{{ __('Changed') }}</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200">
@foreach ($results['voters'] as $response)
<tr wire:key="poll-voter-{{ $response->id }}">
<td class="px-3 py-2 text-gray-900">
{{ $response->voter?->name ?? __('Deleted profile') }}
<span class="text-xs text-gray-500">({{ __(class_basename($response->voter_type)) }})</span>
</td>
<td class="px-3 py-2 text-gray-500">{{ $response->user?->name }}</td>
<td class="px-3 py-2 text-gray-500 whitespace-nowrap">{{ $response->submitted_at->locale($locale)->isoFormat('lll') }}</td>
<td class="px-3 py-2 text-gray-500 whitespace-nowrap">
{{ $response->updated_at && $response->updated_at->gt($response->submitted_at->copy()->addSecond()) ? $response->updated_at->locale($locale)->isoFormat('lll') : '-' }}
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@if ($results['response_count'] > $results['voters']->count())
<p class="mt-2 text-xs text-theme-secondary">{{ __('Showing the latest :count voters.', ['count' => $results['voters']->count()]) }}</p>
@endif
@endif
</div>
</div>
@@ -0,0 +1,103 @@
@php($hasVotes = (bool) $editingPoll?->hasResponses())
@php($sealed = (bool) $editingPoll?->sealed_at)
<div class="space-y-6">
@if ($sealed)
{{-- A sealed result is final: savePoll refuses status and date changes too --}}
<p class="text-sm text-theme-secondary">{{ __('This poll is closed and sealed: its result is final, so its status and dates cannot change any more.') }}</p>
@endif
<fieldset @disabled($sealed) @class(['grid grid-cols-1 gap-4 sm:grid-cols-3', 'opacity-60' => $sealed])>
<div>
<label class="block text-sm text-gray-700 mb-2">{{ __('Status') }}</label>
<select wire:model.live="form.status" class="w-full border border-primary-300 bg-theme-background rounded-md shadow-sm text-sm">
{{-- A poll with votes cannot go back to draft (savePoll refuses it too) --}}
<option value="draft" @disabled($editingPoll?->hasResponses())>{{ __('Draft') }}</option>
<option value="open">{{ __('Open') }}</option>
<option value="closed">{{ __('Closed') }}</option>
</select>
@include('polls.types._error', ['field' => 'form.status'])
</div>
<div>
<label class="block text-sm text-gray-700 mb-2">{{ __('Opens (optional)') }}</label>
@include('polls.types._date', ['model' => 'form.opens_at', 'key' => 'poll-' . $editingPollId . '-opens_at'])
@include('polls.types._error', ['field' => 'form.opens_at'])
</div>
<div>
<label class="block text-sm text-gray-700 mb-2">{{ __('Closes (optional)') }}</label>
@include('polls.types._date', ['model' => 'form.closes_at', 'key' => 'poll-' . $editingPollId . '-closes_at'])
@include('polls.types._error', ['field' => 'form.closes_at'])
</div>
</fieldset>
@if ($originalStatus === 'draft' && ($form['status'] ?? null) === 'open')
<div class="rounded-md bg-gray-50 border border-gray-300 p-4" role="alert">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-5 w-5 text-gray-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd" />
</svg>
</div>
<div class="ml-3 text-sm text-gray-700">
<p>{{ __('When you save, the poll opens: its post and questions become read-only and the post is published. Check the texts, image and questions first.') }}</p>
</div>
</div>
</div>
@endif
@php($latePublications = $this->latePublications())
@if ($latePublications)
<div class="rounded-md bg-gray-50 border border-gray-300 p-4" role="alert">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-5 w-5 text-gray-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd" />
</svg>
</div>
<div class="ml-3 text-sm text-gray-700">
<p>{{ __('The post of this poll is published later than the poll opens: :dates. Until then the poll is not shown in that language. Change the publication date on the Posts page.', ['dates' => $this->formatLatePublications($latePublications)]) }}</p>
</div>
</div>
</div>
@endif
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div>
<label class="block text-sm text-gray-700 mb-2">{{ __('Votes by organizations, banks and admins') }}</label>
{{-- The one-vote rule depends on it, so it is fixed once people voted (savePoll refuses it too) --}}
<select wire:model="form.vote_scope" @disabled($hasVotes) class="w-full border border-primary-300 bg-theme-background rounded-md shadow-sm text-sm disabled:opacity-60">
<option value="per_profile">{{ __('One vote per profile') }}</option>
<option value="per_user">{{ __('One vote per linked user') }}</option>
</select>
@include('polls.types._error', ['field' => 'form.vote_scope'])
</div>
<div>
<label class="block text-sm text-gray-700 mb-2">{{ __('Who may vote for an organization or bank') }}</label>
<select wire:model="form.member_scope" class="w-full border border-primary-300 bg-theme-background rounded-md shadow-sm text-sm">
<option value="all_users">{{ __('All linked users') }}</option>
<option value="managers_coordinators">{{ __('Only managers and coordinators') }}</option>
</select>
</div>
<div>
<label class="block text-sm text-gray-700 mb-2">{{ __('Results visible to voters') }}</label>
<select wire:model="form.results_visibility" class="w-full border border-primary-300 bg-theme-background rounded-md shadow-sm text-sm">
<option value="never">{{ __('Never') }}</option>
<option value="after_vote">{{ __('After voting') }}</option>
<option value="after_close">{{ __('After the poll closes') }}</option>
</select>
</div>
<div>
<label class="block text-sm text-gray-700 mb-2">{{ __('Verification') }}</label>
<select wire:model="form.verification" class="w-full border border-primary-300 bg-theme-background rounded-md shadow-sm text-sm">
<option value="public">{{ __('Public: receipts and anonymous ballot list') }}</option>
<option value="receipts">{{ __('Receipts only') }}</option>
<option value="off">{{ __('Off') }}</option>
</select>
</div>
</div>
<div class="space-y-3">
@include('polls.types._checkbox', ['model' => 'form.allow_vote_change', 'label' => __('Voters may change their vote until the poll closes')])
{{-- Anonymity was promised to the voters: it can be switched on, never off, once people voted --}}
@include('polls.types._checkbox', ['model' => 'form.anonymous', 'label' => __('Anonymous: hide who voted in the results (votes are still recorded once per voter)'), 'disabled' => $hasVotes && $editingPoll?->anonymous])
@include('polls.types._error', ['field' => 'form.anonymous'])
</div>
</div>
@@ -0,0 +1,18 @@
@php($isOpenNow = $poll->isOpen())
<span class="inline-flex px-2 py-1 text-xs font-medium rounded-full
@if ($poll->status === 'draft') bg-gray-100 text-gray-800
@elseif ($poll->status === 'open' && $isOpenNow) bg-green-100 text-green-800
@elseif ($poll->status === 'open') bg-yellow-100 text-yellow-800
@else bg-blue-100 text-blue-800 @endif">
@if ($poll->status === 'draft')
{{ __('Draft') }}
@elseif ($poll->status === 'open' && $isOpenNow)
{{ __('Open') }}
@elseif ($poll->status === 'open' && $poll->opens_at?->isFuture())
{{ __('Scheduled') }}
@elseif ($poll->status === 'open')
{{ __('Ended') }}
@else
{{ __('Closed') }}
@endif
</span>
@@ -0,0 +1,66 @@
<div class="space-y-6">
<p class="text-sm text-theme-secondary">{{ __('Leave everything empty to show the poll to all profiles. Each filter you set narrows the audience.') }}</p>
<div>
<label class="block text-sm text-gray-700 mb-2">{{ __('Profile types') }}</label>
<div class="flex flex-wrap gap-x-6 gap-y-2">
@foreach (['App\Models\User' => __('Users'), 'App\Models\Organization' => __('Organizations'), 'App\Models\Bank' => __('Banks'), 'App\Models\Admin' => __('Admins')] as $class => $label)
<label class="flex items-center space-x-3">
<input type="checkbox" wire:model.live="targeting.target_profile_types" value="{{ $class }}"
class="rounded border-gray-300 text-theme-brand shadow-sm focus:border-theme-brand focus:ring focus:ring-theme-brand focus:ring-opacity-50">
<span class="text-sm text-gray-700">{{ $label }}</span>
</label>
@endforeach
</div>
</div>
<div>
@include('polls.types._checkbox', ['model' => 'targeting.filter_by_location', 'label' => __('Only profiles in a location')])
{{-- x-show on $wire: the checkbox is deferred, so a server-side class would not follow it --}}
<div class="mt-3" x-data x-show="$wire.targeting.filter_by_location" @style(['display: none' => empty($targeting['filter_by_location'])])>
<div wire:ignore>
<livewire:mailings.location-filter wire:key="poll-location-filter-{{ $editingPollId }}"
:selectedCountries="$targeting['location_country_ids'] ?? []"
:selectedDivisions="$targeting['location_division_ids'] ?? []"
:selectedCities="$targeting['location_city_ids'] ?? []"
:selectedDistricts="$targeting['location_district_ids'] ?? []" />
</div>
<p class="mt-1 text-xs text-theme-secondary">{{ __('Only the most specific level you select counts: a city inside a selected country narrows to that city.') }}</p>
</div>
</div>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div>
<label class="block text-sm text-gray-700 mb-2">{{ __('Last login from') }}</label>
@include('polls.types._date', ['model' => 'targeting.last_login_from', 'key' => 'poll-' . $editingPollId . '-last_login_from', 'withTime' => false])
@include('polls.types._error', ['field' => 'targeting.last_login_from'])
</div>
<div>
<label class="block text-sm text-gray-700 mb-2">{{ __('Last login until') }}</label>
@include('polls.types._date', ['model' => 'targeting.last_login_until', 'key' => 'poll-' . $editingPollId . '-last_login_until', 'withTime' => false])
@include('polls.types._error', ['field' => 'targeting.last_login_until'])
</div>
<div>
<label class="block text-sm text-gray-700 mb-2">{{ __('Registered from') }}</label>
@include('polls.types._date', ['model' => 'targeting.registered_from', 'key' => 'poll-' . $editingPollId . '-registered_from', 'withTime' => false])
@include('polls.types._error', ['field' => 'targeting.registered_from'])
</div>
<div>
<label class="block text-sm text-gray-700 mb-2">{{ __('Registered until') }}</label>
@include('polls.types._date', ['model' => 'targeting.registered_until', 'key' => 'poll-' . $editingPollId . '-registered_until', 'withTime' => false])
@include('polls.types._error', ['field' => 'targeting.registered_until'])
</div>
</div>
<div class="p-3 bg-theme-surface rounded-md border border-theme-border flex flex-wrap items-center gap-3">
<x-jetstream.secondary-button wire:click="countMatchingProfiles" wire:loading.attr="disabled" wire:target="countMatchingProfiles">
{{ __('Count matching profiles') }}
</x-jetstream.secondary-button>
@if ($matchingCounts !== null)
<p class="text-sm text-theme-primary">
<strong>{{ trans_choice(':count profile|:count profiles', array_sum($matchingCounts), ['count' => number_format(array_sum($matchingCounts))]) }}</strong>
({{ collect($matchingCounts)->map(fn ($count, $type) => __($type . 's') . ': ' . number_format($count))->implode(', ') }})
</p>
@endif
</div>
</div>