authorize('manageReleases', $group); return Inertia::render('Studio/StudioGroupReleases', [ 'title' => $group->name . ' Releases', 'description' => 'Manage release pipelines, contributors, and publication stages for major group drops.', 'studioGroup' => $this->groups->mapGroupDetail($group, $request->user()), 'listing' => $this->releases->studioListing($group, $request->only(['bucket', 'page', 'per_page'])), 'createUrl' => route('studio.groups.releases.create', ['group' => $group]), ]); } public function create(Request $request, Group $group): Response { $this->authorize('manageReleases', $group); return Inertia::render('Studio/StudioGroupReleaseEditor', [ 'title' => 'Create release', 'description' => 'Build a release page and move it from concept through publishing.', 'studioGroup' => $this->groups->mapGroupDetail($group, $request->user()), 'release' => null, 'statusOptions' => collect((array) config('groups.releases.statuses', []))->map(fn (string $value): array => ['value' => $value, 'label' => str_replace('_', ' ', ucfirst($value))])->values()->all(), 'stageOptions' => collect((array) config('groups.releases.stages', []))->map(fn (string $value): array => ['value' => $value, 'label' => ucfirst($value)])->values()->all(), 'visibilityOptions' => collect((array) config('groups.releases.visibility_options', []))->map(fn (string $value): array => ['value' => $value, 'label' => ucfirst($value)])->values()->all(), 'memberOptions' => $this->releases->memberOptions($group->loadMissing('owner.profile')), 'projectOptions' => $group->projects()->orderBy('title')->get(['id', 'title'])->map(fn ($project): array => ['id' => (int) $project->id, 'title' => $project->title])->values()->all(), 'collectionOptions' => $group->collections()->orderBy('title')->get(['id', 'title'])->map(fn ($collection): array => ['id' => (int) $collection->id, 'title' => $collection->title])->values()->all(), 'artworkOptions' => $group->artworks()->whereNull('deleted_at')->latest('updated_at')->limit(30)->get(['id', 'title'])->map(fn ($artwork): array => ['id' => (int) $artwork->id, 'title' => $artwork->title])->values()->all(), 'storeUrl' => route('studio.groups.releases.store', ['group' => $group]), ]); } public function store(StoreGroupReleaseRequest $request, Group $group): RedirectResponse { $this->authorize('manageReleases', $group); $release = $this->releases->create($group, $request->user(), $request->validated()); return redirect()->route('studio.groups.releases.show', ['group' => $group, 'release' => $release]) ->with('success', 'Release created.'); } public function show(Request $request, Group $group, GroupRelease $release): Response { $this->authorize('manageReleases', $group); abort_unless((int) $release->group_id === (int) $group->id, 404); return Inertia::render('Studio/StudioGroupReleaseEditor', [ 'title' => 'Edit release', 'description' => 'Update the release story, stage, contributors, and publish plan.', 'studioGroup' => $this->groups->mapGroupDetail($group, $request->user()), 'release' => $this->releases->detailPayload($release, $request->user()), 'statusOptions' => collect((array) config('groups.releases.statuses', []))->map(fn (string $value): array => ['value' => $value, 'label' => str_replace('_', ' ', ucfirst($value))])->values()->all(), 'stageOptions' => collect((array) config('groups.releases.stages', []))->map(fn (string $value): array => ['value' => $value, 'label' => ucfirst($value)])->values()->all(), 'visibilityOptions' => collect((array) config('groups.releases.visibility_options', []))->map(fn (string $value): array => ['value' => $value, 'label' => ucfirst($value)])->values()->all(), 'memberOptions' => $this->releases->memberOptions($group->loadMissing('owner.profile')), 'projectOptions' => $group->projects()->orderBy('title')->get(['id', 'title'])->map(fn ($project): array => ['id' => (int) $project->id, 'title' => $project->title])->values()->all(), 'collectionOptions' => $group->collections()->orderBy('title')->get(['id', 'title'])->map(fn ($collection): array => ['id' => (int) $collection->id, 'title' => $collection->title])->values()->all(), 'artworkOptions' => $group->artworks()->whereNull('deleted_at')->latest('updated_at')->limit(30)->get(['id', 'title'])->map(fn ($artwork): array => ['id' => (int) $artwork->id, 'title' => $artwork->title])->values()->all(), 'updateUrl' => route('studio.groups.releases.update', ['group' => $group, 'release' => $release]), 'stageUrl' => route('studio.groups.releases.stage', ['group' => $group, 'release' => $release]), 'publishUrl' => route('studio.groups.releases.publish', ['group' => $group, 'release' => $release]), 'attachArtworkUrl' => route('studio.groups.releases.attach-artwork', ['group' => $group, 'release' => $release]), 'attachContributorUrl' => route('studio.groups.releases.attach-contributor', ['group' => $group, 'release' => $release]), 'storeMilestoneUrl' => route('studio.groups.releases.milestones.store', ['group' => $group, 'release' => $release]), 'updateMilestonePattern' => route('studio.groups.releases.milestones.update', ['group' => $group, 'release' => $release, 'milestone' => '__MILESTONE__']), ]); } public function update(UpdateGroupReleaseRequest $request, Group $group, GroupRelease $release): RedirectResponse { $this->authorize('manageReleases', $group); abort_unless((int) $release->group_id === (int) $group->id, 404); $this->releases->update($release, $request->user(), $request->validated()); return back()->with('success', 'Release updated.'); } public function stage(UpdateGroupReleaseStageRequest $request, Group $group, GroupRelease $release): RedirectResponse { $this->authorize('moveReleaseStage', $group); abort_unless((int) $release->group_id === (int) $group->id, 404); $this->releases->updateStage($release, $request->user(), (string) $request->validated('current_stage')); return back()->with('success', 'Release stage updated.'); } public function publish(Request $request, Group $group, GroupRelease $release): RedirectResponse { $this->authorize('publishReleases', $group); abort_unless((int) $release->group_id === (int) $group->id, 404); $this->releases->publish($release, $request->user()); return back()->with('success', 'Release published.'); } public function attachArtwork(AttachArtworkToGroupReleaseRequest $request, Group $group, GroupRelease $release): RedirectResponse { $this->authorize('manageReleases', $group); abort_unless((int) $release->group_id === (int) $group->id, 404); $artwork = Artwork::query()->findOrFail((int) $request->validated('artwork_id')); $this->releases->attachArtwork($release, $artwork, $request->user()); return back()->with('success', 'Artwork attached to release.'); } public function attachContributor(AttachContributorToGroupReleaseRequest $request, Group $group, GroupRelease $release): RedirectResponse { $this->authorize('manageReleases', $group); abort_unless((int) $release->group_id === (int) $group->id, 404); $contributor = User::query()->findOrFail((int) $request->validated('user_id')); $this->releases->attachContributor($release, $contributor, $request->user(), $request->validated('role_label')); return back()->with('success', 'Contributor attached to release.'); } public function storeMilestone(StoreGroupMilestoneRequest $request, Group $group, GroupRelease $release): RedirectResponse { $this->authorize('manageMilestones', $group); abort_unless((int) $release->group_id === (int) $group->id, 404); $this->releases->createMilestone($release, $request->user(), $request->validated()); return back()->with('success', 'Release milestone created.'); } public function updateMilestone(UpdateGroupMilestoneRequest $request, Group $group, GroupRelease $release, GroupReleaseMilestone $milestone): RedirectResponse { $this->authorize('manageMilestones', $group); abort_unless((int) $release->group_id === (int) $group->id, 404); abort_unless((int) $milestone->group_release_id === (int) $release->id, 404); $this->releases->updateMilestone($milestone, $request->user(), $request->validated()); return back()->with('success', 'Release milestone updated.'); } }