middleware(['admin', '2fa']); } /** * Liste aller Newsletter-Kontakte */ public function index() { $data = [ 'statistics' => $this->getStatistics(), ]; return view('newsletter.index', $data); } /** * DataTables Daten für die Liste */ public function getDatatable(Request $request) { $query = NewsletterContact::query() ->select([ 'id', 'email', 'firstname', 'lastname', 'group_kulturreisen', 'group_ferienwohnungen', 'status', 'source', 'total_bookings_kulturreisen', 'total_bookings_ferienwohnungen', 'last_booking_at', 'last_travel_end_date', 'created_at', ]); // Filter nach Gruppe if ($request->has('group') && $request->group != '') { if ($request->group == 'kulturreisen') { $query->where('group_kulturreisen', true); } elseif ($request->group == 'ferienwohnungen') { $query->where('group_ferienwohnungen', true); } } // Filter nach Status if ($request->has('status') && $request->status != '') { $query->where('status', $request->status); } // Filter nach Source if ($request->has('source') && $request->source != '') { $query->where('source', $request->source); } // Filter nach Datum der letzten Buchung (von) if ($request->has('travel_from') && $request->travel_from != '') { $query->whereDate('last_travel_end_date', '>=', Carbon::parse($request->travel_from)->format('Y-m-d H:i:s')); } // Filter nach Datum der letzten Buchung (bis) if ($request->has('travel_to') && $request->travel_to != '') { $query->whereDate('last_travel_end_date', '<=', Carbon::parse($request->travel_to)->format('Y-m-d H:i:s')); } return DataTables::of($query) ->addColumn('full_name', function ($contact) { return $contact->full_name ?: '-'; }) ->addColumn('groups', function ($contact) { $html = ''; if ($contact->group_kulturreisen) { $html .= 'Kulturreisen '; } if ($contact->group_ferienwohnungen) { $html .= 'Ferienwohnungen'; } return $html ?: '-'; }) ->addColumn('status_badge', function ($contact) { return '' . $contact->status_label . ''; }) ->addColumn('total_bookings', function ($contact) { $html = ''; if ($contact->total_bookings_kulturreisen > 0) { $html .= 'K: ' . $contact->total_bookings_kulturreisen . ' '; } if ($contact->total_bookings_ferienwohnungen > 0) { $html .= 'F: ' . $contact->total_bookings_ferienwohnungen . ''; } return $html ?: '0'; }) ->addColumn('last_booking', function ($contact) { return $contact->last_booking_at ? $contact->last_booking_at->format('d.m.Y') : '-'; }) ->addColumn('last_travel', function ($contact) { return $contact->last_travel_end_date ? $contact->last_travel_end_date->format('d.m.Y') : '-'; }) ->addColumn('source_label', function ($contact) { return $contact->source_label; }) ->addColumn('created', function ($contact) { return $contact->created_at->format('d.m.Y'); }) ->addColumn('actions', function ($contact) { $html = '
'; return $html; }) ->rawColumns(['groups', 'status_badge', 'total_bookings', 'actions']) ->make(true); } /** * Detailansicht eines Kontakts */ public function detail($id) { $contact = NewsletterContact::with(['customer', 'travel_user', 'logs.user']) ->findOrFail($id); $data = [ 'contact' => $contact, ]; return view('newsletter.detail', $data); } /** * Formular zum Bearbeiten eines Kontakts */ public function edit($id) { if ($id === 'new') { $contact = new NewsletterContact(); $contact->status = NewsletterContact::STATUS_ACTIVE; $contact->source = NewsletterContact::SOURCE_MANUAL; } else { $contact = NewsletterContact::findOrFail($id); } $data = [ 'contact' => $contact, 'id' => $id, ]; return view('newsletter.edit', $data); } /** * Speichern eines Kontakts */ public function store($id, Request $request) { $rules = [ 'email' => 'required|email', 'status' => 'required|in:' . implode(',', [ NewsletterContact::STATUS_ACTIVE, NewsletterContact::STATUS_INACTIVE, NewsletterContact::STATUS_UNSUBSCRIBED, NewsletterContact::STATUS_BOUNCED, ]), ]; $validator = Validator::make($request->all(), $rules); if ($validator->fails()) { return back() ->withErrors($validator) ->withInput(); } if ($id === 'new') { $contact = new NewsletterContact(); $isNew = true; } else { $contact = NewsletterContact::findOrFail($id); $isNew = false; } // Speichere alte Werte für Log $oldStatus = $contact->status; $oldGroups = [ 'kulturreisen' => $contact->group_kulturreisen, 'ferienwohnungen' => $contact->group_ferienwohnungen, ]; $contact->email = strtolower(trim($request->email)); $contact->firstname = $request->firstname; $contact->lastname = $request->lastname; $contact->status = $request->status; $contact->source = $request->source ?? NewsletterContact::SOURCE_MANUAL; $contact->group_kulturreisen = $request->has('group_kulturreisen'); $contact->group_ferienwohnungen = $request->has('group_ferienwohnungen'); $contact->notes = $request->notes; if ($isNew) { $contact->subscribed_at = now(); } $contact->save(); // Log erstellen if ($isNew) { $contact->logs()->create([ 'action' => 'subscribed', 'description' => 'Kontakt manuell erstellt', 'user_id' => auth()->id(), ]); } else { // Status geändert? if ($oldStatus !== $contact->status) { $contact->logs()->create([ 'action' => 'status_changed', 'description' => 'Status geändert von ' . NewsletterContact::$statusLabels[$oldStatus] . ' zu ' . $contact->status_label, 'user_id' => auth()->id(), ]); } // Gruppen geändert? if ( $oldGroups['kulturreisen'] !== $contact->group_kulturreisen || $oldGroups['ferienwohnungen'] !== $contact->group_ferienwohnungen ) { $contact->logs()->create([ 'action' => 'group_changed', 'description' => 'Gruppenzugehörigkeit geändert', 'user_id' => auth()->id(), ]); } } \Session()->flash('alert-success', $isNew ? 'Kontakt erstellt' : 'Kontakt aktualisiert'); return redirect()->route('newsletter.detail', $contact->id); } /** * Kontakt löschen (soft delete) */ public function delete($id) { $contact = NewsletterContact::findOrFail($id); $contact->logs()->create([ 'action' => 'unsubscribed', 'description' => 'Kontakt gelöscht', 'user_id' => auth()->id(), ]); $contact->delete(); \Session()->flash('alert-success', 'Kontakt gelöscht'); return redirect()->route('newsletter.index'); } /** * Kontakt abmelden */ public function unsubscribe($id, Request $request) { $contact = NewsletterContact::findOrFail($id); $contact->unsubscribe($request->reason); \Session()->flash('alert-success', 'Kontakt abgemeldet'); return back(); } /** * Kontakt wieder aktivieren */ public function resubscribe($id) { $contact = NewsletterContact::findOrFail($id); $contact->resubscribe(); \Session()->flash('alert-success', 'Kontakt wieder aktiviert'); return back(); } /** * Synchronisation starten */ public function sync(Request $request) { $type = $request->get('type', 'all'); $force = $request->has('force'); $output = []; if ($type === 'all' || $type === 'kulturreisen') { Artisan::call('newsletter:sync-kulturreisen', $force ? ['--force' => true] : []); $output['kulturreisen'] = Artisan::output(); } if ($type === 'all' || $type === 'ferienwohnungen') { Artisan::call('newsletter:sync-ferienwohnungen', $force ? ['--force' => true] : []); $output['ferienwohnungen'] = Artisan::output(); } \Session()->flash('alert-success', 'Synchronisation abgeschlossen'); return back()->with('sync_output', $output); } /** * Export von Kontakten */ public function export(Request $request) { $query = NewsletterContact::query(); // Filter nach Gruppe if ($request->has('group') && $request->group != '') { if ($request->group == 'kulturreisen') { $query->where('group_kulturreisen', true); } elseif ($request->group == 'ferienwohnungen') { $query->where('group_ferienwohnungen', true); } } // Filter nach Status if ($request->has('status') && $request->status != '') { $query->where('status', $request->status); } // Filter nach Source if ($request->has('source') && $request->source != '') { $query->where('source', $request->source); } // Filter nach Datum der letzten Reise (von) if ($request->has('travel_from') && $request->travel_from != '') { $query->whereDate('last_travel_end_date', '>=', Carbon::parse($request->travel_from)->format('Y-m-d H:i:s')); } // Filter nach Datum der letzten Reise (bis) if ($request->has('travel_to') && $request->travel_to != '') { $query->whereDate('last_travel_end_date', '<=', Carbon::parse($request->travel_to)->format('Y-m-d H:i:s')); } $contacts = $query->get(); // Dateiname mit Datum und Filter-Infos $group = $request->get('group', 'all'); $status = $request->get('status', 'all'); $filename = 'newsletter_' . $group . '_' . $status . '_' . date('Y-m-d') . '.csv'; return Excel::download(new NewsletterExport($contacts), $filename); } /** * Statistiken für Dashboard */ private function getStatistics() { return [ 'total' => NewsletterContact::count(), 'active' => NewsletterContact::where('status', NewsletterContact::STATUS_ACTIVE)->count(), 'kulturreisen' => NewsletterContact::where('group_kulturreisen', true)->count(), 'ferienwohnungen' => NewsletterContact::where('group_ferienwohnungen', true)->count(), 'with_bookings' => NewsletterContact::withBookings()->count(), 'multiple_bookers' => NewsletterContact::multipleBookers()->count(), 'unsubscribed' => NewsletterContact::where('status', NewsletterContact::STATUS_UNSUBSCRIBED)->count(), 'last_sync' => NewsletterContact::max('last_synced_at'), ]; } }