optimizations
This commit is contained in:
193
app/Http/Controllers/Studio/StudioNovaCardsController.php
Normal file
193
app/Http/Controllers/Studio/StudioNovaCardsController.php
Normal file
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Studio;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\NovaCard;
|
||||
use App\Services\NovaCards\NovaCardPresenter;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class StudioNovaCardsController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly NovaCardPresenter $presenter,
|
||||
) {
|
||||
}
|
||||
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$cards = NovaCard::query()
|
||||
->with(['category', 'template', 'backgroundImage', 'tags', 'user.profile'])
|
||||
->where('user_id', $request->user()->id)
|
||||
->latest('updated_at')
|
||||
->paginate(18)
|
||||
->withQueryString();
|
||||
|
||||
$baseQuery = NovaCard::query()->where('user_id', $request->user()->id);
|
||||
|
||||
return Inertia::render('Studio/StudioCardsIndex', [
|
||||
'cards' => $this->presenter->paginator($cards, false, $request->user()),
|
||||
'stats' => [
|
||||
'all' => (clone $baseQuery)->count(),
|
||||
'drafts' => (clone $baseQuery)->where('status', NovaCard::STATUS_DRAFT)->count(),
|
||||
'processing' => (clone $baseQuery)->where('status', NovaCard::STATUS_PROCESSING)->count(),
|
||||
'published' => (clone $baseQuery)->where('status', NovaCard::STATUS_PUBLISHED)->count(),
|
||||
],
|
||||
'editorOptions' => $this->presenter->options(),
|
||||
'endpoints' => [
|
||||
'create' => route('studio.cards.create'),
|
||||
'editPattern' => route('studio.cards.edit', ['id' => '__CARD__']),
|
||||
'previewPattern' => route('studio.cards.preview', ['id' => '__CARD__']),
|
||||
'analyticsPattern' => route('studio.cards.analytics', ['id' => '__CARD__']),
|
||||
'draftStore' => route('api.cards.drafts.store'),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function create(Request $request): Response
|
||||
{
|
||||
$options = $this->presenter->optionsWithPresets($this->presenter->options(), $request->user());
|
||||
|
||||
return Inertia::render('Studio/StudioCardEditor', [
|
||||
'card' => null,
|
||||
'previewMode' => false,
|
||||
'mobileSteps' => $this->mobileSteps(),
|
||||
'editorOptions' => $options,
|
||||
'endpoints' => $this->editorEndpoints(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(Request $request, int $id): Response
|
||||
{
|
||||
$card = $this->ownedCard($request, $id);
|
||||
$options = $this->presenter->optionsWithPresets($this->presenter->options(), $request->user());
|
||||
|
||||
return Inertia::render('Studio/StudioCardEditor', [
|
||||
'card' => $this->presenter->card($card, true, $request->user()),
|
||||
'versions' => $this->versionPayloads($card),
|
||||
'previewMode' => false,
|
||||
'mobileSteps' => $this->mobileSteps(),
|
||||
'editorOptions' => $options,
|
||||
'endpoints' => $this->editorEndpoints(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function preview(Request $request, int $id): Response
|
||||
{
|
||||
$card = $this->ownedCard($request, $id);
|
||||
$options = $this->presenter->optionsWithPresets($this->presenter->options(), $request->user());
|
||||
|
||||
return Inertia::render('Studio/StudioCardEditor', [
|
||||
'card' => $this->presenter->card($card, true, $request->user()),
|
||||
'versions' => $this->versionPayloads($card),
|
||||
'previewMode' => true,
|
||||
'mobileSteps' => $this->mobileSteps(),
|
||||
'editorOptions' => $options,
|
||||
'endpoints' => $this->editorEndpoints(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function analytics(Request $request, int $id): Response
|
||||
{
|
||||
$card = $this->ownedCard($request, $id);
|
||||
|
||||
return Inertia::render('Studio/StudioCardAnalytics', [
|
||||
'card' => $this->presenter->card($card, false, $request->user()),
|
||||
'analytics' => [
|
||||
'views' => (int) $card->views_count,
|
||||
'likes' => (int) $card->likes_count,
|
||||
'favorites' => (int) $card->favorites_count,
|
||||
'saves' => (int) $card->saves_count,
|
||||
'remixes' => (int) $card->remixes_count,
|
||||
'comments' => (int) $card->comments_count,
|
||||
'challenge_entries' => (int) $card->challenge_entries_count,
|
||||
'shares' => (int) $card->shares_count,
|
||||
'downloads' => (int) $card->downloads_count,
|
||||
'trending_score' => (float) $card->trending_score,
|
||||
'last_engaged_at' => optional($card->last_engaged_at)?->toDayDateTimeString(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function remix(Request $request, int $id): RedirectResponse
|
||||
{
|
||||
$source = NovaCard::query()->published()->findOrFail($id);
|
||||
abort_unless($source->canBeViewedBy($request->user()), 404);
|
||||
|
||||
$card = app(\App\Services\NovaCards\NovaCardDraftService::class)->createRemix($request->user(), $source->loadMissing('tags'));
|
||||
|
||||
return redirect()->route('studio.cards.edit', ['id' => $card->id]);
|
||||
}
|
||||
|
||||
private function mobileSteps(): array
|
||||
{
|
||||
return [
|
||||
['key' => 'format', 'label' => 'Format', 'description' => 'Choose the canvas shape and basic direction.'],
|
||||
['key' => 'background', 'label' => 'Template & Background', 'description' => 'Pick the visual foundation for the card.'],
|
||||
['key' => 'content', 'label' => 'Text', 'description' => 'Write the quote, author, and source.'],
|
||||
['key' => 'style', 'label' => 'Style', 'description' => 'Fine-tune typography and layout.'],
|
||||
['key' => 'preview', 'label' => 'Preview', 'description' => 'Check the live composition before publish.'],
|
||||
['key' => 'publish', 'label' => 'Publish', 'description' => 'Review metadata and release settings.'],
|
||||
];
|
||||
}
|
||||
|
||||
private function editorEndpoints(): array
|
||||
{
|
||||
return [
|
||||
'draftStore' => route('api.cards.drafts.store'),
|
||||
'draftShowPattern' => route('api.cards.drafts.show', ['id' => '__CARD__']),
|
||||
'draftUpdatePattern' => route('api.cards.drafts.update', ['id' => '__CARD__']),
|
||||
'draftAutosavePattern' => route('api.cards.drafts.autosave', ['id' => '__CARD__']),
|
||||
'draftBackgroundPattern' => route('api.cards.drafts.background', ['id' => '__CARD__']),
|
||||
'draftRenderPattern' => route('api.cards.drafts.render', ['id' => '__CARD__']),
|
||||
'draftPublishPattern' => route('api.cards.drafts.publish', ['id' => '__CARD__']),
|
||||
'draftDeletePattern' => route('api.cards.drafts.destroy', ['id' => '__CARD__']),
|
||||
'draftVersionsPattern' => route('api.cards.drafts.versions', ['id' => '__CARD__']),
|
||||
'draftRestorePattern' => route('api.cards.drafts.restore', ['id' => '__CARD__', 'versionId' => '__VERSION__']),
|
||||
'remixPattern' => route('api.cards.remix', ['id' => '__CARD__']),
|
||||
'duplicatePattern' => route('api.cards.duplicate', ['id' => '__CARD__']),
|
||||
'collectionsIndex' => route('api.cards.collections.index'),
|
||||
'collectionsStore' => route('api.cards.collections.store'),
|
||||
'savePattern' => route('api.cards.save', ['id' => '__CARD__']),
|
||||
'likePattern' => route('api.cards.like', ['id' => '__CARD__']),
|
||||
'favoritePattern' => route('api.cards.favorite', ['id' => '__CARD__']),
|
||||
'challengeSubmitPattern' => route('api.cards.challenges.submit', ['challengeId' => '__CHALLENGE__', 'id' => '__CARD__']),
|
||||
'studioCards' => route('studio.cards.index'),
|
||||
'studioAnalyticsPattern' => route('studio.cards.analytics', ['id' => '__CARD__']),
|
||||
// v3 endpoints
|
||||
'presetsIndex' => route('api.cards.presets.index'),
|
||||
'presetsStore' => route('api.cards.presets.store'),
|
||||
'presetUpdatePattern' => route('api.cards.presets.update', ['id' => '__PRESET__']),
|
||||
'presetDestroyPattern' => route('api.cards.presets.destroy', ['id' => '__PRESET__']),
|
||||
'presetApplyPattern' => route('api.cards.presets.apply', ['presetId' => '__PRESET__', 'cardId' => '__CARD__']),
|
||||
'capturePresetPattern' => route('api.cards.presets.capture', ['cardId' => '__CARD__']),
|
||||
'aiSuggestPattern' => route('api.cards.ai-suggest', ['id' => '__CARD__']),
|
||||
'exportPattern' => route('api.cards.export.store', ['id' => '__CARD__']),
|
||||
'exportStatusPattern' => route('api.cards.exports.show', ['exportId' => '__EXPORT__']),
|
||||
];
|
||||
}
|
||||
|
||||
private function versionPayloads(NovaCard $card): array
|
||||
{
|
||||
return $card->versions()->latest('version_number')->get()->map(fn ($version): array => [
|
||||
'id' => (int) $version->id,
|
||||
'version_number' => (int) $version->version_number,
|
||||
'label' => $version->label,
|
||||
'created_at' => $version->created_at?->toISOString(),
|
||||
'snapshot_json' => is_array($version->snapshot_json) ? $version->snapshot_json : [],
|
||||
])->values()->all();
|
||||
}
|
||||
|
||||
private function ownedCard(Request $request, int $id): NovaCard
|
||||
{
|
||||
return NovaCard::query()
|
||||
->with(['user.profile', 'category', 'template', 'backgroundImage', 'tags', 'versions'])
|
||||
->where('user_id', $request->user()->id)
|
||||
->findOrFail($id);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user