coupon code system eingeführt. coupons werden vom super admin gemanaged. coupons werden mit paddle synchronisiert und dort validiert. plus: einige mobil-optimierungen im tenant admin pwa.

This commit is contained in:
Codex Agent
2025-11-09 20:26:50 +01:00
parent f3c44be76d
commit 082b78cd43
80 changed files with 4855 additions and 435 deletions

View File

@@ -7,6 +7,8 @@ use App\Http\Requests\Tenant\EmotionStoreRequest;
use App\Http\Requests\Tenant\EmotionUpdateRequest;
use App\Http\Resources\Tenant\EmotionResource;
use App\Models\Emotion;
use App\Models\Tenant;
use App\Support\TenantRequestResolver;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
@@ -16,7 +18,7 @@ class EmotionController extends Controller
{
public function index(Request $request): AnonymousResourceCollection
{
$tenantId = $request->tenant->id;
$tenantId = $this->currentTenant($request)->id;
$query = Emotion::query()
->whereNull('tenant_id')
@@ -41,9 +43,10 @@ class EmotionController extends Controller
public function store(EmotionStoreRequest $request): JsonResponse
{
$data = $request->validated();
$tenantId = $this->currentTenant($request)->id;
$payload = [
'tenant_id' => $request->tenant->id,
'tenant_id' => $tenantId,
'name' => $this->localizeValue($data['name']),
'description' => $this->localizeValue($data['description'] ?? null, allowNull: true),
'icon' => $data['icon'] ?? 'lucide-smile',
@@ -70,7 +73,9 @@ class EmotionController extends Controller
public function update(EmotionUpdateRequest $request, Emotion $emotion): JsonResponse
{
if ($emotion->tenant_id && $emotion->tenant_id !== $request->tenant->id) {
$tenantId = $this->currentTenant($request)->id;
if ($emotion->tenant_id && $emotion->tenant_id !== $tenantId) {
abort(403, 'Emotion gehört nicht zu diesem Tenant.');
}
@@ -139,6 +144,7 @@ class EmotionController extends Controller
if (is_string($value) && $value !== '') {
$locale = app()->getLocale() ?: 'de';
return [$locale => $value];
}
@@ -149,9 +155,14 @@ class EmotionController extends Controller
{
$normalized = ltrim($color, '#');
if (strlen($normalized) === 6) {
return '#' . strtolower($normalized);
return '#'.strtolower($normalized);
}
return '#6366f1';
}
protected function currentTenant(Request $request): Tenant
{
return TenantRequestResolver::resolve($request);
}
}

View File

@@ -204,7 +204,7 @@ class SettingsController extends Controller
}
$taken = Tenant::where('custom_domain', $domain)
->where('id', '!=', $request->tenant->id)
->where('id', '!=', $this->resolveTenant($request)->id)
->exists();
return response()->json([

View File

@@ -6,17 +6,19 @@ use App\Http\Controllers\Controller;
use App\Http\Resources\Tenant\TaskCollectionResource;
use App\Models\Event;
use App\Models\TaskCollection;
use App\Models\Tenant;
use App\Services\Tenant\TaskCollectionImportService;
use App\Support\TenantRequestResolver;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Http\JsonResponse;
use Illuminate\Validation\Rule;
class TaskCollectionController extends Controller
{
public function index(Request $request): AnonymousResourceCollection
{
$tenantId = $request->tenant->id;
$tenantId = $this->currentTenant($request)->id;
$query = TaskCollection::query()
->forTenant($tenantId)
@@ -68,11 +70,11 @@ class TaskCollectionController extends Controller
$this->authorizeAccess($request, $collection);
$data = $request->validate([
'event_slug' => ['required', 'string', Rule::exists('events', 'slug')->where('tenant_id', $request->tenant->id)],
'event_slug' => ['required', 'string', Rule::exists('events', 'slug')->where('tenant_id', $this->currentTenant($request)->id)],
]);
$event = Event::where('slug', $data['event_slug'])
->where('tenant_id', $request->tenant->id)
->where('tenant_id', $this->currentTenant($request)->id)
->firstOrFail();
$result = $importService->import($collection, $event);
@@ -87,8 +89,13 @@ class TaskCollectionController extends Controller
protected function authorizeAccess(Request $request, TaskCollection $collection): void
{
if ($collection->tenant_id && $collection->tenant_id !== $request->tenant->id) {
if ($collection->tenant_id && $collection->tenant_id !== $this->currentTenant($request)->id) {
abort(404);
}
}
protected function currentTenant(Request $request): Tenant
{
return TenantRequestResolver::resolve($request);
}
}

View File

@@ -9,7 +9,9 @@ use App\Http\Resources\Tenant\TaskResource;
use App\Models\Event;
use App\Models\Task;
use App\Models\TaskCollection;
use App\Models\Tenant;
use App\Support\ApiError;
use App\Support\TenantRequestResolver;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
@@ -22,14 +24,14 @@ class TaskController extends Controller
*/
public function index(Request $request): AnonymousResourceCollection
{
$tenantId = $request->tenant->id;
$tenantId = $this->currentTenant($request)->id;
$query = Task::query()
->where(function ($inner) use ($tenantId) {
$inner->whereNull('tenant_id')
->orWhere('tenant_id', $tenantId);
})
->with(['taskCollection', 'assignedEvents'])
->with(['taskCollection', 'assignedEvents', 'eventType'])
->orderByRaw('tenant_id is null desc')
->orderBy('sort_order')
->orderBy('created_at', 'desc');
@@ -64,11 +66,12 @@ class TaskController extends Controller
*/
public function store(TaskStoreRequest $request): JsonResponse
{
$tenant = $this->currentTenant($request);
$collectionId = $request->input('collection_id');
$collection = $collectionId ? $this->resolveAccessibleCollection($request, $collectionId) : null;
$payload = $this->prepareTaskPayload($request->validated(), $request->tenant->id);
$payload['tenant_id'] = $request->tenant->id;
$payload = $this->prepareTaskPayload($request->validated(), $tenant->id);
$payload['tenant_id'] = $tenant->id;
if ($collection) {
$payload['collection_id'] = $collection->id;
@@ -77,7 +80,7 @@ class TaskController extends Controller
$task = Task::create($payload);
$task->load(['taskCollection', 'assignedEvents']);
$task->load(['taskCollection', 'assignedEvents', 'eventType']);
return response()->json([
'message' => 'Task erfolgreich erstellt.',
@@ -90,11 +93,11 @@ class TaskController extends Controller
*/
public function show(Request $request, Task $task): JsonResponse
{
if ($task->tenant_id && $task->tenant_id !== $request->tenant->id) {
if ($task->tenant_id && $task->tenant_id !== $this->currentTenant($request)->id) {
abort(404, 'Task nicht gefunden.');
}
$task->load(['taskCollection', 'assignedEvents']);
$task->load(['taskCollection', 'assignedEvents', 'eventType']);
return response()->json(new TaskResource($task));
}
@@ -104,14 +107,16 @@ class TaskController extends Controller
*/
public function update(TaskUpdateRequest $request, Task $task): JsonResponse
{
if ($task->tenant_id !== $request->tenant->id) {
$tenant = $this->currentTenant($request);
if ($task->tenant_id !== $tenant->id) {
abort(404, 'Task nicht gefunden.');
}
$collectionId = $request->input('collection_id');
$collection = $collectionId ? $this->resolveAccessibleCollection($request, $collectionId) : null;
$payload = $this->prepareTaskPayload($request->validated(), $request->tenant->id, $task);
$payload = $this->prepareTaskPayload($request->validated(), $tenant->id, $task);
if ($collection) {
$payload['collection_id'] = $collection->id;
@@ -133,7 +138,7 @@ class TaskController extends Controller
*/
public function destroy(Request $request, Task $task): JsonResponse
{
if ($task->tenant_id !== $request->tenant->id) {
if ($task->tenant_id !== $this->currentTenant($request)->id) {
abort(404, 'Task nicht gefunden.');
}
@@ -149,7 +154,9 @@ class TaskController extends Controller
*/
public function assignToEvent(Request $request, Task $task, Event $event): JsonResponse
{
if ($task->tenant_id !== $request->tenant->id || $event->tenant_id !== $request->tenant->id) {
$tenantId = $this->currentTenant($request)->id;
if ($task->tenant_id !== $tenantId || $event->tenant_id !== $tenantId) {
abort(404);
}
@@ -169,7 +176,9 @@ class TaskController extends Controller
*/
public function bulkAssignToEvent(Request $request, Event $event): JsonResponse
{
if ($event->tenant_id !== $request->tenant->id) {
$tenantId = $this->currentTenant($request)->id;
if ($event->tenant_id !== $tenantId) {
abort(404);
}
@@ -184,7 +193,7 @@ class TaskController extends Controller
}
$tasks = Task::whereIn('id', $taskIds)
->where('tenant_id', $request->tenant->id)
->where('tenant_id', $tenantId)
->get();
$attached = 0;
@@ -205,12 +214,12 @@ class TaskController extends Controller
*/
public function forEvent(Request $request, Event $event): AnonymousResourceCollection
{
if ($event->tenant_id !== $request->tenant->id) {
if ($event->tenant_id !== $this->currentTenant($request)->id) {
abort(404);
}
$tasks = Task::whereHas('assignedEvents', fn ($q) => $q->where('event_id', $event->id))
->with(['taskCollection'])
->with(['taskCollection', 'eventType'])
->orderBy('created_at', 'desc')
->paginate($request->get('per_page', 15));
@@ -222,12 +231,12 @@ class TaskController extends Controller
*/
public function fromCollection(Request $request, TaskCollection $collection): AnonymousResourceCollection
{
if ($collection->tenant_id && $collection->tenant_id !== $request->tenant->id) {
if ($collection->tenant_id && $collection->tenant_id !== $this->currentTenant($request)->id) {
abort(404);
}
$tasks = $collection->tasks()
->with(['assignedEvents'])
->with(['assignedEvents', 'eventType'])
->orderBy('created_at', 'desc')
->paginate($request->get('per_page', 15));
@@ -240,13 +249,20 @@ class TaskController extends Controller
->where(function ($query) use ($request) {
$query->whereNull('tenant_id');
if ($request->tenant?->id) {
$query->orWhere('tenant_id', $request->tenant->id);
$tenantId = $this->currentTenant($request)->id;
if ($tenantId) {
$query->orWhere('tenant_id', $tenantId);
}
})
->firstOrFail();
}
protected function currentTenant(Request $request): Tenant
{
return TenantRequestResolver::resolve($request);
}
protected function prepareTaskPayload(array $data, int $tenantId, ?Task $original = null): array
{
if (array_key_exists('title', $data)) {