Implement academy analytics, billing, and web stories updates

This commit is contained in:
2026-05-26 07:27:29 +02:00
parent 456c3d6bb0
commit 0b33a1b074
177 changed files with 27360 additions and 2685 deletions

View File

@@ -32,7 +32,7 @@ final class StudioNewsController extends Controller
return Inertia::render('Studio/StudioNewsIndex', [
'title' => 'Newsroom',
'description' => 'Plan announcements, publish editorial stories, and connect articles to the rest of Nova.',
'listing' => $this->news->studioListing($request->only(['q', 'status', 'type', 'category_id', 'per_page', 'page'])),
'listing' => $this->news->studioListing($request->only(['q', 'status', 'type', 'category_id', 'per_page', 'page', 'order', 'direction'])),
'statusOptions' => $this->news->editorialStatusOptions(),
'typeOptions' => $this->news->articleTypeOptions(),
'categoryOptions' => $this->news->categoryOptions(),
@@ -56,7 +56,7 @@ final class StudioNewsController extends Controller
'statusOptions' => $this->news->editorialStatusOptions(),
'categoryOptions' => $this->news->categoryOptions(),
'tagOptions' => $this->news->tagOptions(),
'newsTagLimit' => 12,
'newsTagLimit' => 30,
'relationTypeOptions' => $this->news->relationTypeOptions(),
'storeUrl' => route('studio.news.store'),
'coverUploadUrl' => route('api.studio.news.media.upload'),
@@ -92,7 +92,7 @@ final class StudioNewsController extends Controller
'statusOptions' => $this->news->editorialStatusOptions(),
'categoryOptions' => $this->news->categoryOptions(),
'tagOptions' => $this->news->tagOptions(),
'newsTagLimit' => 12,
'newsTagLimit' => 30,
'relationTypeOptions' => $this->news->relationTypeOptions(),
'coverUploadUrl' => route('api.studio.news.media.upload'),
'coverDeleteUrl' => route('api.studio.news.media.destroy'),
@@ -367,21 +367,11 @@ final class StudioNewsController extends Controller
'comments_enabled' => ['nullable', 'boolean'],
'tag_ids' => ['nullable', 'array'],
'tag_ids.*' => ['integer', 'exists:news_tags,id'],
'new_tag_names' => ['nullable', 'array', 'max:12'],
'new_tag_names' => ['nullable', 'array', 'max:30'],
'new_tag_names.*' => ['string', 'max:80'],
'meta_title' => ['nullable', 'string', 'max:255'],
'meta_description' => ['nullable', 'string', 'max:300'],
'meta_keywords' => ['nullable', 'string', 'max:255'],
'canonical_url' => ['nullable', 'string', 'max:2048', function (string $attribute, mixed $value, \Closure $fail): void {
if ($value === '' || $value === null) {
return;
}
$isAbsolute = filter_var($value, FILTER_VALIDATE_URL) !== false;
$isRelative = str_starts_with($value, '/');
if (! $isAbsolute && ! $isRelative) {
$fail('The canonical URL must be a valid URL or a relative path starting with /.');
}
}],
'og_title' => ['nullable', 'string', 'max:255'],
'og_description' => ['nullable', 'string', 'max:300'],
'og_image' => ['nullable', 'string', 'max:2048'],