23-01-2026
This commit is contained in:
parent
8fd1f4d451
commit
389d5d1820
59 changed files with 9642 additions and 883 deletions
185
app/Http/Controllers/API/NavigationController.php
Normal file
185
app/Http/Controllers/API/NavigationController.php
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\NavigationTreeService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class NavigationController extends Controller
|
||||
{
|
||||
protected $navigationService;
|
||||
|
||||
public function __construct(NavigationTreeService $navigationService)
|
||||
{
|
||||
$this->navigationService = $navigationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt den kompletten Navigationsbaum zurück
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function getNavigationTree(): JsonResponse
|
||||
{
|
||||
try {
|
||||
$tree = $this->navigationService->getNavigationTree();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $tree,
|
||||
'meta' => [
|
||||
'total_nodes' => $this->navigationService->countNodes($tree),
|
||||
'generated_at' => now()->toIso8601String()
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt einen spezifischen Teil des Navigationsbaums zurück
|
||||
*
|
||||
* @param int $rootId Die ID des Root-Knotens
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function getNavigationSubTree(int $rootId): JsonResponse
|
||||
{
|
||||
try {
|
||||
$tree = $this->navigationService->getNavigationSubTree($rootId);
|
||||
|
||||
if (!$tree) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'error' => 'Navigation node not found'
|
||||
], 404);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $tree,
|
||||
'meta' => [
|
||||
'total_nodes' => $this->navigationService->countNodes([$tree]),
|
||||
'generated_at' => now()->toIso8601String()
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt eine flache Liste aller Navigationspunkte zurück (ohne Hierarchie)
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function getFlatNavigationList(): JsonResponse
|
||||
{
|
||||
try {
|
||||
$list = $this->navigationService->getFlatNavigationList();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $list,
|
||||
'meta' => [
|
||||
'total_nodes' => count($list),
|
||||
'generated_at' => now()->toIso8601String()
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt nur die aktiven Navigationspunkte zurück
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function getActiveNavigationTree(): JsonResponse
|
||||
{
|
||||
try {
|
||||
$tree = $this->navigationService->getNavigationTree(true);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $tree,
|
||||
'meta' => [
|
||||
'total_nodes' => $this->navigationService->countNodes($tree),
|
||||
'generated_at' => now()->toIso8601String()
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt den Breadcrumb-Pfad für eine bestimmte Seite zurück
|
||||
*
|
||||
* @param int $pageId
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function getBreadcrumb(int $pageId): JsonResponse
|
||||
{
|
||||
try {
|
||||
$breadcrumb = $this->navigationService->getBreadcrumb($pageId);
|
||||
|
||||
if (empty($breadcrumb)) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'error' => 'Page not found'
|
||||
], 404);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $breadcrumb,
|
||||
'meta' => [
|
||||
'depth' => count($breadcrumb),
|
||||
'generated_at' => now()->toIso8601String()
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Löscht den Navigation-Cache
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function clearCache(): JsonResponse
|
||||
{
|
||||
try {
|
||||
$this->navigationService->clearCache();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Navigation cache cleared successfully'
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,12 +8,13 @@ use App\Models\News;
|
|||
use App\Models\Page;
|
||||
use Carbon\Carbon;
|
||||
use IqContent\LaravelFilemanager\Lfm;
|
||||
use Illuminate\Support\Str;
|
||||
use Request;
|
||||
|
||||
class CMSNewsController extends Controller
|
||||
{
|
||||
|
||||
/*
|
||||
/*
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
|
|
@ -21,29 +22,25 @@ class CMSNewsController extends Controller
|
|||
public function __construct()
|
||||
{
|
||||
$this->middleware(['admin', '2fa']);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function index()
|
||||
{
|
||||
$data = [
|
||||
'news' => News::all(),//News::where('lvl', 1)->get(),
|
||||
'news' => News::all(), //News::where('lvl', 1)->get(),
|
||||
];
|
||||
return view('cms.news.index', $data);
|
||||
|
||||
}
|
||||
|
||||
public function detail($id)
|
||||
{
|
||||
if($id === "new") {
|
||||
if ($id === "new") {
|
||||
$news = new News();
|
||||
$id = 'new';
|
||||
$news->status = 1;
|
||||
$news->content_new = "";
|
||||
|
||||
|
||||
}else{
|
||||
} else {
|
||||
$news = News::findOrFail($id);
|
||||
$id = $news->id;
|
||||
}
|
||||
|
|
@ -53,26 +50,25 @@ class CMSNewsController extends Controller
|
|||
'lfm_helper' => app(Lfm::class),
|
||||
];
|
||||
return view('cms.news.detail', $data);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function store($id)
|
||||
{
|
||||
$data = Request::all();
|
||||
if($id === "new") {
|
||||
if ($id === "new") {
|
||||
$news = new News();
|
||||
$news->model = 'news';
|
||||
$news->owner_second = 0;
|
||||
$news->show_in_navi = 1;
|
||||
$news->catalog_id = 1;
|
||||
}else{
|
||||
} else {
|
||||
$news = News::findOrFail($id);
|
||||
}
|
||||
|
||||
|
||||
$news->title = $data['title'];
|
||||
$news->status = isset($data['status']) ? true : false;
|
||||
$news->slug = $data['slug'];
|
||||
$news->slug = Str::slug($data['slug']);
|
||||
$news->date = $data['date'];
|
||||
$news->content_new = $data['content_new'];
|
||||
$news->box_body = $data['image'];
|
||||
|
|
@ -80,37 +76,37 @@ class CMSNewsController extends Controller
|
|||
$news->pagetitle = $data['pagetitle'];
|
||||
$news->keywords = $data['keywords'];
|
||||
|
||||
$news->order = (new Carbon($news->date))->format('Ymd')*-1;
|
||||
$news->order = (new Carbon($news->date))->format('Ymd') * -1;
|
||||
$root_news = News::where('cms_settings', 'news_root')->first();
|
||||
|
||||
if($id != $root_news->id){
|
||||
if ($id != $root_news->id) {
|
||||
//root ID = 3126
|
||||
$news->lvl = 1;
|
||||
$news->owner = $root_news->id;
|
||||
$news->parent_id = $root_news->id;
|
||||
$news->tree_root = $root_news->id;
|
||||
if($first_news = $root_news->children->first()){
|
||||
if ($first_news = $root_news->children->first()) {
|
||||
$news->lft = $first_news->lft;
|
||||
$news->rgt = $first_news->rgt;
|
||||
}else{
|
||||
$news->lft = $root_news->lft +1;
|
||||
$news->rgt = $root_news->lft +2;
|
||||
} else {
|
||||
$news->lft = $root_news->lft + 1;
|
||||
$news->rgt = $root_news->lft + 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
$news->save();
|
||||
\Session()->flash('alert-save', '1');
|
||||
return redirect(route('cms_news_detail', [$news->id]));
|
||||
|
||||
}
|
||||
|
||||
public function delete($id){
|
||||
public function delete($id)
|
||||
{
|
||||
$news = News::findOrFail($id);
|
||||
|
||||
//TODO
|
||||
//check for delete, only delete lvl 2 .,...?
|
||||
if ($news->lvl != 1){
|
||||
if ($news->lvl != 1) {
|
||||
abort(404);
|
||||
die();
|
||||
}
|
||||
|
|
@ -119,6 +115,4 @@ class CMSNewsController extends Controller
|
|||
\Session()->flash('alert-success', __('News gelöscht'));
|
||||
return redirect(route('cms_news'));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
165
app/Http/Controllers/NavigationTreeController.php
Normal file
165
app/Http/Controllers/NavigationTreeController.php
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Services\NavigationTreeService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class NavigationTreeController extends Controller
|
||||
{
|
||||
protected $navigationService;
|
||||
|
||||
public function __construct(NavigationTreeService $navigationService)
|
||||
{
|
||||
$this->navigationService = $navigationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeigt die Navigationsbaum-Übersicht
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('navigation.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die Navigationsbaum-Daten als JSON zurück (Frontend-Struktur)
|
||||
*
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function getData(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$includeHidden = $request->get('include_hidden', true);
|
||||
$tree = $this->navigationService->getFrontendNavigationTree($includeHidden);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $tree,
|
||||
'meta' => [
|
||||
'total_nodes' => $this->navigationService->countNodes($tree),
|
||||
'include_hidden' => $includeHidden,
|
||||
'structure' => 'frontend'
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sucht im Navigationsbaum
|
||||
*
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function search(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$query = $request->get('query', '');
|
||||
$flatList = $this->navigationService->getFlatNavigationList();
|
||||
|
||||
// Filtere nach Suchbegriff
|
||||
$results = array_filter($flatList, function ($node) use ($query) {
|
||||
return stripos($node['title'], $query) !== false
|
||||
|| stripos($node['slug'], $query) !== false
|
||||
|| stripos($node['url'], $query) !== false;
|
||||
});
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => array_values($results),
|
||||
'meta' => [
|
||||
'query' => $query,
|
||||
'total_results' => count($results)
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exportiert den Navigationsbaum als JSON-Datei
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function export(Request $request)
|
||||
{
|
||||
try {
|
||||
$includeHidden = $request->get('include_hidden', true);
|
||||
$tree = $this->navigationService->getFrontendNavigationTree($includeHidden);
|
||||
|
||||
$filename = 'navigation-tree-frontend-' . date('Y-m-d-His') . '.json';
|
||||
$json = json_encode($tree, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
return response($json)
|
||||
->header('Content-Type', 'application/json')
|
||||
->header('Content-Disposition', 'attachment; filename="' . $filename . '"');
|
||||
} catch (\Exception $e) {
|
||||
return back()->with('error', 'Export fehlgeschlagen: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Löscht den Cache
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function clearCache()
|
||||
{
|
||||
try {
|
||||
$this->navigationService->clearCache();
|
||||
return back()->with('success', 'Navigation-Cache erfolgreich gelöscht');
|
||||
} catch (\Exception $e) {
|
||||
return back()->with('error', 'Cache-Löschung fehlgeschlagen: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeigt die Statistiken
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function stats(): JsonResponse
|
||||
{
|
||||
try {
|
||||
$allTree = $this->navigationService->getNavigationTree(false);
|
||||
$activeTree = $this->navigationService->getNavigationTree(true);
|
||||
|
||||
$flatList = $this->navigationService->getFlatNavigationList();
|
||||
|
||||
// Zähle verschiedene Typen
|
||||
$stats = [
|
||||
'total_pages' => count($flatList),
|
||||
'total_nodes' => $this->navigationService->countNodes($allTree),
|
||||
'active_nodes' => $this->navigationService->countNodes($activeTree),
|
||||
'inactive_nodes' => $this->navigationService->countNodes($allTree) - $this->navigationService->countNodes($activeTree),
|
||||
'travel_programs' => count(array_filter($flatList, fn($n) => $n['is_travel_program'])),
|
||||
'fewo_lodgings' => count(array_filter($flatList, fn($n) => $n['is_fewo_lodging'])),
|
||||
'country_pages' => count(array_filter($flatList, fn($n) => $n['is_country_page'])),
|
||||
];
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $stats
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
391
app/Http/Controllers/NewsletterController.php
Normal file
391
app/Http/Controllers/NewsletterController.php
Normal file
|
|
@ -0,0 +1,391 @@
|
|||
<?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'),
|
||||
];
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue