Publication initiale de RelaisSMS : documentation et sources nettoyees

This commit is contained in:
2026-10-21 09:29:39 +02:00
commit ac186b03b1
158 changed files with 24818 additions and 0 deletions
@@ -0,0 +1,186 @@
<?php
namespace App\Http\Controllers;
use App\Models\Contact;
use App\Models\ContactGroup;
use App\Services\Recipients\RecipientResolutionResult;
use App\Services\Recipients\RecipientResolver;
use App\Services\Sms\SmsMessageComposer;
use Illuminate\Contracts\View\View;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
class RecipientSelectionController extends Controller
{
public function index(Request $request, SmsMessageComposer $messageComposer): View
{
return $this->view($request, messageComposer: $messageComposer);
}
public function search(Request $request): JsonResponse
{
$search = trim((string) $request->query('q', ''));
$like = '%'.str_replace(['%', '_'], ['\%', '\_'], $search).'%';
$contacts = Contact::query()
->with('phoneNumbers')
->search($search)
->orderByDesc('is_favorite')
->orderBy('last_name')
->orderBy('first_name')
->limit(10)
->get()
->map(function (Contact $contact): array {
$primaryPhone = $contact->phoneNumbers->firstWhere('is_primary', true)
?? $contact->phoneNumbers->first();
return [
'type' => 'contact',
'id' => $contact->id,
'label' => $contact->displayName(),
'meta' => $primaryPhone?->normalized_number
?: $primaryPhone?->raw_number
?: $contact->email
?: ucfirst($contact->source),
'source' => $contact->source,
'is_favorite' => $contact->is_favorite,
];
});
$groups = ContactGroup::query()
->withCount('contacts')
->when($search !== '', function ($query) use ($like): void {
$query->where(function ($query) use ($like): void {
$query
->where('name', 'like', $like)
->orWhere('description', 'like', $like);
});
})
->orderByDesc('is_favorite')
->orderBy('name')
->limit(10)
->get()
->map(fn (ContactGroup $group): array => [
'type' => 'group',
'id' => $group->id,
'label' => $group->name,
'meta' => $group->contacts_count.' contact(s)',
'source' => $group->source,
'is_favorite' => $group->is_favorite,
]);
return response()->json([
'results' => $groups->concat($contacts)->values(),
]);
}
public function preview(Request $request, RecipientResolver $resolver, SmsMessageComposer $messageComposer): View|RedirectResponse
{
$validated = $request->validate([
'q' => ['nullable', 'string', 'max:255'],
'message' => ['nullable', 'string', 'max:612'],
'include_sms_header' => ['nullable', 'boolean'],
'include_sms_footer' => ['nullable', 'boolean'],
'contact_ids' => ['array'],
'contact_ids.*' => ['integer', Rule::exists('contacts', 'id')->whereNull('deleted_at')],
'group_ids' => ['array'],
'group_ids.*' => ['integer', Rule::exists('contact_groups', 'id')->whereNull('deleted_at')],
]);
$contactIds = $validated['contact_ids'] ?? [];
$groupIds = $validated['group_ids'] ?? [];
if ($contactIds === [] && $groupIds === []) {
return redirect()
->route('recipients.index', ['q' => $validated['q'] ?? null])
->withErrors(['selection' => 'Selectionnez au moins un contact ou un groupe.'])
->withInput();
}
return $this->view(
$request,
$resolver->resolve($contactIds, $groupIds),
$contactIds,
$groupIds,
$messageComposer,
);
}
/**
* @param array<int, int|string> $selectedContactIds
* @param array<int, int|string> $selectedGroupIds
*/
private function view(
Request $request,
?RecipientResolutionResult $resolution = null,
array $selectedContactIds = [],
array $selectedGroupIds = [],
?SmsMessageComposer $messageComposer = null,
): View {
$messageComposer ??= app(SmsMessageComposer::class);
$messageSettings = $messageComposer->settings();
$oldHeaderChoice = $request->old('include_sms_header');
$oldFooterChoice = $request->old('include_sms_footer');
$hasHeaderChoice = $oldHeaderChoice !== null || $request->has('include_sms_header');
$hasFooterChoice = $oldFooterChoice !== null || $request->has('include_sms_footer');
$includeHeader = $hasHeaderChoice
? filter_var($oldHeaderChoice ?? $request->input('include_sms_header'), FILTER_VALIDATE_BOOLEAN)
: $messageSettings['header'] !== '';
$includeFooter = $hasFooterChoice
? filter_var($oldFooterChoice ?? $request->input('include_sms_footer'), FILTER_VALIDATE_BOOLEAN)
: $messageSettings['footer'] !== '';
$messageBody = (string) $request->old('message', $request->input('message', ''));
$search = trim((string) $request->input('q', ''));
$like = '%'.str_replace(['%', '_'], ['\%', '\_'], $search).'%';
$contacts = Contact::query()
->with(['groups', 'phoneNumbers'])
->search($search)
->orderByDesc('is_favorite')
->orderBy('last_name')
->orderBy('first_name')
->limit(50)
->get();
$groups = ContactGroup::query()
->withCount('contacts')
->when($search !== '', function ($query) use ($like): void {
$query->where(function ($query) use ($like): void {
$query
->where('name', 'like', $like)
->orWhere('description', 'like', $like);
});
})
->orderByDesc('is_favorite')
->orderBy('name')
->limit(50)
->get();
return view('recipients.index', [
'contacts' => $contacts,
'groups' => $groups,
'search' => $search,
'selectedContactIds' => collect($selectedContactIds)->map(fn (int|string $id): int => (int) $id)->all(),
'selectedGroupIds' => collect($selectedGroupIds)->map(fn (int|string $id): int => (int) $id)->all(),
'selectedContacts' => Contact::query()
->whereKey($selectedContactIds)
->orderBy('last_name')
->orderBy('first_name')
->get(),
'selectedGroups' => ContactGroup::query()
->whereKey($selectedGroupIds)
->orderBy('name')
->get(),
'resolution' => $resolution,
'smsHeader' => $messageSettings['header'],
'smsFooter' => $messageSettings['footer'],
'includeSmsHeader' => $includeHeader,
'includeSmsFooter' => $includeFooter,
'composedMessage' => $messageComposer->compose($messageBody, $includeHeader, $includeFooter),
]);
}
}