391 lines
13 KiB
PHP
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'),
|
|
];
|
|
}
|
|
}
|