Snapshot of main at 9f39a6e1e (2026-11-02)
This commit is contained in:
commit
6ccf1792d7
2580 files changed
+1040202
No files matched your search
@@ -0,0 +1,180 @@
|
||||
<div>
|
||||
{{-- Tagify single-tag picker --}}
|
||||
<div x-data="callTagPicker">
|
||||
<label class="block text-sm text-theme-primary mb-2">
|
||||
{{ __('Requested activity or skill') }}
|
||||
</label>
|
||||
<div wire:ignore>
|
||||
<input x-ref="callTagsInput"
|
||||
type="text"
|
||||
data-suggestions='@json($suggestions)'
|
||||
placeholder="{{ __('Select a label, or create a new one') }}"
|
||||
class="w-full" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- New Tag Creation Modal --}}
|
||||
@if ($modalVisible)
|
||||
<form wire:submit.prevent="createTag">
|
||||
<x-jetstream.dialog-modal wire:model.live="modalVisible">
|
||||
<x-slot name="title">
|
||||
{{ str_replace('@PLATFORM_NAME@', platform_name(), __('Add a new activity or skill to @PLATFORM_NAME@')) }}
|
||||
</x-slot>
|
||||
|
||||
<x-slot name="content">
|
||||
<div class="my-3 text-xl">
|
||||
<span class="bg-{{ $categoryColor }}-300 inline-flex items-center rounded-md px-3 py-2 text-sm font-normal">
|
||||
{{ $newTag['name'] }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 grid grid-cols-1 gap-6">
|
||||
<x-input
|
||||
label="{{ __('Activity tag (min. 2 words)') }}"
|
||||
placeholder="{{ __('Accurate and unique name for this activity, avoid vague or general keywords') }}"
|
||||
wire:model.live="newTag.name" />
|
||||
</div>
|
||||
|
||||
@if (!$sessionLanguageOk)
|
||||
<div class="mt-3 grid grid-cols-1 gap-6">
|
||||
@php
|
||||
$locale = app()->getLocale();
|
||||
$localeName = \Locale::getDisplayName($locale, $locale);
|
||||
@endphp
|
||||
<x-checkbox :disabled="$sessionLanguageOk" id="sessionLang-ignore-checkbox"
|
||||
label="{{ __('This tag is in :locale.', ['locale' => $localeName]) }}"
|
||||
wire:model.live="sessionLanguageIgnored" />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($translationPossible && $translationAllowed)
|
||||
<div class="mt-6 grid grid-cols-1 gap-6">
|
||||
<x-checkbox id="checkbox"
|
||||
label="{{ __('Attach a translation to this tag (recommended)') }}"
|
||||
wire:model.live="translationVisible" />
|
||||
</div>
|
||||
|
||||
<div class="mt-6 grid grid-cols-1 gap-6" wire:loading wire:target="translationVisible">
|
||||
<x-mini-button flat icon="" primary rounded spinner />
|
||||
<span>{{ __('Loading...') }}</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div>
|
||||
@if ($translationVisible)
|
||||
<div class="my-6 grid grid-cols-1 gap-6 pl-8 md:grid-cols-2" id="select-translation-language">
|
||||
<x-select :options="$translationLanguages" class="placeholder-theme-light"
|
||||
id="translation-language" label="{{ __('Translation language') }}"
|
||||
option-label="name" option-value="lang_code"
|
||||
placeholder="{{ __('Select a translation language') }}"
|
||||
wire:model.live="selectTranslationLanguage" />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-6 pl-8" wire:loading wire:target="selectTranslationLanguage">
|
||||
<x-mini-button flat icon="" primary rounded spinner />
|
||||
<span>{{ __('Loading...') }}</span>
|
||||
</div>
|
||||
|
||||
@if ($selectTranslationLanguage)
|
||||
@php
|
||||
$translationLang = \App\Models\Language::where('lang_code', $selectTranslationLanguage)->first()?->name ?? $selectTranslationLanguage;
|
||||
@endphp
|
||||
|
||||
<hr class="border-t border-theme-primary" />
|
||||
<x-radio id="radio-select"
|
||||
label="{{ str_replace('@LANGUAGE@', $translationLang, __('Select an existing, untranslated activity tag in @LANGUAGE@')) }}"
|
||||
value="select" wire:model.live="translateRadioButton" />
|
||||
|
||||
<div class="my-6 grid grid-cols-1 gap-6 pl-8 md:grid-cols-2" id="select-translation">
|
||||
@if (count($translationOptions) > 0)
|
||||
<x-select
|
||||
:disabled="$translateRadioButton === 'input'"
|
||||
:options="$translationOptions"
|
||||
class="placeholder-theme-light"
|
||||
id="translation" label="" option-label="name" option-value="tag_id"
|
||||
placeholder="{{ __('Select a translation') }}"
|
||||
wire:model.live="selectTagTranslation" />
|
||||
@else
|
||||
<x-select
|
||||
:disabled="true"
|
||||
:options="[['tag_id' => '', 'name' => __('No translations available'), 'disabled' => true]]"
|
||||
class="placeholder-theme-light"
|
||||
id="translation" label="" option-label="name" option-value="tag_id"
|
||||
placeholder="{{ __('No existing translation available') }}"
|
||||
wire:model.live="selectTagTranslation" />
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<hr class="border-t border-theme-primary" />
|
||||
<x-radio id="radio-input"
|
||||
label="{{ str_replace('@LANGUAGE@', $translationLang, __('Or create a new Activity tag in @LANGUAGE@')) }}"
|
||||
value="input" wire:model.live="translateRadioButton" />
|
||||
|
||||
<div id="input-translation">
|
||||
<div class="mt-6 grid grid-cols-1 gap-6 pl-8">
|
||||
<x-input :disabled="$translateRadioButton === 'select'"
|
||||
label="{{ str_replace('@LANGUAGE@', $translationLang, __('Activity tag in @LANGUAGE@ (min. 2 words)')) }}"
|
||||
placeholder="{{ !empty($newTag['name']) ? '\'' . $newTag['name'] . '\' ' . __('in') . ' ' . $translationLang : __('Activity tag name in') . ' ' . $translationLang }}"
|
||||
wire:key="nameInput" wire:model.live="inputTagTranslation.name" />
|
||||
|
||||
@if (!$transLanguageOk)
|
||||
<div class="grid grid-cols-1 gap-6">
|
||||
@php
|
||||
$localeTranslation = $selectTranslationLanguage ?? '';
|
||||
$localeNameTranslation = \Locale::getDisplayName($localeTranslation, $localeTranslation);
|
||||
@endphp
|
||||
<x-checkbox :disabled="$translateRadioButton === 'select'"
|
||||
id="transLang-ignore-checkbox"
|
||||
label="{{ __('This tag is in :localeTranslation.', ['localeTranslation' => $localeNameTranslation]) }}"
|
||||
wire:model.live="transLanguageIgnored" />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="grid grid-cols-1 gap-6 md:grid-cols-2">
|
||||
<x-select :disabled="$translateRadioButton === 'select'"
|
||||
:options="$categoryOptions"
|
||||
class="placeholder-theme-light" id="category"
|
||||
label="{{ __('Category') }}" option-label="name"
|
||||
option-value="category_id"
|
||||
placeholder="{{ __('Select a category') }}"
|
||||
wire:model.live="newTagCategory" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if (!$translationVisible || !$translationAllowed)
|
||||
<div class="mt-3 grid grid-cols-1 gap-6 md:grid-cols-2">
|
||||
<x-select :options="$categoryOptions" class="placeholder-theme-light" id="category"
|
||||
label="{{ __('Category') }}" option-label="name" option-value="category_id"
|
||||
placeholder="{{ __('Select a category') }}"
|
||||
wire:model.live="newTagCategory" />
|
||||
</div>
|
||||
@error('newTagCategory')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
@endif
|
||||
|
||||
<div class="pt-10 my-3 grid grid-cols-1">
|
||||
<x-skill-tag-warning />
|
||||
</div>
|
||||
</x-slot>
|
||||
|
||||
<x-slot name="footer">
|
||||
<x-jetstream.secondary-button wire:click="cancelCreateTag" wire:loading.attr="disabled">
|
||||
{{ __('Cancel') }}
|
||||
</x-jetstream.secondary-button>
|
||||
<x-jetstream.secondary-button
|
||||
class="ml-3"
|
||||
wire:click="createTag"
|
||||
wire:key="createTagButton"
|
||||
wire:loading.attr="disabled">
|
||||
{{ __('Save') }}
|
||||
</x-jetstream.secondary-button>
|
||||
</x-slot>
|
||||
</x-jetstream.dialog-modal>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
@@ -0,0 +1,215 @@
|
||||
<div>
|
||||
<x-jetstream.button
|
||||
wire:click.prevent="openModal()"
|
||||
class="bg-theme-brand hover:bg-opacity-80">
|
||||
{{ __('New Call') }}
|
||||
</x-jetstream.button>
|
||||
|
||||
{{-- No credits modal --}}
|
||||
<x-jetstream.dialog-modal wire:model.live="showNoCreditsModal" wire:key="showCallNoCreditsModal" closeButton="true">
|
||||
<x-slot name="title">
|
||||
{{ trans_with_platform('Post a @PLATFORM_NAME@ call') }}
|
||||
</x-slot>
|
||||
<x-slot name="content">
|
||||
@if ($spendableBalance !== null)
|
||||
<div class="mb-4 text-sm text-theme-secondary">
|
||||
{{ __('Current balance total') }}: <span class="font-semibold text-theme-primary">{{ tbFormat($spendableBalance) }}</span>
|
||||
</div>
|
||||
@endif
|
||||
@livewire('static-post', ['type' => 'SiteContents\\Call\\NotAllowed', 'limit' => 1, 'hideAuthor' => true, 'fallbackText' => trans_with_platform('You need @PLATFORM_CURRENCY_NAME_PLURAL@ to post a call.')])
|
||||
</x-slot>
|
||||
<x-slot name="footer">
|
||||
<x-jetstream.button wire:click="$set('showNoCreditsModal', false)">
|
||||
{{ __('OK') }}
|
||||
</x-jetstream.button>
|
||||
</x-slot>
|
||||
</x-jetstream.dialog-modal>
|
||||
|
||||
@if ($showModal)
|
||||
<x-jetstream.dialog-modal wire:model="showModal" wire:key="showCallModal" closeButton="true">
|
||||
<x-slot name="title">
|
||||
{{ trans_with_platform('Post a @PLATFORM_NAME@ call') }}
|
||||
</x-slot>
|
||||
|
||||
<x-slot name="content">
|
||||
|
||||
<div class="space-y-6">
|
||||
|
||||
{{-- Tag picker (dedicated component) --}}
|
||||
<livewire:calls.call-skill-input wire:key="call-skill-input" />
|
||||
|
||||
@error('tagId')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
|
||||
{{-- Content --}}
|
||||
@php $contentMax = timebank_config('calls.content_max_input', 200); @endphp
|
||||
<div wire:key="call-content"
|
||||
x-data="{ remaining: {{ $contentMax - mb_strlen($content) }} }"
|
||||
x-init="$nextTick(() => { const ta = $el.querySelector('textarea'); if (ta) { ta.addEventListener('input', () => { remaining = {{ $contentMax }} - ta.value.length; }); } })">
|
||||
<label class="block text-sm text-theme-primary mb-2">
|
||||
{{ __('Description') }}
|
||||
<span class="text-theme-secondary text-xs ml-1" x-text="remaining + ' {{ __('characters left') }}'"></span>
|
||||
</label>
|
||||
<x-textarea
|
||||
placeholder="{{ __('Describe your request in more detail...') }}"
|
||||
rows="4"
|
||||
maxlength="{{ $contentMax }}"
|
||||
class="!text-base"
|
||||
wire:model.blur="content" />
|
||||
@error('content')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
{{-- Description translations (only if the selected tag has other-language translations) --}}
|
||||
@if(!empty($this->remainingTagLocales) || !empty($translationContents))
|
||||
<div wire:key="call-translations" class="space-y-4">
|
||||
@foreach($translationContents as $locale => $text)
|
||||
<div wire:key="call-translation-{{ $locale }}">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<label class="block text-sm text-theme-primary">
|
||||
{{ __('Description in :language', ['language' => __('messages.' . $locale)]) }}
|
||||
</label>
|
||||
<button type="button" wire:click="removeTranslation('{{ $locale }}')" class="text-xs text-red-600 hover:underline">
|
||||
{{ __('Remove') }}
|
||||
</button>
|
||||
</div>
|
||||
<x-textarea
|
||||
rows="4"
|
||||
maxlength="{{ $contentMax }}"
|
||||
class="!text-base"
|
||||
wire:model="translationContents.{{ $locale }}" />
|
||||
@error('translationContents.' . $locale)
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
@if(!empty($this->remainingTagLocales))
|
||||
<div>
|
||||
<label class="block text-sm text-theme-primary mb-1">{{ __('Add translation') }}</label>
|
||||
<p class="text-xs text-theme-secondary mb-2">
|
||||
{{ __('Only languages the selected activity tag has been translated into can be selected.') }}
|
||||
</p>
|
||||
<div class="flex items-center space-x-2">
|
||||
<x-select :clearable="false" :searchable="false" class="!w-48"
|
||||
placeholder="{{ __('Select a language') }}"
|
||||
wire:model="newTranslationLocale">
|
||||
@foreach($this->remainingTagLocales as $locale)
|
||||
<x-select.option label="{{ __('messages.' . $locale) }}" value="{{ $locale }}" />
|
||||
@endforeach
|
||||
</x-select>
|
||||
<x-jetstream.secondary-button wire:click="addTranslation()">
|
||||
{{ __('Add') }}
|
||||
</x-jetstream.secondary-button>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Location --}}
|
||||
<div wire:key="call-location">
|
||||
<label class="block text-sm text-theme-primary mb-2">{{ __('Exchange location') }}</label>
|
||||
<livewire:locations.locations-dropdown
|
||||
:hide-label="true"
|
||||
:country="$country"
|
||||
:city="$city"
|
||||
:division="$division"
|
||||
:district="$district"
|
||||
wire:key="call-location-dropdown-{{ $country }}-{{ $city }}" />
|
||||
@error('country')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
{{-- Expiry date --}}
|
||||
<div wire:key="call-till">
|
||||
<label class="block text-sm text-theme-primary mb-2">{{ __('Expires on') }}</label>
|
||||
@php
|
||||
$tillMinDate = now()->addDay()->format('Y-m-d');
|
||||
$activeProfileType = session('activeProfileType');
|
||||
$tillMaxDays = ($activeProfileType && $activeProfileType !== \App\Models\User::class)
|
||||
? timebank_config('calls.till_max_days_non_user')
|
||||
: timebank_config('calls.till_max_days');
|
||||
$tillMaxDate = $tillMaxDays !== null ? now()->addDays($tillMaxDays)->format('Y-m-d') : null;
|
||||
@endphp
|
||||
<div wire:ignore>
|
||||
<x-flatpickr
|
||||
dateFormat="Y-m-d"
|
||||
altFormat="d-m-Y"
|
||||
:minDate="$tillMinDate"
|
||||
:maxDate="$tillMaxDate"
|
||||
placeholder="{{ __('Select a date') }}"
|
||||
wire:model.defer="till"
|
||||
:config="['altInputClass' => 'w-full mt-1 block border border-theme-border focus:border-theme-accent focus:ring-1 focus:ring-theme-accent rounded-md shadow-sm px-3 py-2 text-sm bg-white']"
|
||||
class="mt-1 block w-full border-theme-border focus:border-theme-accent focus:ring-1 focus:ring-theme-accent rounded-md shadow-sm" />
|
||||
</div>
|
||||
@error('till')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
{{-- Visibility --}}
|
||||
<div wire:key="call-public">
|
||||
<x-checkbox
|
||||
id="call-is-public"
|
||||
label="{{ __('Public, visible for search engines and sharable on social media') }}"
|
||||
wire:model.live="isPublic" />
|
||||
@if ($isPublic)
|
||||
<p class="mt-2 text-sm text-red-600">
|
||||
{{ str_replace(':username', $profileName, __('This exposes your username (:username), your profile photo and your profile and work locations!')) }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</x-slot>
|
||||
|
||||
<x-slot name="footer">
|
||||
<div wire:loading wire:target="save">
|
||||
<x-mini-button flat primary rounded spinner />
|
||||
</div>
|
||||
<x-jetstream.secondary-button
|
||||
class="ml-3 w-32 justify-center"
|
||||
wire:click="$set('showModal', false)"
|
||||
wire:loading.attr="disabled">
|
||||
{{ __('Cancel') }}
|
||||
</x-jetstream.secondary-button>
|
||||
<x-jetstream.button
|
||||
class="ml-3 w-32 justify-center"
|
||||
wire:click.prevent="save()"
|
||||
wire:loading.attr="disabled">
|
||||
{{ __('Publish') }}
|
||||
</x-jetstream.button>
|
||||
</x-slot>
|
||||
</x-jetstream.dialog-modal>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function initCallFlatpickr() {
|
||||
const tillInput = document.querySelector('[wire\\:model\\.defer="till"]');
|
||||
if (tillInput && !tillInput._flatpickr) {
|
||||
if (window.LaravelFlatpickr) {
|
||||
window.LaravelFlatpickr.initializeFlatpickr(tillInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
setTimeout(initCallFlatpickr, 100);
|
||||
|
||||
Livewire.hook('morph.updated', () => {
|
||||
setTimeout(initCallFlatpickr, 200);
|
||||
setTimeout(initCallFlatpickr, 500);
|
||||
});
|
||||
|
||||
Livewire.on('showModal', () => {
|
||||
setTimeout(initCallFlatpickr, 300);
|
||||
setTimeout(initCallFlatpickr, 600);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,230 @@
|
||||
<div>
|
||||
@if ($compact)
|
||||
<x-jetstream.secondary-button
|
||||
title="{{ __('Edit') }}"
|
||||
wire:click.prevent="openModal()"
|
||||
wire:target="openModal"
|
||||
wire:loading.attr="disabled">
|
||||
<x-icon class="h-5 w-5" name="pencil-square" />
|
||||
</x-jetstream.secondary-button>
|
||||
@else
|
||||
<x-jetstream.button wire:click.prevent="openModal()">
|
||||
{{ __('Edit Call') }}
|
||||
</x-jetstream.button>
|
||||
@endif
|
||||
|
||||
@if ($showModal)
|
||||
<x-jetstream.dialog-modal wire:model="showModal" wire:key="editCallModal-{{ $call->id }}" closeButton="true">
|
||||
<x-slot name="title">
|
||||
{{ trans_with_platform('Edit @PLATFORM_NAME@ call') }}
|
||||
</x-slot>
|
||||
|
||||
<x-slot name="content">
|
||||
|
||||
<div class="space-y-6">
|
||||
|
||||
{{-- Tag picker (dedicated component) --}}
|
||||
<livewire:calls.call-skill-input :initial-tag-id="$tagId" wire:key="edit-call-skill-input-{{ $call->id }}" />
|
||||
|
||||
@error('tagId')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
|
||||
{{-- Content --}}
|
||||
@php $contentMax = timebank_config('calls.content_max_input', 200); @endphp
|
||||
<div wire:key="edit-call-content-{{ $call->id }}"
|
||||
x-data="{ remaining: {{ $contentMax - mb_strlen($content) }} }"
|
||||
x-init="$nextTick(() => { const ta = $el.querySelector('textarea'); if (ta) { ta.addEventListener('input', () => { remaining = {{ $contentMax }} - ta.value.length; }); } })">
|
||||
<label class="block text-sm text-theme-primary mb-2">
|
||||
{{ __('Description') }}
|
||||
<span class="text-theme-secondary text-xs ml-1" x-text="remaining + ' {{ __('characters left') }}'"></span>
|
||||
</label>
|
||||
<x-textarea
|
||||
placeholder="{{ __('Describe your request in more detail...') }}"
|
||||
rows="4"
|
||||
maxlength="{{ $contentMax }}"
|
||||
class="!text-base"
|
||||
wire:model.blur="content" />
|
||||
@error('content')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
{{-- Description translations (only if the selected tag has other-language translations) --}}
|
||||
@if(!empty($this->remainingTagLocales) || !empty($translationContents))
|
||||
<div wire:key="edit-call-translations-{{ $call->id }}" class="space-y-4">
|
||||
@foreach($translationContents as $locale => $text)
|
||||
<div wire:key="edit-call-translation-{{ $call->id }}-{{ $locale }}">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<label class="block text-sm text-theme-primary">
|
||||
{{ __('Description in :language', ['language' => __('messages.' . $locale)]) }}
|
||||
</label>
|
||||
<button type="button" wire:click="removeTranslation('{{ $locale }}')" class="text-xs text-red-600 hover:underline">
|
||||
{{ __('Remove') }}
|
||||
</button>
|
||||
</div>
|
||||
<x-textarea
|
||||
rows="4"
|
||||
maxlength="{{ $contentMax }}"
|
||||
class="!text-base"
|
||||
wire:model="translationContents.{{ $locale }}" />
|
||||
@error('translationContents.' . $locale)
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
@if(!empty($this->remainingTagLocales))
|
||||
<div>
|
||||
<label class="block text-sm text-theme-primary mb-1">{{ __('Add translation') }}</label>
|
||||
<p class="text-xs text-theme-secondary mb-2">
|
||||
{{ __('Only languages the selected activity tag has been translated into can be selected.') }}
|
||||
</p>
|
||||
<div class="flex items-center space-x-2">
|
||||
<x-select :clearable="false" :searchable="false" class="!w-48"
|
||||
placeholder="{{ __('Select a language') }}"
|
||||
wire:model="newTranslationLocale">
|
||||
@foreach($this->remainingTagLocales as $locale)
|
||||
<x-select.option label="{{ __('messages.' . $locale) }}" value="{{ $locale }}" />
|
||||
@endforeach
|
||||
</x-select>
|
||||
<x-jetstream.secondary-button wire:click="addTranslation()">
|
||||
{{ __('Add') }}
|
||||
</x-jetstream.secondary-button>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Location --}}
|
||||
<div wire:key="edit-call-location-{{ $call->id }}">
|
||||
<label class="block text-sm text-theme-primary mb-2">{{ __('Exchange location') }}</label>
|
||||
<livewire:locations.locations-dropdown
|
||||
:hide-label="true"
|
||||
:country="$country"
|
||||
:city="$city"
|
||||
:division="$division"
|
||||
:district="$district"
|
||||
wire:key="edit-call-location-dropdown-{{ $country }}-{{ $city }}" />
|
||||
@error('country')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
{{-- Expiry date --}}
|
||||
<div wire:key="edit-call-till-{{ $call->id }}">
|
||||
<label class="block text-sm text-theme-primary mb-2">{{ __('Expires on') }}</label>
|
||||
@php
|
||||
$tillMinDate = now()->addDay()->format('Y-m-d');
|
||||
$callableType = $call->callable_type ?? session('activeProfileType');
|
||||
$tillMaxDays = ($callableType && $callableType !== \App\Models\User::class)
|
||||
? timebank_config('calls.till_max_days_non_user')
|
||||
: timebank_config('calls.till_max_days');
|
||||
$tillMaxDate = $tillMaxDays !== null ? now()->addDays($tillMaxDays)->format('Y-m-d') : null;
|
||||
@endphp
|
||||
<div wire:ignore>
|
||||
<x-flatpickr
|
||||
dateFormat="Y-m-d"
|
||||
altFormat="d-m-Y"
|
||||
:minDate="$tillMinDate"
|
||||
:maxDate="$tillMaxDate"
|
||||
placeholder="{{ __('Select a date') }}"
|
||||
wire:model.defer="till"
|
||||
class="mt-1 block border-theme-border focus:border-theme-accent focus:ring-1 focus:ring-theme-accent rounded-md shadow-sm" />
|
||||
</div>
|
||||
@error('till')
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
{{-- Visibility --}}
|
||||
<div wire:key="edit-call-public-{{ $call->id }}">
|
||||
<x-checkbox
|
||||
id="edit-call-is-public-{{ $call->id }}"
|
||||
label="{{ __('Public, visible for search engines and sharable on social media') }}"
|
||||
wire:model.live="isPublic" />
|
||||
@if ($isPublic)
|
||||
<p class="mt-2 text-sm text-red-600">
|
||||
{{ str_replace(':username', $profileName, __('This exposes your username (:username), your profile photo and your profile and work locations!')) }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</x-slot>
|
||||
|
||||
<x-slot name="footer">
|
||||
<div class="flex w-full items-center justify-between">
|
||||
<x-jetstream.danger-button
|
||||
wire:click="confirmDelete()"
|
||||
wire:loading.attr="disabled">
|
||||
{{ __('Delete') }}
|
||||
</x-jetstream.danger-button>
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
<div wire:loading wire:target="save">
|
||||
<x-mini-button flat primary rounded spinner />
|
||||
</div>
|
||||
<x-jetstream.secondary-button
|
||||
wire:click="$set('showModal', false)"
|
||||
wire:loading.attr="disabled">
|
||||
{{ __('Cancel') }}
|
||||
</x-jetstream.secondary-button>
|
||||
<x-jetstream.button
|
||||
wire:click.prevent="save()"
|
||||
wire:loading.attr="disabled">
|
||||
{{ __('Save') }}
|
||||
</x-jetstream.button>
|
||||
</div>
|
||||
</div>
|
||||
</x-slot>
|
||||
</x-jetstream.dialog-modal>
|
||||
@endif
|
||||
|
||||
{{-- Delete confirmation modal --}}
|
||||
<x-jetstream.dialog-modal wire:model="showDeleteConfirm" wire:key="deleteCallModal-{{ $call->id }}">
|
||||
<x-slot name="title">
|
||||
{{ __('Delete Call') }}
|
||||
</x-slot>
|
||||
|
||||
<x-slot name="content">
|
||||
<p class="text-theme-primary">{{ __('Are you sure you want to delete this call? You can undelete this call later.') }}</p>
|
||||
</x-slot>
|
||||
|
||||
<x-slot name="footer">
|
||||
<x-jetstream.secondary-button
|
||||
wire:click="$set('showDeleteConfirm', false)"
|
||||
wire:loading.attr="disabled">
|
||||
{{ __('Cancel') }}
|
||||
</x-jetstream.secondary-button>
|
||||
<x-jetstream.danger-button
|
||||
class="ml-3"
|
||||
wire:click="delete()"
|
||||
wire:loading.attr="disabled">
|
||||
{{ __('Yes, delete') }}
|
||||
</x-jetstream.danger-button>
|
||||
</x-slot>
|
||||
</x-jetstream.dialog-modal>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function initEditCallFlatpickr() {
|
||||
const tillInput = document.querySelector('[wire\\:key="edit-call-till-{{ $call->id }}"] [wire\\:model\\.defer="till"]');
|
||||
if (tillInput && !tillInput._flatpickr) {
|
||||
if (window.LaravelFlatpickr) {
|
||||
window.LaravelFlatpickr.initializeFlatpickr(tillInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
setTimeout(initEditCallFlatpickr, 100);
|
||||
|
||||
Livewire.hook('morph.updated', () => {
|
||||
setTimeout(initEditCallFlatpickr, 200);
|
||||
setTimeout(initEditCallFlatpickr, 500);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,854 @@
|
||||
<div class="mt-12">
|
||||
|
||||
{{-- Action buttons --}}
|
||||
@if (!$isAdminView)
|
||||
<div class="mb-6">
|
||||
@livewire('calls.create', key('calls-create-manage'))
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Search box --}}
|
||||
<div class="mb-4 flex items-center">
|
||||
<div class="relative w-1/3">
|
||||
<input class="w-full rounded-md border border-theme-primary px-3 py-1 pr-10 text-theme-primary shadow-sm focus:border-theme-accent focus:outline-none focus:ring-1 focus:ring-theme-accent sm:text-sm"
|
||||
placeholder="{{ __('Search keywords') . '...' }}" type="text"
|
||||
wire:keydown.enter="$refresh" wire:model="search">
|
||||
@if ($search)
|
||||
<button class="absolute inset-y-0 right-0 flex items-center pr-3 text-theme-secondary hover:text-theme-primary focus:outline-none"
|
||||
wire:click="$set('search', '')">
|
||||
<x-icon mini name="backspace" solid />
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
<x-jetstream.secondary-button class="ml-3 w-32 justify-center" wire:click="$refresh">
|
||||
{{ __('Search') }}
|
||||
</x-jetstream.secondary-button>
|
||||
</div>
|
||||
|
||||
{{-- Filter dropdowns --}}
|
||||
<div class="mb-4 flex flex-wrap items-center gap-3">
|
||||
{{-- Status filter --}}
|
||||
<div>
|
||||
<x-select :clearable="true" :searchable="false" class="!w-40" style="width: 10rem !important; min-width: 10rem !important;"
|
||||
placeholder="{{ __('Status') }}" wire:model.live="statusFilter">
|
||||
<x-select.option label="{{ __('Active') }}" value="active" />
|
||||
<x-select.option label="{{ __('Paused') }}" value="paused" />
|
||||
<x-select.option label="{{ __('Expired') }}" value="expired" />
|
||||
<x-select.option label="{{ __('Deleted') }}" value="deleted" />
|
||||
</x-select>
|
||||
</div>
|
||||
|
||||
@if ($isAdminView)
|
||||
{{-- Callable type filter --}}
|
||||
<div>
|
||||
<x-select :clearable="true" :searchable="false" class="!w-44" style="width: 11rem !important; min-width: 11rem !important;"
|
||||
placeholder="{{ __('Profile type') }}" wire:model.live="callableFilter">
|
||||
<x-select.option label="{{ __('User') }}" value="user" />
|
||||
<x-select.option label="{{ __('Organization') }}" value="organization" />
|
||||
<x-select.option label="{{ __('Bank') }}" value="bank" />
|
||||
</x-select>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Language filter --}}
|
||||
@if (count($availableLocales) > 1)
|
||||
<div>
|
||||
<x-select :clearable="true" :searchable="false" class="!w-36" style="width: 9rem !important; min-width: 9rem !important;"
|
||||
placeholder="{{ __('Language') }}" wire:model.live="localeFilter">
|
||||
@foreach ($availableLocales as $lang)
|
||||
<x-select.option label="{{ $lang['name'] }}" value="{{ $lang['id'] }}" />
|
||||
@endforeach
|
||||
</x-select>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- Bulk action buttons --}}
|
||||
<div class="mb-6 flex items-center justify-end space-x-4">
|
||||
{{-- Print selection --}}
|
||||
<x-jetstream.secondary-button
|
||||
:disabled="$bulkDisabled"
|
||||
wire:click.prevent="confirmBulkPrint">
|
||||
<x-icon class="mr-3 h-5 w-5" name="printer" />
|
||||
{{ __('Print') }} {{ __('selection') }}
|
||||
</x-jetstream.secondary-button>
|
||||
|
||||
@if ($statusFilter === 'deleted')
|
||||
@can('update calls')
|
||||
<x-jetstream.button
|
||||
:disabled="$bulkDisabled"
|
||||
onclick="confirm('{{ __('Are you sure you want to restore the selected calls?') }}') || event.stopImmediatePropagation()"
|
||||
wire:click.prevent="undeleteSelected">
|
||||
<x-icon class="mr-3 h-5 w-5" name="arrow-path" />
|
||||
{{ __('Undelete') }} {{ __('selection') }}
|
||||
</x-jetstream.button>
|
||||
@endcan
|
||||
@else
|
||||
@can('delete calls')
|
||||
@if ($isAdminView)
|
||||
<x-jetstream.danger-button
|
||||
:disabled="$bulkDisabled"
|
||||
wire:click.prevent="confirmAdminDelete">
|
||||
<x-icon class="mr-3 h-5 w-5" name="trash" />
|
||||
{{ __('Delete') }} {{ __('selection') }}
|
||||
</x-jetstream.danger-button>
|
||||
@else
|
||||
<x-jetstream.danger-button
|
||||
:disabled="$bulkDisabled"
|
||||
wire:click.prevent="confirmBulkDelete">
|
||||
<x-icon class="mr-3 h-5 w-5" name="trash" />
|
||||
{{ __('Delete') }} {{ __('selection') }}
|
||||
</x-jetstream.danger-button>
|
||||
@endif
|
||||
@endcan
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- Table --}}
|
||||
<div class="bg-white shadow-sm rounded-lg overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<input type="checkbox" wire:model.live="selectAll"
|
||||
title="{{ __('Select all on current page') }}"
|
||||
class="rounded border-gray-300 text-theme-brand focus:border-theme-accent focus:ring-1 focus:ring-theme-accent">
|
||||
</th>
|
||||
|
||||
@if ($isAdminView)
|
||||
{{-- ID --}}
|
||||
<th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer"
|
||||
wire:click="sortBy('id')">
|
||||
<div class="flex items-center">
|
||||
{{ __('ID') }}
|
||||
@if ($sortField === 'id')
|
||||
<x-icon :name="$sortDirection === 'asc' ? 'chevron-up' : 'chevron-down'" class="ml-1 h-4 w-4" micro />
|
||||
@endif
|
||||
</div>
|
||||
</th>
|
||||
@endif
|
||||
|
||||
{{-- Lang --}}
|
||||
<th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer"
|
||||
wire:click="sortBy('locale')">
|
||||
<div class="flex items-center">
|
||||
{{ __('Lang.') }}
|
||||
@if ($sortField === 'locale')
|
||||
<x-icon :name="$sortDirection === 'asc' ? 'chevron-up' : 'chevron-down'" class="ml-1 h-4 w-4" micro />
|
||||
@endif
|
||||
</div>
|
||||
</th>
|
||||
|
||||
@if ($isAdminView)
|
||||
<th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
{{ __('Profile') }}
|
||||
</th>
|
||||
@endif
|
||||
|
||||
{{-- Tag --}}
|
||||
<th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
{{ __('Tag') }}
|
||||
</th>
|
||||
|
||||
{{-- Description --}}
|
||||
<th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-full">
|
||||
{{ __('Description') }}
|
||||
</th>
|
||||
|
||||
{{-- Public --}}
|
||||
<th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider whitespace-nowrap">
|
||||
{{ __('Public') }}
|
||||
</th>
|
||||
|
||||
{{-- Expires --}}
|
||||
<th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider whitespace-nowrap cursor-pointer"
|
||||
wire:click="sortBy('till')">
|
||||
<div class="flex items-center">
|
||||
{{ __('Expires') }}
|
||||
@if ($sortField === 'till')
|
||||
<x-icon :name="$sortDirection === 'asc' ? 'chevron-up' : 'chevron-down'" class="ml-1 h-4 w-4" micro />
|
||||
@endif
|
||||
</div>
|
||||
</th>
|
||||
|
||||
<th class="px-3 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider whitespace-nowrap" style="min-width: 9rem;">
|
||||
{{ __('Actions') }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
@forelse ($calls as $call)
|
||||
@php $dimClass = $call->trashed() ? 'opacity-50' : ''; @endphp
|
||||
<tr wire:key="calls-row-{{ $call->id }}">
|
||||
<td class="px-3 py-4 whitespace-nowrap {{ $dimClass }}">
|
||||
<input type="checkbox" value="{{ $call->id }}"
|
||||
wire:model.live="bulkSelected"
|
||||
class="rounded border-gray-300 text-theme-brand focus:border-theme-accent focus:ring-1 focus:ring-theme-accent">
|
||||
</td>
|
||||
|
||||
@if ($isAdminView)
|
||||
<td class="px-3 py-4 whitespace-nowrap text-sm text-gray-500 {{ $dimClass }}">
|
||||
{{ $call->id }}
|
||||
</td>
|
||||
@endif
|
||||
|
||||
<td class="px-3 py-4 whitespace-nowrap text-sm font-medium text-gray-700 uppercase {{ $dimClass }}">
|
||||
{{ $call->translations->pluck('locale')->implode(', ') ?: '-' }}
|
||||
</td>
|
||||
|
||||
@if ($isAdminView)
|
||||
<td class="px-3 py-4 whitespace-nowrap {{ $dimClass }}">
|
||||
@if ($call->callable)
|
||||
<div class="relative block cursor-pointer flex-shrink-0"
|
||||
onclick="window.location='{{ $call->callable->profile_url }}'"
|
||||
title="{{ $call->callable->name }} ({{ class_basename($call->callable_type) }})">
|
||||
<img src="{{ $call->callable->profile_photo_url }}"
|
||||
alt="{{ $call->callable->name }}"
|
||||
class="h-6 w-6 rounded-full profile-photo object-cover outline outline-1 outline-offset-0 outline-gray-600">
|
||||
</div>
|
||||
@else
|
||||
<span class="text-sm text-gray-400">-</span>
|
||||
@endif
|
||||
</td>
|
||||
@endif
|
||||
|
||||
<td class="px-3 py-4 whitespace-nowrap {{ $dimClass }}">
|
||||
@if ($call->tag)
|
||||
@php
|
||||
$tagContext = $call->tag->contexts->first();
|
||||
$tagColor = $tagContext?->category?->relatedColor ?? 'gray';
|
||||
$tagCategoryId = $tagContext?->category?->id;
|
||||
@endphp
|
||||
<a href="{{ route('search.show') }}?category={{ $tagCategoryId }}"
|
||||
class="inline-flex items-center rounded-md px-2 py-1 text-xs font-medium bg-{{ $tagColor }}-400 hover:opacity-80">
|
||||
{{ $call->tag->translation?->name ?? $call->tag->name }}
|
||||
</a>
|
||||
@else
|
||||
<span class="text-sm text-gray-400">-</span>
|
||||
@endif
|
||||
</td>
|
||||
|
||||
{{-- Description --}}
|
||||
<td class="px-3 py-4 w-full max-w-0 {{ $dimClass }}">
|
||||
@php
|
||||
$desc = $call->translations->firstWhere('locale', app()->getLocale())?->content
|
||||
?? $call->translations->firstWhere('locale', config('app.fallback_locale'))?->content
|
||||
?? $call->translations->first()?->content;
|
||||
@endphp
|
||||
<div class="text-sm text-gray-700 truncate" title="{{ $desc }}">
|
||||
{{ $desc ?? '-' }}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
{{-- Public --}}
|
||||
<td class="px-3 py-4 whitespace-nowrap text-sm text-gray-500 {{ $dimClass }}">
|
||||
{{ $call->is_public ? __('Yes') : __('No') }}
|
||||
</td>
|
||||
|
||||
<td class="px-3 py-4 whitespace-nowrap {{ $dimClass }}">
|
||||
@if ($call->till)
|
||||
<div class="text-sm leading-tight {{ $call->till < now() ? 'text-red-500' : 'text-gray-500' }}">
|
||||
<div>{{ $call->till->translatedFormat('M j') }}</div>
|
||||
<div>{{ $call->till->translatedFormat('Y') }}</div>
|
||||
</div>
|
||||
@else
|
||||
<span class="text-sm text-gray-400">-</span>
|
||||
@endif
|
||||
</td>
|
||||
|
||||
<td class="px-3 py-4 whitespace-nowrap text-center text-sm font-medium" style="min-width: 9rem;">
|
||||
@if (!$call->trashed())
|
||||
<div class="flex items-center justify-center gap-1 flex-nowrap"
|
||||
x-data="{ loading: false }"
|
||||
x-on:click="loading = true"
|
||||
x-on:admin-action-ready.window="loading = false"
|
||||
x-on:pause-publish-done.window="loading = false"
|
||||
x-on:edit-done.window="loading = false">
|
||||
{{-- Pause / Publish / Blocked indicator --}}
|
||||
@if (!$isAdminView && $call->is_suppressed)
|
||||
<x-jetstream.danger-button title="{{ __('Publication blocked due to policy violation') }}" disabled>
|
||||
<x-icon class="h-5 w-5" name="exclamation-triangle" solid />
|
||||
</x-jetstream.danger-button>
|
||||
@elseif ($isAdminView)
|
||||
{{-- Admin: toggle is_paused, never touch till --}}
|
||||
@can('publish calls')
|
||||
@if ($call->is_paused)
|
||||
<x-jetstream.secondary-button
|
||||
no-spinner
|
||||
title="{{ __('Publish') }}"
|
||||
wire:click="confirmAdminAction({{ $call->id }}, 'publish')"
|
||||
x-bind:disabled="loading">
|
||||
<span x-show="!loading"><x-icon class="h-5 w-5" name="play-circle" solid /></span>
|
||||
<span x-show="loading" class="flex items-center justify-center"><svg class="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg></span>
|
||||
</x-jetstream.secondary-button>
|
||||
@else
|
||||
<x-jetstream.secondary-button
|
||||
no-spinner
|
||||
title="{{ __('Pause') }}"
|
||||
wire:click="confirmAdminAction({{ $call->id }}, 'pause')"
|
||||
x-bind:disabled="loading">
|
||||
<span x-show="!loading"><x-icon class="h-5 w-5" name="pause-circle" solid /></span>
|
||||
<span x-show="loading" class="flex items-center justify-center"><svg class="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg></span>
|
||||
</x-jetstream.secondary-button>
|
||||
@endif
|
||||
@endcan
|
||||
@elseif (!$call->is_paused && ($call->till === null || $call->till > now()))
|
||||
{{-- Non-admin: pause button sets is_paused = true --}}
|
||||
@can('unpublish calls')
|
||||
<x-jetstream.secondary-button
|
||||
no-spinner
|
||||
title="{{ __('Pause') }}"
|
||||
wire:click="pause({{ $call->id }})"
|
||||
x-bind:disabled="loading">
|
||||
<span x-show="!loading"><x-icon class="h-5 w-5" name="pause-circle" solid /></span>
|
||||
<span x-show="loading" class="flex items-center justify-center"><svg class="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg></span>
|
||||
</x-jetstream.secondary-button>
|
||||
@endcan
|
||||
@else
|
||||
{{-- Non-admin: publish button (shown when paused or expired) --}}
|
||||
@can('publish calls')
|
||||
<x-jetstream.secondary-button
|
||||
no-spinner
|
||||
title="{{ $canPublish ? __('Publish') : trans_with_platform(__('You need @PLATFORM_CURRENCY_NAME_PLURAL@ to post a call.')) }}"
|
||||
wire:click="publish({{ $call->id }})"
|
||||
x-bind:disabled="{{ $canPublish ? 'false' : 'true' }} || loading">
|
||||
<span x-show="!loading"><x-icon class="h-5 w-5" name="play-circle" solid /></span>
|
||||
<span x-show="loading" class="flex items-center justify-center"><svg class="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg></span>
|
||||
</x-jetstream.secondary-button>
|
||||
@endcan
|
||||
@endif
|
||||
|
||||
@if ($isAdminView)
|
||||
{{-- Block / Unblock --}}
|
||||
@can('manage calls')
|
||||
@if ($call->is_suppressed)
|
||||
<x-jetstream.danger-button
|
||||
title="{{ __('Unblock') }}"
|
||||
wire:click="unblock({{ $call->id }})"
|
||||
wire:target="unblock({{ $call->id }})"
|
||||
wire:loading.attr="disabled">
|
||||
<x-icon class="h-5 w-5" name="exclamation-triangle" solid />
|
||||
</x-jetstream.danger-button>
|
||||
@else
|
||||
<x-jetstream.secondary-button
|
||||
title="{{ __('Block publication') }}"
|
||||
wire:click="block({{ $call->id }})"
|
||||
wire:target="block({{ $call->id }})"
|
||||
wire:loading.attr="disabled">
|
||||
<x-icon class="h-5 w-5" name="exclamation-triangle" />
|
||||
</x-jetstream.secondary-button>
|
||||
@endif
|
||||
@endcan
|
||||
@else
|
||||
{{-- Edit --}}
|
||||
@can('update calls')
|
||||
<x-jetstream.secondary-button
|
||||
title="{{ __('Edit') }}"
|
||||
x-on:click.stop="$wire.call('openEdit', {{ $call->id }})"
|
||||
wire:target="openEdit({{ $call->id }})"
|
||||
wire:loading.attr="disabled">
|
||||
<x-icon class="h-5 w-5" name="pencil-square" />
|
||||
</x-jetstream.secondary-button>
|
||||
@endcan
|
||||
@endif
|
||||
|
||||
{{-- View --}}
|
||||
<x-jetstream.secondary-button
|
||||
title="{{ __('View') }}"
|
||||
x-on:click.stop="window.open('{{ route('call.show', ['id' => $call->id]) }}', '_blank')">
|
||||
<x-icon class="h-5 w-5" mini name="arrow-top-right-on-square" />
|
||||
</x-jetstream.secondary-button>
|
||||
|
||||
{{-- Delete --}}
|
||||
@can('delete calls')
|
||||
<x-jetstream.danger-button
|
||||
title="{{ __('Delete') }}"
|
||||
wire:click="confirmDeleteRow({{ $call->id }})"
|
||||
wire:target="confirmDeleteRow({{ $call->id }})"
|
||||
wire:loading.attr="disabled">
|
||||
<x-icon class="h-5 w-5" name="trash" />
|
||||
</x-jetstream.danger-button>
|
||||
@endcan
|
||||
</div>
|
||||
@else
|
||||
{{-- Deleted: View + Restore --}}
|
||||
<div class="flex items-center justify-center gap-1 flex-nowrap">
|
||||
<x-jetstream.secondary-button
|
||||
title="{{ __('View') }}"
|
||||
x-on:click.stop="window.open('{{ route('call.show', ['id' => $call->id]) }}', '_blank')">
|
||||
<x-icon class="h-5 w-5" mini name="arrow-top-right-on-square" />
|
||||
</x-jetstream.secondary-button>
|
||||
@can('update calls')
|
||||
<x-jetstream.secondary-button
|
||||
title="{{ __('Restore call') }}"
|
||||
wire:click="confirmRestoreRow({{ $call->id }})"
|
||||
wire:target="confirmRestoreRow({{ $call->id }})"
|
||||
wire:loading.attr="disabled">
|
||||
<x-icon class="h-5 w-5" name="arrow-path" />
|
||||
</x-jetstream.secondary-button>
|
||||
@endcan
|
||||
</div>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="{{ $isAdminView ? 9 : 7 }}" class="px-6 py-12 text-center text-gray-500">
|
||||
{{ __('No results found') }}
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{-- Pagination --}}
|
||||
@if ($calls->hasPages())
|
||||
<div class="bg-white shadow-sm rounded-lg mt-4">
|
||||
<div class="py-3 border-t border-gray-200 flex items-center justify-between px-4">
|
||||
<div class="flex items-center">
|
||||
<select class="w-20 rounded-md border border-theme-primary bg-theme-background px-3 py-2 text-theme-primary shadow-sm focus:border-theme-accent focus:outline-none focus:ring-1 focus:ring-theme-accent sm:text-sm"
|
||||
wire:model.live="perPage">
|
||||
<option value="10">10</option>
|
||||
<option value="20">20</option>
|
||||
<option value="50">50</option>
|
||||
</select>
|
||||
<span class="ml-2 text-sm text-theme-secondary">{{ __('per page') }}</span>
|
||||
</div>
|
||||
{{ $calls->links('livewire.long-paginator') }}
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Inline Edit Modal --}}
|
||||
<x-jetstream.dialog-modal wire:model.live="showEditModal" wire:key="manage-edit-modal" closeButton="true">
|
||||
<x-slot name="title">
|
||||
{{ trans_with_platform('Edit @PLATFORM_NAME@ call') }}
|
||||
</x-slot>
|
||||
|
||||
<x-slot name="content">
|
||||
<div class="space-y-6">
|
||||
|
||||
{{-- Tag picker --}}
|
||||
<livewire:calls.call-skill-input :initial-tag-id="$editTagId" wire:key="manage-edit-tag-{{ $editCallId }}" />
|
||||
@error('editTagId') <p class="mt-1 text-sm text-red-600">{{ $message }}</p> @enderror
|
||||
|
||||
{{-- Content --}}
|
||||
@php $contentMax = timebank_config('calls.content_max_input', 200); @endphp
|
||||
<div x-data="{ remaining: {{ $contentMax - mb_strlen($editContent) }} }"
|
||||
x-init="$nextTick(() => { const ta = $el.querySelector('textarea'); if (ta) { ta.addEventListener('input', () => { remaining = {{ $contentMax }} - ta.value.length; }); } })">
|
||||
<label class="block text-sm text-theme-primary mb-2">
|
||||
{{ __('Description') }}
|
||||
<span class="text-theme-secondary text-xs ml-1" x-text="remaining + ' {{ __('characters left') }}'"></span>
|
||||
</label>
|
||||
<x-textarea
|
||||
placeholder="{{ __('Describe your request in more detail...') }}"
|
||||
rows="4"
|
||||
maxlength="{{ $contentMax }}"
|
||||
class="!text-base"
|
||||
wire:model.blur="editContent" />
|
||||
@error('editContent') <p class="mt-1 text-sm text-red-600">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
{{-- Description translations (only if the selected tag has other-language translations) --}}
|
||||
@if(!empty($this->editRemainingTagLocales) || !empty($editTranslationContents))
|
||||
<div wire:key="manage-edit-translations-{{ $editCallId }}" class="space-y-4">
|
||||
@foreach($editTranslationContents as $locale => $text)
|
||||
<div wire:key="manage-edit-translation-{{ $editCallId }}-{{ $locale }}">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<label class="block text-sm text-theme-primary">
|
||||
{{ __('Description in :language', ['language' => __('messages.' . $locale)]) }}
|
||||
</label>
|
||||
<button type="button" wire:click="removeEditTranslation('{{ $locale }}')" class="text-xs text-red-600 hover:underline">
|
||||
{{ __('Remove') }}
|
||||
</button>
|
||||
</div>
|
||||
<x-textarea
|
||||
rows="4"
|
||||
maxlength="{{ $contentMax }}"
|
||||
class="!text-base"
|
||||
wire:model="editTranslationContents.{{ $locale }}" />
|
||||
@error('editTranslationContents.' . $locale)
|
||||
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
@if(!empty($this->editRemainingTagLocales))
|
||||
<div>
|
||||
<label class="block text-sm text-theme-primary mb-1">{{ __('Add translation') }}</label>
|
||||
<p class="text-xs text-theme-secondary mb-2">
|
||||
{{ __('Only languages the selected activity tag has been translated into can be selected.') }}
|
||||
</p>
|
||||
<div class="flex items-center space-x-2">
|
||||
<x-select :clearable="false" :searchable="false" class="!w-48"
|
||||
placeholder="{{ __('Select a language') }}"
|
||||
wire:model="editNewTranslationLocale">
|
||||
@foreach($this->editRemainingTagLocales as $locale)
|
||||
<x-select.option label="{{ __('messages.' . $locale) }}" value="{{ $locale }}" />
|
||||
@endforeach
|
||||
</x-select>
|
||||
<x-jetstream.secondary-button wire:click="addEditTranslation()">
|
||||
{{ __('Add') }}
|
||||
</x-jetstream.secondary-button>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Location --}}
|
||||
<div>
|
||||
<label class="block text-sm text-theme-primary mb-2">{{ __('Exchange location') }}</label>
|
||||
<livewire:locations.locations-dropdown
|
||||
:hide-label="true"
|
||||
:country="$editCountry"
|
||||
:city="$editCity"
|
||||
:division="$editDivision"
|
||||
:district="$editDistrict"
|
||||
wire:key="manage-edit-location-dropdown-{{ $editCountry }}-{{ $editCity }}" />
|
||||
@error('editCountry') <p class="mt-1 text-sm text-red-600">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
{{-- Expiry date --}}
|
||||
<div>
|
||||
<label class="block text-sm text-theme-primary mb-2">{{ __('Expires on') }}</label>
|
||||
@php
|
||||
$tillMinDate = now()->addDay()->format('Y-m-d');
|
||||
$callableType = ($editCall?->callable_type) ?? session('activeProfileType');
|
||||
$tillMaxDays = ($callableType && $callableType !== \App\Models\User::class)
|
||||
? timebank_config('calls.till_max_days_non_user')
|
||||
: timebank_config('calls.till_max_days');
|
||||
$tillMaxDate = $tillMaxDays !== null ? now()->addDays($tillMaxDays)->format('Y-m-d') : null;
|
||||
@endphp
|
||||
<div wire:ignore>
|
||||
<x-flatpickr
|
||||
dateFormat="Y-m-d"
|
||||
altFormat="d-m-Y"
|
||||
:minDate="$tillMinDate"
|
||||
:maxDate="$tillMaxDate"
|
||||
:value="$editTill"
|
||||
placeholder="{{ __('Select a date') }}"
|
||||
wire:model.defer="editTill"
|
||||
:config="['altInputClass' => 'w-full mt-1 block border border-theme-border focus:border-theme-accent focus:ring-1 focus:ring-theme-accent rounded-md shadow-sm px-3 py-2 text-sm bg-white']"
|
||||
class="mt-1 block w-full border-theme-border focus:border-theme-accent focus:ring-1 focus:ring-theme-accent rounded-md shadow-sm" />
|
||||
</div>
|
||||
@error('editTill') <p class="mt-1 text-sm text-red-600">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
{{-- Visibility --}}
|
||||
<div>
|
||||
<x-checkbox
|
||||
id="manage-edit-is-public"
|
||||
label="{{ __('Public, visible for search engines and sharable on social media') }}"
|
||||
wire:model.live="editIsPublic" />
|
||||
@if ($editIsPublic)
|
||||
<p class="mt-2 text-sm text-red-600">
|
||||
{{ str_replace(':username', $editCall?->callable?->name ?? '', __('Heads up: this exposes your username (:username) and exchange location.')) }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</x-slot>
|
||||
|
||||
<x-slot name="footer">
|
||||
<div class="flex w-full flex-wrap items-center justify-between gap-y-3">
|
||||
@can('delete calls')
|
||||
<x-jetstream.danger-button
|
||||
wire:click="confirmDelete()"
|
||||
wire:loading.attr="disabled">
|
||||
{{ __('Delete') }}
|
||||
</x-jetstream.danger-button>
|
||||
@endcan
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
<x-jetstream.secondary-button
|
||||
wire:click="$set('showEditModal', false)"
|
||||
wire:loading.attr="disabled">
|
||||
{{ __('Cancel') }}
|
||||
</x-jetstream.secondary-button>
|
||||
<x-jetstream.button
|
||||
wire:click.prevent="saveEdit()"
|
||||
wire:loading.attr="disabled"
|
||||
wire:loading.class="opacity-75">
|
||||
{{ __('Save') }}
|
||||
</x-jetstream.button>
|
||||
</div>
|
||||
</div>
|
||||
</x-slot>
|
||||
</x-jetstream.dialog-modal>
|
||||
|
||||
{{-- Delete confirmation modal --}}
|
||||
<x-jetstream.dialog-modal wire:model.live="showDeleteConfirm" wire:key="manage-delete-confirm">
|
||||
<x-slot name="title">{{ __('Delete Call') }}</x-slot>
|
||||
<x-slot name="content">
|
||||
<p class="text-theme-primary">{{ __('Are you sure you want to delete this call? You can undelete this call later.') }}</p>
|
||||
</x-slot>
|
||||
<x-slot name="footer">
|
||||
<x-jetstream.secondary-button
|
||||
wire:click="$set('showDeleteConfirm', false)"
|
||||
wire:loading.attr="disabled">
|
||||
{{ __('Cancel') }}
|
||||
</x-jetstream.secondary-button>
|
||||
@can('delete calls')
|
||||
<x-jetstream.danger-button
|
||||
class="ml-3"
|
||||
wire:click="deleteCall()"
|
||||
wire:loading.attr="disabled">
|
||||
{{ __('Yes, delete') }}
|
||||
</x-jetstream.danger-button>
|
||||
@endcan
|
||||
</x-slot>
|
||||
</x-jetstream.dialog-modal>
|
||||
|
||||
{{-- Non-admin bulk delete confirmation modal --}}
|
||||
@if (!$isAdminView)
|
||||
<x-jetstream.dialog-modal wire:model.live="showDeleteConfirmModal" closeButton="true">
|
||||
<x-slot name="title">
|
||||
{{ __('Delete selection?') }}
|
||||
</x-slot>
|
||||
<x-slot name="content">
|
||||
<p class="text-theme-primary">
|
||||
{{ trans_choice('Are you sure you want to delete :count call? This can always be undone later.|Are you sure you want to delete :count calls? This can always be undone later.', count($bulkSelected), ['count' => count($bulkSelected)]) }}
|
||||
</p>
|
||||
</x-slot>
|
||||
<x-slot name="footer">
|
||||
<div class="flex items-center gap-3 justify-end">
|
||||
<x-jetstream.secondary-button
|
||||
wire:click="$set('showDeleteConfirmModal', false)"
|
||||
wire:loading.attr="disabled">
|
||||
{{ __('Cancel') }}
|
||||
</x-jetstream.secondary-button>
|
||||
@can('delete calls')
|
||||
<x-jetstream.danger-button
|
||||
wire:click="deleteSelected()"
|
||||
wire:loading.attr="disabled">
|
||||
{{ trans_choice('Delete :count call|Delete :count calls', count($bulkSelected), ['count' => count($bulkSelected)]) }}
|
||||
</x-jetstream.danger-button>
|
||||
@endcan
|
||||
</div>
|
||||
</x-slot>
|
||||
</x-jetstream.dialog-modal>
|
||||
@endif
|
||||
|
||||
{{-- Admin bulk delete confirmation modal --}}
|
||||
@if ($isAdminView)
|
||||
<x-jetstream.dialog-modal wire:model.live="showAdminDeleteConfirm" closeButton="true">
|
||||
<x-slot name="title">
|
||||
{{ __('Delete selection?') }}
|
||||
</x-slot>
|
||||
<x-slot name="content">
|
||||
<p class="text-theme-primary">
|
||||
{{ trans_choice('Delete :count call?|Delete :count calls?', count($bulkSelected), ['count' => count($bulkSelected)]) }}
|
||||
</p>
|
||||
<p class="mt-3 text-theme-primary">
|
||||
{{ __('Do you have permission of :names to take this action?', ['names' => $adminDeleteCallableNames]) }}
|
||||
</p>
|
||||
</x-slot>
|
||||
<x-slot name="footer">
|
||||
<div class="flex items-center gap-3 justify-end">
|
||||
<x-jetstream.secondary-button
|
||||
wire:click="$set('showAdminDeleteConfirm', false)"
|
||||
wire:loading.attr="disabled">
|
||||
{{ __('Cancel') }}
|
||||
</x-jetstream.secondary-button>
|
||||
@can('delete calls')
|
||||
<x-jetstream.danger-button
|
||||
wire:click="deleteSelected()"
|
||||
wire:loading.attr="disabled">
|
||||
{{ trans_choice('Delete :count call|Delete :count calls', count($bulkSelected), ['count' => count($bulkSelected)]) }}
|
||||
</x-jetstream.danger-button>
|
||||
@endcan
|
||||
</div>
|
||||
</x-slot>
|
||||
</x-jetstream.dialog-modal>
|
||||
@endif
|
||||
|
||||
{{-- Per-row delete confirmation modal --}}
|
||||
<x-jetstream.dialog-modal wire:model.live="showDeleteRowConfirm" wire:key="manage-delete-row-confirm">
|
||||
<x-slot name="title">{{ __('Delete Call') }}</x-slot>
|
||||
<x-slot name="content">
|
||||
<p class="text-theme-primary">{{ __('Are you sure you want to delete this call? You can undelete this call later.') }}</p>
|
||||
</x-slot>
|
||||
<x-slot name="footer">
|
||||
<x-jetstream.secondary-button
|
||||
wire:click="$set('showDeleteRowConfirm', false)"
|
||||
wire:loading.attr="disabled">
|
||||
{{ __('Cancel') }}
|
||||
</x-jetstream.secondary-button>
|
||||
@can('delete calls')
|
||||
<x-jetstream.danger-button
|
||||
class="ml-3"
|
||||
wire:click="deleteRowCall()"
|
||||
wire:loading.attr="disabled">
|
||||
{{ __('Yes, delete') }}
|
||||
</x-jetstream.danger-button>
|
||||
@endcan
|
||||
</x-slot>
|
||||
</x-jetstream.dialog-modal>
|
||||
|
||||
{{-- Per-row restore confirmation modal --}}
|
||||
<x-jetstream.dialog-modal wire:model.live="showRestoreRowConfirm" wire:key="manage-restore-row-confirm">
|
||||
<x-slot name="title">{{ __('Restore call') }}</x-slot>
|
||||
<x-slot name="content">
|
||||
<p class="text-theme-primary">{{ __('Are you sure you want to restore this call?') }}</p>
|
||||
</x-slot>
|
||||
<x-slot name="footer">
|
||||
<x-jetstream.secondary-button
|
||||
wire:click="$set('showRestoreRowConfirm', false)"
|
||||
wire:loading.attr="disabled">
|
||||
{{ __('Cancel') }}
|
||||
</x-jetstream.secondary-button>
|
||||
<x-jetstream.button
|
||||
class="ml-3"
|
||||
wire:click="restoreRowCall()"
|
||||
wire:loading.attr="disabled">
|
||||
{{ __('Yes, restore') }}
|
||||
</x-jetstream.button>
|
||||
</x-slot>
|
||||
</x-jetstream.dialog-modal>
|
||||
|
||||
{{-- Admin pause/publish confirmation modal --}}
|
||||
@if ($isAdminView)
|
||||
<x-jetstream.dialog-modal wire:model.live="showAdminActionConfirm" closeButton="true">
|
||||
<x-slot name="title">
|
||||
{{ $adminActionType === 'pause' ? __('Pause call') : __('Publish call') }}
|
||||
</x-slot>
|
||||
<x-slot name="content">
|
||||
<p class="text-theme-primary">
|
||||
{{ $adminActionType === 'pause'
|
||||
? __('Pause this call on behalf of :name?', ['name' => $adminActionCallableName])
|
||||
: __('Publish this call on behalf of :name?', ['name' => $adminActionCallableName]) }}
|
||||
</p>
|
||||
<p class="mt-3 text-theme-primary">
|
||||
{{ __('Do you have permission of :name to take this action?', ['name' => $adminActionCallableName]) }}
|
||||
</p>
|
||||
</x-slot>
|
||||
<x-slot name="footer">
|
||||
<div class="flex items-center gap-3 justify-end">
|
||||
<x-jetstream.secondary-button
|
||||
wire:click="$set('showAdminActionConfirm', false)"
|
||||
wire:loading.attr="disabled">
|
||||
{{ __('Cancel') }}
|
||||
</x-jetstream.secondary-button>
|
||||
<x-jetstream.button
|
||||
wire:click="executeAdminAction()"
|
||||
wire:loading.attr="disabled">
|
||||
{{ $adminActionType === 'pause' ? __('Pause call') : __('Publish call') }}
|
||||
</x-jetstream.button>
|
||||
</div>
|
||||
</x-slot>
|
||||
</x-jetstream.dialog-modal>
|
||||
@endif
|
||||
|
||||
{{-- Bulk print modal --}}
|
||||
<x-jetstream.dialog-modal wire:model.live="showBulkPrintModal" wire:key="bulk-print-modal" closeButton="true">
|
||||
<x-slot name="title">
|
||||
{{ __('Print selected calls') }}
|
||||
</x-slot>
|
||||
<x-slot name="content">
|
||||
<div class="space-y-6">
|
||||
{{-- Calls per page --}}
|
||||
<div>
|
||||
<p class="text-sm font-medium text-theme-primary mb-3">{{ __('Calls per page') }}</p>
|
||||
<div class="flex items-center gap-6">
|
||||
@foreach([1, 2, 4] as $option)
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input type="radio"
|
||||
wire:model.live="bulkPrintPerPage"
|
||||
value="{{ $option }}"
|
||||
class="text-theme-brand focus:ring-theme-brand">
|
||||
<span class="text-sm text-theme-primary">{{ $option }}</span>
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Color --}}
|
||||
<div>
|
||||
<p class="text-sm font-medium text-theme-primary mb-3">{{ __('Color') }}</p>
|
||||
<div class="flex items-center gap-6">
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input type="radio"
|
||||
wire:model.live="bulkPrintColor"
|
||||
value="bw"
|
||||
class="text-theme-brand focus:ring-theme-brand">
|
||||
<span class="text-sm text-theme-primary">{{ __('Black & white') }}</span>
|
||||
</label>
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input type="radio"
|
||||
wire:model.live="bulkPrintColor"
|
||||
value="color"
|
||||
class="text-theme-brand focus:ring-theme-brand">
|
||||
<span class="text-sm text-theme-primary">{{ __('Color') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-sm text-theme-secondary">
|
||||
{{ trans_choice(':count call selected|:count calls selected', count($bulkSelected), ['count' => count($bulkSelected)]) }}
|
||||
</p>
|
||||
</div>
|
||||
</x-slot>
|
||||
<x-slot name="footer">
|
||||
<x-jetstream.secondary-button wire:click="$set('showBulkPrintModal', false)" wire:loading.attr="disabled">
|
||||
{{ __('Cancel') }}
|
||||
</x-jetstream.secondary-button>
|
||||
<x-jetstream.button
|
||||
wire:click="executeBulkPrint"
|
||||
wire:loading.attr="disabled"
|
||||
class="ml-3 bg-theme-brand">
|
||||
<x-icon class="mr-2 h-4 w-4" name="printer" />
|
||||
{{ __('Generate PDF') }}
|
||||
</x-jetstream.button>
|
||||
</x-slot>
|
||||
</x-jetstream.dialog-modal>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function initManageCallFlatpickr() {
|
||||
const tillInput = document.querySelector('[wire\\:model\\.defer="editTill"]');
|
||||
if (tillInput && !tillInput._flatpickr && window.LaravelFlatpickr) {
|
||||
window.LaravelFlatpickr.initializeFlatpickr(tillInput);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Initialize on first page load (modal is always in DOM)
|
||||
setTimeout(initManageCallFlatpickr, 100);
|
||||
|
||||
Livewire.hook('morph.updated', () => {
|
||||
setTimeout(initManageCallFlatpickr, 200);
|
||||
});
|
||||
|
||||
// Update flatpickr value/min/max imperatively when a call is opened for editing
|
||||
Livewire.on('manage-edit-opened', (params) => {
|
||||
const data = Array.isArray(params) ? params[0] : params;
|
||||
setTimeout(() => {
|
||||
const tillInput = document.querySelector('[wire\\:model\\.defer="editTill"]');
|
||||
if (!tillInput) return;
|
||||
|
||||
// Initialize if not yet done
|
||||
if (!tillInput._flatpickr) {
|
||||
window.LaravelFlatpickr && window.LaravelFlatpickr.initializeFlatpickr(tillInput);
|
||||
}
|
||||
|
||||
const fp = tillInput._flatpickr;
|
||||
if (!fp) return;
|
||||
|
||||
if (data.minDate) fp.set('minDate', data.minDate);
|
||||
if (data.maxDate) fp.set('maxDate', data.maxDate);
|
||||
fp.setDate(data.value || '', false);
|
||||
}, 100);
|
||||
});
|
||||
|
||||
// Open bulk PDF in new tab
|
||||
Livewire.on('open-bulk-pdf', (params) => {
|
||||
const data = Array.isArray(params) ? params[0] : params;
|
||||
if (data.url) window.open(data.url, '_blank');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,30 @@
|
||||
<div class="flex items-end ml-auto">
|
||||
@php
|
||||
$activeProfile = getActiveProfile();
|
||||
$isOwnCall = $callable && $activeProfile &&
|
||||
get_class($activeProfile) === get_class($callable) &&
|
||||
$activeProfile->id === $callable->id;
|
||||
$isDeleted = $call->trashed();
|
||||
$isExpired = $call->till !== null && $call->till->isPast();
|
||||
$isSuppressed = $call->is_suppressed;
|
||||
$isPaused = $call->is_paused;
|
||||
$callableRemoved = $callable?->isRemoved();
|
||||
$unavailableReason = match(true) {
|
||||
$isDeleted => __('This call has been deleted.'),
|
||||
$isSuppressed => __('This call has been blocked.'),
|
||||
$isPaused => __('This call is currently paused.'),
|
||||
$isExpired => __('This call has expired.'),
|
||||
$callableRemoved => __('This user is no longer available.'),
|
||||
default => null,
|
||||
};
|
||||
@endphp
|
||||
@if ($isOwnCall)
|
||||
@livewire('calls.edit', ['call' => $call], key('edit-call-' . $call->id))
|
||||
@elseif ($unavailableReason)
|
||||
<p class="text-sm text-white/70 italic max-w-48 text-right">{{ $unavailableReason }}</p>
|
||||
@else
|
||||
<x-jetstream.button wire:click="createConversation">
|
||||
{{ __('Respond') }}
|
||||
</x-jetstream.button>
|
||||
@endif
|
||||
</div>
|
||||
Reference in new issue
Block a user