optimizations

This commit is contained in:
2026-03-28 19:15:39 +01:00
parent 0b25d9570a
commit cab4fbd83e
509 changed files with 1016804 additions and 1605 deletions

View File

@@ -0,0 +1,309 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers\Settings;
use App\Http\Controllers\Controller;
use App\Http\Requests\Collections\CollectionProgramAssignmentRequest;
use App\Http\Requests\Collections\CollectionProgrammingCollectionRequest;
use App\Http\Requests\Collections\CollectionProgrammingMergePairRequest;
use App\Http\Requests\Collections\CollectionProgrammingMetadataRequest;
use App\Http\Requests\Collections\CollectionProgrammingPreviewRequest;
use App\Models\Collection;
use App\Models\CollectionProgramAssignment;
use App\Services\CollectionBackgroundJobService;
use App\Services\CollectionObservabilityService;
use App\Services\CollectionProgrammingService;
use App\Services\CollectionService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Inertia\Inertia;
use Inertia\Response;
class CollectionProgrammingController extends Controller
{
public function __construct(
private readonly CollectionProgrammingService $programming,
private readonly CollectionService $collections,
private readonly CollectionBackgroundJobService $backgroundJobs,
private readonly CollectionObservabilityService $observability,
) {
}
public function index(Request $request): Response|JsonResponse
{
$this->authorizeStaff($request);
$assignments = $this->programming->assignments();
if ($request->expectsJson()) {
return response()->json([
'ok' => true,
'assignments' => $assignments->map(fn (CollectionProgramAssignment $assignment): array => $this->mapAssignment($assignment))->values()->all(),
]);
}
$collectionOptions = Collection::query()
->with(['user:id,username,name', 'coverArtwork:id,user_id,title,slug,hash,thumb_ext,published_at,is_public,is_approved,deleted_at'])
->where(function ($query): void {
$query->where(function ($public): void {
$public->where('visibility', Collection::VISIBILITY_PUBLIC)
->whereIn('lifecycle_state', [Collection::LIFECYCLE_PUBLISHED, Collection::LIFECYCLE_FEATURED])
->where('moderation_status', Collection::MODERATION_ACTIVE);
})->orWhereNotNull('program_key');
})
->orderByDesc('ranking_score')
->orderByDesc('updated_at')
->limit(40)
->get();
$programKeyOptions = $assignments->pluck('program_key')
->merge($collectionOptions->pluck('program_key'))
->filter(fn ($value): bool => is_string($value) && $value !== '')
->unique()
->sort()
->values()
->all();
return Inertia::render('Collection/CollectionStaffProgramming', [
'assignments' => $assignments->map(fn (CollectionProgramAssignment $assignment): array => $this->mapAssignment($assignment))->values()->all(),
'collectionOptions' => $this->collections->mapCollectionCardPayloads($collectionOptions, true),
'programKeyOptions' => $programKeyOptions,
'mergeQueue' => $this->programming->mergeQueue(true),
'observabilitySummary' => $this->observability->summary(),
'historyPattern' => route('settings.collections.history', ['collection' => '__COLLECTION__']),
'viewer' => [
'isAdmin' => $this->isAdmin($request),
],
'endpoints' => [
'store' => route('staff.collections.programs.store'),
'updatePattern' => route('staff.collections.programs.update', ['program' => '__PROGRAM__']),
'publicProgramPattern' => route('collections.program.show', ['programKey' => '__PROGRAM__']),
'preview' => route('staff.collections.surfaces.preview'),
'refreshEligibility' => route('staff.collections.eligibility.refresh'),
'duplicateScan' => route('staff.collections.duplicate-scan'),
'refreshRecommendations' => route('staff.collections.recommendation-refresh'),
'metadataUpdate' => route('staff.collections.metadata.update'),
'canonicalizeCandidate' => route('staff.collections.merge-queue.canonicalize'),
'mergeCandidate' => route('staff.collections.merge-queue.merge'),
'rejectCandidate' => route('staff.collections.merge-queue.reject'),
'managePattern' => route('settings.collections.show', ['collection' => '__COLLECTION__']),
'surfaces' => route('settings.collections.surfaces.index'),
],
'seo' => [
'title' => 'Collection Programming — Skinbase Nova',
'description' => 'Staff programming tools for assignments, previews, eligibility diagnostics, and recommendation refreshes.',
'canonical' => route('staff.collections.programming'),
'robots' => 'noindex,follow',
],
])->rootView('collections');
}
public function storeProgram(CollectionProgramAssignmentRequest $request): JsonResponse
{
$this->authorizeStaff($request);
$assignment = $this->programming->upsertAssignment($request->validated(), $request->user());
return response()->json([
'ok' => true,
'assignment' => $this->mapAssignment($assignment),
]);
}
public function updateProgram(CollectionProgramAssignmentRequest $request, CollectionProgramAssignment $program): JsonResponse
{
$this->authorizeStaff($request);
$payload = $request->validated();
$payload['id'] = (int) $program->id;
$assignment = $this->programming->upsertAssignment($payload, $request->user());
return response()->json([
'ok' => true,
'assignment' => $this->mapAssignment($assignment),
]);
}
public function preview(CollectionProgrammingPreviewRequest $request): JsonResponse
{
$this->authorizeStaff($request);
$payload = $request->validated();
return response()->json([
'ok' => true,
'collections' => $this->collections->mapCollectionCardPayloads(
$this->programming->previewProgram((string) $payload['program_key'], (int) ($payload['limit'] ?? 12)),
true,
),
]);
}
public function refreshEligibility(CollectionProgrammingCollectionRequest $request): JsonResponse
{
$this->authorizeStaff($request);
$collection = $this->resolveCollection($request);
return response()->json([
'ok' => true,
'queued' => true,
'result' => $this->backgroundJobs->dispatchHealthRefresh($collection, $request->user()),
]);
}
public function duplicateScan(CollectionProgrammingCollectionRequest $request): JsonResponse
{
$this->authorizeStaff($request);
$collection = $this->resolveCollection($request);
return response()->json([
'ok' => true,
'queued' => true,
'result' => $this->backgroundJobs->dispatchDuplicateScan($collection, $request->user()),
]);
}
public function refreshRecommendations(CollectionProgrammingCollectionRequest $request): JsonResponse
{
$this->authorizeStaff($request);
$collection = $this->resolveCollection($request);
return response()->json([
'ok' => true,
'queued' => true,
'result' => $this->backgroundJobs->dispatchRecommendationRefresh($collection, $request->user()),
]);
}
public function updateMetadata(CollectionProgrammingMetadataRequest $request): JsonResponse
{
$this->authorizeStaff($request);
$payload = $request->validated();
$collection = Collection::query()->findOrFail((int) $payload['collection_id']);
$result = $this->programming->syncHooks($collection, $payload, $request->user());
return response()->json([
'ok' => true,
'message' => 'Experiment and program governance hooks updated.',
'collection' => $this->collections->mapCollectionCardPayloads([$result['collection']->loadMissing('user')], true)[0],
'diagnostics' => $result['diagnostics'],
]);
}
public function canonicalizeCandidate(CollectionProgrammingMergePairRequest $request): JsonResponse
{
$this->authorizeStaff($request);
[$source, $target] = $this->resolveMergePair($request);
$payload = $this->programming->canonicalizePair($source, $target, $request->user());
return response()->json([
'ok' => true,
'message' => 'Canonical target updated from the staff merge queue.',
'source' => $this->collections->mapCollectionCardPayloads([$payload['source']->loadMissing('user')], true)[0],
'target' => $this->collections->mapCollectionCardPayloads([$payload['target']->loadMissing('user')], true)[0],
'mergeQueue' => $payload['mergeQueue'],
]);
}
public function mergeCandidate(CollectionProgrammingMergePairRequest $request): JsonResponse
{
$this->authorizeStaff($request);
[$source, $target] = $this->resolveMergePair($request);
$payload = $this->programming->mergePair($source, $target, $request->user());
return response()->json([
'ok' => true,
'message' => 'Collections merged from the staff merge queue.',
'source' => $this->collections->mapCollectionCardPayloads([$payload['source']->loadMissing('user')], true)[0],
'target' => $this->collections->mapCollectionCardPayloads([$payload['target']->loadMissing('user')], true)[0],
'attached_artwork_ids' => $payload['attached_artwork_ids'],
'mergeQueue' => $payload['mergeQueue'],
]);
}
public function rejectCandidate(CollectionProgrammingMergePairRequest $request): JsonResponse
{
$this->authorizeStaff($request);
[$source, $target] = $this->resolveMergePair($request);
$payload = $this->programming->rejectPair($source, $target, $request->user());
return response()->json([
'ok' => true,
'message' => 'Duplicate candidate dismissed from the staff merge queue.',
'source' => $this->collections->mapCollectionCardPayloads([$payload['source']->loadMissing('user')], true)[0],
'target' => $this->collections->mapCollectionCardPayloads([$payload['target']->loadMissing('user')], true)[0],
'mergeQueue' => $payload['mergeQueue'],
]);
}
private function resolveCollection(CollectionProgrammingCollectionRequest $request): ?Collection
{
$payload = $request->validated();
if (! isset($payload['collection_id'])) {
return null;
}
return Collection::query()->find((int) $payload['collection_id']);
}
/**
* @return array{0: Collection, 1: Collection}
*/
private function resolveMergePair(CollectionProgrammingMergePairRequest $request): array
{
$payload = $request->validated();
return [
Collection::query()->findOrFail((int) $payload['source_collection_id']),
Collection::query()->findOrFail((int) $payload['target_collection_id']),
];
}
private function mapAssignment(CollectionProgramAssignment $assignment): array
{
$assignment->loadMissing(['collection.user', 'creator']);
return [
'id' => (int) $assignment->id,
'program_key' => (string) $assignment->program_key,
'campaign_key' => $assignment->campaign_key,
'placement_scope' => $assignment->placement_scope,
'starts_at' => optional($assignment->starts_at)?->toISOString(),
'ends_at' => optional($assignment->ends_at)?->toISOString(),
'priority' => (int) $assignment->priority,
'notes' => $assignment->notes,
'collection' => $this->collections->mapCollectionCardPayloads([$assignment->collection], true)[0],
'creator' => $assignment->creator ? [
'id' => (int) $assignment->creator->id,
'username' => (string) $assignment->creator->username,
'name' => $assignment->creator->name,
] : null,
];
}
private function authorizeStaff(Request $request): void
{
$user = $request->user();
abort_unless($user && ($user->isAdmin() || $user->isModerator()), 403);
}
private function isAdmin(Request $request): bool
{
$user = $request->user();
return $user !== null && $user->isAdmin();
}
}