Implement multi-tenancy support with OAuth2 authentication for tenant admins, Stripe integration for event purchases and credits ledger, new Filament resources for event purchases, updated API routes and middleware for tenant isolation and token guarding, added factories/seeders/migrations for new models (Tenant, EventPurchase, OAuth entities, etc.), enhanced tests, and documentation updates. Removed outdated DemoAchievementsSeeder.

This commit is contained in:
2025-09-17 19:56:54 +02:00
parent 5fbb9cb240
commit 42d6e98dff
84 changed files with 6125 additions and 155 deletions

View File

@@ -0,0 +1,242 @@
<?php
namespace App\Http\Controllers\Api\Tenant;
use App\Http\Controllers\Controller;
use App\Http\Requests\Tenant\TaskStoreRequest;
use App\Http\Requests\Tenant\TaskUpdateRequest;
use App\Http\Resources\Tenant\TaskResource;
use App\Models\Task;
use App\Models\TaskCollection;
use App\Models\Event;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Http\JsonResponse;
class TaskController extends Controller
{
/**
* Display a listing of the tenant's tasks.
*
* @param Request $request
* @return AnonymousResourceCollection
*/
public function index(Request $request): AnonymousResourceCollection
{
$query = Task::where('tenant_id', $request->tenant->id)
->with(['taskCollection', 'assignedEvents'])
->orderBy('created_at', 'desc');
// Search and filters
if ($search = $request->get('search')) {
$query->where('title', 'like', "%{$search}%")
->orWhere('description', 'like', "%{$search}%");
}
if ($collectionId = $request->get('collection_id')) {
$query->whereHas('taskCollection', fn($q) => $q->where('id', $collectionId));
}
if ($eventId = $request->get('event_id')) {
$query->whereHas('assignedEvents', fn($q) => $q->where('id', $eventId));
}
$perPage = $request->get('per_page', 15);
$tasks = $query->paginate($perPage);
return TaskResource::collection($tasks);
}
/**
* Store a newly created task in storage.
*
* @param TaskStoreRequest $request
* @return JsonResponse
*/
public function store(TaskStoreRequest $request): JsonResponse
{
$task = Task::create(array_merge($request->validated(), [
'tenant_id' => $request->tenant->id,
]));
if ($collectionId = $request->input('collection_id')) {
$task->taskCollection()->associate(TaskCollection::findOrFail($collectionId));
$task->save();
}
$task->load(['taskCollection', 'assignedEvents']);
return response()->json([
'message' => 'Task erfolgreich erstellt.',
'data' => new TaskResource($task),
], 201);
}
/**
* Display the specified task.
*
* @param Request $request
* @param Task $task
* @return JsonResponse
*/
public function show(Request $request, Task $task): JsonResponse
{
if ($task->tenant_id !== $request->tenant->id) {
abort(404, 'Task nicht gefunden.');
}
$task->load(['taskCollection', 'assignedEvents']);
return response()->json(new TaskResource($task));
}
/**
* Update the specified task in storage.
*
* @param TaskUpdateRequest $request
* @param Task $task
* @return JsonResponse
*/
public function update(TaskUpdateRequest $request, Task $task): JsonResponse
{
if ($task->tenant_id !== $request->tenant->id) {
abort(404, 'Task nicht gefunden.');
}
$task->update($request->validated());
if ($collectionId = $request->input('collection_id')) {
$task->taskCollection()->associate(TaskCollection::findOrFail($collectionId));
$task->save();
}
$task->load(['taskCollection', 'assignedEvents']);
return response()->json([
'message' => 'Task erfolgreich aktualisiert.',
'data' => new TaskResource($task),
]);
}
/**
* Remove the specified task from storage.
*
* @param Request $request
* @param Task $task
* @return JsonResponse
*/
public function destroy(Request $request, Task $task): JsonResponse
{
if ($task->tenant_id !== $request->tenant->id) {
abort(404, 'Task nicht gefunden.');
}
$task->delete();
return response()->json([
'message' => 'Task erfolgreich gelöscht.',
]);
}
/**
* Assign task to an event.
*
* @param Request $request
* @param Task $task
* @param Event $event
* @return JsonResponse
*/
public function assignToEvent(Request $request, Task $task, Event $event): JsonResponse
{
if ($task->tenant_id !== $request->tenant->id || $event->tenant_id !== $request->tenant->id) {
abort(404);
}
if ($task->assignedEvents()->where('event_id', $event->id)->exists()) {
return response()->json(['message' => 'Task ist bereits diesem Event zugewiesen.'], 409);
}
$task->assignedEvents()->attach($event->id);
return response()->json([
'message' => 'Task erfolgreich dem Event zugewiesen.',
]);
}
/**
* Bulk assign tasks to an event.
*
* @param Request $request
* @param Event $event
* @return JsonResponse
*/
public function bulkAssignToEvent(Request $request, Event $event): JsonResponse
{
if ($event->tenant_id !== $request->tenant->id) {
abort(404);
}
$taskIds = $request->input('task_ids', []);
if (empty($taskIds)) {
return response()->json(['error' => 'Keine Task-IDs angegeben.'], 400);
}
$tasks = Task::whereIn('id', $taskIds)
->where('tenant_id', $request->tenant->id)
->get();
$attached = 0;
foreach ($tasks as $task) {
if (!$task->assignedEvents()->where('event_id', $event->id)->exists()) {
$task->assignedEvents()->attach($event->id);
$attached++;
}
}
return response()->json([
'message' => "{$attached} Tasks dem Event zugewiesen.",
]);
}
/**
* Get tasks for a specific event.
*
* @param Request $request
* @param Event $event
* @return AnonymousResourceCollection
*/
public function forEvent(Request $request, Event $event): AnonymousResourceCollection
{
if ($event->tenant_id !== $request->tenant->id) {
abort(404);
}
$tasks = Task::whereHas('assignedEvents', fn($q) => $q->where('event_id', $event->id))
->with(['taskCollection'])
->orderBy('created_at', 'desc')
->paginate($request->get('per_page', 15));
return TaskResource::collection($tasks);
}
/**
* Get tasks from a specific collection.
*
* @param Request $request
* @param TaskCollection $collection
* @return AnonymousResourceCollection
*/
public function fromCollection(Request $request, TaskCollection $collection): AnonymousResourceCollection
{
if ($collection->tenant_id !== $request->tenant->id) {
abort(404);
}
$tasks = $collection->tasks()
->with(['assignedEvents'])
->orderBy('created_at', 'desc')
->paginate($request->get('per_page', 15));
return TaskResource::collection($tasks);
}
}