- Reworked the tenant admin login page

- Updated the User model to implement Filament’s tenancy contracts
- Seeded a ready-to-use demo tenant (user, tenant, active package, purchase)
- Introduced a branded, translated 403 error page to replace the generic forbidden message for unauthorised admin hits
- Removed the public “Register” links from the marketing header
- hardened join event logic and improved error handling in the guest pwa.
This commit is contained in:
Codex Agent
2025-10-13 12:50:46 +02:00
parent 9394c3171e
commit 64a5411fb9
69 changed files with 5447 additions and 588 deletions

View File

@@ -3,6 +3,7 @@
use App\Http\Controllers\Api\EventPublicController;
use App\Http\Controllers\Api\Tenant\EventController;
use App\Http\Controllers\Api\Tenant\EventJoinTokenController;
use App\Http\Controllers\Api\Tenant\EventJoinTokenLayoutController;
use App\Http\Controllers\Api\Tenant\SettingsController;
use App\Http\Controllers\Api\Tenant\TaskController;
use App\Http\Controllers\Api\Tenant\PhotoController;
@@ -26,15 +27,15 @@ Route::prefix('v1')->name('api.v1.')->group(function () {
});
Route::middleware('throttle:100,1')->group(function () {
Route::get('/events/{slug}', [EventPublicController::class, 'event'])->name('events.show');
Route::get('/events/{slug}/stats', [EventPublicController::class, 'stats'])->name('events.stats');
Route::get('/events/{slug}/achievements', [EventPublicController::class, 'achievements'])->name('events.achievements');
Route::get('/events/{slug}/emotions', [EventPublicController::class, 'emotions'])->name('events.emotions');
Route::get('/events/{slug}/tasks', [EventPublicController::class, 'tasks'])->name('events.tasks');
Route::get('/events/{slug}/photos', [EventPublicController::class, 'photos'])->name('events.photos');
Route::get('/events/{token}', [EventPublicController::class, 'event'])->name('events.show');
Route::get('/events/{token}/stats', [EventPublicController::class, 'stats'])->name('events.stats');
Route::get('/events/{token}/achievements', [EventPublicController::class, 'achievements'])->name('events.achievements');
Route::get('/events/{token}/emotions', [EventPublicController::class, 'emotions'])->name('events.emotions');
Route::get('/events/{token}/tasks', [EventPublicController::class, 'tasks'])->name('events.tasks');
Route::get('/events/{token}/photos', [EventPublicController::class, 'photos'])->name('events.photos');
Route::get('/photos/{id}', [EventPublicController::class, 'photo'])->name('photos.show');
Route::post('/photos/{id}/like', [EventPublicController::class, 'like'])->name('photos.like');
Route::post('/events/{slug}/upload', [EventPublicController::class, 'upload'])->name('events.upload');
Route::post('/events/{token}/upload', [EventPublicController::class, 'upload'])->name('events.upload');
});
Route::middleware(['tenant.token', 'tenant.isolation', 'throttle:tenant-api'])->prefix('tenant')->group(function () {
@@ -57,6 +58,13 @@ Route::prefix('v1')->name('api.v1.')->group(function () {
Route::prefix('join-tokens')->group(function () {
Route::get('/', [EventJoinTokenController::class, 'index'])->name('tenant.events.join-tokens.index');
Route::post('/', [EventJoinTokenController::class, 'store'])->name('tenant.events.join-tokens.store');
Route::get('{joinToken}/layouts', [EventJoinTokenLayoutController::class, 'index'])
->whereNumber('joinToken')
->name('tenant.events.join-tokens.layouts.index');
Route::get('{joinToken}/layouts/{layout}.{format}', [EventJoinTokenLayoutController::class, 'download'])
->whereNumber('joinToken')
->where('format', 'pdf|svg')
->name('tenant.events.join-tokens.layouts.download');
Route::delete('{joinToken}', [EventJoinTokenController::class, 'destroy'])
->whereNumber('joinToken')
->name('tenant.events.join-tokens.destroy');