setupTenantConnection(); } public function mount() { if (auth()->user()?->level != env('LEVEL_ADMIN', 0)) { return redirect()->to('/dashboard'); } $this->categories = []; $this->getCategories(\App\Models\Category::select('id', 'name')->where('parent_id', null)->orderBy('name')->get(), 0); $this->courses = []; $this->getCourses(\App\Models\Course::select('id', 'name')->where('parent_id', null)->orderBy('name', 'ASC')->get(), 0); $this->schedule_at = now($this->timezone)->addHour()->format('Y-m-d\TH:i'); } public function render() { if (!$this->showForm) { $this->records = SmsMessage::withCount(['recipients']) ->orderBy('created_at', 'desc') ->get(); } else { $this->categories = []; $this->getCategories(\App\Models\Category::select('id', 'name')->where('parent_id', null)->orderBy('name')->get(), 0); $this->courses = []; $this->getCourses(\App\Models\Course::select('id', 'name')->where('parent_id', null)->orderBy('name', 'ASC')->get(), 0); } return view('livewire.sms_comunications'); } protected function baseRules(): array { return [ 'subject' => 'required|string|max:255', 'content' => 'required|string', 'recipients' => 'required|array|min:1', 'recipients.*.phone' => 'required|string', ]; } protected function validateDraft(): void { $rules = []; // $rules = $this->baseRules(); $rules['subject'] = 'required|string|max:255'; $this->validate($rules); } protected function validateSend(): void { $this->validate($this->baseRules()); } protected function validateSchedule(): void { $rules = $this->baseRules(); $rules['schedule_at'] = 'required|date|after:now'; $this->validate($rules); } public function add() { $this->reset(['messageId', 'subject', 'content', 'recipients', 'mode', 'schedule_at']); $this->mode = 'now'; $this->schedule_at = now($this->timezone)->addHour()->format('Y-m-d\TH:i'); $this->showForm = true; $this->dispatchBrowserEvent('init-recipients-table', [ 'selected' => collect($this->recipients)->pluck('member_id')->filter()->values()->all(), ]); } public function edit($id) { try { $msg = SmsMessage::with(['recipients'])->findOrFail($id); $this->messageId = $msg->id; $this->subject = $msg->subject; $this->content = $msg->content; $this->recipients = $msg->recipients->map(fn($r) => [ 'member_id' => $r->member_id, 'phone' => $r->phone, 'first_name' => optional($r->member)->first_name, 'last_name' => optional($r->member)->last_name, ])->toArray(); $this->mode = $msg->status === 'scheduled' ? 'schedule' : 'now'; $this->schedule_at = optional($msg->schedule_at)?->setTimezone($this->timezone)?->format('Y-m-d\TH:i'); $this->showForm = true; $this->locked = $msg->isLocked(); $this->dispatchBrowserEvent('init-recipients-table', [ 'selected' => collect($this->recipients)->pluck('member_id')->filter()->values()->all(), ]); } catch (\Throwable $ex) { $this->error = 'Errore (' . $ex->getMessage() . ')'; } } public function duplicate($id, $withRecipients = true) { try { $copy = SmsMessage::with(['recipients'])->findOrFail($id)->duplicate($withRecipients); $this->edit($copy->id); $this->success = 'Bozza duplicata'; } catch (\Throwable $ex) { $this->error = 'Errore (' . $ex->getMessage() . ')'; } } public function saveDraft() { $this->validateDraft(); DB::transaction(function () { $msg = $this->upsertMessage(status: 'draft', scheduleAt: null); $this->upsertRecipients($msg); $this->messageId = $msg->id; $this->locked = $msg->isLocked(); }); $this->success = 'Bozza salvata'; $this->dispatchBrowserEvent('scroll-top'); } public function sendNow() { $this->validateSend(); if ($this->messageId) { $existing = SmsMessage::findOrFail($this->messageId); if ($existing->isLocked()) { $this->error = 'Questo sms è già in invio o inviato e non può essere modificato.'; return; } } DB::transaction(function () { $msg = $this->upsertMessage(status: 'processing', scheduleAt: null); $this->upsertRecipients($msg, true); $this->messageId = $msg->id; $this->locked = true; }); dispatch(new \App\Jobs\SendSmsMessage($this->messageId)); $this->success = 'Invio avviato'; $this->dispatchBrowserEvent('scroll-top'); } public function scheduleMessage() { $this->validateSchedule(); if ($this->messageId) { $existing = SmsMessage::findOrFail($this->messageId); if ($existing->isLocked()) { $this->error = 'Questo sms è già in invio o inviato e non può essere modificato.'; return; } } $scheduledAt = \Carbon\Carbon::parse($this->schedule_at, $this->timezone)->setTimezone('UTC'); DB::transaction(function () use ($scheduledAt) { $msg = $this->upsertMessage(status: 'scheduled', scheduleAt: $scheduledAt); $this->upsertRecipients($msg, true); $this->messageId = $msg->id; $this->locked = $msg->isLocked(); }); $this->success = 'Sms programmato'; $this->dispatchBrowserEvent('scroll-top'); } protected function upsertMessage(string $status, $scheduleAt): SmsMessage { return SmsMessage::updateOrCreate( ['id' => $this->messageId], [ 'subject' => $this->subject, 'content' => $this->content, 'status' => $status, 'schedule_at' => $scheduleAt, 'created_by' => auth()->id(), ] ); } protected function upsertRecipients(SmsMessage $msg, bool $force = false): void { if (!$force && $msg->isLocked()) return; $msg->recipients()->delete(); $rows = collect($this->recipients)->map(fn($r) => [ 'sms_message_id' => $msg->id, 'member_id' => $r['member_id'] ?? null, 'phone' => $r['phone'], 'status' => 'pending', 'created_at' => now(), 'updated_at' => now(), ])->values()->all(); if ($rows) \App\Models\SmsMessageRecipient::insert($rows); } public function cancel() { $this->showForm = false; $this->reset(['messageId', 'subject', 'content', 'recipients', 'mode', 'schedule_at']); $this->mode = 'now'; $this->schedule_at = now($this->timezone)->addHour()->format('Y-m-d\TH:i'); $this->dispatchBrowserEvent('init-archive-table'); } public function getCategories($records, $indentation) { foreach ($records as $record) { $this->categories[] = array('id' => $record->id, 'name' => $record->getTree()); if (count($record->childs)) $this->getCategories($record->childs, $indentation + 1); } } public function getCourses($records, $indentation) { /** @var \App\Models\Course $record */ foreach ($records as $record) { $this->courses[] = array('id' => $record->id, 'name' => $record->getTree()); if (count($record->childs)) $this->getCourses($record->childs, $indentation + 1); } } public function toggleRecipient($id) { $id = (int)$id; $idx = collect($this->recipients)->search(fn($r) => (int)($r['member_id'] ?? 0) === $id); if ($idx !== false) { array_splice($this->recipients, $idx, 1); return; } $m = Member::select('id', 'phone', 'first_name', 'last_name')->find($id); if (!$m || empty($m->phone)) return; $this->recipients[] = [ 'member_id' => $m->id, 'phone' => $m->phone, 'first_name' => $m->first_name, 'last_name' => $m->last_name, ]; } public function deleteMessage(int $id) { $msg = \App\Models\SmsMessage::findOrFail($id); if (! in_array($msg->status, ['draft', 'failed'], true)) { return; } $msg->delete(); $this->dispatchBrowserEvent('sms-deleted', ['id' => $id]); } }