Implement creator studio and upload updates
This commit is contained in:
200
app/Services/Studio/CreatorStudioChallengeService.php
Normal file
200
app/Services/Studio/CreatorStudioChallengeService.php
Normal file
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Studio;
|
||||
|
||||
use App\Models\NovaCard;
|
||||
use App\Models\NovaCardChallenge;
|
||||
use App\Models\NovaCardChallengeEntry;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
final class CreatorStudioChallengeService
|
||||
{
|
||||
public function build(User $user): array
|
||||
{
|
||||
$entryCounts = NovaCardChallengeEntry::query()
|
||||
->where('user_id', $user->id)
|
||||
->whereNotIn('status', [NovaCardChallengeEntry::STATUS_HIDDEN, NovaCardChallengeEntry::STATUS_REJECTED])
|
||||
->selectRaw('challenge_id, COUNT(*) as aggregate')
|
||||
->groupBy('challenge_id')
|
||||
->pluck('aggregate', 'challenge_id');
|
||||
|
||||
$recentEntriesQuery = NovaCardChallengeEntry::query()
|
||||
->with(['challenge', 'card'])
|
||||
->where('user_id', $user->id)
|
||||
->whereNotIn('status', [NovaCardChallengeEntry::STATUS_HIDDEN, NovaCardChallengeEntry::STATUS_REJECTED])
|
||||
->whereHas('challenge')
|
||||
->whereHas('card');
|
||||
|
||||
$recentEntries = $recentEntriesQuery
|
||||
->latest('created_at')
|
||||
->limit(8)
|
||||
->get()
|
||||
->map(fn (NovaCardChallengeEntry $entry): array => $this->mapEntry($entry))
|
||||
->values()
|
||||
->all();
|
||||
|
||||
$activeChallenges = NovaCardChallenge::query()
|
||||
->whereIn('status', [NovaCardChallenge::STATUS_ACTIVE, NovaCardChallenge::STATUS_COMPLETED])
|
||||
->orderByRaw('CASE WHEN featured = 1 THEN 0 ELSE 1 END')
|
||||
->orderByRaw("CASE WHEN status = 'active' THEN 0 ELSE 1 END")
|
||||
->orderBy('ends_at')
|
||||
->orderByDesc('starts_at')
|
||||
->limit(10)
|
||||
->get()
|
||||
->map(fn (NovaCardChallenge $challenge): array => $this->mapChallenge($challenge, $entryCounts))
|
||||
->values();
|
||||
|
||||
$spotlight = $activeChallenges->first();
|
||||
|
||||
$cardLeaders = NovaCard::query()
|
||||
->where('user_id', $user->id)
|
||||
->whereNull('deleted_at')
|
||||
->where('challenge_entries_count', '>', 0)
|
||||
->orderByDesc('challenge_entries_count')
|
||||
->orderByDesc('published_at')
|
||||
->limit(6)
|
||||
->get()
|
||||
->map(fn (NovaCard $card): array => [
|
||||
'id' => (int) $card->id,
|
||||
'title' => (string) $card->title,
|
||||
'status' => (string) $card->status,
|
||||
'challenge_entries_count' => (int) $card->challenge_entries_count,
|
||||
'views_count' => (int) $card->views_count,
|
||||
'comments_count' => (int) $card->comments_count,
|
||||
'preview_url' => route('studio.cards.preview', ['id' => $card->id]),
|
||||
'edit_url' => route('studio.cards.edit', ['id' => $card->id]),
|
||||
'analytics_url' => route('studio.cards.analytics', ['id' => $card->id]),
|
||||
])
|
||||
->values()
|
||||
->all();
|
||||
|
||||
$cardsAvailable = (int) NovaCard::query()
|
||||
->where('user_id', $user->id)
|
||||
->whereNull('deleted_at')
|
||||
->whereNotIn('status', [NovaCard::STATUS_HIDDEN, NovaCard::STATUS_REJECTED])
|
||||
->count();
|
||||
|
||||
$entryCount = (int) $entryCounts->sum();
|
||||
$featuredEntries = (int) (clone $recentEntriesQuery)
|
||||
->whereIn('status', [NovaCardChallengeEntry::STATUS_FEATURED, NovaCardChallengeEntry::STATUS_WINNER])
|
||||
->count();
|
||||
$winnerEntries = (int) (clone $recentEntriesQuery)
|
||||
->where('status', NovaCardChallengeEntry::STATUS_WINNER)
|
||||
->count();
|
||||
|
||||
return [
|
||||
'summary' => [
|
||||
'active_challenges' => (int) $activeChallenges->where('status', NovaCardChallenge::STATUS_ACTIVE)->count(),
|
||||
'joined_challenges' => (int) $entryCounts->keys()->count(),
|
||||
'entries_submitted' => $entryCount,
|
||||
'featured_entries' => $featuredEntries,
|
||||
'winner_entries' => $winnerEntries,
|
||||
'cards_available' => $cardsAvailable,
|
||||
],
|
||||
'spotlight' => $spotlight,
|
||||
'active_challenges' => $activeChallenges->all(),
|
||||
'recent_entries' => $recentEntries,
|
||||
'card_leaders' => $cardLeaders,
|
||||
'reminders' => $this->reminders($cardsAvailable, $entryCount, $activeChallenges, $featuredEntries, $winnerEntries),
|
||||
];
|
||||
}
|
||||
|
||||
private function mapChallenge(NovaCardChallenge $challenge, Collection $entryCounts): array
|
||||
{
|
||||
return [
|
||||
'id' => (int) $challenge->id,
|
||||
'slug' => (string) $challenge->slug,
|
||||
'title' => (string) $challenge->title,
|
||||
'description' => $challenge->description,
|
||||
'prompt' => $challenge->prompt,
|
||||
'status' => (string) $challenge->status,
|
||||
'official' => (bool) $challenge->official,
|
||||
'featured' => (bool) $challenge->featured,
|
||||
'entries_count' => (int) $challenge->entries_count,
|
||||
'starts_at' => $challenge->starts_at?->toIso8601String(),
|
||||
'ends_at' => $challenge->ends_at?->toIso8601String(),
|
||||
'is_joined' => $entryCounts->has($challenge->id),
|
||||
'submission_count' => (int) ($entryCounts->get($challenge->id) ?? 0),
|
||||
'url' => route('cards.challenges.show', ['slug' => $challenge->slug]),
|
||||
];
|
||||
}
|
||||
|
||||
private function mapEntry(NovaCardChallengeEntry $entry): array
|
||||
{
|
||||
return [
|
||||
'id' => (int) $entry->id,
|
||||
'status' => (string) $entry->status,
|
||||
'submitted_at' => $entry->created_at?->toIso8601String(),
|
||||
'note' => $entry->note,
|
||||
'challenge' => [
|
||||
'id' => (int) $entry->challenge_id,
|
||||
'title' => (string) ($entry->challenge?->title ?? 'Challenge'),
|
||||
'status' => (string) ($entry->challenge?->status ?? ''),
|
||||
'official' => (bool) ($entry->challenge?->official ?? false),
|
||||
'url' => $entry->challenge ? route('cards.challenges.show', ['slug' => $entry->challenge->slug]) : route('cards.challenges'),
|
||||
],
|
||||
'card' => [
|
||||
'id' => (int) $entry->card_id,
|
||||
'title' => (string) ($entry->card?->title ?? 'Card'),
|
||||
'preview_url' => $entry->card ? route('studio.cards.preview', ['id' => $entry->card->id]) : route('studio.cards.index'),
|
||||
'edit_url' => $entry->card ? route('studio.cards.edit', ['id' => $entry->card->id]) : route('studio.cards.create'),
|
||||
'analytics_url' => $entry->card ? route('studio.cards.analytics', ['id' => $entry->card->id]) : route('studio.cards.index'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function reminders(int $cardsAvailable, int $entryCount, Collection $activeChallenges, int $featuredEntries, int $winnerEntries): array
|
||||
{
|
||||
$items = [];
|
||||
|
||||
if ($cardsAvailable === 0) {
|
||||
$items[] = [
|
||||
'title' => 'Create a card to join challenges',
|
||||
'body' => 'Challenge participation starts from published or ready-to-share cards inside Studio.',
|
||||
'href' => route('studio.cards.create'),
|
||||
'cta' => 'Create card',
|
||||
];
|
||||
}
|
||||
|
||||
if ($cardsAvailable > 0 && $entryCount === 0 && $activeChallenges->where('status', NovaCardChallenge::STATUS_ACTIVE)->isNotEmpty()) {
|
||||
$items[] = [
|
||||
'title' => 'You have active challenge windows open',
|
||||
'body' => 'Submit an existing card to the current prompt lineup before the next window closes.',
|
||||
'href' => route('studio.cards.index'),
|
||||
'cta' => 'Open cards',
|
||||
];
|
||||
}
|
||||
|
||||
if ($featuredEntries > 0) {
|
||||
$items[] = [
|
||||
'title' => 'Featured challenge entries are live',
|
||||
'body' => 'Review promoted submissions and keep those cards ready for profile, editorial, or follow-up pushes.',
|
||||
'href' => route('studio.featured'),
|
||||
'cta' => 'Manage featured',
|
||||
];
|
||||
}
|
||||
|
||||
if ($winnerEntries > 0) {
|
||||
$items[] = [
|
||||
'title' => 'Winning challenge work deserves a spotlight',
|
||||
'body' => 'Use featured content and profile curation to extend the reach of cards that already placed well.',
|
||||
'href' => route('studio.profile'),
|
||||
'cta' => 'Open profile',
|
||||
];
|
||||
}
|
||||
|
||||
if ($activeChallenges->isNotEmpty()) {
|
||||
$items[] = [
|
||||
'title' => 'Public challenge archive stays one click away',
|
||||
'body' => 'Use the public challenge directory to review prompts, reference past winners, and see how new runs are framed.',
|
||||
'href' => route('cards.challenges'),
|
||||
'cta' => 'Browse challenges',
|
||||
];
|
||||
}
|
||||
|
||||
return collect($items)->take(4)->values()->all();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user