refactor: align codebase with EventCrew domain and trim legacy band stack
- Update API: events, users, policies, routes, resources, migrations - Remove deprecated models/resources (customers, setlists, invitations, etc.) - Refresh admin app and docs; remove apps/band Made-with: Cursor
This commit is contained in:
@@ -1,75 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\V1\LoginRequest;
|
||||
use App\Http\Requests\Api\V1\RegisterRequest;
|
||||
use App\Http\Resources\Api\V1\UserResource;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
final class AuthController extends Controller
|
||||
{
|
||||
public function login(LoginRequest $request): JsonResponse
|
||||
{
|
||||
$credentials = $request->only('email', 'password');
|
||||
|
||||
if (!Auth::attempt($credentials)) {
|
||||
return $this->unauthorized('Invalid credentials');
|
||||
}
|
||||
|
||||
$user = Auth::user();
|
||||
|
||||
if (!$user->isActive()) {
|
||||
Auth::logout();
|
||||
return $this->forbidden('Your account is inactive');
|
||||
}
|
||||
|
||||
$token = $user->createToken('auth-token')->plainTextToken;
|
||||
|
||||
return $this->success([
|
||||
'user' => new UserResource($user),
|
||||
'token' => $token,
|
||||
], 'Login successful');
|
||||
}
|
||||
|
||||
public function register(RegisterRequest $request): JsonResponse
|
||||
{
|
||||
$user = User::create([
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
'password' => Hash::make($request->password),
|
||||
'type' => 'member',
|
||||
'role' => 'member',
|
||||
'status' => 'active',
|
||||
]);
|
||||
|
||||
$token = $user->createToken('auth-token')->plainTextToken;
|
||||
|
||||
return $this->created([
|
||||
'user' => new UserResource($user),
|
||||
'token' => $token,
|
||||
], 'Registration successful');
|
||||
}
|
||||
|
||||
public function user(Request $request): JsonResponse
|
||||
{
|
||||
return $this->success(
|
||||
new UserResource($request->user())
|
||||
);
|
||||
}
|
||||
|
||||
public function logout(Request $request): JsonResponse
|
||||
{
|
||||
$request->user()->currentAccessToken()->delete();
|
||||
|
||||
return $this->success(null, 'Logged out successfully');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,151 +4,55 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Enums\RsvpStatus;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\V1\InviteToEventRequest;
|
||||
use App\Http\Requests\Api\V1\RsvpEventRequest;
|
||||
use App\Http\Requests\Api\V1\StoreEventRequest;
|
||||
use App\Http\Requests\Api\V1\UpdateEventRequest;
|
||||
use App\Http\Resources\Api\V1\EventCollection;
|
||||
use App\Http\Resources\Api\V1\EventInvitationResource;
|
||||
use App\Http\Resources\Api\V1\EventResource;
|
||||
use App\Models\Event;
|
||||
use App\Models\EventInvitation;
|
||||
use App\Models\Organisation;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
final class EventController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of events.
|
||||
*/
|
||||
public function index(): EventCollection
|
||||
public function index(Organisation $organisation): AnonymousResourceCollection
|
||||
{
|
||||
Gate::authorize('viewAny', Event::class);
|
||||
Gate::authorize('viewAny', [Event::class, $organisation]);
|
||||
|
||||
$events = Event::query()
|
||||
->with(['location', 'customer'])
|
||||
->latest('event_date')
|
||||
$events = $organisation->events()
|
||||
->latest('start_date')
|
||||
->paginate();
|
||||
|
||||
return new EventCollection($events);
|
||||
return EventResource::collection($events);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created event.
|
||||
*/
|
||||
public function store(StoreEventRequest $request): JsonResponse
|
||||
{
|
||||
Gate::authorize('create', Event::class);
|
||||
|
||||
$data = $request->validated();
|
||||
$data['created_by'] = auth()->id();
|
||||
$data['currency'] = $data['currency'] ?? 'EUR';
|
||||
|
||||
$event = Event::create($data);
|
||||
|
||||
return $this->created(
|
||||
new EventResource($event->load(['location', 'customer'])),
|
||||
'Event created successfully'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified event.
|
||||
*/
|
||||
public function show(Event $event): EventResource
|
||||
public function show(Organisation $organisation, Event $event): JsonResponse
|
||||
{
|
||||
Gate::authorize('view', $event);
|
||||
|
||||
return new EventResource(
|
||||
$event->load(['location', 'customer', 'setlist.items.musicNumber', 'invitations.user', 'creator'])
|
||||
);
|
||||
abort_unless($event->organisation_id === $organisation->id, 404);
|
||||
|
||||
return $this->success(new EventResource($event->load('organisation')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified event.
|
||||
*/
|
||||
public function update(UpdateEventRequest $request, Event $event): JsonResponse
|
||||
public function store(StoreEventRequest $request, Organisation $organisation): JsonResponse
|
||||
{
|
||||
Gate::authorize('create', [Event::class, $organisation]);
|
||||
|
||||
$event = $organisation->events()->create($request->validated());
|
||||
|
||||
return $this->created(new EventResource($event));
|
||||
}
|
||||
|
||||
public function update(UpdateEventRequest $request, Organisation $organisation, Event $event): JsonResponse
|
||||
{
|
||||
Gate::authorize('update', $event);
|
||||
|
||||
abort_unless($event->organisation_id === $organisation->id, 404);
|
||||
|
||||
$event->update($request->validated());
|
||||
|
||||
return $this->success(
|
||||
new EventResource($event->load(['location', 'customer'])),
|
||||
'Event updated successfully'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified event.
|
||||
*/
|
||||
public function destroy(Event $event): JsonResponse
|
||||
{
|
||||
Gate::authorize('delete', $event);
|
||||
|
||||
$event->delete();
|
||||
|
||||
return $this->success(null, 'Event deleted successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* Invite members to an event.
|
||||
*/
|
||||
public function invite(InviteToEventRequest $request, Event $event): JsonResponse
|
||||
{
|
||||
Gate::authorize('invite', $event);
|
||||
|
||||
$userIds = $request->validated()['user_ids'];
|
||||
$invitedCount = 0;
|
||||
|
||||
foreach ($userIds as $userId) {
|
||||
// Skip if already invited
|
||||
if ($event->invitations()->where('user_id', $userId)->exists()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$event->invitations()->create([
|
||||
'user_id' => $userId,
|
||||
'rsvp_status' => RsvpStatus::Pending,
|
||||
'invited_at' => now(),
|
||||
]);
|
||||
|
||||
$invitedCount++;
|
||||
}
|
||||
|
||||
return $this->success(
|
||||
EventInvitationResource::collection(
|
||||
$event->invitations()->with('user')->get()
|
||||
),
|
||||
"{$invitedCount} member(s) invited successfully"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Respond to an event invitation (RSVP).
|
||||
*/
|
||||
public function rsvp(RsvpEventRequest $request, Event $event): JsonResponse
|
||||
{
|
||||
Gate::authorize('rsvp', $event);
|
||||
|
||||
$invitation = EventInvitation::where('event_id', $event->id)
|
||||
->where('user_id', auth()->id())
|
||||
->firstOrFail();
|
||||
|
||||
$data = $request->validated();
|
||||
|
||||
$invitation->update([
|
||||
'rsvp_status' => $data['status'],
|
||||
'rsvp_note' => $data['note'] ?? null,
|
||||
'rsvp_responded_at' => now(),
|
||||
]);
|
||||
|
||||
return $this->success(
|
||||
new EventInvitationResource($invitation->load('user')),
|
||||
'RSVP updated successfully'
|
||||
);
|
||||
return $this->success(new EventResource($event->fresh()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
29
api/app/Http/Controllers/Api/V1/LoginController.php
Normal file
29
api/app/Http/Controllers/Api/V1/LoginController.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\V1\LoginRequest;
|
||||
use App\Http\Resources\Api\V1\UserResource;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
final class LoginController extends Controller
|
||||
{
|
||||
public function __invoke(LoginRequest $request): JsonResponse
|
||||
{
|
||||
if (!Auth::attempt($request->only('email', 'password'))) {
|
||||
return $this->unauthorized('Invalid credentials');
|
||||
}
|
||||
|
||||
$user = Auth::user()->load(['organisations', 'roles']);
|
||||
$token = $user->createToken('auth-token')->plainTextToken;
|
||||
|
||||
return $this->success([
|
||||
'user' => new UserResource($user),
|
||||
'token' => $token,
|
||||
], 'Login successful');
|
||||
}
|
||||
}
|
||||
19
api/app/Http/Controllers/Api/V1/LogoutController.php
Normal file
19
api/app/Http/Controllers/Api/V1/LogoutController.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
final class LogoutController extends Controller
|
||||
{
|
||||
public function __invoke(Request $request): JsonResponse
|
||||
{
|
||||
$request->user()->currentAccessToken()->delete();
|
||||
|
||||
return $this->success(null, 'Logged out successfully');
|
||||
}
|
||||
}
|
||||
20
api/app/Http/Controllers/Api/V1/MeController.php
Normal file
20
api/app/Http/Controllers/Api/V1/MeController.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\Api\V1\UserResource;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
final class MeController extends Controller
|
||||
{
|
||||
public function __invoke(Request $request): JsonResponse
|
||||
{
|
||||
$user = $request->user()->load(['organisations', 'events']);
|
||||
|
||||
return $this->success(new UserResource($user));
|
||||
}
|
||||
}
|
||||
55
api/app/Http/Controllers/Api/V1/OrganisationController.php
Normal file
55
api/app/Http/Controllers/Api/V1/OrganisationController.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\V1\StoreOrganisationRequest;
|
||||
use App\Http\Requests\Api\V1\UpdateOrganisationRequest;
|
||||
use App\Http\Resources\Api\V1\OrganisationResource;
|
||||
use App\Models\Organisation;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
final class OrganisationController extends Controller
|
||||
{
|
||||
public function index(): AnonymousResourceCollection
|
||||
{
|
||||
Gate::authorize('viewAny', Organisation::class);
|
||||
|
||||
$user = auth()->user();
|
||||
|
||||
$organisations = $user->hasRole('super_admin')
|
||||
? Organisation::query()->paginate()
|
||||
: $user->organisations()->paginate();
|
||||
|
||||
return OrganisationResource::collection($organisations);
|
||||
}
|
||||
|
||||
public function show(Organisation $organisation): JsonResponse
|
||||
{
|
||||
Gate::authorize('view', $organisation);
|
||||
|
||||
return $this->success(new OrganisationResource($organisation));
|
||||
}
|
||||
|
||||
public function store(StoreOrganisationRequest $request): JsonResponse
|
||||
{
|
||||
Gate::authorize('create', Organisation::class);
|
||||
|
||||
$organisation = Organisation::create($request->validated());
|
||||
|
||||
return $this->created(new OrganisationResource($organisation));
|
||||
}
|
||||
|
||||
public function update(UpdateOrganisationRequest $request, Organisation $organisation): JsonResponse
|
||||
{
|
||||
Gate::authorize('update', $organisation);
|
||||
|
||||
$organisation->update($request->validated());
|
||||
|
||||
return $this->success(new OrganisationResource($organisation->fresh()));
|
||||
}
|
||||
}
|
||||
20
api/app/Http/Middleware/PortalTokenMiddleware.php
Normal file
20
api/app/Http/Middleware/PortalTokenMiddleware.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class PortalTokenMiddleware
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Api\V1;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
final class InviteToEventRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'user_ids' => ['required', 'array', 'min:1'],
|
||||
'user_ids.*' => ['required', 'ulid', 'exists:users,id'],
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'user_ids.required' => 'Please select at least one member to invite.',
|
||||
'user_ids.*.exists' => 'One or more selected members do not exist.',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Api\V1;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
final class RegisterRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email'],
|
||||
'password' => ['required', 'string', 'confirmed', Password::defaults()],
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'name.required' => 'Name is required',
|
||||
'email.required' => 'Email is required',
|
||||
'email.email' => 'Please enter a valid email address',
|
||||
'email.unique' => 'This email is already registered',
|
||||
'password.required' => 'Password is required',
|
||||
'password.confirmed' => 'Password confirmation does not match',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Api\V1;
|
||||
|
||||
use App\Enums\RsvpStatus;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
final class RsvpEventRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'status' => ['required', Rule::enum(RsvpStatus::class)],
|
||||
'note' => ['nullable', 'string', 'max:1000'],
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'status.required' => 'Please select your availability status.',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Api\V1;
|
||||
|
||||
use App\Enums\EventStatus;
|
||||
use App\Enums\EventVisibility;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
final class StoreEventRequest extends FormRequest
|
||||
{
|
||||
@@ -16,37 +13,16 @@ final class StoreEventRequest extends FormRequest
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'title' => ['required', 'string', 'max:255'],
|
||||
'description' => ['nullable', 'string', 'max:5000'],
|
||||
'location_id' => ['nullable', 'ulid', 'exists:locations,id'],
|
||||
'customer_id' => ['nullable', 'ulid', 'exists:customers,id'],
|
||||
'setlist_id' => ['nullable', 'ulid', 'exists:setlists,id'],
|
||||
'event_date' => ['required', 'date', 'after_or_equal:today'],
|
||||
'start_time' => ['required', 'date_format:H:i'],
|
||||
'end_time' => ['nullable', 'date_format:H:i', 'after:start_time'],
|
||||
'load_in_time' => ['nullable', 'date_format:H:i'],
|
||||
'soundcheck_time' => ['nullable', 'date_format:H:i'],
|
||||
'fee' => ['nullable', 'numeric', 'min:0', 'max:999999.99'],
|
||||
'currency' => ['sometimes', 'string', 'size:3'],
|
||||
'status' => ['sometimes', Rule::enum(EventStatus::class)],
|
||||
'visibility' => ['sometimes', Rule::enum(EventVisibility::class)],
|
||||
'rsvp_deadline' => ['nullable', 'date', 'before:event_date'],
|
||||
'notes' => ['nullable', 'string', 'max:5000'],
|
||||
'internal_notes' => ['nullable', 'string', 'max:5000'],
|
||||
'is_public_setlist' => ['sometimes', 'boolean'],
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'event_date.after_or_equal' => 'The event date must be today or a future date.',
|
||||
'end_time.after' => 'The end time must be after the start time.',
|
||||
'rsvp_deadline.before' => 'The RSVP deadline must be before the event date.',
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'slug' => ['required', 'string', 'max:255', 'regex:/^[a-z0-9-]+$/'],
|
||||
'start_date' => ['required', 'date'],
|
||||
'end_date' => ['required', 'date', 'after_or_equal:start_date'],
|
||||
'timezone' => ['sometimes', 'string', 'max:50'],
|
||||
'status' => ['sometimes', 'string', 'in:draft,published,registration_open,buildup,showday,teardown,closed'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
26
api/app/Http/Requests/Api/V1/StoreOrganisationRequest.php
Normal file
26
api/app/Http/Requests/Api/V1/StoreOrganisationRequest.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Api\V1;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
final class StoreOrganisationRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'slug' => ['required', 'string', 'max:255', 'unique:organisations,slug', 'regex:/^[a-z0-9-]+$/'],
|
||||
'billing_status' => ['sometimes', 'string', 'in:active,trial,suspended'],
|
||||
'settings' => ['sometimes', 'array'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Api\V1;
|
||||
|
||||
use App\Enums\EventStatus;
|
||||
use App\Enums\EventVisibility;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
final class UpdateEventRequest extends FormRequest
|
||||
{
|
||||
@@ -16,35 +13,16 @@ final class UpdateEventRequest extends FormRequest
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'title' => ['sometimes', 'string', 'max:255'],
|
||||
'description' => ['nullable', 'string', 'max:5000'],
|
||||
'location_id' => ['nullable', 'ulid', 'exists:locations,id'],
|
||||
'customer_id' => ['nullable', 'ulid', 'exists:customers,id'],
|
||||
'setlist_id' => ['nullable', 'ulid', 'exists:setlists,id'],
|
||||
'event_date' => ['sometimes', 'date'],
|
||||
'start_time' => ['sometimes', 'date_format:H:i'],
|
||||
'end_time' => ['nullable', 'date_format:H:i'],
|
||||
'load_in_time' => ['nullable', 'date_format:H:i'],
|
||||
'soundcheck_time' => ['nullable', 'date_format:H:i'],
|
||||
'fee' => ['nullable', 'numeric', 'min:0', 'max:999999.99'],
|
||||
'currency' => ['sometimes', 'string', 'size:3'],
|
||||
'status' => ['sometimes', Rule::enum(EventStatus::class)],
|
||||
'visibility' => ['sometimes', Rule::enum(EventVisibility::class)],
|
||||
'rsvp_deadline' => ['nullable', 'date'],
|
||||
'notes' => ['nullable', 'string', 'max:5000'],
|
||||
'internal_notes' => ['nullable', 'string', 'max:5000'],
|
||||
'is_public_setlist' => ['sometimes', 'boolean'],
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'end_time.after' => 'The end time must be after the start time.',
|
||||
'name' => ['sometimes', 'string', 'max:255'],
|
||||
'slug' => ['sometimes', 'string', 'max:255', 'regex:/^[a-z0-9-]+$/'],
|
||||
'start_date' => ['sometimes', 'date'],
|
||||
'end_date' => ['sometimes', 'date', 'after_or_equal:start_date'],
|
||||
'timezone' => ['sometimes', 'string', 'max:50'],
|
||||
'status' => ['sometimes', 'string', 'in:draft,published,registration_open,buildup,showday,teardown,closed'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
30
api/app/Http/Requests/Api/V1/UpdateOrganisationRequest.php
Normal file
30
api/app/Http/Requests/Api/V1/UpdateOrganisationRequest.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Api\V1;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
final class UpdateOrganisationRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => ['sometimes', 'string', 'max:255'],
|
||||
'slug' => [
|
||||
'sometimes', 'string', 'max:255', 'regex:/^[a-z0-9-]+$/',
|
||||
Rule::unique('organisations', 'slug')->ignore($this->route('organisation')),
|
||||
],
|
||||
'billing_status' => ['sometimes', 'string', 'in:active,trial,suspended'],
|
||||
'settings' => ['sometimes', 'array'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Resources\Api\V1;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
final class CustomerResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'company_name' => $this->company_name,
|
||||
'type' => $this->type,
|
||||
'email' => $this->email,
|
||||
'phone' => $this->phone,
|
||||
'address' => $this->address,
|
||||
'city' => $this->city,
|
||||
'postal_code' => $this->postal_code,
|
||||
'country' => $this->country,
|
||||
'notes' => $this->notes,
|
||||
'is_portal_enabled' => $this->is_portal_enabled,
|
||||
'display_name' => $this->displayName(),
|
||||
'created_at' => $this->created_at->toIso8601String(),
|
||||
'updated_at' => $this->updated_at->toIso8601String(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Resources\Api\V1;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
|
||||
final class EventCollection extends ResourceCollection
|
||||
{
|
||||
public $collects = EventResource::class;
|
||||
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'data' => $this->collection,
|
||||
];
|
||||
}
|
||||
|
||||
public function with(Request $request): array
|
||||
{
|
||||
return [
|
||||
'success' => true,
|
||||
'meta' => [
|
||||
'pagination' => [
|
||||
'current_page' => $this->currentPage(),
|
||||
'per_page' => $this->perPage(),
|
||||
'total' => $this->total(),
|
||||
'last_page' => $this->lastPage(),
|
||||
'from' => $this->firstItem(),
|
||||
'to' => $this->lastItem(),
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Resources\Api\V1;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
final class EventInvitationResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'event_id' => $this->event_id,
|
||||
'user_id' => $this->user_id,
|
||||
'rsvp_status' => $this->rsvp_status->value,
|
||||
'rsvp_status_label' => $this->rsvp_status->label(),
|
||||
'rsvp_status_color' => $this->rsvp_status->color(),
|
||||
'rsvp_note' => $this->rsvp_note,
|
||||
'rsvp_responded_at' => $this->rsvp_responded_at?->toIso8601String(),
|
||||
'invited_at' => $this->invited_at?->toIso8601String(),
|
||||
'user' => new UserResource($this->whenLoaded('user')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,35 +13,16 @@ final class EventResource extends JsonResource
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'title' => $this->title,
|
||||
'description' => $this->description,
|
||||
'event_date' => $this->event_date->toDateString(),
|
||||
'start_time' => $this->start_time?->format('H:i'),
|
||||
'end_time' => $this->end_time?->format('H:i'),
|
||||
'load_in_time' => $this->load_in_time?->format('H:i'),
|
||||
'soundcheck_time' => $this->soundcheck_time?->format('H:i'),
|
||||
'fee' => $this->fee,
|
||||
'currency' => $this->currency,
|
||||
'status' => $this->status->value,
|
||||
'status_label' => $this->status->label(),
|
||||
'status_color' => $this->status->color(),
|
||||
'visibility' => $this->visibility->value,
|
||||
'visibility_label' => $this->visibility->label(),
|
||||
'rsvp_deadline' => $this->rsvp_deadline?->toIso8601String(),
|
||||
'notes' => $this->notes,
|
||||
'internal_notes' => $this->when(
|
||||
$request->user()?->role === 'admin' || $request->user()?->role === 'booking_agent',
|
||||
$this->internal_notes
|
||||
),
|
||||
'is_public_setlist' => $this->is_public_setlist,
|
||||
'location' => new LocationResource($this->whenLoaded('location')),
|
||||
'customer' => new CustomerResource($this->whenLoaded('customer')),
|
||||
'setlist' => new SetlistResource($this->whenLoaded('setlist')),
|
||||
'invitations' => EventInvitationResource::collection($this->whenLoaded('invitations')),
|
||||
'creator' => new UserResource($this->whenLoaded('creator')),
|
||||
'organisation_id' => $this->organisation_id,
|
||||
'name' => $this->name,
|
||||
'slug' => $this->slug,
|
||||
'start_date' => $this->start_date->toDateString(),
|
||||
'end_date' => $this->end_date->toDateString(),
|
||||
'timezone' => $this->timezone,
|
||||
'status' => $this->status,
|
||||
'created_at' => $this->created_at->toIso8601String(),
|
||||
'updated_at' => $this->updated_at->toIso8601String(),
|
||||
'organisation' => new OrganisationResource($this->whenLoaded('organisation')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Resources\Api\V1;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
final class LocationResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'address' => $this->address,
|
||||
'city' => $this->city,
|
||||
'postal_code' => $this->postal_code,
|
||||
'country' => $this->country,
|
||||
'latitude' => $this->latitude,
|
||||
'longitude' => $this->longitude,
|
||||
'capacity' => $this->capacity,
|
||||
'contact_name' => $this->contact_name,
|
||||
'contact_email' => $this->contact_email,
|
||||
'contact_phone' => $this->contact_phone,
|
||||
'notes' => $this->notes,
|
||||
'created_at' => $this->created_at->toIso8601String(),
|
||||
'updated_at' => $this->updated_at->toIso8601String(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Resources\Api\V1;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
final class MusicAttachmentResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'music_number_id' => $this->music_number_id,
|
||||
'file_name' => $this->file_name,
|
||||
'original_name' => $this->original_name,
|
||||
'file_type' => $this->file_type,
|
||||
'file_size' => $this->file_size,
|
||||
'mime_type' => $this->mime_type,
|
||||
'created_at' => $this->created_at->toIso8601String(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Resources\Api\V1;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
final class MusicNumberResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'title' => $this->title,
|
||||
'artist' => $this->artist,
|
||||
'genre' => $this->genre,
|
||||
'duration_seconds' => $this->duration_seconds,
|
||||
'key' => $this->key,
|
||||
'tempo_bpm' => $this->tempo_bpm,
|
||||
'time_signature' => $this->time_signature,
|
||||
'lyrics' => $this->lyrics,
|
||||
'notes' => $this->notes,
|
||||
'tags' => $this->tags,
|
||||
'play_count' => $this->play_count,
|
||||
'is_active' => $this->is_active,
|
||||
'attachments' => MusicAttachmentResource::collection($this->whenLoaded('attachments')),
|
||||
'created_at' => $this->created_at->toIso8601String(),
|
||||
'updated_at' => $this->updated_at->toIso8601String(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
24
api/app/Http/Resources/Api/V1/OrganisationResource.php
Normal file
24
api/app/Http/Resources/Api/V1/OrganisationResource.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Resources\Api\V1;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
final class OrganisationResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'slug' => $this->slug,
|
||||
'billing_status' => $this->billing_status,
|
||||
'settings' => $this->settings,
|
||||
'created_at' => $this->created_at->toIso8601String(),
|
||||
'updated_at' => $this->updated_at->toIso8601String(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Resources\Api\V1;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
final class SetlistItemResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'setlist_id' => $this->setlist_id,
|
||||
'music_number_id' => $this->music_number_id,
|
||||
'position' => $this->position,
|
||||
'set_number' => $this->set_number,
|
||||
'is_break' => $this->is_break,
|
||||
'break_duration_seconds' => $this->break_duration_seconds,
|
||||
'notes' => $this->notes,
|
||||
'music_number' => new MusicNumberResource($this->whenLoaded('musicNumber')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Resources\Api\V1;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
final class SetlistResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'description' => $this->description,
|
||||
'total_duration_seconds' => $this->total_duration_seconds,
|
||||
'formatted_duration' => $this->formattedDuration(),
|
||||
'is_template' => $this->is_template,
|
||||
'is_archived' => $this->is_archived,
|
||||
'items' => SetlistItemResource::collection($this->whenLoaded('items')),
|
||||
'creator' => new UserResource($this->whenLoaded('creator')),
|
||||
'created_at' => $this->created_at->toIso8601String(),
|
||||
'updated_at' => $this->updated_at->toIso8601String(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,17 +15,28 @@ final class UserResource extends JsonResource
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'email' => $this->email,
|
||||
'phone' => $this->phone,
|
||||
'bio' => $this->bio,
|
||||
'instruments' => $this->instruments,
|
||||
'avatar' => $this->avatar_path ? asset('storage/' . $this->avatar_path) : null,
|
||||
'type' => $this->type,
|
||||
'role' => $this->role,
|
||||
'status' => $this->status,
|
||||
'roles' => $this->getRoleNames()->values()->all(),
|
||||
'timezone' => $this->timezone,
|
||||
'locale' => $this->locale,
|
||||
'avatar' => $this->avatar,
|
||||
'email_verified_at' => $this->email_verified_at?->toIso8601String(),
|
||||
'created_at' => $this->created_at->toIso8601String(),
|
||||
'updated_at' => $this->updated_at->toIso8601String(),
|
||||
'organisations' => $this->whenLoaded('organisations', fn () =>
|
||||
$this->organisations->map(fn ($org) => [
|
||||
'id' => $org->id,
|
||||
'name' => $org->name,
|
||||
'slug' => $org->slug,
|
||||
'role' => $org->pivot->role,
|
||||
])
|
||||
),
|
||||
'event_roles' => $this->whenLoaded('events', fn () =>
|
||||
$this->events->map(fn ($event) => [
|
||||
'event_id' => $event->id,
|
||||
'event_name' => $event->name,
|
||||
'role' => $event->pivot->role,
|
||||
])
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user