name('index'); Route::get('/home', [HomeController::class, 'index']); Route::get('/robots.txt', RobotsTxtController::class)->name('robots.txt'); Route::get('/sitemap.xml', [SitemapController::class, 'index'])->name('sitemap.index'); Route::get('/sitemaps/{name}.xml', [SitemapController::class, 'show']) ->where('name', '[A-Za-z0-9\-]+') ->name('sitemap.show'); // ── PUBLIC GALLERIES / LISTS ───────────────────────────────────────────────── Route::get('/featured', [FeaturedArtworksController::class, 'index'])->name('featured'); Route::get('/uploads/latest', [LatestController::class, 'index'])->name('uploads.latest'); Route::get('/uploads/daily', [DailyUploadsController::class, 'index'])->name('uploads.daily'); Route::get('/members/photos', [MembersController::class, 'photos'])->name('members.photos'); Route::get('/downloads/today', [TodayDownloadsController::class, 'index'])->name('downloads.today'); Route::get('/comments/monthly', [MonthlyCommentatorsController::class, 'index'])->name('comments.monthly'); // ── DISCOVER (/discover/*) ──────────────────────────────────────────────────── Route::prefix('discover')->name('discover.')->group(function () { Route::get('/', fn () => redirect('/discover/trending', 301)); Route::get('/trending', [DiscoverController::class, 'trending'])->name('trending'); Route::get('/rising', [DiscoverController::class, 'rising'])->name('rising'); Route::get('/fresh', [DiscoverController::class, 'fresh'])->name('fresh'); Route::get('/top-rated', [DiscoverController::class, 'topRated'])->name('top-rated'); Route::get('/most-downloaded', [DiscoverController::class, 'mostDownloaded'])->name('most-downloaded'); Route::get('/on-this-day', [DiscoverController::class, 'onThisDay'])->name('on-this-day'); Route::middleware('auth')->get('/following', [DiscoverController::class, 'following'])->name('following'); Route::middleware('auth')->get('/for-you', [DiscoverController::class, 'forYou'])->name('for-you'); }); // ── EXPLORE (/explore/*) ────────────────────────────────────────────────────── Route::prefix('explore')->name('explore.')->group(function () { Route::get('/', [ExploreController::class, 'index'])->name('index'); Route::get('/members', fn () => redirect()->route('creators.top', request()->query(), 301))->name('members.redirect'); Route::get('/memebers', fn () => redirect()->route('creators.top', request()->query(), 301))->name('memebers.redirect'); Route::get('/{type}', [ExploreController::class, 'byType']) ->where('type', '[a-z0-9][a-z0-9\-]*') ->name('type'); Route::get('/{type}/{mode}', [ExploreController::class, 'byTypeMode']) ->where('type', '[a-z0-9][a-z0-9\-]*') ->where('mode', 'trending|new-hot|best|latest') ->name('type.mode'); }); // ── BLOG (/blog/*) ──────────────────────────────────────────────────────────── Route::prefix('blog')->name('blog.')->group(function () { Route::get('/', [BlogController::class, 'index'])->name('index'); Route::get('/{slug}', [BlogController::class, 'show'])->where('slug', '[a-z0-9\-]+')->name('show'); }); // ── PAGES (DB-driven static pages) ─────────────────────────────────────────── Route::get('/pages/{slug}', [PageController::class, 'show']) ->where('slug', '[a-z0-9\-]+') ->name('pages.show'); Route::get('/about', [PageController::class, 'marketing'])->defaults('slug', 'about')->name('about'); Route::get('/help', \App\Http\Controllers\Web\HelpCenterPageController::class)->name('help'); Route::get('/help/studio', \App\Http\Controllers\Web\StudioHelpPageController::class)->name('help.studio'); Route::get('/help/upload', \App\Http\Controllers\Web\UploadHelpPageController::class)->name('help.upload'); Route::get('/help/cards', \App\Http\Controllers\Web\CardsHelpPageController::class)->name('help.cards'); Route::get('/help/worlds', \App\Http\Controllers\Web\WorldsHelpPageController::class)->name('help.worlds'); Route::get('/help/profile', \App\Http\Controllers\Web\ProfileHelpPageController::class)->name('help.profile'); Route::get('/help/auth', \App\Http\Controllers\Web\AuthHelpPageController::class)->name('help.auth'); Route::get('/help/account', \App\Http\Controllers\Web\AccountHelpPageController::class)->name('help.account'); Route::get('/help/troubleshooting', \App\Http\Controllers\Web\TroubleshootingHelpPageController::class)->name('help.troubleshooting'); Route::get('/help/groups', \App\Http\Controllers\Web\GroupHelpPageController::class)->name('help.groups'); Route::get('/help/groups/quickstart', \App\Http\Controllers\Web\GroupQuickstartPageController::class)->name('help.groups.quickstart'); Route::get('/help/groups/faq', \App\Http\Controllers\Web\GroupFaqPageController::class)->name('help.groups.faq'); Route::get('/contact', [PageController::class, 'marketing'])->defaults('slug', 'contact')->name('contact'); Route::get('/legal/{section}', [PageController::class, 'legal']) ->where('section', 'terms|privacy|cookies') ->name('legal'); // ── FOOTER ──────────────────────────────────────────────────────────────────── Route::get('/rss-feeds', [RssFeedController::class, 'index'])->name('rss-feeds'); Route::get('/faq', [FooterController::class, 'faq'])->name('faq'); Route::get('/rules-and-guidelines', [FooterController::class, 'rules'])->name('rules'); Route::get('/privacy-policy', [FooterController::class, 'privacyPolicy'])->name('privacy-policy'); Route::get('/terms-of-service', [FooterController::class, 'termsOfService'])->name('terms-of-service'); Route::get('/staff', [StaffController::class, 'index'])->name('staff'); Route::get('/bug-report', [BugReportController::class, 'show'])->name('bug-report'); Route::post('/bug-report', [BugReportController::class, 'submit'])->middleware('auth')->name('bug-report.submit'); Route::get('/contact', [ApplicationController::class, 'show'])->name('contact.show'); Route::post('/contact', [ApplicationController::class, 'submit'])->middleware('throttle:6,1')->name('contact.submit'); $cpPrefix = trim((string) config('cpad.webroot', config('cp.webroot', 'cp')), '/'); // ── LEGACY RSS (.xml feeds — old site compatibility) ────────────────────────── Route::get('/rss/latest-uploads.xml', [RssFeedController::class, 'latestUploads'])->name('rss.uploads'); Route::get('/rss/latest-skins.xml', [RssFeedController::class, 'latestSkins'])->name('rss.skins'); Route::get('/rss/latest-wallpapers.xml', [RssFeedController::class, 'latestWallpapers'])->name('rss.wallpapers'); Route::get('/rss/latest-photos.xml', [RssFeedController::class, 'latestPhotos'])->name('rss.photos'); // ── RSS 2.0 feeds (/rss/*) ──────────────────────────────────────────────────── Route::middleware('throttle:60,1')->group(function () { Route::get('/rss', GlobalFeedController::class)->name('rss.global'); Route::prefix('rss/discover')->name('rss.discover.')->group(function () { Route::get('/', [DiscoverFeedController::class, 'index'])->name('index'); Route::get('/trending', [DiscoverFeedController::class, 'trending'])->name('trending'); Route::get('/fresh', [DiscoverFeedController::class, 'fresh'])->name('fresh'); Route::get('/rising', [DiscoverFeedController::class, 'rising'])->name('rising'); }); Route::prefix('rss/explore')->name('rss.explore.')->group(function () { Route::get('/{type}', [ExploreFeedController::class, 'byType']) ->where('type', '[a-z0-9][a-z0-9\-]*') ->name('type'); Route::get('/{type}/{mode}', [ExploreFeedController::class, 'byTypeMode']) ->where('type', '[a-z0-9][a-z0-9\-]*') ->where('mode', 'trending|latest|best') ->name('type.mode'); }); Route::get('/rss/tag/{slug}', TagFeedController::class) ->where('slug', '[a-z0-9\-]+') ->name('rss.tag'); Route::get('/rss/creator/{username}', CreatorFeedController::class) ->where('username', '[A-Za-z0-9_\-]{3,20}') ->name('rss.creator'); Route::get('/rss/blog', BlogFeedController::class)->name('rss.blog'); }); // ── CREATORS (/creators/*) ──────────────────────────────────────────────────── Route::prefix('creators')->name('creators.')->group(function () { Route::get('/top', [\App\Http\Controllers\User\TopAuthorsController::class, 'index'])->name('top'); Route::get('/rising', [DiscoverController::class, 'risingCreators'])->name('rising'); }); Route::get('/leaderboard', \App\Http\Controllers\Web\LeaderboardPageController::class) ->name('leaderboard'); // ── STORIES (/stories/*) ────────────────────────────────────────────────────── Route::prefix('stories')->name('stories.')->group(function () { Route::get('/', [StoryController::class, 'index'])->name('index'); Route::get('/tag/{tag}', [StoryController::class, 'tag']) ->where('tag', '[a-z0-9\-]+') ->name('tag'); Route::get('/creator/{username}', [StoryController::class, 'creator']) ->where('username', '[A-Za-z0-9_\-]{1,50}') ->name('creator'); Route::get('/author/{username}', fn (string $username) => redirect()->route('stories.creator', ['username' => $username], 301)) ->where('username', '[A-Za-z0-9_\-]{1,50}') ->name('author'); Route::get('/category/{category}', [StoryController::class, 'category']) ->where('category', 'creator_story|tutorial|interview|project_breakdown|announcement|resource') ->name('category'); Route::get('/{slug}', [StoryController::class, 'show']) ->where('slug', '[a-z0-9\-]+') ->name('show'); }); Route::middleware(['auth', 'ensure.onboarding.complete', 'creator.access'])->prefix('creator/stories')->name('creator.stories.')->group(function () { Route::get('/', [StoryController::class, 'dashboard'])->name('index'); Route::get('/create', [StoryController::class, 'create'])->name('create'); Route::post('/', [StoryController::class, 'store'])->name('store'); Route::get('/artworks/search', [StoryController::class, 'searchArtworks'])->name('artworks.search'); Route::post('/upload-image', [StoryController::class, 'uploadImage'])->name('upload-image'); Route::get('/{story}/edit', [StoryController::class, 'edit'])->name('edit'); Route::put('/{story}', [StoryController::class, 'update'])->name('update'); Route::delete('/{story}', [StoryController::class, 'destroy'])->name('destroy'); Route::post('/{story}/autosave', [StoryController::class, 'autosave'])->name('autosave'); Route::post('/{story}/submit-review', [StoryController::class, 'submitForReview'])->name('submit-review'); Route::post('/{story}/publish', [StoryController::class, 'publishNow'])->name('publish-now'); Route::get('/{story}/preview', [StoryController::class, 'preview'])->name('preview'); Route::get('/{story}/analytics', [StoryController::class, 'analytics'])->name('analytics'); }); // ── TAGS ────────────────────────────────────────────────────────────────────── Route::get('/tags', [\App\Http\Controllers\Web\TagController::class, 'index'])->name('tags.index'); Route::get('/tag/{tag:slug}', [\App\Http\Controllers\Web\TagController::class, 'show']) ->where('tag', '[a-z0-9\-]+') ->name('tags.show'); Route::get('/tags/{tag}', [\App\Http\Controllers\Web\Posts\HashtagFeedController::class, 'index']) ->where('tag', '[A-Za-z][A-Za-z0-9_]{1,63}') ->name('feed.hashtag'); // ── CATEGORIES DIRECTORY ───────────────────────────────────────────────────── Route::get('/categories', [CategoryController::class, 'index'])->name('categories.index'); // ── FOLLOWING (shortcut) ────────────────────────────────────────────────────── Route::middleware('auth')->get('/following', function () { return redirect()->route('dashboard.following'); })->name('following.redirect'); // ── ART / ARTWORKS ──────────────────────────────────────────────────────────── Route::get('/art/{id}/similar', \App\Http\Controllers\Web\SimilarArtworksPageController::class) ->whereNumber('id') ->name('art.similar'); Route::get('/art/{id}/similar-results', [\App\Http\Controllers\Web\SimilarArtworksPageController::class, 'results']) ->whereNumber('id') ->name('art.similar.results'); Route::get('/art/{id}/{slug?}', [ArtworkPageController::class, 'show']) ->where('id', '\d+') ->name('art.show'); Route::get('/download/artwork/{id}', ArtworkDownloadController::class) ->whereNumber('id') ->middleware('throttle:downloads') ->name('art.download'); // ── NEWS (/news/*) ──────────────────────────────────────────────────────────── Route::prefix('news')->name('news.')->group(function () { Route::get('/', [FrontendNewsController::class, 'index'])->name('index'); Route::get('archive/{year}/{month}', [FrontendNewsController::class, 'archive'])->whereNumber('year')->whereNumber('month')->name('archive'); Route::get('author/{username}', [FrontendNewsController::class, 'author']) ->where('username', '[A-Za-z0-9_-]{3,20}') ->name('author'); Route::get('category/{slug}', [FrontendNewsController::class, 'category'])->name('category'); Route::get('tag/{slug}', [FrontendNewsController::class, 'tag'])->name('tag'); Route::get('{slug}', [FrontendNewsController::class, 'show']) ->where('slug', '[a-z0-9\-]+') ->name('show'); }); Route::get('/rss/news', [NewsRssController::class, 'feed'])->name('news.rss'); Route::get('/collections/featured', [\App\Http\Controllers\Web\CollectionDiscoveryController::class, 'featured']) ->name('collections.featured'); Route::get('/collections/trending', [\App\Http\Controllers\Web\CollectionDiscoveryController::class, 'trending']) ->name('collections.trending'); Route::get('/worlds', [WorldController::class, 'index'])->name('worlds.index'); Route::get('/worlds/create', function (Request $request) { $user = $request->user(); if (! $user) { return redirect()->guest(route('login')); } if ($user->can('create', World::class)) { return redirect()->route('studio.worlds.create', $request->query(), 302); } return redirect()->route('worlds.index', $request->query(), 302); }) ->name('worlds.create.redirect'); Route::get('/worlds/{world:slug}', [WorldController::class, 'show']) ->where('world', '^(?!create$)[a-z0-9]+(?:-[a-z0-9]+)*$') ->name('worlds.show'); Route::get('/collections/editorial', [\App\Http\Controllers\Web\CollectionDiscoveryController::class, 'editorial']) ->name('collections.editorial'); Route::get('/collections/community', [\App\Http\Controllers\Web\CollectionDiscoveryController::class, 'community']) ->name('collections.community'); Route::get('/collections/seasonal', [\App\Http\Controllers\Web\CollectionDiscoveryController::class, 'seasonal']) ->name('collections.seasonal'); Route::get('/collections/campaigns/{campaignKey}', [\App\Http\Controllers\Web\CollectionDiscoveryController::class, 'campaign']) ->where('campaignKey', '[A-Za-z0-9_-]{1,80}') ->name('collections.campaign.show'); Route::get('/collections/program/{programKey}', [\App\Http\Controllers\Web\CollectionDiscoveryController::class, 'program']) ->where('programKey', '[A-Za-z0-9_-]{1,80}') ->name('collections.program.show'); Route::get('/collections/recommended', [\App\Http\Controllers\Web\CollectionDiscoveryController::class, 'recommended']) ->name('collections.recommended'); Route::get('/collections/search', [\App\Http\Controllers\Web\CollectionDiscoveryController::class, 'search']) ->name('collections.search'); Route::get('/groups', [\App\Http\Controllers\GroupController::class, 'index'])->name('groups.index'); Route::get('/groups/{group}', [\App\Http\Controllers\GroupController::class, 'show'])->name('groups.show'); Route::get('/groups/{group}/posts', [\App\Http\Controllers\GroupController::class, 'posts'])->name('groups.posts.index'); Route::get('/groups/{group}/projects', [\App\Http\Controllers\GroupController::class, 'projects'])->name('groups.projects.index'); Route::get('/groups/{group}/projects/{project}', [\App\Http\Controllers\GroupProjectController::class, 'show'])->name('groups.projects.show'); Route::get('/groups/{group}/releases', [\App\Http\Controllers\GroupController::class, 'releases'])->name('groups.releases.index'); Route::get('/groups/{group}/releases/{release}', [\App\Http\Controllers\GroupReleaseController::class, 'show'])->name('groups.releases.show'); Route::get('/groups/{group}/challenges', [\App\Http\Controllers\GroupController::class, 'challenges'])->name('groups.challenges.index'); Route::get('/groups/{group}/challenges/{challenge}', [\App\Http\Controllers\GroupChallengeController::class, 'show'])->name('groups.challenges.show'); Route::get('/groups/{group}/events', [\App\Http\Controllers\GroupController::class, 'events'])->name('groups.events.index'); Route::get('/groups/{group}/events/{event}', [\App\Http\Controllers\GroupEventController::class, 'show'])->name('groups.events.show'); Route::get('/groups/{group}/activity', [\App\Http\Controllers\GroupController::class, 'activity'])->name('groups.activity.index'); Route::get('/groups/{group}/assets/{asset}/download', [\App\Http\Controllers\GroupAssetController::class, 'download'])->name('groups.assets.download'); Route::get('/groups/{group}/{section}', [\App\Http\Controllers\GroupController::class, 'show']) ->where('section', 'overview|artworks|collections|members|about|posts|projects|releases|challenges|events|activity') ->name('groups.section'); Route::get('/groups/{group}/posts/{post}', [\App\Http\Controllers\GroupPostController::class, 'show']) ->name('groups.posts.show'); Route::middleware('auth')->group(function () { Route::post('/me/saved/collections/lists', [CollectionSavedLibraryController::class, 'storeList']) ->name('me.saved.collections.lists.store'); Route::get('/me/saved/collections/lists/{listSlug}', [\App\Http\Controllers\User\SavedCollectionController::class, 'showList']) ->name('me.saved.collections.lists.show'); Route::post('/me/saved/collections/{collection}/lists', [CollectionSavedLibraryController::class, 'storeItem']) ->name('me.saved.collections.lists.items.store'); Route::patch('/me/saved/collections/{collection}/note', [CollectionSavedLibraryController::class, 'updateNote']) ->name('me.saved.collections.notes.update'); Route::post('/me/saved/collections/lists/{list}/items/reorder', [CollectionSavedLibraryController::class, 'reorderItems']) ->name('me.saved.collections.lists.items.reorder'); Route::delete('/me/saved/collections/lists/{list}/items/{collection}', [CollectionSavedLibraryController::class, 'destroyItem']) ->name('me.saved.collections.lists.items.destroy'); Route::post('/collections/{collection}/follow', [\App\Http\Controllers\CollectionEngagementController::class, 'follow'])->name('collections.follow'); Route::delete('/collections/{collection}/follow', [\App\Http\Controllers\CollectionEngagementController::class, 'unfollow'])->name('collections.unfollow'); Route::post('/collections/{collection}/like', [\App\Http\Controllers\CollectionEngagementController::class, 'like'])->name('collections.like'); Route::delete('/collections/{collection}/like', [\App\Http\Controllers\CollectionEngagementController::class, 'unlike'])->name('collections.unlike'); Route::post('/collections/{collection}/save', [\App\Http\Controllers\CollectionEngagementController::class, 'save'])->name('collections.save'); Route::delete('/collections/{collection}/save', [\App\Http\Controllers\CollectionEngagementController::class, 'unsave'])->name('collections.unsave'); Route::post('/collections/{collection}/submissions', [\App\Http\Controllers\CollectionSubmissionController::class, 'store'])->name('collections.submissions.store'); Route::delete('/collections/submissions/{submission}', [\App\Http\Controllers\CollectionSubmissionController::class, 'destroy'])->name('collections.submissions.destroy'); Route::post('/collections/submissions/{submission}/approve', [\App\Http\Controllers\CollectionSubmissionController::class, 'approve'])->name('collections.submissions.approve'); Route::post('/collections/submissions/{submission}/reject', [\App\Http\Controllers\CollectionSubmissionController::class, 'reject'])->name('collections.submissions.reject'); Route::post('/collections/{collection}/comments', [\App\Http\Controllers\CollectionCommentController::class, 'store'])->name('collections.comments.store'); Route::delete('/collections/{collection}/comments/{comment}', [\App\Http\Controllers\CollectionCommentController::class, 'destroy'])->name('collections.comments.destroy'); Route::post('/groups/{group}/follow', [\App\Http\Controllers\GroupEngagementController::class, 'follow'])->name('groups.follow'); Route::delete('/groups/{group}/follow', [\App\Http\Controllers\GroupEngagementController::class, 'unfollow'])->name('groups.unfollow'); Route::post('/groups/{group}/join-requests', [\App\Http\Controllers\GroupJoinRequestController::class, 'store'])->name('groups.join-requests.store'); Route::delete('/groups/{group}/join-requests/{joinRequest}', [\App\Http\Controllers\GroupJoinRequestController::class, 'destroy'])->name('groups.join-requests.destroy'); Route::post('/groups/{group}/challenges/{challenge}/entries', [\App\Http\Controllers\GroupChallengeController::class, 'attachArtwork'])->name('groups.challenges.entries.store'); }); Route::post('/collections/{collection}/share', [\App\Http\Controllers\CollectionEngagementController::class, 'share']) ->name('collections.share'); Route::get('/collections/{collection}/comments', [\App\Http\Controllers\CollectionCommentController::class, 'index']) ->name('collections.comments.index'); Route::get('/collections/series/{seriesKey}', [ProfileCollectionController::class, 'showSeries']) ->where('seriesKey', '[A-Za-z0-9_-]{1,80}') ->name('collections.series.show'); // ── PROFILES (@username) ────────────────────────────────────────────────────── Route::get('/@{username}/gallery', [ProfileController::class, 'showGalleryByUsername']) ->where('username', '[A-Za-z0-9_-]{3,20}') ->name('profile.gallery'); Route::get('/@{username}/collections/{slug}', [ProfileCollectionController::class, 'show']) ->where('username', '[A-Za-z0-9_-]{3,20}') ->where('slug', '[a-z0-9\-]{2,140}') ->name('profile.collections.show'); Route::get('/@{username}/{tab}', [ProfileController::class, 'showTabByUsername']) ->where('username', '[A-Za-z0-9_-]{3,20}') ->where('tab', 'posts|artworks|stories|achievements|collections|about|stats|favourites|activity') ->name('profile.tab'); Route::get('/@{username}', [ProfileController::class, 'showByUsername']) ->where('username', '[A-Za-z0-9_-]{3,20}') ->name('profile.show'); Route::middleware('auth')->post('/@{username}/follow', [ProfileController::class, 'toggleFollow']) ->where('username', '[A-Za-z0-9_-]{3,20}') ->name('profile.follow'); Route::middleware('auth')->post('/@{username}/comment', [ProfileController::class, 'storeComment']) ->where('username', '[A-Za-z0-9_-]{3,20}') ->name('profile.comment'); Route::middleware('auth')->get('/me/saved/collections', [SavedCollectionController::class, 'index']) ->name('me.saved.collections'); // ── DASHBOARD ───────────────────────────────────────────────────────────────── Route::get('/dashboard', [DashboardController::class, 'index']) ->middleware(['auth', 'verified']) ->name('dashboard'); Route::middleware(['auth', 'creator.access'])->prefix('creator')->name('creator.')->group(function () { Route::get('/artworks', fn () => redirect()->route('studio.artworks'))->name('artworks'); Route::get('/analytics', fn () => redirect()->route('studio.analytics'))->name('analytics'); }); Route::middleware(['auth', \App\Http\Middleware\NoIndexDashboard::class]) ->get('/manage', [\App\Http\Controllers\Dashboard\ManageController::class, 'index']) ->name('manage'); Route::middleware(['auth', \App\Http\Middleware\NoIndexDashboard::class])->prefix('dashboard')->name('dashboard.')->group(function () { Route::get('/artworks', [DashboardArtworkController::class, 'index'])->name('artworks.index'); Route::get('/artworks/{id}/edit', [DashboardArtworkController::class, 'edit'])->whereNumber('id')->name('artworks.edit'); Route::put('/artworks/{id}', [DashboardArtworkController::class, 'update'])->whereNumber('id')->name('artworks.update'); Route::delete('/artworks/{id}', [DashboardArtworkController::class, 'destroy'])->whereNumber('id')->name('artworks.destroy'); Route::get('/favorites', [\App\Http\Controllers\Dashboard\FavoriteController::class, 'index'])->name('favorites'); Route::delete('/favorites/{artwork}', [\App\Http\Controllers\Dashboard\FavoriteController::class, 'destroy'])->name('favorites.destroy'); Route::get('/followers', [\App\Http\Controllers\Dashboard\FollowerController::class, 'index'])->name('followers'); Route::get('/following', [\App\Http\Controllers\Dashboard\FollowingController::class, 'index'])->name('following'); Route::get('/comments', fn () => redirect()->route('dashboard.comments.received', request()->query(), 302))->name('comments'); Route::get('/comments/received', [\App\Http\Controllers\Dashboard\CommentController::class, 'received'])->name('comments.received'); Route::get('/notifications', [\App\Http\Controllers\Dashboard\NotificationController::class, 'index'])->name('notifications'); Route::get('/gallery', [\App\Http\Controllers\Dashboard\DashboardGalleryController::class, 'index'])->name('gallery'); Route::get('/awards', [\App\Http\Controllers\Dashboard\DashboardAwardsController::class, 'index'])->name('awards'); }); // Canonical dashboard profile / settings Route::middleware(['auth'])->get('/dashboard/profile', [ProfileController::class, 'editSettings'])->name('dashboard.profile'); Route::middleware(['auth'])->get('/settings/profile', [ProfileController::class, 'editSettings'])->name('settings.profile'); // ── STUDIO Pro (/studio/*) ──────────────────────────────────────────────────── Route::middleware(['auth', 'ensure.onboarding.complete'])->prefix('studio')->name('studio.')->group(function () { Route::get('/', [StudioController::class, 'index'])->name('index'); Route::get('/content', [StudioController::class, 'content'])->name('content'); Route::get('/artworks', [StudioController::class, 'artworks'])->name('artworks'); Route::get('/drafts', [StudioController::class, 'drafts'])->name('drafts'); Route::get('/upload-queue', [StudioController::class, 'uploadQueue'])->name('upload-queue'); Route::get('/scheduled', [StudioController::class, 'scheduled'])->name('scheduled'); Route::get('/calendar', [StudioController::class, 'calendar'])->name('calendar'); Route::get('/archived', [StudioController::class, 'archived'])->name('archived'); Route::get('/artworks/drafts', fn () => redirect()->route('studio.drafts', request()->query(), 302)); Route::get('/artworks/archived', fn () => redirect()->route('studio.archived', request()->query(), 302)); Route::get('/artworks/{id}/edit', [StudioController::class, 'edit'])->whereNumber('id')->name('artworks.edit'); Route::get('/artworks/{id}/analytics', [StudioController::class, 'analytics'])->whereNumber('id')->name('artworks.analytics'); Route::get('/analytics', [StudioController::class, 'analyticsOverview'])->name('analytics'); Route::get('/collections', [StudioController::class, 'collections'])->name('collections'); Route::get('/stories', [StudioController::class, 'stories'])->name('stories'); Route::get('/assets', [StudioController::class, 'assets'])->name('assets'); Route::get('/comments', [StudioController::class, 'comments'])->name('comments'); Route::get('/activity', [StudioController::class, 'activity'])->name('activity'); Route::get('/inbox', [StudioController::class, 'inbox'])->name('inbox'); Route::get('/challenges', [StudioController::class, 'challenges'])->name('challenges'); Route::get('/followers', [StudioController::class, 'followers'])->name('followers'); Route::get('/search', [StudioController::class, 'search'])->name('search'); Route::get('/growth', [StudioController::class, 'growth'])->name('growth'); Route::get('/profile', [StudioController::class, 'profile'])->name('profile'); Route::get('/featured', [StudioController::class, 'featured'])->name('featured'); Route::get('/preferences', [StudioController::class, 'preferences'])->name('preferences'); Route::get('/settings', [StudioController::class, 'settings'])->name('settings'); Route::get('/cards', [\App\Http\Controllers\Studio\StudioNovaCardsController::class, 'index'])->name('cards.index'); Route::get('/cards/create', [\App\Http\Controllers\Studio\StudioNovaCardsController::class, 'create'])->name('cards.create'); Route::post('/cards/remix/{id}', [\App\Http\Controllers\Studio\StudioNovaCardsController::class, 'remix'])->whereNumber('id')->name('cards.remix'); Route::get('/cards/{id}/edit', [\App\Http\Controllers\Studio\StudioNovaCardsController::class, 'edit'])->whereNumber('id')->name('cards.edit'); Route::get('/cards/{id}/preview', [\App\Http\Controllers\Studio\StudioNovaCardsController::class, 'preview'])->whereNumber('id')->name('cards.preview'); Route::get('/cards/{id}/analytics', [\App\Http\Controllers\Studio\StudioNovaCardsController::class, 'analytics'])->whereNumber('id')->name('cards.analytics'); Route::get('/news', [StudioNewsController::class, 'index'])->name('news.index'); Route::get('/news/create', [StudioNewsController::class, 'create'])->name('news.create'); Route::post('/news', [StudioNewsController::class, 'store'])->name('news.store'); Route::get('/news/entity-search', [StudioNewsController::class, 'entitySearch'])->name('news.entity-search'); Route::get('/news/categories', [StudioNewsController::class, 'categories'])->name('news.categories'); Route::post('/news/categories', [StudioNewsController::class, 'storeCategory'])->name('news.categories.store'); Route::patch('/news/categories/{category}', [StudioNewsController::class, 'updateCategory'])->whereNumber('category')->name('news.categories.update'); Route::get('/news/tags', [StudioNewsController::class, 'tags'])->name('news.tags'); Route::post('/news/tags', [StudioNewsController::class, 'storeTag'])->name('news.tags.store'); Route::patch('/news/tags/{tag}', [StudioNewsController::class, 'updateTag'])->whereNumber('tag')->name('news.tags.update'); Route::get('/news/{article}/preview', [StudioNewsController::class, 'preview'])->whereNumber('article')->name('news.preview'); Route::get('/news/{article}/edit', [StudioNewsController::class, 'edit'])->whereNumber('article')->name('news.edit'); Route::patch('/news/{article}', [StudioNewsController::class, 'update'])->whereNumber('article')->name('news.update'); Route::post('/news/{article}/publish', [StudioNewsController::class, 'publish'])->whereNumber('article')->name('news.publish'); Route::post('/news/{article}/archive', [StudioNewsController::class, 'archive'])->whereNumber('article')->name('news.archive'); Route::post('/news/{article}/feature', [StudioNewsController::class, 'feature'])->whereNumber('article')->name('news.feature'); Route::post('/news/{article}/pin', [StudioNewsController::class, 'pin'])->whereNumber('article')->name('news.pin'); Route::get('/worlds', [StudioWorldController::class, 'index'])->name('worlds.index'); Route::get('/worlds/create', [StudioWorldController::class, 'create'])->name('worlds.create'); Route::post('/worlds', [StudioWorldController::class, 'store'])->name('worlds.store'); Route::get('/worlds/entity-search', [StudioWorldController::class, 'entitySearch'])->name('worlds.entity-search'); Route::get('/worlds/{world}/preview', [StudioWorldController::class, 'preview'])->whereNumber('world')->name('worlds.preview'); Route::get('/worlds/{world}/edit', [StudioWorldController::class, 'edit'])->whereNumber('world')->name('worlds.edit'); Route::patch('/worlds/{world}', [StudioWorldController::class, 'update'])->whereNumber('world')->name('worlds.update'); Route::post('/worlds/{world}/submissions/{submission}/approve', [StudioWorldController::class, 'approveSubmission'])->whereNumber('world')->whereNumber('submission')->name('worlds.submissions.approve'); Route::post('/worlds/{world}/submissions/{submission}/remove', [StudioWorldController::class, 'removeSubmission'])->whereNumber('world')->whereNumber('submission')->name('worlds.submissions.remove'); Route::post('/worlds/{world}/submissions/{submission}/block', [StudioWorldController::class, 'blockSubmission'])->whereNumber('world')->whereNumber('submission')->name('worlds.submissions.block'); Route::post('/worlds/{world}/submissions/{submission}/unblock', [StudioWorldController::class, 'unblockSubmission'])->whereNumber('world')->whereNumber('submission')->name('worlds.submissions.unblock'); Route::post('/worlds/{world}/submissions/{submission}/restore', [StudioWorldController::class, 'restoreSubmission'])->whereNumber('world')->whereNumber('submission')->name('worlds.submissions.restore'); Route::post('/worlds/{world}/submissions/{submission}/feature', [StudioWorldController::class, 'featureSubmission'])->whereNumber('world')->whereNumber('submission')->name('worlds.submissions.feature'); Route::post('/worlds/{world}/submissions/{submission}/unfeature', [StudioWorldController::class, 'unfeatureSubmission'])->whereNumber('world')->whereNumber('submission')->name('worlds.submissions.unfeature'); Route::post('/worlds/{world}/submissions/{submission}/pending', [StudioWorldController::class, 'pendingSubmission'])->whereNumber('world')->whereNumber('submission')->name('worlds.submissions.pending'); Route::post('/worlds/{world}/publish', [StudioWorldController::class, 'publish'])->whereNumber('world')->name('worlds.publish'); Route::post('/worlds/{world}/archive', [StudioWorldController::class, 'archive'])->whereNumber('world')->name('worlds.archive'); Route::post('/worlds/{world}/duplicate', [StudioWorldController::class, 'duplicate'])->whereNumber('world')->name('worlds.duplicate'); Route::post('/worlds/{world}/new-edition', [StudioWorldController::class, 'newEdition'])->whereNumber('world')->name('worlds.new-edition'); Route::get('/groups', [\App\Http\Controllers\Studio\GroupStudioController::class, 'index'])->name('groups.index'); Route::get('/groups/create', [\App\Http\Controllers\Studio\GroupStudioController::class, 'create'])->name('groups.create'); Route::post('/groups', [\App\Http\Controllers\Studio\GroupStudioController::class, 'store'])->name('groups.store'); Route::get('/groups/{group}', [\App\Http\Controllers\Studio\GroupStudioController::class, 'show'])->name('groups.show'); Route::get('/groups/{group}/artworks', [\App\Http\Controllers\Studio\GroupStudioController::class, 'artworks'])->name('groups.artworks'); Route::get('/groups/{group}/collections', [\App\Http\Controllers\Studio\GroupStudioController::class, 'collections'])->name('groups.collections'); Route::get('/groups/{group}/members', [\App\Http\Controllers\Studio\GroupStudioController::class, 'members'])->name('groups.members'); Route::get('/groups/{group}/invitations', [\App\Http\Controllers\Studio\GroupStudioController::class, 'invitations'])->name('groups.invitations'); Route::get('/groups/{group}/join-requests', [\App\Http\Controllers\Studio\GroupJoinRequestStudioController::class, 'index'])->name('groups.join-requests'); Route::get('/groups/{group}/review', [\App\Http\Controllers\Studio\GroupReviewStudioController::class, 'index'])->name('groups.review'); Route::get('/groups/{group}/recruitment', [\App\Http\Controllers\Studio\GroupRecruitmentStudioController::class, 'show'])->name('groups.recruitment'); Route::patch('/groups/{group}/recruitment', [\App\Http\Controllers\Studio\GroupRecruitmentStudioController::class, 'update'])->name('groups.recruitment.update'); Route::get('/groups/{group}/posts', [\App\Http\Controllers\Studio\GroupPostStudioController::class, 'index'])->name('groups.posts.index'); Route::get('/groups/{group}/posts/create', [\App\Http\Controllers\Studio\GroupPostStudioController::class, 'create'])->name('groups.posts.create'); Route::post('/groups/{group}/posts', [\App\Http\Controllers\Studio\GroupPostStudioController::class, 'store'])->name('groups.posts.store'); Route::get('/groups/{group}/posts/{post}/edit', [\App\Http\Controllers\Studio\GroupPostStudioController::class, 'edit'])->name('groups.posts.edit'); Route::patch('/groups/{group}/posts/{post}', [\App\Http\Controllers\Studio\GroupPostStudioController::class, 'update'])->name('groups.posts.update'); Route::post('/groups/{group}/posts/{post}/publish', [\App\Http\Controllers\Studio\GroupPostStudioController::class, 'publish'])->name('groups.posts.publish'); Route::post('/groups/{group}/posts/{post}/pin', [\App\Http\Controllers\Studio\GroupPostStudioController::class, 'pin'])->name('groups.posts.pin'); Route::post('/groups/{group}/posts/{post}/archive', [\App\Http\Controllers\Studio\GroupPostStudioController::class, 'archive'])->name('groups.posts.archive'); Route::get('/groups/{group}/projects', [\App\Http\Controllers\Studio\GroupProjectStudioController::class, 'index'])->name('groups.projects.index'); Route::get('/groups/{group}/projects/create', [\App\Http\Controllers\Studio\GroupProjectStudioController::class, 'create'])->name('groups.projects.create'); Route::post('/groups/{group}/projects', [\App\Http\Controllers\Studio\GroupProjectStudioController::class, 'store'])->name('groups.projects.store'); Route::get('/groups/{group}/projects/{project}/edit', [\App\Http\Controllers\Studio\GroupProjectStudioController::class, 'edit'])->name('groups.projects.edit'); Route::patch('/groups/{group}/projects/{project}', [\App\Http\Controllers\Studio\GroupProjectStudioController::class, 'update'])->name('groups.projects.update'); Route::post('/groups/{group}/projects/{project}/attach-artwork', [\App\Http\Controllers\Studio\GroupProjectStudioController::class, 'attachArtwork'])->name('groups.projects.attach-artwork'); Route::post('/groups/{group}/projects/{project}/attach-asset', [\App\Http\Controllers\Studio\GroupProjectStudioController::class, 'attachAsset'])->name('groups.projects.attach-asset'); Route::post('/groups/{group}/projects/{project}/status', [\App\Http\Controllers\Studio\GroupProjectStudioController::class, 'status'])->name('groups.projects.status'); Route::post('/groups/{group}/projects/{project}/milestones', [\App\Http\Controllers\Studio\GroupProjectStudioController::class, 'storeMilestone'])->name('groups.projects.milestones.store'); Route::patch('/groups/{group}/projects/{project}/milestones/{milestone}', [\App\Http\Controllers\Studio\GroupProjectStudioController::class, 'updateMilestone'])->name('groups.projects.milestones.update'); Route::get('/groups/{group}/releases', [\App\Http\Controllers\Studio\GroupReleaseStudioController::class, 'index'])->name('groups.releases.index'); Route::get('/groups/{group}/releases/create', [\App\Http\Controllers\Studio\GroupReleaseStudioController::class, 'create'])->name('groups.releases.create'); Route::post('/groups/{group}/releases', [\App\Http\Controllers\Studio\GroupReleaseStudioController::class, 'store'])->name('groups.releases.store'); Route::get('/groups/{group}/releases/{release}', [\App\Http\Controllers\Studio\GroupReleaseStudioController::class, 'show'])->name('groups.releases.show'); Route::patch('/groups/{group}/releases/{release}', [\App\Http\Controllers\Studio\GroupReleaseStudioController::class, 'update'])->name('groups.releases.update'); Route::post('/groups/{group}/releases/{release}/stage', [\App\Http\Controllers\Studio\GroupReleaseStudioController::class, 'stage'])->name('groups.releases.stage'); Route::post('/groups/{group}/releases/{release}/publish', [\App\Http\Controllers\Studio\GroupReleaseStudioController::class, 'publish'])->name('groups.releases.publish'); Route::post('/groups/{group}/releases/{release}/attach-artwork', [\App\Http\Controllers\Studio\GroupReleaseStudioController::class, 'attachArtwork'])->name('groups.releases.attach-artwork'); Route::post('/groups/{group}/releases/{release}/attach-contributor', [\App\Http\Controllers\Studio\GroupReleaseStudioController::class, 'attachContributor'])->name('groups.releases.attach-contributor'); Route::post('/groups/{group}/releases/{release}/milestones', [\App\Http\Controllers\Studio\GroupReleaseStudioController::class, 'storeMilestone'])->name('groups.releases.milestones.store'); Route::patch('/groups/{group}/releases/{release}/milestones/{milestone}', [\App\Http\Controllers\Studio\GroupReleaseStudioController::class, 'updateMilestone'])->name('groups.releases.milestones.update'); Route::get('/groups/{group}/reputation', [\App\Http\Controllers\Studio\GroupReputationStudioController::class, 'show'])->name('groups.reputation'); Route::get('/groups/{group}/challenges', [\App\Http\Controllers\Studio\GroupChallengeStudioController::class, 'index'])->name('groups.challenges.index'); Route::get('/groups/{group}/challenges/create', [\App\Http\Controllers\Studio\GroupChallengeStudioController::class, 'create'])->name('groups.challenges.create'); Route::post('/groups/{group}/challenges', [\App\Http\Controllers\Studio\GroupChallengeStudioController::class, 'store'])->name('groups.challenges.store'); Route::get('/groups/{group}/challenges/{challenge}/edit', [\App\Http\Controllers\Studio\GroupChallengeStudioController::class, 'edit'])->name('groups.challenges.edit'); Route::patch('/groups/{group}/challenges/{challenge}', [\App\Http\Controllers\Studio\GroupChallengeStudioController::class, 'update'])->name('groups.challenges.update'); Route::post('/groups/{group}/challenges/{challenge}/publish', [\App\Http\Controllers\Studio\GroupChallengeStudioController::class, 'publish'])->name('groups.challenges.publish'); Route::post('/groups/{group}/challenges/{challenge}/attach-artwork', [\App\Http\Controllers\Studio\GroupChallengeStudioController::class, 'attachArtwork'])->name('groups.challenges.attach-artwork'); Route::get('/groups/{group}/events', [\App\Http\Controllers\Studio\GroupEventStudioController::class, 'index'])->name('groups.events.index'); Route::get('/groups/{group}/events/create', [\App\Http\Controllers\Studio\GroupEventStudioController::class, 'create'])->name('groups.events.create'); Route::post('/groups/{group}/events', [\App\Http\Controllers\Studio\GroupEventStudioController::class, 'store'])->name('groups.events.store'); Route::get('/groups/{group}/events/{event}/edit', [\App\Http\Controllers\Studio\GroupEventStudioController::class, 'edit'])->name('groups.events.edit'); Route::patch('/groups/{group}/events/{event}', [\App\Http\Controllers\Studio\GroupEventStudioController::class, 'update'])->name('groups.events.update'); Route::post('/groups/{group}/events/{event}/publish', [\App\Http\Controllers\Studio\GroupEventStudioController::class, 'publish'])->name('groups.events.publish'); Route::get('/groups/{group}/assets', [\App\Http\Controllers\Studio\GroupAssetStudioController::class, 'index'])->name('groups.assets.index'); Route::post('/groups/{group}/assets', [\App\Http\Controllers\Studio\GroupAssetStudioController::class, 'store'])->name('groups.assets.store'); Route::patch('/groups/{group}/assets/{asset}', [\App\Http\Controllers\Studio\GroupAssetStudioController::class, 'update'])->name('groups.assets.update'); Route::get('/groups/{group}/activity', [\App\Http\Controllers\Studio\GroupActivityStudioController::class, 'index'])->name('groups.activity'); Route::post('/groups/{group}/activity/{item}/pin', [\App\Http\Controllers\Studio\GroupActivityStudioController::class, 'pin'])->name('groups.activity.pin'); Route::get('/groups/{group}/settings', [\App\Http\Controllers\Studio\GroupStudioController::class, 'settings'])->name('groups.settings'); Route::patch('/groups/{group}', [\App\Http\Controllers\Studio\GroupStudioController::class, 'update'])->name('groups.update'); Route::post('/groups/{group}/archive', [\App\Http\Controllers\Studio\GroupStudioController::class, 'archive'])->name('groups.archive'); Route::post('/groups/{group}/members', [\App\Http\Controllers\GroupMemberController::class, 'store'])->name('groups.members.store'); Route::patch('/groups/{group}/members/{member}', [\App\Http\Controllers\GroupMemberController::class, 'update'])->name('groups.members.update'); Route::patch('/groups/{group}/members/{member}/permissions', [\App\Http\Controllers\GroupMemberController::class, 'updatePermissions'])->name('groups.members.permissions.update'); Route::post('/groups/{group}/members/{member}/transfer', [\App\Http\Controllers\GroupMemberController::class, 'transfer'])->name('groups.members.transfer'); Route::delete('/groups/{group}/members/{member}', [\App\Http\Controllers\GroupMemberController::class, 'destroy'])->name('groups.members.destroy'); Route::delete('/groups/{group}/invitations/{invitation}', [\App\Http\Controllers\GroupMemberController::class, 'destroyInvitation'])->name('groups.invitations.destroy'); Route::post('/groups/{group}/join-requests/{joinRequest}/approve', [\App\Http\Controllers\Studio\GroupJoinRequestStudioController::class, 'approve'])->name('groups.join-requests.approve'); Route::post('/groups/{group}/join-requests/{joinRequest}/reject', [\App\Http\Controllers\Studio\GroupJoinRequestStudioController::class, 'reject'])->name('groups.join-requests.reject'); Route::post('/groups/{group}/artworks/{artwork}/approve', [\App\Http\Controllers\Studio\GroupReviewStudioController::class, 'approve'])->name('groups.artworks.approve'); Route::post('/groups/{group}/artworks/{artwork}/needs-changes', [\App\Http\Controllers\Studio\GroupReviewStudioController::class, 'needsChanges'])->name('groups.artworks.needs-changes'); Route::post('/groups/{group}/artworks/{artwork}/reject', [\App\Http\Controllers\Studio\GroupReviewStudioController::class, 'reject'])->name('groups.artworks.reject'); Route::post('/groups/invitations/{invitation}/accept', [\App\Http\Controllers\GroupMemberController::class, 'acceptInvitation'])->name('groups.invitations.accept'); Route::post('/groups/invitations/{invitation}/decline', [\App\Http\Controllers\GroupMemberController::class, 'declineInvitation'])->name('groups.invitations.decline'); Route::post('/groups/members/{member}/accept', [\App\Http\Controllers\GroupMemberController::class, 'accept'])->name('groups.members.accept'); Route::post('/groups/members/{member}/decline', [\App\Http\Controllers\GroupMemberController::class, 'decline'])->name('groups.members.decline'); }); Route::get('/cards', [\App\Http\Controllers\Web\NovaCardsController::class, 'index'])->name('cards.index'); Route::get('/cards/popular', [\App\Http\Controllers\Web\NovaCardsController::class, 'popular'])->name('cards.popular'); Route::get('/cards/rising', [\App\Http\Controllers\Web\NovaCardsController::class, 'rising'])->name('cards.rising'); Route::get('/cards/remixed', [\App\Http\Controllers\Web\NovaCardsController::class, 'remixed'])->name('cards.remixed'); Route::get('/cards/remix-highlights', [\App\Http\Controllers\Web\NovaCardsController::class, 'remixHighlights'])->name('cards.remix-highlights'); Route::get('/cards/editorial', [\App\Http\Controllers\Web\NovaCardsController::class, 'editorial'])->name('cards.editorial'); Route::get('/cards/seasonal', [\App\Http\Controllers\Web\NovaCardsController::class, 'seasonal'])->name('cards.seasonal'); Route::get('/cards/collections/{slug}-{id}', [\App\Http\Controllers\Web\NovaCardsController::class, 'collection']) ->where('slug', '[a-z0-9\-]+') ->whereNumber('id') ->name('cards.collections.show'); Route::get('/cards/{slug}-{id}/lineage', [\App\Http\Controllers\Web\NovaCardsController::class, 'lineage']) ->where('slug', '[a-z0-9\-]+') ->whereNumber('id') ->name('cards.lineage'); Route::get('/cards/challenges', [\App\Http\Controllers\Web\NovaCardsController::class, 'challenges'])->name('cards.challenges'); Route::get('/cards/challenges/{slug}', [\App\Http\Controllers\Web\NovaCardsController::class, 'challenge'])->name('cards.challenges.show'); Route::get('/cards/templates', [\App\Http\Controllers\Web\NovaCardsController::class, 'templates'])->name('cards.templates'); Route::get('/cards/assets', [\App\Http\Controllers\Web\NovaCardsController::class, 'assets'])->name('cards.assets'); Route::get('/cards/category/{categorySlug}', [\App\Http\Controllers\Web\NovaCardsController::class, 'category'])->name('cards.category'); Route::get('/cards/mood/{moodSlug}', [\App\Http\Controllers\Web\NovaCardsController::class, 'mood'])->name('cards.mood'); Route::get('/cards/style/{styleSlug}', [\App\Http\Controllers\Web\NovaCardsController::class, 'style'])->name('cards.style'); Route::get('/cards/palette/{paletteSlug}', [\App\Http\Controllers\Web\NovaCardsController::class, 'palette'])->name('cards.palette'); Route::get('/cards/tag/{tagSlug}', [\App\Http\Controllers\Web\NovaCardsController::class, 'tag'])->name('cards.tag'); Route::get('/cards/creator/{username}/portfolio', [\App\Http\Controllers\Web\NovaCardsController::class, 'creatorPortfolio']) ->where('username', '[A-Za-z0-9_-]{3,20}') ->name('cards.creator.portfolio'); Route::get('/cards/creator/{username}', [\App\Http\Controllers\Web\NovaCardsController::class, 'creator']) ->where('username', '[A-Za-z0-9_-]{3,20}') ->name('cards.creator'); Route::get('/cards/{slug}-{id}', [\App\Http\Controllers\Web\NovaCardsController::class, 'show']) ->where('slug', '[a-z0-9\-]+') ->whereNumber('id') ->name('cards.show'); // Internal render frame — accessed only by the Playwright Node renderer with a signed URL. Route::get('/internal/nova-cards/render-frame/{uuid}', [\App\Http\Controllers\Internal\NovaCardRenderFrameController::class, 'show']) ->where('uuid', '[0-9a-f\-]{36}') ->name('nova-cards.render-frame'); Route::middleware('auth')->group(function () { Route::post('/cards/{card}/comments', [\App\Http\Controllers\NovaCardCommentController::class, 'store']) ->whereNumber('card') ->name('cards.comments.store'); Route::delete('/cards/{card}/comments/{comment}', [\App\Http\Controllers\NovaCardCommentController::class, 'destroy']) ->whereNumber('card') ->whereNumber('comment') ->name('cards.comments.destroy'); }); Route::middleware(['auth', 'admin.moderation'])->prefix('cp/cards')->name('cp.cards.')->group(function () { Route::get('/', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'index'])->name('index'); Route::patch('/{card}', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'updateCard'])->whereNumber('card')->name('update'); Route::patch('/creators/{user}', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'updateCreator'])->whereNumber('user')->name('creators.update'); Route::get('/templates', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'templates'])->name('templates.index'); Route::post('/templates', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'storeTemplate'])->name('templates.store'); Route::patch('/templates/{template}', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'updateTemplate'])->whereNumber('template')->name('templates.update'); Route::get('/asset-packs', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'assetPacks'])->name('asset-packs.index'); Route::post('/asset-packs', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'storeAssetPack'])->name('asset-packs.store'); Route::patch('/asset-packs/{assetPack}', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'updateAssetPack'])->whereNumber('assetPack')->name('asset-packs.update'); Route::get('/challenges', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'challenges'])->name('challenges.index'); Route::post('/challenges', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'storeChallenge'])->name('challenges.store'); Route::patch('/challenges/{challenge}', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'updateChallenge'])->whereNumber('challenge')->name('challenges.update'); Route::get('/collections', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'collections'])->name('collections.index'); Route::post('/collections', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'storeCollection'])->name('collections.store'); Route::patch('/collections/{collection}', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'updateCollection'])->whereNumber('collection')->name('collections.update'); Route::post('/collections/{collection}/cards', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'storeCollectionCard'])->whereNumber('collection')->name('collections.cards.store'); Route::delete('/collections/{collection}/cards/{card}', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'destroyCollectionCard'])->whereNumber('collection')->whereNumber('card')->name('collections.cards.destroy'); Route::post('/categories', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'storeCategory'])->name('categories.store'); Route::patch('/categories/{category}', [\App\Http\Controllers\Settings\NovaCardAdminController::class, 'updateCategory'])->whereNumber('category')->name('categories.update'); }); Route::middleware(['artwork.maturity.access'])->prefix('cp/maturity')->name('cp.maturity.')->group(function () { Route::get('/', [\App\Http\Controllers\Settings\ArtworkMaturityAdminController::class, 'index'])->name('index'); Route::get('/queue', [\App\Http\Controllers\Settings\ArtworkMaturityAdminController::class, 'list'])->name('list'); Route::post('/{artwork:id}/review', [\App\Http\Controllers\Settings\ArtworkMaturityAdminController::class, 'review'])->whereNumber('artwork')->name('review'); }); Route::middleware(['artwork.maturity.access'])->prefix('cp/ai-biography')->name('cp.ai-biography.')->group(function () { Route::get('/', [\App\Http\Controllers\Settings\AiBiographyAdminController::class, 'index'])->name('index'); Route::post('/users/{user}/rebuild', [\App\Http\Controllers\Settings\AiBiographyAdminController::class, 'rebuild'])->whereNumber('user')->name('rebuild'); Route::post('/records/{biography}/approve', [\App\Http\Controllers\Settings\AiBiographyAdminController::class, 'approve'])->whereNumber('biography')->name('approve'); Route::post('/records/{biography}/flag', [\App\Http\Controllers\Settings\AiBiographyAdminController::class, 'flag'])->whereNumber('biography')->name('flag'); Route::post('/records/{biography}/hide', [\App\Http\Controllers\Settings\AiBiographyAdminController::class, 'hide'])->whereNumber('biography')->name('hide'); Route::post('/records/{biography}/show', [\App\Http\Controllers\Settings\AiBiographyAdminController::class, 'show'])->whereNumber('biography')->name('show'); }); // ── SETTINGS / PROFILE EDIT ─────────────────────────────────────────────────── Route::middleware(['auth', 'normalize.username', 'ensure.onboarding.complete'])->group(function () { Route::get('/profile', fn () => redirect()->route('dashboard.profile', [], 301))->name('legacy.profile.redirect'); Route::get('/settings', fn () => redirect()->route('dashboard.profile', [], 302))->name('settings'); Route::get('/profile/edit', fn () => redirect()->route('dashboard.profile', [], 302))->name('profile.edit'); Route::match(['post','put','patch'], '/profile', [ProfileController::class, 'update'])->name('profile.update'); Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy'); Route::match(['post', 'put'], '/profile/password', [ProfileController::class, 'password'])->name('profile.password'); Route::post('/avatar/upload', [AvatarController::class, 'upload'])->middleware('throttle:20,1')->name('avatar.upload'); Route::post('/settings/profile/update', [ProfileController::class, 'updateProfileSection'])->middleware('forum.bot.protection:profile_update')->name('settings.profile.update'); Route::post('/settings/account/username', [ProfileController::class, 'updateUsername'])->middleware('forum.bot.protection:profile_update')->name('settings.account.username'); Route::post('/settings/account/update', [ProfileController::class, 'updateAccountSection'])->middleware('forum.bot.protection:profile_update')->name('settings.account.update'); Route::post('/settings/email/request', [ProfileController::class, 'requestEmailChange']) ->middleware('throttle:email-change-request') ->name('settings.email.request'); Route::post('/settings/email/verify', [ProfileController::class, 'verifyEmailChange']) ->middleware('throttle:10,1') ->name('settings.email.verify'); Route::post('/settings/personal/update', [ProfileController::class, 'updatePersonalSection'])->middleware('forum.bot.protection:profile_update')->name('settings.personal.update'); Route::post('/settings/notifications/update', [ProfileController::class, 'updateNotificationsSection'])->middleware('forum.bot.protection:profile_update')->name('settings.notifications.update'); Route::post('/settings/content/update', [ProfileController::class, 'updateContentPreferencesSection'])->middleware('forum.bot.protection:profile_update')->name('settings.content.update'); Route::post('/settings/security/password', [ProfileController::class, 'updateSecurityPassword'])->middleware('forum.bot.protection:profile_update')->name('settings.security.password'); Route::get('/settings/collections/create', [CollectionManageController::class, 'create'])->name('settings.collections.create'); Route::post('/settings/collections', [CollectionManageController::class, 'store'])->name('settings.collections.store'); Route::post('/settings/collections/smart-preview', [CollectionManageController::class, 'smartPreview'])->name('settings.collections.smart.preview'); Route::post('/settings/collections/reorder-profile', [CollectionManageController::class, 'reorderProfile'])->name('settings.collections.reorder-profile'); Route::get('/settings/collections/dashboard', [CollectionInsightsController::class, 'dashboard'])->name('settings.collections.dashboard'); Route::get('/settings/collections/search', [CollectionInsightsController::class, 'search'])->name('settings.collections.search'); Route::post('/settings/collections/bulk-actions', [CollectionInsightsController::class, 'bulkActions'])->name('settings.collections.bulk-actions'); Route::get('/settings/collections/{collection}', [CollectionManageController::class, 'show'])->name('settings.collections.show'); Route::get('/settings/collections/{collection}/edit', [CollectionManageController::class, 'edit'])->name('settings.collections.edit'); Route::get('/settings/collections/{collection}/analytics', [CollectionInsightsController::class, 'analytics'])->name('settings.collections.analytics'); Route::get('/settings/collections/{collection}/health', [CollectionInsightsController::class, 'health'])->name('settings.collections.health'); Route::get('/settings/collections/{collection}/history', [CollectionInsightsController::class, 'history'])->name('settings.collections.history'); Route::post('/settings/collections/{collection}/history/{history}/restore', [CollectionInsightsController::class, 'restoreHistory'])->name('settings.collections.history.restore'); Route::patch('/settings/collections/{collection}', [CollectionManageController::class, 'update'])->name('settings.collections.update'); Route::post('/settings/collections/{collection}/presentation', [CollectionManageController::class, 'updatePresentation'])->name('settings.collections.presentation'); Route::post('/settings/collections/{collection}/campaign', [CollectionManageController::class, 'updateCampaign'])->name('settings.collections.campaign'); Route::post('/settings/collections/{collection}/series', [CollectionManageController::class, 'updateSeries'])->name('settings.collections.series'); Route::post('/settings/collections/{collection}/lifecycle', [CollectionManageController::class, 'updateLifecycle'])->name('settings.collections.lifecycle'); Route::post('/settings/collections/{collection}/linked-collections', [CollectionManageController::class, 'syncLinkedCollections'])->name('settings.collections.linked.sync'); Route::post('/settings/collections/{collection}/entity-links', [CollectionManageController::class, 'syncEntityLinks'])->name('settings.collections.entity-links.sync'); Route::delete('/settings/collections/{collection}', [CollectionManageController::class, 'destroy'])->name('settings.collections.destroy'); Route::post('/settings/collections/{collection}/feature', [CollectionManageController::class, 'feature'])->name('settings.collections.feature'); Route::delete('/settings/collections/{collection}/feature', [CollectionManageController::class, 'unfeature'])->name('settings.collections.unfeature'); Route::patch('/settings/collections/{collection}/smart-rules', [CollectionManageController::class, 'updateSmartRules'])->name('settings.collections.smart.rules'); Route::post('/settings/collections/{collection}/ai/suggest-title', [CollectionAiController::class, 'suggestTitle'])->name('settings.collections.ai.suggest-title'); Route::post('/settings/collections/{collection}/ai/suggest-summary', [CollectionAiController::class, 'suggestSummary'])->name('settings.collections.ai.suggest-summary'); Route::post('/settings/collections/{collection}/ai/suggest-cover', [CollectionAiController::class, 'suggestCover'])->name('settings.collections.ai.suggest-cover'); Route::post('/settings/collections/{collection}/ai/suggest-grouping', [CollectionAiController::class, 'suggestGrouping'])->name('settings.collections.ai.suggest-grouping'); Route::post('/settings/collections/{collection}/ai/suggest-related-artworks', [CollectionAiController::class, 'suggestRelatedArtworks'])->name('settings.collections.ai.suggest-related-artworks'); Route::post('/settings/collections/{collection}/ai/suggest-tags', [CollectionAiController::class, 'suggestTags'])->name('settings.collections.ai.suggest-tags'); Route::post('/settings/collections/{collection}/ai/suggest-seo-description', [CollectionAiController::class, 'suggestSeoDescription'])->name('settings.collections.ai.suggest-seo-description'); Route::post('/settings/collections/{collection}/ai/explain-smart-rules', [CollectionAiController::class, 'explainSmartRules'])->name('settings.collections.ai.explain-smart-rules'); Route::post('/settings/collections/{collection}/ai/suggest-split-themes', [CollectionAiController::class, 'suggestSplitThemes'])->name('settings.collections.ai.suggest-split-themes'); Route::post('/settings/collections/{collection}/ai/suggest-merge-idea', [CollectionAiController::class, 'suggestMergeIdea'])->name('settings.collections.ai.suggest-merge-idea'); Route::post('/settings/collections/{collection}/ai/detect-weak-metadata', [CollectionAiController::class, 'detectWeakMetadata'])->name('settings.collections.ai.detect-weak-metadata'); Route::post('/settings/collections/{collection}/ai/suggest-stale-refresh', [CollectionAiController::class, 'suggestStaleRefresh'])->name('settings.collections.ai.suggest-stale-refresh'); Route::post('/settings/collections/{collection}/ai/suggest-campaign-fit', [CollectionAiController::class, 'suggestCampaignFit'])->name('settings.collections.ai.suggest-campaign-fit'); Route::post('/settings/collections/{collection}/ai/suggest-related-collections-to-link', [CollectionAiController::class, 'suggestRelatedCollectionsToLink'])->name('settings.collections.ai.suggest-related-collections-to-link'); Route::post('/settings/collections/{collection}/ai/quality-review', [CollectionInsightsController::class, 'qualityReview'])->name('settings.collections.ai.quality-review'); Route::post('/settings/collections/{collection}/workflow', [CollectionInsightsController::class, 'workflowUpdate'])->name('settings.collections.workflow'); Route::post('/settings/collections/{collection}/quality-refresh', [CollectionInsightsController::class, 'qualityRefresh'])->name('settings.collections.quality-refresh'); Route::post('/settings/collections/{collection}/canonicalize', [CollectionInsightsController::class, 'canonicalize'])->name('settings.collections.canonicalize'); Route::post('/settings/collections/{collection}/merge', [CollectionInsightsController::class, 'merge'])->name('settings.collections.merge'); Route::post('/settings/collections/{collection}/merge/reject', [CollectionInsightsController::class, 'rejectDuplicate'])->name('settings.collections.merge.reject'); Route::post('/settings/collections/{collection}/artworks', [CollectionManageController::class, 'attachArtworks'])->name('settings.collections.artworks.attach'); Route::get('/settings/collections/artworks/{artwork}/options', [CollectionManageController::class, 'artworkCollectionOptions'])->name('settings.collections.artworks.options'); Route::get('/settings/collections/{collection}/artworks/available', [CollectionManageController::class, 'availableArtworks'])->name('settings.collections.artworks.available'); Route::delete('/settings/collections/{collection}/artworks/{artwork}', [CollectionManageController::class, 'removeArtwork'])->name('settings.collections.artworks.remove'); Route::post('/settings/collections/{collection}/reorder', [CollectionManageController::class, 'reorderArtworks'])->name('settings.collections.artworks.reorder'); Route::post('/settings/collections/{collection}/members', [\App\Http\Controllers\CollectionCollaborationController::class, 'store'])->name('settings.collections.members.store'); Route::patch('/settings/collections/{collection}/members/{member}', [\App\Http\Controllers\CollectionCollaborationController::class, 'update'])->name('settings.collections.members.update'); Route::post('/settings/collections/{collection}/members/{member}/transfer', [\App\Http\Controllers\CollectionCollaborationController::class, 'transfer'])->name('settings.collections.members.transfer'); Route::delete('/settings/collections/{collection}/members/{member}', [\App\Http\Controllers\CollectionCollaborationController::class, 'destroy'])->name('settings.collections.members.destroy'); Route::post('/settings/collections/members/{member}/accept', [\App\Http\Controllers\CollectionCollaborationController::class, 'accept'])->name('settings.collections.members.accept'); Route::post('/settings/collections/members/{member}/decline', [\App\Http\Controllers\CollectionCollaborationController::class, 'decline'])->name('settings.collections.members.decline'); Route::get('/settings/collections/surfaces', [CollectionSurfaceController::class, 'index'])->name('settings.collections.surfaces.index'); Route::post('/settings/collections/surfaces/definitions', [CollectionSurfaceController::class, 'storeDefinition'])->name('settings.collections.surfaces.definitions.store'); Route::patch('/settings/collections/surfaces/definitions/{definition}', [CollectionSurfaceController::class, 'updateDefinition'])->name('settings.collections.surfaces.definitions.update'); Route::delete('/settings/collections/surfaces/definitions/{definition}', [CollectionSurfaceController::class, 'destroyDefinition'])->name('settings.collections.surfaces.definitions.destroy'); Route::post('/settings/collections/surfaces/placements', [CollectionSurfaceController::class, 'storePlacement'])->name('settings.collections.surfaces.placements.store'); Route::patch('/settings/collections/surfaces/placements/{placement}', [CollectionSurfaceController::class, 'updatePlacement'])->name('settings.collections.surfaces.placements.update'); Route::delete('/settings/collections/surfaces/placements/{placement}', [CollectionSurfaceController::class, 'destroyPlacement'])->name('settings.collections.surfaces.placements.destroy'); Route::get('/settings/collections/surfaces/definitions/{definition}/preview', [CollectionSurfaceController::class, 'preview'])->name('settings.collections.surfaces.preview'); Route::post('/settings/collections/surfaces/batch-editorial', [CollectionSurfaceController::class, 'batchEditorial'])->name('settings.collections.surfaces.batch-editorial'); Route::get('/staff/collections/programming', [CollectionProgrammingController::class, 'index'])->name('staff.collections.programming'); Route::post('/staff/collections/programs', [CollectionProgrammingController::class, 'storeProgram'])->name('staff.collections.programs.store'); Route::patch('/staff/collections/programs/{program}', [CollectionProgrammingController::class, 'updateProgram'])->name('staff.collections.programs.update'); Route::post('/staff/collections/surfaces/preview', [CollectionProgrammingController::class, 'preview'])->name('staff.collections.surfaces.preview'); Route::post('/staff/collections/eligibility/refresh', [CollectionProgrammingController::class, 'refreshEligibility'])->name('staff.collections.eligibility.refresh'); Route::post('/staff/collections/duplicate-scan', [CollectionProgrammingController::class, 'duplicateScan'])->name('staff.collections.duplicate-scan'); Route::post('/staff/collections/recommendation-refresh', [CollectionProgrammingController::class, 'refreshRecommendations'])->name('staff.collections.recommendation-refresh'); Route::post('/staff/collections/metadata', [CollectionProgrammingController::class, 'updateMetadata'])->name('staff.collections.metadata.update'); Route::post('/staff/collections/merge-queue/canonicalize', [CollectionProgrammingController::class, 'canonicalizeCandidate'])->name('staff.collections.merge-queue.canonicalize'); Route::post('/staff/collections/merge-queue/merge', [CollectionProgrammingController::class, 'mergeCandidate'])->name('staff.collections.merge-queue.merge'); Route::post('/staff/collections/merge-queue/reject', [CollectionProgrammingController::class, 'rejectCandidate'])->name('staff.collections.merge-queue.reject'); }); // ── UPLOAD ──────────────────────────────────────────────────────────────────── Route::middleware(['auth', 'ensure.onboarding.complete'])->group(function () { Route::get('/upload', function (Request $request) { $contentTypes = ContentType::with(['rootCategories.children'])->ordered()->get()->map(function ($ct) { return [ 'id' => $ct->id, 'name' => $ct->name, 'slug' => $ct->slug, 'mascot_url' => $ct->mascot_url, 'cover_art_url' => $ct->cover_art_url, 'categories' => $ct->rootCategories->map(function ($c) { return [ 'id' => $c->id, 'name' => $c->name, 'children' => $c->children->map(function ($ch) { return ['id' => $ch->id, 'name' => $ch->name]; })->values()->all(), ]; })->values()->all(), ]; })->values()->all(); $availableGroups = []; $contributorOptionsByGroup = []; $initialGroupSlug = trim((string) $request->query('group', '')); $user = $request->user(); if ($user) { $membershipService = app(GroupMembershipService::class); $availableGroups = app(\App\Services\GroupService::class)->studioOptionsForUser($user); foreach ($availableGroups as $groupOption) { $group = Group::query()->with('members')->where('slug', (string) ($groupOption['slug'] ?? ''))->first(); if (! $group || ! $group->hasActiveMember($user)) { continue; } $contributorOptionsByGroup[(string) $group->slug] = $membershipService->contributorOptions($group); } if ($initialGroupSlug !== '' && ! collect($availableGroups)->contains(fn (array $group): bool => (string) ($group['slug'] ?? '') === $initialGroupSlug)) { $initialGroupSlug = ''; } } return Inertia::render('Upload/Index', [ 'draftId' => null, 'content_types' => $contentTypes, 'suggested_tags' => [], 'eligible_worlds' => app(\App\Services\Worlds\WorldSubmissionService::class)->eligibleWorldOptions($user), 'group_options' => $availableGroups, 'contributor_options_by_group' => $contributorOptionsByGroup, 'initial_group' => $initialGroupSlug !== '' ? $initialGroupSlug : null, 'filesCdnUrl' => config('cdn.files_url'), 'chunkSize' => (int) config('uploads.chunk.max_bytes', 5242880), 'chunkRequestTimeoutMs' => (int) config('uploads.chunk.request_timeout_ms', 45000), 'feature_flags' => [ 'uploads_v2' => (bool) config('features.uploads_v2', false), ], ]); })->name('upload'); Route::get('/upload/draft/{id}', function (Request $request, string $id) { $contentTypes = ContentType::with(['rootCategories.children'])->ordered()->get()->map(function ($ct) { return [ 'id' => $ct->id, 'name' => $ct->name, 'slug' => $ct->slug, 'mascot_url' => $ct->mascot_url, 'cover_art_url' => $ct->cover_art_url, 'categories' => $ct->rootCategories->map(function ($c) { return [ 'id' => $c->id, 'name' => $c->name, 'children' => $c->children->map(function ($ch) { return ['id' => $ch->id, 'name' => $ch->name]; })->values()->all(), ]; })->values()->all(), ]; })->values()->all(); $availableGroups = []; $contributorOptionsByGroup = []; $initialGroupSlug = trim((string) $request->query('group', '')); $user = $request->user(); if ($user) { $membershipService = app(GroupMembershipService::class); $availableGroups = app(\App\Services\GroupService::class)->studioOptionsForUser($user); foreach ($availableGroups as $groupOption) { $group = Group::query()->with('members')->where('slug', (string) ($groupOption['slug'] ?? ''))->first(); if (! $group || ! $group->hasActiveMember($user)) { continue; } $contributorOptionsByGroup[(string) $group->slug] = $membershipService->contributorOptions($group); } if ($initialGroupSlug !== '' && ! collect($availableGroups)->contains(fn (array $group): bool => (string) ($group['slug'] ?? '') === $initialGroupSlug)) { $initialGroupSlug = ''; } } return Inertia::render('Upload/Index', [ 'draftId' => $id, 'content_types' => $contentTypes, 'suggested_tags' => [], 'eligible_worlds' => app(\App\Services\Worlds\WorldSubmissionService::class)->eligibleWorldOptions($user), 'group_options' => $availableGroups, 'contributor_options_by_group' => $contributorOptionsByGroup, 'initial_group' => $initialGroupSlug !== '' ? $initialGroupSlug : null, 'filesCdnUrl' => config('cdn.files_url'), 'chunkSize' => (int) config('uploads.chunk.max_bytes', 5242880), 'chunkRequestTimeoutMs' => (int) config('uploads.chunk.request_timeout_ms', 45000), 'feature_flags' => [ 'uploads_v2' => (bool) config('features.uploads_v2', false), ], ]); })->whereUuid('id')->name('upload.draft'); }); // ── AUTH ────────────────────────────────────────────────────────────────────── require __DIR__.'/auth.php'; // ── LEGACY ROUTES ───────────────────────────────────────────────────────────── require __DIR__.'/legacy.php'; // ── SEARCH ──────────────────────────────────────────────────────────────────── Route::get('/search', [\App\Http\Controllers\Web\SearchController::class, 'index'])->name('search'); // ── MISC ────────────────────────────────────────────────────────────────────── Route::view('/data-deletion', 'privacy.data-deletion')->name('privacy.data_deletion'); Route::view('/blank', 'blank')->name('blank'); // ── MESSAGES ────────────────────────────────────────────────────────────────── Route::middleware(['auth', 'ensure.onboarding.complete']) ->get('/messages/attachments/{id}', [\App\Http\Controllers\Api\Messaging\AttachmentController::class, 'show']) ->whereNumber('id') ->name('messages.attachments.show'); Route::middleware(['auth', 'ensure.onboarding.complete'])->prefix('messages')->name('messages.')->group(function () { Route::get('/', [\App\Http\Controllers\Messaging\MessagesPageController::class, 'index'])->name('index'); Route::get('/{id}', [\App\Http\Controllers\Messaging\MessagesPageController::class, 'show'])->whereNumber('id')->name('show'); }); Route::middleware(['auth', 'admin.moderation'])->get('/admin/usernames/moderation', function () { return Inertia::render('Admin/UsernameQueue'); })->name('admin.usernames.moderation'); // ── COMMUNITY ACTIVITY ──────────────────────────────────────────────────────── Route::get('/community/activity', [\App\Http\Controllers\Web\CommunityActivityController::class, 'index']) ->name('community.activity'); // ── FEEDS ───────────────────────────────────────────────────────────────────── Route::middleware(['auth', 'ensure.onboarding.complete']) ->get('/feed/following', [\App\Http\Controllers\Web\Posts\FollowingFeedController::class, 'index']) ->name('feed.following'); Route::get('/feed/trending', [\App\Http\Controllers\Web\Posts\TrendingFeedController::class, 'index']) ->name('feed.trending'); Route::middleware(['auth']) ->get('/feed/saved', [\App\Http\Controllers\Web\Posts\SavedFeedController::class, 'index']) ->name('feed.saved'); Route::get('/feed/search', [\App\Http\Controllers\Web\Posts\SearchFeedController::class, 'index']) ->name('feed.search'); // ── CONTENT BROWSER (artwork / category universal router) ───────────────────── Route::bind('artwork', function ($value) { return $value; }); Route::get('/{contentTypeSlug}/{categoryPath}/{artwork}', [BrowseGalleryController::class, 'showArtwork']) ->where('contentTypeSlug', '[a-z0-9][a-z0-9\-]*') ->where('categoryPath', '[^/]+(?:/[^/]+)*') ->name('artworks.show'); Route::get('/{contentTypeSlug}/{path?}', [BrowseGalleryController::class, 'content']) ->where('contentTypeSlug', '[a-z0-9][a-z0-9\-]*') ->where('path', '.*') ->name('content.route'); // ── FALLBACK 404 — must be last ─────────────────────────────────────────────── Route::fallback(function (\Illuminate\Http\Request $request) { return app(\App\Http\Controllers\Web\ErrorController::class)->handleNotFound($request); })->name('404.fallback');