feat: Inertia profile settings page, Studio edit redesign, EGS, Nova UI components\n\n- Redesign /dashboard/profile as Inertia React page (Settings/ProfileEdit)\n with SettingsLayout sidebar, Nova UI components (TextInput, Textarea,\n Toggle, Select, RadioGroup, Modal, Button), avatar drag-and-drop,\n password change, and account deletion sections\n- Redesign Studio artwork edit page with two-column layout, Nova components,\n integrated TagPicker, and version history modal\n- Add shared MarkdownEditor component\n- Add Early-Stage Growth System (EGS): SpotlightEngine, FeedBlender,\n GridFiller, AdaptiveTimeWindow, ActivityLayer, admin panel\n- Fix upload category/tag persistence (V1+V2 paths)\n- Fix tag source enum, category tree display, binding resolution\n- Add settings.jsx Vite entry, settings.blade.php wrapper\n- Update ProfileController with JSON response support for API calls\n- Various route fixes (profile.edit, toolbar settings link)"

This commit is contained in:
2026-03-03 20:57:43 +01:00
parent dc51d65440
commit b9c2d8597d
114 changed files with 8760 additions and 693 deletions

View File

@@ -7,6 +7,8 @@ namespace App\Services;
use App\Models\Artwork;
use App\Models\Tag;
use App\Services\ArtworkSearchService;
use App\Services\EarlyGrowth\EarlyGrowth;
use App\Services\EarlyGrowth\GridFiller;
use App\Services\Recommendation\RecommendationService;
use App\Services\UserPreferenceService;
use App\Support\AvatarUrl;
@@ -30,7 +32,8 @@ final class HomepageService
private readonly ArtworkService $artworks,
private readonly ArtworkSearchService $search,
private readonly UserPreferenceService $prefs,
private readonly RecommendationService $reco,
private readonly RecommendationService $reco,
private readonly GridFiller $gridFiller,
) {}
// ─────────────────────────────────────────────────────────────────────────
@@ -255,11 +258,16 @@ final class HomepageService
}
/**
* Fresh uploads: latest 12 approved public artworks.
* Fresh uploads: latest 10 approved public artworks.
* EGS: GridFiller ensures the section is never empty even on low-traffic days.
*/
public function getFreshUploads(int $limit = 10): array
{
return Cache::remember("homepage.fresh.{$limit}", self::CACHE_TTL, function () use ($limit): array {
// Include EGS mode in cache key so toggling EGS updates the section within TTL
$egsKey = EarlyGrowth::gridFillerEnabled() ? 'egs-' . EarlyGrowth::mode() : 'std';
$cacheKey = "homepage.fresh.{$limit}.{$egsKey}";
return Cache::remember($cacheKey, self::CACHE_TTL, function () use ($limit): array {
$artworks = Artwork::public()
->published()
->with(['user:id,name,username', 'user.profile:user_id,avatar_hash'])
@@ -267,6 +275,9 @@ final class HomepageService
->limit($limit)
->get();
// EGS: fill up to $limit when fresh uploads are sparse
$artworks = $this->gridFiller->fillCollection($artworks, $limit);
return $artworks->map(fn ($a) => $this->serializeArtwork($a))->values()->all();
});
}