mein-sterntours/app/Http/Controllers/NewsletterController.php
2026-01-23 17:34:40 +01:00

391 lines
13 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\NewsletterContact;
use App\Models\NewsletterLog;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Validator;
use Yajra\DataTables\Facades\DataTables;
use Maatwebsite\Excel\Facades\Excel;
use App\Exports\NewsletterExport;
use Carbon\Carbon;
class NewsletterController extends Controller
{
public function __construct()
{
$this->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 .= '<span class="badge badge-info">Kulturreisen</span> ';
}
if ($contact->group_ferienwohnungen) {
$html .= '<span class="badge badge-primary">Ferienwohnungen</span>';
}
return $html ?: '-';
})
->addColumn('status_badge', function ($contact) {
return '<span class="badge badge-' . $contact->status_color . '">' . $contact->status_label . '</span>';
})
->addColumn('total_bookings', function ($contact) {
$html = '';
if ($contact->total_bookings_kulturreisen > 0) {
$html .= '<span class="badge badge-secondary">K: ' . $contact->total_bookings_kulturreisen . '</span> ';
}
if ($contact->total_bookings_ferienwohnungen > 0) {
$html .= '<span class="badge badge-secondary">F: ' . $contact->total_bookings_ferienwohnungen . '</span>';
}
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 = '<div class="btn-group">';
$html .= '<a href="' . route('newsletter.detail', $contact->id) . '" class="btn btn-sm btn-info"><i class="fa fa-eye"></i></a>';
$html .= '<a href="' . route('newsletter.edit', $contact->id) . '" class="btn btn-sm btn-primary"><i class="fa fa-edit"></i></a>';
$html .= '</div>';
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'),
];
}
}