diff --git a/.env.example b/.env.example index 32cf826a..c765ce9f 100644 --- a/.env.example +++ b/.env.example @@ -399,6 +399,12 @@ GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= GOOGLE_REDIRECT_URI=/auth/google/callback +# Optional light theme feature +# Set LIGHT_THEME_ENABLED=true to allow a light theme in the client-side toggle. +# Set LIGHT_THEME_SHOW_SWITCH=true to display the theme switch in the toolbar. +LIGHT_THEME_ENABLED=false +LIGHT_THEME_SHOW_SWITCH=false + # Discord — https://discord.com/developers/applications DISCORD_CLIENT_ID= DISCORD_CLIENT_SECRET= diff --git a/app/Console/Commands/AcademyBillingHealthCommand.php b/app/Console/Commands/AcademyBillingHealthCommand.php index 4d9351eb..27685fad 100644 --- a/app/Console/Commands/AcademyBillingHealthCommand.php +++ b/app/Console/Commands/AcademyBillingHealthCommand.php @@ -92,9 +92,9 @@ final class AcademyBillingHealthCommand extends Command */ private function buildReport(): array { - $stripeKey = (string) config('cashier.key', ''); - $stripeSecret = (string) config('cashier.secret', env('STRIPE_SECRET', '')); - $webhookSecret = (string) config('cashier.webhook.secret', env('STRIPE_WEBHOOK_SECRET', '')); + $stripeKey = $this->configuredString(config('cashier.key')); + $stripeSecret = $this->firstConfiguredString(config('cashier.secret'), env('STRIPE_SECRET')); + $webhookSecret = $this->firstConfiguredString(config('cashier.webhook.secret'), env('STRIPE_WEBHOOK_SECRET')); $currency = trim((string) config('cashier.currency', env('CASHIER_CURRENCY', ''))); $currencyLocale = trim((string) config('cashier.currency_locale', env('CASHIER_CURRENCY_LOCALE', ''))); $academyEnabled = (bool) config('academy.enabled', true); @@ -285,4 +285,21 @@ final class AcademyBillingHealthCommand extends Command return self::SUCCESS; } + private function firstConfiguredString(mixed ...$values): string + { + foreach ($values as $value) { + $value = $this->configuredString($value); + + if ($value !== '') { + return $value; + } + } + + return ''; + } + + private function configuredString(mixed $value): string + { + return is_string($value) ? trim($value) : ''; + } } \ No newline at end of file diff --git a/app/Http/Controllers/Academy/AcademyBillingController.php b/app/Http/Controllers/Academy/AcademyBillingController.php index f2faf29b..514baec7 100644 --- a/app/Http/Controllers/Academy/AcademyBillingController.php +++ b/app/Http/Controllers/Academy/AcademyBillingController.php @@ -12,7 +12,14 @@ use App\Support\AcademyAnalytics\AcademyAnalyticsContentType; use App\Support\Seo\SeoFactory; use Laravel\Cashier\Checkout; use Laravel\Cashier\Subscription; +use Stripe\Exception\InvalidRequestException; +use Illuminate\Support\Facades\Log; +use App\Mail\AcademyAccessIssue; +use App\Models\StaffApplication; +use Illuminate\Support\Facades\Mail; +use Illuminate\Support\Facades\Storage; +use Illuminate\Support\Str; final class AcademyBillingController extends Controller { public function __construct( @@ -48,6 +55,7 @@ final class AcademyBillingController extends Controller 'activePlanKey' => $activePlan['key'] ?? null, 'activePlanLabel' => $activePlan['label'] ?? null, 'catalog' => $this->catalog(), + 'missingRemote' => $this->plans->missingRemotePriceIds(), 'links' => [ 'login' => \route('login'), 'pricing' => \route('academy.pricing'), @@ -64,7 +72,7 @@ final class AcademyBillingController extends Controller 'isGuest' => $user === null, 'isSubscriber' => $user?->hasAcademyCreatorAccess() || $user?->hasAcademyProAccess(), ], - ])->rootView('collections'); + ])->rootView('academy'); } public function checkout(\Illuminate\Http\Request $request): Checkout|\Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse @@ -110,6 +118,46 @@ final class AcademyBillingController extends Controller } if ($this->access->hasActiveAcademySubscription($user)) { + // If the user already has an Academy subscription, allow an in-place upgrade + // (e.g. Creator -> Pro) by swapping the subscription to the requested price. + $subscription = $this->academySubscription($user); + $currentPlan = $this->activePlan($user); + + // If current plan exists and the requested plan ranks higher, perform swap. + if ($currentPlan !== null && ($this->planRank((string) $plan['tier']) > $this->planRank((string) $currentPlan['tier']))) { + try { + if ($subscription instanceof Subscription) { + $subscription->swap((string) $plan['stripe_price_id']); + } + + return \redirect()->route('academy.billing.account')->with('success', 'Subscription upgraded — your new plan is active.'); + } catch (\Throwable $e) { + $context = [ + 'user_id' => $user->id ?? null, + 'user_email' => $user->email ?? null, + 'stripe_id' => $user->stripe_id ?? null, + 'route' => 'academy.billing.checkout', + 'attempt' => 'swap_subscription', + 'plan_key' => $plan['key'] ?? null, + 'plan_price_id' => $plan['stripe_price_id'] ?? null, + 'request_ip' => $request->ip(), + 'user_agent' => $request->header('User-Agent'), + 'exception_class' => \get_class($e), + 'exception_message' => $e->getMessage(), + 'exception_code' => $e->getCode(), + 'exception_trace' => \method_exists($e, 'getTraceAsString') ? $e->getTraceAsString() : null, + ]; + + if (method_exists($e, 'getStripeCode')) { + $context['stripe_code'] = $e->getStripeCode(); + } + + Log::error('Academy billing: failed to swap subscription for upgrade', $context); + + return $this->checkoutErrorResponse($request, $e); + } + } + return \redirect()->route('academy.billing.portal'); } @@ -133,8 +181,91 @@ final class AcademyBillingController extends Controller 'academy_tier' => (string) $plan['tier'], ], ]); + } catch (InvalidRequestException $e) { + // Stripe returned a request error (e.g. missing/deleted customer). Try to recover once by + // clearing stored `stripe_id`, recreating the customer in Stripe and retrying the checkout. + if (str_contains($e->getMessage(), 'No such customer')) { + try { + $user->forceFill(['stripe_id' => null])->save(); + + // Create a fresh Stripe customer and persist the id + if (method_exists($user, 'createAsStripeCustomer')) { + $user->createAsStripeCustomer(); + } else { + // fallback to createOrGet behavior + $user->createOrGetStripeCustomer(); + } + + return $user + ->newSubscription($this->plans->subscriptionName(), (string) $plan['stripe_price_id']) + ->withMetadata([ + 'skinbase_module' => 'academy', + 'user_id' => (string) $user->id, + 'academy_plan' => (string) $plan['key'], + 'academy_tier' => (string) $plan['tier'], + ]) + ->checkout([ + 'success_url' => \route('academy.billing.success').'?session_id={CHECKOUT_SESSION_ID}', + 'cancel_url' => \route('academy.billing.cancel'), + 'allow_promotion_codes' => true, + 'metadata' => [ + 'skinbase_module' => 'academy', + 'user_id' => (string) $user->id, + 'academy_plan' => (string) $plan['key'], + 'academy_tier' => (string) $plan['tier'], + ], + ]); + } catch (\Throwable $inner) { + $context = [ + 'user_id' => $user->id ?? null, + 'user_email' => $user->email ?? null, + 'stripe_id' => $user->stripe_id ?? null, + 'route' => 'academy.billing.checkout', + 'attempt' => 'recreate_customer_and_checkout', + 'plan_key' => $plan['key'] ?? null, + 'plan_price_id' => $plan['stripe_price_id'] ?? null, + 'request_ip' => $request->ip(), + 'user_agent' => $request->header('User-Agent'), + 'exception_class' => \get_class($inner), + 'exception_message' => $inner->getMessage(), + 'exception_code' => $inner->getCode(), + 'exception_trace' => \method_exists($inner, 'getTraceAsString') ? $inner->getTraceAsString() : null, + ]; + + if (method_exists($inner, 'getStripeCode')) { + $context['stripe_code'] = $inner->getStripeCode(); + } + + Log::error('Academy billing: failed to recover Stripe customer and start checkout', $context); + + return $this->checkoutErrorResponse($request, $inner); + } + } + + // Not a recoverable customer-missing error; rethrow to be handled below + throw $e; } catch (\Throwable $exception) { - \report($exception); + $context = [ + 'user_id' => $user->id ?? null, + 'user_email' => $user->email ?? null, + 'stripe_id' => $user->stripe_id ?? null, + 'route' => 'academy.billing.checkout', + 'attempt' => 'start_checkout', + 'plan_key' => $plan['key'] ?? null, + 'plan_price_id' => $plan['stripe_price_id'] ?? null, + 'request_ip' => $request->ip(), + 'user_agent' => $request->header('User-Agent'), + 'exception_class' => \get_class($exception), + 'exception_message' => $exception->getMessage(), + 'exception_code' => $exception->getCode(), + 'exception_trace' => \method_exists($exception, 'getTraceAsString') ? $exception->getTraceAsString() : null, + ]; + + if (method_exists($exception, 'getStripeCode')) { + $context['stripe_code'] = $exception->getStripeCode(); + } + + Log::error('Academy billing: unexpected error starting checkout', $context); return $this->checkoutErrorResponse($request, $exception); } @@ -161,7 +292,68 @@ final class AcademyBillingController extends Controller return \redirect()->route('academy.billing.account')->with('error', 'No Stripe billing profile is connected to this account yet.'); } - return $user->redirectToBillingPortal(\route('academy.billing.account')); + try { + return $user->redirectToBillingPortal(\route('academy.billing.account')); + } catch (\Exception $e) { + // If the Stripe customer was deleted or invalid, attempt a recovery similar to checkout. + if ($e instanceof \Stripe\Exception\InvalidRequestException && str_contains($e->getMessage(), 'No such customer')) { + try { + $user->forceFill(['stripe_id' => null])->save(); + + if (method_exists($user, 'createAsStripeCustomer')) { + $user->createAsStripeCustomer(); + } else { + $user->createOrGetStripeCustomer(); + } + + return $user->redirectToBillingPortal(\route('academy.billing.account')); + } catch (\Throwable $inner) { + $context = [ + 'user_id' => $user->id ?? null, + 'user_email' => $user->email ?? null, + 'stripe_id' => $user->stripe_id ?? null, + 'route' => 'academy.billing.portal', + 'attempt' => 'recreate_customer_and_redirect', + 'request_ip' => $request->ip(), + 'user_agent' => $request->header('User-Agent'), + 'exception_class' => \get_class($inner), + 'exception_message' => $inner->getMessage(), + 'exception_code' => $inner->getCode(), + 'exception_trace' => \method_exists($inner, 'getTraceAsString') ? $inner->getTraceAsString() : null, + ]; + + if (method_exists($inner, 'getStripeCode')) { + $context['stripe_code'] = $inner->getStripeCode(); + } + + Log::error('Academy billing: failed to recover Stripe customer and open billing portal', $context); + + return \redirect()->route('academy.billing.account')->with('error', 'Could not open the subscription manager. Please email academy@skinbase.org with your account details and checkout session id if available.'); + } + } + + $context = [ + 'user_id' => $user->id ?? null, + 'user_email' => $user->email ?? null, + 'stripe_id' => $user->stripe_id ?? null, + 'route' => 'academy.billing.portal', + 'attempt' => 'redirect_to_portal', + 'request_ip' => $request->ip(), + 'user_agent' => $request->header('User-Agent'), + 'exception_class' => \get_class($e), + 'exception_message' => $e->getMessage(), + 'exception_code' => $e->getCode(), + 'exception_trace' => \method_exists($e, 'getTraceAsString') ? $e->getTraceAsString() : null, + ]; + + if (method_exists($e, 'getStripeCode')) { + $context['stripe_code'] = $e->getStripeCode(); + } + + Log::error('Academy billing: could not open Stripe billing portal', $context); + + return \redirect()->route('academy.billing.account')->with('error', 'Could not open the subscription manager. Please email academy@skinbase.org with your account details and checkout session id if available.'); + } } public function success(\Illuminate\Http\Request $request): \Inertia\Response @@ -180,9 +372,10 @@ final class AcademyBillingController extends Controller 'pricing' => \route('academy.pricing'), 'account' => $user ? \route('academy.billing.account') : null, 'academy' => \route('academy.index'), + 'reportIssue' => $user ? \route('academy.billing.report_issue') : null, ], 'sessionId' => $request->query('session_id'), - ])->rootView('collections'); + ])->rootView('academy'); } public function cancel(): \Inertia\Response @@ -195,8 +388,104 @@ final class AcademyBillingController extends Controller 'pricing' => \route('academy.pricing'), 'academy' => \route('academy.index'), ], - ])->rootView('collections'); + ])->rootView('academy'); } + + public function reportIssue(\Illuminate\Http\Request $request): \Illuminate\Http\RedirectResponse + { + /** @var User|null $user */ + $user = $request->user(); + + if (! $user instanceof User) { + return redirect()->route('login'); + } + + $validated = $request->validate([ + 'message' => ['nullable', 'string', 'max:2000'], + 'session_id' => ['nullable', 'string'], + 'issue_type' => ['nullable', 'string', 'in:billing,payment,upgrade,downgrade,cancel,access,other'], + 'contact_email' => ['nullable', 'email:rfc', 'max:255'], + ]); + + $payload = [ + 'id' => (string) Str::uuid(), + 'submitted_at' => now()->toISOString(), + 'ip' => $request->ip(), + 'user_agent' => $request->userAgent(), + 'data' => [ + 'topic' => 'contact', + 'name' => (string) ($user->name ?: $user->username ?: 'Academy billing user'), + 'email' => (string) ($validated['contact_email'] ?? $user->email), + 'message' => $validated['message'] ?? null, + 'issue_type' => $validated['issue_type'] ?? 'billing', + 'session_id' => $validated['session_id'] ?? $request->query('session_id'), + 'source' => 'academy_billing', + 'user_id' => (string) $user->id, + 'account_email' => (string) $user->email, + 'current_url' => $request->fullUrl(), + ], + ]; + + try { + try { + Storage::append('staff_applications.jsonl', json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); + } catch (\Throwable $e) { + // best-effort store; do not fail the user when file storage is unavailable + } + + $application = null; + + try { + $application = StaffApplication::create([ + 'id' => $payload['id'], + 'topic' => 'contact', + 'name' => $payload['data']['name'], + 'email' => $payload['data']['email'], + 'role' => 'academy_billing_support', + 'portfolio' => null, + 'message' => $payload['data']['message'], + 'payload' => $payload, + 'ip' => $payload['ip'], + 'user_agent' => $payload['user_agent'], + ]); + } catch (\Throwable $e) { + // ignore DB errors and fall back to a lightweight model for mail + } + + $to = config('mail.from.address'); + + if ($to) { + if (! $application) { + $application = new StaffApplication([ + 'topic' => 'contact', + 'name' => $payload['data']['name'], + 'email' => $payload['data']['email'], + 'role' => 'academy_billing_support', + 'message' => $payload['data']['message'], + 'payload' => $payload, + 'ip' => $payload['ip'], + 'user_agent' => $payload['user_agent'], + ]); + $application->id = $payload['id']; + $application->created_at = now(); + } + + Mail::to($to)->send(new AcademyAccessIssue( + $user, + $payload['data']['message'] ?? null, + $payload['data']['session_id'] ?? null, + $payload['data']['issue_type'] ?? null, + $payload['data']['email'] ?? null, + )); + } + + return redirect()->back()->with('success', 'Support request sent — we will verify and activate your access shortly.'); + } catch (\Throwable $e) { + report($e); + + return redirect()->back()->with('error', 'Could not send the support request. Please try again later or email academy@skinbase.org.'); + } + } public function account(\Illuminate\Http\Request $request): \Inertia\Response { @@ -230,8 +519,10 @@ final class AcademyBillingController extends Controller 'portal' => \route('academy.billing.portal'), 'pricing' => \route('academy.pricing'), 'academy' => \route('academy.index'), + 'checkout' => \route('academy.billing.checkout'), + 'reportIssue' => \route('academy.billing.report_issue'), ], - ])->rootView('collections'); + ])->rootView('academy'); } /** @@ -279,6 +570,7 @@ final class AcademyBillingController extends Controller 'price_display' => $plan['price_display'], 'configured' => $plan['configured'], 'price_id_valid' => $plan['price_id_valid'], + 'remote_price_exists' => $plan['remote_price_exists'] ?? false, ]] : []; return [ @@ -419,4 +711,4 @@ final class AcademyBillingController extends Controller return \redirect()->route('academy.pricing')->with('error', $message); } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Academy/AcademyChallengeController.php b/app/Http/Controllers/Academy/AcademyChallengeController.php index 2b340608..95709855 100644 --- a/app/Http/Controllers/Academy/AcademyChallengeController.php +++ b/app/Http/Controllers/Academy/AcademyChallengeController.php @@ -64,7 +64,7 @@ final class AcademyChallengeController extends Controller 'isGuest' => $request->user() === null, 'isSubscriber' => $request->user()?->hasAcademyCreatorAccess() || $request->user()?->hasAcademyProAccess(), ], - ])->rootView('collections'); + ])->rootView('academy'); } public function show(Request $request, string $slug): Response @@ -126,6 +126,6 @@ final class AcademyChallengeController extends Controller 'isSubscriber' => $request->user()?->hasAcademyCreatorAccess() || $request->user()?->hasAcademyProAccess(), 'isLocked' => (bool) ($payload['locked'] ?? false), ], - ])->rootView('collections'); + ])->rootView('academy'); } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Academy/AcademyChallengeSubmissionController.php b/app/Http/Controllers/Academy/AcademyChallengeSubmissionController.php index a1ba56f1..1669ac7b 100644 --- a/app/Http/Controllers/Academy/AcademyChallengeSubmissionController.php +++ b/app/Http/Controllers/Academy/AcademyChallengeSubmissionController.php @@ -47,7 +47,7 @@ final class AcademyChallengeSubmissionController extends Controller 'published_at' => $artwork->published_at?->toISOString(), ])->values()->all(), 'submitUrl' => route('academy.challenges.submit.store', ['slug' => $challenge->slug]), - ])->rootView('collections'); + ])->rootView('academy'); } public function store(StoreAcademyChallengeSubmissionRequest $request, string $slug): RedirectResponse @@ -63,4 +63,4 @@ final class AcademyChallengeSubmissionController extends Controller return redirect()->route('academy.challenges.show', ['slug' => $challenge->slug]) ->with('success', 'Challenge submission received and queued for review.'); } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Academy/AcademyCourseController.php b/app/Http/Controllers/Academy/AcademyCourseController.php index 7e6d1569..c5c3f539 100644 --- a/app/Http/Controllers/Academy/AcademyCourseController.php +++ b/app/Http/Controllers/Academy/AcademyCourseController.php @@ -102,7 +102,7 @@ final class AcademyCourseController extends Controller 'isGuest' => $request->user() === null, 'isSubscriber' => $request->user()?->hasAcademyCreatorAccess() || $request->user()?->hasAcademyProAccess(), ], - ])->rootView('collections'); + ])->rootView('academy'); } public function show(Request $request, AcademyCourse $course): Response @@ -218,6 +218,6 @@ final class AcademyCourseController extends Controller 'isSubscriber' => $request->user()?->hasAcademyCreatorAccess() || $request->user()?->hasAcademyProAccess(), 'isLocked' => false, ], - ])->rootView('collections'); + ])->rootView('academy'); } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Academy/AcademyCourseLessonController.php b/app/Http/Controllers/Academy/AcademyCourseLessonController.php index 4935ae65..0c29ee50 100644 --- a/app/Http/Controllers/Academy/AcademyCourseLessonController.php +++ b/app/Http/Controllers/Academy/AcademyCourseLessonController.php @@ -119,6 +119,6 @@ final class AcademyCourseLessonController extends Controller ], 'outline' => $courseOutline, ], - ])->rootView('collections'); + ])->rootView('academy'); } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Academy/AcademyHomeController.php b/app/Http/Controllers/Academy/AcademyHomeController.php index 94849757..4e943f57 100644 --- a/app/Http/Controllers/Academy/AcademyHomeController.php +++ b/app/Http/Controllers/Academy/AcademyHomeController.php @@ -98,6 +98,6 @@ final class AcademyHomeController extends Controller 'isGuest' => $request->user() === null, 'isSubscriber' => $request->user()?->hasAcademyCreatorAccess() || $request->user()?->hasAcademyProAccess(), ], - ])->rootView('collections'); + ])->rootView('academy'); } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Academy/AcademyLessonController.php b/app/Http/Controllers/Academy/AcademyLessonController.php index 80749c7a..489865a3 100644 --- a/app/Http/Controllers/Academy/AcademyLessonController.php +++ b/app/Http/Controllers/Academy/AcademyLessonController.php @@ -112,7 +112,7 @@ final class AcademyLessonController extends Controller 'isGuest' => $request->user() === null, 'isSubscriber' => $request->user()?->hasAcademyCreatorAccess() || $request->user()?->hasAcademyProAccess(), ], - ])->rootView('collections'); + ])->rootView('academy'); } public function show(Request $request, string $slug): Response @@ -220,6 +220,6 @@ final class AcademyLessonController extends Controller 'isSubscriber' => $request->user()?->hasAcademyCreatorAccess() || $request->user()?->hasAcademyProAccess(), 'isLocked' => (bool) ($payload['locked'] ?? false), ], - ])->rootView('collections'); + ])->rootView('academy'); } } diff --git a/app/Http/Controllers/Academy/AcademyPricingController.php b/app/Http/Controllers/Academy/AcademyPricingController.php index 7353d39d..2101fd6f 100644 --- a/app/Http/Controllers/Academy/AcademyPricingController.php +++ b/app/Http/Controllers/Academy/AcademyPricingController.php @@ -79,6 +79,6 @@ final class AcademyPricingController extends Controller 'isGuest' => $request->user() === null, 'isSubscriber' => $request->user()?->hasAcademyCreatorAccess() || $request->user()?->hasAcademyProAccess(), ], - ])->rootView('collections'); + ])->rootView('academy'); } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Academy/AcademyPromptController.php b/app/Http/Controllers/Academy/AcademyPromptController.php index 9b842445..bb242d69 100644 --- a/app/Http/Controllers/Academy/AcademyPromptController.php +++ b/app/Http/Controllers/Academy/AcademyPromptController.php @@ -126,7 +126,7 @@ final class AcademyPromptController extends Controller 'isGuest' => $request->user() === null, 'isSubscriber' => $request->user()?->hasAcademyCreatorAccess() || $request->user()?->hasAcademyProAccess(), ], - ])->rootView('collections'); + ])->rootView('academy'); } public function popular(Request $request): Response @@ -260,7 +260,7 @@ final class AcademyPromptController extends Controller 'isGuest' => $request->user() === null, 'isSubscriber' => $request->user()?->hasAcademyCreatorAccess() || $request->user()?->hasAcademyProAccess(), ], - ])->rootView('collections'); + ])->rootView('academy'); } /** @@ -366,7 +366,7 @@ final class AcademyPromptController extends Controller 'isSubscriber' => $request->user()?->hasAcademyCreatorAccess() || $request->user()?->hasAcademyProAccess(), 'isLocked' => (bool) ($payload['locked'] ?? false), ], - ])->rootView('collections'); + ])->rootView('academy'); } /** @@ -466,4 +466,4 @@ final class AcademyPromptController extends Controller ->values() ->all(); } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Academy/AcademyPromptPackController.php b/app/Http/Controllers/Academy/AcademyPromptPackController.php index 4db0c5be..2ffec7a1 100644 --- a/app/Http/Controllers/Academy/AcademyPromptPackController.php +++ b/app/Http/Controllers/Academy/AcademyPromptPackController.php @@ -64,7 +64,7 @@ final class AcademyPromptPackController extends Controller 'isGuest' => $request->user() === null, 'isSubscriber' => $request->user()?->hasAcademyCreatorAccess() || $request->user()?->hasAcademyProAccess(), ], - ])->rootView('collections'); + ])->rootView('academy'); } public function show(Request $request, string $slug): Response @@ -110,6 +110,6 @@ final class AcademyPromptPackController extends Controller 'isSubscriber' => $request->user()?->hasAcademyCreatorAccess() || $request->user()?->hasAcademyProAccess(), 'isLocked' => (bool) ($payload['locked'] ?? false), ], - ])->rootView('collections'); + ])->rootView('academy'); } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Api/ArtworkTagController.php b/app/Http/Controllers/Api/ArtworkTagController.php index dda3b9cd..559cddef 100644 --- a/app/Http/Controllers/Api/ArtworkTagController.php +++ b/app/Http/Controllers/Api/ArtworkTagController.php @@ -30,6 +30,7 @@ final class ArtworkTagController extends Controller $queueConnection = (string) config('queue.default', 'sync'); $visionEnabled = (bool) config('vision.enabled', true); + $autoTaggingEnabled = (bool) config('vision.auto_tagging.enabled', false); $queuedCount = 0; $failedCount = 0; @@ -56,7 +57,7 @@ final class ArtworkTagController extends Controller $triggered = false; $shouldTrigger = request()->boolean('trigger', false); - if ($shouldTrigger && $visionEnabled && ! empty($artwork->hash) && $queuedCount === 0) { + if ($shouldTrigger && $visionEnabled && $autoTaggingEnabled && ! empty($artwork->hash) && $queuedCount === 0) { AutoTagArtworkJob::dispatch((int) $artwork->id, (string) $artwork->hash); $triggered = true; $queuedCount = max(1, $queuedCount); @@ -89,6 +90,7 @@ final class ArtworkTagController extends Controller 'queued_jobs' => $queuedCount, 'failed_jobs' => $failedCount, 'triggered' => $triggered, + 'auto_tagging_enabled' => $autoTaggingEnabled, 'ai_tag_count' => (int) $tags->where('is_ai', true)->count(), 'total_tag_count' => (int) $tags->count(), ], diff --git a/app/Http/Controllers/Api/UploadController.php b/app/Http/Controllers/Api/UploadController.php index 24e0b8d8..bdbed06e 100644 --- a/app/Http/Controllers/Api/UploadController.php +++ b/app/Http/Controllers/Api/UploadController.php @@ -239,10 +239,18 @@ final class UploadController extends Controller } // Derivatives are available now; dispatch AI auto-tagging. - AutoTagArtworkJob::dispatch($artworkId, $validated->hash)->afterCommit(); - DetectArtworkMaturityJob::dispatch($artworkId, $validated->hash)->afterCommit(); - GenerateArtworkEmbeddingJob::dispatch($artworkId, $validated->hash)->afterCommit(); - AnalyzeArtworkAiAssistJob::dispatch($artworkId)->afterCommit(); + if ((bool) config('vision.auto_tagging.enabled', false)) { + AutoTagArtworkJob::dispatch($artworkId, $validated->hash)->afterCommit(); + } + if ((bool) config('vision.upload.maturity.enabled', false)) { + DetectArtworkMaturityJob::dispatch($artworkId, $validated->hash)->afterCommit(); + } + if ((bool) config('vision.upload.embeddings.enabled', true)) { + GenerateArtworkEmbeddingJob::dispatch($artworkId, $validated->hash)->afterCommit(); + } + if ((bool) config('vision.upload.ai_assist.enabled', false)) { + AnalyzeArtworkAiAssistJob::dispatch($artworkId)->afterCommit(); + } return UploadSessionStatus::PROCESSED; }); diff --git a/app/Http/Controllers/Moderation/StaffApplicationsController.php b/app/Http/Controllers/Moderation/StaffApplicationsController.php new file mode 100644 index 00000000..de1c2f4e --- /dev/null +++ b/app/Http/Controllers/Moderation/StaffApplicationsController.php @@ -0,0 +1,98 @@ + trim((string) $request->query('q', '')), + 'topic' => trim((string) $request->query('topic', 'all')), + ]; + + $query = StaffApplication::query()->latest('created_at'); + + if ($filters['q'] !== '') { + $search = $filters['q']; + $query->where(function ($builder) use ($search): void { + $builder + ->where('name', 'like', '%' . $search . '%') + ->orWhere('email', 'like', '%' . $search . '%') + ->orWhere('role', 'like', '%' . $search . '%') + ->orWhere('message', 'like', '%' . $search . '%'); + }); + } + + if ($filters['topic'] !== '' && $filters['topic'] !== 'all') { + $query->where('topic', $filters['topic']); + } + + $items = $query + ->paginate(20) + ->withQueryString() + ->through(fn (StaffApplication $application): array => $this->serializeApplication($application)); + + $stats = [ + 'total' => StaffApplication::query()->count(), + 'applications' => StaffApplication::query()->where('topic', 'application')->count(), + 'bug' => StaffApplication::query()->where('topic', 'bug')->count(), + 'contact' => StaffApplication::query()->where('topic', 'contact')->count(), + 'other' => StaffApplication::query()->whereNotIn('topic', ['application', 'bug', 'contact'])->count(), + ]; + + $topics = StaffApplication::query() + ->select('topic') + ->distinct() + ->orderBy('topic') + ->pluck('topic') + ->values() + ->all(); + + return Inertia::render('Moderation/StaffApplications/Index', [ + 'title' => 'Staff Applications', + 'items' => $items, + 'filters' => $filters, + 'stats' => $stats, + 'topics' => $topics, + 'endpoints' => [ + 'index' => route('admin.staff-applications.index'), + ], + ])->rootView('moderation'); + } + + public function show(StaffApplication $staffApplication): Response + { + return Inertia::render('Moderation/StaffApplications/Show', [ + 'title' => 'Staff Application', + 'item' => $this->serializeApplication($staffApplication, true), + 'backUrl' => route('admin.staff-applications.index'), + ])->rootView('moderation'); + } + + private function serializeApplication(StaffApplication $application, bool $detailed = false): array + { + return [ + 'id' => (string) $application->id, + 'topic' => (string) ($application->topic ?: 'contact'), + 'name' => (string) ($application->name ?: 'Unknown'), + 'email' => (string) ($application->email ?: ''), + 'role' => $application->role, + 'portfolio' => $application->portfolio, + 'message' => $application->message, + 'ip' => $application->ip, + 'user_agent' => $application->user_agent, + 'created_at' => optional($application->created_at)?->toIso8601String(), + 'payload' => $detailed ? ($application->payload ?? []) : [], + 'show_url' => route('admin.staff-applications.show', ['staffApplication' => $application]), + ]; + } +} diff --git a/app/Http/Controllers/Moderation/StoriesController.php b/app/Http/Controllers/Moderation/StoriesController.php new file mode 100644 index 00000000..3014c7fa --- /dev/null +++ b/app/Http/Controllers/Moderation/StoriesController.php @@ -0,0 +1,105 @@ + trim((string) $request->query('q', '')), + 'status' => trim((string) $request->query('status', 'all')), + ]; + + $query = Story::query() + ->with('creator:id,name,username') + ->latest('created_at') + ->latest('id'); + + if ($filters['q'] !== '') { + $search = $filters['q']; + $query->where(function ($builder) use ($search): void { + $builder + ->where('title', 'like', '%' . $search . '%') + ->orWhere('slug', 'like', '%' . $search . '%') + ->orWhereHas('creator', function ($creatorQuery) use ($search): void { + $creatorQuery + ->where('name', 'like', '%' . $search . '%') + ->orWhere('username', 'like', '%' . $search . '%'); + }); + }); + } + + if ($filters['status'] !== '' && $filters['status'] !== 'all') { + $query->where('status', $filters['status']); + } + + $stories = $query + ->paginate(24) + ->withQueryString() + ->through(fn (Story $story): array => $this->serializeStory($story)); + + $statsQuery = Story::query(); + if ($filters['q'] !== '') { + $search = $filters['q']; + $statsQuery->where(function ($builder) use ($search): void { + $builder + ->where('title', 'like', '%' . $search . '%') + ->orWhere('slug', 'like', '%' . $search . '%') + ->orWhereHas('creator', function ($creatorQuery) use ($search): void { + $creatorQuery + ->where('name', 'like', '%' . $search . '%') + ->orWhere('username', 'like', '%' . $search . '%'); + }); + }); + } + + $stats = [ + 'total' => (clone $statsQuery)->count(), + 'published' => (clone $statsQuery)->where('status', 'published')->count(), + 'draft' => (clone $statsQuery)->where('status', 'draft')->count(), + 'scheduled' => (clone $statsQuery)->where('status', 'scheduled')->count(), + 'pending_review' => (clone $statsQuery)->where('status', 'pending_review')->count(), + 'archived' => (clone $statsQuery)->where('status', 'archived')->count(), + ]; + + return Inertia::render('Moderation/Stories', [ + 'title' => 'Stories', + 'stories' => $stories, + 'filters' => $filters, + 'stats' => $stats, + 'endpoints' => [ + 'index' => route('admin.stories'), + ], + ])->rootView('moderation'); + } + + private function serializeStory(Story $story): array + { + return [ + 'id' => (int) $story->id, + 'title' => (string) ($story->title ?: 'Untitled story'), + 'slug' => (string) $story->slug, + 'excerpt' => $story->excerpt, + 'status' => (string) ($story->status ?: 'draft'), + 'published_at' => optional($story->published_at)?->toIso8601String(), + 'created_at' => optional($story->created_at)?->toIso8601String(), + 'cover_url' => $story->coverUrl, + 'public_url' => $story->url, + 'open_url' => $story->status === 'published' ? $story->url : null, + 'creator' => $story->creator ? [ + 'id' => (int) $story->creator->id, + 'name' => (string) $story->creator->name, + 'username' => (string) $story->creator->username, + ] : null, + ]; + } +} diff --git a/app/Http/Controllers/Moderation/UsernameQueueController.php b/app/Http/Controllers/Moderation/UsernameQueueController.php new file mode 100644 index 00000000..65d5f282 --- /dev/null +++ b/app/Http/Controllers/Moderation/UsernameQueueController.php @@ -0,0 +1,116 @@ + trim((string) $request->query('q', '')), + 'status' => trim((string) $request->query('status', 'pending')), + ]; + + $requestColumns = Schema::hasTable('username_approval_requests') + ? Schema::getColumnListing('username_approval_requests') + : []; + + $query = DB::table('username_approval_requests as requests') + ->leftJoin('users', 'users.id', '=', 'requests.user_id') + ->select([ + 'requests.id', + 'requests.user_id', + 'requests.requested_username', + 'requests.status', + 'requests.context', + 'requests.similar_to', + 'requests.review_note', + 'requests.reviewed_at', + 'requests.created_at', + 'users.username as current_username', + 'users.name as current_name', + ]) + ->orderByDesc('requests.created_at'); + + if ($filters['status'] !== '' && $filters['status'] !== 'all') { + $query->where('requests.status', $filters['status']); + } + + if ($filters['q'] !== '') { + $search = $filters['q']; + $query->where(function ($builder) use ($search): void { + $builder + ->where('requests.requested_username', 'like', '%' . $search . '%') + ->orWhere('requests.context', 'like', '%' . $search . '%') + ->orWhere('users.username', 'like', '%' . $search . '%') + ->orWhere('users.name', 'like', '%' . $search . '%'); + }); + } + + $requests = $query->paginate(20)->withQueryString()->through(function ($row): array { + return [ + 'id' => (int) $row->id, + 'user_id' => $row->user_id !== null ? (int) $row->user_id : null, + 'requested_username' => (string) $row->requested_username, + 'status' => (string) ($row->status ?? 'pending'), + 'context' => $row->context ?? null, + 'similar_to' => $row->similar_to ?? null, + 'review_note' => $row->review_note ?? null, + 'reviewed_at' => $this->serializeTimestamp($row->reviewed_at ?? null), + 'created_at' => $this->serializeTimestamp($row->created_at ?? null), + 'current_username' => $row->current_username, + 'current_name' => $row->current_name, + 'approve_url' => route('api.admin.usernames.approve', ['id' => $row->id]), + 'reject_url' => route('api.admin.usernames.reject', ['id' => $row->id]), + ]; + }); + + $stats = [ + 'total' => Schema::hasTable('username_approval_requests') ? DB::table('username_approval_requests')->count() : 0, + 'pending' => Schema::hasTable('username_approval_requests') ? DB::table('username_approval_requests')->where('status', 'pending')->count() : 0, + 'approved' => Schema::hasTable('username_approval_requests') ? DB::table('username_approval_requests')->where('status', 'approved')->count() : 0, + 'rejected' => Schema::hasTable('username_approval_requests') ? DB::table('username_approval_requests')->where('status', 'rejected')->count() : 0, + ]; + + return Inertia::render('Moderation/UsernameQueue', [ + 'title' => 'Username Queue', + 'requests' => $requests, + 'stats' => $stats, + 'filters' => $filters, + 'options' => [ + 'statuses' => [ + ['value' => 'all', 'label' => 'All statuses'], + ['value' => 'pending', 'label' => 'Pending'], + ['value' => 'approved', 'label' => 'Approved'], + ['value' => 'rejected', 'label' => 'Rejected'], + ], + ], + 'endpoints' => [ + 'index' => route('admin.usernames'), + 'refresh' => route('admin.usernames'), + ], + ])->rootView('moderation'); + } + + private function serializeTimestamp(mixed $value): ?string + { + if ($value === null || $value === '') { + return null; + } + + try { + return \Illuminate\Support\Carbon::parse((string) $value)->toIso8601String(); + } catch (\Throwable) { + return null; + } + } +} diff --git a/app/Http/Controllers/Settings/AcademyAdminController.php b/app/Http/Controllers/Settings/AcademyAdminController.php index 6b52ed8c..2531243b 100644 --- a/app/Http/Controllers/Settings/AcademyAdminController.php +++ b/app/Http/Controllers/Settings/AcademyAdminController.php @@ -17,6 +17,7 @@ use App\Models\AcademyBadge; use App\Models\AcademyCategory; use App\Models\AcademyChallenge; use App\Models\AcademyChallengeSubmission; +use App\Models\AcademyContentMetricDaily; use App\Models\AcademyCourse; use App\Models\AcademyCourseLesson; use App\Models\AcademyCourseSection; @@ -31,6 +32,7 @@ use App\Services\Academy\AcademyAdminBillingOverviewService; use App\Services\Academy\AcademyCacheService; use App\Services\Academy\AcademyCourseLessonOrderingService; use App\Services\Academy\AcademyLessonMarkdownRenderer; +use App\Support\AcademyAnalytics\AcademyAnalyticsContentType; use Illuminate\Database\Eloquent\Model; use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; @@ -604,34 +606,48 @@ final class AcademyAdminController extends Controller $meta = $this->resourceMeta($resource); $search = trim((string) request()->query('search', '')); $query = $meta['model']::query(); + $filters = [ + 'search' => $search, + ]; + $summary = null; if ($resource === 'courses') { $query->withCount('courseLessons'); if ($search !== '') { - $query->where(function ($builder) use ($search): void { - $like = '%'.str_replace(['%', '_'], ['\\%', '\\_'], $search).'%'; - - $builder->where('title', 'like', $like) - ->orWhere('slug', 'like', $like) - ->orWhere('subtitle', 'like', $like) - ->orWhere('excerpt', 'like', $like) - ->orWhere('description', 'like', $like); - }); + $this->applyCourseAdminSearch($query, $search); } $query->orderByDesc('is_featured') ->orderBy('order_num') ->orderByDesc('updated_at') ->orderByDesc('id'); + } elseif ($resource === 'prompts') { + $query->with('category'); + $query->withSum(['metrics as total_views' => function ($builder): void { + $builder->where('content_type', AcademyAnalyticsContentType::PROMPT); + }], 'views'); + $promptFilters = [ + 'category' => (string) request()->query('category', 'all'), + 'featured' => (string) request()->query('featured', 'all'), + 'prompt_of_week' => (string) request()->query('prompt_of_week', 'all'), + 'active' => (string) request()->query('active', 'all'), + 'access_level' => (string) request()->query('access_level', 'all'), + 'difficulty' => (string) request()->query('difficulty', 'all'), + 'order' => (string) request()->query('order', 'updated_desc'), + ]; + + $filters = array_merge($filters, $promptFilters); + + $this->applyPromptAdminSearch($query, $search); + $this->applyPromptAdminFilters($query, $promptFilters); + $this->applyPromptAdminOrdering($query, $promptFilters['order']); + + $summary = $this->promptAdminSummary($search, $promptFilters); } else { $query->latest('updated_at'); } - if ($resource === 'prompts') { - $query->with('category'); - } - if ($resource === 'lessons') { $query->with('courses:id,title'); } @@ -646,48 +662,191 @@ final class AcademyAdminController extends Controller 'items' => $items, 'columns' => $meta['columns'], 'createUrl' => route($meta['route_base'].'.create'), - 'filters' => [ - 'search' => $search, - ], + 'filters' => $filters, + 'filterOptions' => $resource === 'prompts' ? [ + 'categories' => $this->promptAdminCategoryFilterOptions(), + 'difficulty' => $this->filterOptionsWithAll($this->difficultyOptions(), 'All difficulties'), + 'access' => $this->filterOptionsWithAll($this->accessOptions(), 'All access levels'), + 'featured' => [ + ['value' => 'all', 'label' => 'Any featured state'], + ['value' => 'yes', 'label' => 'Featured only'], + ['value' => 'no', 'label' => 'Not featured'], + ], + 'promptOfWeek' => [ + ['value' => 'all', 'label' => 'Any weekly state'], + ['value' => 'yes', 'label' => 'Prompt of the week'], + ['value' => 'no', 'label' => 'Not prompt of the week'], + ], + 'active' => [ + ['value' => 'all', 'label' => 'Any visibility state'], + ['value' => 'active', 'label' => 'Active only'], + ['value' => 'inactive', 'label' => 'Inactive only'], + ], + 'order' => [ + ['value' => 'updated_desc', 'label' => 'Updated newest'], + ['value' => 'updated_asc', 'label' => 'Updated oldest'], + ['value' => 'views_desc', 'label' => 'Most viewed'], + ['value' => 'title_asc', 'label' => 'Title A-Z'], + ['value' => 'title_desc', 'label' => 'Title Z-A'], + ['value' => 'access_asc', 'label' => 'Access'], + ['value' => 'difficulty_asc', 'label' => 'Difficulty'], + ['value' => 'featured_desc', 'label' => 'Featured first'], + ], + ] : null, 'summary' => $resource === 'courses' ? [ 'total' => (int) $items->total(), - 'published' => (int) (clone $meta['model']::query())->when($search !== '', function ($builder) use ($search): void { - $like = '%'.str_replace(['%', '_'], ['\\%', '\\_'], $search).'%'; - - $builder->where(function ($inner) use ($like): void { - $inner->where('title', 'like', $like) - ->orWhere('slug', 'like', $like) - ->orWhere('subtitle', 'like', $like) - ->orWhere('excerpt', 'like', $like) - ->orWhere('description', 'like', $like); - }); - })->where('status', AcademyCourse::STATUS_PUBLISHED)->count(), - 'featured' => (int) (clone $meta['model']::query())->when($search !== '', function ($builder) use ($search): void { - $like = '%'.str_replace(['%', '_'], ['\\%', '\\_'], $search).'%'; - - $builder->where(function ($inner) use ($like): void { - $inner->where('title', 'like', $like) - ->orWhere('slug', 'like', $like) - ->orWhere('subtitle', 'like', $like) - ->orWhere('excerpt', 'like', $like) - ->orWhere('description', 'like', $like); - }); - })->where('is_featured', true)->count(), - 'drafts' => (int) (clone $meta['model']::query())->when($search !== '', function ($builder) use ($search): void { - $like = '%'.str_replace(['%', '_'], ['\\%', '\\_'], $search).'%'; - - $builder->where(function ($inner) use ($like): void { - $inner->where('title', 'like', $like) - ->orWhere('slug', 'like', $like) - ->orWhere('subtitle', 'like', $like) - ->orWhere('excerpt', 'like', $like) - ->orWhere('description', 'like', $like); - }); - })->where('status', AcademyCourse::STATUS_DRAFT)->count(), - ] : null, + 'published' => (int) (clone $meta['model']::query())->tap(fn ($builder) => $this->applyCourseAdminSearch($builder, $search))->where('status', AcademyCourse::STATUS_PUBLISHED)->count(), + 'featured' => (int) (clone $meta['model']::query())->tap(fn ($builder) => $this->applyCourseAdminSearch($builder, $search))->where('is_featured', true)->count(), + 'drafts' => (int) (clone $meta['model']::query())->tap(fn ($builder) => $this->applyCourseAdminSearch($builder, $search))->where('status', AcademyCourse::STATUS_DRAFT)->count(), + ] : $summary, ]); } + private function applyCourseAdminSearch($query, string $search): void + { + if ($search === '') { + return; + } + + $like = '%'.str_replace(['%', '_'], ['\\%', '\\_'], $search).'%'; + + $query->where(function ($builder) use ($like): void { + $builder->where('title', 'like', $like) + ->orWhere('slug', 'like', $like) + ->orWhere('subtitle', 'like', $like) + ->orWhere('excerpt', 'like', $like) + ->orWhere('description', 'like', $like); + }); + } + + private function applyPromptAdminSearch($query, string $search): void + { + if ($search === '') { + return; + } + + $like = '%'.str_replace(['%', '_'], ['\\%', '\\_'], $search).'%'; + + $query->where(function ($builder) use ($like): void { + $builder->where('title', 'like', $like) + ->orWhere('slug', 'like', $like) + ->orWhere('excerpt', 'like', $like) + ->orWhere('prompt', 'like', $like) + ->orWhere('negative_prompt', 'like', $like) + ->orWhere('usage_notes', 'like', $like) + ->orWhere('workflow_notes', 'like', $like) + ->orWhereHas('category', function ($categoryQuery) use ($like): void { + $categoryQuery->where('name', 'like', $like); + }); + }); + } + + private function applyPromptAdminFilters($query, array $filters, bool $includeAccessFilter = true): void + { + $category = (string) ($filters['category'] ?? 'all'); + $featured = (string) ($filters['featured'] ?? 'all'); + $promptOfWeek = (string) ($filters['prompt_of_week'] ?? 'all'); + $active = (string) ($filters['active'] ?? 'all'); + $accessLevel = (string) ($filters['access_level'] ?? 'all'); + $difficulty = (string) ($filters['difficulty'] ?? 'all'); + + if ($category === 'uncategorized') { + $query->whereNull('category_id'); + } elseif ($category !== '' && $category !== 'all' && ctype_digit($category)) { + $query->where('category_id', (int) $category); + } + + if ($featured === 'yes') { + $query->where('featured', true); + } elseif ($featured === 'no') { + $query->where('featured', false); + } + + if ($promptOfWeek === 'yes') { + $query->where('prompt_of_week', true); + } elseif ($promptOfWeek === 'no') { + $query->where('prompt_of_week', false); + } + + if ($active === 'active') { + $query->where('active', true); + } elseif ($active === 'inactive') { + $query->where('active', false); + } + + if ($includeAccessFilter && in_array($accessLevel, ['free', 'creator', 'pro'], true)) { + $query->where('access_level', $accessLevel); + } + + if (in_array($difficulty, array_column($this->difficultyOptions(), 'value'), true)) { + $query->where('difficulty', $difficulty); + } + } + + private function applyPromptAdminOrdering($query, string $order): void + { + match ($order) { + 'updated_asc' => $query->orderBy('updated_at')->orderBy('id'), + 'views_desc' => $query->orderByDesc('total_views')->orderByDesc('updated_at')->orderByDesc('id'), + 'title_asc' => $query->orderBy('title')->orderByDesc('updated_at'), + 'title_desc' => $query->orderByDesc('title')->orderByDesc('updated_at'), + 'access_asc' => $query->orderByRaw("FIELD(access_level, 'free', 'creator', 'pro')")->orderBy('title'), + 'difficulty_asc' => $query->orderBy('difficulty')->orderBy('title'), + 'featured_desc' => $query->orderByDesc('featured')->orderByDesc('prompt_of_week')->orderBy('title'), + default => $query->orderByDesc('updated_at')->orderByDesc('id'), + }; + } + + private function promptAdminSummary(string $search, array $filters): array + { + $summaryQuery = AcademyPromptTemplate::query(); + $accessSummaryQuery = AcademyPromptTemplate::query(); + + $this->applyPromptAdminSearch($summaryQuery, $search); + $this->applyPromptAdminFilters($summaryQuery, $filters); + + $this->applyPromptAdminSearch($accessSummaryQuery, $search); + $this->applyPromptAdminFilters($accessSummaryQuery, $filters, false); + + return [ + 'total' => (int) $summaryQuery->count(), + 'active' => (int) (clone $summaryQuery)->where('active', true)->count(), + 'featured' => (int) (clone $summaryQuery)->where('featured', true)->count(), + 'promptOfWeek' => (int) (clone $summaryQuery)->where('prompt_of_week', true)->count(), + 'access' => [ + 'free' => (int) (clone $accessSummaryQuery)->where('access_level', 'free')->count(), + 'creator' => (int) (clone $accessSummaryQuery)->where('access_level', 'creator')->count(), + 'pro' => (int) (clone $accessSummaryQuery)->where('access_level', 'pro')->count(), + ], + ]; + } + + private function promptAdminCategoryFilterOptions(): array + { + return AcademyCategory::query() + ->where('type', 'prompt') + ->orderBy('order_num') + ->orderBy('name') + ->get() + ->map(fn (AcademyCategory $category): array => ['value' => (string) $category->id, 'label' => $category->name]) + ->prepend(['value' => 'uncategorized', 'label' => 'Uncategorized']) + ->prepend(['value' => 'all', 'label' => 'All categories']) + ->values() + ->all(); + } + + private function filterOptionsWithAll(array $options, string $allLabel): array + { + return collect($options) + ->map(fn (array $option): array => [ + 'value' => (string) ($option['value'] ?? ''), + 'label' => (string) ($option['label'] ?? $option['value'] ?? ''), + ]) + ->prepend(['value' => 'all', 'label' => $allLabel]) + ->values() + ->all(); + } + private function renderForm(string $resource, Model $record): Response { $meta = $this->resourceMeta($resource); @@ -893,7 +1052,7 @@ final class AcademyAdminController extends Controller 'singular' => 'prompt template', 'subtitle' => 'Manage prompt previews, premium prompts, and prompt of the week.', 'route_base' => 'admin.academy.prompts', - 'columns' => ['title', 'difficulty', 'access_level', 'prompt_of_week', 'active'], + 'columns' => ['title', 'category_name', 'difficulty', 'access_level', 'prompt_of_week', 'active'], 'fields' => [ ['name' => 'category_id', 'label' => 'Category', 'type' => 'select', 'options' => $this->categoryOptions('prompt')], ['name' => 'title', 'label' => 'Title', 'type' => 'text'], @@ -907,6 +1066,7 @@ final class AcademyAdminController extends Controller ['name' => 'placeholders', 'label' => 'Placeholders JSON', 'type' => 'json'], ['name' => 'helper_prompts', 'label' => 'Helper Prompts JSON', 'type' => 'json'], ['name' => 'prompt_variants', 'label' => 'Prompt Variants JSON', 'type' => 'json'], + ['name' => 'filled_examples', 'label' => 'Filled Examples JSON', 'type' => 'json'], ['name' => 'difficulty', 'label' => 'Difficulty', 'type' => 'select', 'options' => $this->difficultyOptions()], ['name' => 'access_level', 'label' => 'Access', 'type' => 'select', 'options' => $this->accessOptions()], ['name' => 'aspect_ratio', 'label' => 'Aspect Ratio', 'type' => 'text'], @@ -1048,6 +1208,7 @@ final class AcademyAdminController extends Controller 'active' => (bool) $model->active, 'preview_image_url' => $this->resolvePromptPreviewImageUrl((string) ($model->preview_image ?? '')), 'comparisons_count' => count($this->serializePromptToolNotes((array) ($model->tool_notes ?? []))), + 'views_count' => (int) ($model->total_views ?? 0), 'tags' => array_values(array_filter(array_map(static fn ($tag): string => trim((string) $tag), (array) ($model->tags ?? [])))), 'updated_at' => optional($model->updated_at)->toIso8601String(), 'preview_url' => route('academy.prompts.show', ['slug' => $model->slug]), @@ -1167,6 +1328,7 @@ final class AcademyAdminController extends Controller 'placeholders' => $this->encodePrettyJsonForForm($record->placeholders), 'helper_prompts' => $this->encodePrettyJsonForForm($record->helper_prompts), 'prompt_variants' => $this->encodePrettyJsonForForm($record->prompt_variants), + 'filled_examples' => $this->encodePrettyJsonForForm($record->filled_examples), 'difficulty' => (string) ($record->difficulty ?? 'beginner'), 'access_level' => (string) ($record->access_level ?? 'free'), 'aspect_ratio' => (string) ($record->aspect_ratio ?? ''), @@ -2174,6 +2336,7 @@ final class AcademyAdminController extends Controller $validated['placeholders'] = $this->normalizePromptPlaceholders($validated['placeholders'] ?? null); $validated['helper_prompts'] = $this->normalizePromptHelperPrompts($validated['helper_prompts'] ?? null); $validated['prompt_variants'] = $this->normalizePromptVariants($validated['prompt_variants'] ?? null); + $validated['filled_examples'] = $this->normalizePromptFilledExamples($validated['filled_examples'] ?? null); $validated['tool_notes'] = $this->normalizePromptToolNotes((array) ($validated['tool_notes'] ?? [])); $previousToolNotes = $this->normalizePromptToolNotes((array) ($prompt?->tool_notes ?? [])); @@ -2382,6 +2545,56 @@ final class AcademyAdminController extends Controller ->all(); } + /** + * @return array> + */ + private function normalizePromptFilledExamples(mixed $filledExamples): array + { + if (! is_array($filledExamples)) { + return []; + } + + return collect($filledExamples) + ->filter(static fn ($example): bool => is_array($example)) + ->map(function (array $example): array { + return [ + 'title' => $this->nullableTrimmedString($example['title'] ?? null), + 'description' => $this->nullableTrimmedString($example['description'] ?? null), + 'placeholder_values' => collect(is_array($example['placeholder_values'] ?? null) ? $example['placeholder_values'] : []) + ->mapWithKeys(function ($value, $key): array { + $normalizedKey = trim((string) $key); + + if ($normalizedKey === '') { + return []; + } + + $normalizedValue = $this->normalizePromptJsonValue($value); + + if ($normalizedValue === null || $normalizedValue === '' || $normalizedValue === []) { + return []; + } + + return [$normalizedKey => $normalizedValue]; + }) + ->all(), + 'prompt' => $this->nullableTrimmedString($example['prompt'] ?? null), + 'negative_prompt' => $this->nullableTrimmedString($example['negative_prompt'] ?? null), + ]; + }) + ->filter(function (array $example): bool { + return collect([ + $example['title'] ?? null, + $example['description'] ?? null, + $example['prompt'] ?? null, + $example['negative_prompt'] ?? null, + $example['placeholder_values'] ?? null, + ])->contains(fn ($item): bool => $item !== null && $item !== '' && $item !== []); + }) + ->take(5) + ->values() + ->all(); + } + /** * @return array */ diff --git a/app/Http/Controllers/Settings/FeaturedArtworkAdminController.php b/app/Http/Controllers/Settings/FeaturedArtworkAdminController.php index 1b05bf77..3594e4da 100644 --- a/app/Http/Controllers/Settings/FeaturedArtworkAdminController.php +++ b/app/Http/Controllers/Settings/FeaturedArtworkAdminController.php @@ -27,8 +27,10 @@ class FeaturedArtworkAdminController extends Controller { $isAdminSurface = $request->routeIs('admin.artworks.featured.*'); $routePrefix = $isAdminSurface ? 'admin.artworks.featured.' : 'admin.cp.artworks.featured.'; + $pageName = $isAdminSurface ? 'Moderation/FeaturedArtworks' : 'Collection/FeaturedArtworksAdmin'; + $rootView = $isAdminSurface ? 'moderation' : 'collections'; - return Inertia::render($isAdminSurface ? 'Admin/FeaturedArtworks' : 'Collection/FeaturedArtworksAdmin', array_merge( + return Inertia::render($pageName, array_merge( $this->featuredArtworks->pageProps(), [ 'endpoints' => [ @@ -49,7 +51,7 @@ class FeaturedArtworkAdminController extends Controller 'robots' => 'index,follow', ], ], - ))->rootView($isAdminSurface ? 'admin' : 'collections'); + ))->rootView($rootView); } public function search(Request $request): JsonResponse @@ -215,4 +217,4 @@ class FeaturedArtworkAdminController extends Controller { return Schema::hasColumn('artwork_features', 'force_hero'); } -} \ No newline at end of file +} diff --git a/app/Http/Requests/Academy/UpsertAcademyPromptTemplateRequest.php b/app/Http/Requests/Academy/UpsertAcademyPromptTemplateRequest.php index ad4e500b..0f2de7bb 100644 --- a/app/Http/Requests/Academy/UpsertAcademyPromptTemplateRequest.php +++ b/app/Http/Requests/Academy/UpsertAcademyPromptTemplateRequest.php @@ -27,6 +27,7 @@ class UpsertAcademyPromptTemplateRequest extends FormRequest 'placeholders' => $this->normalizePlaceholders($this->input('placeholders')), 'helper_prompts' => $this->normalizeHelperPrompts($this->input('helper_prompts')), 'prompt_variants' => $this->normalizePromptVariants($this->input('prompt_variants')), + 'filled_examples' => $this->normalizeFilledExamples($this->input('filled_examples')), 'tool_notes' => collect($this->input('tool_notes', [])) ->filter(static fn ($note): bool => is_array($note) || is_string($note)) ->map(function ($note): array|string { @@ -59,8 +60,10 @@ class UpsertAcademyPromptTemplateRequest extends FormRequest $promptId = $this->route('academyPromptTemplate')?->id; return [ - 'category_id' => ['nullable', 'integer', 'exists:academy_categories,id'], - 'new_category_name' => ['nullable', 'string', 'max:120'], + // Require either an existing category selection or a new category name. + 'category_id' => ['nullable', 'integer', 'exists:academy_categories,id', 'required_without:new_category_name'], + 'new_category_name' => ['nullable', 'string', 'max:120', 'required_without:category_id'], + 'title' => ['required', 'string', 'max:180'], 'slug' => ['required', 'string', 'max:180', Rule::unique('academy_prompt_templates', 'slug')->ignore($promptId)], 'excerpt' => ['nullable', 'string'], @@ -112,6 +115,12 @@ class UpsertAcademyPromptTemplateRequest extends FormRequest 'prompt_variants.*.risk_notes' => ['nullable', 'array'], 'prompt_variants.*.risk_notes.*' => ['nullable', 'string'], 'prompt_variants.*.active' => ['nullable', 'boolean'], + 'filled_examples' => ['nullable', 'array', 'max:5'], + 'filled_examples.*.title' => ['nullable', 'string', 'max:180'], + 'filled_examples.*.description' => ['nullable', 'string'], + 'filled_examples.*.placeholder_values' => ['nullable', 'array'], + 'filled_examples.*.prompt' => ['required_with:filled_examples', 'string'], + 'filled_examples.*.negative_prompt' => ['nullable', 'string'], 'difficulty' => ['required', 'string', Rule::in((array) config('academy.difficulty_levels', []))], 'access_level' => ['required', 'string', Rule::in(['free', 'creator', 'pro'])], 'aspect_ratio' => ['nullable', 'string', 'max:20'], @@ -283,6 +292,53 @@ class UpsertAcademyPromptTemplateRequest extends FormRequest ->all(); } + private function normalizeFilledExamples(mixed $value): mixed + { + $value = $this->decodeStructuredInput($value); + + if ($value === null) { + return []; + } + + if (! is_array($value)) { + return $value; + } + + $value = $this->normalizeStructuredObjectList($value, ['title', 'description', 'placeholder_values', 'prompt', 'negative_prompt']); + + return collect($value) + ->values() + ->map(function ($example): mixed { + if (! is_array($example)) { + return $example; + } + + return [ + 'title' => $this->normalizeOptionalString($example['title'] ?? null), + 'description' => $this->normalizeOptionalString($example['description'] ?? null), + 'placeholder_values' => is_array($example['placeholder_values'] ?? null) ? $example['placeholder_values'] : [], + 'prompt' => $this->normalizeOptionalString($example['prompt'] ?? null), + 'negative_prompt' => $this->normalizeOptionalString($example['negative_prompt'] ?? null), + ]; + }) + ->filter(function ($example): bool { + if (! is_array($example)) { + return true; + } + + return collect([ + $example['title'] ?? null, + $example['description'] ?? null, + $example['prompt'] ?? null, + $example['negative_prompt'] ?? null, + $example['placeholder_values'] ?? null, + ])->contains(fn ($item): bool => $item !== null && $item !== '' && $item !== []); + }) + ->take(5) + ->values() + ->all(); + } + private function normalizePromptVariants(mixed $value): mixed { $value = $this->decodeStructuredInput($value); diff --git a/app/Jobs/AutoTagArtworkJob.php b/app/Jobs/AutoTagArtworkJob.php index e43988a0..b78de2fa 100644 --- a/app/Jobs/AutoTagArtworkJob.php +++ b/app/Jobs/AutoTagArtworkJob.php @@ -54,6 +54,10 @@ final class AutoTagArtworkJob implements ShouldQueue public function handle(TagService $tagService, TagNormalizer $normalizer, ?VisionService $vision = null): void { + if (! (bool) config('vision.auto_tagging.enabled', false)) { + return; + } + $vision ??= app(VisionService::class); if (! $vision->isEnabled()) { diff --git a/app/Jobs/GenerateDerivativesJob.php b/app/Jobs/GenerateDerivativesJob.php index 654e0fc6..b74bc6c2 100644 --- a/app/Jobs/GenerateDerivativesJob.php +++ b/app/Jobs/GenerateDerivativesJob.php @@ -54,10 +54,18 @@ final class GenerateDerivativesJob implements ShouldQueue } // Auto-tagging is async and must never block publish. - AutoTagArtworkJob::dispatch($this->artworkId, $this->hash)->afterCommit(); - DetectArtworkMaturityJob::dispatch($this->artworkId, $this->hash)->afterCommit(); - GenerateArtworkEmbeddingJob::dispatch($this->artworkId, $this->hash)->afterCommit(); - AnalyzeArtworkAiAssistJob::dispatch($this->artworkId)->afterCommit(); + if ((bool) config('vision.auto_tagging.enabled', false)) { + AutoTagArtworkJob::dispatch($this->artworkId, $this->hash)->afterCommit(); + } + if ((bool) config('vision.upload.maturity.enabled', false)) { + DetectArtworkMaturityJob::dispatch($this->artworkId, $this->hash)->afterCommit(); + } + if ((bool) config('vision.upload.embeddings.enabled', true)) { + GenerateArtworkEmbeddingJob::dispatch($this->artworkId, $this->hash)->afterCommit(); + } + if ((bool) config('vision.upload.ai_assist.enabled', false)) { + AnalyzeArtworkAiAssistJob::dispatch($this->artworkId)->afterCommit(); + } } public function failed(\Throwable $exception): void diff --git a/app/Jobs/RecComputeSimilarByTagsJob.php b/app/Jobs/RecComputeSimilarByTagsJob.php index 7c6e9012..acad5553 100644 --- a/app/Jobs/RecComputeSimilarByTagsJob.php +++ b/app/Jobs/RecComputeSimilarByTagsJob.php @@ -9,9 +9,11 @@ use App\Models\RecArtworkRec; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; +use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; /** * Compute tag-based (+ category boost) similarity for artworks. @@ -30,6 +32,7 @@ final class RecComputeSimilarByTagsJob implements ShouldQueue public function __construct( private readonly ?int $artworkId = null, private readonly int $batchSize = 200, + private readonly ?int $afterArtworkId = null, ) { $queue = (string) config('recommendations.queue', 'default'); if ($queue !== '') { @@ -37,6 +40,22 @@ final class RecComputeSimilarByTagsJob implements ShouldQueue } } + /** + * @return array + */ + public function middleware(): array + { + if ($this->artworkId === null) { + return []; + } + + return [ + (new WithoutOverlapping('rec-similar-tags:'.$this->artworkId)) + ->expireAfter($this->timeout + 60) + ->dontRelease(), + ]; + } + public function handle(): void { $modelVersion = (string) config('recommendations.similarity.model_version', 'sim_v1'); @@ -51,19 +70,68 @@ final class RecComputeSimilarByTagsJob implements ShouldQueue ->pluck('cnt', 'tag_id') ->all(); - $query = Artwork::query()->public()->published()->select('id', 'user_id'); - if ($this->artworkId !== null) { - $query->where('id', $this->artworkId); + $artwork = Artwork::query()->public()->published()->select('id', 'user_id')->find($this->artworkId); + + if (! $artwork instanceof Artwork) { + return; + } + + $this->processArtworkSafely($artwork, $tagFreqs, $modelVersion, $candidatePool, $maxPerAuthor, $resultLimit); + + return; } - $query->chunkById($this->batchSize, function ($artworks) use ( - $tagFreqs, $modelVersion, $candidatePool, $maxPerAuthor, $resultLimit - ) { - foreach ($artworks as $artwork) { - $this->processArtwork($artwork, $tagFreqs, $modelVersion, $candidatePool, $maxPerAuthor, $resultLimit); - } - }); + $artworks = Artwork::query() + ->public() + ->published() + ->select('id', 'user_id') + ->when($this->afterArtworkId !== null, fn ($query) => $query->where('id', '>', $this->afterArtworkId)) + ->orderBy('id') + ->limit($this->batchSize) + ->get(); + + if ($artworks->isEmpty()) { + return; + } + + foreach ($artworks as $artwork) { + $this->processArtworkSafely($artwork, $tagFreqs, $modelVersion, $candidatePool, $maxPerAuthor, $resultLimit); + } + + if ($artworks->count() === $this->batchSize) { + static::dispatch(null, $this->batchSize, (int) $artworks->last()->id); + } + } + + public function failed(\Throwable $exception): void + { + Log::error('[RecComputeSimilarByTags] Job failed permanently.', [ + 'artwork_id' => $this->artworkId, + 'batch_size' => $this->batchSize, + 'after_artwork_id' => $this->afterArtworkId, + 'attempts' => $this->attempts(), + 'exception_class' => $exception::class, + 'exception_message' => $exception->getMessage(), + ]); + } + + private function processArtworkSafely( + Artwork $artwork, + array $tagFreqs, + string $modelVersion, + int $candidatePool, + int $maxPerAuthor, + int $resultLimit, + ): void { + try { + $this->processArtwork($artwork, $tagFreqs, $modelVersion, $candidatePool, $maxPerAuthor, $resultLimit); + } catch (\Throwable $exception) { + Log::warning("[RecComputeSimilarByTags] Failed for artwork {$artwork->id}: {$exception->getMessage()}", [ + 'artwork_id' => $artwork->id, + 'exception_class' => $exception::class, + ]); + } } private function processArtwork( diff --git a/app/Jobs/RecComputeSimilarHybridJob.php b/app/Jobs/RecComputeSimilarHybridJob.php index 9dd9e19d..e968b593 100644 --- a/app/Jobs/RecComputeSimilarHybridJob.php +++ b/app/Jobs/RecComputeSimilarHybridJob.php @@ -9,6 +9,7 @@ use App\Models\RecArtworkRec; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; +use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\DB; @@ -25,7 +26,10 @@ final class RecComputeSimilarHybridJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - public int $tries = 2; + // This recompute is idempotent and already guards per-artwork execution. + // Keep retries to a minimum so transient failures do not turn into + // Horizon's max-attempt exception noise. + public int $tries = 1; public int $timeout = 900; public function __construct( @@ -38,6 +42,24 @@ final class RecComputeSimilarHybridJob implements ShouldQueue } } + /** + * @return array + */ + public function middleware(): array + { + if ($this->artworkId === null) { + return []; + } + + return [ + // Many artwork lifecycle events can queue this same recompute burstily. + // Keep only one in flight per artwork and drop overlapping duplicates. + (new WithoutOverlapping('rec-similar-hybrid:'.$this->artworkId)) + ->expireAfter($this->timeout + 60) + ->dontRelease(), + ]; + } + public function handle(): void { $modelVersion = (string) config('recommendations.similarity.model_version', 'sim_v1'); @@ -50,26 +72,90 @@ final class RecComputeSimilarHybridJob implements ShouldQueue ? (array) config('recommendations.similarity.weights_with_vector') : (array) config('recommendations.similarity.weights_without_vector'); - $query = Artwork::query()->public()->published()->select('id', 'user_id'); - if ($this->artworkId !== null) { - $query->where('id', $this->artworkId); + $artwork = Artwork::query() + ->public() + ->published() + ->select('id', 'user_id') + ->find($this->artworkId); + + if (! $artwork instanceof Artwork) { + return; + } + + $this->processArtworkSafely( + collect([$artwork]), + $modelVersion, + $vectorEnabled, + $resultLimit, + $maxPerAuthor, + $minCatsTop12, + $weights, + ); + + return; } - $query->chunkById($this->batchSize, function ($artworks) use ( - $modelVersion, $vectorEnabled, $resultLimit, $maxPerAuthor, $minCatsTop12, $weights - ) { - foreach ($artworks as $artwork) { - try { - $this->processArtwork( - $artwork, $modelVersion, $vectorEnabled, $resultLimit, - $maxPerAuthor, $minCatsTop12, $weights - ); - } catch (\Throwable $e) { - Log::warning("[RecComputeSimilarHybrid] Failed for artwork {$artwork->id}: {$e->getMessage()}"); - } + Artwork::query() + ->public() + ->published() + ->select('id', 'user_id') + ->chunkById($this->batchSize, function ($artworks) use ( + $modelVersion, $vectorEnabled, $resultLimit, $maxPerAuthor, $minCatsTop12, $weights + ) { + $this->processArtworkSafely( + $artworks, + $modelVersion, + $vectorEnabled, + $resultLimit, + $maxPerAuthor, + $minCatsTop12, + $weights, + ); + }); + } + + public function failed(\Throwable $exception): void + { + Log::error('[RecComputeSimilarHybrid] Job failed permanently.', [ + 'artwork_id' => $this->artworkId, + 'batch_size' => $this->batchSize, + 'attempts' => $this->attempts(), + 'exception_class' => $exception::class, + 'exception_message' => $exception->getMessage(), + ]); + } + + /** + * @param iterable $artworks + */ + private function processArtworkSafely( + iterable $artworks, + string $modelVersion, + bool $vectorEnabled, + int $resultLimit, + int $maxPerAuthor, + int $minCatsTop12, + array $weights, + ): void { + foreach ($artworks as $artwork) { + try { + $this->processArtwork( + $artwork, + $modelVersion, + $vectorEnabled, + $resultLimit, + $maxPerAuthor, + $minCatsTop12, + $weights, + ); + } catch (\Throwable $e) { + Log::warning("[RecComputeSimilarHybrid] Failed for artwork {$artwork->id}: {$e->getMessage()}", [ + 'artwork_id' => $artwork->id, + 'exception_class' => $e::class, + ]); } - }); + } } private function processArtwork( diff --git a/app/Mail/AcademyAccessIssue.php b/app/Mail/AcademyAccessIssue.php new file mode 100644 index 00000000..070cdbbb --- /dev/null +++ b/app/Mail/AcademyAccessIssue.php @@ -0,0 +1,47 @@ +issueType ? ' ['.$this->issueType.']' : '').' from '.$this->user->email; + $replyTo = trim((string) ($this->contactEmail ?: $this->user->email)); + + $mail = $this->subject($subject) + ->view('emails.academy_access_issue') + ->with([ + 'user' => $this->user, + 'message' => $this->message, + 'sessionId' => $this->sessionId, + 'issueType' => $this->issueType, + 'contactEmail' => $this->contactEmail, + ]); + + if ($replyTo !== '') { + $mail->replyTo($replyTo); + } + + return $mail; + } +} diff --git a/app/Models/AcademyPromptTemplate.php b/app/Models/AcademyPromptTemplate.php index 9bd9b877..f954b248 100644 --- a/app/Models/AcademyPromptTemplate.php +++ b/app/Models/AcademyPromptTemplate.php @@ -28,6 +28,7 @@ class AcademyPromptTemplate extends Model 'placeholders', 'helper_prompts', 'prompt_variants', + 'filled_examples', 'difficulty', 'access_level', 'aspect_ratio', @@ -49,6 +50,7 @@ class AcademyPromptTemplate extends Model 'placeholders' => 'array', 'helper_prompts' => 'array', 'prompt_variants' => 'array', + 'filled_examples' => 'array', 'featured' => 'boolean', 'prompt_of_week' => 'boolean', 'active' => 'boolean', @@ -75,10 +77,15 @@ class AcademyPromptTemplate extends Model return $this->hasMany(AcademySavedPrompt::class, 'prompt_template_id'); } + public function metrics(): HasMany + { + return $this->hasMany(AcademyContentMetricDaily::class, 'content_id'); + } + public function packs(): BelongsToMany { return $this->belongsToMany(AcademyPromptPack::class, 'academy_prompt_pack_items', 'prompt_template_id', 'pack_id') ->withPivot('order_num') ->withTimestamps(); } -} \ No newline at end of file +} diff --git a/app/Services/Academy/AcademyAccessService.php b/app/Services/Academy/AcademyAccessService.php index 8b321153..c0cc6313 100644 --- a/app/Services/Academy/AcademyAccessService.php +++ b/app/Services/Academy/AcademyAccessService.php @@ -344,7 +344,18 @@ final class AcademyAccessService $previewImage = $this->promptPreviewImagePayload((string) ($prompt->preview_image ?? '')); $documentation = $this->promptDocumentationPayload($prompt->documentation); $placeholders = $this->promptPlaceholdersPayload((array) ($prompt->placeholders ?? [])); + $allFilledExamples = $this->promptFilledExamplesPayload((array) ($prompt->filled_examples ?? [])); + $filledExamplesTotal = count($allFilledExamples); + $hasFullFilledExamplesAccess = (bool) (($viewer?->hasAcademyProAccess() ?? false) || ($viewer?->hasStaffAccess() ?? false)); + $hasPartialFilledExamplesAccess = (bool) ($viewer?->hasAcademyCreatorAccess() ?? false); + $visibleFilledExamples = match (true) { + ! $includeFull => [], + $hasFullFilledExamplesAccess => $allFilledExamples, + $hasPartialFilledExamplesAccess => array_slice($allFilledExamples, 0, 2), + default => [], + }; $hasPlaceholderInputs = $this->promptHasPlaceholderInputs((string) $prompt->prompt, $placeholders); + $hasFilledExamples = $allFilledExamples !== []; $hasHelperPrompts = $this->promptHelperPromptsPayload((array) ($prompt->helper_prompts ?? [])) !== []; $hasPromptVariants = $this->promptVariantsPayload((array) ($prompt->prompt_variants ?? [])) !== []; $helperPrompts = $authorized && $includeFull @@ -367,6 +378,12 @@ final class AcademyAccessService 'documentation' => $documentation, 'placeholders' => $placeholders, 'has_placeholder_inputs' => $hasPlaceholderInputs, + 'filled_examples' => $visibleFilledExamples, + 'has_filled_examples' => $hasFilledExamples, + 'filled_examples_total' => $filledExamplesTotal, + 'can_access_filled_examples' => ($hasFullFilledExamplesAccess || $hasPartialFilledExamplesAccess) && $includeFull, + 'has_more_filled_examples' => $filledExamplesTotal > count($visibleFilledExamples), + 'has_full_filled_examples_access' => $hasFullFilledExamplesAccess, 'has_helper_prompts' => $hasHelperPrompts, 'has_prompt_variants' => $hasPromptVariants, 'helper_prompts' => $helperPrompts, @@ -396,6 +413,47 @@ final class AcademyAccessService ]; } + /** + * @param array $filledExamples + * @return array> + */ + private function promptFilledExamplesPayload(array $filledExamples): array + { + return collect($filledExamples) + ->filter(static fn ($example): bool => is_array($example)) + ->map(function (array $example): array { + return [ + 'title' => $this->nullableTrimmedString($example['title'] ?? null), + 'description' => $this->nullableTrimmedString($example['description'] ?? null), + 'placeholder_values' => collect(is_array($example['placeholder_values'] ?? null) ? $example['placeholder_values'] : []) + ->mapWithKeys(function ($value, $key): array { + $normalizedKey = trim((string) $key); + + if ($normalizedKey === '') { + return []; + } + + return [$normalizedKey => $value]; + }) + ->all(), + 'prompt' => trim((string) ($example['prompt'] ?? '')), + 'negative_prompt' => $this->nullableTrimmedString($example['negative_prompt'] ?? null), + ]; + }) + ->filter(function (array $example): bool { + return collect([ + $example['title'] ?? null, + $example['description'] ?? null, + $example['prompt'] ?? null, + $example['negative_prompt'] ?? null, + $example['placeholder_values'] ?? null, + ])->contains(fn ($item): bool => $item !== null && $item !== '' && $item !== []); + }) + ->take(5) + ->values() + ->all(); + } + /** * @param mixed $documentation * @return array diff --git a/app/Services/Academy/AcademyBillingPlanService.php b/app/Services/Academy/AcademyBillingPlanService.php index 550bd6a8..aa8d0ad2 100644 --- a/app/Services/Academy/AcademyBillingPlanService.php +++ b/app/Services/Academy/AcademyBillingPlanService.php @@ -5,8 +5,11 @@ declare(strict_types=1); namespace App\Services\Academy; use Illuminate\Support\Arr; +use Illuminate\Support\Facades\Cache; use Illuminate\Support\Str; use RuntimeException; +use Stripe\Exception\InvalidRequestException; +use Stripe\StripeClient; final class AcademyBillingPlanService { @@ -64,6 +67,7 @@ final class AcademyBillingPlanService $plan['stripe_price_id'] = trim((string) ($plan['stripe_price_id'] ?? '')); $plan['configured'] = $plan['stripe_price_id'] !== ''; $plan['price_id_valid'] = $this->isValidPriceId($plan['stripe_price_id']); + $plan['remote_price_exists'] = $this->remotePriceExists($plan['stripe_price_id']); $plan['price_display'] = $plan['amount'] !== '' ? $plan['amount'].' '.$plan['currency'] : null; return $plan; @@ -145,4 +149,86 @@ final class AcademyBillingPlanService return preg_match('/^price_[A-Za-z0-9]+$/', $priceId) === 1; } -} \ No newline at end of file + + public function remotePriceExists(?string $priceId): ?bool + { + $priceId = trim((string) $priceId); + + if ($priceId === '') { + return false; + } + + // Avoid calling Stripe in local/testing environments — assume exists there. + if (app()->environment(['local', 'testing'])) { + return true; + } + + $cacheKey = 'academy.remote_price_exists:'.md5($priceId); + + return Cache::remember($cacheKey, 300, function () use ($priceId): ?bool { + try { + $secret = $this->stripeSecret(); + + if ($secret === null) { + return null; + } + + $client = new StripeClient($secret); + $price = $client->prices->retrieve($priceId, []); + + // If Stripe returned an object with an id, it exists. Also ensure product exists where possible. + if (is_object($price) && ! empty($price->id)) { + return true; + } + + return false; + } catch (InvalidRequestException $e) { + report($e); + + return false; + } catch (\Throwable $e) { + report($e); + + // Auth, network, or transient Stripe failures should not make + // public pricing look fully misconfigured. + return null; + } + }); + } + + private function stripeSecret(): ?string + { + foreach ([config('cashier.secret'), env('STRIPE_SECRET')] as $candidate) { + if (! is_string($candidate)) { + continue; + } + + $candidate = trim($candidate); + + if ($candidate !== '') { + return $candidate; + } + } + + return null; + } + + /** + * @return array + */ + public function missingRemotePriceIds(?string $planKey = null): array + { + if ($planKey !== null) { + $plan = $this->plan($planKey); + + return $plan !== null && $this->remotePriceExists($plan['stripe_price_id'] ?? '') === false + ? [$this->normalizePlanKey($planKey)] + : []; + } + + return collect(array_keys($this->plans())) + ->filter(fn (string $key): bool => $this->remotePriceExists($this->plan($key)['stripe_price_id'] ?? '') === false) + ->values() + ->all(); + } +} diff --git a/app/Services/Uploads/UploadQueueService.php b/app/Services/Uploads/UploadQueueService.php index d53ef4cb..8590cba0 100644 --- a/app/Services/Uploads/UploadQueueService.php +++ b/app/Services/Uploads/UploadQueueService.php @@ -15,6 +15,7 @@ use App\Models\User; use App\Services\Artworks\ArtworkDraftService; use App\Services\Artworks\ArtworkPublicationService; use App\Services\Maturity\ArtworkMaturityService; +use App\Services\Studio\StudioAiAssistService; use App\Services\TagService; use Carbon\CarbonInterface; use Illuminate\Database\Eloquent\Collection as EloquentCollection; @@ -189,7 +190,9 @@ final class UploadQueueService $item = $this->itemQuery()->findOrFail($itemId); $item->forceFill([ - 'processing_stage' => UploadBatchItem::STAGE_MATURITY_CHECK, + 'processing_stage' => $this->uploadMaturityEnabled() + ? UploadBatchItem::STAGE_MATURITY_CHECK + : UploadBatchItem::STAGE_FINALIZED, 'error_code' => null, 'error_message' => null, 'processed_at' => now(), @@ -290,7 +293,7 @@ final class UploadQueueService 'apply_category' => $this->applyCategory($item, (int) ($params['category_id'] ?? 0)), 'apply_tags' => $this->applyTags($item, (array) ($params['tags'] ?? [])), 'set_visibility' => $this->setVisibility($item, (string) ($params['visibility'] ?? '')), - 'generate_ai' => $this->retryProcessing($item), + 'generate_ai' => $this->requestAiGeneration($item), default => throw ValidationException::withMessages([ 'action' => ['Unsupported upload queue action.'], ]), @@ -341,16 +344,40 @@ final class UploadQueueService $item->forceFill([ 'status' => UploadBatchItem::STATUS_PROCESSING, - 'processing_stage' => UploadBatchItem::STAGE_MATURITY_CHECK, + 'processing_stage' => $this->uploadMaturityEnabled() + ? UploadBatchItem::STAGE_MATURITY_CHECK + : UploadBatchItem::STAGE_FINALIZED, 'error_code' => null, 'error_message' => null, 'is_ready_to_publish' => false, ])->save(); - AutoTagArtworkJob::dispatch((int) $artwork->id, (string) $artwork->hash)->afterCommit(); - DetectArtworkMaturityJob::dispatch((int) $artwork->id, (string) $artwork->hash)->afterCommit(); - GenerateArtworkEmbeddingJob::dispatch((int) $artwork->id, (string) $artwork->hash)->afterCommit(); - AnalyzeArtworkAiAssistJob::dispatch((int) $artwork->id, true)->afterCommit(); + if ((bool) config('vision.auto_tagging.enabled', false)) { + AutoTagArtworkJob::dispatch((int) $artwork->id, (string) $artwork->hash)->afterCommit(); + } + if ($this->uploadMaturityEnabled()) { + DetectArtworkMaturityJob::dispatch((int) $artwork->id, (string) $artwork->hash)->afterCommit(); + } + if ((bool) config('vision.upload.embeddings.enabled', true)) { + GenerateArtworkEmbeddingJob::dispatch((int) $artwork->id, (string) $artwork->hash)->afterCommit(); + } + if ((bool) config('vision.upload.ai_assist.enabled', false)) { + AnalyzeArtworkAiAssistJob::dispatch((int) $artwork->id, true)->afterCommit(); + } + + return $this->refreshItem((int) $item->id); + } + + private function requestAiGeneration(UploadBatchItem $item): UploadBatchItem + { + $artwork = $item->artwork; + if (! $artwork || trim((string) ($artwork->hash ?? '')) === '' || trim((string) ($artwork->file_path ?? '')) === '') { + throw ValidationException::withMessages([ + 'item' => ['This item cannot generate AI suggestions safely. Re-upload the original file instead.'], + ]); + } + + app(StudioAiAssistService::class)->queueAnalysis($artwork, true); return $this->refreshItem((int) $item->id); } @@ -543,13 +570,13 @@ final class UploadQueueService $maturityStatus = Str::lower((string) ($artwork?->maturity_status ?? ArtworkMaturityService::STATUS_CLEAR)); $maturityAiStatus = Str::lower((string) ($artwork?->maturity_ai_status ?? ArtworkMaturityService::AI_STATUS_NOT_REQUESTED)); $aiStatus = Str::lower((string) ($artwork?->ai_status ?? '')); - $visionEnabled = (bool) config('vision.enabled', true); + $uploadMaturityEnabled = $this->uploadMaturityEnabled(); - $maturityPending = $visionEnabled && in_array($maturityAiStatus, [ + $maturityPending = $uploadMaturityEnabled && in_array($maturityAiStatus, [ ArtworkMaturityService::AI_STATUS_PENDING, ArtworkMaturityService::AI_STATUS_NOT_REQUESTED, ], true); - $maturityFailed = $visionEnabled && $maturityAiStatus === ArtworkMaturityService::AI_STATUS_FAILED; + $maturityFailed = $uploadMaturityEnabled && $maturityAiStatus === ArtworkMaturityService::AI_STATUS_FAILED; $needsReview = $maturityStatus === ArtworkMaturityService::STATUS_SUSPECTED || $maturityFailed; $needsMetadata = ! $hasTitle || ! $hasCategory; $blockingUploadFailure = ! $hasProcessedMedia && ($this->nullableString($item->error_code) !== null || $this->nullableText($item->error_message) !== null); @@ -634,9 +661,9 @@ final class UploadQueueService } if ($maturityStatus === ArtworkMaturityService::STATUS_SUSPECTED) { $missing[] = 'Needs maturity review'; - } elseif ((bool) config('vision.enabled', true) && in_array($maturityAiStatus, [ArtworkMaturityService::AI_STATUS_PENDING, ArtworkMaturityService::AI_STATUS_NOT_REQUESTED], true)) { + } elseif ($this->uploadMaturityEnabled() && in_array($maturityAiStatus, [ArtworkMaturityService::AI_STATUS_PENDING, ArtworkMaturityService::AI_STATUS_NOT_REQUESTED], true)) { $missing[] = 'Maturity analysis pending'; - } elseif ((bool) config('vision.enabled', true) && $maturityAiStatus === ArtworkMaturityService::AI_STATUS_FAILED) { + } elseif ($this->uploadMaturityEnabled() && $maturityAiStatus === ArtworkMaturityService::AI_STATUS_FAILED) { $missing[] = 'Maturity check failed'; } @@ -690,6 +717,12 @@ final class UploadQueueService return (int) round((collect($checks)->filter()->count() / count($checks)) * 100); } + private function uploadMaturityEnabled(): bool + { + return (bool) config('vision.enabled', true) + && (bool) config('vision.upload.maturity.enabled', false); + } + private function normalizeDefaults(array $defaults): array { $visibility = (string) ($defaults['visibility'] ?? Artwork::VISIBILITY_PUBLIC); diff --git a/bootstrap/ssr/assets/ArtworkShareModal-BI8kkaqs.js b/bootstrap/ssr/assets/ArtworkShareModal-BPM8yel5.js similarity index 99% rename from bootstrap/ssr/assets/ArtworkShareModal-BI8kkaqs.js rename to bootstrap/ssr/assets/ArtworkShareModal-BPM8yel5.js index 9b5f2555..1789116e 100644 --- a/bootstrap/ssr/assets/ArtworkShareModal-BI8kkaqs.js +++ b/bootstrap/ssr/assets/ArtworkShareModal-BPM8yel5.js @@ -18,7 +18,7 @@ import "./vendor-tooltip-CIQaDNlG.js"; import "node:process"; import "node:path"; import "node:url"; -import "./vendor-realtime-DYEIbD6w.js"; +import "./vendor-realtime-Koiu-_pw.js"; import "buffer"; import "child_process"; import "net"; diff --git a/bootstrap/ssr/assets/vendor-realtime-DYEIbD6w.js b/bootstrap/ssr/assets/vendor-realtime-Koiu-_pw.js similarity index 100% rename from bootstrap/ssr/assets/vendor-realtime-DYEIbD6w.js rename to bootstrap/ssr/assets/vendor-realtime-Koiu-_pw.js index ae798d41..79b999f8 100644 --- a/bootstrap/ssr/assets/vendor-realtime-DYEIbD6w.js +++ b/bootstrap/ssr/assets/vendor-realtime-Koiu-_pw.js @@ -1,17 +1,17 @@ import require$$0 from "util"; import stream from "stream"; +import require$$4 from "https"; import require$$5 from "url"; import require$$6 from "fs"; import require$$1 from "crypto"; import require$$4$2 from "assert"; import require$$1$1 from "buffer"; import require$$2 from "child_process"; +import require$$4$1 from "events"; import require$$8 from "net"; import require$$10 from "tls"; import { c as commonjsGlobal, g as getDefaultExportFromCjs } from "./vendor-tiptap-DRFaxGEb.js"; -import require$$4$1 from "events"; import require$$3 from "http"; -import require$$4 from "https"; class u { constructor() { this.notificationCreatedEvent = ".Illuminate\\Notifications\\Events\\BroadcastNotificationCreated"; diff --git a/bootstrap/ssr/ssr-manifest.json b/bootstrap/ssr/ssr-manifest.json index 5f191398..9dee535b 100644 --- a/bootstrap/ssr/ssr-manifest.json +++ b/bootstrap/ssr/ssr-manifest.json @@ -14,10 +14,10 @@ "\u0000D:/Sites/Skinbase26/node_modules/nprogress/nprogress.js?commonjs-es-import": [], "\u0000D:/Sites/Skinbase26/node_modules/nprogress/nprogress.js?commonjs-module": [], "\u0000D:/Sites/Skinbase26/node_modules/pusher-js/dist/node/pusher.js?commonjs-es-import": [ - "/build/assets/vendor-realtime-DYEIbD6w.js" + "/build/assets/vendor-realtime-Koiu-_pw.js" ], "\u0000D:/Sites/Skinbase26/node_modules/pusher-js/dist/node/pusher.js?commonjs-module": [ - "/build/assets/vendor-realtime-DYEIbD6w.js" + "/build/assets/vendor-realtime-Koiu-_pw.js" ], "\u0000D:/Sites/Skinbase26/node_modules/qs/lib/index.js?commonjs-es-import": [], "\u0000D:/Sites/Skinbase26/node_modules/react-dom/cjs/react-dom-client.development.js?commonjs-exports": [], @@ -97,46 +97,46 @@ "/build/assets/vendor-tiptap-DRFaxGEb.js" ], "\u0000assert?commonjs-external": [ - "/build/assets/vendor-realtime-DYEIbD6w.js" + "/build/assets/vendor-realtime-Koiu-_pw.js" ], "\u0000buffer?commonjs-external": [ - "/build/assets/vendor-realtime-DYEIbD6w.js" + "/build/assets/vendor-realtime-Koiu-_pw.js" ], "\u0000child_process?commonjs-external": [ - "/build/assets/vendor-realtime-DYEIbD6w.js" + "/build/assets/vendor-realtime-Koiu-_pw.js" ], "\u0000commonjsHelpers.js": [ "/build/assets/vendor-tiptap-DRFaxGEb.js" ], "\u0000crypto?commonjs-external": [ - "/build/assets/vendor-realtime-DYEIbD6w.js" + "/build/assets/vendor-realtime-Koiu-_pw.js" ], "\u0000events?commonjs-external": [ - "/build/assets/vendor-realtime-DYEIbD6w.js" + "/build/assets/vendor-realtime-Koiu-_pw.js" ], "\u0000fs?commonjs-external": [ - "/build/assets/vendor-realtime-DYEIbD6w.js" + "/build/assets/vendor-realtime-Koiu-_pw.js" ], "\u0000http?commonjs-external": [ - "/build/assets/vendor-realtime-DYEIbD6w.js" + "/build/assets/vendor-realtime-Koiu-_pw.js" ], "\u0000https?commonjs-external": [ - "/build/assets/vendor-realtime-DYEIbD6w.js" + "/build/assets/vendor-realtime-Koiu-_pw.js" ], "\u0000net?commonjs-external": [ - "/build/assets/vendor-realtime-DYEIbD6w.js" + "/build/assets/vendor-realtime-Koiu-_pw.js" ], "\u0000stream?commonjs-external": [ - "/build/assets/vendor-realtime-DYEIbD6w.js" + "/build/assets/vendor-realtime-Koiu-_pw.js" ], "\u0000tls?commonjs-external": [ - "/build/assets/vendor-realtime-DYEIbD6w.js" + "/build/assets/vendor-realtime-Koiu-_pw.js" ], "\u0000url?commonjs-external": [ - "/build/assets/vendor-realtime-DYEIbD6w.js" + "/build/assets/vendor-realtime-Koiu-_pw.js" ], "\u0000util?commonjs-external": [ - "/build/assets/vendor-realtime-DYEIbD6w.js" + "/build/assets/vendor-realtime-Koiu-_pw.js" ], "node_modules/@emoji-mart/data/sets/15/native.json": [ "/build/assets/emoji-data-4xGXbtDn.js" @@ -1035,7 +1035,7 @@ "node_modules/inline-style-parser/cjs/index.js": [], "node_modules/is-plain-obj/index.js": [], "node_modules/laravel-echo/dist/echo.js": [ - "/build/assets/vendor-realtime-DYEIbD6w.js" + "/build/assets/vendor-realtime-Koiu-_pw.js" ], "node_modules/linkifyjs/dist/linkify.mjs": [ "/build/assets/vendor-tiptap-DRFaxGEb.js" @@ -1935,7 +1935,7 @@ ], "node_modules/proxy-from-env/index.js": [], "node_modules/pusher-js/dist/node/pusher.js": [ - "/build/assets/vendor-realtime-DYEIbD6w.js" + "/build/assets/vendor-realtime-Koiu-_pw.js" ], "node_modules/qs/lib/formats.js": [], "node_modules/qs/lib/index.js": [], @@ -2148,6 +2148,11 @@ "resources/js/Pages/Moderation/ArtworkMaturityQueue.jsx": [], "resources/js/Pages/Moderation/Enhance/Index.jsx": [], "resources/js/Pages/Moderation/Enhance/Show.jsx": [], + "resources/js/Pages/Moderation/FeaturedArtworks.jsx": [], + "resources/js/Pages/Moderation/StaffApplications/Index.jsx": [], + "resources/js/Pages/Moderation/StaffApplications/Show.jsx": [], + "resources/js/Pages/Moderation/Stories.jsx": [], + "resources/js/Pages/Moderation/UsernameQueue.jsx": [], "resources/js/Pages/Moderation/WorldWebStoriesIndex.jsx": [], "resources/js/Pages/Moderation/WorldWebStoryEditor.jsx": [], "resources/js/Pages/News/NewsComments.jsx": [], @@ -2252,7 +2257,7 @@ "resources/js/components/artwork/ArtworkRecommendationsRails.jsx": [], "resources/js/components/artwork/ArtworkShareButton.jsx": [], "resources/js/components/artwork/ArtworkShareModal.jsx": [ - "/build/assets/ArtworkShareModal-BI8kkaqs.js" + "/build/assets/ArtworkShareModal-BPM8yel5.js" ], "resources/js/components/artwork/ArtworkTags.jsx": [], "resources/js/components/artwork/AuthorBioPopover.jsx": [], diff --git a/bootstrap/ssr/ssr.js b/bootstrap/ssr/ssr.js index d6933c44..9ef81e07 100644 --- a/bootstrap/ssr/ssr.js +++ b/bootstrap/ssr/ssr.js @@ -17,7 +17,7 @@ import { t as tippy } from "./assets/vendor-tooltip-CIQaDNlG.js"; import minproc from "node:process"; import minpath from "node:path"; import { fileURLToPath } from "node:url"; -import { P as Pusher, E as E$2 } from "./assets/vendor-realtime-DYEIbD6w.js"; +import { P as Pusher, E as E$2 } from "./assets/vendor-realtime-Koiu-_pw.js"; import { u as useReducedMotion, m as motion, A as AnimatePresence } from "./assets/vendor-motion-CotXNotG.js"; import * as s from "process"; import require$$2 from "async_hooks"; @@ -12387,11 +12387,64 @@ function formatDate$j(iso) { } } function AcademyBillingAccount({ currentTier, isSubscribed, subscription, activePlan = null, links = {} }) { + const { flash, auth } = X$1().props; + const { data, setData, post: post2, processing } = G$1({ + issue_type: "billing", + contact_email: auth?.user?.email || "", + message: "", + session_id: null + }); + function IssueTypeDropdown({ value, onChange }) { + const options = [ + { value: "billing", label: "Billing question" }, + { value: "payment", label: "Payment problem" }, + { value: "upgrade", label: "Upgrade problem" }, + { value: "downgrade", label: "Downgrade problem" }, + { value: "cancel", label: "Cancellation problem" }, + { value: "access", label: "Access not updated" }, + { value: "other", label: "Other" } + ]; + const [open, setOpen] = reactExports.useState(false); + const ref2 = reactExports.useRef(null); + reactExports.useEffect(() => { + function onDoc(e) { + if (ref2.current && !ref2.current.contains(e.target)) setOpen(false); + } + document.addEventListener("mousedown", onDoc); + return () => document.removeEventListener("mousedown", onDoc); + }, []); + const current = options.find((o) => o.value === value) || options[0]; + return /* @__PURE__ */ React.createElement("div", { ref: ref2, className: "relative" }, /* @__PURE__ */ React.createElement( + "button", + { + type: "button", + onClick: () => setOpen((s2) => !s2), + className: "w-full text-left rounded-xl border border-amber-300/20 bg-black/20 p-3 text-sm text-amber-50 flex items-center justify-between" + }, + /* @__PURE__ */ React.createElement("span", null, current.label), + /* @__PURE__ */ React.createElement("svg", { className: "ml-2 h-4 w-4 text-amber-100/70", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React.createElement("path", { d: "M6 8l4 4 4-4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })) + ), open ? /* @__PURE__ */ React.createElement("div", { className: "absolute left-0 top-full mt-2 w-full rounded-xl border border-white/10 bg-[#10192e] shadow-2xl z-50 overflow-hidden" }, options.map((opt) => /* @__PURE__ */ React.createElement( + "button", + { + key: opt.value, + type: "button", + onClick: () => { + onChange(opt.value); + setOpen(false); + }, + className: `w-full text-left px-4 py-3 text-sm ${opt.value === value ? "bg-white/[0.03] text-white" : "text-slate-300 hover:bg-white/[0.02]"}` + }, + opt.label + ))) : null); + } + function getCsrfToken2() { + return document.querySelector('meta[name="csrf-token"]')?.getAttribute("content") || ""; + } const endsAt = formatDate$j(subscription?.endsAt); const onGracePeriod = subscription?.onGracePeriod === true; const subscriptionActive = subscription?.active === true; - return /* @__PURE__ */ React.createElement("main", { className: "min-h-screen bg-[radial-gradient(circle_at_top_left,_rgba(56,189,248,0.14),_transparent_24%),radial-gradient(circle_at_bottom_right,_rgba(251,191,36,0.14),_transparent_26%),linear-gradient(180deg,_#07111f_0%,_#0f172a_45%,_#111827_100%)] px-4 py-8 sm:px-6 lg:px-8" }, /* @__PURE__ */ React.createElement(Se$1, { title: "Academy Subscription" }), /* @__PURE__ */ React.createElement("div", { className: "mx-auto max-w-[1280px] space-y-8" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[40px] border border-white/10 bg-[linear-gradient(135deg,rgba(7,17,31,0.95),rgba(12,24,45,0.9),rgba(15,23,42,0.96))] p-8 shadow-[0_32px_100px_rgba(2,6,23,0.42)] md:p-10" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-3" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-sky-100/85" }, "Skinbase Academy"), /* @__PURE__ */ React.createElement(AccessBadge, { tier: currentTier })), /* @__PURE__ */ React.createElement("h1", { className: "mt-4 text-4xl font-semibold tracking-[-0.05em] text-white md:text-5xl" }, isSubscribed ? "Your subscription" : "Academy subscription"), /* @__PURE__ */ React.createElement("p", { className: "mt-4 max-w-2xl text-base leading-8 text-slate-300" }, isSubscribed ? "Your Academy access is active. Manage, upgrade, or cancel your subscription here at any time." : "You are on the free Academy tier. Upgrade to Creator or Pro to unlock premium content.")), onGracePeriod && endsAt ? /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-amber-300/25 bg-amber-300/[0.06] px-6 py-5" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-amber-100" }, "Your subscription was cancelled and will end on ", endsAt, "."), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm leading-6 text-amber-100/75" }, "You still have full access until that date. Open the subscription portal to resume your plan if you change your mind."), /* @__PURE__ */ React.createElement( - xe, + return /* @__PURE__ */ React.createElement("main", { className: "min-h-screen bg-[radial-gradient(circle_at_top_left,_rgba(56,189,248,0.14),_transparent_24%),radial-gradient(circle_at_bottom_right,_rgba(251,191,36,0.14),_transparent_26%),linear-gradient(180deg,_#07111f_0%,_#0f172a_45%,_#111827_100%)] px-4 py-8 sm:px-6 lg:px-8" }, /* @__PURE__ */ React.createElement(Se$1, { title: "Academy Subscription" }), /* @__PURE__ */ React.createElement("div", { className: "mx-auto max-w-[1280px] space-y-8" }, flash?.error ? /* @__PURE__ */ React.createElement("section", { className: "rounded-[20px] border border-rose-300/20 bg-rose-300/8 p-4" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-rose-100" }, flash.error)) : null, flash?.success ? /* @__PURE__ */ React.createElement("section", { className: "rounded-[20px] border border-emerald-300/20 bg-emerald-300/8 p-4" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-emerald-100" }, flash.success)) : null, /* @__PURE__ */ React.createElement("section", { className: "rounded-[40px] border border-white/10 bg-[linear-gradient(135deg,rgba(7,17,31,0.95),rgba(12,24,45,0.9),rgba(15,23,42,0.96))] p-8 shadow-[0_32px_100px_rgba(2,6,23,0.42)] md:p-10" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-3" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-sky-100/85" }, "Skinbase Academy"), /* @__PURE__ */ React.createElement(AccessBadge, { tier: currentTier })), /* @__PURE__ */ React.createElement("h1", { className: "mt-4 text-4xl font-semibold tracking-[-0.05em] text-white md:text-5xl" }, isSubscribed ? "Your subscription" : "Academy subscription"), /* @__PURE__ */ React.createElement("p", { className: "mt-4 max-w-2xl text-base leading-8 text-slate-300" }, isSubscribed ? "Your Academy access is active. Manage, upgrade, or cancel your subscription here at any time." : "You are on the free Academy tier. Upgrade to Creator or Pro to unlock premium content.")), onGracePeriod && endsAt ? /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-amber-300/25 bg-amber-300/[0.06] px-6 py-5" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-amber-100" }, "Your subscription was cancelled and will end on ", endsAt, "."), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm leading-6 text-amber-100/75" }, "You still have full access until that date. Open the subscription portal to resume your plan if you change your mind."), /* @__PURE__ */ React.createElement( + "a", { href: links.portal, className: "mt-4 inline-flex items-center rounded-full border border-amber-300/30 bg-amber-300/12 px-5 py-2.5 text-sm font-semibold text-amber-100 transition hover:bg-amber-300/20" @@ -12411,13 +12464,13 @@ function AcademyBillingAccount({ currentTier, isSubscribed, subscription, active className: "rounded-full border border-white/10 bg-white/[0.05] px-5 py-3 text-sm font-semibold text-white transition hover:border-white/20 hover:bg-white/[0.08]" }, "Back to Academy" - ))) : null, isSubscribed ? /* @__PURE__ */ React.createElement("section", { className: "grid gap-5 xl:grid-cols-[minmax(0,1fr)_320px]" }, /* @__PURE__ */ React.createElement("div", { className: "space-y-5 rounded-[32px] border border-white/10 bg-white/[0.04] p-6 md:p-7" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400" }, "Subscription details"), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 sm:grid-cols-2" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Active plan"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-lg font-semibold text-white" }, activePlan?.label || "Academy plan"), activePlan?.price_display ? /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, activePlan.price_display, " / month") : null), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Status"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-lg font-semibold capitalize text-white" }, onGracePeriod ? "Cancelling" : subscriptionActive ? "Active" : subscription?.status || "Active"), onGracePeriod && endsAt ? /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-amber-300/80" }, "Access ends ", endsAt) : null, !onGracePeriod && subscriptionActive ? /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-emerald-300/80" }, "Renews automatically") : null)), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Your Academy access"), /* @__PURE__ */ React.createElement("div", { className: "mt-3 flex flex-wrap items-center gap-3" }, /* @__PURE__ */ React.createElement(AccessBadge, { tier: currentTier }), /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-300" }, currentTier === "pro" ? "Full access to all Academy lessons and content." : currentTier === "creator" ? "Full access to all Creator lessons and prompts." : "Access to free Academy content.")))), /* @__PURE__ */ React.createElement("aside", { className: "space-y-3 rounded-[32px] border border-white/10 bg-black/20 p-6" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-300" }, "Manage"), /* @__PURE__ */ React.createElement("p", { className: "text-xs leading-6 text-slate-400" }, "Use the subscription portal to upgrade, downgrade, or cancel. Changes take effect at your next billing date."), /* @__PURE__ */ React.createElement( - xe, + ))) : null, isSubscribed ? /* @__PURE__ */ React.createElement("section", { className: "grid gap-5 xl:grid-cols-[minmax(0,1fr)_320px]" }, /* @__PURE__ */ React.createElement("div", { className: "space-y-5 rounded-[32px] border border-white/10 bg-white/[0.04] p-6 md:p-7" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400" }, "Subscription details"), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 sm:grid-cols-2" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Active plan"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-lg font-semibold text-white" }, activePlan?.label || "Academy plan"), activePlan?.price_display ? /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, activePlan.price_display, " / month") : null), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Status"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-lg font-semibold capitalize text-white" }, onGracePeriod ? "Cancelling" : subscriptionActive ? "Active" : subscription?.status || "Active"), onGracePeriod && endsAt ? /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-amber-300/80" }, "Access ends ", endsAt) : null, !onGracePeriod && subscriptionActive ? /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-emerald-300/80" }, "Renews automatically") : null)), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Your Academy access"), /* @__PURE__ */ React.createElement("div", { className: "mt-3 flex flex-wrap items-center gap-3" }, /* @__PURE__ */ React.createElement(AccessBadge, { tier: currentTier }), /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-300" }, currentTier === "pro" ? "Full access to all Academy lessons and content." : currentTier === "creator" ? "Full access to all Creator lessons and prompts." : "Access to free Academy content.")))), /* @__PURE__ */ React.createElement("aside", { className: "space-y-3 rounded-[32px] border border-white/10 bg-black/20 p-6" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-300" }, "Manage"), /* @__PURE__ */ React.createElement("p", { className: "text-xs leading-6 text-slate-400" }, "Use the subscription portal to cancel or manage billing details. Plan upgrades are handled here on Skinbase."), /* @__PURE__ */ React.createElement( + "a", { href: links.portal, className: "mt-2 inline-flex w-full items-center justify-center rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100 transition hover:border-sky-300/40 hover:bg-sky-300/18" }, - "Upgrade, downgrade or cancel" + "Open billing portal" ), /* @__PURE__ */ React.createElement( xe, { @@ -12425,7 +12478,44 @@ function AcademyBillingAccount({ currentTier, isSubscribed, subscription, active className: "inline-flex w-full items-center justify-center rounded-full border border-white/10 bg-white/[0.05] px-5 py-3 text-sm font-semibold text-white transition hover:border-white/20 hover:bg-white/[0.08]" }, "Compare plans" - ), /* @__PURE__ */ React.createElement( + ), activePlan?.tier === "creator" ? /* @__PURE__ */ React.createElement("form", { action: links.checkout, method: "POST", "data-no-inertia": true, className: "mt-2" }, /* @__PURE__ */ React.createElement("input", { type: "hidden", name: "_token", value: getCsrfToken2() }), /* @__PURE__ */ React.createElement("input", { type: "hidden", name: "plan", value: "pro_monthly" }), /* @__PURE__ */ React.createElement("button", { type: "submit", className: "inline-flex w-full items-center justify-center rounded-full border border-emerald-300/25 bg-emerald-300/10 px-5 py-3 text-sm font-semibold text-emerald-100 transition hover:bg-emerald-300/18" }, "Upgrade to Pro now")) : null, links.reportIssue ? /* @__PURE__ */ React.createElement("div", { className: "mt-3 rounded-2xl border border-amber-300/20 bg-amber-300/8 p-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm font-semibold text-amber-100" }, "Need help with billing or access?"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-xs leading-5 text-amber-100/80" }, "Send a quick report here if payment, access, or subscription changes do not behave as expected."), /* @__PURE__ */ React.createElement( + "form", + { + onSubmit: (event) => { + event.preventDefault(); + post2(links.reportIssue, { preserveScroll: true }); + }, + className: "mt-3 space-y-3" + }, + /* @__PURE__ */ React.createElement("div", { className: "grid gap-3" }, /* @__PURE__ */ React.createElement("label", { className: "space-y-1 relative" }, /* @__PURE__ */ React.createElement("span", { className: "text-xs font-medium text-amber-100/80" }, "Issue type"), /* @__PURE__ */ React.createElement(IssueTypeDropdown, { value: data.issue_type, onChange: (v2) => setData("issue_type", v2) })), /* @__PURE__ */ React.createElement("label", { className: "space-y-1" }, /* @__PURE__ */ React.createElement("span", { className: "text-xs font-medium text-amber-100/80" }, "Reply email"), /* @__PURE__ */ React.createElement( + "input", + { + type: "email", + value: data.contact_email, + onChange: (event) => setData("contact_email", event.target.value), + placeholder: "you@example.com", + className: "w-full rounded-xl border border-amber-300/20 bg-black/20 p-3 text-sm text-amber-50 placeholder:text-amber-100/40" + } + ))), + /* @__PURE__ */ React.createElement( + "textarea", + { + value: data.message, + onChange: (event) => setData("message", event.target.value), + placeholder: "Describe the issue you hit, what you expected, and anything already charged or missing", + className: "min-h-[96px] w-full rounded-xl border border-amber-300/20 bg-black/20 p-3 text-sm text-amber-50 placeholder:text-amber-100/40" + } + ), + /* @__PURE__ */ React.createElement( + "button", + { + type: "submit", + disabled: processing, + className: "inline-flex w-full items-center justify-center rounded-full border border-amber-300/30 bg-amber-300/12 px-5 py-3 text-sm font-semibold text-amber-100 transition hover:bg-amber-300/18 disabled:cursor-not-allowed disabled:opacity-60" + }, + processing ? "Sending report..." : "Send support report" + ) + )) : null, /* @__PURE__ */ React.createElement( xe, { href: links.academy || "/academy", @@ -12495,7 +12585,7 @@ function PlanCard({ product, selectedPlan, currentTier, isSubscribed, activePlan const activeTier = typeof currentTier === "string" ? currentTier.toLowerCase() : "free"; const isActivePlan = selectedPlan?.key === activePlanKey; const isHigherTierCovered = activeTier === "pro" && product.tier === "creator"; - const isPlanReady = Boolean(selectedPlan?.configured && selectedPlan?.price_id_valid); + const isPlanReady = Boolean(selectedPlan?.configured && selectedPlan?.price_id_valid && selectedPlan?.remote_price_exists !== false); const isSubscribedElsewhere = isSubscribed && !isActivePlan; return /* @__PURE__ */ React.createElement("article", { className: `relative overflow-hidden rounded-[32px] border p-6 transition md:p-7 ${isActivePlan ? "border-emerald-300/25 bg-[linear-gradient(180deg,rgba(16,185,129,0.1),rgba(15,23,42,0.96))] shadow-[0_28px_90px_rgba(5,150,105,0.14)]" : product.featured ? "border-sky-300/25 bg-[linear-gradient(180deg,rgba(14,165,233,0.12),rgba(15,23,42,0.96))] shadow-[0_28px_90px_rgba(2,132,199,0.14)]" : "border-white/10 bg-white/[0.04]"}` }, /* @__PURE__ */ React.createElement("div", { className: "absolute inset-x-0 top-0 h-px bg-[linear-gradient(90deg,transparent,rgba(255,255,255,0.45),transparent)]" }), /* @__PURE__ */ React.createElement("div", { className: "flex items-start justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400" }, product.badge), /* @__PURE__ */ React.createElement("h2", { className: "mt-3 text-3xl font-semibold tracking-[-0.05em] text-white" }, product.name), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-7 text-slate-300" }, product.description)), /* @__PURE__ */ React.createElement("div", { className: "flex shrink-0 flex-col items-end gap-2" }, isActivePlan ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-emerald-300/30 bg-emerald-300/14 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-emerald-100" }, "Your plan") : /* @__PURE__ */ React.createElement(AccessBadge, { tier: product.tier }))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 rounded-[24px] border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.2em] text-slate-400" }, "Monthly"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-3xl font-semibold tracking-[-0.04em] text-white" }, selectedPlan?.price_display || "—"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-xs uppercase tracking-[0.18em] text-slate-500" }, "Billed monthly · cancel anytime")), /* @__PURE__ */ React.createElement("div", { className: "mt-6 space-y-3 text-sm text-slate-300" }, product.features.map((feature) => /* @__PURE__ */ React.createElement("div", { key: feature, className: "flex items-start gap-2.5 rounded-2xl border border-white/10 bg-black/20 px-4 py-3" }, /* @__PURE__ */ React.createElement("span", { className: "mt-px shrink-0 text-emerald-400" }, "✓"), /* @__PURE__ */ React.createElement("span", null, feature)))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 space-y-3" }, isActivePlan ? /* @__PURE__ */ React.createElement(ActionButton$1, { href: manageHref, tone: "emerald" }, "Manage subscription") : null, isSubscribedElsewhere && !isHigherTierCovered ? /* @__PURE__ */ React.createElement(ActionButton$1, { href: manageHref, tone: "default" }, "Switch to ", product.name) : null, isHigherTierCovered && !isActivePlan ? /* @__PURE__ */ React.createElement("p", { className: "text-center text-xs text-slate-500" }, "Included in your Pro plan") : null, !isSubscribed && loginHref ? /* @__PURE__ */ React.createElement(ActionButton$1, { href: loginHref, tone: "primary" }, billingEnabled ? `Get ${product.name}` : "Coming soon") : null, !isSubscribed && !loginHref ? /* @__PURE__ */ React.createElement( ActionButton$1, @@ -12801,7 +12891,7 @@ function SidePanel({ currentTier, isSubscribed, activePlanLabel, activePlanPrice { title: "Switch freely", body: "Move between Creator and Pro from your subscription manager." } ].map(({ title, body: body2 }) => /* @__PURE__ */ React.createElement("div", { key: title, className: "rounded-2xl border border-white/10 bg-white/[0.03] px-4 py-3" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm font-semibold text-white" }, title), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-xs leading-5 text-slate-400" }, body2))))); } -function AcademyBillingPricing({ seo, billingEnabled, currentTier, isSubscribed, activePlanKey = null, activePlanLabel = null, catalog = [], links = {}, analytics }) { +function AcademyBillingPricing({ seo, billingEnabled, currentTier, isSubscribed, activePlanKey = null, activePlanLabel = null, catalog = [], links = {}, analytics, missingRemote = [] }) { const { auth, errors, flash } = X$1().props; useAcademyPageAnalytics(analytics); const loginHref = auth?.user ? null : `${links.login || "/login"}?intended=${encodeURIComponent(links.pricing || "/academy/pricing")}`; @@ -12836,7 +12926,7 @@ function AcademyBillingPricing({ seo, billingEnabled, currentTier, isSubscribed, }; const hero = heroText(currentTier, isSubscribed); const showFreeBadgeAsCurrentPlan = currentTier === "free" && !isSubscribed && auth?.user; - return /* @__PURE__ */ React.createElement("main", { className: "min-h-screen bg-[radial-gradient(circle_at_top_left,_rgba(251,191,36,0.16),_transparent_22%),radial-gradient(circle_at_bottom_right,_rgba(56,189,248,0.18),_transparent_26%),linear-gradient(180deg,_#07111f_0%,_#0f172a_45%,_#111827_100%)] px-4 py-8 sm:px-6 lg:px-8" }, /* @__PURE__ */ React.createElement(SeoHead, { seo: seo || {}, title: "Skinbase Academy — Plans & Pricing", description: seo?.description }), /* @__PURE__ */ React.createElement("div", { className: "mx-auto max-w-[1380px] space-y-8" }, /* @__PURE__ */ React.createElement("section", { className: "overflow-hidden rounded-[40px] border border-white/10 bg-[linear-gradient(135deg,rgba(7,17,31,0.95),rgba(12,24,45,0.9),rgba(67,20,7,0.82))] p-8 shadow-[0_32px_100px_rgba(2,6,23,0.42)] md:p-10 lg:p-12" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-8 xl:grid-cols-[minmax(0,1fr)_320px] xl:items-start" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-3" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-amber-200/85" }, "Skinbase Academy"), currentTier !== "free" ? /* @__PURE__ */ React.createElement(AccessBadge, { tier: currentTier }) : null), /* @__PURE__ */ React.createElement("h1", { className: "mt-4 max-w-3xl text-4xl font-semibold tracking-[-0.055em] text-white md:text-5xl xl:text-6xl" }, hero.heading), /* @__PURE__ */ React.createElement("p", { className: "mt-5 max-w-2xl text-base leading-8 text-slate-300 md:text-lg" }, hero.body), errors?.plan ? /* @__PURE__ */ React.createElement("p", { className: "mt-4 text-sm font-medium text-rose-200" }, errors.plan) : null, flash?.error ? /* @__PURE__ */ React.createElement("p", { className: "mt-4 rounded-2xl border border-rose-300/20 bg-rose-300/10 px-4 py-3 text-sm font-medium text-rose-100" }, flash.error) : null, flash?.success ? /* @__PURE__ */ React.createElement("p", { className: "mt-4 rounded-2xl border border-emerald-300/20 bg-emerald-300/10 px-4 py-3 text-sm font-medium text-emerald-100" }, flash.success) : null), /* @__PURE__ */ React.createElement( + return /* @__PURE__ */ React.createElement("main", { className: "min-h-screen bg-[radial-gradient(circle_at_top_left,_rgba(251,191,36,0.16),_transparent_22%),radial-gradient(circle_at_bottom_right,_rgba(56,189,248,0.18),_transparent_26%),linear-gradient(180deg,_#07111f_0%,_#0f172a_45%,_#111827_100%)] px-4 py-8 sm:px-6 lg:px-8" }, /* @__PURE__ */ React.createElement(SeoHead, { seo: seo || {}, title: "Skinbase Academy — Plans & Pricing", description: seo?.description }), /* @__PURE__ */ React.createElement("div", { className: "mx-auto max-w-[1380px] space-y-8" }, /* @__PURE__ */ React.createElement("section", { className: "overflow-hidden rounded-[40px] border border-white/10 bg-[linear-gradient(135deg,rgba(7,17,31,0.95),rgba(12,24,45,0.9),rgba(67,20,7,0.82))] p-8 shadow-[0_32px_100px_rgba(2,6,23,0.42)] md:p-10 lg:p-12" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-8 xl:grid-cols-[minmax(0,1fr)_320px] xl:items-start" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-3" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-amber-200/85" }, "Skinbase Academy"), currentTier !== "free" ? /* @__PURE__ */ React.createElement(AccessBadge, { tier: currentTier }) : null), /* @__PURE__ */ React.createElement("h1", { className: "mt-4 max-w-3xl text-4xl font-semibold tracking-[-0.055em] text-white md:text-5xl xl:text-6xl" }, hero.heading), /* @__PURE__ */ React.createElement("p", { className: "mt-5 max-w-2xl text-base leading-8 text-slate-300 md:text-lg" }, hero.body), errors?.plan ? /* @__PURE__ */ React.createElement("p", { className: "mt-4 text-sm font-medium text-rose-200" }, errors.plan) : null, flash?.error ? /* @__PURE__ */ React.createElement("p", { className: "mt-4 rounded-2xl border border-rose-300/20 bg-rose-300/10 px-4 py-3 text-sm font-medium text-rose-100" }, flash.error) : null, flash?.success ? /* @__PURE__ */ React.createElement("p", { className: "mt-4 rounded-2xl border border-emerald-300/20 bg-emerald-300/10 px-4 py-3 text-sm font-medium text-emerald-100" }, flash.success) : null, Array.isArray(missingRemote) && missingRemote.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "mt-4 rounded-2xl border border-amber-300/20 bg-amber-300/8 px-4 py-3 text-sm font-medium text-amber-50" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold" }, "Purchases temporarily disabled:"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-xs" }, "The following plans could not be verified in Stripe: ", missingRemote.join(", "))) : null), /* @__PURE__ */ React.createElement( SidePanel, { currentTier, @@ -12871,7 +12961,14 @@ const __vite_glob_0_2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.def default: AcademyBillingPricing }, Symbol.toStringTag, { value: "Module" })); function AcademyBillingSuccess({ currentTier, isSubscribed, links = {} }) { - return /* @__PURE__ */ React.createElement("main", { className: "flex min-h-screen items-center bg-[radial-gradient(circle_at_top_left,_rgba(56,189,248,0.14),_transparent_24%),radial-gradient(circle_at_bottom_right,_rgba(16,185,129,0.14),_transparent_24%),linear-gradient(180deg,_#07111f_0%,_#0f172a_45%,_#111827_100%)] px-4 py-8 sm:px-6 lg:px-8" }, /* @__PURE__ */ React.createElement(Se$1, { title: "Subscription Confirmed" }), /* @__PURE__ */ React.createElement("div", { className: "mx-auto w-full max-w-[640px] space-y-6" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[40px] border border-emerald-300/20 bg-[linear-gradient(135deg,rgba(7,17,31,0.95),rgba(12,24,45,0.92),rgba(6,78,59,0.82))] p-8 shadow-[0_32px_100px_rgba(2,6,23,0.42)] md:p-10" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ React.createElement("span", { className: "text-3xl leading-none" }, "🎉"), isSubscribed ? /* @__PURE__ */ React.createElement(AccessBadge, { tier: currentTier }) : null), /* @__PURE__ */ React.createElement("h1", { className: "mt-5 text-4xl font-semibold tracking-[-0.05em] text-white md:text-5xl" }, isSubscribed ? "Welcome to Academy." : "You're all set."), /* @__PURE__ */ React.createElement("p", { className: "mt-4 max-w-lg text-base leading-8 text-slate-300" }, isSubscribed ? "Your subscription is active and all premium content for your plan is now unlocked. Head to Academy and start exploring." : "Your payment was confirmed and your subscription is activating now. This usually takes just a moment. If you don't see your access right away, refresh the Academy page in a few seconds.")), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-3" }, /* @__PURE__ */ React.createElement( + const { auth } = X$1().props; + const sessionId = X$1().props.sessionId || null; + const userEmail = auth?.user?.email ?? null; + const { data, setData, post: post2, processing } = G$1({ message: "", session_id: sessionId }); + return /* @__PURE__ */ React.createElement("main", { className: "flex min-h-screen items-center bg-[radial-gradient(circle_at_top_left,_rgba(56,189,248,0.14),_transparent_24%),radial-gradient(circle_at_bottom_right,_rgba(16,185,129,0.14),_transparent_24%),linear-gradient(180deg,_#07111f_0%,_#0f172a_45%,_#111827_100%)] px-4 py-8 sm:px-6 lg:px-8" }, /* @__PURE__ */ React.createElement(Se$1, { title: "Subscription Confirmed" }), /* @__PURE__ */ React.createElement("div", { className: "mx-auto w-full max-w-[640px] space-y-6" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[40px] border border-emerald-300/20 bg-[linear-gradient(135deg,rgba(7,17,31,0.95),rgba(12,24,45,0.92),rgba(6,78,59,0.82))] p-8 shadow-[0_32px_100px_rgba(2,6,23,0.42)] md:p-10" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ React.createElement("span", { className: "text-3xl leading-none" }, "🎉"), isSubscribed ? /* @__PURE__ */ React.createElement(AccessBadge, { tier: currentTier }) : null), /* @__PURE__ */ React.createElement("h1", { className: "mt-5 text-4xl font-semibold tracking-[-0.05em] text-white md:text-5xl" }, isSubscribed ? "Welcome to Academy." : "You're all set."), /* @__PURE__ */ React.createElement("p", { className: "mt-4 max-w-lg text-base leading-8 text-slate-300" }, isSubscribed ? "Your subscription is active and all premium content for your plan is now unlocked. Head to Academy and start exploring." : "Your payment was confirmed and your subscription is activating now. This usually takes just a moment. If you don't see your access right away, refresh the Academy page in a few seconds."), !isSubscribed ? /* @__PURE__ */ React.createElement("div", { className: "mt-4 rounded-2xl border border-amber-300/20 bg-amber-300/8 px-4 py-3 text-sm text-amber-50" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold" }, "If your access isn't updated automatically"), /* @__PURE__ */ React.createElement("p", { className: "mt-1" }, "If your Academy access doesn't appear within a few minutes, email ", /* @__PURE__ */ React.createElement("strong", null, "academy@skinbase.org"), " or click the button below to open a prefilled message. Include your account email", userEmail ? ` (${userEmail})` : "", " and the checkout session id", sessionId ? `: ${sessionId}` : "."), /* @__PURE__ */ React.createElement("div", { className: "mt-3 flex flex-wrap items-start gap-4" }, /* @__PURE__ */ React.createElement("form", { onSubmit: (e) => { + e.preventDefault(); + post2(links.reportIssue, { preserveScroll: true }); + }, className: "flex w-full max-w-lg items-start gap-2" }, /* @__PURE__ */ React.createElement("textarea", { value: data.message, onChange: (e) => setData("message", e.target.value), placeholder: "Optional: Tell us what you expected to see or any useful details", className: "flex-1 rounded-md bg-black/20 border border-amber-300/20 p-2 text-sm text-amber-50", rows: 3 }), /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: processing, className: "rounded-full border border-amber-300/30 bg-amber-300/12 px-4 py-2 text-sm font-semibold text-amber-900 hover:bg-amber-300/16" }, "Send report")), /* @__PURE__ */ React.createElement("div", { className: "text-xs text-amber-100" }, /* @__PURE__ */ React.createElement("div", null, "- Wait 2–3 minutes and refresh the Academy page."), /* @__PURE__ */ React.createElement("div", null, "- If you still lack access, use the form above or email academy@skinbase.org.")))) : null), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-3" }, /* @__PURE__ */ React.createElement( xe, { href: links.academy || "/academy", @@ -14448,6 +14545,131 @@ function PromptPlaceholderCard({ placeholder }) { }; return /* @__PURE__ */ React.createElement("article", { className: "rounded-[28px] border border-white/10 bg-[linear-gradient(180deg,rgba(255,255,255,0.04),rgba(15,23,42,0.2))] p-5 shadow-[0_16px_40px_rgba(2,6,23,0.16)]" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-start justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400" }, "Placeholder"), /* @__PURE__ */ React.createElement("code", { className: "mt-3 inline-flex rounded-full border border-white/10 bg-black/25 px-3 py-1.5 font-mono text-sm text-white" }, "[", placeholder.key || "VALUE", "]"), placeholder.label ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-lg font-semibold tracking-[-0.03em] text-white" }, placeholder.label) : null), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, placeholder.type ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-300" }, placeholder.type) : null, placeholder.required ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-amber-300/20 bg-amber-300/10 px-3 py-1 text-[10px] font-semibold uppercase tracking-[0.18em] text-amber-100" }, "Required") : null)), placeholder.description ? /* @__PURE__ */ React.createElement("p", { className: "mt-4 text-sm leading-7 text-slate-300" }, placeholder.description) : null, example != null && example !== "" ? /* @__PURE__ */ React.createElement("div", { className: "mt-4 rounded-[22px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400" }, "Example"), renderValue(example)) : null, defaultValue != null && defaultValue !== "" ? /* @__PURE__ */ React.createElement("div", { className: "mt-4 rounded-[22px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400" }, "Default"), renderValue(defaultValue)) : null); } +function PromptFilledExampleCard({ example, analytics, contentId, index: index2 }) { + const placeholderEntries = Object.entries(example?.placeholder_values || {}).filter(([key, value]) => String(key || "").trim() && value != null && value !== "" && value !== false); + return /* @__PURE__ */ React.createElement("article", { className: "rounded-[28px] border border-white/10 bg-black/20 p-5 shadow-[0_18px_42px_rgba(2,6,23,0.16)]" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-start justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-violet-200/75" }, "Filled example ", index2 + 1), /* @__PURE__ */ React.createElement("h3", { className: "mt-2 text-xl font-semibold tracking-[-0.03em] text-white" }, example?.title || `Example ${index2 + 1}`), example?.description ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-7 text-slate-300" }, example.description) : null), example?.prompt ? /* @__PURE__ */ React.createElement( + PromptCopyButton, + { + prompt: example.prompt, + label: "Copy example", + analytics, + contentId, + eventType: "academy_prompt_filled_example_copy", + metadata: { copy_type: "filled_example", filled_example_index: index2, source: "prompt_filled_examples" } + } + ) : null), placeholderEntries.length ? /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-wrap gap-2" }, placeholderEntries.map(([key, value]) => /* @__PURE__ */ React.createElement("span", { key, className: "rounded-full border border-violet-300/20 bg-violet-300/10 px-3 py-1.5 text-xs font-semibold uppercase tracking-[0.16em] text-violet-100" }, key, ": ", /* @__PURE__ */ React.createElement("span", { className: "normal-case tracking-normal text-white" }, String(value))))) : null, example?.prompt ? /* @__PURE__ */ React.createElement("pre", { className: "mt-5 whitespace-pre-wrap rounded-[24px] border border-white/10 bg-slate-950/80 p-4 text-sm leading-7 text-slate-100" }, example.prompt) : null, example?.negative_prompt ? /* @__PURE__ */ React.createElement("div", { className: "mt-4 rounded-[24px] border border-white/10 bg-slate-950/60 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400" }, "Negative prompt"), /* @__PURE__ */ React.createElement( + PromptCopyButton, + { + prompt: example.negative_prompt, + label: "Copy negative", + analytics, + contentId, + eventType: "academy_prompt_filled_example_negative_copy", + metadata: { copy_type: "filled_example_negative", filled_example_index: index2, source: "prompt_filled_examples" } + } + )), /* @__PURE__ */ React.createElement("pre", { className: "mt-3 whitespace-pre-wrap text-sm leading-7 text-slate-200" }, example.negative_prompt)) : null); +} +function PromptFilledExamplesSection({ examples, analytics, contentId }) { + const visibleExamples = Array.isArray(examples) ? examples.filter((example) => example && typeof example === "object") : []; + const [activeExampleIndex, setActiveExampleIndex] = reactExports.useState(0); + const examplesScrollRef = reactExports.useRef(null); + const [canScrollExamplesLeft, setCanScrollExamplesLeft] = reactExports.useState(false); + const [canScrollExamplesRight, setCanScrollExamplesRight] = reactExports.useState(false); + reactExports.useEffect(() => { + if (typeof window === "undefined") { + return void 0; + } + const updateExampleScrollState = () => { + const element22 = examplesScrollRef.current; + if (!element22) { + setCanScrollExamplesLeft(false); + setCanScrollExamplesRight(false); + return; + } + const maxScrollLeft = Math.max(0, element22.scrollWidth - element22.clientWidth); + setCanScrollExamplesLeft(element22.scrollLeft > 6); + setCanScrollExamplesRight(element22.scrollLeft < maxScrollLeft - 6); + }; + updateExampleScrollState(); + const element2 = examplesScrollRef.current; + if (!element2) { + return void 0; + } + element2.addEventListener("scroll", updateExampleScrollState, { passive: true }); + window.addEventListener("resize", updateExampleScrollState, { passive: true }); + return () => { + element2.removeEventListener("scroll", updateExampleScrollState); + window.removeEventListener("resize", updateExampleScrollState); + }; + }, [visibleExamples.length]); + reactExports.useEffect(() => { + if (!visibleExamples.length) { + setActiveExampleIndex(0); + return; + } + setActiveExampleIndex((current) => Math.max(0, Math.min(current, visibleExamples.length - 1))); + }, [visibleExamples.length]); + if (!visibleExamples.length) return null; + const activeExample = visibleExamples[activeExampleIndex] || visibleExamples[0]; + const activeExampleLabel = String(activeExample?.title || "").trim() || `Example ${activeExampleIndex + 1}`; + const activeExampleDescription = String(activeExample?.description || "").trim(); + const scrollExamples = (direction) => { + const element2 = examplesScrollRef.current; + if (!element2) return; + const amount = Math.max(220, Math.floor(element2.clientWidth * 0.65)); + element2.scrollBy({ + left: direction === "left" ? -amount : amount, + behavior: "smooth" + }); + }; + return /* @__PURE__ */ React.createElement("div", { className: "mt-6 space-y-5" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-violet-300/15 bg-violet-300/10 p-5 md:p-6" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-violet-100/80" }, "Selected example"), /* @__PURE__ */ React.createElement("h3", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white md:text-[2rem]" }, activeExampleLabel), activeExampleDescription ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-7 text-slate-200 md:text-base" }, activeExampleDescription) : null), /* @__PURE__ */ React.createElement("div", { className: "relative" }, /* @__PURE__ */ React.createElement("div", { className: `pointer-events-none absolute inset-y-0 left-0 z-10 w-14 bg-gradient-to-r from-[#211c3a] via-[#211c3a]/85 to-transparent transition ${canScrollExamplesLeft ? "opacity-100" : "opacity-0"}`, "aria-hidden": "true" }), /* @__PURE__ */ React.createElement("div", { className: `pointer-events-none absolute inset-y-0 right-0 z-10 w-14 bg-gradient-to-l from-[#211c3a] via-[#211c3a]/85 to-transparent transition ${canScrollExamplesRight ? "opacity-100" : "opacity-0"}`, "aria-hidden": "true" }), /* @__PURE__ */ React.createElement( + "button", + { + type: "button", + "aria-label": "Scroll filled examples left", + onClick: () => scrollExamples("left"), + className: `absolute left-2 top-1/2 z-20 flex h-10 w-10 -translate-y-1/2 items-center justify-center rounded-full border border-white/12 bg-slate-950/80 text-white/80 shadow-[0_16px_36px_rgba(2,6,23,0.28)] backdrop-blur transition ${canScrollExamplesLeft ? "opacity-100 hover:scale-105 hover:bg-slate-900/95" : "pointer-events-none opacity-0"}` + }, + /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-chevron-left text-sm" }) + ), /* @__PURE__ */ React.createElement( + "button", + { + type: "button", + "aria-label": "Scroll filled examples right", + onClick: () => scrollExamples("right"), + className: `absolute right-2 top-1/2 z-20 flex h-10 w-10 -translate-y-1/2 items-center justify-center rounded-full border border-white/12 bg-slate-950/80 text-white/80 shadow-[0_16px_36px_rgba(2,6,23,0.28)] backdrop-blur transition ${canScrollExamplesRight ? "opacity-100 hover:scale-105 hover:bg-slate-900/95" : "pointer-events-none opacity-0"}` + }, + /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-chevron-right text-sm" }) + ), /* @__PURE__ */ React.createElement("div", { ref: examplesScrollRef, className: "overflow-x-auto pb-1 scrollbar-none [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden" }, /* @__PURE__ */ React.createElement("div", { className: "inline-flex min-w-full gap-2.5 px-1 py-1" }, visibleExamples.map((example, index2) => { + const isActive = index2 === activeExampleIndex; + const exampleLabel = String(example?.title || "").trim() || `Example ${index2 + 1}`; + return /* @__PURE__ */ React.createElement( + "button", + { + key: `${example.title || "filled-example-tab"}-${index2}`, + type: "button", + onClick: () => setActiveExampleIndex(index2), + "aria-pressed": isActive, + title: exampleLabel, + className: `max-w-full whitespace-nowrap rounded-full border px-4 py-2.5 text-sm font-semibold uppercase tracking-[0.18em] transition ${isActive ? "border-violet-300/30 bg-violet-300/18 text-white shadow-[0_12px_30px_rgba(76,29,149,0.24)]" : "border-white/10 bg-white/[0.04] text-violet-100/80 hover:border-violet-300/20 hover:bg-violet-300/10 hover:text-white"}` + }, + /* @__PURE__ */ React.createElement("span", { className: "block max-w-[240px] truncate" }, exampleLabel) + ); + })))), /* @__PURE__ */ React.createElement( + PromptFilledExampleCard, + { + key: `${activeExample.title || "filled-example-active"}-${activeExampleIndex}`, + example: activeExample, + analytics, + contentId, + index: activeExampleIndex + } + )); +} +function PromptHelperPromptCard({ helperPrompt, analytics, contentId }) { + if (!helperPrompt || typeof helperPrompt !== "object") return null; + return /* @__PURE__ */ React.createElement("details", { className: "group rounded-[28px] border border-white/10 bg-[linear-gradient(180deg,rgba(255,255,255,0.04),rgba(15,23,42,0.2))] p-5 shadow-[0_16px_40px_rgba(2,6,23,0.16)]" }, /* @__PURE__ */ React.createElement("summary", { className: "flex cursor-pointer list-none flex-wrap items-start justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-[#ffd8cd]" }, "Helper prompt"), /* @__PURE__ */ React.createElement("h3", { className: "mt-2 text-xl font-semibold tracking-[-0.03em] text-white" }, helperPrompt.title || "Helper prompt"), helperPrompt.description ? /* @__PURE__ */ React.createElement("p", { className: "mt-2 max-w-2xl text-sm leading-7 text-slate-300" }, helperPrompt.description) : null), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, helperPrompt.type ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-300" }, formatMetaDisplay(helperPrompt.type)) : null, helperPrompt.expected_output ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-sky-300/18 bg-sky-300/10 px-3 py-1 text-[10px] font-semibold uppercase tracking-[0.18em] text-sky-100" }, helperPrompt.expected_output) : null)), /* @__PURE__ */ React.createElement("div", { className: "mt-5 rounded-[24px] border border-white/10 bg-black/25 p-4 md:p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400" }, "Prompt text"), /* @__PURE__ */ React.createElement(PromptCopyButton, { prompt: helperPrompt.prompt, label: "Copy helper prompt", analytics, contentId, eventType: "academy_prompt_helper_copy", metadata: { copy_type: "helper_prompt", helper_prompt_title: helperPrompt.title || "", source: "prompt_helper" } })), /* @__PURE__ */ React.createElement("pre", { className: "mt-4 whitespace-pre-wrap rounded-[22px] border border-white/10 bg-slate-950/70 p-4 text-sm leading-7 text-slate-100" }, helperPrompt.prompt))); +} function PromptVariantCard({ variant, analytics, contentId }) { if (!variant || typeof variant !== "object") return null; return /* @__PURE__ */ React.createElement("article", { className: `rounded-[28px] border p-5 shadow-[0_16px_40px_rgba(2,6,23,0.16)] ${variant.recommended ? "border-[#ffcfbf]/22 bg-[linear-gradient(180deg,rgba(255,207,191,0.08),rgba(15,23,42,0.24))]" : "border-white/10 bg-[linear-gradient(180deg,rgba(255,255,255,0.04),rgba(15,23,42,0.2))]"}` }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-start justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400" }, "Prompt variant"), /* @__PURE__ */ React.createElement("h3", { className: "mt-2 text-xl font-semibold tracking-[-0.03em] text-white" }, variant.title || "Variant"), variant.description ? /* @__PURE__ */ React.createElement("p", { className: "mt-2 max-w-2xl text-sm leading-7 text-slate-300" }, variant.description) : null), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, variant.recommended ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-[#ffcfbf]/22 bg-[#ffcfbf]/12 px-3 py-1 text-[10px] font-semibold uppercase tracking-[0.18em] text-[#fff0ea]" }, "Recommended") : null, variant.slug ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 font-mono text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-300" }, variant.slug) : null)), variant.recommended_for?.length ? /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2" }, variant.recommended_for.map((item) => /* @__PURE__ */ React.createElement("span", { key: item, className: "rounded-full border border-sky-300/20 bg-sky-300/10 px-3 py-1.5 text-xs font-semibold uppercase tracking-[0.18em] text-sky-100" }, item))) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-5 rounded-[24px] border border-white/10 bg-black/25 p-4 md:p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400" }, "Variant prompt"), /* @__PURE__ */ React.createElement(PromptCopyButton, { prompt: variant.prompt, label: "Copy variant", analytics, contentId, eventType: "academy_prompt_variant_copy", metadata: { copy_type: "prompt_variant", variant_title: variant.title || "", source: "prompt_variant" } })), /* @__PURE__ */ React.createElement("pre", { className: "mt-4 whitespace-pre-wrap rounded-[22px] border border-white/10 bg-slate-950/70 p-4 text-sm leading-7 text-slate-100" }, variant.prompt)), variant.negative_prompt ? /* @__PURE__ */ React.createElement("div", { className: "mt-4 rounded-[24px] border border-white/10 bg-black/20 p-4 md:p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400" }, "Negative prompt"), /* @__PURE__ */ React.createElement(PromptCopyButton, { prompt: variant.negative_prompt, label: "Copy negative", analytics, contentId, eventType: "academy_prompt_variant_negative_copy", metadata: { copy_type: "prompt_variant_negative", variant_title: variant.title || "", source: "prompt_variant" } })), /* @__PURE__ */ React.createElement("pre", { className: "mt-4 whitespace-pre-wrap rounded-[22px] border border-white/10 bg-slate-950/70 p-4 text-sm leading-7 text-slate-200" }, variant.negative_prompt)) : null, variant.risk_notes?.length ? /* @__PURE__ */ React.createElement("div", { className: "mt-4 rounded-[22px] border border-amber-300/15 bg-amber-300/10 p-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-amber-100" }, "Risk notes"), /* @__PURE__ */ React.createElement("ul", { className: "mt-3 space-y-2 text-sm leading-7 text-slate-100" }, variant.risk_notes.map((item) => /* @__PURE__ */ React.createElement("li", { key: item }, item)))) : null); @@ -14681,9 +14903,21 @@ function AcademyShow({ pageType, item, relatedLessons = [], relatedCourses = [], promptDocumentation.summary || promptDocumentation.display_notes || promptDocumentation.best_for.length || promptDocumentation.how_to_use.length || promptDocumentation.required_inputs.length || promptDocumentation.workflow.length || promptDocumentation.tips.length || promptDocumentation.common_mistakes.length || promptDocumentation.data_accuracy_notes.length ); const hasPromptPlaceholders = Boolean(item?.has_placeholder_inputs) && promptPlaceholders.length > 0; - Boolean(item?.has_helper_prompts) && !promptHasFullAccess; + const promptFilledExamples = Array.isArray(item?.filled_examples) ? item.filled_examples.filter((example) => example && typeof example === "object" && [ + example.title, + example.description, + example.prompt, + example.negative_prompt, + ...example.placeholder_values && typeof example.placeholder_values === "object" ? Object.values(example.placeholder_values) : [] + ].some((value) => value != null && value !== "" && value !== false)) : []; + const hasPromptFilledExamples = promptFilledExamples.length > 0; + const promptFilledExamplesTotal = Number(item?.filled_examples_total || promptFilledExamples.length || 0); + const promptHasMoreFilledExamples = Boolean(item?.has_more_filled_examples) || promptFilledExamplesTotal > promptFilledExamples.length; + Boolean(item?.has_full_filled_examples_access); + const promptHasLockedFilledExamples = Boolean(item?.has_filled_examples) && (!Boolean(item?.can_access_filled_examples) || promptHasMoreFilledExamples); + const promptHasLockedHelperPrompts = Boolean(item?.has_helper_prompts) && !promptHasFullAccess; const promptHasLockedVariants = Boolean(item?.has_prompt_variants) && !promptHasFullAccess; - promptHelperPrompts.length > 0; + const hasPromptHelperPrompts = promptHelperPrompts.length > 0; const hasPromptVariants = promptVariants.length > 0; const promptAccessRequirement = item?.access_requirement || promptRequirementText(item?.access_level); const promptUnlockTitle = item?.unlock_heading || promptUnlockHeading(item?.access_level); @@ -15168,7 +15402,7 @@ function AcademyShow({ pageType, item, relatedLessons = [], relatedCourses = [], eventType: "academy_prompt_negative_copy", metadata: { copy_type: "negative_prompt", source: "prompt_body" } } - )), /* @__PURE__ */ React.createElement("pre", { className: "mt-4 whitespace-pre-wrap rounded-[24px] border border-white/10 bg-slate-950/70 p-4 text-sm leading-7 text-slate-200 md:p-5" }, item.negative_prompt)) : null)), promptUsageNotes || promptWorkflowNotes ? /* @__PURE__ */ React.createElement("section", { className: "academy-paywalled-content rounded-[32px] border border-white/10 bg-[linear-gradient(180deg,rgba(15,23,42,0.92),rgba(15,23,42,0.82))] p-6 text-slate-200 shadow-[0_18px_50px_rgba(2,6,23,0.18)] md:p-8" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-end justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-slate-400" }, "Prompt guidance"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white" }, "How to use this prompt")), !promptHasFullAccess ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-amber-300/20 bg-amber-300/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-amber-100" }, "Full notes visible with access") : null), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-5 md:grid-cols-2" }, promptUsageNotes ? /* @__PURE__ */ React.createElement("div", { className: "rounded-[26px] border border-white/10 bg-black/20 p-5" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-sky-200/75" }, "Usage notes"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 whitespace-pre-wrap text-sm leading-7 text-slate-200" }, promptUsageNotes)) : null, promptWorkflowNotes ? /* @__PURE__ */ React.createElement("div", { className: "rounded-[26px] border border-white/10 bg-black/20 p-5" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-emerald-200/75" }, "Workflow notes"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 whitespace-pre-wrap text-sm leading-7 text-slate-200" }, promptWorkflowNotes)) : null)) : null, hasPromptDocumentation ? /* @__PURE__ */ React.createElement(PromptDocumentationPanel, { documentation: promptDocumentation }) : null, hasPromptPlaceholders ? /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-[linear-gradient(180deg,rgba(255,255,255,0.045),rgba(148,163,184,0.03))] p-6 text-slate-200 shadow-[0_24px_70px_rgba(2,6,23,0.2)] md:p-8" }, /* @__PURE__ */ React.createElement("div", { className: "max-w-3xl" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-slate-400" }, "Data"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white" }, "Placeholders and required inputs"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-7 text-slate-300" }, "Prepare these variables before using the final prompt so the output stays consistent and reusable.")), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-5 xl:grid-cols-2" }, promptPlaceholders.map((placeholder, index2) => /* @__PURE__ */ React.createElement(PromptPlaceholderCard, { key: `${placeholder.key || "placeholder"}-${index2}`, placeholder })))) : null, null, null, hasPromptVariants ? /* @__PURE__ */ React.createElement(PromptVariantsSection, { variants: promptVariants, analytics, contentId: item.id }) : null, promptHasLockedVariants ? /* @__PURE__ */ React.createElement("section", { className: "academy-paywalled-content rounded-[32px] border border-white/10 bg-[linear-gradient(180deg,rgba(255,255,255,0.045),rgba(148,163,184,0.03))] p-6 text-slate-200 shadow-[0_24px_70px_rgba(2,6,23,0.2)] md:p-8" }, /* @__PURE__ */ React.createElement("div", { className: "max-w-3xl" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-slate-400" }, "Variants"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white" }, "Alternative prompt versions are included"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-7 text-slate-300" }, "This prompt includes recommended or model-specific variants, but they stay locked until your Academy access level matches the template.")), /* @__PURE__ */ React.createElement("div", { className: "mt-6" }, /* @__PURE__ */ React.createElement(LockedPanel, { pricingUrl, label: "prompt", accessLevel: item?.access_level, onUpgrade: () => trackUpgradeClick(analytics, { source: "prompt_variant_locked_panel" }) }))) : null, promptComparisons.length ? /* @__PURE__ */ React.createElement("section", { className: "academy-paywalled-content rounded-[32px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(255,183,139,0.12),transparent_30%),linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.92))] p-6 text-slate-200 shadow-[0_24px_80px_rgba(2,6,23,0.28)] md:p-8" }, /* @__PURE__ */ React.createElement("div", { className: "max-w-3xl" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-[#ffcfbf]" }, "AI model comparisons"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white md:text-3xl" }, "How different models respond to the same prompt"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-7 text-slate-300 md:text-base" }, "Use these notes to decide which provider fits the result you want before you start tuning or post-processing.")), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-5 xl:grid-cols-2" }, promptComparisons.map((note, index2) => /* @__PURE__ */ React.createElement(PromptToolNoteCard, { key: `${note.provider || "provider"}-${note.model_name || "model"}-${index2}`, note, index: index2, galleryIndex: index2, onOpenImage: openPromptComparisonGallery })))) : null), /* @__PURE__ */ React.createElement("aside", { className: "space-y-6 lg:sticky lg:top-6 lg:self-start" }, lessonTags.length ? /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-[linear-gradient(180deg,rgba(15,23,42,0.92),rgba(15,23,42,0.84))] p-6 text-slate-200 shadow-[0_18px_50px_rgba(2,6,23,0.18)]" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-slate-400" }, "Microtags"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2" }, lessonTags.map((tag) => /* @__PURE__ */ React.createElement("span", { key: tag, className: "rounded-full border border-sky-300/20 bg-sky-300/10 px-3 py-1.5 text-xs font-semibold uppercase tracking-[0.18em] text-sky-100" }, tag)))) : null, /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-[linear-gradient(180deg,rgba(15,23,42,0.92),rgba(15,23,42,0.84))] p-6 text-slate-200 shadow-[0_18px_50px_rgba(2,6,23,0.18)]" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-slate-400" }, "Best use case"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-7 text-slate-300" }, promptBestUseCase))))) : /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-white/[0.04] p-6 text-slate-200" }, pageType === "pack" ? /* @__PURE__ */ React.createElement("div", { className: "space-y-5" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm leading-8 text-slate-200" }, item.description), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 md:grid-cols-2" }, (item.prompts || []).map((prompt) => /* @__PURE__ */ React.createElement("div", { key: prompt.id, className: "rounded-2xl border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("h3", { className: "text-lg font-semibold text-white" }, prompt.title), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm leading-7 text-slate-300" }, prompt.excerpt || prompt.prompt_preview))))) : null, pageType === "challenge" ? /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-6 md:grid-cols-2" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400" }, "Brief"), /* @__PURE__ */ React.createElement("div", { className: "mt-3 whitespace-pre-wrap text-sm leading-8 text-slate-200" }, item.brief || item.description)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400" }, "Rules"), /* @__PURE__ */ React.createElement("div", { className: "mt-3 whitespace-pre-wrap text-sm leading-8 text-slate-200" }, item.rules || "No special rules posted yet."))), (item.submissions || []).length ? /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400" }, "Approved submissions"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-4 md:grid-cols-2" }, item.submissions.map((submission) => /* @__PURE__ */ React.createElement("div", { key: submission.id, className: "rounded-2xl border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("h3", { className: "text-lg font-semibold text-white" }, submission.artwork?.title || "Submission"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-slate-400" }, submission.user?.name || "Unknown creator"))))) : null) : null)), /* @__PURE__ */ React.createElement(ImageLightbox, { gallery: lightboxGallery, onClose: () => setLightboxGallery(null), onNavigate: navigateLightboxGallery })); + )), /* @__PURE__ */ React.createElement("pre", { className: "mt-4 whitespace-pre-wrap rounded-[24px] border border-white/10 bg-slate-950/70 p-4 text-sm leading-7 text-slate-200 md:p-5" }, item.negative_prompt)) : null)), promptUsageNotes || promptWorkflowNotes ? /* @__PURE__ */ React.createElement("section", { className: "academy-paywalled-content rounded-[32px] border border-white/10 bg-[linear-gradient(180deg,rgba(15,23,42,0.92),rgba(15,23,42,0.82))] p-6 text-slate-200 shadow-[0_18px_50px_rgba(2,6,23,0.18)] md:p-8" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-end justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-slate-400" }, "Prompt guidance"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white" }, "How to use this prompt")), !promptHasFullAccess ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-amber-300/20 bg-amber-300/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-amber-100" }, "Full notes visible with access") : null), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-5 md:grid-cols-2" }, promptUsageNotes ? /* @__PURE__ */ React.createElement("div", { className: "rounded-[26px] border border-white/10 bg-black/20 p-5" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-sky-200/75" }, "Usage notes"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 whitespace-pre-wrap text-sm leading-7 text-slate-200" }, promptUsageNotes)) : null, promptWorkflowNotes ? /* @__PURE__ */ React.createElement("div", { className: "rounded-[26px] border border-white/10 bg-black/20 p-5" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-emerald-200/75" }, "Workflow notes"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 whitespace-pre-wrap text-sm leading-7 text-slate-200" }, promptWorkflowNotes)) : null)) : null, hasPromptDocumentation ? /* @__PURE__ */ React.createElement(PromptDocumentationPanel, { documentation: promptDocumentation }) : null, hasPromptPlaceholders ? /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-[linear-gradient(180deg,rgba(255,255,255,0.045),rgba(148,163,184,0.03))] p-6 text-slate-200 shadow-[0_24px_70px_rgba(2,6,23,0.2)] md:p-8" }, /* @__PURE__ */ React.createElement("div", { className: "max-w-3xl" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-slate-400" }, "Data"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white" }, "Placeholders and required inputs"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-7 text-slate-300" }, "Prepare these variables before using the final prompt so the output stays consistent and reusable.")), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-5 xl:grid-cols-2" }, promptPlaceholders.map((placeholder, index2) => /* @__PURE__ */ React.createElement(PromptPlaceholderCard, { key: `${placeholder.key || "placeholder"}-${index2}`, placeholder })))) : null, hasPromptFilledExamples ? /* @__PURE__ */ React.createElement("section", { className: "academy-paywalled-content rounded-[32px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(167,139,250,0.12),transparent_30%),linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.92))] p-6 text-slate-200 shadow-[0_24px_80px_rgba(2,6,23,0.28)] md:p-8" }, /* @__PURE__ */ React.createElement("div", { className: "max-w-3xl" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-violet-100/80" }, "Filled examples"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white md:text-3xl" }, promptFilledExamplesTotal > 0 ? `${promptFilledExamplesTotal} ready-made prompt runs for real user inputs` : "Ready-made prompt runs for real user inputs"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-7 text-slate-300 md:text-base" }, promptHasMoreFilledExamples ? `You can view ${promptFilledExamples.length} example${promptFilledExamples.length === 1 ? "" : "s"} right now. Upgrade to Pro to unlock all ${promptFilledExamplesTotal} filled prompt runs and copy a closer starting point instead of filling everything from scratch.` : "These examples show how the prompt looks after swapping real placeholder values, so you can copy a closer starting point instead of filling everything from scratch.")), /* @__PURE__ */ React.createElement(PromptFilledExamplesSection, { examples: promptFilledExamples, analytics, contentId: item.id })) : null, promptHasLockedFilledExamples ? /* @__PURE__ */ React.createElement("section", { className: "academy-paywalled-content rounded-[32px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(167,139,250,0.12),transparent_30%),linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.92))] p-6 text-slate-200 shadow-[0_24px_80px_rgba(2,6,23,0.28)] md:p-8" }, /* @__PURE__ */ React.createElement("div", { className: "max-w-3xl" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-violet-100/80" }, "Filled examples"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white md:text-3xl" }, promptHasMoreFilledExamples && hasPromptFilledExamples ? `${Math.max(promptFilledExamplesTotal - promptFilledExamples.length, 0)} more filled prompt example${promptFilledExamplesTotal - promptFilledExamples.length === 1 ? "" : "s"} are available` : `${promptFilledExamplesTotal || 5} filled prompt examples are included`), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-7 text-slate-300 md:text-base" }, promptHasMoreFilledExamples && hasPromptFilledExamples ? "Creator access includes a smaller set here. Upgrade to Academy Pro to unlock the remaining filled prompt runs." : "This prompt ships with ready-made filled examples for different user inputs, but they unlock only for Academy Pro members.")), /* @__PURE__ */ React.createElement("div", { className: "mt-6" }, /* @__PURE__ */ React.createElement(LockedPanel, { pricingUrl, label: "prompt", accessLevel: "pro", onUpgrade: () => trackUpgradeClick(analytics, { source: "prompt_filled_examples_locked_panel" }) }))) : null, hasPromptHelperPrompts ? /* @__PURE__ */ React.createElement("section", { className: "academy-paywalled-content rounded-[32px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(255,183,139,0.12),transparent_30%),linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.92))] p-6 text-slate-200 shadow-[0_24px_80px_rgba(2,6,23,0.28)] md:p-8" }, /* @__PURE__ */ React.createElement("div", { className: "max-w-3xl" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-[#ffcfbf]" }, "Data helpers"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white md:text-3xl" }, "Helper prompts for preparation and validation"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-7 text-slate-300 md:text-base" }, "Use these supporting prompts before or after the main prompt when you need better source data, cleaner structure, or a validation pass.")), /* @__PURE__ */ React.createElement("div", { className: "mt-6 space-y-5" }, promptHelperPrompts.map((helperPrompt, index2) => /* @__PURE__ */ React.createElement(PromptHelperPromptCard, { key: `${helperPrompt.title || "helper"}-${index2}`, helperPrompt, analytics, contentId: item.id })))) : null, promptHasLockedHelperPrompts ? /* @__PURE__ */ React.createElement("section", { className: "academy-paywalled-content rounded-[32px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(255,183,139,0.12),transparent_30%),linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.92))] p-6 text-slate-200 shadow-[0_24px_80px_rgba(2,6,23,0.28)] md:p-8" }, /* @__PURE__ */ React.createElement("div", { className: "max-w-3xl" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-[#ffcfbf]" }, "Data helpers"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white md:text-3xl" }, "Helper prompts are included with this template"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-7 text-slate-300 md:text-base" }, "Data collection, validation, or refinement prompts are available once your Academy access matches this template.")), /* @__PURE__ */ React.createElement("div", { className: "mt-6" }, /* @__PURE__ */ React.createElement(LockedPanel, { pricingUrl, label: "prompt", accessLevel: item?.access_level, onUpgrade: () => trackUpgradeClick(analytics, { source: "prompt_helper_locked_panel" }) }))) : null, hasPromptVariants ? /* @__PURE__ */ React.createElement(PromptVariantsSection, { variants: promptVariants, analytics, contentId: item.id }) : null, promptHasLockedVariants ? /* @__PURE__ */ React.createElement("section", { className: "academy-paywalled-content rounded-[32px] border border-white/10 bg-[linear-gradient(180deg,rgba(255,255,255,0.045),rgba(148,163,184,0.03))] p-6 text-slate-200 shadow-[0_24px_70px_rgba(2,6,23,0.2)] md:p-8" }, /* @__PURE__ */ React.createElement("div", { className: "max-w-3xl" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-slate-400" }, "Variants"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white" }, "Alternative prompt versions are included"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-7 text-slate-300" }, "This prompt includes recommended or model-specific variants, but they stay locked until your Academy access level matches the template.")), /* @__PURE__ */ React.createElement("div", { className: "mt-6" }, /* @__PURE__ */ React.createElement(LockedPanel, { pricingUrl, label: "prompt", accessLevel: item?.access_level, onUpgrade: () => trackUpgradeClick(analytics, { source: "prompt_variant_locked_panel" }) }))) : null, promptComparisons.length ? /* @__PURE__ */ React.createElement("section", { className: "academy-paywalled-content rounded-[32px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(255,183,139,0.12),transparent_30%),linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.92))] p-6 text-slate-200 shadow-[0_24px_80px_rgba(2,6,23,0.28)] md:p-8" }, /* @__PURE__ */ React.createElement("div", { className: "max-w-3xl" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-[#ffcfbf]" }, "AI model comparisons"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white md:text-3xl" }, "How different models respond to the same prompt"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-7 text-slate-300 md:text-base" }, "Use these notes to decide which provider fits the result you want before you start tuning or post-processing.")), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-5 xl:grid-cols-2" }, promptComparisons.map((note, index2) => /* @__PURE__ */ React.createElement(PromptToolNoteCard, { key: `${note.provider || "provider"}-${note.model_name || "model"}-${index2}`, note, index: index2, galleryIndex: index2, onOpenImage: openPromptComparisonGallery })))) : null), /* @__PURE__ */ React.createElement("aside", { className: "space-y-6 lg:sticky lg:top-6 lg:self-start" }, lessonTags.length ? /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-[linear-gradient(180deg,rgba(15,23,42,0.92),rgba(15,23,42,0.84))] p-6 text-slate-200 shadow-[0_18px_50px_rgba(2,6,23,0.18)]" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-slate-400" }, "Microtags"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2" }, lessonTags.map((tag) => /* @__PURE__ */ React.createElement("span", { key: tag, className: "rounded-full border border-sky-300/20 bg-sky-300/10 px-3 py-1.5 text-xs font-semibold uppercase tracking-[0.18em] text-sky-100" }, tag)))) : null, /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-[linear-gradient(180deg,rgba(15,23,42,0.92),rgba(15,23,42,0.84))] p-6 text-slate-200 shadow-[0_18px_50px_rgba(2,6,23,0.18)]" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.24em] text-slate-400" }, "Best use case"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-7 text-slate-300" }, promptBestUseCase))))) : /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-white/[0.04] p-6 text-slate-200" }, pageType === "pack" ? /* @__PURE__ */ React.createElement("div", { className: "space-y-5" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm leading-8 text-slate-200" }, item.description), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 md:grid-cols-2" }, (item.prompts || []).map((prompt) => /* @__PURE__ */ React.createElement("div", { key: prompt.id, className: "rounded-2xl border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("h3", { className: "text-lg font-semibold text-white" }, prompt.title), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm leading-7 text-slate-300" }, prompt.excerpt || prompt.prompt_preview))))) : null, pageType === "challenge" ? /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-6 md:grid-cols-2" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400" }, "Brief"), /* @__PURE__ */ React.createElement("div", { className: "mt-3 whitespace-pre-wrap text-sm leading-8 text-slate-200" }, item.brief || item.description)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400" }, "Rules"), /* @__PURE__ */ React.createElement("div", { className: "mt-3 whitespace-pre-wrap text-sm leading-8 text-slate-200" }, item.rules || "No special rules posted yet."))), (item.submissions || []).length ? /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400" }, "Approved submissions"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-4 md:grid-cols-2" }, item.submissions.map((submission) => /* @__PURE__ */ React.createElement("div", { key: submission.id, className: "rounded-2xl border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("h3", { className: "text-lg font-semibold text-white" }, submission.artwork?.title || "Submission"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-slate-400" }, submission.user?.name || "Unknown creator"))))) : null) : null)), /* @__PURE__ */ React.createElement(ImageLightbox, { gallery: lightboxGallery, onClose: () => setLightboxGallery(null), onNavigate: navigateLightboxGallery })); } const __vite_glob_0_9 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, @@ -15188,7 +15422,8 @@ const buildAdminNavGroups = (isAdmin) => [ items: [ { label: "All Users", href: "/moderation/users", icon: "fa-solid fa-users" }, { label: "Staff", href: "/moderation/users?role=admin", icon: "fa-solid fa-shield-halved" }, - { label: "Moderators", href: "/moderation/users?role=moderator", icon: "fa-solid fa-user-shield" } + { label: "Moderators", href: "/moderation/users?role=moderator", icon: "fa-solid fa-user-shield" }, + { label: "Staff Applications", href: "/moderation/staff-applications", icon: "fa-solid fa-user-check" } ] }, { @@ -15201,8 +15436,7 @@ const buildAdminNavGroups = (isAdmin) => [ { label: "Web Stories", href: "/moderation/web-stories", icon: "fa-solid fa-book-open-reader" }, { label: "Homepage Announcements", href: "/moderation/homepage/announcements", icon: "fa-solid fa-bullhorn" }, { label: "Upload Queue", href: "/moderation/uploads", icon: "fa-solid fa-cloud-arrow-up" }, - { label: "Username Queue", href: "/moderation/usernames/moderation", icon: "fa-solid fa-id-badge" }, - { label: "AI Biography", href: "/moderation/ai-biography", icon: "fa-solid fa-wand-magic-sparkles" } + { label: "Username Queue", href: "/moderation/usernames/moderation", icon: "fa-solid fa-id-badge" } ] }, { @@ -15286,21 +15520,21 @@ const __vite_glob_0_13 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de function MetricCell({ value, suffix = "" }) { return /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, value, suffix); } -function StatCard$g({ label, value, suffix = "" }) { +function StatCard$j({ label, value, suffix = "" }) { return /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/[0.08] bg-white/[0.04] p-5" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, label), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-3xl font-bold text-white" }, value, suffix)); } function AcademyAnalyticsContent({ nav = [], range: range2, title, subtitle, summary = null, rows = [] }) { - return /* @__PURE__ */ React.createElement(AdminLayout, { title, subtitle }, /* @__PURE__ */ React.createElement(Se$1, { title: `Admin · ${title}` }), /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement(AnalyticsNav, { items: nav }), /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Range"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm text-slate-300" }, range2?.from, " to ", range2?.to)), summary ? /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(StatCard$g, { label: "Views", value: Number(summary.views || 0).toLocaleString() }), /* @__PURE__ */ React.createElement(StatCard$g, { label: "Unique Visitors", value: Number(summary.uniqueVisitors || 0).toLocaleString() }), /* @__PURE__ */ React.createElement(StatCard$g, { label: "Engaged Views", value: Number(summary.engagedViews || 0).toLocaleString() }), /* @__PURE__ */ React.createElement(StatCard$g, { label: "Engagement Rate", value: Number(summary.engagementRate || 0).toLocaleString(), suffix: "%" }), /* @__PURE__ */ React.createElement(StatCard$g, { label: "Avg Engaged Seconds", value: Number(summary.avgEngagedSeconds || 0).toLocaleString(), suffix: "s" }), /* @__PURE__ */ React.createElement(StatCard$g, { label: "Scroll 50%", value: Number(summary.scroll50 || 0).toLocaleString() }), /* @__PURE__ */ React.createElement(StatCard$g, { label: "Scroll 100%", value: Number(summary.scroll100 || 0).toLocaleString() }), /* @__PURE__ */ React.createElement(StatCard$g, { label: "Deep Scroll Rate", value: Number(summary.deepScrollRate || 0).toLocaleString(), suffix: "%" })) : null, /* @__PURE__ */ React.createElement("div", { className: "overflow-hidden rounded-[28px] border border-white/[0.08] bg-white/[0.03]" }, /* @__PURE__ */ React.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React.createElement("table", { className: "min-w-full text-left text-sm" }, /* @__PURE__ */ React.createElement("thead", { className: "border-b border-white/[0.08] bg-black/20 text-[11px] uppercase tracking-[0.18em] text-slate-500" }, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Title"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Type"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Access"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Views"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Unique"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Engaged"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Likes"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Saves"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Copies"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Starts"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Completions"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Upgrade Clicks"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Popularity"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Trend"))), /* @__PURE__ */ React.createElement("tbody", null, rows.length ? rows.map((row) => /* @__PURE__ */ React.createElement("tr", { key: `${row.content_type}-${row.content_id || "none"}`, className: "border-b border-white/[0.06] align-top text-slate-300" }, /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-white" }, row.title), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-xs uppercase tracking-[0.16em] text-slate-500" }, "ID ", row.content_id || "n/a")), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, row.content_type_label), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, row.access_level || "n/a"), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, /* @__PURE__ */ React.createElement(MetricCell, { value: row.views })), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, /* @__PURE__ */ React.createElement(MetricCell, { value: row.unique_visitors })), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, /* @__PURE__ */ React.createElement(MetricCell, { value: row.engaged_views })), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, /* @__PURE__ */ React.createElement(MetricCell, { value: row.likes })), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, /* @__PURE__ */ React.createElement(MetricCell, { value: row.saves })), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, /* @__PURE__ */ React.createElement(MetricCell, { value: row.prompt_copies })), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, /* @__PURE__ */ React.createElement(MetricCell, { value: row.starts })), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, /* @__PURE__ */ React.createElement(MetricCell, { value: row.completions })), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, /* @__PURE__ */ React.createElement(MetricCell, { value: row.upgrade_clicks })), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, /* @__PURE__ */ React.createElement(MetricCell, { value: row.popularity_score })), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, row.trend))) : /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("td", { colSpan: 14, className: "px-4 py-10 text-center text-slate-400" }, "No rollup data available yet for this view.")))))))); + return /* @__PURE__ */ React.createElement(AdminLayout, { title, subtitle }, /* @__PURE__ */ React.createElement(Se$1, { title: `Admin · ${title}` }), /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement(AnalyticsNav, { items: nav }), /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Range"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm text-slate-300" }, range2?.from, " to ", range2?.to)), summary ? /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(StatCard$j, { label: "Views", value: Number(summary.views || 0).toLocaleString() }), /* @__PURE__ */ React.createElement(StatCard$j, { label: "Unique Visitors", value: Number(summary.uniqueVisitors || 0).toLocaleString() }), /* @__PURE__ */ React.createElement(StatCard$j, { label: "Engaged Views", value: Number(summary.engagedViews || 0).toLocaleString() }), /* @__PURE__ */ React.createElement(StatCard$j, { label: "Engagement Rate", value: Number(summary.engagementRate || 0).toLocaleString(), suffix: "%" }), /* @__PURE__ */ React.createElement(StatCard$j, { label: "Avg Engaged Seconds", value: Number(summary.avgEngagedSeconds || 0).toLocaleString(), suffix: "s" }), /* @__PURE__ */ React.createElement(StatCard$j, { label: "Scroll 50%", value: Number(summary.scroll50 || 0).toLocaleString() }), /* @__PURE__ */ React.createElement(StatCard$j, { label: "Scroll 100%", value: Number(summary.scroll100 || 0).toLocaleString() }), /* @__PURE__ */ React.createElement(StatCard$j, { label: "Deep Scroll Rate", value: Number(summary.deepScrollRate || 0).toLocaleString(), suffix: "%" })) : null, /* @__PURE__ */ React.createElement("div", { className: "overflow-hidden rounded-[28px] border border-white/[0.08] bg-white/[0.03]" }, /* @__PURE__ */ React.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React.createElement("table", { className: "min-w-full text-left text-sm" }, /* @__PURE__ */ React.createElement("thead", { className: "border-b border-white/[0.08] bg-black/20 text-[11px] uppercase tracking-[0.18em] text-slate-500" }, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Title"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Type"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Access"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Views"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Unique"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Engaged"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Likes"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Saves"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Copies"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Starts"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Completions"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Upgrade Clicks"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Popularity"), /* @__PURE__ */ React.createElement("th", { className: "px-4 py-3" }, "Trend"))), /* @__PURE__ */ React.createElement("tbody", null, rows.length ? rows.map((row) => /* @__PURE__ */ React.createElement("tr", { key: `${row.content_type}-${row.content_id || "none"}`, className: "border-b border-white/[0.06] align-top text-slate-300" }, /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-white" }, row.title), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-xs uppercase tracking-[0.16em] text-slate-500" }, "ID ", row.content_id || "n/a")), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, row.content_type_label), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, row.access_level || "n/a"), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, /* @__PURE__ */ React.createElement(MetricCell, { value: row.views })), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, /* @__PURE__ */ React.createElement(MetricCell, { value: row.unique_visitors })), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, /* @__PURE__ */ React.createElement(MetricCell, { value: row.engaged_views })), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, /* @__PURE__ */ React.createElement(MetricCell, { value: row.likes })), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, /* @__PURE__ */ React.createElement(MetricCell, { value: row.saves })), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, /* @__PURE__ */ React.createElement(MetricCell, { value: row.prompt_copies })), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, /* @__PURE__ */ React.createElement(MetricCell, { value: row.starts })), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, /* @__PURE__ */ React.createElement(MetricCell, { value: row.completions })), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, /* @__PURE__ */ React.createElement(MetricCell, { value: row.upgrade_clicks })), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, /* @__PURE__ */ React.createElement(MetricCell, { value: row.popularity_score })), /* @__PURE__ */ React.createElement("td", { className: "px-4 py-4" }, row.trend))) : /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("td", { colSpan: 14, className: "px-4 py-10 text-center text-slate-400" }, "No rollup data available yet for this view.")))))))); } const __vite_glob_0_10 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: AcademyAnalyticsContent }, Symbol.toStringTag, { value: "Module" })); -function StatCard$f({ label, value }) { +function StatCard$i({ label, value }) { return /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/[0.08] bg-white/[0.04] p-5" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, label), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-3xl font-bold text-white" }, Number(value || 0).toLocaleString())); } function AcademyAnalyticsFunnel({ nav = [], range: range2, summary = {}, bestConverters = [] }) { - return /* @__PURE__ */ React.createElement(AdminLayout, { title: "Academy Funnel", subtitle: "Early conversion signals from premium previews, upgrade clicks, and learning starts." }, /* @__PURE__ */ React.createElement(Se$1, { title: "Admin · Academy Funnel" }), /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement(AnalyticsNav, { items: nav }), /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Range"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm text-slate-300" }, range2?.from, " to ", range2?.to)), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(StatCard$f, { label: "Academy Visitors", value: summary.academyVisitors }), /* @__PURE__ */ React.createElement(StatCard$f, { label: "Premium Preview Views", value: summary.premiumPreviewViews }), /* @__PURE__ */ React.createElement(StatCard$f, { label: "Upgrade Clicks", value: summary.upgradeClicks }), /* @__PURE__ */ React.createElement(StatCard$f, { label: "Learning Starts", value: summary.starts }), /* @__PURE__ */ React.createElement(StatCard$f, { label: "Completions", value: summary.completions }), /* @__PURE__ */ React.createElement(StatCard$f, { label: "Checkout Starts", value: summary.checkoutStarts }), /* @__PURE__ */ React.createElement(StatCard$f, { label: "Subscriptions", value: summary.subscriptions })), /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Best Converting Content"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, bestConverters.length ? bestConverters.map((item) => /* @__PURE__ */ React.createElement("div", { key: `${item.content_type}-${item.content_id || "none"}`, className: "rounded-2xl border border-white/[0.08] bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-white" }, item.title), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-xs uppercase tracking-[0.18em] text-slate-500" }, item.content_type_label)), /* @__PURE__ */ React.createElement("div", { className: "text-right" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm font-semibold text-sky-100" }, item.conversion_score), /* @__PURE__ */ React.createElement("p", { className: "text-xs uppercase tracking-[0.18em] text-slate-500" }, "conversion"))))) : /* @__PURE__ */ React.createElement("p", { className: "rounded-2xl border border-dashed border-white/[0.08] bg-black/20 px-4 py-6 text-sm text-slate-400" }, "No conversion signals have been rolled up yet."))))); + return /* @__PURE__ */ React.createElement(AdminLayout, { title: "Academy Funnel", subtitle: "Early conversion signals from premium previews, upgrade clicks, and learning starts." }, /* @__PURE__ */ React.createElement(Se$1, { title: "Admin · Academy Funnel" }), /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement(AnalyticsNav, { items: nav }), /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Range"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm text-slate-300" }, range2?.from, " to ", range2?.to)), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(StatCard$i, { label: "Academy Visitors", value: summary.academyVisitors }), /* @__PURE__ */ React.createElement(StatCard$i, { label: "Premium Preview Views", value: summary.premiumPreviewViews }), /* @__PURE__ */ React.createElement(StatCard$i, { label: "Upgrade Clicks", value: summary.upgradeClicks }), /* @__PURE__ */ React.createElement(StatCard$i, { label: "Learning Starts", value: summary.starts }), /* @__PURE__ */ React.createElement(StatCard$i, { label: "Completions", value: summary.completions }), /* @__PURE__ */ React.createElement(StatCard$i, { label: "Checkout Starts", value: summary.checkoutStarts }), /* @__PURE__ */ React.createElement(StatCard$i, { label: "Subscriptions", value: summary.subscriptions })), /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Best Converting Content"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, bestConverters.length ? bestConverters.map((item) => /* @__PURE__ */ React.createElement("div", { key: `${item.content_type}-${item.content_id || "none"}`, className: "rounded-2xl border border-white/[0.08] bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-white" }, item.title), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-xs uppercase tracking-[0.18em] text-slate-500" }, item.content_type_label)), /* @__PURE__ */ React.createElement("div", { className: "text-right" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm font-semibold text-sky-100" }, item.conversion_score), /* @__PURE__ */ React.createElement("p", { className: "text-xs uppercase tracking-[0.18em] text-slate-500" }, "conversion"))))) : /* @__PURE__ */ React.createElement("p", { className: "rounded-2xl border border-dashed border-white/[0.08] bg-black/20 px-4 py-6 text-sm text-slate-400" }, "No conversion signals have been rolled up yet."))))); } const __vite_glob_0_11 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, @@ -15378,7 +15612,7 @@ const __vite_glob_0_12 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de __proto__: null, default: AcademyAnalyticsIntelligence }, Symbol.toStringTag, { value: "Module" })); -function StatCard$e({ label, value }) { +function StatCard$h({ label, value }) { return /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/[0.08] bg-white/[0.04] p-5" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, label), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-3xl font-bold text-white" }, Number(value || 0).toLocaleString())); } function formatDelta(delta) { @@ -15409,7 +15643,7 @@ function ContentList({ title, items = [] }) { return /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, title), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, items.length ? items.map((item) => /* @__PURE__ */ React.createElement("div", { key: `${item.content_type}-${item.content_id || "none"}`, className: "rounded-2xl border border-white/[0.08] bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-sm font-semibold text-white" }, item.title), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-xs uppercase tracking-[0.18em] text-slate-500" }, item.content_type_label)), /* @__PURE__ */ React.createElement("div", { className: "text-right" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm font-semibold text-sky-100" }, item.popularity_score), /* @__PURE__ */ React.createElement("p", { className: "text-xs uppercase tracking-[0.18em] text-slate-500" }, "popularity"))))) : /* @__PURE__ */ React.createElement("p", { className: "rounded-2xl border border-dashed border-white/[0.08] bg-black/20 px-4 py-6 text-sm text-slate-400" }, "No rollup data yet for this range."))); } function AcademyAnalyticsOverview({ nav = [], range: range2, stats, promptLibraryTrend = null, popularPromptPeriodUsage = null, topContent = [], topWeek = [] }) { - return /* @__PURE__ */ React.createElement(AdminLayout, { title: "Academy Analytics", subtitle: "Daily rollup overview for Academy traffic, engagement, and subscription intent." }, /* @__PURE__ */ React.createElement(Se$1, { title: "Admin · Academy Analytics" }), /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement(AnalyticsNav, { items: nav }), /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Range"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm text-slate-300" }, range2?.from, " to ", range2?.to)), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(StatCard$e, { label: "Views", value: stats.views }), /* @__PURE__ */ React.createElement(StatCard$e, { label: "Unique Visitors", value: stats.uniqueVisitors }), /* @__PURE__ */ React.createElement(StatCard$e, { label: "Logged-in Views", value: stats.userViews }), /* @__PURE__ */ React.createElement(StatCard$e, { label: "Guest Views", value: stats.guestViews }), /* @__PURE__ */ React.createElement(StatCard$e, { label: "Subscriber Views", value: stats.subscriberViews }), /* @__PURE__ */ React.createElement(StatCard$e, { label: "Prompt Copies", value: stats.promptCopies }), /* @__PURE__ */ React.createElement(StatCard$e, { label: "Likes", value: stats.likes }), /* @__PURE__ */ React.createElement(StatCard$e, { label: "Saves", value: stats.saves }), /* @__PURE__ */ React.createElement(StatCard$e, { label: "Lesson Completions", value: stats.lessonCompletions }), /* @__PURE__ */ React.createElement(StatCard$e, { label: "Course Starts", value: stats.courseStarts }), /* @__PURE__ */ React.createElement(StatCard$e, { label: "Upgrade Clicks", value: stats.upgradeClicks })), promptLibraryTrend ? /* @__PURE__ */ React.createElement(PromptLibraryTrend, { trend: promptLibraryTrend }) : null, popularPromptPeriodUsage ? /* @__PURE__ */ React.createElement(PopularPromptPeriodUsage, { usage: popularPromptPeriodUsage }) : null, /* @__PURE__ */ React.createElement("div", { className: "grid gap-6 xl:grid-cols-2" }, /* @__PURE__ */ React.createElement(ContentList, { title: "Top Content In Range", items: topContent }), /* @__PURE__ */ React.createElement(ContentList, { title: "Top Content This Week", items: topWeek })))); + return /* @__PURE__ */ React.createElement(AdminLayout, { title: "Academy Analytics", subtitle: "Daily rollup overview for Academy traffic, engagement, and subscription intent." }, /* @__PURE__ */ React.createElement(Se$1, { title: "Admin · Academy Analytics" }), /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement(AnalyticsNav, { items: nav }), /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Range"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm text-slate-300" }, range2?.from, " to ", range2?.to)), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(StatCard$h, { label: "Views", value: stats.views }), /* @__PURE__ */ React.createElement(StatCard$h, { label: "Unique Visitors", value: stats.uniqueVisitors }), /* @__PURE__ */ React.createElement(StatCard$h, { label: "Logged-in Views", value: stats.userViews }), /* @__PURE__ */ React.createElement(StatCard$h, { label: "Guest Views", value: stats.guestViews }), /* @__PURE__ */ React.createElement(StatCard$h, { label: "Subscriber Views", value: stats.subscriberViews }), /* @__PURE__ */ React.createElement(StatCard$h, { label: "Prompt Copies", value: stats.promptCopies }), /* @__PURE__ */ React.createElement(StatCard$h, { label: "Likes", value: stats.likes }), /* @__PURE__ */ React.createElement(StatCard$h, { label: "Saves", value: stats.saves }), /* @__PURE__ */ React.createElement(StatCard$h, { label: "Lesson Completions", value: stats.lessonCompletions }), /* @__PURE__ */ React.createElement(StatCard$h, { label: "Course Starts", value: stats.courseStarts }), /* @__PURE__ */ React.createElement(StatCard$h, { label: "Upgrade Clicks", value: stats.upgradeClicks })), promptLibraryTrend ? /* @__PURE__ */ React.createElement(PromptLibraryTrend, { trend: promptLibraryTrend }) : null, popularPromptPeriodUsage ? /* @__PURE__ */ React.createElement(PopularPromptPeriodUsage, { usage: popularPromptPeriodUsage }) : null, /* @__PURE__ */ React.createElement("div", { className: "grid gap-6 xl:grid-cols-2" }, /* @__PURE__ */ React.createElement(ContentList, { title: "Top Content In Range", items: topContent }), /* @__PURE__ */ React.createElement(ContentList, { title: "Top Content This Week", items: topWeek })))); } const __vite_glob_0_14 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, @@ -15431,7 +15665,7 @@ const __vite_glob_0_15 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de __proto__: null, default: AcademyAnalyticsSearch }, Symbol.toStringTag, { value: "Module" })); -function StatCard$d({ label, value, hint = null }) { +function StatCard$g({ label, value, hint = null }) { return /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/[0.08] bg-white/[0.04] p-5" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, label), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-3xl font-bold text-white" }, value.toLocaleString()), hint ? /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-slate-400" }, hint) : null); } function formatTimestamp$1(value) { @@ -15464,7 +15698,7 @@ function formatEventSummary(summary) { function AcademyBilling({ summary, planBreakdown, recentEvents, links }) { const missingPlans = Array.isArray(summary.missing_plan_keys) ? summary.missing_plan_keys : []; const noData = summary.enabled && (summary.active_subscribers || 0) === 0 && (summary.ended_subscriptions || 0) === 0 && (summary.recent_webhook_count || 0) === 0; - return /* @__PURE__ */ React.createElement(AdminLayout, { title: "Academy Billing", subtitle: "Moderation overview of Academy subscriptions, Stripe webhook sync activity, and plan readiness." }, /* @__PURE__ */ React.createElement(Se$1, { title: "Admin · Academy Billing" }), noData ? /* @__PURE__ */ React.createElement("div", { className: "mb-6 rounded-2xl border border-sky-300/20 bg-sky-300/[0.06] px-5 py-4 text-sm text-sky-100" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold" }, "No subscriber data in the database yet."), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sky-100/70" }, "Subscription records are created when Stripe sends webhook events to this server after a completed checkout. In local development, use", " ", /* @__PURE__ */ React.createElement("code", { className: "rounded bg-black/30 px-1.5 py-0.5 font-mono text-xs" }, "stripe listen --forward-to ", window.location.origin, "/stripe/webhook"), " ", "to forward events. On production, confirm the Stripe webhook is configured and active.")) : null, /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(StatCard$d, { label: "Active Subscribers", value: summary.active_subscribers || 0 }), /* @__PURE__ */ React.createElement(StatCard$d, { label: "Creator Subscribers", value: summary.creator_subscribers || 0 }), /* @__PURE__ */ React.createElement(StatCard$d, { label: "Pro Subscribers", value: summary.pro_subscribers || 0 }), /* @__PURE__ */ React.createElement(StatCard$d, { label: "Grace Period", value: summary.grace_period_subscribers || 0, hint: "Canceled subscriptions that still keep access until the billing period ends." })), /* @__PURE__ */ React.createElement("div", { className: "mt-8 grid gap-6 xl:grid-cols-[1.2fr_0.8fr]" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Plan Health"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-xl font-semibold text-white" }, "Configured Academy plans")), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement(xe, { href: links.dashboard, className: "rounded-full border border-white/[0.08] bg-white/[0.04] px-4 py-2 text-sm font-semibold text-slate-200 transition hover:border-white/15 hover:bg-white/[0.06] hover:text-white" }, "Dashboard"), /* @__PURE__ */ React.createElement(xe, { href: links.pricing, className: "rounded-full border border-sky-300/20 bg-sky-300/10 px-4 py-2 text-sm font-semibold text-sky-100 transition hover:border-sky-300/30 hover:bg-sky-300/15" }, "Public pricing"), /* @__PURE__ */ React.createElement(xe, { href: links.account, className: "rounded-full border border-emerald-300/20 bg-emerald-300/10 px-4 py-2 text-sm font-semibold text-emerald-100 transition hover:border-emerald-300/30 hover:bg-emerald-300/15" }, "My billing account"))), missingPlans.length ? /* @__PURE__ */ React.createElement("div", { className: "mt-5 rounded-2xl border border-amber-300/25 bg-amber-300/10 px-4 py-3 text-sm text-amber-100" }, "Missing Stripe price IDs for: ", missingPlans.join(", ")) : /* @__PURE__ */ React.createElement("div", { className: "mt-5 rounded-2xl border border-emerald-300/20 bg-emerald-300/10 px-4 py-3 text-sm text-emerald-100" }, "All configured Academy plans have Stripe price IDs."), /* @__PURE__ */ React.createElement("div", { className: "mt-5 grid gap-3 md:grid-cols-2" }, planBreakdown.map((plan) => /* @__PURE__ */ React.createElement("div", { key: plan.key, className: "rounded-2xl border border-white/[0.08] bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-base font-semibold text-white" }, plan.label), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, plan.tier, " · ", plan.interval)), /* @__PURE__ */ React.createElement("span", { className: `rounded-full px-2.5 py-1 text-xs font-semibold uppercase tracking-[0.18em] ${plan.configured ? "bg-emerald-300/12 text-emerald-100" : "bg-amber-300/12 text-amber-100"}` }, plan.configured ? "configured" : "missing")), /* @__PURE__ */ React.createElement("p", { className: "mt-4 text-3xl font-bold text-white" }, (plan.subscribers || 0).toLocaleString()), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "active subscriptions on this plan"))))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Webhook Sync"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-xl font-semibold text-white" }, "Recent Stripe activity"), /* @__PURE__ */ React.createElement("div", { className: "mt-5 space-y-3" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/[0.08] bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-400" }, "Billing enabled"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-lg font-semibold text-white" }, summary.enabled ? "Yes" : "No")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/[0.08] bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-400" }, "Webhook audits stored"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-lg font-semibold text-white" }, (summary.recent_webhook_count || 0).toLocaleString())), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/[0.08] bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-400" }, "Last processed webhook"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-lg font-semibold text-white" }, formatTimestamp$1(summary.last_webhook_at))), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/[0.08] bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-400" }, "Ended subscriptions"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-lg font-semibold text-white" }, (summary.ended_subscriptions || 0).toLocaleString()))))), /* @__PURE__ */ React.createElement("section", { className: "mt-8 rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Audit Trail"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-xl font-semibold text-white" }, "Latest academy billing events")), /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-400" }, "Only the safe local summary is stored, not the raw Stripe payload.")), /* @__PURE__ */ React.createElement("div", { className: "mt-5 overflow-x-auto" }, /* @__PURE__ */ React.createElement("table", { className: "min-w-full divide-y divide-white/[0.08] text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", { className: "text-left text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, /* @__PURE__ */ React.createElement("th", { className: "px-3 py-3" }, "Event"), /* @__PURE__ */ React.createElement("th", { className: "px-3 py-3" }, "Plan"), /* @__PURE__ */ React.createElement("th", { className: "px-3 py-3" }, "Tier"), /* @__PURE__ */ React.createElement("th", { className: "px-3 py-3" }, "User"), /* @__PURE__ */ React.createElement("th", { className: "px-3 py-3" }, "Processed"), /* @__PURE__ */ React.createElement("th", { className: "px-3 py-3" }, "Summary"))), /* @__PURE__ */ React.createElement("tbody", { className: "divide-y divide-white/[0.06]" }, recentEvents.length ? recentEvents.map((event) => /* @__PURE__ */ React.createElement("tr", { key: event.id }, /* @__PURE__ */ React.createElement("td", { className: "px-3 py-3 font-medium text-white" }, event.event_type), /* @__PURE__ */ React.createElement("td", { className: "px-3 py-3" }, event.academy_plan || "n/a"), /* @__PURE__ */ React.createElement("td", { className: "px-3 py-3" }, event.academy_tier || "n/a"), /* @__PURE__ */ React.createElement("td", { className: "px-3 py-3" }, event.user_id || "guest/unresolved"), /* @__PURE__ */ React.createElement("td", { className: "px-3 py-3" }, formatTimestamp$1(event.processed_at || event.created_at)), /* @__PURE__ */ React.createElement("td", { className: "px-3 py-3 text-slate-400" }, formatEventSummary(event.payload_summary)))) : /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("td", { colSpan: "6", className: "px-3 py-6 text-center text-slate-400" }, "No Academy billing webhook audits have been stored yet."))))))); + return /* @__PURE__ */ React.createElement(AdminLayout, { title: "Academy Billing", subtitle: "Moderation overview of Academy subscriptions, Stripe webhook sync activity, and plan readiness." }, /* @__PURE__ */ React.createElement(Se$1, { title: "Admin · Academy Billing" }), noData ? /* @__PURE__ */ React.createElement("div", { className: "mb-6 rounded-2xl border border-sky-300/20 bg-sky-300/[0.06] px-5 py-4 text-sm text-sky-100" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold" }, "No subscriber data in the database yet."), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sky-100/70" }, "Subscription records are created when Stripe sends webhook events to this server after a completed checkout. In local development, use", " ", /* @__PURE__ */ React.createElement("code", { className: "rounded bg-black/30 px-1.5 py-0.5 font-mono text-xs" }, "stripe listen --forward-to ", window.location.origin, "/stripe/webhook"), " ", "to forward events. On production, confirm the Stripe webhook is configured and active.")) : null, /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(StatCard$g, { label: "Active Subscribers", value: summary.active_subscribers || 0 }), /* @__PURE__ */ React.createElement(StatCard$g, { label: "Creator Subscribers", value: summary.creator_subscribers || 0 }), /* @__PURE__ */ React.createElement(StatCard$g, { label: "Pro Subscribers", value: summary.pro_subscribers || 0 }), /* @__PURE__ */ React.createElement(StatCard$g, { label: "Grace Period", value: summary.grace_period_subscribers || 0, hint: "Canceled subscriptions that still keep access until the billing period ends." })), /* @__PURE__ */ React.createElement("div", { className: "mt-8 grid gap-6 xl:grid-cols-[1.2fr_0.8fr]" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Plan Health"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-xl font-semibold text-white" }, "Configured Academy plans")), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement(xe, { href: links.dashboard, className: "rounded-full border border-white/[0.08] bg-white/[0.04] px-4 py-2 text-sm font-semibold text-slate-200 transition hover:border-white/15 hover:bg-white/[0.06] hover:text-white" }, "Dashboard"), /* @__PURE__ */ React.createElement(xe, { href: links.pricing, className: "rounded-full border border-sky-300/20 bg-sky-300/10 px-4 py-2 text-sm font-semibold text-sky-100 transition hover:border-sky-300/30 hover:bg-sky-300/15" }, "Public pricing"), /* @__PURE__ */ React.createElement(xe, { href: links.account, className: "rounded-full border border-emerald-300/20 bg-emerald-300/10 px-4 py-2 text-sm font-semibold text-emerald-100 transition hover:border-emerald-300/30 hover:bg-emerald-300/15" }, "My billing account"))), missingPlans.length ? /* @__PURE__ */ React.createElement("div", { className: "mt-5 rounded-2xl border border-amber-300/25 bg-amber-300/10 px-4 py-3 text-sm text-amber-100" }, "Missing Stripe price IDs for: ", missingPlans.join(", ")) : /* @__PURE__ */ React.createElement("div", { className: "mt-5 rounded-2xl border border-emerald-300/20 bg-emerald-300/10 px-4 py-3 text-sm text-emerald-100" }, "All configured Academy plans have Stripe price IDs."), /* @__PURE__ */ React.createElement("div", { className: "mt-5 grid gap-3 md:grid-cols-2" }, planBreakdown.map((plan) => /* @__PURE__ */ React.createElement("div", { key: plan.key, className: "rounded-2xl border border-white/[0.08] bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-base font-semibold text-white" }, plan.label), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, plan.tier, " · ", plan.interval)), /* @__PURE__ */ React.createElement("span", { className: `rounded-full px-2.5 py-1 text-xs font-semibold uppercase tracking-[0.18em] ${plan.configured ? "bg-emerald-300/12 text-emerald-100" : "bg-amber-300/12 text-amber-100"}` }, plan.configured ? "configured" : "missing")), /* @__PURE__ */ React.createElement("p", { className: "mt-4 text-3xl font-bold text-white" }, (plan.subscribers || 0).toLocaleString()), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "active subscriptions on this plan"))))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Webhook Sync"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-xl font-semibold text-white" }, "Recent Stripe activity"), /* @__PURE__ */ React.createElement("div", { className: "mt-5 space-y-3" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/[0.08] bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-400" }, "Billing enabled"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-lg font-semibold text-white" }, summary.enabled ? "Yes" : "No")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/[0.08] bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-400" }, "Webhook audits stored"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-lg font-semibold text-white" }, (summary.recent_webhook_count || 0).toLocaleString())), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/[0.08] bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-400" }, "Last processed webhook"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-lg font-semibold text-white" }, formatTimestamp$1(summary.last_webhook_at))), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/[0.08] bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-400" }, "Ended subscriptions"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-lg font-semibold text-white" }, (summary.ended_subscriptions || 0).toLocaleString()))))), /* @__PURE__ */ React.createElement("section", { className: "mt-8 rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Audit Trail"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-xl font-semibold text-white" }, "Latest academy billing events")), /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-400" }, "Only the safe local summary is stored, not the raw Stripe payload.")), /* @__PURE__ */ React.createElement("div", { className: "mt-5 overflow-x-auto" }, /* @__PURE__ */ React.createElement("table", { className: "min-w-full divide-y divide-white/[0.08] text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", { className: "text-left text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, /* @__PURE__ */ React.createElement("th", { className: "px-3 py-3" }, "Event"), /* @__PURE__ */ React.createElement("th", { className: "px-3 py-3" }, "Plan"), /* @__PURE__ */ React.createElement("th", { className: "px-3 py-3" }, "Tier"), /* @__PURE__ */ React.createElement("th", { className: "px-3 py-3" }, "User"), /* @__PURE__ */ React.createElement("th", { className: "px-3 py-3" }, "Processed"), /* @__PURE__ */ React.createElement("th", { className: "px-3 py-3" }, "Summary"))), /* @__PURE__ */ React.createElement("tbody", { className: "divide-y divide-white/[0.06]" }, recentEvents.length ? recentEvents.map((event) => /* @__PURE__ */ React.createElement("tr", { key: event.id }, /* @__PURE__ */ React.createElement("td", { className: "px-3 py-3 font-medium text-white" }, event.event_type), /* @__PURE__ */ React.createElement("td", { className: "px-3 py-3" }, event.academy_plan || "n/a"), /* @__PURE__ */ React.createElement("td", { className: "px-3 py-3" }, event.academy_tier || "n/a"), /* @__PURE__ */ React.createElement("td", { className: "px-3 py-3" }, event.user_id || "guest/unresolved"), /* @__PURE__ */ React.createElement("td", { className: "px-3 py-3" }, formatTimestamp$1(event.processed_at || event.created_at)), /* @__PURE__ */ React.createElement("td", { className: "px-3 py-3 text-slate-400" }, formatEventSummary(event.payload_summary)))) : /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("td", { colSpan: "6", className: "px-3 py-6 text-center text-slate-400" }, "No Academy billing webhook audits have been stored yet."))))))); } const __vite_glob_0_16 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, @@ -23410,6 +23644,131 @@ function serializeStructuredJson(value) { return ""; } } +function parseStructuredJson(value) { + if (value == null || value === "") return null; + if (typeof value === "string") { + const trimmed = value.trim(); + if (!trimmed) { + return null; + } + return JSON.parse(trimmed); + } + return value; +} +function toDisplayText(value) { + if (value == null) return ""; + if (typeof value === "string") return value.trim(); + if (typeof value === "number" || typeof value === "boolean") return String(value); + if (Array.isArray(value)) return value.map((item) => toDisplayText(item)).filter(Boolean).join(", "); + try { + return JSON.stringify(value); + } catch { + return ""; + } +} +function humanizePlaceholderKey(value) { + const normalized = String(value || "").replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim(); + if (!normalized) { + return "Placeholder"; + } + return normalized.split(" ").map((part) => part ? `${part.charAt(0).toUpperCase()}${part.slice(1).toLowerCase()}` : "").join(" "); +} +function escapeRegExp(value) { + return String(value || "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} +function buildPlaceholderSeedValues(placeholder, limit = 5) { + const readableLabel = humanizePlaceholderKey(placeholder?.label || placeholder?.key || "Placeholder"); + const seeded = [ + placeholder?.example, + placeholder?.default, + ...Array.isArray(placeholder?.examples) ? placeholder.examples : [], + ...Array.isArray(placeholder?.options) ? placeholder.options : [], + ...Array.isArray(placeholder?.choices) ? placeholder.choices : [], + ...Array.isArray(placeholder?.values) ? placeholder.values : [] + ].map((entry) => toDisplayText(entry)).map((entry) => entry.trim()).filter(Boolean); + const unique = Array.from(new Set(seeded)); + while (unique.length < limit) { + unique.push(`${readableLabel} ${unique.length + 1}`); + } + return unique.slice(0, limit); +} +function normalizePromptPlaceholders(value) { + if (!Array.isArray(value)) return []; + return value.map((placeholder) => { + if (!placeholder || typeof placeholder !== "object") return null; + const key = String(placeholder.key || "").trim(); + const label = String(placeholder.label || "").trim(); + if (!key && !label) { + return null; + } + return { + ...placeholder, + key, + label + }; + }).filter(Boolean); +} +function applyPlaceholderValuesToPrompt(template, placeholderValues, placeholders) { + let nextText = String(template || ""); + let replacementCount = 0; + placeholders.forEach((placeholder) => { + const key = String(placeholder?.key || "").trim(); + if (!key) return; + const replacement = toDisplayText(placeholderValues[key]); + if (!replacement) return; + const patterns = [ + new RegExp(`\\[${escapeRegExp(key)}\\]`, "g"), + new RegExp(`\\{\\{\\s*${escapeRegExp(key)}\\s*\\}\\}`, "g"), + new RegExp(`\\{${escapeRegExp(key)}\\}`, "g"), + new RegExp(`<${escapeRegExp(key)}>`, "g") + ]; + patterns.forEach((pattern) => { + nextText = nextText.replace(pattern, () => { + replacementCount += 1; + return replacement; + }); + }); + }); + if (replacementCount === 0 && placeholders.length > 0) { + const placeholderSummary = placeholders.map((placeholder) => { + const key = String(placeholder?.key || "").trim(); + if (!key) return null; + const readableLabel = humanizePlaceholderKey(placeholder.label || key); + const replacement = toDisplayText(placeholderValues[key]); + return replacement ? `- ${readableLabel}: ${replacement}` : null; + }).filter(Boolean).join("\n"); + if (placeholderSummary) { + nextText = `${nextText.trim()} + +Placeholder values: +${placeholderSummary}`.trim(); + } + } + return nextText.trim(); +} +function buildStarterFilledExamples({ title, excerpt, prompt, negativePrompt, placeholders }) { + const normalizedPlaceholders = normalizePromptPlaceholders(placeholders); + const exampleCount = Math.min(5, Math.max(1, normalizedPlaceholders.length ? 5 : 1)); + const fallbackTitle = stripPlainText(title) || "Prompt"; + const fallbackDescription = stripPlainText(excerpt) || "Starter example generated from the current placeholders. Review and refine before publishing."; + return Array.from({ length: exampleCount }, (_2, index2) => { + const placeholderValues = normalizedPlaceholders.reduce((accumulator, placeholder) => { + const key = String(placeholder?.key || "").trim(); + if (!key) return accumulator; + const seeds = buildPlaceholderSeedValues(placeholder, 5); + accumulator[key] = seeds[index2 % seeds.length]; + return accumulator; + }, {}); + const titleParts = Object.values(placeholderValues).map((value) => stripPlainText(value)).filter(Boolean).slice(0, 2); + return { + title: titleParts.length > 0 ? `Example ${index2 + 1} · ${titleParts.join(" · ")}`.slice(0, 180) : `Example ${index2 + 1} · ${fallbackTitle}`.slice(0, 180), + description: `${fallbackDescription} Starter ${index2 + 1} for editors.`.trim(), + placeholder_values: placeholderValues, + prompt: applyPlaceholderValuesToPrompt(prompt, placeholderValues, normalizedPlaceholders), + negative_prompt: negativePrompt ? applyPlaceholderValuesToPrompt(negativePrompt, placeholderValues, normalizedPlaceholders) : "" + }; + }); +} function copyTextToClipboard(text2) { const source = String(text2 || ""); if (!source) return Promise.reject(new Error("Nothing to copy")); @@ -23533,6 +23892,7 @@ const PROMPT_FIELD_TAB_MAP = { placeholders: "advanced", helper_prompts: "advanced", prompt_variants: "advanced", + filled_examples: "advanced", preview_image: "media", preview_image_file: "media", published_at: "publish", @@ -23706,6 +24066,7 @@ function parsePromptImport(rawText, categoryOptions) { if (parsed.placeholders != null) apply("placeholders", serializeStructuredJson(parsed.placeholders)); if (parsed.helper_prompts != null) apply("helper_prompts", serializeStructuredJson(parsed.helper_prompts)); if (parsed.prompt_variants != null) apply("prompt_variants", serializeStructuredJson(parsed.prompt_variants)); + if (parsed.filled_examples != null) apply("filled_examples", serializeStructuredJson(parsed.filled_examples)); if (parsed.preview_image != null) apply("preview_image", String(parsed.preview_image)); if (parsed.preview_image_url != null && parsed.preview_image == null) apply("preview_image", String(parsed.preview_image_url)); if (parsed.published_at != null) apply("published_at", String(parsed.published_at)); @@ -23846,6 +24207,30 @@ function PromptJsonImportDialog({ open, value, error, onChange, onClose, onApply active: true } ], + filled_examples: [ + { + title: "Alpine sunrise travel poster", + description: "A scenic poster version tuned for crisp mountain light and clean copy-safe composition.", + placeholder_values: { + LOCATION: "Lake Bled, Slovenia", + SEASON: "spring", + MOOD: "calm sunrise" + }, + prompt: "Create a calm sunrise travel poster of Lake Bled in spring, with clear mountain reflections, light mist, soft golden light, and a clean editorial composition.", + negative_prompt: "muddy light, cluttered foreground, oversharpening, distorted architecture" + }, + { + title: "Misty forest variant", + description: "Leans into atmosphere and fog while keeping the same placeholder structure.", + placeholder_values: { + LOCATION: "Triglav National Park", + SEASON: "autumn", + MOOD: "misty cinematic" + }, + prompt: "Create a cinematic autumn landscape in Triglav National Park with layered mist, warm foliage, soft directional light, and strong depth.", + negative_prompt: "flat composition, weak fog, repetitive trees, blown highlights" + } + ], preview_image: "https://files.skinbase.org/prompts/peaceful-fantasy-forest.webp", featured: false, prompt_of_week: false, @@ -23885,6 +24270,7 @@ Recommended fields: - placeholders: array of prompt variable objects - helper_prompts: array of supporting prompts used before or after the main prompt - prompt_variants: array of alternative prompt versions +- filled_examples: array of up to 5 filled prompt examples with placeholder_values and final prompts - preview_image: path or URL - featured: boolean - prompt_of_week: boolean @@ -23924,10 +24310,18 @@ prompt_variants object fields: - risk_notes - active boolean +filled_examples object fields: +- title +- description +- placeholder_values: object keyed by placeholder name +- prompt +- negative_prompt + Rules: - Return one JSON object only. - Keep excerpt concise and readable in cards. - Keep tags relevant and production-usable. +- Include exactly 5 filled_examples whenever the prompt uses placeholders or has clear user-editable parameters. - If you include tool_notes, keep them normalized and consistent.`; const aiPromptExamples = [ { @@ -23939,6 +24333,7 @@ Create a Skinbase Academy prompt template JSON object from the following creativ - Write a prompt that is immediately usable. - Write an excerpt that works in cards and search results. - Add 5 to 12 focused tags. +- Include 5 filled_examples with realistic placeholder_values and ready-to-copy final prompts. - Include 2 to 4 tool_notes comparisons when the brief mentions multiple AI providers. Creative brief: @@ -23952,6 +24347,7 @@ Generate a prompt template JSON object for Skinbase Academy. - Focus on the same core prompt being tested across multiple AI image providers. - Include tool_notes entries for each provider. - Each tool_notes item should explain settings, strengths, weaknesses, and best_for in plain production language. +- Include 5 filled_examples that show how users would swap placeholder values in real projects. - Return JSON only. Source notes: @@ -23965,6 +24361,7 @@ Convert the following source prompt page into structured Skinbase Academy prompt - Preserve the core instruction intent. - Normalize tags and metadata. - Convert provider reviews into tool_notes. +- Generate 5 filled_examples that demonstrate realistic filled-in prompt runs for end users. - Use category/category_slug when category_id is unknown. - Return JSON only. @@ -24018,7 +24415,7 @@ Source content: placeholder: '{\n "title": "Peaceful Fantasy Forest Wallpaper",\n "excerpt": "Short summary...",\n "prompt": "Main prompt text...",\n "tool_notes": []\n}', className: "nova-scrollbar w-full rounded-[24px] border border-white/10 bg-black/20 px-4 py-3 font-mono text-sm text-white outline-none placeholder:text-white/30" } - ), error ? /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-rose-300/20 bg-rose-400/10 px-4 py-3 text-sm text-rose-100" }, error) : null), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-white/[0.03] p-4 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Recognized keys"), /* @__PURE__ */ React.createElement("div", { className: "mt-3 space-y-2 leading-6 text-slate-400" }, /* @__PURE__ */ React.createElement("p", null, "title, slug, excerpt"), /* @__PURE__ */ React.createElement("p", null, "category_id, category, category_slug"), /* @__PURE__ */ React.createElement("p", null, "difficulty, access_level, aspect_ratio"), /* @__PURE__ */ React.createElement("p", null, "tags"), /* @__PURE__ */ React.createElement("p", null, "prompt, negative_prompt"), /* @__PURE__ */ React.createElement("p", null, "usage_notes, workflow_notes"), /* @__PURE__ */ React.createElement("p", null, "documentation, placeholders"), /* @__PURE__ */ React.createElement("p", null, "helper_prompts, prompt_variants"), /* @__PURE__ */ React.createElement("p", null, "preview_image, preview_image_url"), /* @__PURE__ */ React.createElement("p", null, "published_at, seo_title, seo_description"), /* @__PURE__ */ React.createElement("p", null, "featured, prompt_of_week, active"), /* @__PURE__ */ React.createElement("p", null, "tool_notes, comparisons")))) : null, activeImportTab === "structure" ? /* @__PURE__ */ React.createElement("div", { className: "grid h-full min-h-0 gap-5 xl:grid-cols-[minmax(0,1.2fr)_360px]" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "mb-3 flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Structure example"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => copyText(JSON.stringify(structureExample, null, 2), "Structure example"), className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1.5 text-xs font-semibold text-white transition hover:bg-white/[0.08]" }, "Copy example")), /* @__PURE__ */ React.createElement("pre", { className: "nova-scrollbar max-h-[52vh] overflow-auto rounded-[20px] border border-white/10 bg-slate-950/80 p-4 text-xs leading-6 text-slate-200" }, JSON.stringify(structureExample, null, 2))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-white/[0.03] p-4 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Notes"), /* @__PURE__ */ React.createElement("div", { className: "mt-3 space-y-3 leading-6 text-slate-400" }, /* @__PURE__ */ React.createElement("p", null, "`tool_notes` can be an array of comparison objects or a simpler array under `comparisons`."), /* @__PURE__ */ React.createElement("p", null, "`documentation`, `placeholders`, `helper_prompts`, and `prompt_variants` can be nested JSON and are preserved during import."), /* @__PURE__ */ React.createElement("p", null, "`tags` can be strings or objects with `name`, `label`, `title`, or `slug`."), /* @__PURE__ */ React.createElement("p", null, "`preview_image` accepts either a stored path or an external URL.")))) : null, activeImportTab === "docs" ? /* @__PURE__ */ React.createElement("div", { className: "grid h-full min-h-0 gap-5 xl:grid-cols-[minmax(0,1.2fr)_360px]" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 p-4 text-sm leading-7 text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Field guide"), /* @__PURE__ */ React.createElement("div", { className: "mt-3 space-y-3 text-slate-400" }, /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "title"), " - public prompt name used in the library and detail page."), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "excerpt"), " - short summary for cards, preview blocks, and search results."), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "prompt"), " - the main instruction body shown to creators."), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "negative_prompt"), " - exclusions, defects, or anti-patterns."), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "documentation"), " - structured user-facing guidance for summary, workflow, tips, and common mistakes."), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "placeholders"), " - prompt variables such as `CITY_NAME` or `MONTHLY_WEATHER_DATA`."), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "helper_prompts"), " - supporting prompts for data collection, validation, or refinement."), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "prompt_variants"), " - alternative versions of the same prompt for safer or model-specific output."), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "tool_notes"), " - structured comparison notes for provider/model variants."), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "preview_image"), " - existing asset URL or stored path. File upload still happens separately."), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "category_id"), " is preferred when known. `category` or `category_slug` are used for best-effort matching."))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-white/[0.03] p-4 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Import rules"), /* @__PURE__ */ React.createElement("div", { className: "mt-3 space-y-2 leading-6 text-slate-400" }, /* @__PURE__ */ React.createElement("p", null, "Unknown keys are ignored, so broader AI output is safe to paste."), /* @__PURE__ */ React.createElement("p", null, "Use JSON booleans for featured, prompt_of_week, and active."), /* @__PURE__ */ React.createElement("p", null, "Use `YYYY-MM-DD HH:MM:SS` for `published_at` when scheduling is needed."), /* @__PURE__ */ React.createElement("p", null, "Use `documentation` for longer public guidance, and keep `usage_notes` short and practical."), /* @__PURE__ */ React.createElement("p", null, "Use `helper_prompts` for data collection or validation prompts, and `prompt_variants` for safer or model-specific alternatives."), /* @__PURE__ */ React.createElement("p", null, "Keep comparison rows normalized so provider/model names remain consistent in the frontend.")))) : null, activeImportTab === "prompts" ? /* @__PURE__ */ React.createElement("div", { className: "grid h-full min-h-0 items-start gap-5 xl:grid-cols-[minmax(0,1.2fr)_360px]" }, /* @__PURE__ */ React.createElement("div", { className: "grid min-w-0 gap-4" }, aiPromptExamples.map((example) => /* @__PURE__ */ React.createElement("div", { key: example.title, className: "min-w-0 overflow-hidden rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-start justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-sky-200/70" }, example.title), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => copyText(example.prompt, example.title), className: "shrink-0 rounded-full border border-white/10 bg-white/[0.04] px-3 py-1.5 text-xs font-semibold text-white transition hover:bg-white/[0.08]" }, "Copy prompt")), /* @__PURE__ */ React.createElement("pre", { className: "nova-scrollbar mt-3 max-h-56 min-w-0 overflow-auto whitespace-pre-wrap break-words rounded-[18px] border border-white/10 bg-slate-950/80 p-4 text-sm leading-6 text-slate-200 [overflow-wrap:anywhere]" }, example.prompt)))), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 self-start rounded-[24px] border border-white/10 bg-white/[0.03] p-4 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Prompt tips"), /* @__PURE__ */ React.createElement("div", { className: "mt-3 space-y-2 leading-6 text-slate-400" }, /* @__PURE__ */ React.createElement("p", null, "Tell the model to return JSON only, with no explanation text."), /* @__PURE__ */ React.createElement("p", null, "Ask for `tool_notes` when you want provider-by-provider comparison output."), /* @__PURE__ */ React.createElement("p", null, "Ask for `documentation`, `placeholders`, `helper_prompts`, and `prompt_variants` only when the prompt needs advanced structure."), /* @__PURE__ */ React.createElement("p", null, "Tell the model to keep titles and tags production-ready, not overly verbose.")))) : null), copyFeedback ? /* @__PURE__ */ React.createElement("div", { className: "px-6 pb-2 text-right text-xs font-medium text-sky-200/80" }, copyFeedback) : null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-end gap-3 border-t border-white/[0.06] px-6 py-4" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => onClose?.(), className: "inline-flex items-center justify-center rounded-full border border-white/[0.08] bg-white/[0.04] px-4 py-2 text-sm font-medium text-white/70 transition hover:bg-white/[0.08] hover:text-white" }, "Cancel"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => onApply?.(), className: "inline-flex items-center justify-center rounded-full border border-sky-300/25 bg-sky-400/90 px-4 py-2 text-sm font-semibold text-slate-950 transition hover:brightness-110" }, "Apply JSON"))) + ), error ? /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-rose-300/20 bg-rose-400/10 px-4 py-3 text-sm text-rose-100" }, error) : null), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-white/[0.03] p-4 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Recognized keys"), /* @__PURE__ */ React.createElement("div", { className: "mt-3 space-y-2 leading-6 text-slate-400" }, /* @__PURE__ */ React.createElement("p", null, "title, slug, excerpt"), /* @__PURE__ */ React.createElement("p", null, "category_id, category, category_slug"), /* @__PURE__ */ React.createElement("p", null, "difficulty, access_level, aspect_ratio"), /* @__PURE__ */ React.createElement("p", null, "tags"), /* @__PURE__ */ React.createElement("p", null, "prompt, negative_prompt"), /* @__PURE__ */ React.createElement("p", null, "usage_notes, workflow_notes"), /* @__PURE__ */ React.createElement("p", null, "documentation, placeholders"), /* @__PURE__ */ React.createElement("p", null, "helper_prompts, prompt_variants"), /* @__PURE__ */ React.createElement("p", null, "filled_examples"), /* @__PURE__ */ React.createElement("p", null, "preview_image, preview_image_url"), /* @__PURE__ */ React.createElement("p", null, "published_at, seo_title, seo_description"), /* @__PURE__ */ React.createElement("p", null, "featured, prompt_of_week, active"), /* @__PURE__ */ React.createElement("p", null, "tool_notes, comparisons")))) : null, activeImportTab === "structure" ? /* @__PURE__ */ React.createElement("div", { className: "grid h-full min-h-0 gap-5 xl:grid-cols-[minmax(0,1.2fr)_360px]" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "mb-3 flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Structure example"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => copyText(JSON.stringify(structureExample, null, 2), "Structure example"), className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1.5 text-xs font-semibold text-white transition hover:bg-white/[0.08]" }, "Copy example")), /* @__PURE__ */ React.createElement("pre", { className: "nova-scrollbar max-h-[52vh] overflow-auto rounded-[20px] border border-white/10 bg-slate-950/80 p-4 text-xs leading-6 text-slate-200" }, JSON.stringify(structureExample, null, 2))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-white/[0.03] p-4 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Notes"), /* @__PURE__ */ React.createElement("div", { className: "mt-3 space-y-3 leading-6 text-slate-400" }, /* @__PURE__ */ React.createElement("p", null, "`tool_notes` can be an array of comparison objects or a simpler array under `comparisons`."), /* @__PURE__ */ React.createElement("p", null, "`documentation`, `placeholders`, `helper_prompts`, `prompt_variants`, and `filled_examples` can be nested JSON and are preserved during import."), /* @__PURE__ */ React.createElement("p", null, "`tags` can be strings or objects with `name`, `label`, `title`, or `slug`."), /* @__PURE__ */ React.createElement("p", null, "`preview_image` accepts either a stored path or an external URL.")))) : null, activeImportTab === "docs" ? /* @__PURE__ */ React.createElement("div", { className: "grid h-full min-h-0 gap-5 xl:grid-cols-[minmax(0,1.2fr)_360px]" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 p-4 text-sm leading-7 text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Field guide"), /* @__PURE__ */ React.createElement("div", { className: "mt-3 space-y-3 text-slate-400" }, /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "title"), " - public prompt name used in the library and detail page."), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "excerpt"), " - short summary for cards, preview blocks, and search results."), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "prompt"), " - the main instruction body shown to creators."), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "negative_prompt"), " - exclusions, defects, or anti-patterns."), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "documentation"), " - structured user-facing guidance for summary, workflow, tips, and common mistakes."), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "placeholders"), " - prompt variables such as `CITY_NAME` or `MONTHLY_WEATHER_DATA`."), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "helper_prompts"), " - supporting prompts for data collection, validation, or refinement."), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "prompt_variants"), " - alternative versions of the same prompt for safer or model-specific output."), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "filled_examples"), " - up to 5 ready-to-copy filled prompt runs that show real placeholder substitutions."), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "tool_notes"), " - structured comparison notes for provider/model variants."), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "preview_image"), " - existing asset URL or stored path. File upload still happens separately."), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", { className: "text-slate-200" }, "category_id"), " is preferred when known. `category` or `category_slug` are used for best-effort matching."))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-white/[0.03] p-4 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Import rules"), /* @__PURE__ */ React.createElement("div", { className: "mt-3 space-y-2 leading-6 text-slate-400" }, /* @__PURE__ */ React.createElement("p", null, "Unknown keys are ignored, so broader AI output is safe to paste."), /* @__PURE__ */ React.createElement("p", null, "Use JSON booleans for featured, prompt_of_week, and active."), /* @__PURE__ */ React.createElement("p", null, "Use `YYYY-MM-DD HH:MM:SS` for `published_at` when scheduling is needed."), /* @__PURE__ */ React.createElement("p", null, "Use `documentation` for longer public guidance, and keep `usage_notes` short and practical."), /* @__PURE__ */ React.createElement("p", null, "Use `helper_prompts` for data collection or validation prompts, `prompt_variants` for safer or model-specific alternatives, and `filled_examples` for ready-made filled prompt runs."), /* @__PURE__ */ React.createElement("p", null, "Keep comparison rows normalized so provider/model names remain consistent in the frontend.")))) : null, activeImportTab === "prompts" ? /* @__PURE__ */ React.createElement("div", { className: "grid h-full min-h-0 items-start gap-5 xl:grid-cols-[minmax(0,1.2fr)_360px]" }, /* @__PURE__ */ React.createElement("div", { className: "grid min-w-0 gap-4" }, aiPromptExamples.map((example) => /* @__PURE__ */ React.createElement("div", { key: example.title, className: "min-w-0 overflow-hidden rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-start justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-sky-200/70" }, example.title), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => copyText(example.prompt, example.title), className: "shrink-0 rounded-full border border-white/10 bg-white/[0.04] px-3 py-1.5 text-xs font-semibold text-white transition hover:bg-white/[0.08]" }, "Copy prompt")), /* @__PURE__ */ React.createElement("pre", { className: "nova-scrollbar mt-3 max-h-56 min-w-0 overflow-auto whitespace-pre-wrap break-words rounded-[18px] border border-white/10 bg-slate-950/80 p-4 text-sm leading-6 text-slate-200 [overflow-wrap:anywhere]" }, example.prompt)))), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 self-start rounded-[24px] border border-white/10 bg-white/[0.03] p-4 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Prompt tips"), /* @__PURE__ */ React.createElement("div", { className: "mt-3 space-y-2 leading-6 text-slate-400" }, /* @__PURE__ */ React.createElement("p", null, "Tell the model to return JSON only, with no explanation text."), /* @__PURE__ */ React.createElement("p", null, "Ask for `tool_notes` when you want provider-by-provider comparison output."), /* @__PURE__ */ React.createElement("p", null, "Ask for `documentation`, `placeholders`, `helper_prompts`, `prompt_variants`, and `filled_examples` when the prompt needs advanced structure and user-ready examples."), /* @__PURE__ */ React.createElement("p", null, "Tell the model to keep titles and tags production-ready, not overly verbose.")))) : null), copyFeedback ? /* @__PURE__ */ React.createElement("div", { className: "px-6 pb-2 text-right text-xs font-medium text-sky-200/80" }, copyFeedback) : null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-end gap-3 border-t border-white/[0.06] px-6 py-4" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => onClose?.(), className: "inline-flex items-center justify-center rounded-full border border-white/[0.08] bg-white/[0.04] px-4 py-2 text-sm font-medium text-white/70 transition hover:bg-white/[0.08] hover:text-white" }, "Cancel"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => onApply?.(), className: "inline-flex items-center justify-center rounded-full border border-sky-300/25 bg-sky-400/90 px-4 py-2 text-sm font-semibold text-slate-950 transition hover:brightness-110" }, "Apply JSON"))) ), document.body ); @@ -24379,7 +24776,7 @@ function PromptComparisonEditor({ comparisons, setComparisons, editorContext }) } )))); } -function Field$5({ field, form }) { +function Field$6({ field, form }) { const value = form.data[field.name]; if (field.type === "checkbox") { return /* @__PURE__ */ React.createElement("label", { className: "flex items-center gap-3 rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("input", { type: "checkbox", checked: Boolean(value), onChange: (event) => form.setData(field.name, event.target.checked) }), field.label); @@ -24575,6 +24972,7 @@ function PromptEditor({ title, subtitle, fields, record, submitUrl, indexUrl, de const placeholdersField = reactExports.useMemo(() => getField(fields, "placeholders"), [fields]); const helperPromptsField = reactExports.useMemo(() => getField(fields, "helper_prompts"), [fields]); const promptVariantsField = reactExports.useMemo(() => getField(fields, "prompt_variants"), [fields]); + const filledExamplesField = reactExports.useMemo(() => getField(fields, "filled_examples"), [fields]); const slugTouchedRef = reactExports.useRef(Boolean(String(record.slug || "").trim())); const [activeTab, setActiveTab] = reactExports.useState("overview"); const [jsonImportOpen, setJsonImportOpen] = reactExports.useState(false); @@ -24638,13 +25036,58 @@ function PromptEditor({ title, subtitle, fields, record, submitUrl, indexUrl, de setJsonImportError(error instanceof Error ? error.message : "Could not parse JSON."); } }; + const generateStarterFilledExamples = () => { + let parsedPlaceholders; + try { + parsedPlaceholders = parseStructuredJson(form.data.placeholders); + } catch { + const message = `${placeholdersField?.label || "Placeholders JSON"} must be valid JSON before generating filled examples.`; + form.setError("placeholders", message); + setActiveTab("advanced"); + showToast(message, "error"); + return; + } + const normalizedPlaceholders = normalizePromptPlaceholders(parsedPlaceholders); + if (normalizedPlaceholders.length === 0) { + const message = "Add at least one placeholder before generating starter filled examples."; + form.setError("placeholders", message); + setActiveTab("advanced"); + showToast(message, "error"); + return; + } + const promptText = String(form.data.prompt || "").trim(); + if (!promptText) { + const message = "Write the main prompt before generating starter filled examples."; + form.setError("prompt", message); + setActiveTab("prompt"); + showToast(message, "error"); + return; + } + const existingExamples = String(form.data.filled_examples || "").trim(); + if (existingExamples && typeof window !== "undefined" && !window.confirm("Replace the current filled examples with a new 5-example starter set?")) { + return; + } + const generatedExamples = buildStarterFilledExamples({ + title: form.data.title, + excerpt: form.data.excerpt, + prompt: promptText, + negativePrompt: form.data.negative_prompt, + placeholders: normalizedPlaceholders + }); + form.clearErrors("placeholders"); + form.clearErrors("filled_examples"); + form.setData("filled_examples", serializeStructuredJson(generatedExamples)); + setActiveTab("advanced"); + showToast("Generated 5 starter filled examples. Review them before saving.", "success"); + }; const submit = (event) => { event.preventDefault(); const advancedJsonFields = [ { name: "documentation", label: documentationField?.label || "Documentation JSON" }, { name: "placeholders", label: placeholdersField?.label || "Placeholders JSON" }, { name: "helper_prompts", label: helperPromptsField?.label || "Helper Prompts JSON" }, - { name: "prompt_variants", label: promptVariantsField?.label || "Prompt Variants JSON" } + { name: "prompt_variants", label: promptVariantsField?.label || "Prompt Variants JSON" }, + { name: "filled_examples", label: filledExamplesField?.label || "Filled Examples JSON" } ]; const parsedJsonFields = {}; for (const field of advancedJsonFields) { @@ -24692,18 +25135,23 @@ function PromptEditor({ title, subtitle, fields, record, submitUrl, indexUrl, de } form.post(submitUrl, submitOptions); }; - return /* @__PURE__ */ React.createElement(AdminLayout, { title, subtitle }, /* @__PURE__ */ React.createElement(Se$1, { title: `Admin · ${title}` }), /* @__PURE__ */ React.createElement("form", { onSubmit: submit, className: "space-y-6 pb-16" }, editorLinks.preview ? /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-3" }, /* @__PURE__ */ React.createElement(xe, { href: editorLinks.preview, className: "rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100" }, "Preview public page")) : null, /* @__PURE__ */ React.createElement("section", { className: "relative overflow-hidden rounded-[28px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(56,189,248,0.14),transparent_34%),linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.94))] shadow-[0_24px_70px_rgba(2,6,23,0.34)] backdrop-blur" }, heroPreviewImage ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "absolute inset-y-0 right-0 w-full bg-[radial-gradient(circle_at_top_left,rgba(56,189,248,0.12),transparent_24%),linear-gradient(90deg,rgba(2,6,23,0.98)_0%,rgba(2,6,23,0.94)_34%,rgba(2,6,23,0.7)_100%)]" }), /* @__PURE__ */ React.createElement("img", { src: heroPreviewImage, alt: "", "aria-hidden": "true", className: "absolute inset-y-0 right-0 h-full w-full object-cover opacity-[0.08] blur-[5px]" })) : null, /* @__PURE__ */ React.createElement("div", { className: "relative grid gap-4 border-b border-white/10 px-5 py-3 lg:grid-cols-[140px_minmax(0,1fr)_auto] lg:items-stretch" }, /* @__PURE__ */ React.createElement("div", { className: "lg:min-h-[150px]" }, heroPreviewImage ? /* @__PURE__ */ React.createElement("div", { className: "h-full overflow-hidden rounded-[20px] border border-white/10 bg-black/25 shadow-[0_16px_34px_rgba(2,6,23,0.26)] backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("div", { className: "relative h-full min-h-[150px] overflow-hidden" }, /* @__PURE__ */ React.createElement("img", { src: heroPreviewImage, alt: form.data.title || "Prompt preview", className: "h-full w-full object-cover" }), /* @__PURE__ */ React.createElement("div", { className: "absolute inset-0 bg-[linear-gradient(180deg,rgba(2,6,23,0.04),rgba(2,6,23,0.48))]" }), /* @__PURE__ */ React.createElement("div", { className: "absolute inset-x-0 bottom-0 border-t border-white/10 bg-[linear-gradient(180deg,rgba(2,6,23,0.32),rgba(2,6,23,0.78))] px-3 py-2.5" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] text-sky-100/80" }, "Loaded preview"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-xs font-semibold text-white" }, "Current prompt image")))) : /* @__PURE__ */ React.createElement("div", { className: "flex h-full min-h-[150px] items-center justify-center rounded-[20px] border border-dashed border-white/10 bg-black/20 px-4 text-center text-xs leading-5 text-slate-400" }, "Upload a prompt preview image in the Media tab to surface it here.")), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1 self-center" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2 text-xs font-semibold uppercase tracking-[0.18em] text-slate-400" }, /* @__PURE__ */ React.createElement(xe, { href: indexUrl, className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1.5 text-white transition hover:bg-white/[0.08]" }, "Back to prompts"), /* @__PURE__ */ React.createElement("span", null, destroyUrl ? "Edit prompt" : "New prompt")), /* @__PURE__ */ React.createElement("h1", { className: "mt-3 text-3xl font-semibold tracking-[-0.05em] text-white" }, form.data.title || "Untitled academy prompt"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 max-w-3xl text-sm leading-6 text-slate-300" }, "Keep the prompt editor focused like a production worksheet: identity first, then the actual prompt body, then model comparisons and publishing details in separate tabs.")), /* @__PURE__ */ React.createElement("div", { className: "self-start lg:justify-self-end" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-nowrap items-center gap-2 lg:justify-end" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => setJsonImportOpen(true), className: "inline-flex items-center gap-2 whitespace-nowrap rounded-2xl border border-white/10 bg-slate-800/90 px-4 py-2 text-sm font-semibold text-white shadow-[inset_0_1px_0_rgba(255,255,255,0.04)] transition hover:bg-slate-700/90" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-file-import text-xs" }), /* @__PURE__ */ React.createElement("span", null, "Import JSON")), /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: form.processing, className: "inline-flex items-center gap-2 whitespace-nowrap rounded-2xl border border-sky-300/25 bg-sky-300/18 px-4 py-2 text-sm font-semibold text-sky-100 shadow-[inset_0_1px_0_rgba(255,255,255,0.05)] transition hover:bg-sky-300/24" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-floppy-disk text-xs" }), /* @__PURE__ */ React.createElement("span", null, form.processing ? "Saving..." : "Save prompt")))))), /* @__PURE__ */ React.createElement(PromptEditorTabs, { activeTab, onChange: setActiveTab, errorCounts: tabErrorCounts }), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5 shadow-[0_18px_50px_rgba(2,6,23,0.14)]" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Current workspace"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white" }, activeTabMeta.label), /* @__PURE__ */ React.createElement("p", { className: "mt-2 max-w-2xl text-sm leading-6 text-slate-400" }, activeTabMeta.description)), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 sm:grid-cols-4" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Prompt words"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-lg font-semibold text-white" }, promptWordCount.toLocaleString())), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Negative words"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-lg font-semibold text-white" }, negativePromptWordCount.toLocaleString())), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Tags"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-lg font-semibold text-white" }, tagCount)), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Comparisons"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-lg font-semibold text-white" }, comparisonCount))))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-6 xl:grid-cols-[minmax(0,1fr)_360px] xl:items-start" }, /* @__PURE__ */ React.createElement("div", { className: "min-w-0 space-y-6", role: "tabpanel", id: `prompt-editor-panel-${activeTab}`, "aria-labelledby": `prompt-editor-tab-${activeTab}` }, /* @__PURE__ */ React.createElement(SectionCard$4, { eyebrow: "Identity", title: "Core prompt details", description: "Set the catalog identity first so the prompt is easy to find, sort, and preview.", className: sectionClassName("prompt-identity") }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 md:grid-cols-2" }, categoryField ? /* @__PURE__ */ React.createElement(NovaSelect, { label: categoryField.label, value: form.data.category_id ?? "", onChange: (nextValue) => { + const hasRequiredCategory2 = reactExports.useMemo(() => { + const existing = String(form.data.category_id || "").trim(); + const named = String(form.data.new_category_name || "").trim(); + return Boolean(existing || named); + }, [form.data.category_id, form.data.new_category_name]); + return /* @__PURE__ */ React.createElement(AdminLayout, { title, subtitle }, /* @__PURE__ */ React.createElement(Se$1, { title: `Admin · ${title}` }), /* @__PURE__ */ React.createElement("form", { onSubmit: submit, className: "space-y-6 pb-16" }, editorLinks.preview ? /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-3" }, /* @__PURE__ */ React.createElement(xe, { href: editorLinks.preview, className: "rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100" }, "Preview public page")) : null, /* @__PURE__ */ React.createElement("section", { className: "relative overflow-hidden rounded-[28px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(56,189,248,0.14),transparent_34%),linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.94))] shadow-[0_24px_70px_rgba(2,6,23,0.34)] backdrop-blur" }, heroPreviewImage ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "absolute inset-y-0 right-0 w-full bg-[radial-gradient(circle_at_top_left,rgba(56,189,248,0.12),transparent_24%),linear-gradient(90deg,rgba(2,6,23,0.98)_0%,rgba(2,6,23,0.94)_34%,rgba(2,6,23,0.7)_100%)]" }), /* @__PURE__ */ React.createElement("img", { src: heroPreviewImage, alt: "", "aria-hidden": "true", className: "absolute inset-y-0 right-0 h-full w-full object-cover opacity-[0.08] blur-[5px]" })) : null, /* @__PURE__ */ React.createElement("div", { className: "relative grid gap-4 border-b border-white/10 px-5 py-3 lg:grid-cols-[140px_minmax(0,1fr)_auto] lg:items-stretch" }, /* @__PURE__ */ React.createElement("div", { className: "lg:min-h-[150px]" }, heroPreviewImage ? /* @__PURE__ */ React.createElement("div", { className: "h-full overflow-hidden rounded-[20px] border border-white/10 bg-black/25 shadow-[0_16px_34px_rgba(2,6,23,0.26)] backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("div", { className: "relative h-full min-h-[150px] overflow-hidden" }, /* @__PURE__ */ React.createElement("img", { src: heroPreviewImage, alt: form.data.title || "Prompt preview", className: "h-full w-full object-cover" }), /* @__PURE__ */ React.createElement("div", { className: "absolute inset-0 bg-[linear-gradient(180deg,rgba(2,6,23,0.04),rgba(2,6,23,0.48))]" }), /* @__PURE__ */ React.createElement("div", { className: "absolute inset-x-0 bottom-0 border-t border-white/10 bg-[linear-gradient(180deg,rgba(2,6,23,0.32),rgba(2,6,23,0.78))] px-3 py-2.5" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] text-sky-100/80" }, "Loaded preview"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-xs font-semibold text-white" }, "Current prompt image")))) : /* @__PURE__ */ React.createElement("div", { className: "flex h-full min-h-[150px] items-center justify-center rounded-[20px] border border-dashed border-white/10 bg-black/20 px-4 text-center text-xs leading-5 text-slate-400" }, "Upload a prompt preview image in the Media tab to surface it here.")), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1 self-center" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2 text-xs font-semibold uppercase tracking-[0.18em] text-slate-400" }, /* @__PURE__ */ React.createElement(xe, { href: indexUrl, className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1.5 text-white transition hover:bg-white/[0.08]" }, "Back to prompts"), /* @__PURE__ */ React.createElement("span", null, destroyUrl ? "Edit prompt" : "New prompt")), /* @__PURE__ */ React.createElement("h1", { className: "mt-3 text-3xl font-semibold tracking-[-0.05em] text-white" }, form.data.title || "Untitled academy prompt"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 max-w-3xl text-sm leading-6 text-slate-300" }, "Keep the prompt editor focused like a production worksheet: identity first, then the actual prompt body, then model comparisons and publishing details in separate tabs.")), /* @__PURE__ */ React.createElement("div", { className: "self-start lg:justify-self-end" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-nowrap items-center gap-2 lg:justify-end" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => setJsonImportOpen(true), className: "inline-flex items-center gap-2 whitespace-nowrap rounded-2xl border border-white/10 bg-slate-800/90 px-4 py-2 text-sm font-semibold text-white shadow-[inset_0_1px_0_rgba(255,255,255,0.04)] transition hover:bg-slate-700/90" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-file-import text-xs" }), /* @__PURE__ */ React.createElement("span", null, "Import JSON")), /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: form.processing || !hasRequiredCategory2, className: "inline-flex items-center gap-2 whitespace-nowrap rounded-2xl border border-sky-300/25 bg-sky-300/18 px-4 py-2 text-sm font-semibold text-sky-100 shadow-[inset_0_1px_0_rgba(255,255,255,0.05)] transition hover:bg-sky-300/24" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-floppy-disk text-xs" }), /* @__PURE__ */ React.createElement("span", null, form.processing ? "Saving..." : "Save prompt")))))), /* @__PURE__ */ React.createElement(PromptEditorTabs, { activeTab, onChange: setActiveTab, errorCounts: tabErrorCounts }), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5 shadow-[0_18px_50px_rgba(2,6,23,0.14)]" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Current workspace"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white" }, activeTabMeta.label), /* @__PURE__ */ React.createElement("p", { className: "mt-2 max-w-2xl text-sm leading-6 text-slate-400" }, activeTabMeta.description)), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 sm:grid-cols-4" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Prompt words"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-lg font-semibold text-white" }, promptWordCount.toLocaleString())), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Negative words"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-lg font-semibold text-white" }, negativePromptWordCount.toLocaleString())), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Tags"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-lg font-semibold text-white" }, tagCount)), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Comparisons"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-lg font-semibold text-white" }, comparisonCount))))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-6 xl:grid-cols-[minmax(0,1fr)_360px] xl:items-start" }, /* @__PURE__ */ React.createElement("div", { className: "min-w-0 space-y-6", role: "tabpanel", id: `prompt-editor-panel-${activeTab}`, "aria-labelledby": `prompt-editor-tab-${activeTab}` }, /* @__PURE__ */ React.createElement(SectionCard$4, { eyebrow: "Identity", title: "Core prompt details", description: "Set the catalog identity first so the prompt is easy to find, sort, and preview.", className: sectionClassName("prompt-identity") }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 md:grid-cols-2" }, categoryField ? /* @__PURE__ */ React.createElement(NovaSelect, { label: categoryField.label, value: form.data.category_id ?? "", onChange: (nextValue) => { form.setData("category_id", nextValue ?? ""); if (nextValue) { form.setData("new_category_name", ""); } - }, options: categoryOptions, searchable: true, searchPlaceholder: "Filter categories...", className: "rounded-2xl bg-black/20", error: form.errors.category_id }) : null, /* @__PURE__ */ React.createElement(TextField$1, { label: "Or enter new category", value: form.data.new_category_name || "", onChange: (event) => form.setData("new_category_name", event.target.value), error: form.errors.new_category_name, placeholder: "New prompt category name" }), difficultyField ? /* @__PURE__ */ React.createElement(NovaSelect, { label: difficultyField.label, value: form.data.difficulty ?? "", onChange: (nextValue) => form.setData("difficulty", nextValue ?? ""), options: difficultyField.options || [], searchable: false, className: "rounded-2xl bg-black/20", error: form.errors.difficulty }) : null), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-black/20 px-4 py-3 text-xs leading-6 text-slate-400" }, "Choose an existing category from the dropdown or type a new category name. When you save, a new prompt category will be created automatically and attached to this prompt."), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 md:grid-cols-2" }, accessField ? /* @__PURE__ */ React.createElement(NovaSelect, { label: accessField.label, value: form.data.access_level ?? "", onChange: (nextValue) => form.setData("access_level", nextValue ?? ""), options: accessField.options || [], searchable: false, className: "rounded-2xl bg-black/20", error: form.errors.access_level }) : null, /* @__PURE__ */ React.createElement(TextField$1, { label: "Aspect ratio", value: form.data.aspect_ratio || "", onChange: (event) => form.setData("aspect_ratio", event.target.value), error: form.errors.aspect_ratio, placeholder: "1:1, 16:9, 3:2" })), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(TextField$1, { label: "Title", value: form.data.title || "", onChange: (event) => form.setData("title", event.target.value), error: form.errors.title, maxLength: 180 }), /* @__PURE__ */ React.createElement("label", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("span", null, "Slug"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => { + }, options: categoryOptions, searchable: true, searchPlaceholder: "Filter categories...", className: "rounded-2xl bg-black/20", error: form.errors.category_id }) : null, /* @__PURE__ */ React.createElement(TextField$1, { label: "Or enter new category", value: form.data.new_category_name || "", onChange: (event) => form.setData("new_category_name", event.target.value), error: form.errors.new_category_name, placeholder: "New prompt category name" }), !hasRequiredCategory2 ? /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-xs text-rose-300" }, "Choose an existing category or enter a new category name before saving.") : null, difficultyField ? /* @__PURE__ */ React.createElement(NovaSelect, { label: difficultyField.label, value: form.data.difficulty ?? "", onChange: (nextValue) => form.setData("difficulty", nextValue ?? ""), options: difficultyField.options || [], searchable: false, className: "rounded-2xl bg-black/20", error: form.errors.difficulty }) : null), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-black/20 px-4 py-3 text-xs leading-6 text-slate-400" }, "Choose an existing category from the dropdown or type a new category name. When you save, a new prompt category will be created automatically and attached to this prompt."), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 md:grid-cols-2" }, accessField ? /* @__PURE__ */ React.createElement(NovaSelect, { label: accessField.label, value: form.data.access_level ?? "", onChange: (nextValue) => form.setData("access_level", nextValue ?? ""), options: accessField.options || [], searchable: false, className: "rounded-2xl bg-black/20", error: form.errors.access_level }) : null, /* @__PURE__ */ React.createElement(TextField$1, { label: "Aspect ratio", value: form.data.aspect_ratio || "", onChange: (event) => form.setData("aspect_ratio", event.target.value), error: form.errors.aspect_ratio, placeholder: "1:1, 16:9, 3:2" })), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(TextField$1, { label: "Title", value: form.data.title || "", onChange: (event) => form.setData("title", event.target.value), error: form.errors.title, maxLength: 180 }), /* @__PURE__ */ React.createElement("label", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("span", null, "Slug"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => { slugTouchedRef.current = false; form.setData("slug", slugifyPromptTitle(form.data.title)); }, className: "rounded-full border border-white/10 bg-white/[0.04] px-2.5 py-1 text-[10px] font-semibold text-white" }, "Sync")), /* @__PURE__ */ React.createElement("input", { value: form.data.slug || "", onChange: (event) => { slugTouchedRef.current = String(event.target.value).trim() !== ""; form.setData("slug", event.target.value); - }, className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-sm text-white outline-none", maxLength: 180, placeholder: "prompt-template-slug" }), form.errors.slug ? /* @__PURE__ */ React.createElement("p", { className: "text-xs text-rose-300" }, form.errors.slug) : null)), /* @__PURE__ */ React.createElement(TextAreaField, { label: "Excerpt", value: form.data.excerpt || "", onChange: (event) => form.setData("excerpt", event.target.value), error: form.errors.excerpt, rows: 4, hint: "Short summary shown in the library and preview cards." }), /* @__PURE__ */ React.createElement(TextField$1, { label: "Tags", value: form.data.tags || "", onChange: (event) => form.setData("tags", event.target.value), error: form.errors.tags, placeholder: "wallpaper, cinematic, neon, portrait" })), /* @__PURE__ */ React.createElement(SectionCard$4, { eyebrow: "Prompt body", title: "Prompt instructions", description: "Write the instruction stack, guardrails, and workflow notes without cramming publishing settings into the same view.", className: sectionClassName("prompt-body") }, /* @__PURE__ */ React.createElement(TextAreaField, { label: "Prompt", value: form.data.prompt || "", onChange: (event) => form.setData("prompt", event.target.value), error: form.errors.prompt, rows: 12, hint: "This is the main model instruction used by creators." }), /* @__PURE__ */ React.createElement(TextAreaField, { label: "Negative prompt", value: form.data.negative_prompt || "", onChange: (event) => form.setData("negative_prompt", event.target.value), error: form.errors.negative_prompt, rows: 6, hint: "Optional exclusions, artifacts, or anti-patterns to avoid." }), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(TextAreaField, { label: "Usage notes", value: form.data.usage_notes || "", onChange: (event) => form.setData("usage_notes", event.target.value), error: form.errors.usage_notes, rows: 6, hint: "Explain how to apply the prompt in a practical workflow." }), /* @__PURE__ */ React.createElement(TextAreaField, { label: "Workflow notes", value: form.data.workflow_notes || "", onChange: (event) => form.setData("workflow_notes", event.target.value), error: form.errors.workflow_notes, rows: 6, hint: "Internal editorial notes, camera settings, or prompt variants." }))), /* @__PURE__ */ React.createElement(SectionCard$4, { eyebrow: "Structured docs", title: "Advanced prompt metadata", description: "Use JSON editors for advanced prompt guidance, variables, supporting prompts, and reusable variants. Keep them valid JSON so the public prompt page can render them safely.", className: sectionClassName("prompt-advanced") }, /* @__PURE__ */ React.createElement(TextAreaField, { label: documentationField?.label || "Documentation JSON", value: form.data.documentation || "", onChange: (event) => form.setData("documentation", event.target.value), error: form.errors.documentation, rows: 12, hint: "Object with summary, best_for, how_to_use, required_inputs, workflow, tips, common_mistakes, data_accuracy_notes, and display_notes." }), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 xl:grid-cols-2" }, /* @__PURE__ */ React.createElement(TextAreaField, { label: placeholdersField?.label || "Placeholders JSON", value: form.data.placeholders || "", onChange: (event) => form.setData("placeholders", event.target.value), error: form.errors.placeholders, rows: 12, hint: "Array of variable objects with key, label, description, required, example, default, and type." }), /* @__PURE__ */ React.createElement(TextAreaField, { label: helperPromptsField?.label || "Helper Prompts JSON", value: form.data.helper_prompts || "", onChange: (event) => form.setData("helper_prompts", event.target.value), error: form.errors.helper_prompts, rows: 12, hint: "Array of supporting prompts used for data collection, preparation, validation, or refinement." })), /* @__PURE__ */ React.createElement(TextAreaField, { label: promptVariantsField?.label || "Prompt Variants JSON", value: form.data.prompt_variants || "", onChange: (event) => form.setData("prompt_variants", event.target.value), error: form.errors.prompt_variants, rows: 12, hint: "Array of alternative prompt versions with prompt, negative_prompt, recommended flags, and risk notes." })), /* @__PURE__ */ React.createElement(SectionCard$4, { eyebrow: "Structured blocks", title: "AI model comparisons", description: "Add reusable same-prompt comparison notes without burying provider-specific behavior inside the main prompt body.", className: sectionClassName("prompt-comparisons") }, /* @__PURE__ */ React.createElement(PromptComparisonEditor, { comparisons: Array.isArray(form.data.tool_notes) ? form.data.tool_notes : [], setComparisons: (nextValue) => form.setData("tool_notes", normalizePromptComparisons(nextValue, { preserveEmpty: true })), editorContext })), /* @__PURE__ */ React.createElement("div", { className: sectionClassName("prompt-media") }, /* @__PURE__ */ React.createElement(PromptPreviewDropzone, { form, previewUrl })), /* @__PURE__ */ React.createElement(SectionCard$4, { eyebrow: "Publishing", title: "Release controls", description: "Choose when the prompt becomes visible and how it behaves in the academy.", className: sectionClassName("prompt-publishing") }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 md:grid-cols-2" }, publishedAtField ? /* @__PURE__ */ React.createElement(DateTimePicker, { label: publishedAtField.label, value: form.data.published_at || "", onChange: (nextValue) => form.setData("published_at", nextValue || ""), error: form.errors.published_at, clearable: true, className: "bg-black/20" }) : null, /* @__PURE__ */ React.createElement(TextField$1, { label: "SEO title", value: form.data.seo_title || "", onChange: (event) => form.setData("seo_title", event.target.value), error: form.errors.seo_title, maxLength: 180 })), seoDescriptionField ? /* @__PURE__ */ React.createElement(TextAreaField, { label: seoDescriptionField.label, value: form.data.seo_description || "", onChange: (event) => form.setData("seo_description", event.target.value), error: form.errors.seo_description, rows: 4 }) : null, /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 md:grid-cols-3" }, featuredField ? /* @__PURE__ */ React.createElement(ToggleField$1, { label: featuredField.label, checked: Boolean(form.data.featured), onChange: (event) => form.setData("featured", event.target.checked), help: "Highlight this prompt in featured rails.", error: form.errors.featured }) : null, promptOfWeekField ? /* @__PURE__ */ React.createElement(ToggleField$1, { label: promptOfWeekField.label, checked: Boolean(form.data.prompt_of_week), onChange: (event) => form.setData("prompt_of_week", event.target.checked), help: "Promote this prompt as the current weekly pick.", error: form.errors.prompt_of_week }) : null, activeField ? /* @__PURE__ */ React.createElement(ToggleField$1, { label: activeField.label, checked: Boolean(form.data.active), onChange: (event) => form.setData("active", event.target.checked), help: "Keep draft prompts hidden until they are ready.", error: form.errors.active }) : null)), /* @__PURE__ */ React.createElement(SectionCard$4, { eyebrow: "Preview", title: "Public-facing snapshot", description: "Check the prompt card summary, tags, and current image before publishing.", className: sectionClassName("prompt-preview") }, /* @__PURE__ */ React.createElement("div", { className: "overflow-hidden rounded-[24px] border border-white/10 bg-black/30" }, previewUrl || form.data.preview_image ? /* @__PURE__ */ React.createElement("img", { src: previewUrl || form.data.preview_image, alt: "Prompt preview", className: "h-64 w-full object-cover" }) : /* @__PURE__ */ React.createElement("div", { className: "flex h-64 items-center justify-center px-6 text-center text-sm text-slate-500" }, "No preview image selected yet.")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400" }, "Prompt summary"), /* @__PURE__ */ React.createElement("h3", { className: "mt-3 text-2xl font-semibold tracking-[-0.04em] text-white" }, form.data.title || "Untitled prompt"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm leading-7 text-slate-400" }, form.data.excerpt || "Add a concise excerpt to give the prompt some context in the library."), /* @__PURE__ */ React.createElement("dl", { className: "mt-4 grid grid-cols-2 gap-3 text-xs text-slate-400" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.03] px-3 py-2" }, /* @__PURE__ */ React.createElement("dt", { className: "uppercase tracking-[0.16em] text-slate-500" }, "Difficulty"), /* @__PURE__ */ React.createElement("dd", { className: "mt-1 text-sm text-white" }, form.data.difficulty || "—")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.03] px-3 py-2" }, /* @__PURE__ */ React.createElement("dt", { className: "uppercase tracking-[0.16em] text-slate-500" }, "Access"), /* @__PURE__ */ React.createElement("dd", { className: "mt-1 text-sm text-white" }, form.data.access_level || "—")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.03] px-3 py-2" }, /* @__PURE__ */ React.createElement("dt", { className: "uppercase tracking-[0.16em] text-slate-500" }, "Aspect"), /* @__PURE__ */ React.createElement("dd", { className: "mt-1 text-sm text-white" }, form.data.aspect_ratio || "—")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.03] px-3 py-2" }, /* @__PURE__ */ React.createElement("dt", { className: "uppercase tracking-[0.16em] text-slate-500" }, "Comparisons"), /* @__PURE__ */ React.createElement("dd", { className: "mt-1 text-sm text-white" }, comparisonCount)))))), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 space-y-6 xl:sticky xl:top-6 xl:self-start" }, /* @__PURE__ */ React.createElement(SectionCard$4, { eyebrow: "At a glance", title: "Prompt status", description: "A compact summary while you work through the tabs." }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 sm:grid-cols-2 xl:grid-cols-1" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Prompt words"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-lg font-semibold text-white" }, promptWordCount.toLocaleString())), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Tags"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-lg font-semibold text-white" }, tagCount)), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Comparisons"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-lg font-semibold text-white" }, comparisonCount)), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Visibility"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-lg font-semibold text-white" }, form.data.active ? "Active" : "Draft"))), /* @__PURE__ */ React.createElement("p", { className: "text-xs leading-6 text-slate-500" }, "Uploaded images are converted to WebP and stored on the Contabo S3-backed CDN before the record is saved.")))), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-3 rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: form.processing, className: "rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100" }, form.processing ? "Saving..." : "Save prompt"), /* @__PURE__ */ React.createElement(xe, { href: indexUrl, className: "rounded-full border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white" }, "Back"), destroyUrl ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => { + }, className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-sm text-white outline-none", maxLength: 180, placeholder: "prompt-template-slug" }), form.errors.slug ? /* @__PURE__ */ React.createElement("p", { className: "text-xs text-rose-300" }, form.errors.slug) : null)), /* @__PURE__ */ React.createElement(TextAreaField, { label: "Excerpt", value: form.data.excerpt || "", onChange: (event) => form.setData("excerpt", event.target.value), error: form.errors.excerpt, rows: 4, hint: "Short summary shown in the library and preview cards." }), /* @__PURE__ */ React.createElement(TextField$1, { label: "Tags", value: form.data.tags || "", onChange: (event) => form.setData("tags", event.target.value), error: form.errors.tags, placeholder: "wallpaper, cinematic, neon, portrait" })), /* @__PURE__ */ React.createElement(SectionCard$4, { eyebrow: "Prompt body", title: "Prompt instructions", description: "Write the instruction stack, guardrails, and workflow notes without cramming publishing settings into the same view.", className: sectionClassName("prompt-body") }, /* @__PURE__ */ React.createElement(TextAreaField, { label: "Prompt", value: form.data.prompt || "", onChange: (event) => form.setData("prompt", event.target.value), error: form.errors.prompt, rows: 12, hint: "This is the main model instruction used by creators." }), /* @__PURE__ */ React.createElement(TextAreaField, { label: "Negative prompt", value: form.data.negative_prompt || "", onChange: (event) => form.setData("negative_prompt", event.target.value), error: form.errors.negative_prompt, rows: 6, hint: "Optional exclusions, artifacts, or anti-patterns to avoid." }), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(TextAreaField, { label: "Usage notes", value: form.data.usage_notes || "", onChange: (event) => form.setData("usage_notes", event.target.value), error: form.errors.usage_notes, rows: 6, hint: "Explain how to apply the prompt in a practical workflow." }), /* @__PURE__ */ React.createElement(TextAreaField, { label: "Workflow notes", value: form.data.workflow_notes || "", onChange: (event) => form.setData("workflow_notes", event.target.value), error: form.errors.workflow_notes, rows: 6, hint: "Internal editorial notes, camera settings, or prompt variants." }))), /* @__PURE__ */ React.createElement(SectionCard$4, { eyebrow: "Structured docs", title: "Advanced prompt metadata", description: "Use JSON editors for advanced prompt guidance, variables, supporting prompts, and reusable variants. Keep them valid JSON so the public prompt page can render them safely.", className: sectionClassName("prompt-advanced") }, /* @__PURE__ */ React.createElement(TextAreaField, { label: documentationField?.label || "Documentation JSON", value: form.data.documentation || "", onChange: (event) => form.setData("documentation", event.target.value), error: form.errors.documentation, rows: 12, hint: "Object with summary, best_for, how_to_use, required_inputs, workflow, tips, common_mistakes, data_accuracy_notes, and display_notes." }), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 xl:grid-cols-2" }, /* @__PURE__ */ React.createElement(TextAreaField, { label: placeholdersField?.label || "Placeholders JSON", value: form.data.placeholders || "", onChange: (event) => form.setData("placeholders", event.target.value), error: form.errors.placeholders, rows: 12, hint: "Array of variable objects with key, label, description, required, example, default, and type." }), /* @__PURE__ */ React.createElement(TextAreaField, { label: helperPromptsField?.label || "Helper Prompts JSON", value: form.data.helper_prompts || "", onChange: (event) => form.setData("helper_prompts", event.target.value), error: form.errors.helper_prompts, rows: 12, hint: "Array of supporting prompts used for data collection, preparation, validation, or refinement." })), /* @__PURE__ */ React.createElement(TextAreaField, { label: promptVariantsField?.label || "Prompt Variants JSON", value: form.data.prompt_variants || "", onChange: (event) => form.setData("prompt_variants", event.target.value), error: form.errors.prompt_variants, rows: 12, hint: "Array of alternative prompt versions with prompt, negative_prompt, recommended flags, and risk notes." }), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3 rounded-[24px] border border-white/10 bg-black/20 px-4 py-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-sm font-semibold text-white" }, "Starter filled examples"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-xs leading-5 text-slate-400" }, "Generate 5 editable examples from the current placeholders, prompt text, and negative prompt.")), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: generateStarterFilledExamples, className: "rounded-full border border-amber-300/25 bg-amber-300/12 px-4 py-2.5 text-sm font-semibold text-amber-100 transition hover:bg-amber-300/18" }, "Generate 5 starter examples")), /* @__PURE__ */ React.createElement(TextAreaField, { label: filledExamplesField?.label || "Filled Examples JSON", value: form.data.filled_examples || "", onChange: (event) => form.setData("filled_examples", event.target.value), error: form.errors.filled_examples, rows: 12, hint: "Array of up to 5 filled prompt examples with title, description, placeholder_values, prompt, and optional negative_prompt." })), /* @__PURE__ */ React.createElement(SectionCard$4, { eyebrow: "Structured blocks", title: "AI model comparisons", description: "Add reusable same-prompt comparison notes without burying provider-specific behavior inside the main prompt body.", className: sectionClassName("prompt-comparisons") }, /* @__PURE__ */ React.createElement(PromptComparisonEditor, { comparisons: Array.isArray(form.data.tool_notes) ? form.data.tool_notes : [], setComparisons: (nextValue) => form.setData("tool_notes", normalizePromptComparisons(nextValue, { preserveEmpty: true })), editorContext })), /* @__PURE__ */ React.createElement("div", { className: sectionClassName("prompt-media") }, /* @__PURE__ */ React.createElement(PromptPreviewDropzone, { form, previewUrl })), /* @__PURE__ */ React.createElement(SectionCard$4, { eyebrow: "Publishing", title: "Release controls", description: "Choose when the prompt becomes visible and how it behaves in the academy.", className: sectionClassName("prompt-publishing") }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 md:grid-cols-2" }, publishedAtField ? /* @__PURE__ */ React.createElement(DateTimePicker, { label: publishedAtField.label, value: form.data.published_at || "", onChange: (nextValue) => form.setData("published_at", nextValue || ""), error: form.errors.published_at, clearable: true, className: "bg-black/20" }) : null, /* @__PURE__ */ React.createElement(TextField$1, { label: "SEO title", value: form.data.seo_title || "", onChange: (event) => form.setData("seo_title", event.target.value), error: form.errors.seo_title, maxLength: 180 })), seoDescriptionField ? /* @__PURE__ */ React.createElement(TextAreaField, { label: seoDescriptionField.label, value: form.data.seo_description || "", onChange: (event) => form.setData("seo_description", event.target.value), error: form.errors.seo_description, rows: 4 }) : null, /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 md:grid-cols-3" }, featuredField ? /* @__PURE__ */ React.createElement(ToggleField$1, { label: featuredField.label, checked: Boolean(form.data.featured), onChange: (event) => form.setData("featured", event.target.checked), help: "Highlight this prompt in featured rails.", error: form.errors.featured }) : null, promptOfWeekField ? /* @__PURE__ */ React.createElement(ToggleField$1, { label: promptOfWeekField.label, checked: Boolean(form.data.prompt_of_week), onChange: (event) => form.setData("prompt_of_week", event.target.checked), help: "Promote this prompt as the current weekly pick.", error: form.errors.prompt_of_week }) : null, activeField ? /* @__PURE__ */ React.createElement(ToggleField$1, { label: activeField.label, checked: Boolean(form.data.active), onChange: (event) => form.setData("active", event.target.checked), help: "Keep draft prompts hidden until they are ready.", error: form.errors.active }) : null)), /* @__PURE__ */ React.createElement(SectionCard$4, { eyebrow: "Preview", title: "Public-facing snapshot", description: "Check the prompt card summary, tags, and current image before publishing.", className: sectionClassName("prompt-preview") }, /* @__PURE__ */ React.createElement("div", { className: "overflow-hidden rounded-[24px] border border-white/10 bg-black/30" }, previewUrl || form.data.preview_image ? /* @__PURE__ */ React.createElement("img", { src: previewUrl || form.data.preview_image, alt: "Prompt preview", className: "h-64 w-full object-cover" }) : /* @__PURE__ */ React.createElement("div", { className: "flex h-64 items-center justify-center px-6 text-center text-sm text-slate-500" }, "No preview image selected yet.")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400" }, "Prompt summary"), /* @__PURE__ */ React.createElement("h3", { className: "mt-3 text-2xl font-semibold tracking-[-0.04em] text-white" }, form.data.title || "Untitled prompt"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm leading-7 text-slate-400" }, form.data.excerpt || "Add a concise excerpt to give the prompt some context in the library."), /* @__PURE__ */ React.createElement("dl", { className: "mt-4 grid grid-cols-2 gap-3 text-xs text-slate-400" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.03] px-3 py-2" }, /* @__PURE__ */ React.createElement("dt", { className: "uppercase tracking-[0.16em] text-slate-500" }, "Difficulty"), /* @__PURE__ */ React.createElement("dd", { className: "mt-1 text-sm text-white" }, form.data.difficulty || "—")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.03] px-3 py-2" }, /* @__PURE__ */ React.createElement("dt", { className: "uppercase tracking-[0.16em] text-slate-500" }, "Access"), /* @__PURE__ */ React.createElement("dd", { className: "mt-1 text-sm text-white" }, form.data.access_level || "—")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.03] px-3 py-2" }, /* @__PURE__ */ React.createElement("dt", { className: "uppercase tracking-[0.16em] text-slate-500" }, "Aspect"), /* @__PURE__ */ React.createElement("dd", { className: "mt-1 text-sm text-white" }, form.data.aspect_ratio || "—")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.03] px-3 py-2" }, /* @__PURE__ */ React.createElement("dt", { className: "uppercase tracking-[0.16em] text-slate-500" }, "Comparisons"), /* @__PURE__ */ React.createElement("dd", { className: "mt-1 text-sm text-white" }, comparisonCount)))))), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 space-y-6 xl:sticky xl:top-6 xl:self-start" }, /* @__PURE__ */ React.createElement(SectionCard$4, { eyebrow: "At a glance", title: "Prompt status", description: "A compact summary while you work through the tabs." }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 sm:grid-cols-2 xl:grid-cols-1" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Prompt words"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-lg font-semibold text-white" }, promptWordCount.toLocaleString())), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Tags"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-lg font-semibold text-white" }, tagCount)), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Comparisons"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-lg font-semibold text-white" }, comparisonCount)), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Visibility"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-lg font-semibold text-white" }, form.data.active ? "Active" : "Draft"))), /* @__PURE__ */ React.createElement("p", { className: "text-xs leading-6 text-slate-500" }, "Uploaded images are converted to WebP and stored on the Contabo S3-backed CDN before the record is saved.")))), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-3 rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: form.processing, className: "rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100" }, form.processing ? "Saving..." : "Save prompt"), /* @__PURE__ */ React.createElement(xe, { href: indexUrl, className: "rounded-full border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white" }, "Back"), destroyUrl ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => { if (!window.confirm("Delete this record?")) return; At.delete(destroyUrl); }, className: "rounded-full border border-rose-300/20 bg-rose-300/10 px-5 py-3 text-sm font-semibold text-rose-100" }, "Delete") : null)), /* @__PURE__ */ React.createElement( @@ -24755,7 +25203,7 @@ function GenericEditor({ title, subtitle, fields, record, submitUrl, indexUrl, d } form.post(submitUrl, submitOptions); }; - return /* @__PURE__ */ React.createElement(AdminLayout, { title, subtitle }, /* @__PURE__ */ React.createElement(Se$1, { title: `Admin · ${title}` }), editorLinks.builder || editorLinks.preview ? /* @__PURE__ */ React.createElement("div", { className: "mb-5 flex flex-wrap gap-3" }, editorLinks.builder ? /* @__PURE__ */ React.createElement(xe, { href: editorLinks.builder, className: "rounded-full border border-amber-300/20 bg-amber-300/10 px-5 py-3 text-sm font-semibold text-amber-100" }, "Open builder") : null, editorLinks.preview ? /* @__PURE__ */ React.createElement(xe, { href: editorLinks.preview, className: "rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100" }, "Preview public page") : null) : null, /* @__PURE__ */ React.createElement("form", { onSubmit: submit, className: "space-y-5 rounded-[30px] border border-white/[0.08] bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-5" }, fields.map((field) => /* @__PURE__ */ React.createElement(Field$5, { key: field.name, field, form }))), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-3" }, /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: form.processing, className: "rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100" }, form.processing ? "Saving..." : "Save"), /* @__PURE__ */ React.createElement(xe, { href: indexUrl, className: "rounded-full border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white" }, "Back"), destroyUrl ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => { + return /* @__PURE__ */ React.createElement(AdminLayout, { title, subtitle }, /* @__PURE__ */ React.createElement(Se$1, { title: `Admin · ${title}` }), editorLinks.builder || editorLinks.preview ? /* @__PURE__ */ React.createElement("div", { className: "mb-5 flex flex-wrap gap-3" }, editorLinks.builder ? /* @__PURE__ */ React.createElement(xe, { href: editorLinks.builder, className: "rounded-full border border-amber-300/20 bg-amber-300/10 px-5 py-3 text-sm font-semibold text-amber-100" }, "Open builder") : null, editorLinks.preview ? /* @__PURE__ */ React.createElement(xe, { href: editorLinks.preview, className: "rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100" }, "Preview public page") : null) : null, /* @__PURE__ */ React.createElement("form", { onSubmit: submit, className: "space-y-5 rounded-[30px] border border-white/[0.08] bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-5" }, fields.map((field) => /* @__PURE__ */ React.createElement(Field$6, { key: field.name, field, form }))), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-3" }, /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: form.processing, className: "rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100" }, form.processing ? "Saving..." : "Save"), /* @__PURE__ */ React.createElement(xe, { href: indexUrl, className: "rounded-full border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white" }, "Back"), destroyUrl ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => { if (!window.confirm("Delete this record?")) return; At.delete(destroyUrl); }, className: "rounded-full border border-rose-300/20 bg-rose-300/10 px-5 py-3 text-sm font-semibold text-rose-100" }, "Delete") : null)), /* @__PURE__ */ React.createElement( @@ -24915,14 +25363,33 @@ function courseSummary(items = [], summary = null) { visibleOnPage: accumulator.visibleOnPage + 1 }), { total: 0, published: 0, featured: 0, drafts: 0, visibleOnPage: 0 }); } -function promptSummary(items = []) { - return items.reduce((summary, item) => ({ - total: summary.total + 1, - active: summary.active + (item.active ? 1 : 0), - featured: summary.featured + (item.featured ? 1 : 0), - promptOfWeek: summary.promptOfWeek + (item.prompt_of_week ? 1 : 0), - comparisons: summary.comparisons + Number(item.comparisons_count || 0) - }), { total: 0, active: 0, featured: 0, promptOfWeek: 0, comparisons: 0 }); +function promptSummary(items = [], summary = null) { + if (summary && typeof summary === "object") { + return { + total: Number(summary.total || 0), + active: Number(summary.active || 0), + featured: Number(summary.featured || 0), + promptOfWeek: Number(summary.promptOfWeek || 0), + comparisons: Array.isArray(items) ? items.reduce((count, item) => count + Number(item.comparisons_count || 0), 0) : 0, + access: { + free: Number(summary.access?.free || 0), + creator: Number(summary.access?.creator || 0), + pro: Number(summary.access?.pro || 0) + } + }; + } + return items.reduce((summary2, item) => ({ + total: summary2.total + 1, + active: summary2.active + (item.active ? 1 : 0), + featured: summary2.featured + (item.featured ? 1 : 0), + promptOfWeek: summary2.promptOfWeek + (item.prompt_of_week ? 1 : 0), + comparisons: summary2.comparisons + Number(item.comparisons_count || 0), + access: { + free: summary2.access.free + (item.access_level === "free" ? 1 : 0), + creator: summary2.access.creator + (item.access_level === "creator" ? 1 : 0), + pro: summary2.access.pro + (item.access_level === "pro" ? 1 : 0) + } + }), { total: 0, active: 0, featured: 0, promptOfWeek: 0, comparisons: 0, access: { free: 0, creator: 0, pro: 0 } }); } function PromptFlag({ children, tone = "default" }) { const toneClass = tone === "warm" ? "border-[#ffcfbf]/20 bg-[#ffcfbf]/10 text-[#fff0ea]" : tone === "sky" ? "border-sky-300/20 bg-sky-300/10 text-sky-100" : tone === "emerald" ? "border-emerald-300/20 bg-emerald-300/10 text-emerald-100" : "border-white/10 bg-white/[0.05] text-slate-200"; @@ -25012,16 +25479,56 @@ function PromptPreview({ item, compact = false }) { return /* @__PURE__ */ React.createElement("div", { className: "flex h-full items-center justify-center bg-[radial-gradient(circle_at_top_left,rgba(56,189,248,0.18),transparent_30%),linear-gradient(135deg,rgba(15,23,42,0.98),rgba(30,41,59,0.94))] p-6 text-center text-slate-300" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Prompt preview"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm font-semibold text-white" }, "No image attached yet"))); } function PromptMeta({ item }) { - return /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, item.category_name ? /* @__PURE__ */ React.createElement(PromptFlag, { tone: "warm" }, item.category_name) : null, item.difficulty ? /* @__PURE__ */ React.createElement(PromptFlag, null, item.difficulty) : null, item.access_level ? /* @__PURE__ */ React.createElement(PromptFlag, null, item.access_level) : null, item.aspect_ratio ? /* @__PURE__ */ React.createElement(PromptFlag, null, item.aspect_ratio) : null, item.featured ? /* @__PURE__ */ React.createElement(PromptFlag, { tone: "sky" }, "Featured") : null, item.prompt_of_week ? /* @__PURE__ */ React.createElement(PromptFlag, { tone: "emerald" }, "Prompt of week") : null, /* @__PURE__ */ React.createElement(PromptFlag, { tone: item.active ? "sky" : "default" }, item.active ? "Active" : "Draft")); + return /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, item.access_level ? /* @__PURE__ */ React.createElement(AccessBadge, { tier: item.access_level, className: "px-3 py-1" }) : null, item.category_name ? /* @__PURE__ */ React.createElement(PromptFlag, { tone: "warm" }, item.category_name) : null, item.difficulty ? /* @__PURE__ */ React.createElement(PromptFlag, null, item.difficulty) : null, item.aspect_ratio ? /* @__PURE__ */ React.createElement(PromptFlag, null, item.aspect_ratio) : null, item.featured ? /* @__PURE__ */ React.createElement(PromptFlag, { tone: "sky" }, "Featured") : null, item.prompt_of_week ? /* @__PURE__ */ React.createElement(PromptFlag, { tone: "emerald" }, "Prompt of week") : null, /* @__PURE__ */ React.createElement(PromptFlag, { tone: item.active ? "sky" : "default" }, item.active ? "Active" : "Draft")); } function PromptGalleryCard({ item }) { - return /* @__PURE__ */ React.createElement("article", { className: "group overflow-hidden rounded-[30px] border border-white/[0.08] bg-[linear-gradient(135deg,rgba(8,15,28,0.98),rgba(15,23,42,0.92))] shadow-[0_24px_80px_rgba(2,6,23,0.24)]" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-0 xl:grid-cols-[340px_minmax(0,1fr)]" }, /* @__PURE__ */ React.createElement("div", { className: "relative min-h-[250px] overflow-hidden border-b border-white/10 xl:min-h-full xl:border-b-0 xl:border-r xl:border-white/10" }, /* @__PURE__ */ React.createElement(PromptPreview, { item }), /* @__PURE__ */ React.createElement("div", { className: "absolute inset-0 bg-[linear-gradient(180deg,rgba(2,6,23,0.04),rgba(2,6,23,0.32))]" }), /* @__PURE__ */ React.createElement("div", { className: "absolute left-4 top-4 flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement(PromptFlag, { tone: "warm" }, item.comparisons_count || 0, " comparisons"), item.slug ? /* @__PURE__ */ React.createElement(PromptFlag, null, item.slug) : null)), /* @__PURE__ */ React.createElement("div", { className: "flex h-full flex-col justify-between p-6 lg:p-7" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(PromptMeta, { item }), /* @__PURE__ */ React.createElement("h2", { className: "mt-4 text-2xl font-semibold tracking-[-0.04em] text-white" }, item.title), /* @__PURE__ */ React.createElement("p", { className: "mt-3 max-w-3xl text-sm leading-7 text-slate-300" }, item.excerpt || "Add an excerpt to make this prompt easier to scan in moderation."), item.tags?.length ? /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-wrap gap-2" }, item.tags.slice(0, 5).map((tag) => /* @__PURE__ */ React.createElement("span", { key: tag, className: "rounded-full border border-white/10 bg-black/20 px-3 py-1 text-xs font-semibold uppercase tracking-[0.18em] text-slate-300" }, tag))) : null), /* @__PURE__ */ React.createElement("div", { className: "mt-6 flex flex-wrap items-center justify-between gap-4 border-t border-white/10 pt-5" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 sm:grid-cols-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Updated"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm font-semibold text-white" }, formatDateLabel$1(item.updated_at))), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Access"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm font-semibold text-white" }, item.access_level || "free")), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Status"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm font-semibold text-white" }, item.active ? "Visible" : "Hidden"))), /* @__PURE__ */ React.createElement(PromptActions, { item }))))); + return /* @__PURE__ */ React.createElement("article", { className: "group overflow-hidden rounded-[30px] border border-white/[0.08] bg-[linear-gradient(135deg,rgba(8,15,28,0.98),rgba(15,23,42,0.92))] shadow-[0_24px_80px_rgba(2,6,23,0.24)]" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-0 xl:grid-cols-[340px_minmax(0,1fr)]" }, /* @__PURE__ */ React.createElement("div", { className: "relative min-h-[250px] overflow-hidden border-b border-white/10 xl:min-h-full xl:border-b-0 xl:border-r xl:border-white/10" }, /* @__PURE__ */ React.createElement(PromptPreview, { item }), /* @__PURE__ */ React.createElement("div", { className: "absolute inset-0 bg-[linear-gradient(180deg,rgba(2,6,23,0.04),rgba(2,6,23,0.32))]" }), /* @__PURE__ */ React.createElement("div", { className: "absolute left-4 top-4 flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement(AccessBadge, { tier: item.access_level || "free", className: "px-3 py-1.5 text-[12px]" }), /* @__PURE__ */ React.createElement(PromptFlag, null, Number(item.views_count || 0).toLocaleString(), " views"), /* @__PURE__ */ React.createElement(PromptFlag, { tone: "warm" }, item.comparisons_count || 0, " comparisons"), item.slug ? /* @__PURE__ */ React.createElement(PromptFlag, null, item.slug) : null)), /* @__PURE__ */ React.createElement("div", { className: "flex h-full flex-col justify-between p-6 lg:p-7" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(PromptMeta, { item }), /* @__PURE__ */ React.createElement("h2", { className: "mt-4 text-2xl font-semibold tracking-[-0.04em] text-white" }, item.title), /* @__PURE__ */ React.createElement("p", { className: "mt-3 max-w-3xl text-sm leading-7 text-slate-300" }, item.excerpt || "Add an excerpt to make this prompt easier to scan in moderation."), item.tags?.length ? /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-wrap gap-2" }, item.tags.slice(0, 5).map((tag) => /* @__PURE__ */ React.createElement("span", { key: tag, className: "rounded-full border border-white/10 bg-black/20 px-3 py-1 text-xs font-semibold uppercase tracking-[0.18em] text-slate-300" }, tag))) : null), /* @__PURE__ */ React.createElement("div", { className: "mt-6 flex flex-wrap items-center justify-between gap-4 border-t border-white/10 pt-5" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 sm:grid-cols-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Updated"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm font-semibold text-white" }, formatDateLabel$1(item.updated_at))), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Access"), /* @__PURE__ */ React.createElement("div", { className: "mt-2" }, /* @__PURE__ */ React.createElement(AccessBadge, { tier: item.access_level || "free" }))), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Status"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm font-semibold text-white" }, item.active ? "Visible" : "Hidden"))), /* @__PURE__ */ React.createElement(PromptActions, { item }))))); } function PromptGridCard({ item }) { - return /* @__PURE__ */ React.createElement("article", { className: "group overflow-hidden rounded-[28px] border border-white/[0.08] bg-[linear-gradient(180deg,rgba(255,255,255,0.04),rgba(15,23,42,0.18))] shadow-[0_18px_60px_rgba(2,6,23,0.18)]" }, /* @__PURE__ */ React.createElement("div", { className: "relative h-52 overflow-hidden border-b border-white/10" }, /* @__PURE__ */ React.createElement(PromptPreview, { item, compact: true }), /* @__PURE__ */ React.createElement("div", { className: "absolute inset-0 bg-[linear-gradient(180deg,rgba(2,6,23,0.02),rgba(2,6,23,0.34))]" })), /* @__PURE__ */ React.createElement("div", { className: "p-5" }, /* @__PURE__ */ React.createElement(PromptMeta, { item }), /* @__PURE__ */ React.createElement("h2", { className: "mt-4 text-xl font-semibold tracking-[-0.04em] text-white" }, item.title), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-7 text-slate-300" }, item.excerpt || "No excerpt added yet."), /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex items-center justify-between gap-3 text-sm text-slate-400" }, /* @__PURE__ */ React.createElement("span", null, formatDateLabel$1(item.updated_at)), /* @__PURE__ */ React.createElement("span", null, item.comparisons_count || 0, " comparisons")), /* @__PURE__ */ React.createElement("div", { className: "mt-5" }, /* @__PURE__ */ React.createElement(PromptActions, { item })))); + return /* @__PURE__ */ React.createElement("article", { className: "group overflow-hidden rounded-[28px] border border-white/[0.08] bg-[linear-gradient(180deg,rgba(255,255,255,0.04),rgba(15,23,42,0.18))] shadow-[0_18px_60px_rgba(2,6,23,0.18)]" }, /* @__PURE__ */ React.createElement("div", { className: "relative h-52 overflow-hidden border-b border-white/10" }, /* @__PURE__ */ React.createElement(PromptPreview, { item, compact: true }), /* @__PURE__ */ React.createElement("div", { className: "absolute inset-0 bg-[linear-gradient(180deg,rgba(2,6,23,0.02),rgba(2,6,23,0.34))]" }), /* @__PURE__ */ React.createElement("div", { className: "absolute left-4 top-4" }, /* @__PURE__ */ React.createElement(AccessBadge, { tier: item.access_level || "free", className: "px-3 py-1.5 text-[12px]" }))), /* @__PURE__ */ React.createElement("div", { className: "p-5" }, /* @__PURE__ */ React.createElement(PromptMeta, { item }), /* @__PURE__ */ React.createElement("h2", { className: "mt-4 text-xl font-semibold tracking-[-0.04em] text-white" }, item.title), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-7 text-slate-300" }, item.excerpt || "No excerpt added yet."), /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex items-center justify-between gap-3 text-sm text-slate-400" }, /* @__PURE__ */ React.createElement("span", null, formatDateLabel$1(item.updated_at)), /* @__PURE__ */ React.createElement("span", null, Number(item.views_count || 0).toLocaleString(), " views")), /* @__PURE__ */ React.createElement("div", { className: "mt-5" }, /* @__PURE__ */ React.createElement(PromptActions, { item })))); } function PromptTable({ items }) { - return /* @__PURE__ */ React.createElement("div", { className: "overflow-hidden rounded-[30px] border border-white/[0.08] bg-[linear-gradient(180deg,rgba(15,23,42,0.92),rgba(2,6,23,0.92))] shadow-[0_24px_80px_rgba(2,6,23,0.22)]" }, /* @__PURE__ */ React.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React.createElement("table", { className: "min-w-full divide-y divide-white/10 text-left" }, /* @__PURE__ */ React.createElement("thead", { className: "bg-white/[0.04] text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400" }, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", { className: "px-5 py-4" }, "Prompt"), /* @__PURE__ */ React.createElement("th", { className: "px-5 py-4" }, "Category"), /* @__PURE__ */ React.createElement("th", { className: "px-5 py-4" }, "Access"), /* @__PURE__ */ React.createElement("th", { className: "px-5 py-4" }, "Signals"), /* @__PURE__ */ React.createElement("th", { className: "px-5 py-4" }, "Updated"), /* @__PURE__ */ React.createElement("th", { className: "px-5 py-4 text-right" }, "Actions"))), /* @__PURE__ */ React.createElement("tbody", { className: "divide-y divide-white/10 text-sm text-slate-200" }, items.map((item) => /* @__PURE__ */ React.createElement("tr", { key: item.id, className: "align-top transition hover:bg-white/[0.03]" }, /* @__PURE__ */ React.createElement("td", { className: "px-5 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex gap-4" }, /* @__PURE__ */ React.createElement("div", { className: "h-16 w-20 overflow-hidden rounded-2xl border border-white/10 bg-black/30 shrink-0" }, /* @__PURE__ */ React.createElement(PromptPreview, { item, compact: true })), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-white" }, item.title), /* @__PURE__ */ React.createElement("p", { className: "mt-1 max-w-md text-sm leading-6 text-slate-400" }, item.excerpt || "No excerpt added yet.")))), /* @__PURE__ */ React.createElement("td", { className: "px-5 py-4" }, item.category_name || "Uncategorized"), /* @__PURE__ */ React.createElement("td", { className: "px-5 py-4" }, item.access_level || "free"), /* @__PURE__ */ React.createElement("td", { className: "px-5 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React.createElement("p", null, item.comparisons_count || 0, " comparisons"), /* @__PURE__ */ React.createElement("p", null, item.difficulty || "No difficulty"), /* @__PURE__ */ React.createElement("p", null, item.active ? "Active" : "Draft"))), /* @__PURE__ */ React.createElement("td", { className: "px-5 py-4" }, formatDateLabel$1(item.updated_at)), /* @__PURE__ */ React.createElement("td", { className: "px-5 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex justify-end gap-2" }, item.preview_url ? /* @__PURE__ */ React.createElement(xe, { href: item.preview_url, className: "rounded-full border border-[#ffcfbf]/20 bg-[#ffcfbf]/10 px-3 py-2 text-xs font-semibold text-[#fff0ea]" }, "Preview") : null, /* @__PURE__ */ React.createElement(xe, { href: item.edit_url, className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-2 text-xs font-semibold text-white" }, "Edit"))))))))); + return /* @__PURE__ */ React.createElement("div", { className: "overflow-hidden rounded-[30px] border border-white/[0.08] bg-[linear-gradient(180deg,rgba(15,23,42,0.92),rgba(2,6,23,0.92))] shadow-[0_24px_80px_rgba(2,6,23,0.22)]" }, /* @__PURE__ */ React.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React.createElement("table", { className: "min-w-full divide-y divide-white/10 text-left" }, /* @__PURE__ */ React.createElement("thead", { className: "bg-white/[0.04] text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400" }, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", { className: "px-5 py-4" }, "Prompt"), /* @__PURE__ */ React.createElement("th", { className: "px-5 py-4" }, "Category"), /* @__PURE__ */ React.createElement("th", { className: "px-5 py-4" }, "Access"), /* @__PURE__ */ React.createElement("th", { className: "px-5 py-4" }, "Signals"), /* @__PURE__ */ React.createElement("th", { className: "px-5 py-4" }, "Views"), /* @__PURE__ */ React.createElement("th", { className: "px-5 py-4" }, "Updated"), /* @__PURE__ */ React.createElement("th", { className: "px-5 py-4 text-right" }, "Actions"))), /* @__PURE__ */ React.createElement("tbody", { className: "divide-y divide-white/10 text-sm text-slate-200" }, items.map((item) => /* @__PURE__ */ React.createElement("tr", { key: item.id, className: "align-top transition hover:bg-white/[0.03]" }, /* @__PURE__ */ React.createElement("td", { className: "px-5 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex gap-4" }, /* @__PURE__ */ React.createElement("div", { className: "h-16 w-20 overflow-hidden rounded-2xl border border-white/10 bg-black/30 shrink-0" }, /* @__PURE__ */ React.createElement(PromptPreview, { item, compact: true })), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-white" }, item.title), /* @__PURE__ */ React.createElement("p", { className: "mt-1 max-w-md text-sm leading-6 text-slate-400" }, item.excerpt || "No excerpt added yet.")))), /* @__PURE__ */ React.createElement("td", { className: "px-5 py-4" }, item.category_name || "Uncategorized"), /* @__PURE__ */ React.createElement("td", { className: "px-5 py-4" }, /* @__PURE__ */ React.createElement(AccessBadge, { tier: item.access_level || "free", className: "px-3 py-1.5 text-[12px]" })), /* @__PURE__ */ React.createElement("td", { className: "px-5 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React.createElement("p", null, item.comparisons_count || 0, " comparisons"), /* @__PURE__ */ React.createElement("p", null, item.difficulty || "No difficulty"), /* @__PURE__ */ React.createElement("p", null, item.active ? "Active" : "Draft"))), /* @__PURE__ */ React.createElement("td", { className: "px-5 py-4" }, Number(item.views_count || 0).toLocaleString()), /* @__PURE__ */ React.createElement("td", { className: "px-5 py-4" }, formatDateLabel$1(item.updated_at)), /* @__PURE__ */ React.createElement("td", { className: "px-5 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex justify-end gap-2" }, item.preview_url ? /* @__PURE__ */ React.createElement(xe, { href: item.preview_url, className: "rounded-full border border-[#ffcfbf]/20 bg-[#ffcfbf]/10 px-3 py-2 text-xs font-semibold text-[#fff0ea]" }, "Preview") : null, /* @__PURE__ */ React.createElement(xe, { href: item.edit_url, className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-2 text-xs font-semibold text-white" }, "Edit"))))))))); +} +function PromptStatCard({ label, value, tone = "default" }) { + const toneClass = tone === "sky" ? "border-sky-300/20 bg-sky-300/10 text-sky-100" : tone === "emerald" ? "border-emerald-300/20 bg-emerald-300/10 text-emerald-100" : tone === "warm" ? "border-amber-300/20 bg-amber-300/10 text-amber-100" : "border-white/10 bg-black/20 text-slate-300"; + return /* @__PURE__ */ React.createElement("div", { className: `rounded-[24px] border px-5 py-4 ${toneClass}` }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] opacity-70" }, label), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white" }, value)); +} +function PromptSelect({ value, options = [], onChange }) { + return /* @__PURE__ */ React.createElement( + "select", + { + value, + onChange: (event) => onChange(event.target.value), + className: "w-full rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-sm text-white focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10" + }, + options.map((option) => /* @__PURE__ */ React.createElement("option", { key: `${option.value}-${option.label}`, value: option.value, className: "bg-slate-950 text-white" }, option.label)) + ); +} +function PromptSearchBar({ filters, onChange, onSubmit, onReset, viewMode, onViewModeChange, filterOptions = {} }) { + return /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-4 shadow-[0_18px_50px_rgba(2,6,23,0.14)]" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-4 2xl:flex-row 2xl:items-start 2xl:justify-between" }, /* @__PURE__ */ React.createElement("form", { onSubmit, className: "flex-1 space-y-4" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 xl:grid-cols-[minmax(0,1.2fr)_repeat(3,minmax(0,0.8fr))]" }, /* @__PURE__ */ React.createElement("div", { className: "relative" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-magnifying-glass absolute left-3.5 top-1/2 -translate-y-1/2 text-xs text-slate-500" }), /* @__PURE__ */ React.createElement( + "input", + { + name: "search", + value: filters.search, + onChange: (event) => onChange("search", event.target.value), + placeholder: "Search title, slug, excerpt, prompt text, or category…", + className: "w-full rounded-2xl border border-white/10 bg-black/20 py-3 pl-9 pr-4 text-sm text-white placeholder:text-slate-600 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/10" + } + )), /* @__PURE__ */ React.createElement(PromptSelect, { value: filters.category, onChange: (value) => onChange("category", value), options: filterOptions.categories }), /* @__PURE__ */ React.createElement(PromptSelect, { value: filters.access_level, onChange: (value) => onChange("access_level", value), options: filterOptions.access }), /* @__PURE__ */ React.createElement(PromptSelect, { value: filters.order, onChange: (value) => onChange("order", value), options: filterOptions.order })), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(PromptSelect, { value: filters.difficulty, onChange: (value) => onChange("difficulty", value), options: filterOptions.difficulty }), /* @__PURE__ */ React.createElement(PromptSelect, { value: filters.featured, onChange: (value) => onChange("featured", value), options: filterOptions.featured }), /* @__PURE__ */ React.createElement(PromptSelect, { value: filters.prompt_of_week, onChange: (value) => onChange("prompt_of_week", value), options: filterOptions.promptOfWeek }), /* @__PURE__ */ React.createElement(PromptSelect, { value: filters.active, onChange: (value) => onChange("active", value), options: filterOptions.active })), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-3" }, /* @__PURE__ */ React.createElement("button", { type: "submit", className: "rounded-2xl bg-sky-300/12 px-4 py-3 text-sm font-semibold text-sky-100 transition hover:bg-sky-300/16" }, "Apply filters"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: onReset, className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm font-semibold text-white/80 transition hover:bg-white/[0.08]" }, "Reset"))), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2" }, PROMPT_VIEW_OPTIONS.map((option) => { + const active = option.value === viewMode; + return /* @__PURE__ */ React.createElement( + "button", + { + key: option.value, + type: "button", + onClick: () => onViewModeChange(option.value), + className: `inline-flex items-center gap-2 rounded-full border px-4 py-2.5 text-sm font-semibold transition ${active ? "border-sky-300/25 bg-sky-300/12 text-sky-100" : "border-white/10 bg-white/[0.04] text-slate-200 hover:border-white/20 hover:bg-white/[0.07]"}` + }, + /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${option.icon} text-xs` }), + /* @__PURE__ */ React.createElement("span", null, option.label) + ); + })))); } function PromptHeroCollage({ items = [] }) { const images = items.map((item) => item?.preview_image_url).filter(Boolean).slice(0, 4); @@ -25105,10 +25612,21 @@ function renderCrudCell(column, item) { } return /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-white" }, String(item[column] ?? "")); } -function PromptIndexContent({ title, subtitle, items, createUrl }) { +function PromptIndexContent({ title, subtitle, items, createUrl, filters = {}, summary = {}, filterOptions = {} }) { + const { url } = X$1(); const promptItems = items?.data || []; - const summary = promptSummary(promptItems); + const stats = reactExports.useMemo(() => promptSummary(promptItems, summary), [promptItems, summary]); const [viewMode, setViewMode] = reactExports.useState("gallery"); + const [query, setQuery] = reactExports.useState({ + search: filters.search || "", + category: filters.category || "all", + featured: filters.featured || "all", + prompt_of_week: filters.prompt_of_week || "all", + active: filters.active || "all", + access_level: filters.access_level || "all", + difficulty: filters.difficulty || "all", + order: filters.order || "updated_desc" + }); reactExports.useEffect(() => { if (typeof window === "undefined") return; const storedView = window.localStorage.getItem(PROMPT_VIEW_STORAGE_KEY); @@ -25120,27 +25638,76 @@ function PromptIndexContent({ title, subtitle, items, createUrl }) { if (typeof window === "undefined") return; window.localStorage.setItem(PROMPT_VIEW_STORAGE_KEY, viewMode); }, [viewMode]); - return /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement("section", { className: "overflow-hidden rounded-[38px] border border-white/[0.08] bg-[radial-gradient(circle_at_top_left,rgba(56,189,248,0.16),transparent_24%),radial-gradient(circle_at_bottom_right,rgba(255,207,191,0.16),transparent_24%),linear-gradient(135deg,rgba(4,9,18,0.98),rgba(15,23,42,0.92))] shadow-[0_28px_90px_rgba(2,6,23,0.28)]" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-8 p-6 xl:grid-cols-[minmax(0,1fr)_360px] xl:items-start xl:p-10" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-[#ffcfbf]/20 bg-[#ffcfbf]/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.24em] text-[#fff0ea]" }, "Academy moderation"), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.05] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.24em] text-slate-300" }, "Prompt library")), /* @__PURE__ */ React.createElement("h2", { className: "mt-5 max-w-4xl text-4xl font-semibold tracking-[-0.055em] text-white md:text-5xl xl:text-6xl" }, title), /* @__PURE__ */ React.createElement("p", { className: "mt-5 max-w-3xl text-base leading-8 text-slate-300 md:text-lg" }, subtitle, " Review prompts in a visual-first moderation surface, jump into edits quickly, and switch between gallery, grid, or table depending on the task in front of you."), /* @__PURE__ */ React.createElement("div", { className: "mt-7 grid gap-3 sm:grid-cols-3" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 px-5 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Visual-first"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm font-semibold text-white" }, "Curate covers and prompt outputs before opening the form.")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 px-5 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Workflow-ready"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm font-semibold text-white" }, "Switch between gallery, compact cards, and scan-heavy tables.")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 px-5 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Comparison-aware"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm font-semibold text-white" }, "Spot prompts with provider notes and attached result references."))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 flex flex-wrap gap-3" }, PROMPT_VIEW_OPTIONS.map((option) => { - const active = option.value === viewMode; - return /* @__PURE__ */ React.createElement( - "button", - { - key: option.value, - type: "button", - onClick: () => setViewMode(option.value), - className: `inline-flex items-center gap-2 rounded-full border px-4 py-2 text-sm font-semibold transition ${active ? "border-sky-300/25 bg-sky-300/12 text-sky-100" : "border-white/10 bg-white/[0.04] text-slate-200 hover:border-white/20 hover:bg-white/[0.07]"}` - }, - /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${option.icon}` }), - /* @__PURE__ */ React.createElement("span", null, option.label, " view") - ); - })), /* @__PURE__ */ React.createElement("div", { className: "mt-7 flex flex-nowrap gap-3 overflow-x-auto pb-1" }, /* @__PURE__ */ React.createElement(xe, { href: createUrl, className: "inline-flex items-center gap-2 whitespace-nowrap rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-plus text-xs" }), "Create prompt"), /* @__PURE__ */ React.createElement(xe, { href: "/academy/prompts", className: "inline-flex items-center gap-2 whitespace-nowrap rounded-full border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white/85" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-book-open text-xs" }), "Open public library"), /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center gap-2 whitespace-nowrap rounded-full border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white/85" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-layer-group text-xs" }), summary.total, " prompts in view")), /* @__PURE__ */ React.createElement("div", { className: "mt-7 grid gap-3 sm:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 px-5 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Active"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white" }, summary.active)), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 px-5 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Featured"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white" }, summary.featured)), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 px-5 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Prompt of week"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white" }, summary.promptOfWeek)), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 px-5 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Comparisons"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white" }, summary.comparisons)))), /* @__PURE__ */ React.createElement("div", { className: "xl:pt-2" }, /* @__PURE__ */ React.createElement(PromptHeroCollage, { items: promptItems })))), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-400" }, "Manage Academy content below. Changes clear Academy cache automatically."), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-3" }, /* @__PURE__ */ React.createElement(xe, { href: "/academy/prompts", className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white/85" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-book-open text-xs" }), "View public library"), /* @__PURE__ */ React.createElement(xe, { href: createUrl, className: "inline-flex items-center gap-2 rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-plus text-xs" }), "Create prompt"))), promptItems.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/[0.08] bg-white/[0.03] px-6 py-12 text-center text-slate-400" }, "No prompt templates exist yet.") : viewMode === "table" ? /* @__PURE__ */ React.createElement(PromptTable, { items: promptItems }) : viewMode === "grid" ? /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2 2xl:grid-cols-3" }, promptItems.map((item) => /* @__PURE__ */ React.createElement(PromptGridCard, { key: item.id, item }))) : /* @__PURE__ */ React.createElement("div", { className: "space-y-5" }, promptItems.map((item) => /* @__PURE__ */ React.createElement(PromptGalleryCard, { key: item.id, item }))), /* @__PURE__ */ React.createElement(PaginationLinks, { links: items?.links })); + reactExports.useEffect(() => { + setQuery({ + search: filters.search || "", + category: filters.category || "all", + featured: filters.featured || "all", + prompt_of_week: filters.prompt_of_week || "all", + active: filters.active || "all", + access_level: filters.access_level || "all", + difficulty: filters.difficulty || "all", + order: filters.order || "updated_desc" + }); + }, [filters]); + const currentPath = url.split("?")[0]; + const meta = items?.meta || {}; + const hasFilters = Boolean( + (query.search || "").trim() || query.category !== "all" || query.featured !== "all" || query.prompt_of_week !== "all" || query.active !== "all" || query.access_level !== "all" || query.difficulty !== "all" || query.order !== "updated_desc" + ); + const applyQuery = (nextQuery) => { + const payload = {}; + if ((nextQuery.search || "").trim()) payload.search = nextQuery.search.trim(); + if (nextQuery.category && nextQuery.category !== "all") payload.category = nextQuery.category; + if (nextQuery.featured && nextQuery.featured !== "all") payload.featured = nextQuery.featured; + if (nextQuery.prompt_of_week && nextQuery.prompt_of_week !== "all") payload.prompt_of_week = nextQuery.prompt_of_week; + if (nextQuery.active && nextQuery.active !== "all") payload.active = nextQuery.active; + if (nextQuery.access_level && nextQuery.access_level !== "all") payload.access_level = nextQuery.access_level; + if (nextQuery.difficulty && nextQuery.difficulty !== "all") payload.difficulty = nextQuery.difficulty; + if (nextQuery.order && nextQuery.order !== "updated_desc") payload.order = nextQuery.order; + At.get(currentPath, payload, { preserveScroll: true, preserveState: true, replace: true }); + }; + const handleFilterChange = (key, value) => { + setQuery((current) => ({ ...current, [key]: value })); + }; + const handleSubmit = (event) => { + event.preventDefault(); + applyQuery(query); + }; + const handleReset = () => { + const nextQuery = { + search: "", + category: "all", + featured: "all", + prompt_of_week: "all", + active: "all", + access_level: "all", + difficulty: "all", + order: "updated_desc" + }; + setQuery(nextQuery); + At.get(currentPath, {}, { preserveScroll: true, preserveState: true, replace: true }); + }; + return /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement("section", { className: "overflow-hidden rounded-[38px] border border-white/[0.08] bg-[radial-gradient(circle_at_top_left,rgba(56,189,248,0.16),transparent_24%),radial-gradient(circle_at_bottom_right,rgba(255,207,191,0.16),transparent_24%),linear-gradient(135deg,rgba(4,9,18,0.98),rgba(15,23,42,0.92))] shadow-[0_28px_90px_rgba(2,6,23,0.28)]" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-8 p-6 xl:grid-cols-[minmax(0,1fr)_360px] xl:items-start xl:p-10" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-[#ffcfbf]/20 bg-[#ffcfbf]/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.24em] text-[#fff0ea]" }, "Academy moderation"), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.05] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.24em] text-slate-300" }, "Prompt library")), /* @__PURE__ */ React.createElement("h2", { className: "mt-5 max-w-4xl text-4xl font-semibold tracking-[-0.055em] text-white md:text-5xl xl:text-6xl" }, title), /* @__PURE__ */ React.createElement("p", { className: "mt-5 max-w-3xl text-base leading-8 text-slate-300 md:text-lg" }, subtitle, " Review prompts in a visual-first moderation surface, jump into edits quickly, and switch between gallery, grid, or table depending on the task in front of you."), /* @__PURE__ */ React.createElement("div", { className: "mt-7 grid gap-3 sm:grid-cols-3" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 px-5 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Visual-first"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm font-semibold text-white" }, "Curate covers and prompt outputs before opening the form.")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 px-5 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Workflow-ready"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm font-semibold text-white" }, "Switch between gallery, compact cards, and scan-heavy tables.")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 px-5 py-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Comparison-aware"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm font-semibold text-white" }, "Spot prompts with provider notes and attached result references."))), /* @__PURE__ */ React.createElement("div", { className: "mt-7 flex flex-nowrap gap-3 overflow-x-auto pb-1" }, /* @__PURE__ */ React.createElement(xe, { href: createUrl, className: "inline-flex items-center gap-2 whitespace-nowrap rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-plus text-xs" }), "Create prompt"), /* @__PURE__ */ React.createElement(xe, { href: "/academy/prompts", className: "inline-flex items-center gap-2 whitespace-nowrap rounded-full border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white/85" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-book-open text-xs" }), "Open public library"), /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center gap-2 whitespace-nowrap rounded-full border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white/85" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-layer-group text-xs" }), stats.total, " prompts in view")), /* @__PURE__ */ React.createElement("div", { className: "mt-7 grid gap-3 sm:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(PromptStatCard, { label: "Active", value: stats.active, tone: "emerald" }), /* @__PURE__ */ React.createElement(PromptStatCard, { label: "Featured", value: stats.featured, tone: "sky" }), /* @__PURE__ */ React.createElement(PromptStatCard, { label: "Prompt of week", value: stats.promptOfWeek, tone: "warm" }), /* @__PURE__ */ React.createElement(PromptStatCard, { label: "Views on page", value: promptItems.reduce((count, item) => count + Number(item.views_count || 0), 0).toLocaleString() })), /* @__PURE__ */ React.createElement("div", { className: "mt-3 grid gap-3 sm:grid-cols-3" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 px-5 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Free access"), /* @__PURE__ */ React.createElement(AccessBadge, { tier: "free" })), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white" }, stats.access.free)), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 px-5 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Creator access"), /* @__PURE__ */ React.createElement(AccessBadge, { tier: "creator" })), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white" }, stats.access.creator)), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 px-5 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("p", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Pro access"), /* @__PURE__ */ React.createElement(AccessBadge, { tier: "pro" })), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white" }, stats.access.pro)))), /* @__PURE__ */ React.createElement("div", { className: "xl:pt-2" }, /* @__PURE__ */ React.createElement(PromptHeroCollage, { items: promptItems })))), /* @__PURE__ */ React.createElement( + PromptSearchBar, + { + filters: query, + onChange: handleFilterChange, + onSubmit: handleSubmit, + onReset: handleReset, + viewMode, + onViewModeChange: setViewMode, + filterOptions + } + ), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-400" }, meta.total ? /* @__PURE__ */ React.createElement(React.Fragment, null, "Showing ", meta.from || 0, "-", meta.to || 0, " of ", meta.total, " prompts", hasFilters ? /* @__PURE__ */ React.createElement("span", { className: "ml-2 text-sky-200" }, "with active search or filters") : null) : "Manage Academy content below. Changes clear Academy cache automatically."), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-3" }, /* @__PURE__ */ React.createElement(xe, { href: "/academy/prompts", className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white/85" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-book-open text-xs" }), "View public library"), /* @__PURE__ */ React.createElement(xe, { href: createUrl, className: "inline-flex items-center gap-2 rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-plus text-xs" }), "Create prompt"))), promptItems.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/[0.08] bg-white/[0.03] px-6 py-12 text-center text-slate-400" }, hasFilters ? /* @__PURE__ */ React.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React.createElement("p", { className: "text-lg font-semibold text-white" }, "No prompt templates matched these filters."), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: handleReset, className: "rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white" }, "Reset filters")) : "No prompt templates exist yet.") : viewMode === "table" ? /* @__PURE__ */ React.createElement(PromptTable, { items: promptItems }) : viewMode === "grid" ? /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2 2xl:grid-cols-3" }, promptItems.map((item) => /* @__PURE__ */ React.createElement(PromptGridCard, { key: item.id, item }))) : /* @__PURE__ */ React.createElement("div", { className: "space-y-5" }, promptItems.map((item) => /* @__PURE__ */ React.createElement(PromptGalleryCard, { key: item.id, item }))), /* @__PURE__ */ React.createElement(PaginationLinks, { links: items?.links })); } function AcademyCrudIndex({ title, subtitle, items, columns, createUrl }) { const flash = X$1().props.flash || {}; const resource = X$1().props.resource; const filters = X$1().props.filters || {}; const summary = X$1().props.summary || {}; - return /* @__PURE__ */ React.createElement(AdminLayout, { title, subtitle }, /* @__PURE__ */ React.createElement(Se$1, { title: `Admin · ${title}` }), flash.success ? /* @__PURE__ */ React.createElement("div", { className: "mb-6 rounded-2xl border border-emerald-300/20 bg-emerald-300/10 px-4 py-3 text-sm text-emerald-100" }, flash.success) : null, resource === "courses" ? /* @__PURE__ */ React.createElement(CourseIndexContent, { title, subtitle, items, createUrl, filters, summary }) : resource === "prompts" ? /* @__PURE__ */ React.createElement(PromptIndexContent, { title, subtitle, items, createUrl }) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "mb-6 flex items-center justify-between gap-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-400" }, "Manage Academy content below. Changes clear Academy cache automatically."), /* @__PURE__ */ React.createElement(xe, { href: createUrl, className: "rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100" }, "Create record")), (items?.data || []).length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/[0.08] bg-white/[0.03] px-6 py-12 text-center text-slate-400" }, "No records exist yet.") : /* @__PURE__ */ React.createElement("div", { className: "space-y-4" }, items.data.map((item) => /* @__PURE__ */ React.createElement("div", { key: item.id, className: "rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 lg:grid-cols-[minmax(0,1fr)_auto] lg:items-center" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-2 sm:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-6" }, columns.map((column) => /* @__PURE__ */ React.createElement("div", { key: column }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, column.replaceAll("_", " ")), /* @__PURE__ */ React.createElement("div", { className: "mt-1" }, renderCrudCell(column, item))))), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-3 lg:justify-end" }, item.builder_url ? /* @__PURE__ */ React.createElement(xe, { href: item.builder_url, className: "rounded-full border border-amber-300/20 bg-amber-300/10 px-4 py-2 text-sm font-semibold text-amber-100" }, "Builder") : null, /* @__PURE__ */ React.createElement(xe, { href: item.edit_url, className: "rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white" }, "Edit"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => { + const filterOptions = X$1().props.filterOptions || {}; + return /* @__PURE__ */ React.createElement(AdminLayout, { title, subtitle }, /* @__PURE__ */ React.createElement(Se$1, { title: `Admin · ${title}` }), flash.success ? /* @__PURE__ */ React.createElement("div", { className: "mb-6 rounded-2xl border border-emerald-300/20 bg-emerald-300/10 px-4 py-3 text-sm text-emerald-100" }, flash.success) : null, resource === "courses" ? /* @__PURE__ */ React.createElement(CourseIndexContent, { title, subtitle, items, createUrl, filters, summary }) : resource === "prompts" ? /* @__PURE__ */ React.createElement(PromptIndexContent, { title, subtitle, items, createUrl, filters, summary, filterOptions }) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "mb-6 flex items-center justify-between gap-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-400" }, "Manage Academy content below. Changes clear Academy cache automatically."), /* @__PURE__ */ React.createElement(xe, { href: createUrl, className: "rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100" }, "Create record")), (items?.data || []).length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/[0.08] bg-white/[0.03] px-6 py-12 text-center text-slate-400" }, "No records exist yet.") : /* @__PURE__ */ React.createElement("div", { className: "space-y-4" }, items.data.map((item) => /* @__PURE__ */ React.createElement("div", { key: item.id, className: "rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 lg:grid-cols-[minmax(0,1fr)_auto] lg:items-center" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-2 sm:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-6" }, columns.map((column) => /* @__PURE__ */ React.createElement("div", { key: column }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, column.replaceAll("_", " ")), /* @__PURE__ */ React.createElement("div", { className: "mt-1" }, renderCrudCell(column, item))))), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-3 lg:justify-end" }, item.builder_url ? /* @__PURE__ */ React.createElement(xe, { href: item.builder_url, className: "rounded-full border border-amber-300/20 bg-amber-300/10 px-4 py-2 text-sm font-semibold text-amber-100" }, "Builder") : null, /* @__PURE__ */ React.createElement(xe, { href: item.edit_url, className: "rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white" }, "Edit"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => { if (!window.confirm("Delete this record?")) return; At.delete(item.destroy_url, { preserveScroll: true }); }, className: "rounded-full border border-rose-300/20 bg-rose-300/10 px-4 py-2 text-sm font-semibold text-rose-100" }, "Delete")))))))); @@ -25149,11 +25716,11 @@ const __vite_glob_0_21 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de __proto__: null, default: AcademyCrudIndex }, Symbol.toStringTag, { value: "Module" })); -function StatCard$c({ label, value }) { +function StatCard$f({ label, value }) { return /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/[0.08] bg-white/[0.04] p-5" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, label), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-3xl font-bold text-white" }, value.toLocaleString())); } function AcademyDashboard({ stats, links }) { - return /* @__PURE__ */ React.createElement(AdminLayout, { title: "Academy Dashboard", subtitle: "Overview of Academy content, challenge activity, and live Academy subscription health." }, /* @__PURE__ */ React.createElement(Se$1, { title: "Admin · Academy Dashboard" }), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(StatCard$c, { label: "Courses", value: stats.courses }), /* @__PURE__ */ React.createElement(StatCard$c, { label: "Lessons", value: stats.lessons }), /* @__PURE__ */ React.createElement(StatCard$c, { label: "Prompts", value: stats.prompts }), /* @__PURE__ */ React.createElement(StatCard$c, { label: "Prompt Packs", value: stats.packs }), /* @__PURE__ */ React.createElement(StatCard$c, { label: "Challenges", value: stats.challenges }), /* @__PURE__ */ React.createElement(StatCard$c, { label: "Submissions", value: stats.submissions }), /* @__PURE__ */ React.createElement(StatCard$c, { label: "Badges", value: stats.badges }), /* @__PURE__ */ React.createElement(StatCard$c, { label: "Active Subscribers", value: stats.active_subscribers || 0 }), /* @__PURE__ */ React.createElement(StatCard$c, { label: "Creator Subscribers", value: stats.creator_subscribers }), /* @__PURE__ */ React.createElement(StatCard$c, { label: "Pro Subscribers", value: stats.pro_subscribers }), /* @__PURE__ */ React.createElement(StatCard$c, { label: "Grace Period", value: stats.grace_period_subscribers || 0 })), /* @__PURE__ */ React.createElement("div", { className: "mt-8 rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Modules"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 md:grid-cols-2 xl:grid-cols-3" }, Object.entries(links).map(([key, href]) => /* @__PURE__ */ React.createElement(xe, { key, href, className: "rounded-2xl border border-white/[0.08] bg-black/20 px-4 py-4 text-sm font-semibold text-white transition hover:border-white/15 hover:bg-white/[0.05]" }, key.replaceAll("_", " ")))))); + return /* @__PURE__ */ React.createElement(AdminLayout, { title: "Academy Dashboard", subtitle: "Overview of Academy content, challenge activity, and live Academy subscription health." }, /* @__PURE__ */ React.createElement(Se$1, { title: "Admin · Academy Dashboard" }), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(StatCard$f, { label: "Courses", value: stats.courses }), /* @__PURE__ */ React.createElement(StatCard$f, { label: "Lessons", value: stats.lessons }), /* @__PURE__ */ React.createElement(StatCard$f, { label: "Prompts", value: stats.prompts }), /* @__PURE__ */ React.createElement(StatCard$f, { label: "Prompt Packs", value: stats.packs }), /* @__PURE__ */ React.createElement(StatCard$f, { label: "Challenges", value: stats.challenges }), /* @__PURE__ */ React.createElement(StatCard$f, { label: "Submissions", value: stats.submissions }), /* @__PURE__ */ React.createElement(StatCard$f, { label: "Badges", value: stats.badges }), /* @__PURE__ */ React.createElement(StatCard$f, { label: "Active Subscribers", value: stats.active_subscribers || 0 }), /* @__PURE__ */ React.createElement(StatCard$f, { label: "Creator Subscribers", value: stats.creator_subscribers }), /* @__PURE__ */ React.createElement(StatCard$f, { label: "Pro Subscribers", value: stats.pro_subscribers }), /* @__PURE__ */ React.createElement(StatCard$f, { label: "Grace Period", value: stats.grace_period_subscribers || 0 })), /* @__PURE__ */ React.createElement("div", { className: "mt-8 rounded-[28px] border border-white/[0.08] bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Modules"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 md:grid-cols-2 xl:grid-cols-3" }, Object.entries(links).map(([key, href]) => /* @__PURE__ */ React.createElement(xe, { key, href, className: "rounded-2xl border border-white/[0.08] bg-black/20 px-4 py-4 text-sm font-semibold text-white transition hover:border-white/15 hover:bg-white/[0.05]" }, key.replaceAll("_", " ")))))); } const __vite_glob_0_22 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, @@ -25171,7 +25738,7 @@ function getCsrfToken$h() { if (typeof document === "undefined") return ""; return document.querySelector('meta[name="csrf-token"]')?.getAttribute("content") || ""; } -async function requestJson$q(url, { method = "POST", body: body2 } = {}) { +async function requestJson$r(url, { method = "POST", body: body2 } = {}) { const response = await fetch(url, { method, credentials: "same-origin", @@ -25189,7 +25756,7 @@ async function requestJson$q(url, { method = "POST", body: body2 } = {}) { } return payload; } -function formatDateTime$5(value) { +function formatDateTime$8(value) { if (!value) return "—"; const date = new Date(value); if (Number.isNaN(date.getTime())) return "—"; @@ -25208,7 +25775,7 @@ function Badge$3({ children, tone = "slate" }) { }; return /* @__PURE__ */ React.createElement("span", { className: `inline-flex items-center rounded-full border px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] ${tones2[tone] || tones2.slate}` }, children); } -function StatCard$b({ label, value, tone = "sky" }) { +function StatCard$e({ label, value, tone = "sky" }) { const tones2 = { sky: "border-sky-300/15 bg-sky-400/10 text-sky-100", amber: "border-amber-300/15 bg-amber-400/10 text-amber-100", @@ -25275,7 +25842,7 @@ function AiBiographyAdmin() { setBusyKey(actionKey); setError(""); try { - const payload = await requestJson$q(url); + const payload = await requestJson$r(url); setNotice(payload.message || "Action completed."); At.reload({ only: ["records", "stats", "filters"], @@ -25287,7 +25854,7 @@ function AiBiographyAdmin() { setBusyKey(""); } } - return /* @__PURE__ */ React.createElement("div", { className: "w-full pb-16 pt-8" }, /* @__PURE__ */ React.createElement(Se$1, { title: "AI Biography Review" }), /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(125,211,252,0.2),transparent_32%),linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.9))] p-6 shadow-[0_24px_70px_rgba(2,6,23,0.32)]" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.28em] text-sky-200/80" }, "Moderator surface"), /* @__PURE__ */ React.createElement("h1", { className: "mt-3 text-3xl font-semibold tracking-[-0.04em] text-white" }, "AI biography review"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 max-w-3xl text-sm leading-relaxed text-slate-300" }, "Browse active biographies and historical generations, inspect review flags and failures, and rebuild a creator biography directly from cPad.")), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-3 text-xs uppercase tracking-[0.16em] text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.05] px-4 py-2" }, "Page ", records.current_page || 1, " / ", records.last_page || 1), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.05] px-4 py-2" }, Number(records.total || 0).toLocaleString(), " records"))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 md:grid-cols-2 xl:grid-cols-3" }, /* @__PURE__ */ React.createElement(StatCard$b, { label: "Total records", value: stats.total_records, tone: "sky" }), /* @__PURE__ */ React.createElement(StatCard$b, { label: "Active", value: stats.active_records, tone: "emerald" }), /* @__PURE__ */ React.createElement(StatCard$b, { label: "Needs review", value: stats.needs_review, tone: "amber" }), /* @__PURE__ */ React.createElement(StatCard$b, { label: "Hidden active", value: stats.hidden_active, tone: "slate" }), /* @__PURE__ */ React.createElement(StatCard$b, { label: "Failed", value: stats.failed, tone: "rose" }), /* @__PURE__ */ React.createElement(StatCard$b, { label: "User edited", value: stats.user_edited_active, tone: "sky" })), /* @__PURE__ */ React.createElement("form", { onSubmit: applyFilters, className: "mt-6 grid gap-3 lg:grid-cols-[2fr_repeat(5,minmax(0,1fr))]" }, /* @__PURE__ */ React.createElement("label", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400" }, "Search creator"), /* @__PURE__ */ React.createElement( + return /* @__PURE__ */ React.createElement("div", { className: "w-full pb-16 pt-8" }, /* @__PURE__ */ React.createElement(Se$1, { title: "AI Biography Review" }), /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(125,211,252,0.2),transparent_32%),linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.9))] p-6 shadow-[0_24px_70px_rgba(2,6,23,0.32)]" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.28em] text-sky-200/80" }, "Moderator surface"), /* @__PURE__ */ React.createElement("h1", { className: "mt-3 text-3xl font-semibold tracking-[-0.04em] text-white" }, "AI biography review"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 max-w-3xl text-sm leading-relaxed text-slate-300" }, "Browse active biographies and historical generations, inspect review flags and failures, and rebuild a creator biography directly from cPad.")), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-3 text-xs uppercase tracking-[0.16em] text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.05] px-4 py-2" }, "Page ", records.current_page || 1, " / ", records.last_page || 1), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.05] px-4 py-2" }, Number(records.total || 0).toLocaleString(), " records"))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 md:grid-cols-2 xl:grid-cols-3" }, /* @__PURE__ */ React.createElement(StatCard$e, { label: "Total records", value: stats.total_records, tone: "sky" }), /* @__PURE__ */ React.createElement(StatCard$e, { label: "Active", value: stats.active_records, tone: "emerald" }), /* @__PURE__ */ React.createElement(StatCard$e, { label: "Needs review", value: stats.needs_review, tone: "amber" }), /* @__PURE__ */ React.createElement(StatCard$e, { label: "Hidden active", value: stats.hidden_active, tone: "slate" }), /* @__PURE__ */ React.createElement(StatCard$e, { label: "Failed", value: stats.failed, tone: "rose" }), /* @__PURE__ */ React.createElement(StatCard$e, { label: "User edited", value: stats.user_edited_active, tone: "sky" })), /* @__PURE__ */ React.createElement("form", { onSubmit: applyFilters, className: "mt-6 grid gap-3 lg:grid-cols-[2fr_repeat(5,minmax(0,1fr))]" }, /* @__PURE__ */ React.createElement("label", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400" }, "Search creator"), /* @__PURE__ */ React.createElement( "input", { value: filters.q || "", @@ -25319,7 +25886,7 @@ function AiBiographyAdmin() { }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-rotate text-[10px]" }), busyKey === rebuildKey ? "Rebuilding…" : "Rebuild" - ))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-4 md:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Prompt"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-sm text-slate-200" }, record.prompt_version || "—")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Model"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-sm text-slate-200" }, record.model || "—")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Generated"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-sm text-slate-200" }, formatDateTime$5(record.generated_at))), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Last attempted"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-sm text-slate-200" }, formatDateTime$5(record.last_attempted_at)))), record.last_error_code || record.last_error_reason ? /* @__PURE__ */ React.createElement("div", { className: "mt-4 rounded-2xl border border-rose-300/20 bg-rose-400/10 px-4 py-3 text-sm leading-relaxed text-rose-100" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-rose-100/80" }, "Last error"), /* @__PURE__ */ React.createElement("div", { className: "mt-2" }, record.last_error_code || "generation_failed", record.last_error_reason ? ` • ${record.last_error_reason}` : "")) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-4 xl:grid-cols-[minmax(0,1fr)_320px]" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400" }, "Biography text"), /* @__PURE__ */ React.createElement("div", { className: "mt-3 max-h-[320px] overflow-y-auto whitespace-pre-wrap rounded-2xl border border-white/10 bg-slate-950/60 px-4 py-4 text-sm leading-relaxed text-slate-100" }, record.text || "No biography text stored for this record.")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400" }, "Review actions"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3" }, /* @__PURE__ */ React.createElement( + ))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-4 md:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Prompt"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-sm text-slate-200" }, record.prompt_version || "—")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Model"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-sm text-slate-200" }, record.model || "—")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Generated"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-sm text-slate-200" }, formatDateTime$8(record.generated_at))), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Last attempted"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-sm text-slate-200" }, formatDateTime$8(record.last_attempted_at)))), record.last_error_code || record.last_error_reason ? /* @__PURE__ */ React.createElement("div", { className: "mt-4 rounded-2xl border border-rose-300/20 bg-rose-400/10 px-4 py-3 text-sm leading-relaxed text-rose-100" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-rose-100/80" }, "Last error"), /* @__PURE__ */ React.createElement("div", { className: "mt-2" }, record.last_error_code || "generation_failed", record.last_error_reason ? ` • ${record.last_error_reason}` : "")) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-4 xl:grid-cols-[minmax(0,1fr)_320px]" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400" }, "Biography text"), /* @__PURE__ */ React.createElement("div", { className: "mt-3 max-h-[320px] overflow-y-auto whitespace-pre-wrap rounded-2xl border border-white/10 bg-slate-950/60 px-4 py-4 text-sm leading-relaxed text-slate-100" }, record.text || "No biography text stored for this record.")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400" }, "Review actions"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3" }, /* @__PURE__ */ React.createElement( "button", { type: "button", @@ -25349,7 +25916,7 @@ function AiBiographyAdmin() { }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${record.is_hidden ? "fa-eye" : "fa-eye-slash"} text-[10px]` }), busyKey === visibilityKey ? "Saving…" : record.is_hidden ? "Show publicly" : "Hide publicly" - )), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-2 text-xs leading-relaxed text-slate-300" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-slate-100" }, "Approved:"), " ", formatDateTime$5(record.approved_at)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-slate-100" }, "Created:"), " ", formatDateTime$5(record.created_at)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-slate-100" }, "Updated:"), " ", formatDateTime$5(record.updated_at)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-slate-100" }, "Source hash:"), " ", record.source_hash || "—"))))); + )), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-2 text-xs leading-relaxed text-slate-300" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-slate-100" }, "Approved:"), " ", formatDateTime$8(record.approved_at)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-slate-100" }, "Created:"), " ", formatDateTime$8(record.created_at)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-slate-100" }, "Updated:"), " ", formatDateTime$8(record.updated_at)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-slate-100" }, "Source hash:"), " ", record.source_hash || "—"))))); })), records.prev_page_url || records.next_page_url ? /* @__PURE__ */ React.createElement("div", { className: "mt-8 flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, records.prev_page_url ? /* @__PURE__ */ React.createElement(xe, { href: records.prev_page_url, preserveScroll: true, className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.09]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-left text-[10px]" }), "Previous") : null), /* @__PURE__ */ React.createElement("div", { className: "text-xs uppercase tracking-[0.16em] text-slate-400" }, "Showing page ", records.current_page || 1, " of ", records.last_page || 1), /* @__PURE__ */ React.createElement("div", null, records.next_page_url ? /* @__PURE__ */ React.createElement(xe, { href: records.next_page_url, preserveScroll: true, className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.09]" }, "Next", /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-right text-[10px]" })) : null)) : null); } const __vite_glob_0_94 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ @@ -25485,7 +26052,7 @@ const __vite_glob_0_27 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de __proto__: null, default: AuthAudit }, Symbol.toStringTag, { value: "Module" })); -function formatDateTime$4(value) { +function formatDateTime$7(value) { if (!value) return "—"; try { return new Intl.DateTimeFormat(void 0, { @@ -25499,7 +26066,7 @@ function formatDateTime$4(value) { return String(value); } } -function StatCard$a({ icon, label, value, tone = "sky" }) { +function StatCard$d({ icon, label, value, tone = "sky" }) { const tones2 = { sky: "border-sky-400/20 bg-sky-400/10 text-sky-200", rose: "border-rose-400/20 bg-rose-400/10 text-rose-200", @@ -25532,7 +26099,7 @@ function DailyActivity({ selectedDate, summary, queues, sections }) { onChange: onDateChange, className: "rounded-2xl border border-white/10 bg-slate-950/70 px-4 py-3 text-sm text-white outline-none transition focus:border-rose-400/50" } - )))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 sm:grid-cols-2 xl:grid-cols-3" }, /* @__PURE__ */ React.createElement(StatCard$a, { icon: "fa-solid fa-user-plus", label: "New Users", value: summary.new_users, tone: "sky" }), /* @__PURE__ */ React.createElement(StatCard$a, { icon: "fa-solid fa-images", label: "New Artworks", value: summary.new_artworks, tone: "rose" }), /* @__PURE__ */ React.createElement(StatCard$a, { icon: "fa-solid fa-feather-pointed", label: "New Stories", value: summary.new_stories, tone: "amber" }), /* @__PURE__ */ React.createElement(StatCard$a, { icon: "fa-solid fa-cloud-arrow-up", label: "Upload Events", value: summary.upload_events, tone: "emerald" }), /* @__PURE__ */ React.createElement(StatCard$a, { icon: "fa-solid fa-flag", label: "Report Events", value: summary.report_events, tone: "rose" }), /* @__PURE__ */ React.createElement(StatCard$a, { icon: "fa-solid fa-fingerprint", label: "Auth Events", value: summary.auth_events, tone: "sky" })), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 lg:grid-cols-3" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-500" }, "Queues right now"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between rounded-2xl border border-white/8 bg-black/10 px-4 py-3" }, /* @__PURE__ */ React.createElement("span", null, "Pending uploads"), /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, queues.pending_uploads)), /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between rounded-2xl border border-white/8 bg-black/10 px-4 py-3" }, /* @__PURE__ */ React.createElement("span", null, "Open reports"), /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, queues.open_reports)), /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between rounded-2xl border border-white/8 bg-black/10 px-4 py-3" }, /* @__PURE__ */ React.createElement("span", null, "Pending username requests"), /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, queues.pending_username_requests)))), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] p-5 lg:col-span-2" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-500" }, "Moderation throughput on this day"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 sm:grid-cols-3" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/8 bg-black/10 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-xs uppercase tracking-[0.18em] text-slate-500" }, "Moderated uploads"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-2xl font-bold text-white" }, summary.moderated_uploads)), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/8 bg-black/10 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-xs uppercase tracking-[0.18em] text-slate-500" }, "Moderated reports"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-2xl font-bold text-white" }, summary.moderated_reports)), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/8 bg-black/10 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-xs uppercase tracking-[0.18em] text-slate-500" }, "Username events"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-2xl font-bold text-white" }, summary.username_events))))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 space-y-6" }, /* @__PURE__ */ React.createElement(SectionCard$3, { title: "Uploads", subtitle: "New uploads and same-day moderation decisions.", actionHref: "/moderation/uploads", actionLabel: "Open uploads" }, /* @__PURE__ */ React.createElement( + )))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 sm:grid-cols-2 xl:grid-cols-3" }, /* @__PURE__ */ React.createElement(StatCard$d, { icon: "fa-solid fa-user-plus", label: "New Users", value: summary.new_users, tone: "sky" }), /* @__PURE__ */ React.createElement(StatCard$d, { icon: "fa-solid fa-images", label: "New Artworks", value: summary.new_artworks, tone: "rose" }), /* @__PURE__ */ React.createElement(StatCard$d, { icon: "fa-solid fa-feather-pointed", label: "New Stories", value: summary.new_stories, tone: "amber" }), /* @__PURE__ */ React.createElement(StatCard$d, { icon: "fa-solid fa-cloud-arrow-up", label: "Upload Events", value: summary.upload_events, tone: "emerald" }), /* @__PURE__ */ React.createElement(StatCard$d, { icon: "fa-solid fa-flag", label: "Report Events", value: summary.report_events, tone: "rose" }), /* @__PURE__ */ React.createElement(StatCard$d, { icon: "fa-solid fa-fingerprint", label: "Auth Events", value: summary.auth_events, tone: "sky" })), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 lg:grid-cols-3" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-500" }, "Queues right now"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between rounded-2xl border border-white/8 bg-black/10 px-4 py-3" }, /* @__PURE__ */ React.createElement("span", null, "Pending uploads"), /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, queues.pending_uploads)), /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between rounded-2xl border border-white/8 bg-black/10 px-4 py-3" }, /* @__PURE__ */ React.createElement("span", null, "Open reports"), /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, queues.open_reports)), /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between rounded-2xl border border-white/8 bg-black/10 px-4 py-3" }, /* @__PURE__ */ React.createElement("span", null, "Pending username requests"), /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, queues.pending_username_requests)))), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] p-5 lg:col-span-2" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-500" }, "Moderation throughput on this day"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 sm:grid-cols-3" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/8 bg-black/10 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-xs uppercase tracking-[0.18em] text-slate-500" }, "Moderated uploads"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-2xl font-bold text-white" }, summary.moderated_uploads)), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/8 bg-black/10 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-xs uppercase tracking-[0.18em] text-slate-500" }, "Moderated reports"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-2xl font-bold text-white" }, summary.moderated_reports)), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/8 bg-black/10 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-xs uppercase tracking-[0.18em] text-slate-500" }, "Username events"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-2xl font-bold text-white" }, summary.username_events))))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 space-y-6" }, /* @__PURE__ */ React.createElement(SectionCard$3, { title: "Uploads", subtitle: "New uploads and same-day moderation decisions.", actionHref: "/moderation/uploads", actionLabel: "Open uploads" }, /* @__PURE__ */ React.createElement( DataTable, { emptyLabel: "No upload activity on this day.", @@ -25540,8 +26107,8 @@ function DailyActivity({ selectedDate, summary, queues, sections }) { columns: [ { key: "title", label: "Upload", render: (row) => /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "font-semibold text-white" }, row.title), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs uppercase tracking-[0.16em] text-slate-500" }, row.type, " · ", row.status, " · ", row.processing_state)) }, { key: "moderation_status", label: "Moderation", render: (row) => /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", null, row.moderation_status), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs text-slate-500" }, row.moderation_note || "No note")) }, - { key: "created_at", label: "Created", render: (row) => formatDateTime$4(row.created_at) }, - { key: "moderated_at", label: "Moderated", render: (row) => formatDateTime$4(row.moderated_at) } + { key: "created_at", label: "Created", render: (row) => formatDateTime$7(row.created_at) }, + { key: "moderated_at", label: "Moderated", render: (row) => formatDateTime$7(row.moderated_at) } ] } )), /* @__PURE__ */ React.createElement(SectionCard$3, { title: "Reports", subtitle: "User reports created or reviewed during the selected day." }, /* @__PURE__ */ React.createElement( @@ -25553,7 +26120,7 @@ function DailyActivity({ selectedDate, summary, queues, sections }) { { key: "reason", label: "Report", render: (row) => /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "font-semibold text-white" }, row.reason), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs uppercase tracking-[0.16em] text-slate-500" }, row.status, " · ", row.target_type, " #", row.target_id)) }, { key: "reporter", label: "Reporter", render: (row) => row.reporter ? `@${row.reporter.username}` : "—" }, { key: "target", label: "Target", render: (row) => row.target?.title || row.target?.context || "Resolved via moderation target" }, - { key: "last_moderated_at", label: "Reviewed", render: (row) => /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", null, formatDateTime$4(row.last_moderated_at)), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs text-slate-500" }, row.last_moderated_by ? `@${row.last_moderated_by.username}` : "Unassigned")) } + { key: "last_moderated_at", label: "Reviewed", render: (row) => /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", null, formatDateTime$7(row.last_moderated_at)), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs text-slate-500" }, row.last_moderated_by ? `@${row.last_moderated_by.username}` : "Unassigned")) } ] } )), /* @__PURE__ */ React.createElement("div", { className: "grid gap-6 xl:grid-cols-2" }, /* @__PURE__ */ React.createElement(SectionCard$3, { title: "Username Requests", subtitle: "Requests created or reviewed on this day.", actionHref: "/moderation/usernames/moderation", actionLabel: "Open usernames" }, /* @__PURE__ */ React.createElement( @@ -25564,8 +26131,8 @@ function DailyActivity({ selectedDate, summary, queues, sections }) { columns: [ { key: "requested_username", label: "Request", render: (row) => /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "font-semibold text-white" }, row.requested_username), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs text-slate-500" }, "Current: ", row.current_username || row.current_name || "Unknown user")) }, { key: "status", label: "Status" }, - { key: "created_at", label: "Created", render: (row) => formatDateTime$4(row.created_at) }, - { key: "reviewed_at", label: "Reviewed", render: (row) => formatDateTime$4(row.reviewed_at) } + { key: "created_at", label: "Created", render: (row) => formatDateTime$7(row.created_at) }, + { key: "reviewed_at", label: "Reviewed", render: (row) => formatDateTime$7(row.reviewed_at) } ] } )), /* @__PURE__ */ React.createElement(SectionCard$3, { title: "Auth Audit", subtitle: "Authentication events for the selected day.", actionHref: "/moderation/auth-audit", actionLabel: "Open audit" }, /* @__PURE__ */ React.createElement( @@ -25577,7 +26144,7 @@ function DailyActivity({ selectedDate, summary, queues, sections }) { { key: "event_type", label: "Event", render: (row) => /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "font-semibold text-white" }, row.event_type), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs text-slate-500" }, row.status, " · ", row.ip || "No IP")) }, { key: "user", label: "User", render: (row) => row.user ? `@${row.user.username || row.user.name}` : row.identifier || "Guest" }, { key: "reason", label: "Reason", render: (row) => row.reason || "—" }, - { key: "created_at", label: "When", render: (row) => formatDateTime$4(row.created_at) } + { key: "created_at", label: "When", render: (row) => formatDateTime$7(row.created_at) } ] } ))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-6 xl:grid-cols-3" }, /* @__PURE__ */ React.createElement(SectionCard$3, { title: "Users", subtitle: "Accounts created on this day.", actionHref: "/moderation/users", actionLabel: "Open users" }, /* @__PURE__ */ React.createElement( @@ -25588,7 +26155,7 @@ function DailyActivity({ selectedDate, summary, queues, sections }) { columns: [ { key: "username", label: "User", render: (row) => /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "font-semibold text-white" }, row.username ? `@${row.username}` : row.name), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs text-slate-500" }, row.email)) }, { key: "role", label: "Role" }, - { key: "created_at", label: "Joined", render: (row) => formatDateTime$4(row.created_at) } + { key: "created_at", label: "Joined", render: (row) => formatDateTime$7(row.created_at) } ] } )), /* @__PURE__ */ React.createElement(SectionCard$3, { title: "Artworks", subtitle: "Artwork records created on this day.", actionHref: "/moderation/artworks", actionLabel: "Open artworks" }, /* @__PURE__ */ React.createElement( @@ -25599,7 +26166,7 @@ function DailyActivity({ selectedDate, summary, queues, sections }) { columns: [ { key: "title", label: "Artwork", render: (row) => /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "font-semibold text-white" }, row.title), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs text-slate-500" }, row.user?.username ? `@${row.user.username}` : "Unknown artist")) }, { key: "status", label: "Status" }, - { key: "created_at", label: "Created", render: (row) => formatDateTime$4(row.created_at) } + { key: "created_at", label: "Created", render: (row) => formatDateTime$7(row.created_at) } ] } )), /* @__PURE__ */ React.createElement(SectionCard$3, { title: "Stories", subtitle: "Creator stories created on this day.", actionHref: "/moderation/stories", actionLabel: "Open stories" }, /* @__PURE__ */ React.createElement( @@ -25610,7 +26177,7 @@ function DailyActivity({ selectedDate, summary, queues, sections }) { columns: [ { key: "title", label: "Story", render: (row) => /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "font-semibold text-white" }, row.title), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs text-slate-500" }, row.creator?.username ? `@${row.creator.username}` : "Unknown creator")) }, { key: "status", label: "Status" }, - { key: "created_at", label: "Created", render: (row) => formatDateTime$4(row.created_at) } + { key: "created_at", label: "Created", render: (row) => formatDateTime$7(row.created_at) } ] } ))))); @@ -25619,7 +26186,7 @@ const __vite_glob_0_28 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de __proto__: null, default: DailyActivity }, Symbol.toStringTag, { value: "Module" })); -function StatCard$9({ icon, label, value, color: color2 = "sky" }) { +function StatCard$c({ icon, label, value, color: color2 = "sky" }) { const colors = { sky: "from-sky-500/20 to-sky-500/5 border-sky-500/20 text-sky-400", rose: "from-rose-500/20 to-rose-500/5 border-rose-500/20 text-rose-400", @@ -25629,7 +26196,7 @@ function StatCard$9({ icon, label, value, color: color2 = "sky" }) { return /* @__PURE__ */ React.createElement("div", { className: `rounded-2xl border bg-gradient-to-br p-6 ${colors[color2]}` }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-xs font-semibold uppercase tracking-wider text-slate-400" }, label), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-3xl font-bold text-white" }, value.toLocaleString())), /* @__PURE__ */ React.createElement("div", { className: `flex h-12 w-12 items-center justify-center rounded-xl bg-white/5` }, /* @__PURE__ */ React.createElement("i", { className: `${icon} text-xl ${colors[color2].split(" ").at(-1)}` })))); } function Dashboard({ stats }) { - return /* @__PURE__ */ React.createElement(AdminLayout, { title: "Dashboard", subtitle: "Overview of your Skinbase platform" }, /* @__PURE__ */ React.createElement(Se$1, { title: "Admin Dashboard" }), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-4" }, /* @__PURE__ */ React.createElement(StatCard$9, { icon: "fa-solid fa-users", label: "Total Users", value: stats.total_users, color: "sky" }), /* @__PURE__ */ React.createElement(StatCard$9, { icon: "fa-solid fa-user-plus", label: "New Today", value: stats.new_users_today, color: "violet" }), /* @__PURE__ */ React.createElement(StatCard$9, { icon: "fa-solid fa-shield-halved", label: "Staff Members", value: stats.staff_count, color: "rose" }), /* @__PURE__ */ React.createElement(StatCard$9, { icon: "fa-solid fa-user-shield", label: "Moderators", value: stats.moderator_count, color: "amber" })), /* @__PURE__ */ React.createElement("div", { className: "mt-10" }, /* @__PURE__ */ React.createElement("h2", { className: "mb-4 text-sm font-semibold uppercase tracking-wider text-slate-500" }, "Quick Actions"), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-3" }, [ + return /* @__PURE__ */ React.createElement(AdminLayout, { title: "Dashboard", subtitle: "Overview of your Skinbase platform" }, /* @__PURE__ */ React.createElement(Se$1, { title: "Admin Dashboard" }), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-4" }, /* @__PURE__ */ React.createElement(StatCard$c, { icon: "fa-solid fa-users", label: "Total Users", value: stats.total_users, color: "sky" }), /* @__PURE__ */ React.createElement(StatCard$c, { icon: "fa-solid fa-user-plus", label: "New Today", value: stats.new_users_today, color: "violet" }), /* @__PURE__ */ React.createElement(StatCard$c, { icon: "fa-solid fa-shield-halved", label: "Staff Members", value: stats.staff_count, color: "rose" }), /* @__PURE__ */ React.createElement(StatCard$c, { icon: "fa-solid fa-user-shield", label: "Moderators", value: stats.moderator_count, color: "amber" })), /* @__PURE__ */ React.createElement("div", { className: "mt-10" }, /* @__PURE__ */ React.createElement("h2", { className: "mb-4 text-sm font-semibold uppercase tracking-wider text-slate-500" }, "Quick Actions"), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-3" }, [ { label: "Daily Activity", href: "/moderation/activity", icon: "fa-solid fa-calendar-day", desc: "Review everything created or moderated on a selected day" }, { label: "Manage Users", href: "/moderation/users", icon: "fa-solid fa-users", desc: "Search, promote or demote users" }, { label: "Staff Roles", href: "/moderation/users?role=admin", icon: "fa-solid fa-shield-halved", desc: "View all admins, managers and editorial staff" }, @@ -25638,8 +26205,7 @@ function Dashboard({ stats }) { { label: "Stories", href: "/moderation/stories", icon: "fa-solid fa-feather-pointed", desc: "Browse all creator stories" }, { label: "Artworks", href: "/moderation/artworks", icon: "fa-solid fa-images", desc: "Browse all uploaded artworks" }, { label: "Enhance Jobs", href: "/moderation/enhance", icon: "fa-solid fa-up-right-and-down-left-from-center", desc: "Inspect queued, failed, and completed image enhance jobs" }, - { label: "Featured Artworks", href: "/moderation/artworks/featured", icon: "fa-solid fa-star", desc: "Curate the homepage featured artwork lineup" }, - { label: "AI Biography", href: "/moderation/ai-biography", icon: "fa-solid fa-wand-magic-sparkles", desc: "Review generated creator biographies and moderation flags" } + { label: "Featured Artworks", href: "/moderation/artworks/featured", icon: "fa-solid fa-star", desc: "Curate the homepage featured artwork lineup" } ].map((item) => /* @__PURE__ */ React.createElement( "a", { @@ -25764,7 +26330,7 @@ function getCsrfToken$g() { if (typeof document === "undefined") return ""; return document.querySelector('meta[name="csrf-token"]')?.getAttribute("content") || ""; } -async function requestJson$p(url, { method = "POST", body: body2 } = {}) { +async function requestJson$q(url, { method = "POST", body: body2 } = {}) { const response = await fetch(url, { method, credentials: "same-origin", @@ -25795,7 +26361,7 @@ function localInputToIso$1(value) { if (Number.isNaN(date.getTime())) return null; return date.toISOString(); } -function formatDateTime$3(value) { +function formatDateTime$6(value) { if (!value) return "—"; const date = new Date(value); if (Number.isNaN(date.getTime())) return "—"; @@ -25893,7 +26459,7 @@ function Badge$2({ label, tone = "slate" }) { }; return /* @__PURE__ */ React.createElement("span", { className: cn("inline-flex rounded-full border px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em]", toneClasses2[tone] || toneClasses2.slate) }, label); } -function Field$4({ label, help, children }) { +function Field$5({ label, help, children }) { return /* @__PURE__ */ React.createElement("label", { className: "block space-y-2" }, /* @__PURE__ */ React.createElement("span", { className: "text-sm font-semibold text-white" }, label), children, help ? /* @__PURE__ */ React.createElement("span", { className: "block text-xs leading-relaxed text-slate-400" }, help) : null); } function NoticeBanner({ notice }) { @@ -25905,7 +26471,7 @@ function NoticeBanner({ notice }) { }; return /* @__PURE__ */ React.createElement("div", { className: cn("rounded-[22px] border px-4 py-3 text-sm leading-6 shadow-[0_10px_30px_rgba(2,6,23,0.18)]", toneClasses2[notice.tone] || toneClasses2.info) }, notice.message); } -function StatCard$8({ label, value, detail, tone = "sky" }) { +function StatCard$b({ label, value, detail, tone = "sky" }) { const toneClasses2 = { sky: "border-sky-300/15 bg-sky-400/10 text-sky-100", amber: "border-amber-300/15 bg-amber-400/10 text-amber-100", @@ -26033,7 +26599,7 @@ function FeaturedArtworksAdmin() { setNotice(null); try { const url = `${endpoints.search}?q=${encodeURIComponent(searchQuery.trim())}`; - const payload = await requestJson$p(url, { method: "GET" }); + const payload = await requestJson$q(url, { method: "GET" }); const results = Array.isArray(payload.results) ? payload.results : []; setSearchResults(results); if (results.length === 0) { @@ -26075,7 +26641,7 @@ function FeaturedArtworksAdmin() { setBusy("submit"); setNotice(null); try { - const payload = await requestJson$p( + const payload = await requestJson$q( editingId ? endpoints.updatePattern.replace("__FEATURE__", String(editingId)) : endpoints.store, { method: editingId ? "PATCH" : "POST", @@ -26100,7 +26666,7 @@ function FeaturedArtworksAdmin() { setBusy(`toggle-${entry.id}`); setNotice(null); try { - const payload = await requestJson$p(endpoints.togglePattern.replace("__FEATURE__", String(entry.id)), { + const payload = await requestJson$q(endpoints.togglePattern.replace("__FEATURE__", String(entry.id)), { method: "PATCH" }); syncPayload(payload); @@ -26117,7 +26683,7 @@ function FeaturedArtworksAdmin() { setBusy(`delete-${entry.id}`); setNotice(null); try { - const payload = await requestJson$p(endpoints.destroyPattern.replace("__FEATURE__", String(entry.id)), { + const payload = await requestJson$q(endpoints.destroyPattern.replace("__FEATURE__", String(entry.id)), { method: "DELETE" }); syncPayload(payload); @@ -26134,7 +26700,7 @@ function FeaturedArtworksAdmin() { setBusy(`force-${entry.id}`); setNotice(null); try { - const payload = await requestJson$p(endpoints.forceHeroPattern.replace("__FEATURE__", String(entry.id)), { + const payload = await requestJson$q(endpoints.forceHeroPattern.replace("__FEATURE__", String(entry.id)), { method: "PATCH" }); syncPayload(payload); @@ -26249,14 +26815,14 @@ function FeaturedArtworksAdmin() { className: "rounded-full border border-white/10 px-5 py-3 text-sm font-semibold text-slate-100 transition hover:border-white/20 hover:bg-white/[0.06]" }, "Review attention items" - ), winner?.artwork?.canonical_url ? /* @__PURE__ */ React.createElement("a", { href: winner.artwork.canonical_url, target: "_blank", rel: "noreferrer", className: "rounded-full border border-amber-300/20 px-5 py-3 text-sm font-semibold text-amber-100 transition hover:border-amber-300/40 hover:bg-amber-400/10" }, "Open current winner") : null)), /* @__PURE__ */ React.createElement("div", { className: "grid w-full max-w-2xl gap-4 sm:grid-cols-2" }, /* @__PURE__ */ React.createElement(StatCard$8, { label: "Active pool", value: stats.active || 0, detail: "Rows currently allowed to compete for the homepage slot.", tone: "sky" }), /* @__PURE__ */ React.createElement(StatCard$8, { label: "Eligible now", value: stats.eligible || 0, detail: "Rows that can win without any manual override.", tone: "emerald" }), /* @__PURE__ */ React.createElement(StatCard$8, { label: "Force Hero", value: activeForceHeroEntry ? "On" : "Off", detail: activeForceHeroEntry ? `${activeForceHeroEntry.artwork?.title || "Selected artwork"} is overriding the normal order.` : "Normal winner logic is in control.", tone: "amber" }), /* @__PURE__ */ React.createElement(StatCard$8, { label: "Expiring soon", value: soonExpiringEntries.length, detail: soonExpiringEntries[0] ? `${soonExpiringEntries[0].artwork?.title || "Next row"} is the nearest expiry.` : "No featured rows expire in the next 7 days.", tone: soonExpiringEntries.length > 0 ? "rose" : "sky" })))), /* @__PURE__ */ React.createElement(NoticeBanner, { notice }), /* @__PURE__ */ React.createElement("div", { className: "grid gap-8 xl:grid-cols-[1.3fr_0.7fr]" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-white/[0.04] p-6 shadow-[0_22px_70px_rgba(2,6,23,0.28)] backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "text-xs font-semibold uppercase tracking-[0.2em] text-slate-400" }, "Current Homepage Hero"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-3xl font-semibold tracking-[-0.05em] text-white" }, winner ? winner.artwork?.title : "No eligible featured artwork"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 max-w-2xl text-sm leading-7 text-slate-300" }, winner?.selection_reason || "There is no active, non-expired, eligible featured artwork right now.")), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, winner ? /* @__PURE__ */ React.createElement(Badge$2, { label: "Winner", tone: "amber" }) : /* @__PURE__ */ React.createElement(Badge$2, { label: "No winner", tone: "rose" }), winner?.is_force_hero ? /* @__PURE__ */ React.createElement(Badge$2, { label: "Force Hero", tone: "amber" }) : /* @__PURE__ */ React.createElement(Badge$2, { label: "Normal logic", tone: "sky" }))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-6 lg:grid-cols-[240px_1fr]" }, /* @__PURE__ */ React.createElement("a", { href: winner?.artwork?.canonical_url || "#", className: "overflow-hidden rounded-[26px] border border-white/10 bg-[#09121f]", target: "_blank", rel: "noreferrer" }, /* @__PURE__ */ React.createElement( + ), winner?.artwork?.canonical_url ? /* @__PURE__ */ React.createElement("a", { href: winner.artwork.canonical_url, target: "_blank", rel: "noreferrer", className: "rounded-full border border-amber-300/20 px-5 py-3 text-sm font-semibold text-amber-100 transition hover:border-amber-300/40 hover:bg-amber-400/10" }, "Open current winner") : null)), /* @__PURE__ */ React.createElement("div", { className: "grid w-full max-w-2xl gap-4 sm:grid-cols-2" }, /* @__PURE__ */ React.createElement(StatCard$b, { label: "Active pool", value: stats.active || 0, detail: "Rows currently allowed to compete for the homepage slot.", tone: "sky" }), /* @__PURE__ */ React.createElement(StatCard$b, { label: "Eligible now", value: stats.eligible || 0, detail: "Rows that can win without any manual override.", tone: "emerald" }), /* @__PURE__ */ React.createElement(StatCard$b, { label: "Force Hero", value: activeForceHeroEntry ? "On" : "Off", detail: activeForceHeroEntry ? `${activeForceHeroEntry.artwork?.title || "Selected artwork"} is overriding the normal order.` : "Normal winner logic is in control.", tone: "amber" }), /* @__PURE__ */ React.createElement(StatCard$b, { label: "Expiring soon", value: soonExpiringEntries.length, detail: soonExpiringEntries[0] ? `${soonExpiringEntries[0].artwork?.title || "Next row"} is the nearest expiry.` : "No featured rows expire in the next 7 days.", tone: soonExpiringEntries.length > 0 ? "rose" : "sky" })))), /* @__PURE__ */ React.createElement(NoticeBanner, { notice }), /* @__PURE__ */ React.createElement("div", { className: "grid gap-8 xl:grid-cols-[1.3fr_0.7fr]" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-white/[0.04] p-6 shadow-[0_22px_70px_rgba(2,6,23,0.28)] backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "text-xs font-semibold uppercase tracking-[0.2em] text-slate-400" }, "Current Homepage Hero"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-3xl font-semibold tracking-[-0.05em] text-white" }, winner ? winner.artwork?.title : "No eligible featured artwork"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 max-w-2xl text-sm leading-7 text-slate-300" }, winner?.selection_reason || "There is no active, non-expired, eligible featured artwork right now.")), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, winner ? /* @__PURE__ */ React.createElement(Badge$2, { label: "Winner", tone: "amber" }) : /* @__PURE__ */ React.createElement(Badge$2, { label: "No winner", tone: "rose" }), winner?.is_force_hero ? /* @__PURE__ */ React.createElement(Badge$2, { label: "Force Hero", tone: "amber" }) : /* @__PURE__ */ React.createElement(Badge$2, { label: "Normal logic", tone: "sky" }))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-6 lg:grid-cols-[240px_1fr]" }, /* @__PURE__ */ React.createElement("a", { href: winner?.artwork?.canonical_url || "#", className: "overflow-hidden rounded-[26px] border border-white/10 bg-[#09121f]", target: "_blank", rel: "noreferrer" }, /* @__PURE__ */ React.createElement( "img", { src: winner?.artwork?.thumbnail?.url, alt: winner?.artwork?.title || "Winner preview", className: "h-full min-h-[220px] w-full object-cover" } - )), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(MetricTile, { label: "Artist", value: winner?.artwork?.owner?.display_name || "Unknown", hint: winner?.artwork?.owner?.type === "group" ? "Group publisher" : `@${winner?.artwork?.owner?.username || ""}` }), /* @__PURE__ */ React.createElement(MetricTile, { label: "Priority", value: winner?.priority ?? "—", hint: "Primary tie-breaker in hero selection." }), /* @__PURE__ */ React.createElement(MetricTile, { label: "Medal score", value: winner?.medals?.score_30d || 0, hint: "Last 30 days." }), /* @__PURE__ */ React.createElement(MetricTile, { label: "Featured Since", value: formatDateTime$3(winner?.featured_at), hint: "Used after priority and medal score." }))), winner?.is_force_hero ? /* @__PURE__ */ React.createElement("div", { className: "mt-5 rounded-[22px] border border-amber-300/20 bg-amber-400/10 px-4 py-3 text-sm leading-6 text-amber-50" }, "Force Hero is active. This row bypasses the normal ranking until editors disable the override from the roster below.") : null), /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-white/[0.04] p-6 shadow-[0_22px_70px_rgba(2,6,23,0.28)] backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("div", { className: "text-xs font-semibold uppercase tracking-[0.2em] text-slate-400" }, "Editorial Radar"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white" }, "Know what happens next before you touch a row."), /* @__PURE__ */ React.createElement("div", { className: "mt-6 space-y-4" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.18em] text-slate-400" }, "Natural fallback"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-lg font-semibold text-white" }, naturalFallback?.artwork?.title || "No fallback candidate"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-sm leading-6 text-slate-400" }, naturalFallback ? `${formatOwner(naturalFallback)} · Priority ${naturalFallback.priority} · Medal ${naturalFallback.medals?.score_30d || 0}` : "All remaining rows are either inactive, expired, or ineligible.")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.18em] text-slate-400" }, "Needs attention"), attentionEntries.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-sm leading-6 text-slate-300" }, "No active rows are currently blocked by expiry or eligibility rules.") : /* @__PURE__ */ React.createElement("div", { className: "mt-3 space-y-3" }, attentionEntries.map((entry) => /* @__PURE__ */ React.createElement( + )), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(MetricTile, { label: "Artist", value: winner?.artwork?.owner?.display_name || "Unknown", hint: winner?.artwork?.owner?.type === "group" ? "Group publisher" : `@${winner?.artwork?.owner?.username || ""}` }), /* @__PURE__ */ React.createElement(MetricTile, { label: "Priority", value: winner?.priority ?? "—", hint: "Primary tie-breaker in hero selection." }), /* @__PURE__ */ React.createElement(MetricTile, { label: "Medal score", value: winner?.medals?.score_30d || 0, hint: "Last 30 days." }), /* @__PURE__ */ React.createElement(MetricTile, { label: "Featured Since", value: formatDateTime$6(winner?.featured_at), hint: "Used after priority and medal score." }))), winner?.is_force_hero ? /* @__PURE__ */ React.createElement("div", { className: "mt-5 rounded-[22px] border border-amber-300/20 bg-amber-400/10 px-4 py-3 text-sm leading-6 text-amber-50" }, "Force Hero is active. This row bypasses the normal ranking until editors disable the override from the roster below.") : null), /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-white/[0.04] p-6 shadow-[0_22px_70px_rgba(2,6,23,0.28)] backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("div", { className: "text-xs font-semibold uppercase tracking-[0.2em] text-slate-400" }, "Editorial Radar"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white" }, "Know what happens next before you touch a row."), /* @__PURE__ */ React.createElement("div", { className: "mt-6 space-y-4" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.18em] text-slate-400" }, "Natural fallback"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-lg font-semibold text-white" }, naturalFallback?.artwork?.title || "No fallback candidate"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-sm leading-6 text-slate-400" }, naturalFallback ? `${formatOwner(naturalFallback)} · Priority ${naturalFallback.priority} · Medal ${naturalFallback.medals?.score_30d || 0}` : "All remaining rows are either inactive, expired, or ineligible.")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.18em] text-slate-400" }, "Needs attention"), attentionEntries.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-sm leading-6 text-slate-300" }, "No active rows are currently blocked by expiry or eligibility rules.") : /* @__PURE__ */ React.createElement("div", { className: "mt-3 space-y-3" }, attentionEntries.map((entry) => /* @__PURE__ */ React.createElement( "button", { type: "button", @@ -26272,7 +26838,7 @@ function FeaturedArtworksAdmin() { }, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold text-white" }, entry.artwork?.title || `Featured row #${entry.id}`), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs leading-5 text-slate-400" }, buildEntrySummary(entry)) - )))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.18em] text-slate-400" }, "Next expiry checkpoint"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-lg font-semibold text-white" }, soonExpiringEntries[0]?.artwork?.title || "Nothing expiring soon"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-sm leading-6 text-slate-400" }, soonExpiringEntries[0] ? `${formatRelativeExpiry(soonExpiringEntries[0].expires_at)} · ${formatDateTime$3(soonExpiringEntries[0].expires_at)}` : "No featured rows expire in the next 7 days."))))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-8 xl:grid-cols-[1.25fr_0.75fr]" }, /* @__PURE__ */ React.createElement("section", { ref: rosterRef, className: "rounded-[30px] border border-white/10 bg-white/[0.04] p-6 shadow-[0_22px_70px_rgba(2,6,23,0.28)] backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "text-xs font-semibold uppercase tracking-[0.2em] text-slate-400" }, "Featured Pool"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white" }, "Every featured row, with enough context to act quickly."), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm leading-6 text-slate-400" }, "Showing ", visibleEntries.length, " of ", filteredEntries.length, " matching rows. ", entries.length, " rows in the full pool.")), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 sm:grid-cols-[minmax(0,1fr)_220px_124px] lg:w-[720px]" }, /* @__PURE__ */ React.createElement( + )))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.18em] text-slate-400" }, "Next expiry checkpoint"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-lg font-semibold text-white" }, soonExpiringEntries[0]?.artwork?.title || "Nothing expiring soon"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-sm leading-6 text-slate-400" }, soonExpiringEntries[0] ? `${formatRelativeExpiry(soonExpiringEntries[0].expires_at)} · ${formatDateTime$6(soonExpiringEntries[0].expires_at)}` : "No featured rows expire in the next 7 days."))))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-8 xl:grid-cols-[1.25fr_0.75fr]" }, /* @__PURE__ */ React.createElement("section", { ref: rosterRef, className: "rounded-[30px] border border-white/10 bg-white/[0.04] p-6 shadow-[0_22px_70px_rgba(2,6,23,0.28)] backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "text-xs font-semibold uppercase tracking-[0.2em] text-slate-400" }, "Featured Pool"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white" }, "Every featured row, with enough context to act quickly."), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm leading-6 text-slate-400" }, "Showing ", visibleEntries.length, " of ", filteredEntries.length, " matching rows. ", entries.length, " rows in the full pool.")), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 sm:grid-cols-[minmax(0,1fr)_220px_124px] lg:w-[720px]" }, /* @__PURE__ */ React.createElement( "input", { type: "text", @@ -26291,7 +26857,7 @@ function FeaturedArtworksAdmin() { onClick: (nextValue) => React.startTransition(() => setFilter(nextValue)), count: filterCounts[option.value] ?? 0 } - )))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 space-y-4" }, filteredEntries.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "rounded-[26px] border border-dashed border-white/10 bg-black/20 px-6 py-12 text-center text-sm leading-6 text-slate-400" }, "No featured entries match the current search and filter combination.") : visibleEntries.map((entry) => /* @__PURE__ */ React.createElement("article", { key: entry.id, className: "rounded-[28px] border border-white/10 bg-[linear-gradient(180deg,rgba(255,255,255,0.05),rgba(255,255,255,0.02))] p-5 shadow-[0_20px_50px_rgba(2,6,23,0.22)]" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-5 xl:flex-row xl:items-start xl:justify-between" }, /* @__PURE__ */ React.createElement("div", { className: "flex min-w-0 gap-4" }, /* @__PURE__ */ React.createElement("a", { href: entry.artwork?.canonical_url || "#", target: "_blank", rel: "noreferrer", className: "h-28 w-28 shrink-0 overflow-hidden rounded-[22px] border border-white/10 bg-[#08111d]" }, /* @__PURE__ */ React.createElement("img", { src: entry.artwork?.thumbnail?.url, alt: entry.artwork?.title || "Artwork preview", className: "h-full w-full object-cover" })), /* @__PURE__ */ React.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2" }, /* @__PURE__ */ React.createElement("h3", { className: "text-lg font-semibold tracking-[-0.03em] text-white" }, entry.artwork?.title || "Missing artwork"), /* @__PURE__ */ React.createElement("span", { className: "text-xs text-slate-400" }, "Artwork #", entry.artwork?.id || entry.artwork_id), /* @__PURE__ */ React.createElement("span", { className: "text-xs text-slate-500" }, "Row #", entry.id)), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-sm text-slate-300" }, formatOwner(entry)), /* @__PURE__ */ React.createElement("p", { className: "mt-3 max-w-3xl text-sm leading-6 text-slate-400" }, buildEntrySummary(entry)), /* @__PURE__ */ React.createElement("div", { className: "mt-3 flex flex-wrap gap-2" }, (entry.status_badges || []).map((badge, index2) => /* @__PURE__ */ React.createElement(Badge$2, { key: `${entry.id}-${badge.label}-${index2}`, label: badge.label, tone: badge.tone }))))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 sm:grid-cols-2 xl:w-[420px] xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(MetricTile, { label: "Priority", value: entry.priority, hint: "Higher wins first" }), /* @__PURE__ */ React.createElement(MetricTile, { label: "Medal score", value: entry.medals?.score_30d || 0, hint: "Last 30d" }), /* @__PURE__ */ React.createElement(MetricTile, { label: "Featured", value: formatShortDate$3(entry.featured_at), hint: formatDateTime$3(entry.featured_at) }), /* @__PURE__ */ React.createElement(MetricTile, { label: "Expires", value: formatRelativeExpiry(entry.expires_at), hint: formatDateTime$3(entry.expires_at) }))), /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-col gap-4 border-t border-white/10 pt-4 xl:flex-row xl:items-center xl:justify-between" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm leading-6 text-slate-400" }, "Visibility: ", /* @__PURE__ */ React.createElement("span", { className: "text-slate-200" }, entry.artwork?.visibility || "—"), /* @__PURE__ */ React.createElement("span", { className: "mx-2 text-slate-600" }, "•"), "Published: ", /* @__PURE__ */ React.createElement("span", { className: "text-slate-200" }, entry.artwork?.published_at ? "Yes" : "No")), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2 xl:justify-end" }, /* @__PURE__ */ React.createElement("a", { href: entry.artwork?.canonical_url || "#", target: "_blank", rel: "noreferrer", className: "rounded-full border border-white/10 px-4 py-2 text-xs font-semibold uppercase tracking-[0.16em] text-slate-100 transition hover:border-white/20 hover:bg-white/5" }, "Open artwork"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => editEntry(entry), className: "rounded-full border border-white/10 px-4 py-2 text-xs font-semibold uppercase tracking-[0.16em] text-slate-100 transition hover:border-white/20 hover:bg-white/5" }, "Edit row"), capabilities.forceHeroEnabled ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => handleForceHero(entry), disabled: busy === `force-${entry.id}`, className: cn("rounded-full border px-4 py-2 text-xs font-semibold uppercase tracking-[0.16em] transition disabled:cursor-not-allowed disabled:opacity-60", entry.is_force_hero ? "border-amber-300/25 text-amber-100 hover:border-amber-300/40 hover:bg-amber-400/10" : "border-amber-300/15 text-amber-50 hover:border-amber-300/30 hover:bg-amber-400/5") }, busy === `force-${entry.id}` ? "Saving…" : entry.is_force_hero ? "Disable Force Hero" : "Force Hero") : null, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => handleToggle(entry), disabled: busy === `toggle-${entry.id}`, className: "rounded-full border border-sky-300/20 px-4 py-2 text-xs font-semibold uppercase tracking-[0.16em] text-sky-100 transition hover:border-sky-300/40 hover:bg-sky-400/10 disabled:cursor-not-allowed disabled:opacity-60" }, busy === `toggle-${entry.id}` ? "Saving…" : entry.is_active ? "Deactivate" : "Activate"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => handleDelete(entry), disabled: busy === `delete-${entry.id}`, className: "rounded-full border border-rose-300/20 px-4 py-2 text-xs font-semibold uppercase tracking-[0.16em] text-rose-100 transition hover:border-rose-300/40 hover:bg-rose-400/10 disabled:cursor-not-allowed disabled:opacity-60" }, busy === `delete-${entry.id}` ? "Deleting…" : "Delete"))))), hasMoreEntries ? /* @__PURE__ */ React.createElement("div", { ref: loadMoreRef, className: "rounded-[24px] border border-dashed border-white/10 bg-black/20 px-5 py-6 text-center" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm text-slate-300" }, "Loading more rows as you reach the bottom."), /* @__PURE__ */ React.createElement( + )))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 space-y-4" }, filteredEntries.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "rounded-[26px] border border-dashed border-white/10 bg-black/20 px-6 py-12 text-center text-sm leading-6 text-slate-400" }, "No featured entries match the current search and filter combination.") : visibleEntries.map((entry) => /* @__PURE__ */ React.createElement("article", { key: entry.id, className: "rounded-[28px] border border-white/10 bg-[linear-gradient(180deg,rgba(255,255,255,0.05),rgba(255,255,255,0.02))] p-5 shadow-[0_20px_50px_rgba(2,6,23,0.22)]" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-5 xl:flex-row xl:items-start xl:justify-between" }, /* @__PURE__ */ React.createElement("div", { className: "flex min-w-0 gap-4" }, /* @__PURE__ */ React.createElement("a", { href: entry.artwork?.canonical_url || "#", target: "_blank", rel: "noreferrer", className: "h-28 w-28 shrink-0 overflow-hidden rounded-[22px] border border-white/10 bg-[#08111d]" }, /* @__PURE__ */ React.createElement("img", { src: entry.artwork?.thumbnail?.url, alt: entry.artwork?.title || "Artwork preview", className: "h-full w-full object-cover" })), /* @__PURE__ */ React.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2" }, /* @__PURE__ */ React.createElement("h3", { className: "text-lg font-semibold tracking-[-0.03em] text-white" }, entry.artwork?.title || "Missing artwork"), /* @__PURE__ */ React.createElement("span", { className: "text-xs text-slate-400" }, "Artwork #", entry.artwork?.id || entry.artwork_id), /* @__PURE__ */ React.createElement("span", { className: "text-xs text-slate-500" }, "Row #", entry.id)), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-sm text-slate-300" }, formatOwner(entry)), /* @__PURE__ */ React.createElement("p", { className: "mt-3 max-w-3xl text-sm leading-6 text-slate-400" }, buildEntrySummary(entry)), /* @__PURE__ */ React.createElement("div", { className: "mt-3 flex flex-wrap gap-2" }, (entry.status_badges || []).map((badge, index2) => /* @__PURE__ */ React.createElement(Badge$2, { key: `${entry.id}-${badge.label}-${index2}`, label: badge.label, tone: badge.tone }))))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 sm:grid-cols-2 xl:w-[420px] xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(MetricTile, { label: "Priority", value: entry.priority, hint: "Higher wins first" }), /* @__PURE__ */ React.createElement(MetricTile, { label: "Medal score", value: entry.medals?.score_30d || 0, hint: "Last 30d" }), /* @__PURE__ */ React.createElement(MetricTile, { label: "Featured", value: formatShortDate$3(entry.featured_at), hint: formatDateTime$6(entry.featured_at) }), /* @__PURE__ */ React.createElement(MetricTile, { label: "Expires", value: formatRelativeExpiry(entry.expires_at), hint: formatDateTime$6(entry.expires_at) }))), /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-col gap-4 border-t border-white/10 pt-4 xl:flex-row xl:items-center xl:justify-between" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm leading-6 text-slate-400" }, "Visibility: ", /* @__PURE__ */ React.createElement("span", { className: "text-slate-200" }, entry.artwork?.visibility || "—"), /* @__PURE__ */ React.createElement("span", { className: "mx-2 text-slate-600" }, "•"), "Published: ", /* @__PURE__ */ React.createElement("span", { className: "text-slate-200" }, entry.artwork?.published_at ? "Yes" : "No")), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2 xl:justify-end" }, /* @__PURE__ */ React.createElement("a", { href: entry.artwork?.canonical_url || "#", target: "_blank", rel: "noreferrer", className: "rounded-full border border-white/10 px-4 py-2 text-xs font-semibold uppercase tracking-[0.16em] text-slate-100 transition hover:border-white/20 hover:bg-white/5" }, "Open artwork"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => editEntry(entry), className: "rounded-full border border-white/10 px-4 py-2 text-xs font-semibold uppercase tracking-[0.16em] text-slate-100 transition hover:border-white/20 hover:bg-white/5" }, "Edit row"), capabilities.forceHeroEnabled ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => handleForceHero(entry), disabled: busy === `force-${entry.id}`, className: cn("rounded-full border px-4 py-2 text-xs font-semibold uppercase tracking-[0.16em] transition disabled:cursor-not-allowed disabled:opacity-60", entry.is_force_hero ? "border-amber-300/25 text-amber-100 hover:border-amber-300/40 hover:bg-amber-400/10" : "border-amber-300/15 text-amber-50 hover:border-amber-300/30 hover:bg-amber-400/5") }, busy === `force-${entry.id}` ? "Saving…" : entry.is_force_hero ? "Disable Force Hero" : "Force Hero") : null, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => handleToggle(entry), disabled: busy === `toggle-${entry.id}`, className: "rounded-full border border-sky-300/20 px-4 py-2 text-xs font-semibold uppercase tracking-[0.16em] text-sky-100 transition hover:border-sky-300/40 hover:bg-sky-400/10 disabled:cursor-not-allowed disabled:opacity-60" }, busy === `toggle-${entry.id}` ? "Saving…" : entry.is_active ? "Deactivate" : "Activate"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => handleDelete(entry), disabled: busy === `delete-${entry.id}`, className: "rounded-full border border-rose-300/20 px-4 py-2 text-xs font-semibold uppercase tracking-[0.16em] text-rose-100 transition hover:border-rose-300/40 hover:bg-rose-400/10 disabled:cursor-not-allowed disabled:opacity-60" }, busy === `delete-${entry.id}` ? "Deleting…" : "Delete"))))), hasMoreEntries ? /* @__PURE__ */ React.createElement("div", { ref: loadMoreRef, className: "rounded-[24px] border border-dashed border-white/10 bg-black/20 px-5 py-6 text-center" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm text-slate-300" }, "Loading more rows as you reach the bottom."), /* @__PURE__ */ React.createElement( "button", { type: "button", @@ -26299,7 +26865,7 @@ function FeaturedArtworksAdmin() { className: "mt-4 rounded-full border border-white/10 px-4 py-2 text-xs font-semibold uppercase tracking-[0.16em] text-slate-100 transition hover:border-white/20 hover:bg-white/5" }, "Load 24 more" - )) : filteredEntries.length > PAGE_SIZE$1 ? /* @__PURE__ */ React.createElement("div", { className: "text-center text-xs font-semibold uppercase tracking-[0.18em] text-slate-500" }, "All matching rows loaded") : null)), /* @__PURE__ */ React.createElement("section", { ref: composerRef, className: "rounded-[30px] border border-white/10 bg-white/[0.04] p-6 shadow-[0_22px_70px_rgba(2,6,23,0.28)] backdrop-blur-sm xl:sticky xl:top-6 xl:self-start" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "text-xs font-semibold uppercase tracking-[0.2em] text-slate-400" }, "Editorial Composer"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white" }, editingId ? `Edit featured row #${editingId}` : "Build a new featured row"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm leading-6 text-slate-400" }, "Search, inspect readiness, choose timing, and ship the change without leaving the page context.")), editingId ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: resetEditor, className: "rounded-full border border-white/10 px-4 py-2 text-xs font-semibold uppercase tracking-[0.18em] text-slate-200 transition hover:border-white/20 hover:bg-white/5" }, "Cancel edit") : null), !editingId ? /* @__PURE__ */ React.createElement("form", { onSubmit: handleArtworkSearch, className: "mt-6 space-y-4 rounded-[26px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement(Field$4, { label: "Artwork selector", help: "Search by artwork ID, title, slug, artist, or group, then lock one result into the composer." }, /* @__PURE__ */ React.createElement("div", { className: "flex gap-3" }, /* @__PURE__ */ React.createElement( + )) : filteredEntries.length > PAGE_SIZE$1 ? /* @__PURE__ */ React.createElement("div", { className: "text-center text-xs font-semibold uppercase tracking-[0.18em] text-slate-500" }, "All matching rows loaded") : null)), /* @__PURE__ */ React.createElement("section", { ref: composerRef, className: "rounded-[30px] border border-white/10 bg-white/[0.04] p-6 shadow-[0_22px_70px_rgba(2,6,23,0.28)] backdrop-blur-sm xl:sticky xl:top-6 xl:self-start" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "text-xs font-semibold uppercase tracking-[0.2em] text-slate-400" }, "Editorial Composer"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.04em] text-white" }, editingId ? `Edit featured row #${editingId}` : "Build a new featured row"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm leading-6 text-slate-400" }, "Search, inspect readiness, choose timing, and ship the change without leaving the page context.")), editingId ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: resetEditor, className: "rounded-full border border-white/10 px-4 py-2 text-xs font-semibold uppercase tracking-[0.18em] text-slate-200 transition hover:border-white/20 hover:bg-white/5" }, "Cancel edit") : null), !editingId ? /* @__PURE__ */ React.createElement("form", { onSubmit: handleArtworkSearch, className: "mt-6 space-y-4 rounded-[26px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement(Field$5, { label: "Artwork selector", help: "Search by artwork ID, title, slug, artist, or group, then lock one result into the composer." }, /* @__PURE__ */ React.createElement("div", { className: "flex gap-3" }, /* @__PURE__ */ React.createElement( "input", { type: "text", @@ -26356,7 +26922,7 @@ function FeaturedArtworksAdmin() { className: "rounded-full border border-amber-300/20 px-4 py-2 text-xs font-semibold uppercase tracking-[0.16em] text-amber-50 transition hover:border-amber-300/40 hover:bg-amber-400/10" }, "Find existing row" - ))) : null, /* @__PURE__ */ React.createElement("form", { onSubmit: handleSubmit, className: "mt-6 space-y-5" }, /* @__PURE__ */ React.createElement(Field$4, { label: "Priority", help: "Higher priority always wins before medal score is considered." }, /* @__PURE__ */ React.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React.createElement( + ))) : null, /* @__PURE__ */ React.createElement("form", { onSubmit: handleSubmit, className: "mt-6 space-y-5" }, /* @__PURE__ */ React.createElement(Field$5, { label: "Priority", help: "Higher priority always wins before medal score is considered." }, /* @__PURE__ */ React.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React.createElement( "input", { type: "number", @@ -26390,13 +26956,13 @@ function FeaturedArtworksAdmin() { className: "rounded-full border border-white/10 px-3 py-1.5 text-xs font-semibold uppercase tracking-[0.16em] text-slate-300 transition hover:border-white/20 hover:bg-white/[0.05]" }, "Match winner" - ) : null))), /* @__PURE__ */ React.createElement(Field$4, { label: "Active", help: "Inactive rows stay visible in admin but cannot win the homepage hero." }, /* @__PURE__ */ React.createElement("label", { className: "flex h-[52px] items-center gap-3 rounded-2xl border border-white/10 bg-[#08111d] px-4 py-3 text-sm text-slate-100" }, /* @__PURE__ */ React.createElement( + ) : null))), /* @__PURE__ */ React.createElement(Field$5, { label: "Active", help: "Inactive rows stay visible in admin but cannot win the homepage hero." }, /* @__PURE__ */ React.createElement("label", { className: "flex h-[52px] items-center gap-3 rounded-2xl border border-white/10 bg-[#08111d] px-4 py-3 text-sm text-slate-100" }, /* @__PURE__ */ React.createElement( Checkbox, { checked: Boolean(form.is_active), onChange: (event) => setForm((current) => ({ ...current, is_active: event.target.checked })) } - ), /* @__PURE__ */ React.createElement("span", null, form.is_active ? "Active on save" : "Inactive on save"))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 sm:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$4, { label: "Featured Since" }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: form.featured_at, onChange: (nextValue) => setForm((current) => ({ ...current, featured_at: nextValue })), placeholder: "Featured since", clearable: true, className: "bg-[#08111d]" })), /* @__PURE__ */ React.createElement(Field$4, { label: "Expires" }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: form.expires_at, onChange: (nextValue) => setForm((current) => ({ ...current, expires_at: nextValue })), placeholder: "Expiry date", clearable: true, className: "bg-[#08111d]" }))), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-3" }, /* @__PURE__ */ React.createElement( + ), /* @__PURE__ */ React.createElement("span", null, form.is_active ? "Active on save" : "Inactive on save"))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 sm:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$5, { label: "Featured Since" }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: form.featured_at, onChange: (nextValue) => setForm((current) => ({ ...current, featured_at: nextValue })), placeholder: "Featured since", clearable: true, className: "bg-[#08111d]" })), /* @__PURE__ */ React.createElement(Field$5, { label: "Expires" }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: form.expires_at, onChange: (nextValue) => setForm((current) => ({ ...current, expires_at: nextValue })), placeholder: "Expiry date", clearable: true, className: "bg-[#08111d]" }))), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-3" }, /* @__PURE__ */ React.createElement( "button", { type: "submit", @@ -27640,7 +28206,7 @@ function ArtworkHero({ artwork, presentMd, presentLg, presentXl, mediaWidth = nu onError: (event) => { event.currentTarget.onerror = null; if (mainImageMode === "primary") { - setMainImageMode("fallback"); + setMainImageMode(hasRealArtworkImage ? "hidden" : "fallback"); setIsLoaded(false); return; } @@ -40855,7 +41421,7 @@ function useWebShare({ onFallback } = {}) { ); return { canNativeShare, share }; } -const ArtworkShareModal = reactExports.lazy(() => import("./assets/ArtworkShareModal-BI8kkaqs.js")); +const ArtworkShareModal = reactExports.lazy(() => import("./assets/ArtworkShareModal-BPM8yel5.js")); function ShareIcon() { return /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: 1.5, stroke: "currentColor", className: "h-5 w-5" }, /* @__PURE__ */ React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 1 1 0-2.684m0 2.684 6.632 3.316m-6.632-6 6.632-3.316m0 0a3 3 0 1 0 5.367-2.684 3 3 0 0 0-5.367 2.684Zm0 9.316a3 3 0 1 0 5.368 2.684 3 3 0 0 0-5.368-2.684Z" })); } @@ -41182,14 +41748,6 @@ function ArtworkActionBar({ artwork, stats, canonicalUrl, onStatsChange }) { }, /* @__PURE__ */ React.createElement(ChartIcon, null), "Statistics" - ) : null, enhanceUrl ? /* @__PURE__ */ React.createElement( - "a", - { - href: enhanceUrl, - className: "inline-flex items-center gap-2 rounded-full border border-violet-300/25 bg-violet-400/12 px-5 py-2.5 text-sm font-medium text-violet-50 transition-all duration-200 hover:border-violet-200/40 hover:bg-violet-400/18 hover:text-white" - }, - /* @__PURE__ */ React.createElement(EnhanceIcon, null), - "Enhance image" ) : null, /* @__PURE__ */ React.createElement( "button", { @@ -75070,7 +75628,7 @@ function CategoriesPage({ apiUrl: apiUrl2 = "/api/categories", pageTitle = "Cate const showingStart = loadedCount > 0 ? 1 : 0; const showingEnd = loadedCount; const hasMorePages = meta.current_page < meta.last_page; - return /* @__PURE__ */ React.createElement("div", { className: "pb-24 text-white" }, /* @__PURE__ */ React.createElement("section", { className: "relative overflow-hidden" }, /* @__PURE__ */ React.createElement("div", { className: "absolute inset-x-0 top-0 h-[28rem] bg-[radial-gradient(circle_at_top_left,rgba(34,211,238,0.12),transparent_38%),radial-gradient(circle_at_top_right,rgba(249,115,22,0.14),transparent_34%)]" }), /* @__PURE__ */ React.createElement("div", { className: "relative w-full px-6 pb-8 pt-14 sm:px-8 sm:pt-20 xl:px-10 2xl:px-14 lg:pt-24" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-8 lg:grid-cols-[minmax(0,1.2fr)_20rem] lg:items-end" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "inline-flex rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-xs font-semibold uppercase tracking-[0.24em] text-white/50 backdrop-blur-sm" }, "Category directory"), /* @__PURE__ */ React.createElement("h1", { className: "mt-5 max-w-4xl text-4xl font-semibold tracking-[-0.05em] text-white sm:text-5xl lg:text-6xl" }, pageTitle), /* @__PURE__ */ React.createElement("p", { className: "mt-5 max-w-2xl text-base leading-8 text-white/62 sm:text-lg" }, pageDescription || "Browse all wallpapers, skins, themes and digital art categories")), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 sm:grid-cols-3 lg:grid-cols-1" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/22 p-5 backdrop-blur-md shadow-[0_24px_60px_rgba(0,0,0,0.24)]" }, /* @__PURE__ */ React.createElement("p", { className: "text-xs font-semibold uppercase tracking-[0.2em] text-white/40" }, "Categories"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-3xl font-semibold tracking-[-0.04em] text-white" }, numberFormatter$1.format(summary.total_categories))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/22 p-5 backdrop-blur-md shadow-[0_24px_60px_rgba(0,0,0,0.24)]" }, /* @__PURE__ */ React.createElement("p", { className: "text-xs font-semibold uppercase tracking-[0.2em] text-white/40" }, "Artworks indexed"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-3xl font-semibold tracking-[-0.04em] text-white" }, numberFormatter$1.format(summary.total_artworks))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/22 p-5 backdrop-blur-md shadow-[0_24px_60px_rgba(0,0,0,0.24)]" }, /* @__PURE__ */ React.createElement("p", { className: "text-xs font-semibold uppercase tracking-[0.2em] text-white/40" }, "View"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-3xl font-semibold tracking-[-0.04em] text-white" }, "Grid")))), /* @__PURE__ */ React.createElement("div", { className: "mt-10 rounded-[30px] border border-white/10 bg-black/25 p-4 shadow-[0_30px_80px_rgba(0,0,0,0.25)] backdrop-blur-xl sm:p-5" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 lg:grid-cols-[minmax(0,1fr)_16rem] lg:items-center" }, /* @__PURE__ */ React.createElement("label", { className: "relative block" }, /* @__PURE__ */ React.createElement("span", { className: "pointer-events-none absolute left-4 top-1/2 -translate-y-1/2 text-white/35" }, /* @__PURE__ */ React.createElement("svg", { viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": "true", className: "h-5 w-5" }, /* @__PURE__ */ React.createElement("path", { fillRule: "evenodd", d: "M8.5 3a5.5 5.5 0 1 0 3.473 9.765l3.63 3.63a.75.75 0 1 0 1.06-1.06l-3.63-3.63A5.5 5.5 0 0 0 8.5 3Zm-4 5.5a4 4 0 1 1 8 0 4 4 0 0 1-8 0Z", clipRule: "evenodd" }))), /* @__PURE__ */ React.createElement( + return /* @__PURE__ */ React.createElement("div", { className: "categories-page pb-24 text-white" }, /* @__PURE__ */ React.createElement("section", { className: "relative overflow-hidden" }, /* @__PURE__ */ React.createElement("div", { className: "absolute inset-x-0 top-0 h-[28rem] bg-[radial-gradient(circle_at_top_left,rgba(34,211,238,0.12),transparent_38%),radial-gradient(circle_at_top_right,rgba(249,115,22,0.14),transparent_34%)]" }), /* @__PURE__ */ React.createElement("div", { className: "relative w-full px-6 pb-8 pt-14 sm:px-8 sm:pt-20 xl:px-10 2xl:px-14 lg:pt-24" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-8 lg:grid-cols-[minmax(0,1.2fr)_20rem] lg:items-end" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "inline-flex rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-xs font-semibold uppercase tracking-[0.24em] text-white/50 backdrop-blur-sm" }, "Category directory"), /* @__PURE__ */ React.createElement("h1", { className: "mt-5 max-w-4xl text-4xl font-semibold tracking-[-0.05em] text-white sm:text-5xl lg:text-6xl" }, pageTitle), /* @__PURE__ */ React.createElement("p", { className: "mt-5 max-w-2xl text-base leading-8 text-white/62 sm:text-lg" }, pageDescription || "Browse all wallpapers, skins, themes and digital art categories")), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 sm:grid-cols-3 lg:grid-cols-1" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/22 p-5 backdrop-blur-md shadow-[0_24px_60px_rgba(0,0,0,0.24)]" }, /* @__PURE__ */ React.createElement("p", { className: "text-xs font-semibold uppercase tracking-[0.2em] text-white/40" }, "Categories"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-3xl font-semibold tracking-[-0.04em] text-white" }, numberFormatter$1.format(summary.total_categories))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/22 p-5 backdrop-blur-md shadow-[0_24px_60px_rgba(0,0,0,0.24)]" }, /* @__PURE__ */ React.createElement("p", { className: "text-xs font-semibold uppercase tracking-[0.2em] text-white/40" }, "Artworks indexed"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-3xl font-semibold tracking-[-0.04em] text-white" }, numberFormatter$1.format(summary.total_artworks))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/22 p-5 backdrop-blur-md shadow-[0_24px_60px_rgba(0,0,0,0.24)]" }, /* @__PURE__ */ React.createElement("p", { className: "text-xs font-semibold uppercase tracking-[0.2em] text-white/40" }, "View"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-3xl font-semibold tracking-[-0.04em] text-white" }, "Grid")))), /* @__PURE__ */ React.createElement("div", { className: "mt-10 rounded-[30px] border border-white/10 bg-black/25 p-4 shadow-[0_30px_80px_rgba(0,0,0,0.25)] backdrop-blur-xl sm:p-5" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 lg:grid-cols-[minmax(0,1fr)_16rem] lg:items-center" }, /* @__PURE__ */ React.createElement("label", { className: "relative block" }, /* @__PURE__ */ React.createElement("span", { className: "pointer-events-none absolute left-4 top-1/2 -translate-y-1/2 text-white/35" }, /* @__PURE__ */ React.createElement("svg", { viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": "true", className: "h-5 w-5" }, /* @__PURE__ */ React.createElement("path", { fillRule: "evenodd", d: "M8.5 3a5.5 5.5 0 1 0 3.473 9.765l3.63 3.63a.75.75 0 1 0 1.06-1.06l-3.63-3.63A5.5 5.5 0 0 0 8.5 3Zm-4 5.5a4 4 0 1 1 8 0 4 4 0 0 1-8 0Z", clipRule: "evenodd" }))), /* @__PURE__ */ React.createElement( "input", { type: "search", @@ -75164,7 +75722,7 @@ function CollectionVisibilityBadge({ visibility, className = "" }) { const style = STYLES[value] || STYLES.public; return /* @__PURE__ */ React.createElement("span", { className: `inline-flex items-center rounded-full border px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.18em] ${style} ${className}`.trim() }, label); } -async function requestJson$o(url, { method = "GET", body: body2 } = {}) { +async function requestJson$p(url, { method = "GET", body: body2 } = {}) { const response = await fetch(url, { method, credentials: "same-origin", @@ -75247,7 +75805,7 @@ function CollectionCard({ collection, isOwner, onDelete, onToggleFeature, onMove } setSaveBusy(true); try { - const payload = await requestJson$o(targetUrl, { + const payload = await requestJson$p(targetUrl, { method: saved ? "DELETE" : "POST", body: saved ? void 0 : { context: saveContext, @@ -75390,7 +75948,7 @@ async function fetchSearchResults(baseUrl, filters) { } return payload; } -async function requestJson$n(url, { method = "POST", body: body2 } = {}) { +async function requestJson$o(url, { method = "POST", body: body2 } = {}) { const response = await fetch(url, { method, credentials: "same-origin", @@ -75582,7 +76140,7 @@ function CollectionDashboard() { } setBulkState({ busy: true, error: "", notice: "" }); try { - const response = await requestJson$n(endpoints.bulkActions, { method: "POST", body: payload }); + const response = await requestJson$o(endpoints.bulkActions, { method: "POST", body: payload }); const updates = new Map((Array.isArray(response.collections) ? response.collections : []).map((collection) => [Number(collection.id), collection])); setSearchState((current) => ({ ...current, @@ -75814,7 +76372,7 @@ const __vite_glob_0_43 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de function getCsrfToken$d() { return document.querySelector('meta[name="csrf-token"]')?.getAttribute("content") || ""; } -function formatDateTime$2(value) { +function formatDateTime$5(value) { if (!value) return "Unknown time"; const date = new Date(value); if (Number.isNaN(date.getTime())) return "Unknown time"; @@ -75866,7 +76424,7 @@ function CollectionHistory() { setBusyId(null); } } - return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Se$1, null, /* @__PURE__ */ React.createElement("title", null, seo.title || `${collection.title || "Collection"} History — Skinbase`), /* @__PURE__ */ React.createElement("meta", { name: "description", content: seo.description || "Collection audit history." }), seo.canonical ? /* @__PURE__ */ React.createElement("link", { rel: "canonical", href: seo.canonical }) : null, /* @__PURE__ */ React.createElement("meta", { name: "robots", content: seo.robots || "noindex,follow" })), /* @__PURE__ */ React.createElement("div", { className: "relative min-h-screen overflow-hidden pb-16" }, /* @__PURE__ */ React.createElement("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-x-0 top-0 -z-10 h-[32rem] opacity-95", style: { background: "radial-gradient(circle at 14% 14%, rgba(56,189,248,0.16), transparent 26%), radial-gradient(circle at 84% 20%, rgba(244,63,94,0.14), transparent 24%), linear-gradient(180deg, #07101d 0%, #0a1220 42%, #08111f 100%)" } }), /* @__PURE__ */ React.createElement("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 -z-10 opacity-[0.05]", style: { backgroundImage: "url(/gfx/noise.png)", backgroundSize: "180px" } }), /* @__PURE__ */ React.createElement("div", { className: "mx-auto max-w-6xl px-4 pt-8 md:px-6" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-3 text-sm text-slate-300" }, props.dashboardUrl ? /* @__PURE__ */ React.createElement("a", { href: props.dashboardUrl, className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 transition hover:bg-white/[0.07] hover:text-white" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-left fa-fw text-[11px]" }), "Dashboard") : null, props.analyticsUrl ? /* @__PURE__ */ React.createElement("a", { href: props.analyticsUrl, className: "inline-flex items-center gap-2 rounded-full border border-sky-300/20 bg-sky-400/10 px-4 py-2 font-semibold text-sky-100 transition hover:bg-sky-400/15" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-chart-column fa-fw text-[11px]" }), "Analytics") : null, collection.manage_url ? /* @__PURE__ */ React.createElement("a", { href: collection.manage_url, className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 transition hover:bg-white/[0.07] hover:text-white" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-pen-to-square fa-fw text-[11px]" }), "Manage") : null), /* @__PURE__ */ React.createElement("section", { className: "mt-6 rounded-[34px] border border-white/10 bg-white/[0.04] p-6 shadow-[0_30px_90px_rgba(2,6,23,0.28)] backdrop-blur-sm md:p-8" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Audit"), /* @__PURE__ */ React.createElement("h1", { className: "mt-3 text-4xl font-semibold tracking-[-0.05em] text-white md:text-5xl" }, collection.title || "Collection history"), /* @__PURE__ */ React.createElement("p", { className: "mt-4 max-w-3xl text-sm leading-relaxed text-slate-300 md:text-[15px]" }, "A chronological log of lifecycle transitions, editorial changes, artwork operations, and moderation-adjacent actions for this collection.")), /* @__PURE__ */ React.createElement("section", { className: "mt-8 space-y-4" }, notice ? /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-rose-300/20 bg-rose-500/10 px-5 py-4 text-sm text-rose-100" }, notice) : null, entries.length ? entries.map((entry) => /* @__PURE__ */ React.createElement("article", { key: entry.id, className: "rounded-[30px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-start justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2" }, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-sky-300/20 bg-sky-400/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-sky-100" }, String(entry.action_type || "updated").replace(/_/g, " ")), entry.actor?.username ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-300" }, "@", entry.actor.username) : /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400" }, "System"), entry.can_restore ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-emerald-300/20 bg-emerald-400/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-emerald-100" }, "Restorable") : null), /* @__PURE__ */ React.createElement("h2", { className: "mt-4 text-xl font-semibold text-white" }, entry.summary || "Collection updated"), entry.can_restore && Array.isArray(entry.restore_fields) && entry.restore_fields.length ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-xs uppercase tracking-[0.18em] text-slate-400" }, "Restores: ", entry.restore_fields.join(", ")) : null), /* @__PURE__ */ React.createElement("div", { className: "flex flex-col items-end gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm text-slate-400" }, formatDateTime$2(entry.created_at)), props.canRestoreHistory && entry.can_restore ? /* @__PURE__ */ React.createElement( + return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Se$1, null, /* @__PURE__ */ React.createElement("title", null, seo.title || `${collection.title || "Collection"} History — Skinbase`), /* @__PURE__ */ React.createElement("meta", { name: "description", content: seo.description || "Collection audit history." }), seo.canonical ? /* @__PURE__ */ React.createElement("link", { rel: "canonical", href: seo.canonical }) : null, /* @__PURE__ */ React.createElement("meta", { name: "robots", content: seo.robots || "noindex,follow" })), /* @__PURE__ */ React.createElement("div", { className: "relative min-h-screen overflow-hidden pb-16" }, /* @__PURE__ */ React.createElement("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-x-0 top-0 -z-10 h-[32rem] opacity-95", style: { background: "radial-gradient(circle at 14% 14%, rgba(56,189,248,0.16), transparent 26%), radial-gradient(circle at 84% 20%, rgba(244,63,94,0.14), transparent 24%), linear-gradient(180deg, #07101d 0%, #0a1220 42%, #08111f 100%)" } }), /* @__PURE__ */ React.createElement("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 -z-10 opacity-[0.05]", style: { backgroundImage: "url(/gfx/noise.png)", backgroundSize: "180px" } }), /* @__PURE__ */ React.createElement("div", { className: "mx-auto max-w-6xl px-4 pt-8 md:px-6" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-3 text-sm text-slate-300" }, props.dashboardUrl ? /* @__PURE__ */ React.createElement("a", { href: props.dashboardUrl, className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 transition hover:bg-white/[0.07] hover:text-white" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-left fa-fw text-[11px]" }), "Dashboard") : null, props.analyticsUrl ? /* @__PURE__ */ React.createElement("a", { href: props.analyticsUrl, className: "inline-flex items-center gap-2 rounded-full border border-sky-300/20 bg-sky-400/10 px-4 py-2 font-semibold text-sky-100 transition hover:bg-sky-400/15" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-chart-column fa-fw text-[11px]" }), "Analytics") : null, collection.manage_url ? /* @__PURE__ */ React.createElement("a", { href: collection.manage_url, className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 transition hover:bg-white/[0.07] hover:text-white" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-pen-to-square fa-fw text-[11px]" }), "Manage") : null), /* @__PURE__ */ React.createElement("section", { className: "mt-6 rounded-[34px] border border-white/10 bg-white/[0.04] p-6 shadow-[0_30px_90px_rgba(2,6,23,0.28)] backdrop-blur-sm md:p-8" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Audit"), /* @__PURE__ */ React.createElement("h1", { className: "mt-3 text-4xl font-semibold tracking-[-0.05em] text-white md:text-5xl" }, collection.title || "Collection history"), /* @__PURE__ */ React.createElement("p", { className: "mt-4 max-w-3xl text-sm leading-relaxed text-slate-300 md:text-[15px]" }, "A chronological log of lifecycle transitions, editorial changes, artwork operations, and moderation-adjacent actions for this collection.")), /* @__PURE__ */ React.createElement("section", { className: "mt-8 space-y-4" }, notice ? /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-rose-300/20 bg-rose-500/10 px-5 py-4 text-sm text-rose-100" }, notice) : null, entries.length ? entries.map((entry) => /* @__PURE__ */ React.createElement("article", { key: entry.id, className: "rounded-[30px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-start justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2" }, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-sky-300/20 bg-sky-400/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-sky-100" }, String(entry.action_type || "updated").replace(/_/g, " ")), entry.actor?.username ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-300" }, "@", entry.actor.username) : /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400" }, "System"), entry.can_restore ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-emerald-300/20 bg-emerald-400/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-emerald-100" }, "Restorable") : null), /* @__PURE__ */ React.createElement("h2", { className: "mt-4 text-xl font-semibold text-white" }, entry.summary || "Collection updated"), entry.can_restore && Array.isArray(entry.restore_fields) && entry.restore_fields.length ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-xs uppercase tracking-[0.18em] text-slate-400" }, "Restores: ", entry.restore_fields.join(", ")) : null), /* @__PURE__ */ React.createElement("div", { className: "flex flex-col items-end gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm text-slate-400" }, formatDateTime$5(entry.created_at)), props.canRestoreHistory && entry.can_restore ? /* @__PURE__ */ React.createElement( "button", { type: "button", @@ -75994,7 +76552,7 @@ function firstEntitySelection(options) { id: options?.[firstType]?.[0]?.id || "" }; } -async function requestJson$m(url, { method = "GET", body: body2 } = {}) { +async function requestJson$n(url, { method = "GET", body: body2 } = {}) { const response = await fetch(url, { method, credentials: "same-origin", @@ -76140,10 +76698,10 @@ function buildRuleSummary(rule, smartRuleOptions) { const value = String(rule.value || "").trim() || "Any value"; return `${label} ${rule.operator} ${value}`; } -function Field$3({ label, children, help }) { +function Field$4({ label, children, help }) { return /* @__PURE__ */ React.createElement("label", { className: "block space-y-2" }, /* @__PURE__ */ React.createElement("span", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400" }, label), children, help ? /* @__PURE__ */ React.createElement("p", { className: "text-xs text-slate-500" }, help) : null); } -function StatCard$7({ icon, label, value, tone = "default" }) { +function StatCard$a({ icon, label, value, tone = "default" }) { const toneClass = tone === "accent" ? "border-sky-300/20 bg-sky-400/10 text-sky-100" : "border-white/10 bg-white/[0.04] text-white"; return /* @__PURE__ */ React.createElement("div", { className: `rounded-[24px] border px-4 py-4 ${toneClass}` }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${icon} text-[10px]` }), label), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-2xl font-semibold tracking-[-0.03em]" }, value)); } @@ -76245,7 +76803,7 @@ function LayoutModuleCard({ module, index: index2, total, onToggle, onSlotChange onChange: (event) => onToggle(module.key, event.target.checked), label: module.locked ? "Required" : module.enabled ? "Enabled" : "Disabled" } - ))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 md:grid-cols-[minmax(0,1fr)_auto]" }, /* @__PURE__ */ React.createElement(Field$3, { label: "Placement" }, /* @__PURE__ */ React.createElement( + ))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 md:grid-cols-[minmax(0,1fr)_auto]" }, /* @__PURE__ */ React.createElement(Field$4, { label: "Placement" }, /* @__PURE__ */ React.createElement( NovaSelect, { value: module.slot, @@ -76324,7 +76882,7 @@ function SmartRuleRow({ }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-trash-can fa-fw" }), "Remove" - )), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-4 md:grid-cols-[1fr_180px_minmax(0,1.15fr)]" }, /* @__PURE__ */ React.createElement(Field$3, { label: "Field" }, /* @__PURE__ */ React.createElement( + )), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-4 md:grid-cols-[1fr_180px_minmax(0,1.15fr)]" }, /* @__PURE__ */ React.createElement(Field$4, { label: "Field" }, /* @__PURE__ */ React.createElement( NovaSelect, { value: rule.field, @@ -76332,7 +76890,7 @@ function SmartRuleRow({ options: fieldOptions.map((option) => ({ value: option.value, label: option.label })), searchable: false } - )), /* @__PURE__ */ React.createElement(Field$3, { label: "Operator" }, /* @__PURE__ */ React.createElement( + )), /* @__PURE__ */ React.createElement(Field$4, { label: "Operator" }, /* @__PURE__ */ React.createElement( NovaSelect, { value: rule.operator, @@ -76340,7 +76898,7 @@ function SmartRuleRow({ options: operatorOptions.map((option) => ({ value: option.value, label: option.label })), searchable: false } - )), rule.field === "created_at" ? /* @__PURE__ */ React.createElement(Field$3, { label: "Date Range" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 sm:grid-cols-2" }, /* @__PURE__ */ React.createElement( + )), rule.field === "created_at" ? /* @__PURE__ */ React.createElement(Field$4, { label: "Date Range" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 sm:grid-cols-2" }, /* @__PURE__ */ React.createElement( DateTimePicker, { value: rule.value?.from || "", @@ -76360,7 +76918,7 @@ function SmartRuleRow({ clearable: true, className: "bg-white/[0.04]" } - ))) : rule.field === "is_featured" || rule.field === "is_mature" ? /* @__PURE__ */ React.createElement(Field$3, { label: "Value" }, /* @__PURE__ */ React.createElement( + ))) : rule.field === "is_featured" || rule.field === "is_mature" ? /* @__PURE__ */ React.createElement(Field$4, { label: "Value" }, /* @__PURE__ */ React.createElement( NovaSelect, { value: rule.value ? "true" : "false", @@ -76374,7 +76932,7 @@ function SmartRuleRow({ ], searchable: false } - )) : rule.field === "tags" ? /* @__PURE__ */ React.createElement(Field$3, { label: "Value", help: "Type a tag name exactly as it appears on your artworks." }, /* @__PURE__ */ React.createElement( + )) : rule.field === "tags" ? /* @__PURE__ */ React.createElement(Field$4, { label: "Value", help: "Type a tag name exactly as it appears on your artworks." }, /* @__PURE__ */ React.createElement( "input", { type: "text", @@ -76383,7 +76941,7 @@ function SmartRuleRow({ className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", placeholder: "e.g. dark-fantasy" } - )) : valueOptions.length ? /* @__PURE__ */ React.createElement(Field$3, { label: "Value" }, /* @__PURE__ */ React.createElement( + )) : valueOptions.length ? /* @__PURE__ */ React.createElement(Field$4, { label: "Value" }, /* @__PURE__ */ React.createElement( NovaSelect, { value: rule.value, @@ -76392,7 +76950,7 @@ function SmartRuleRow({ placeholder: "Select one", searchable: false } - )) : /* @__PURE__ */ React.createElement(Field$3, { label: "Value" }, /* @__PURE__ */ React.createElement( + )) : /* @__PURE__ */ React.createElement(Field$4, { label: "Value" }, /* @__PURE__ */ React.createElement( "input", { type: "text", @@ -76755,7 +77313,7 @@ function CollectionManage() { setErrors({}); setNotice(""); try { - const payload = await requestJson$m(mode === "create" ? endpoints.store : endpoints.update, { + const payload = await requestJson$n(mode === "create" ? endpoints.store : endpoints.update, { method: mode === "create" ? "POST" : "PATCH", body: buildPayload2() }); @@ -76790,7 +77348,7 @@ function CollectionManage() { setPreviewing(true); setErrors({}); try { - const payload = await requestJson$m(endpoints.smartPreview, { + const payload = await requestJson$n(endpoints.smartPreview, { method: "POST", body: { smart_rules_json: { @@ -76813,7 +77371,7 @@ function CollectionManage() { setSearching(true); try { const url = `${endpoints.available}?search=${encodeURIComponent(search2)}`; - const payload = await requestJson$m(url); + const payload = await requestJson$n(url); setAvailable(payload?.data || []); } catch (error) { setErrors(error?.payload?.errors || { form: [error.message] }); @@ -76838,7 +77396,7 @@ function CollectionManage() { if (!url) return; setAiState((current) => ({ ...current, busy: kind })); try { - const payload = await requestJson$m(url, { + const payload = await requestJson$n(url, { method: "POST", body: { draft: buildPayload2() } }); @@ -76856,7 +77414,7 @@ function CollectionManage() { if (!endpoints?.aiQualityReview) return; setAiState((current) => ({ ...current, busy: "qualityReview" })); try { - const payload = await requestJson$m(endpoints.aiQualityReview); + const payload = await requestJson$n(endpoints.aiQualityReview); setAiState((current) => ({ ...current, busy: "", @@ -76893,7 +77451,7 @@ function CollectionManage() { setSaving(true); setErrors({}); try { - const payload = await requestJson$m(endpoints.attach, { + const payload = await requestJson$n(endpoints.attach, { method: "POST", body: { artwork_ids: selectedIds } }); @@ -76914,7 +77472,7 @@ function CollectionManage() { setSaving(true); setErrors({}); try { - const payload = await requestJson$m(artwork.remove_url, { method: "DELETE" }); + const payload = await requestJson$n(artwork.remove_url, { method: "DELETE" }); setCollectionState(payload.collection); setAttached(payload.attachedArtworks || []); setAvailable(payload.availableArtworks || []); @@ -76946,7 +77504,7 @@ function CollectionManage() { setSaving(true); setErrors({}); try { - const payload = await requestJson$m(endpoints.reorder, { + const payload = await requestJson$n(endpoints.reorder, { method: "POST", body: { ordered_artwork_ids: attached.map((artwork) => artwork.id) } }); @@ -76967,7 +77525,7 @@ function CollectionManage() { setFeatureBusy(true); setErrors({}); try { - const payload = await requestJson$m(url, { + const payload = await requestJson$n(url, { method: isFeatured ? "DELETE" : "POST" }); if (payload.collection) { @@ -76985,7 +77543,7 @@ function CollectionManage() { setSaving(true); setErrors({}); try { - const payload = await requestJson$m(endpoints.syncLinkedCollections, { + const payload = await requestJson$n(endpoints.syncLinkedCollections, { method: "POST", body: { related_collection_ids: nextIds @@ -77028,7 +77586,7 @@ function CollectionManage() { setSaving(true); setErrors({}); try { - const payload = await requestJson$m(endpoints.syncEntityLinks, { + const payload = await requestJson$n(endpoints.syncEntityLinks, { method: "POST", body: { entity_links: nextLinks @@ -77104,7 +77662,7 @@ function CollectionManage() { setSaving(true); setErrors({}); try { - const payload = await requestJson$m(endpoints.canonicalize, { + const payload = await requestJson$n(endpoints.canonicalize, { method: "POST", body: { target_collection_id: targetId } }); @@ -77122,7 +77680,7 @@ function CollectionManage() { setSaving(true); setErrors({}); try { - const payload = await requestJson$m(endpoints.merge, { + const payload = await requestJson$n(endpoints.merge, { method: "POST", body: { target_collection_id: targetId } }); @@ -77140,7 +77698,7 @@ function CollectionManage() { setSaving(true); setErrors({}); try { - const payload = await requestJson$m(endpoints.rejectDuplicate, { + const payload = await requestJson$n(endpoints.rejectDuplicate, { method: "POST", body: { target_collection_id: targetId } }); @@ -77156,7 +77714,7 @@ function CollectionManage() { if (!window.confirm("Delete this collection? Artworks will remain untouched.")) return; setSaving(true); try { - const payload = await requestJson$m(endpoints.delete, { method: "DELETE" }); + const payload = await requestJson$n(endpoints.delete, { method: "DELETE" }); window.location.assign(payload.redirect); } catch (error) { setErrors(error?.payload?.errors || { form: [error.message] }); @@ -77180,7 +77738,7 @@ function CollectionManage() { setSaving(true); setErrors({}); try { - const payload = await requestJson$m(endpoints.inviteMember, { + const payload = await requestJson$n(endpoints.inviteMember, { method: "POST", body: body2 }); @@ -77198,7 +77756,7 @@ function CollectionManage() { async function handleMemberRoleChange(member, role) { const url = endpoints?.memberUpdatePattern?.replace("__MEMBER__", member.id); if (!url) return; - const payload = await requestJson$m(url, { + const payload = await requestJson$n(url, { method: "PATCH", body: { role } }); @@ -77207,14 +77765,14 @@ function CollectionManage() { async function handleRemoveMember(member) { const url = endpoints?.memberDeletePattern?.replace("__MEMBER__", member.id); if (!url) return; - const payload = await requestJson$m(url, { method: "DELETE" }); + const payload = await requestJson$n(url, { method: "DELETE" }); setMembers(payload?.members || []); } async function handleTransferMember(member) { const url = endpoints?.memberTransferPattern?.replace("__MEMBER__", member.id); if (!url) return; if (!window.confirm(`Transfer collection ownership to @${member?.user?.username}? You will keep editor access.`)) return; - const payload = await requestJson$m(url, { method: "POST" }); + const payload = await requestJson$n(url, { method: "POST" }); applyCollectionPayload(payload?.collection); setMembers(payload?.members || []); setNotice(`Ownership transferred to @${member?.user?.username}.`); @@ -77222,24 +77780,24 @@ function CollectionManage() { async function handleAcceptMember(member) { const url = endpoints?.acceptMemberPattern?.replace("__MEMBER__", member.id); if (!url) return; - const payload = await requestJson$m(url, { method: "POST" }); + const payload = await requestJson$n(url, { method: "POST" }); setMembers(payload?.members || []); } async function handleDeclineMember(member) { const url = endpoints?.declineMemberPattern?.replace("__MEMBER__", member.id); if (!url) return; - const payload = await requestJson$m(url, { method: "POST" }); + const payload = await requestJson$n(url, { method: "POST" }); setMembers(payload?.members || []); } async function handleSubmissionAction(submission, action) { const url = action === "approve" ? endpoints?.submissionApprovePattern?.replace("__SUBMISSION__", submission.id) : action === "reject" ? endpoints?.submissionRejectPattern?.replace("__SUBMISSION__", submission.id) : endpoints?.submissionDeletePattern?.replace("__SUBMISSION__", submission.id); if (!url) return; - const payload = await requestJson$m(url, { method: action === "withdraw" ? "DELETE" : "POST" }); + const payload = await requestJson$n(url, { method: action === "withdraw" ? "DELETE" : "POST" }); setSubmissions(payload?.submissions || []); } async function handleModerationStatusChange(value) { if (!endpoints?.adminModerationUpdate) return; - const payload = await requestJson$m(endpoints.adminModerationUpdate, { + const payload = await requestJson$n(endpoints.adminModerationUpdate, { method: "PATCH", body: { moderation_status: value } }); @@ -77248,7 +77806,7 @@ function CollectionManage() { } async function handleModerationToggle(key, value) { if (!endpoints?.adminInteractionsUpdate) return; - const payload = await requestJson$m(endpoints.adminInteractionsUpdate, { + const payload = await requestJson$n(endpoints.adminInteractionsUpdate, { method: "PATCH", body: { [key]: value } }); @@ -77257,7 +77815,7 @@ function CollectionManage() { } async function handleAdminUnfeature() { if (!endpoints?.adminUnfeature) return; - const payload = await requestJson$m(endpoints.adminUnfeature, { + const payload = await requestJson$n(endpoints.adminUnfeature, { method: "POST" }); applyCollectionPayload(payload?.collection); @@ -77266,7 +77824,7 @@ function CollectionManage() { async function handleAdminRemoveMember(member) { const url = endpoints?.adminMemberRemovePattern?.replace("__MEMBER__", member.id); if (!url) return; - const payload = await requestJson$m(url, { method: "DELETE" }); + const payload = await requestJson$n(url, { method: "DELETE" }); applyCollectionPayload(payload?.collection); setMembers(payload?.members || []); setNotice("Collaborator removed by moderation action."); @@ -77307,7 +77865,7 @@ function CollectionManage() { icon: "fa-wand-magic-sparkles", onClick: () => updateForm("mode", "smart") } - )), /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$3, { label: "Title" }, /* @__PURE__ */ React.createElement( + )), /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$4, { label: "Title" }, /* @__PURE__ */ React.createElement( "input", { type: "text", @@ -77317,7 +77875,7 @@ function CollectionManage() { placeholder: "Dark Fantasy Series", maxLength: 120 } - )), /* @__PURE__ */ React.createElement(Field$3, { label: "Visibility" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: form.visibility, onChange: (val) => updateForm("visibility", val), searchable: false, options: [{ value: "public", label: "Public — visible to everyone" }, { value: "unlisted", label: "Unlisted — accessible by link only" }, { value: "private", label: "Private — only you can see it" }] }))), /* @__PURE__ */ React.createElement(Field$3, { label: "URL Slug", help: "Auto-generated from the title. Edit to customise the collection URL." }, /* @__PURE__ */ React.createElement( + )), /* @__PURE__ */ React.createElement(Field$4, { label: "Visibility" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: form.visibility, onChange: (val) => updateForm("visibility", val), searchable: false, options: [{ value: "public", label: "Public — visible to everyone" }, { value: "unlisted", label: "Unlisted — accessible by link only" }, { value: "private", label: "Private — only you can see it" }] }))), /* @__PURE__ */ React.createElement(Field$4, { label: "URL Slug", help: "Auto-generated from the title. Edit to customise the collection URL." }, /* @__PURE__ */ React.createElement( "input", { type: "text", @@ -77330,7 +77888,7 @@ function CollectionManage() { placeholder: "dark-fantasy-series", maxLength: 140 } - )), /* @__PURE__ */ React.createElement(Field$3, { label: "Summary", help: "A short line shown on collection cards and in search results." }, /* @__PURE__ */ React.createElement( + )), /* @__PURE__ */ React.createElement(Field$4, { label: "Summary", help: "A short line shown on collection cards and in search results." }, /* @__PURE__ */ React.createElement( "input", { type: "text", @@ -77340,7 +77898,7 @@ function CollectionManage() { placeholder: "Best performing sci-fi wallpapers from the last year", maxLength: 320 } - )), /* @__PURE__ */ React.createElement(AdvancedSection, { title: "Description & Presentation", icon: "fa-palette", defaultOpen: mode === "edit" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$3, { label: "Subtitle", help: "Optional short line that sits under the title." }, /* @__PURE__ */ React.createElement( + )), /* @__PURE__ */ React.createElement(AdvancedSection, { title: "Description & Presentation", icon: "fa-palette", defaultOpen: mode === "edit" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$4, { label: "Subtitle", help: "Optional short line that sits under the title." }, /* @__PURE__ */ React.createElement( "input", { type: "text", @@ -77350,7 +77908,7 @@ function CollectionManage() { placeholder: "A moody archive of midnight environments", maxLength: 160 } - )), /* @__PURE__ */ React.createElement(Field$3, { label: "Presentation Style" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: form.presentation_style, onChange: (val) => updateForm("presentation_style", val), searchable: false, options: [{ value: "standard", label: "Standard" }, { value: "editorial_grid", label: "Editorial Grid" }, { value: "hero_grid", label: "Hero Grid" }, { value: "masonry", label: "Masonry" }] }))), /* @__PURE__ */ React.createElement(Field$3, { label: "Description", help: "Describe the mood, focus, or story behind this showcase." }, /* @__PURE__ */ React.createElement( + )), /* @__PURE__ */ React.createElement(Field$4, { label: "Presentation Style" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: form.presentation_style, onChange: (val) => updateForm("presentation_style", val), searchable: false, options: [{ value: "standard", label: "Standard" }, { value: "editorial_grid", label: "Editorial Grid" }, { value: "hero_grid", label: "Hero Grid" }, { value: "masonry", label: "Masonry" }] }))), /* @__PURE__ */ React.createElement(Field$4, { label: "Description", help: "Describe the mood, focus, or story behind this showcase." }, /* @__PURE__ */ React.createElement( "textarea", { value: form.description, @@ -77359,7 +77917,7 @@ function CollectionManage() { placeholder: "A curated selection of pieces that share a common visual language…", maxLength: 1e3 } - )), /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$3, { label: "Emphasis Mode" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: form.emphasis_mode, onChange: (val) => updateForm("emphasis_mode", val), searchable: false, options: [{ value: "cover_heavy", label: "Cover Heavy" }, { value: "balanced", label: "Balanced" }, { value: "artwork_first", label: "Artwork First" }] })), /* @__PURE__ */ React.createElement(Field$3, { label: "Theme" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: form.theme_token, onChange: (val) => updateForm("theme_token", val), searchable: false, options: [{ value: "default", label: "Default" }, { value: "subtle-blue", label: "Subtle Blue" }, { value: "violet", label: "Violet" }, { value: "amber", label: "Amber" }] }))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2" }, !isSmartMode ? /* @__PURE__ */ React.createElement(Field$3, { label: "Sort Order", help: "Manual keeps the display order under your direct control." }, /* @__PURE__ */ React.createElement(NovaSelect, { value: form.sort_mode, onChange: (val) => updateForm("sort_mode", val), searchable: false, options: [{ value: "manual", label: "Manual" }, { value: "newest", label: "Newest first" }, { value: "oldest", label: "Oldest first" }, { value: "popular", label: "Most popular" }] })) : /* @__PURE__ */ React.createElement(Field$3, { label: "Match Mode", help: "All rules must match, or any one rule is enough." }, /* @__PURE__ */ React.createElement( + )), /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$4, { label: "Emphasis Mode" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: form.emphasis_mode, onChange: (val) => updateForm("emphasis_mode", val), searchable: false, options: [{ value: "cover_heavy", label: "Cover Heavy" }, { value: "balanced", label: "Balanced" }, { value: "artwork_first", label: "Artwork First" }] })), /* @__PURE__ */ React.createElement(Field$4, { label: "Theme" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: form.theme_token, onChange: (val) => updateForm("theme_token", val), searchable: false, options: [{ value: "default", label: "Default" }, { value: "subtle-blue", label: "Subtle Blue" }, { value: "violet", label: "Violet" }, { value: "amber", label: "Amber" }] }))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2" }, !isSmartMode ? /* @__PURE__ */ React.createElement(Field$4, { label: "Sort Order", help: "Manual keeps the display order under your direct control." }, /* @__PURE__ */ React.createElement(NovaSelect, { value: form.sort_mode, onChange: (val) => updateForm("sort_mode", val), searchable: false, options: [{ value: "manual", label: "Manual" }, { value: "newest", label: "Newest first" }, { value: "oldest", label: "Oldest first" }, { value: "popular", label: "Most popular" }] })) : /* @__PURE__ */ React.createElement(Field$4, { label: "Match Mode", help: "All rules must match, or any one rule is enough." }, /* @__PURE__ */ React.createElement( NovaSelect, { value: smartRules.match, @@ -77367,7 +77925,7 @@ function CollectionManage() { searchable: false, options: [{ value: "all", label: "All rules" }, { value: "any", label: "Any rule" }] } - )), !isSmartMode ? /* @__PURE__ */ React.createElement(Field$3, { label: "Cover Artwork", help: attachedCoverOptions.length ? "Choose a cover from artworks already attached to this collection." : "Attach artworks first to pick a manual cover." }, /* @__PURE__ */ React.createElement( + )), !isSmartMode ? /* @__PURE__ */ React.createElement(Field$4, { label: "Cover Artwork", help: attachedCoverOptions.length ? "Choose a cover from artworks already attached to this collection." : "Attach artworks first to pick a manual cover." }, /* @__PURE__ */ React.createElement( NovaSelect, { value: String(form.cover_artwork_id || ""), @@ -77376,7 +77934,7 @@ function CollectionManage() { placeholder: "Automatic cover", options: attachedCoverOptions.map((a) => ({ value: String(a.id), label: a.title })) } - )) : /* @__PURE__ */ React.createElement(Field$3, { label: "Smart Sort", help: "How matching artworks should be ordered in this collection." }, /* @__PURE__ */ React.createElement( + )) : /* @__PURE__ */ React.createElement(Field$4, { label: "Smart Sort", help: "How matching artworks should be ordered in this collection." }, /* @__PURE__ */ React.createElement( NovaSelect, { value: smartRules.sort, @@ -77386,7 +77944,7 @@ function CollectionManage() { }, options: smartRuleOptions?.sort_options || [] } - )))), /* @__PURE__ */ React.createElement(AdvancedSection, { title: "Collaboration & Access", icon: "fa-user-group", defaultOpen: mode === "edit" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$3, { label: "Collection Type" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: form.type, onChange: (val) => updateForm("type", val), searchable: false, options: [{ value: "personal", label: "Personal" }, { value: "community", label: "Community" }, { value: "editorial", label: "Editorial" }] })), /* @__PURE__ */ React.createElement(Field$3, { label: "Collaboration Mode" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: form.collaboration_mode, onChange: (val) => updateForm("collaboration_mode", val), searchable: false, options: [{ value: "closed", label: "Closed — curated by you only" }, { value: "invite_only", label: "Invite only" }, { value: "open", label: "Open submissions" }] }))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 md:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-white" }, /* @__PURE__ */ React.createElement(Checkbox, { checked: form.allow_submissions, onChange: (event) => updateForm("allow_submissions", event.target.checked), label: "Allow submissions" })), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-white" }, /* @__PURE__ */ React.createElement(Checkbox, { checked: form.allow_comments, onChange: (event) => updateForm("allow_comments", event.target.checked), label: "Allow comments" })), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-white" }, /* @__PURE__ */ React.createElement(Checkbox, { checked: form.allow_saves, onChange: (event) => updateForm("allow_saves", event.target.checked), label: "Allow saves" })), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-white" }, /* @__PURE__ */ React.createElement(Checkbox, { checked: form.commercial_eligibility, onChange: (event) => updateForm("commercial_eligibility", event.target.checked), label: "Commercially eligible" }))), form.type === "editorial" ? /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-3" }, /* @__PURE__ */ React.createElement(Field$3, { label: "Editorial Owner", help: "Choose whether this editorial lives under the current curator, another staff account, or the system identity." }, /* @__PURE__ */ React.createElement(NovaSelect, { value: form.editorial_owner_mode, onChange: (val) => updateForm("editorial_owner_mode", val), searchable: false, options: [{ value: "creator", label: "Current curator" }, { value: "staff_account", label: "Staff account" }, { value: "system", label: "System editorial identity" }] })), form.editorial_owner_mode === "staff_account" ? /* @__PURE__ */ React.createElement(Field$3, { label: "Staff Account Username", help: "Must be an admin or moderator username." }, /* @__PURE__ */ React.createElement( + )))), /* @__PURE__ */ React.createElement(AdvancedSection, { title: "Collaboration & Access", icon: "fa-user-group", defaultOpen: mode === "edit" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$4, { label: "Collection Type" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: form.type, onChange: (val) => updateForm("type", val), searchable: false, options: [{ value: "personal", label: "Personal" }, { value: "community", label: "Community" }, { value: "editorial", label: "Editorial" }] })), /* @__PURE__ */ React.createElement(Field$4, { label: "Collaboration Mode" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: form.collaboration_mode, onChange: (val) => updateForm("collaboration_mode", val), searchable: false, options: [{ value: "closed", label: "Closed — curated by you only" }, { value: "invite_only", label: "Invite only" }, { value: "open", label: "Open submissions" }] }))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 md:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-white" }, /* @__PURE__ */ React.createElement(Checkbox, { checked: form.allow_submissions, onChange: (event) => updateForm("allow_submissions", event.target.checked), label: "Allow submissions" })), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-white" }, /* @__PURE__ */ React.createElement(Checkbox, { checked: form.allow_comments, onChange: (event) => updateForm("allow_comments", event.target.checked), label: "Allow comments" })), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-white" }, /* @__PURE__ */ React.createElement(Checkbox, { checked: form.allow_saves, onChange: (event) => updateForm("allow_saves", event.target.checked), label: "Allow saves" })), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-white" }, /* @__PURE__ */ React.createElement(Checkbox, { checked: form.commercial_eligibility, onChange: (event) => updateForm("commercial_eligibility", event.target.checked), label: "Commercially eligible" }))), form.type === "editorial" ? /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-3" }, /* @__PURE__ */ React.createElement(Field$4, { label: "Editorial Owner", help: "Choose whether this editorial lives under the current curator, another staff account, or the system identity." }, /* @__PURE__ */ React.createElement(NovaSelect, { value: form.editorial_owner_mode, onChange: (val) => updateForm("editorial_owner_mode", val), searchable: false, options: [{ value: "creator", label: "Current curator" }, { value: "staff_account", label: "Staff account" }, { value: "system", label: "System editorial identity" }] })), form.editorial_owner_mode === "staff_account" ? /* @__PURE__ */ React.createElement(Field$4, { label: "Staff Account Username", help: "Must be an admin or moderator username." }, /* @__PURE__ */ React.createElement( "input", { type: "text", @@ -77396,7 +77954,7 @@ function CollectionManage() { placeholder: "skinbase-editorial", maxLength: 60 } - )) : null, form.editorial_owner_mode === "system" ? /* @__PURE__ */ React.createElement(Field$3, { label: "System Owner Label", help: "Public-facing label for system-owned editorials." }, /* @__PURE__ */ React.createElement( + )) : null, form.editorial_owner_mode === "system" ? /* @__PURE__ */ React.createElement(Field$4, { label: "System Owner Label", help: "Public-facing label for system-owned editorials." }, /* @__PURE__ */ React.createElement( "input", { type: "text", @@ -77406,7 +77964,7 @@ function CollectionManage() { placeholder: "Skinbase Editorial", maxLength: 120 } - )) : null) : null), /* @__PURE__ */ React.createElement(AdvancedSection, { title: "Campaign & Events", icon: "fa-bullhorn", defaultOpen: mode === "edit" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(Field$3, { label: "Event Key", help: "Internal identifier used by discovery and promotion logic." }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.event_key, onChange: (event) => updateForm("event_key", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Event Label" }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.event_label, onChange: (event) => updateForm("event_label", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 120 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Season Key", help: "Groups related collections by season on landing surfaces." }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.season_key, onChange: (event) => updateForm("season_key", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Badge Label", help: "Short public badge on cards and headers." }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.badge_label, onChange: (event) => updateForm("badge_label", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 80 }))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(Field$3, { label: "Campaign Key", help: "Operational identifier for recommendation and placement logic." }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.campaign_key, onChange: (event) => updateForm("campaign_key", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Campaign Label", help: "Public-facing campaign or promotion label." }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.campaign_label, onChange: (event) => updateForm("campaign_label", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 120 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Spotlight Style", help: "Controls the visual frame for the public campaign banner." }, /* @__PURE__ */ React.createElement(NovaSelect, { value: form.spotlight_style, onChange: (val) => updateForm("spotlight_style", val), searchable: false, options: [{ value: "default", label: "Default" }, { value: "editorial", label: "Editorial" }, { value: "seasonal", label: "Seasonal" }, { value: "challenge", label: "Challenge" }, { value: "community", label: "Community" }] })), /* @__PURE__ */ React.createElement(Field$3, { label: "Banner Text", help: "Short line shown in the collection spotlight banner." }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.banner_text, onChange: (event) => updateForm("banner_text", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 200 })))), /* @__PURE__ */ React.createElement(AdvancedSection, { title: "Series", icon: "fa-layer-group", defaultOpen: mode === "edit" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-3" }, /* @__PURE__ */ React.createElement(Field$3, { label: "Series Key", help: "Use the same key across all linked collections in a series." }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.series_key, onChange: (event) => updateForm("series_key", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Series Title", help: "Optional public heading shown for the whole series." }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.series_title, onChange: (event) => updateForm("series_title", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 160 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Series Order", help: "Sequence position for public next & previous navigation." }, /* @__PURE__ */ React.createElement("input", { type: "number", min: "1", max: "9999", value: form.series_order, onChange: (event) => updateForm("series_order", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]" }))), /* @__PURE__ */ React.createElement(Field$3, { label: "Series Description", help: "Optional public intro shown on series landing pages." }, /* @__PURE__ */ React.createElement( + )) : null) : null), /* @__PURE__ */ React.createElement(AdvancedSection, { title: "Campaign & Events", icon: "fa-bullhorn", defaultOpen: mode === "edit" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(Field$4, { label: "Event Key", help: "Internal identifier used by discovery and promotion logic." }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.event_key, onChange: (event) => updateForm("event_key", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$4, { label: "Event Label" }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.event_label, onChange: (event) => updateForm("event_label", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 120 })), /* @__PURE__ */ React.createElement(Field$4, { label: "Season Key", help: "Groups related collections by season on landing surfaces." }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.season_key, onChange: (event) => updateForm("season_key", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$4, { label: "Badge Label", help: "Short public badge on cards and headers." }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.badge_label, onChange: (event) => updateForm("badge_label", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 80 }))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(Field$4, { label: "Campaign Key", help: "Operational identifier for recommendation and placement logic." }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.campaign_key, onChange: (event) => updateForm("campaign_key", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$4, { label: "Campaign Label", help: "Public-facing campaign or promotion label." }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.campaign_label, onChange: (event) => updateForm("campaign_label", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 120 })), /* @__PURE__ */ React.createElement(Field$4, { label: "Spotlight Style", help: "Controls the visual frame for the public campaign banner." }, /* @__PURE__ */ React.createElement(NovaSelect, { value: form.spotlight_style, onChange: (val) => updateForm("spotlight_style", val), searchable: false, options: [{ value: "default", label: "Default" }, { value: "editorial", label: "Editorial" }, { value: "seasonal", label: "Seasonal" }, { value: "challenge", label: "Challenge" }, { value: "community", label: "Community" }] })), /* @__PURE__ */ React.createElement(Field$4, { label: "Banner Text", help: "Short line shown in the collection spotlight banner." }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.banner_text, onChange: (event) => updateForm("banner_text", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 200 })))), /* @__PURE__ */ React.createElement(AdvancedSection, { title: "Series", icon: "fa-layer-group", defaultOpen: mode === "edit" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-3" }, /* @__PURE__ */ React.createElement(Field$4, { label: "Series Key", help: "Use the same key across all linked collections in a series." }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.series_key, onChange: (event) => updateForm("series_key", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$4, { label: "Series Title", help: "Optional public heading shown for the whole series." }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.series_title, onChange: (event) => updateForm("series_title", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 160 })), /* @__PURE__ */ React.createElement(Field$4, { label: "Series Order", help: "Sequence position for public next & previous navigation." }, /* @__PURE__ */ React.createElement("input", { type: "number", min: "1", max: "9999", value: form.series_order, onChange: (event) => updateForm("series_order", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]" }))), /* @__PURE__ */ React.createElement(Field$4, { label: "Series Description", help: "Optional public intro shown on series landing pages." }, /* @__PURE__ */ React.createElement( "textarea", { value: form.series_description, @@ -77414,7 +77972,7 @@ function CollectionManage() { className: "min-h-[96px] w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 400 } - ))), /* @__PURE__ */ React.createElement(AdvancedSection, { title: "Scheduling & Lifecycle", icon: "fa-calendar-days", defaultOpen: mode === "edit" }, /* @__PURE__ */ React.createElement(Field$3, { label: "Lifecycle State", help: "Draft keeps it hidden. Published makes it live. Archived retires it from active surfaces." }, /* @__PURE__ */ React.createElement(NovaSelect, { value: form.lifecycle_state, onChange: (val) => updateForm("lifecycle_state", val), searchable: false, options: [{ value: "draft", label: "Draft" }, { value: "scheduled", label: "Scheduled" }, { value: "published", label: "Published" }, { value: "featured", label: "Featured" }, { value: "archived", label: "Archived" }, { value: "expired", label: "Expired" }] })), /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$3, { label: "Publish At", help: "Leave empty to publish immediately. A future time keeps it off public surfaces until it goes live." }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: form.published_at, onChange: (nextValue) => updateForm("published_at", nextValue), placeholder: "Publish time", clearable: true, className: "bg-white/[0.04]" })), /* @__PURE__ */ React.createElement(Field$3, { label: "Unpublish At", help: "Optional automatic sunset time for seasonal or editorial collections." }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: form.unpublished_at, onChange: (nextValue) => updateForm("unpublished_at", nextValue), placeholder: "Unpublish time", clearable: true, className: "bg-white/[0.04]" }))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$3, { label: "Archive At", help: "Optional timestamp for moving the collection to long-term archive workflows." }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: form.archived_at, onChange: (nextValue) => updateForm("archived_at", nextValue), placeholder: "Archive time", clearable: true, className: "bg-white/[0.04]" })), /* @__PURE__ */ React.createElement(Field$3, { label: "Expire At", help: "Optional hard expiry for promotional or seasonal collections." }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: form.expired_at, onChange: (nextValue) => updateForm("expired_at", nextValue), placeholder: "Expiry time", clearable: true, className: "bg-white/[0.04]" })))), /* @__PURE__ */ React.createElement(AdvancedSection, { title: "Commercial & Administration", icon: "fa-briefcase", defaultOpen: mode === "edit" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(Field$3, { label: "Promotion Tier" }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.promotion_tier, onChange: (event) => updateForm("promotion_tier", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 40 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Monetization Status" }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.monetization_ready_status, onChange: (event) => updateForm("monetization_ready_status", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 40 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Sponsorship Label" }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.sponsorship_label, onChange: (event) => updateForm("sponsorship_label", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 120 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Partner Label" }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.partner_label, onChange: (event) => updateForm("partner_label", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 120 }))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$3, { label: "Brand Safe Status" }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.brand_safe_status, onChange: (event) => updateForm("brand_safe_status", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 40 })), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 self-end rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-white" }, /* @__PURE__ */ React.createElement(Checkbox, { checked: form.analytics_enabled, onChange: (event) => updateForm("analytics_enabled", event.target.checked), label: "Analytics enabled" }))), /* @__PURE__ */ React.createElement(Field$3, { label: "Editorial Notes", help: "Internal editorial context for campaign planning, curation rationale, and staff handoff." }, /* @__PURE__ */ React.createElement( + ))), /* @__PURE__ */ React.createElement(AdvancedSection, { title: "Scheduling & Lifecycle", icon: "fa-calendar-days", defaultOpen: mode === "edit" }, /* @__PURE__ */ React.createElement(Field$4, { label: "Lifecycle State", help: "Draft keeps it hidden. Published makes it live. Archived retires it from active surfaces." }, /* @__PURE__ */ React.createElement(NovaSelect, { value: form.lifecycle_state, onChange: (val) => updateForm("lifecycle_state", val), searchable: false, options: [{ value: "draft", label: "Draft" }, { value: "scheduled", label: "Scheduled" }, { value: "published", label: "Published" }, { value: "featured", label: "Featured" }, { value: "archived", label: "Archived" }, { value: "expired", label: "Expired" }] })), /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$4, { label: "Publish At", help: "Leave empty to publish immediately. A future time keeps it off public surfaces until it goes live." }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: form.published_at, onChange: (nextValue) => updateForm("published_at", nextValue), placeholder: "Publish time", clearable: true, className: "bg-white/[0.04]" })), /* @__PURE__ */ React.createElement(Field$4, { label: "Unpublish At", help: "Optional automatic sunset time for seasonal or editorial collections." }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: form.unpublished_at, onChange: (nextValue) => updateForm("unpublished_at", nextValue), placeholder: "Unpublish time", clearable: true, className: "bg-white/[0.04]" }))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$4, { label: "Archive At", help: "Optional timestamp for moving the collection to long-term archive workflows." }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: form.archived_at, onChange: (nextValue) => updateForm("archived_at", nextValue), placeholder: "Archive time", clearable: true, className: "bg-white/[0.04]" })), /* @__PURE__ */ React.createElement(Field$4, { label: "Expire At", help: "Optional hard expiry for promotional or seasonal collections." }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: form.expired_at, onChange: (nextValue) => updateForm("expired_at", nextValue), placeholder: "Expiry time", clearable: true, className: "bg-white/[0.04]" })))), /* @__PURE__ */ React.createElement(AdvancedSection, { title: "Commercial & Administration", icon: "fa-briefcase", defaultOpen: mode === "edit" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(Field$4, { label: "Promotion Tier" }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.promotion_tier, onChange: (event) => updateForm("promotion_tier", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 40 })), /* @__PURE__ */ React.createElement(Field$4, { label: "Monetization Status" }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.monetization_ready_status, onChange: (event) => updateForm("monetization_ready_status", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 40 })), /* @__PURE__ */ React.createElement(Field$4, { label: "Sponsorship Label" }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.sponsorship_label, onChange: (event) => updateForm("sponsorship_label", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 120 })), /* @__PURE__ */ React.createElement(Field$4, { label: "Partner Label" }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.partner_label, onChange: (event) => updateForm("partner_label", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 120 }))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$4, { label: "Brand Safe Status" }, /* @__PURE__ */ React.createElement("input", { type: "text", value: form.brand_safe_status, onChange: (event) => updateForm("brand_safe_status", event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 40 })), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 self-end rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-white" }, /* @__PURE__ */ React.createElement(Checkbox, { checked: form.analytics_enabled, onChange: (event) => updateForm("analytics_enabled", event.target.checked), label: "Analytics enabled" }))), /* @__PURE__ */ React.createElement(Field$4, { label: "Editorial Notes", help: "Internal editorial context for campaign planning, curation rationale, and staff handoff." }, /* @__PURE__ */ React.createElement( "textarea", { value: form.editorial_notes, @@ -77422,7 +77980,7 @@ function CollectionManage() { className: "min-h-[96px] w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]", maxLength: 2e3 } - )), canModerate ? /* @__PURE__ */ React.createElement(Field$3, { label: "Staff Commercial Notes", help: "Admin-only notes for sponsorship readiness, partner handling, and commercial review." }, /* @__PURE__ */ React.createElement( + )), canModerate ? /* @__PURE__ */ React.createElement(Field$4, { label: "Staff Commercial Notes", help: "Admin-only notes for sponsorship readiness, partner handling, and commercial review." }, /* @__PURE__ */ React.createElement( "textarea", { value: form.staff_commercial_notes, @@ -77469,7 +78027,7 @@ function CollectionManage() { }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${featureBusy ? "fa-circle-notch fa-spin" : collectionState?.is_featured ? "fa-star" : "fa-sparkles"} fa-fw` }), collectionState?.is_featured ? "Remove from Featured" : "Feature this Collection" - ) : null, mode === "edit" && form.visibility !== "public" ? /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-xs leading-relaxed text-slate-400" }, "Only public collections can be featured, liked, or followed publicly.") : null)), /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Live Stats"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid grid-cols-2 gap-3" }, /* @__PURE__ */ React.createElement(StatCard$7, { icon: "fa-images", label: "Artworks", value: (collectionState?.artworks_count ?? attached.length ?? 0).toLocaleString(), tone: "accent" }), /* @__PURE__ */ React.createElement(StatCard$7, { icon: "fa-eye", label: "Views", value: (collectionState?.views_count ?? 0).toLocaleString() }), /* @__PURE__ */ React.createElement(StatCard$7, { icon: "fa-heart", label: "Likes", value: (collectionState?.likes_count ?? 0).toLocaleString() }), /* @__PURE__ */ React.createElement(StatCard$7, { icon: "fa-bell", label: "Followers", value: (collectionState?.followers_count ?? 0).toLocaleString() })), /* @__PURE__ */ React.createElement("div", { className: "mt-3 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-xs leading-relaxed text-slate-400" }, "Shares: ", (collectionState?.shares_count ?? 0).toLocaleString(), " ", collectionState?.last_activity_at ? `• Active ${new Date(collectionState.last_activity_at).toLocaleDateString()}` : ""), /* @__PURE__ */ React.createElement("div", { className: "mt-3 grid grid-cols-2 gap-3" }, /* @__PURE__ */ React.createElement(StatCard$7, { icon: "fa-gauge-high", label: "Quality", value: collectionState?.quality_score != null ? Number(collectionState.quality_score).toFixed(1) : "Pending" }), /* @__PURE__ */ React.createElement(StatCard$7, { icon: "fa-ranking-star", label: "Ranking", value: collectionState?.ranking_score != null ? Number(collectionState.ranking_score).toFixed(1) : "Pending" }))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Series & Campaign"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, "Series: ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, form.series_key || "Not set"), form.series_order ? ` • #${form.series_order}` : ""), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, "Series title: ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, form.series_title || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, "Campaign: ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, form.campaign_label || form.campaign_key || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, "Promotion tier: ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, form.promotion_tier || "None")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, "Brand safety: ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, form.brand_safe_status || "Unset")))), mode === "edit" ? /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Insight Links"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3 text-sm text-slate-300" }, endpoints?.dashboard ? /* @__PURE__ */ React.createElement("a", { href: endpoints.dashboard, className: "flex items-center justify-between rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("span", null, "Portfolio dashboard"), /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-up-right-from-square text-slate-500" })) : null, endpoints?.analytics ? /* @__PURE__ */ React.createElement("a", { href: endpoints.analytics, className: "flex items-center justify-between rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("span", null, "Collection analytics"), /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-up-right-from-square text-slate-500" })) : null, endpoints?.history ? /* @__PURE__ */ React.createElement("a", { href: endpoints.history, className: "flex items-center justify-between rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("span", null, "Audit history"), /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-up-right-from-square text-slate-500" })) : null, endpoints?.staffSurfaces ? /* @__PURE__ */ React.createElement("a", { href: endpoints.staffSurfaces, className: "flex items-center justify-between rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("span", null, "Staff surfaces"), /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-up-right-from-square text-slate-500" })) : null, endpoints?.staffProgramming ? /* @__PURE__ */ React.createElement("a", { href: endpoints.staffProgramming, className: "flex items-center justify-between rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("span", null, "Programming studio"), /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-up-right-from-square text-slate-500" })) : null)) : null, /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Quick Start"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3 text-sm leading-relaxed text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, "Start with just a title — everything else can be filled in later. The advanced sections are there when you need them."), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, "Manual"), " is great for hand-picked storytelling. ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, "Smart"), " keeps a collection up to date automatically using rules you define."))))) : null, mode !== "edit" || activeTab === "artworks" ? isSmartMode ? /* @__PURE__ */ React.createElement("div", { className: "mt-8 grid gap-6 xl:grid-cols-[minmax(0,1.18fr)_420px]" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Smart Builder"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.03em] text-white" }, "Define collection rules"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 max-w-2xl text-sm leading-relaxed text-slate-300" }, "Rules only evaluate your own artworks. This keeps smart collections creator-first and avoids pulling content from other users.")), /* @__PURE__ */ React.createElement( + ) : null, mode === "edit" && form.visibility !== "public" ? /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-xs leading-relaxed text-slate-400" }, "Only public collections can be featured, liked, or followed publicly.") : null)), /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Live Stats"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid grid-cols-2 gap-3" }, /* @__PURE__ */ React.createElement(StatCard$a, { icon: "fa-images", label: "Artworks", value: (collectionState?.artworks_count ?? attached.length ?? 0).toLocaleString(), tone: "accent" }), /* @__PURE__ */ React.createElement(StatCard$a, { icon: "fa-eye", label: "Views", value: (collectionState?.views_count ?? 0).toLocaleString() }), /* @__PURE__ */ React.createElement(StatCard$a, { icon: "fa-heart", label: "Likes", value: (collectionState?.likes_count ?? 0).toLocaleString() }), /* @__PURE__ */ React.createElement(StatCard$a, { icon: "fa-bell", label: "Followers", value: (collectionState?.followers_count ?? 0).toLocaleString() })), /* @__PURE__ */ React.createElement("div", { className: "mt-3 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-xs leading-relaxed text-slate-400" }, "Shares: ", (collectionState?.shares_count ?? 0).toLocaleString(), " ", collectionState?.last_activity_at ? `• Active ${new Date(collectionState.last_activity_at).toLocaleDateString()}` : ""), /* @__PURE__ */ React.createElement("div", { className: "mt-3 grid grid-cols-2 gap-3" }, /* @__PURE__ */ React.createElement(StatCard$a, { icon: "fa-gauge-high", label: "Quality", value: collectionState?.quality_score != null ? Number(collectionState.quality_score).toFixed(1) : "Pending" }), /* @__PURE__ */ React.createElement(StatCard$a, { icon: "fa-ranking-star", label: "Ranking", value: collectionState?.ranking_score != null ? Number(collectionState.ranking_score).toFixed(1) : "Pending" }))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Series & Campaign"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, "Series: ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, form.series_key || "Not set"), form.series_order ? ` • #${form.series_order}` : ""), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, "Series title: ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, form.series_title || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, "Campaign: ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, form.campaign_label || form.campaign_key || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, "Promotion tier: ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, form.promotion_tier || "None")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, "Brand safety: ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, form.brand_safe_status || "Unset")))), mode === "edit" ? /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Insight Links"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3 text-sm text-slate-300" }, endpoints?.dashboard ? /* @__PURE__ */ React.createElement("a", { href: endpoints.dashboard, className: "flex items-center justify-between rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("span", null, "Portfolio dashboard"), /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-up-right-from-square text-slate-500" })) : null, endpoints?.analytics ? /* @__PURE__ */ React.createElement("a", { href: endpoints.analytics, className: "flex items-center justify-between rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("span", null, "Collection analytics"), /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-up-right-from-square text-slate-500" })) : null, endpoints?.history ? /* @__PURE__ */ React.createElement("a", { href: endpoints.history, className: "flex items-center justify-between rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("span", null, "Audit history"), /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-up-right-from-square text-slate-500" })) : null, endpoints?.staffSurfaces ? /* @__PURE__ */ React.createElement("a", { href: endpoints.staffSurfaces, className: "flex items-center justify-between rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("span", null, "Staff surfaces"), /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-up-right-from-square text-slate-500" })) : null, endpoints?.staffProgramming ? /* @__PURE__ */ React.createElement("a", { href: endpoints.staffProgramming, className: "flex items-center justify-between rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("span", null, "Programming studio"), /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-up-right-from-square text-slate-500" })) : null)) : null, /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Quick Start"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3 text-sm leading-relaxed text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, "Start with just a title — everything else can be filled in later. The advanced sections are there when you need them."), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, "Manual"), " is great for hand-picked storytelling. ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, "Smart"), " keeps a collection up to date automatically using rules you define."))))) : null, mode !== "edit" || activeTab === "artworks" ? isSmartMode ? /* @__PURE__ */ React.createElement("div", { className: "mt-8 grid gap-6 xl:grid-cols-[minmax(0,1.18fr)_420px]" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Smart Builder"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.03em] text-white" }, "Define collection rules"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 max-w-2xl text-sm leading-relaxed text-slate-300" }, "Rules only evaluate your own artworks. This keeps smart collections creator-first and avoids pulling content from other users.")), /* @__PURE__ */ React.createElement( "button", { type: "button", @@ -77693,7 +78251,7 @@ function CollectionManage() { }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${featureBusy ? "fa-circle-notch fa-spin" : collectionState?.is_featured ? "fa-star" : "fa-sparkles"} fa-fw` }), collectionState?.is_featured ? "Remove from Featured" : "Feature this Collection" - ) : null)), /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Live Stats"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid grid-cols-2 gap-3" }, /* @__PURE__ */ React.createElement(StatCard$7, { icon: "fa-images", label: "Artworks", value: (collectionState?.artworks_count ?? attached.length ?? 0).toLocaleString(), tone: "accent" }), /* @__PURE__ */ React.createElement(StatCard$7, { icon: "fa-eye", label: "Views", value: (collectionState?.views_count ?? 0).toLocaleString() }), /* @__PURE__ */ React.createElement(StatCard$7, { icon: "fa-heart", label: "Likes", value: (collectionState?.likes_count ?? 0).toLocaleString() }), /* @__PURE__ */ React.createElement(StatCard$7, { icon: "fa-bell", label: "Followers", value: (collectionState?.followers_count ?? 0).toLocaleString() })), /* @__PURE__ */ React.createElement("div", { className: "mt-3 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-xs leading-relaxed text-slate-400" }, "Shares: ", (collectionState?.shares_count ?? 0).toLocaleString(), " ", collectionState?.last_activity_at ? `• Active ${new Date(collectionState.last_activity_at).toLocaleDateString()}` : "")), /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-amber-200/80" }, "Health"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-xl font-semibold tracking-[-0.03em] text-white" }, "Readiness and warnings")), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: handleAiQualityReview, disabled: aiState.busy !== "", className: "inline-flex items-center gap-2 rounded-2xl border border-sky-300/20 bg-sky-400/10 px-3 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-sky-100 transition hover:bg-sky-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${aiState.busy === "qualityReview" ? "fa-circle-notch fa-spin" : "fa-wand-magic-sparkles"} fa-fw` }), aiState.busy === "qualityReview" ? "Reviewing…" : "AI fix suggestions")), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid grid-cols-2 gap-3" }, /* @__PURE__ */ React.createElement(StatCard$7, { icon: "fa-heart-pulse", label: "Health", value: formatStateLabel(collectionState?.health_state), tone: "accent" }), /* @__PURE__ */ React.createElement(StatCard$7, { icon: "fa-clipboard-check", label: "Readiness", value: formatStateLabel(collectionState?.readiness_state) })), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2 text-xs font-semibold uppercase tracking-[0.14em]" }, /* @__PURE__ */ React.createElement("span", { className: `rounded-full border px-3 py-2 ${collectionState?.placement_eligibility ? "border-emerald-300/20 bg-emerald-400/10 text-emerald-100" : "border-rose-300/20 bg-rose-400/10 text-rose-100"}` }, collectionState?.placement_eligibility ? "Placement eligible" : "Placement blocked"), collectionState?.campaign_key ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-2 text-slate-200" }, "Campaign · ", collectionState.campaign_key) : null, collectionState?.program_key ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-2 text-slate-200" }, "Program · ", collectionState.program_key) : null), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid grid-cols-2 gap-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Metadata"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-lg font-semibold text-white" }, collectionState?.metadata_completeness_score !== null && collectionState?.metadata_completeness_score !== void 0 ? Number(collectionState.metadata_completeness_score).toFixed(1) : "N/A")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Editorial"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-lg font-semibold text-white" }, collectionState?.editorial_readiness_score !== null && collectionState?.editorial_readiness_score !== void 0 ? Number(collectionState.editorial_readiness_score).toFixed(1) : "N/A"))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, (collectionState?.health_flags?.length ? collectionState.health_flags : [collectionState?.health_state].filter(Boolean)).map((flag) => { + ) : null)), /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Live Stats"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid grid-cols-2 gap-3" }, /* @__PURE__ */ React.createElement(StatCard$a, { icon: "fa-images", label: "Artworks", value: (collectionState?.artworks_count ?? attached.length ?? 0).toLocaleString(), tone: "accent" }), /* @__PURE__ */ React.createElement(StatCard$a, { icon: "fa-eye", label: "Views", value: (collectionState?.views_count ?? 0).toLocaleString() }), /* @__PURE__ */ React.createElement(StatCard$a, { icon: "fa-heart", label: "Likes", value: (collectionState?.likes_count ?? 0).toLocaleString() }), /* @__PURE__ */ React.createElement(StatCard$a, { icon: "fa-bell", label: "Followers", value: (collectionState?.followers_count ?? 0).toLocaleString() })), /* @__PURE__ */ React.createElement("div", { className: "mt-3 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-xs leading-relaxed text-slate-400" }, "Shares: ", (collectionState?.shares_count ?? 0).toLocaleString(), " ", collectionState?.last_activity_at ? `• Active ${new Date(collectionState.last_activity_at).toLocaleDateString()}` : "")), /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-amber-200/80" }, "Health"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-xl font-semibold tracking-[-0.03em] text-white" }, "Readiness and warnings")), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: handleAiQualityReview, disabled: aiState.busy !== "", className: "inline-flex items-center gap-2 rounded-2xl border border-sky-300/20 bg-sky-400/10 px-3 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-sky-100 transition hover:bg-sky-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${aiState.busy === "qualityReview" ? "fa-circle-notch fa-spin" : "fa-wand-magic-sparkles"} fa-fw` }), aiState.busy === "qualityReview" ? "Reviewing…" : "AI fix suggestions")), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid grid-cols-2 gap-3" }, /* @__PURE__ */ React.createElement(StatCard$a, { icon: "fa-heart-pulse", label: "Health", value: formatStateLabel(collectionState?.health_state), tone: "accent" }), /* @__PURE__ */ React.createElement(StatCard$a, { icon: "fa-clipboard-check", label: "Readiness", value: formatStateLabel(collectionState?.readiness_state) })), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2 text-xs font-semibold uppercase tracking-[0.14em]" }, /* @__PURE__ */ React.createElement("span", { className: `rounded-full border px-3 py-2 ${collectionState?.placement_eligibility ? "border-emerald-300/20 bg-emerald-400/10 text-emerald-100" : "border-rose-300/20 bg-rose-400/10 text-rose-100"}` }, collectionState?.placement_eligibility ? "Placement eligible" : "Placement blocked"), collectionState?.campaign_key ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-2 text-slate-200" }, "Campaign · ", collectionState.campaign_key) : null, collectionState?.program_key ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-2 text-slate-200" }, "Program · ", collectionState.program_key) : null), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid grid-cols-2 gap-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Metadata"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-lg font-semibold text-white" }, collectionState?.metadata_completeness_score !== null && collectionState?.metadata_completeness_score !== void 0 ? Number(collectionState.metadata_completeness_score).toFixed(1) : "N/A")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Editorial"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-lg font-semibold text-white" }, collectionState?.editorial_readiness_score !== null && collectionState?.editorial_readiness_score !== void 0 ? Number(collectionState.editorial_readiness_score).toFixed(1) : "N/A"))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, (collectionState?.health_flags?.length ? collectionState.health_flags : [collectionState?.health_state].filter(Boolean)).map((flag) => { const meta = healthFlagMeta(flag); return /* @__PURE__ */ React.createElement("div", { key: flag, className: `rounded-2xl border px-4 py-3 ${meta.tone}` }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em]" }, meta.label), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-sm leading-relaxed" }, meta.description)); })), aiState?.qualityReview?.missing_metadata?.length ? /* @__PURE__ */ React.createElement("div", { className: "mt-4 rounded-2xl border border-sky-300/20 bg-sky-400/10 px-4 py-3 text-sm text-sky-100" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-sky-100/90" }, "Suggested fixes"), /* @__PURE__ */ React.createElement("div", { className: "mt-2" }, "Focus first on: ", aiState.qualityReview.missing_metadata.join(" • ")), aiState?.qualityReview?.suggested_summary?.summary ? /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-xs text-sky-100/80" }, "Suggested summary: ", aiState.qualityReview.suggested_summary.summary) : null) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-4 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-xs leading-relaxed text-slate-400" }, "Last health check: ", formatDateTimeLabel(collectionState?.last_health_check_at) || "Not recorded yet", /* @__PURE__ */ React.createElement("br", null), "Recommendation refresh: ", formatDateTimeLabel(collectionState?.last_recommendation_refresh_at) || "Not recorded yet")), /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Studio Notes"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3 text-sm leading-relaxed text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, "Use the Details tab for metadata and publishing options, then switch to Artworks for ordering or smart rule management."), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, "Community and editorial collections work best when collaborators, submissions, and moderation are reviewed regularly.")))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-start justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-amber-200/80" }, "Merge Review"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.03em] text-white" }, "Compare duplicate candidates"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 max-w-3xl text-sm leading-relaxed text-slate-300" }, "Review candidate overlaps, choose a canonical destination, merge when the target is final, or dismiss false positives so they stop resurfacing.")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-slate-300" }, duplicateCandidates.length, " active candidate", duplicateCandidates.length === 1 ? "" : "s")), canonicalTarget ? /* @__PURE__ */ React.createElement("div", { className: "mt-6 rounded-[26px] border border-amber-300/20 bg-amber-400/10 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-amber-100" }, "Current canonical target"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-lg font-semibold text-white" }, canonicalTarget.title), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-amber-100/80" }, canonicalTarget.owner?.name || canonicalTarget.owner?.username || "Collection")), canonicalTarget.manage_url ? /* @__PURE__ */ React.createElement("a", { href: canonicalTarget.manage_url, className: "inline-flex items-center gap-2 rounded-2xl border border-white/10 bg-white/[0.06] px-4 py-3 text-sm font-semibold text-white transition hover:bg-white/[0.1]" }, "Open target", /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-up-right-from-square fa-fw text-slate-400" })) : null)) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-6 space-y-4" }, duplicateCandidates.length ? duplicateCandidates.map((candidate) => /* @__PURE__ */ React.createElement("div", { key: `merge-candidate-${candidate.collection?.id}`, className: "rounded-[28px] border border-white/10 bg-white/[0.04] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 xl:grid-cols-[minmax(0,1fr)_320px]" }, /* @__PURE__ */ React.createElement("div", null, candidate.collection ? /* @__PURE__ */ React.createElement(CollectionCard, { collection: candidate.collection, isOwner: true }) : null), /* @__PURE__ */ React.createElement("div", { className: "space-y-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-[#0d1726] px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Compare"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 flex flex-wrap gap-2" }, (candidate.comparison?.match_reasons || []).map((reason) => /* @__PURE__ */ React.createElement("span", { key: reason, className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[10px] font-semibold uppercase tracking-[0.14em] text-slate-300" }, reason.replaceAll("_", " ")))), /* @__PURE__ */ React.createElement("div", { className: "mt-3 grid grid-cols-3 gap-2 text-xs text-slate-400" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-xl border border-white/10 bg-white/[0.04] px-3 py-2" }, "Shared artworks: ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, candidate.comparison?.shared_artworks_count ?? 0)), /* @__PURE__ */ React.createElement("div", { className: "rounded-xl border border-white/10 bg-white/[0.04] px-3 py-2" }, "Current: ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, candidate.comparison?.source_artworks_count ?? 0)), /* @__PURE__ */ React.createElement("div", { className: "rounded-xl border border-white/10 bg-white/[0.04] px-3 py-2" }, "Target: ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, candidate.comparison?.target_artworks_count ?? 0)))), candidate.decision?.action_type ? /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-xs text-slate-400" }, "Latest decision: ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold uppercase tracking-[0.14em] text-white" }, candidate.decision.action_type.replaceAll("_", " ")), candidate.decision.summary ? /* @__PURE__ */ React.createElement("div", { className: "mt-1" }, candidate.decision.summary) : null) : null, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement( @@ -77823,7 +78381,7 @@ function CollectionManage() { onMoveUp: () => moveLayoutModule(index2, -1), onMoveDown: () => moveLayoutModule(index2, 1) } - ))))) : null, mode === "edit" && activeTab === "moderation" ? /* @__PURE__ */ React.createElement("section", { className: "mt-8 rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Moderation"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.03em] text-white" }, "Admin controls"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 max-w-3xl text-sm leading-relaxed text-slate-300" }, "Restrict public visibility, disable risky interactions, unfeature collections, or remove collaborators when a curation surface needs intervention.")), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-5 xl:grid-cols-2" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-white/[0.04] p-5" }, /* @__PURE__ */ React.createElement(Field$3, { label: "Moderation Status" }, /* @__PURE__ */ React.createElement( + ))))) : null, mode === "edit" && activeTab === "moderation" ? /* @__PURE__ */ React.createElement("section", { className: "mt-8 rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Moderation"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold tracking-[-0.03em] text-white" }, "Admin controls"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 max-w-3xl text-sm leading-relaxed text-slate-300" }, "Restrict public visibility, disable risky interactions, unfeature collections, or remove collaborators when a curation surface needs intervention.")), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-5 xl:grid-cols-2" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-white/[0.04] p-5" }, /* @__PURE__ */ React.createElement(Field$4, { label: "Moderation Status" }, /* @__PURE__ */ React.createElement( NovaSelect, { value: collectionState?.moderation_status || "active", @@ -77842,7 +78400,7 @@ const __vite_glob_0_45 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de __proto__: null, default: CollectionManage }, Symbol.toStringTag, { value: "Module" })); -function StatCard$6({ icon, label, value }) { +function StatCard$9({ icon, label, value }) { return /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-white/[0.05] px-4 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${icon} text-[10px]` }), label), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-2xl font-semibold tracking-[-0.03em] text-white" }, value)); } function CollectionSeriesShow() { @@ -77853,7 +78411,7 @@ function CollectionSeriesShow() { const collections = Array.isArray(props.collections) ? props.collections : []; const leadCollection = props.leadCollection || null; const stats = props.stats || {}; - return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(SeoHead, { seo, title: seo.title || `${title} — Skinbase`, description: seo.description || description }), /* @__PURE__ */ React.createElement("div", { className: "relative min-h-screen overflow-hidden pb-16" }, /* @__PURE__ */ React.createElement("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-x-0 top-0 -z-10 h-[36rem] opacity-95", style: { background: "radial-gradient(circle at 10% 15%, rgba(59,130,246,0.18), transparent 28%), radial-gradient(circle at 84% 18%, rgba(34,197,94,0.16), transparent 24%), linear-gradient(180deg, #07101d 0%, #0a1220 42%, #08111f 100%)" } }), /* @__PURE__ */ React.createElement("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 -z-10 opacity-[0.05]", style: { backgroundImage: "url(/gfx/noise.png)", backgroundSize: "180px" } }), /* @__PURE__ */ React.createElement("div", { className: "mx-auto max-w-7xl px-4 pt-8 md:px-6" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("a", { href: "/collections/featured", className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 transition hover:bg-white/[0.07] hover:text-white" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-left fa-fw text-[11px]" }), "Back to collections"), leadCollection?.url ? /* @__PURE__ */ React.createElement("a", { href: leadCollection.url, className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 transition hover:bg-white/[0.07] hover:text-white" }, "Lead collection") : null), /* @__PURE__ */ React.createElement("section", { className: "mt-6 overflow-hidden rounded-[34px] border border-white/10 bg-white/[0.04] p-6 shadow-[0_30px_90px_rgba(2,6,23,0.28)] backdrop-blur-sm md:p-8" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-8 xl:grid-cols-[minmax(0,1.15fr)_400px] xl:items-end" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Series"), /* @__PURE__ */ React.createElement("h1", { className: "mt-3 text-4xl font-semibold tracking-[-0.05em] text-white md:text-5xl" }, title), /* @__PURE__ */ React.createElement("p", { className: "mt-4 max-w-3xl text-sm leading-relaxed text-slate-300 md:text-[15px]" }, description), props.seriesKey ? /* @__PURE__ */ React.createElement("div", { className: "mt-5 inline-flex rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-xs font-semibold uppercase tracking-[0.18em] text-slate-300" }, props.seriesKey) : null), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 sm:grid-cols-3 xl:grid-cols-1" }, /* @__PURE__ */ React.createElement(StatCard$6, { icon: "fa-layer-group", label: "Collections", value: Number(stats.collections || collections.length).toLocaleString() }), /* @__PURE__ */ React.createElement(StatCard$6, { icon: "fa-user-group", label: "Creators", value: Number(stats.owners || 0).toLocaleString() }), /* @__PURE__ */ React.createElement(StatCard$6, { icon: "fa-images", label: "Artworks", value: Number(stats.artworks || 0).toLocaleString() })))), leadCollection ? /* @__PURE__ */ React.createElement("section", { className: "mt-8 rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-end justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Lead Entry"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Start with the opening collection")), stats.latest_activity_at ? /* @__PURE__ */ React.createElement("div", { className: "text-xs uppercase tracking-[0.16em] text-slate-400" }, "Latest activity ", new Date(stats.latest_activity_at).toLocaleDateString()) : null), /* @__PURE__ */ React.createElement("div", { className: "mt-5 max-w-xl" }, /* @__PURE__ */ React.createElement(CollectionCard, { collection: leadCollection, isOwner: false }))) : null, /* @__PURE__ */ React.createElement("section", { className: "mt-8 pb-8" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-end justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Sequence"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Public collections in order"))), /* @__PURE__ */ React.createElement("div", { className: "mt-5 grid grid-cols-1 gap-5 md:grid-cols-2 xl:grid-cols-3" }, collections.map((collection) => /* @__PURE__ */ React.createElement(CollectionCard, { key: collection.id, collection, isOwner: false }))))))); + return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(SeoHead, { seo, title: seo.title || `${title} — Skinbase`, description: seo.description || description }), /* @__PURE__ */ React.createElement("div", { className: "relative min-h-screen overflow-hidden pb-16" }, /* @__PURE__ */ React.createElement("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-x-0 top-0 -z-10 h-[36rem] opacity-95", style: { background: "radial-gradient(circle at 10% 15%, rgba(59,130,246,0.18), transparent 28%), radial-gradient(circle at 84% 18%, rgba(34,197,94,0.16), transparent 24%), linear-gradient(180deg, #07101d 0%, #0a1220 42%, #08111f 100%)" } }), /* @__PURE__ */ React.createElement("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 -z-10 opacity-[0.05]", style: { backgroundImage: "url(/gfx/noise.png)", backgroundSize: "180px" } }), /* @__PURE__ */ React.createElement("div", { className: "mx-auto max-w-7xl px-4 pt-8 md:px-6" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("a", { href: "/collections/featured", className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 transition hover:bg-white/[0.07] hover:text-white" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-left fa-fw text-[11px]" }), "Back to collections"), leadCollection?.url ? /* @__PURE__ */ React.createElement("a", { href: leadCollection.url, className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 transition hover:bg-white/[0.07] hover:text-white" }, "Lead collection") : null), /* @__PURE__ */ React.createElement("section", { className: "mt-6 overflow-hidden rounded-[34px] border border-white/10 bg-white/[0.04] p-6 shadow-[0_30px_90px_rgba(2,6,23,0.28)] backdrop-blur-sm md:p-8" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-8 xl:grid-cols-[minmax(0,1.15fr)_400px] xl:items-end" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Series"), /* @__PURE__ */ React.createElement("h1", { className: "mt-3 text-4xl font-semibold tracking-[-0.05em] text-white md:text-5xl" }, title), /* @__PURE__ */ React.createElement("p", { className: "mt-4 max-w-3xl text-sm leading-relaxed text-slate-300 md:text-[15px]" }, description), props.seriesKey ? /* @__PURE__ */ React.createElement("div", { className: "mt-5 inline-flex rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-xs font-semibold uppercase tracking-[0.18em] text-slate-300" }, props.seriesKey) : null), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 sm:grid-cols-3 xl:grid-cols-1" }, /* @__PURE__ */ React.createElement(StatCard$9, { icon: "fa-layer-group", label: "Collections", value: Number(stats.collections || collections.length).toLocaleString() }), /* @__PURE__ */ React.createElement(StatCard$9, { icon: "fa-user-group", label: "Creators", value: Number(stats.owners || 0).toLocaleString() }), /* @__PURE__ */ React.createElement(StatCard$9, { icon: "fa-images", label: "Artworks", value: Number(stats.artworks || 0).toLocaleString() })))), leadCollection ? /* @__PURE__ */ React.createElement("section", { className: "mt-8 rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-end justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Lead Entry"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Start with the opening collection")), stats.latest_activity_at ? /* @__PURE__ */ React.createElement("div", { className: "text-xs uppercase tracking-[0.16em] text-slate-400" }, "Latest activity ", new Date(stats.latest_activity_at).toLocaleDateString()) : null), /* @__PURE__ */ React.createElement("div", { className: "mt-5 max-w-xl" }, /* @__PURE__ */ React.createElement(CollectionCard, { collection: leadCollection, isOwner: false }))) : null, /* @__PURE__ */ React.createElement("section", { className: "mt-8 pb-8" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-end justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Sequence"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Public collections in order"))), /* @__PURE__ */ React.createElement("div", { className: "mt-5 grid grid-cols-1 gap-5 md:grid-cols-2 xl:grid-cols-3" }, collections.map((collection) => /* @__PURE__ */ React.createElement(CollectionCard, { key: collection.id, collection, isOwner: false }))))))); } const __vite_glob_0_46 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, @@ -77979,7 +78537,7 @@ async function sendFeedbackSignal(endpoint, payload) { } return response.json().catch(() => null); } -async function requestJson$l(endpoint, { method = "GET", body: body2 } = {}) { +async function requestJson$m(endpoint, { method = "GET", body: body2 } = {}) { const response = await fetch(endpoint, { method, credentials: "same-origin", @@ -78041,7 +78599,7 @@ function ActionLink$1({ href, label, children, onClick }) { href: href || "#", "aria-label": label, onClick, - className: "inline-flex h-10 w-10 items-center justify-center rounded-lg border border-white/10 bg-white/10 text-white/90 shadow-[0_14px_36px_rgba(2,6,23,0.38)] backdrop-blur-md transition duration-200 hover:bg-white/20 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-300/80" + className: "artwork-card__action-btn inline-flex h-10 w-10 items-center justify-center rounded-lg border border-white/10 bg-white/10 text-white/90 shadow-[0_14px_36px_rgba(2,6,23,0.38)] backdrop-blur-md transition duration-200 hover:bg-white/20 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-300/80" }, children ); @@ -78053,7 +78611,7 @@ function ActionButton({ label, children, onClick }) { type: "button", "aria-label": label, onClick, - className: "inline-flex h-10 w-10 items-center justify-center rounded-lg border border-white/10 bg-white/10 text-white/90 shadow-[0_14px_36px_rgba(2,6,23,0.38)] backdrop-blur-md transition duration-200 hover:bg-white/20 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-300/80" + className: "artwork-card__action-btn inline-flex h-10 w-10 items-center justify-center rounded-lg border border-white/10 bg-white/10 text-white/90 shadow-[0_14px_36px_rgba(2,6,23,0.38)] backdrop-blur-md transition duration-200 hover:bg-white/20 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-300/80" }, children ); @@ -78376,7 +78934,7 @@ function ArtworkCard$1({ } setCollectionOptionsLoading(true); try { - const payload = await requestJson$l(collectionOptionsEndpoint); + const payload = await requestJson$m(collectionOptionsEndpoint); setCollectionOptions(Array.isArray(payload?.data) ? payload.data : []); setCollectionCreateUrl(payload?.meta?.create_url || "/settings/collections/create"); setCollectionOptionsLoaded(true); @@ -78392,7 +78950,7 @@ function ArtworkCard$1({ setCollectionPickerError(""); setCollectionPickerNotice(""); try { - await requestJson$l(collection.attach_url, { + await requestJson$m(collection.attach_url, { method: "POST", body: { artwork_ids: [Number(item.id)] } }); @@ -78453,7 +79011,7 @@ function ArtworkCard$1({ style: articleStyle, ...articleData }, - /* @__PURE__ */ React.createElement("div", { className: cx$6("relative overflow-hidden rounded-[1.6rem] border border-white/8 bg-slate-950/80 shadow-[0_18px_60px_rgba(2,6,23,0.45)] transition duration-300 ease-out group-hover:-translate-y-1 group-hover:scale-[1.02] group-hover:border-white/14 group-hover:shadow-[0_24px_80px_rgba(8,47,73,0.5)] group-focus-within:-translate-y-1 group-focus-within:scale-[1.02] group-focus-within:border-sky-200/40", frameClassName) }, /* @__PURE__ */ React.createElement( + /* @__PURE__ */ React.createElement("div", { className: cx$6("artwork-card__frame relative overflow-hidden rounded-[1.6rem] border border-white/8 bg-slate-950/80 shadow-[0_18px_60px_rgba(2,6,23,0.45)] transition duration-300 ease-out group-hover:-translate-y-1 group-hover:scale-[1.02] group-hover:border-white/14 group-hover:shadow-[0_24px_80px_rgba(8,47,73,0.5)] group-focus-within:-translate-y-1 group-focus-within:scale-[1.02] group-focus-within:border-sky-200/40", frameClassName) }, /* @__PURE__ */ React.createElement( "a", { href, @@ -78462,7 +79020,7 @@ function ArtworkCard$1({ className: "absolute inset-0 z-10 rounded-[inherit] cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-300/80" }, /* @__PURE__ */ React.createElement("span", { className: "sr-only" }, cardLabel) - ), /* @__PURE__ */ React.createElement("div", { className: cx$6("relative overflow-hidden bg-slate-900", aspectClass, mediaClassName), style: mediaStyle }, /* @__PURE__ */ React.createElement("div", { className: "absolute inset-0 bg-[radial-gradient(circle_at_top,_rgba(148,163,184,0.28),_transparent_52%),linear-gradient(180deg,rgba(15,23,42,0.08),rgba(15,23,42,0.42))]" }), /* @__PURE__ */ React.createElement( + ), /* @__PURE__ */ React.createElement("div", { className: cx$6("artwork-card__media relative overflow-hidden bg-slate-900", aspectClass, mediaClassName), style: mediaStyle }, /* @__PURE__ */ React.createElement("div", { className: "artwork-card__media-glow absolute inset-0 bg-[radial-gradient(circle_at_top,_rgba(148,163,184,0.28),_transparent_52%),linear-gradient(180deg,rgba(15,23,42,0.08),rgba(15,23,42,0.42))]" }), /* @__PURE__ */ React.createElement( "img", { src: image2, @@ -78479,10 +79037,10 @@ function ArtworkCard$1({ swapImageToFallbackOnce(event, IMAGE_FALLBACK, { clearResponsive: true }); } } - ), /* @__PURE__ */ React.createElement("div", { className: "pointer-events-none absolute inset-0 bg-gradient-to-t from-black/85 via-black/38 to-transparent opacity-90 transition duration-300 md:opacity-45 md:group-hover:opacity-100 md:group-focus-within:opacity-100" }), resolvedMetricBadge?.label || relativePublishedAt ? /* @__PURE__ */ React.createElement("div", { className: "pointer-events-none absolute inset-x-0 top-0 z-20 flex items-start justify-between gap-3 p-3" }, /* @__PURE__ */ React.createElement("div", null, resolvedMetricBadge?.label ? /* @__PURE__ */ React.createElement(BadgePill, { className: resolvedMetricBadge.className || "bg-emerald-500/14 text-emerald-200 ring-emerald-400/30", iconClass: resolvedMetricBadge.iconClass }, resolvedMetricBadge.label) : null, isMatureArtwork ? /* @__PURE__ */ React.createElement(BadgePill, { className: "mt-2 bg-amber-500/16 text-amber-100 ring-amber-300/30", iconClass: "fa-solid fa-triangle-exclamation text-[10px]" }, "Mature content") : null), relativePublishedAt ? /* @__PURE__ */ React.createElement(BadgePill, { className: "bg-black/45 text-white/75 ring-white/12", iconClass: "fa-regular fa-clock text-[10px]" }, relativePublishedAt) : null) : null, showActions && /* @__PURE__ */ React.createElement("div", { className: cx$6( - "absolute right-3 z-20 flex max-w-[14rem] flex-wrap justify-end translate-y-2 gap-2 opacity-100 transition duration-200 md:opacity-0 md:group-hover:translate-y-0 md:group-hover:opacity-100 md:group-focus-within:translate-y-0 md:group-focus-within:opacity-100", + ), /* @__PURE__ */ React.createElement("div", { className: "artwork-card__media-shade pointer-events-none absolute inset-0 bg-gradient-to-t from-black/85 via-black/38 to-transparent opacity-90 transition duration-300 md:opacity-45 md:group-hover:opacity-100 md:group-focus-within:opacity-100" }), resolvedMetricBadge?.label || relativePublishedAt ? /* @__PURE__ */ React.createElement("div", { className: "pointer-events-none absolute inset-x-0 top-0 z-20 flex items-start justify-between gap-3 p-3" }, /* @__PURE__ */ React.createElement("div", null, resolvedMetricBadge?.label ? /* @__PURE__ */ React.createElement(BadgePill, { className: resolvedMetricBadge.className || "bg-emerald-500/14 text-emerald-200 ring-emerald-400/30", iconClass: resolvedMetricBadge.iconClass }, resolvedMetricBadge.label) : null, isMatureArtwork ? /* @__PURE__ */ React.createElement(BadgePill, { className: "mt-2 bg-amber-500/16 text-amber-100 ring-amber-300/30", iconClass: "fa-solid fa-triangle-exclamation text-[10px]" }, "Mature content") : null), relativePublishedAt ? /* @__PURE__ */ React.createElement(BadgePill, { className: "bg-black/45 text-white/75 ring-white/12", iconClass: "fa-regular fa-clock text-[10px]" }, relativePublishedAt) : null) : null, showActions && /* @__PURE__ */ React.createElement("div", { className: cx$6( + "artwork-card__actions absolute right-3 z-20 flex max-w-[14rem] flex-wrap justify-end translate-y-2 gap-2 opacity-100 transition duration-200 md:opacity-0 md:group-hover:translate-y-0 md:group-hover:opacity-100 md:group-focus-within:translate-y-0 md:group-focus-within:opacity-100", relativePublishedAt ? "top-12" : "top-3" - ) }, /* @__PURE__ */ React.createElement(ActionButton, { label: liked ? "Unlike artwork" : "Like artwork", onClick: handleLike }, /* @__PURE__ */ React.createElement(HeartIcon, { className: cx$6("h-4 w-4 transition-transform duration-200", liked ? "fill-current text-rose-300" : "", likeBusy ? "scale-90" : "") })), /* @__PURE__ */ React.createElement(ActionLink$1, { href: downloadHref, label: downloadBusy ? "Downloading artwork" : "Download artwork", onClick: handleDownload }, /* @__PURE__ */ React.createElement(DownloadIcon, { className: cx$6("h-4 w-4", downloadBusy ? "animate-pulse text-emerald-300" : "") })), /* @__PURE__ */ React.createElement(ActionLink$1, { href, label: "View artwork" }, /* @__PURE__ */ React.createElement(ViewIcon, { className: "h-4 w-4" })), canAddToCollection ? /* @__PURE__ */ React.createElement(ActionButton, { label: "Add artwork to collection", onClick: handleOpenCollectionPicker }, /* @__PURE__ */ React.createElement(CollectionIcon, { className: "h-4 w-4" })) : null, canHideRecommendation ? /* @__PURE__ */ React.createElement(ActionButton, { label: hideBusy ? "Hiding artwork" : "Hide artwork", onClick: handleHideArtwork }, /* @__PURE__ */ React.createElement(HideIcon, { className: cx$6("h-4 w-4", hideBusy ? "animate-pulse text-amber-200" : "text-white/90") })) : null), /* @__PURE__ */ React.createElement("div", { className: "pointer-events-none absolute inset-x-0 bottom-0 z-20 bg-gradient-to-t from-black/80 via-black/40 to-transparent p-3 backdrop-blur-[2px] opacity-100 transition-opacity duration-200 md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100" }, shouldBlurMature ? /* @__PURE__ */ React.createElement("div", { className: "mb-2 inline-flex rounded-full border border-amber-300/20 bg-black/55 px-3 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-amber-100" }, "Blurred by your content settings") : null, /* @__PURE__ */ React.createElement("h3", { className: cx$6("truncate font-semibold text-white", titleClass) }, title), showAuthor ? /* @__PURE__ */ React.createElement("div", { className: "mt-1 flex items-start justify-between gap-3 text-xs text-white/80" }, /* @__PURE__ */ React.createElement("span", { className: "flex min-w-0 items-start gap-3" }, /* @__PURE__ */ React.createElement( + ) }, /* @__PURE__ */ React.createElement(ActionButton, { label: liked ? "Unlike artwork" : "Like artwork", onClick: handleLike }, /* @__PURE__ */ React.createElement(HeartIcon, { className: cx$6("h-4 w-4 transition-transform duration-200", liked ? "fill-current text-rose-300" : "", likeBusy ? "scale-90" : "") })), /* @__PURE__ */ React.createElement(ActionLink$1, { href: downloadHref, label: downloadBusy ? "Downloading artwork" : "Download artwork", onClick: handleDownload }, /* @__PURE__ */ React.createElement(DownloadIcon, { className: cx$6("h-4 w-4", downloadBusy ? "animate-pulse text-emerald-300" : "") })), /* @__PURE__ */ React.createElement(ActionLink$1, { href, label: "View artwork" }, /* @__PURE__ */ React.createElement(ViewIcon, { className: "h-4 w-4" })), canAddToCollection ? /* @__PURE__ */ React.createElement(ActionButton, { label: "Add artwork to collection", onClick: handleOpenCollectionPicker }, /* @__PURE__ */ React.createElement(CollectionIcon, { className: "h-4 w-4" })) : null, canHideRecommendation ? /* @__PURE__ */ React.createElement(ActionButton, { label: hideBusy ? "Hiding artwork" : "Hide artwork", onClick: handleHideArtwork }, /* @__PURE__ */ React.createElement(HideIcon, { className: cx$6("h-4 w-4", hideBusy ? "animate-pulse text-amber-200" : "text-white/90") })) : null), /* @__PURE__ */ React.createElement("div", { className: "artwork-card__meta pointer-events-none absolute inset-x-0 bottom-0 z-20 bg-gradient-to-t from-black/80 via-black/40 to-transparent p-3 backdrop-blur-[2px] opacity-100 transition-opacity duration-200 md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100" }, shouldBlurMature ? /* @__PURE__ */ React.createElement("div", { className: "mb-2 inline-flex rounded-full border border-amber-300/20 bg-black/55 px-3 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-amber-100" }, "Blurred by your content settings") : null, /* @__PURE__ */ React.createElement("h3", { className: cx$6("truncate font-semibold text-white", titleClass) }, title), showAuthor ? /* @__PURE__ */ React.createElement("div", { className: "mt-1 flex items-start justify-between gap-3 text-xs text-white/80" }, /* @__PURE__ */ React.createElement("span", { className: "flex min-w-0 items-start gap-3" }, /* @__PURE__ */ React.createElement( "img", { src: avatar, @@ -78946,7 +79504,7 @@ function getCsrfToken$9() { if (typeof document === "undefined") return ""; return document.querySelector('meta[name="csrf-token"]')?.getAttribute("content") || ""; } -async function requestJson$k(url, { method = "POST", body: body2 } = {}) { +async function requestJson$l(url, { method = "POST", body: body2 } = {}) { const response = await fetch(url, { method, credentials: "same-origin", @@ -79399,7 +79957,7 @@ function CollectionShow() { } setState((current) => ({ ...current, busy: true, notice: "" })); try { - const payload = await requestJson$k(state.liked ? engagement.unlike_url : engagement.like_url, { + const payload = await requestJson$l(state.liked ? engagement.unlike_url : engagement.like_url, { method: state.liked ? "DELETE" : "POST" }); setState((current) => ({ ...current, liked: Boolean(payload?.liked), busy: false })); @@ -79415,7 +79973,7 @@ function CollectionShow() { } setState((current) => ({ ...current, busy: true, notice: "" })); try { - const payload = await requestJson$k(state.following ? engagement.unfollow_url : engagement.follow_url, { + const payload = await requestJson$l(state.following ? engagement.unfollow_url : engagement.follow_url, { method: state.following ? "DELETE" : "POST" }); setState((current) => ({ ...current, following: Boolean(payload?.following), busy: false })); @@ -79426,7 +79984,7 @@ function CollectionShow() { } async function handleShare() { try { - const payload = await requestJson$k(engagement?.share_url, { method: "POST" }); + const payload = await requestJson$l(engagement?.share_url, { method: "POST" }); setCollection((current) => ({ ...current, shares_count: payload?.shares_count ?? current.shares_count })); await share({ title: collection?.title, @@ -79444,7 +80002,7 @@ function CollectionShow() { } setState((current) => ({ ...current, busy: true, notice: "" })); try { - const payload = await requestJson$k(state.saved ? engagement.unsave_url : engagement.save_url, { + const payload = await requestJson$l(state.saved ? engagement.unsave_url : engagement.save_url, { method: state.saved ? "DELETE" : "POST", body: state.saved ? void 0 : { context: "collection_detail", @@ -79461,7 +80019,7 @@ function CollectionShow() { } } async function handleCommentSubmit(body2) { - const payload = await requestJson$k(commentsEndpoint, { + const payload = await requestJson$l(commentsEndpoint, { method: "POST", body: { body: body2 } }); @@ -79469,14 +80027,14 @@ function CollectionShow() { setCollection((current) => ({ ...current, comments_count: payload?.comments_count ?? current.comments_count })); } async function handleDeleteComment(commentId) { - const payload = await requestJson$k(`${commentsEndpoint}/${commentId}`, { method: "DELETE" }); + const payload = await requestJson$l(`${commentsEndpoint}/${commentId}`, { method: "DELETE" }); setComments(payload?.comments || []); setCollection((current) => ({ ...current, comments_count: payload?.comments_count ?? current.comments_count })); } async function handleSubmitArtwork() { if (!submitEndpoint || !selectedArtworkId) return; try { - const payload = await requestJson$k(submitEndpoint, { + const payload = await requestJson$l(submitEndpoint, { method: "POST", body: { artwork_id: selectedArtworkId } }); @@ -79488,7 +80046,7 @@ function CollectionShow() { } async function handleSubmissionAction(submission, action) { const url = action === "approve" ? `/collections/submissions/${submission.id}/approve` : action === "reject" ? `/collections/submissions/${submission.id}/reject` : `/collections/submissions/${submission.id}`; - const payload = await requestJson$k(url, { + const payload = await requestJson$l(url, { method: action === "withdraw" ? "DELETE" : "POST" }); setSubmissions(payload?.submissions || []); @@ -79504,7 +80062,7 @@ function CollectionShow() { const reason = window.prompt("Why are you reporting this? (required)"); if (!reason || !reason.trim()) return; try { - await requestJson$k(reportEndpoint, { + await requestJson$l(reportEndpoint, { method: "POST", body: { target_type: targetType, @@ -79573,7 +80131,7 @@ function getCsrfToken$8() { if (typeof document === "undefined") return ""; return document.querySelector('meta[name="csrf-token"]')?.getAttribute("content") || ""; } -async function requestJson$j(url, { method = "POST", body: body2 } = {}) { +async function requestJson$k(url, { method = "POST", body: body2 } = {}) { const response = await fetch(url, { method, credentials: "same-origin", @@ -79601,10 +80159,10 @@ function isoToLocalInput$1(value) { function titleize(value) { return String(value || "").split("_").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" "); } -function Field$2({ label, help, children }) { +function Field$3({ label, help, children }) { return /* @__PURE__ */ React.createElement("label", { className: "block space-y-2" }, /* @__PURE__ */ React.createElement("span", { className: "text-sm font-semibold text-white" }, label), children, help ? /* @__PURE__ */ React.createElement("span", { className: "block text-xs leading-relaxed text-slate-400" }, help) : null); } -function StatCard$5({ label, value, tone = "sky" }) { +function StatCard$8({ label, value, tone = "sky" }) { const toneClasses2 = { sky: "border-sky-300/15 bg-sky-400/10 text-sky-100", amber: "border-amber-300/15 bg-amber-400/10 text-amber-100", @@ -79785,7 +80343,7 @@ function CollectionStaffProgramming() { setNotice(""); try { const url = assignmentForm.id ? endpoints.updatePattern?.replace("__PROGRAM__", String(assignmentForm.id)) : endpoints.store; - const payload = await requestJson$j(url, { + const payload = await requestJson$k(url, { method: assignmentForm.id ? "PATCH" : "POST", body: { collection_id: Number(assignmentForm.collection_id), @@ -79812,7 +80370,7 @@ function CollectionStaffProgramming() { setBusy("preview"); setNotice(""); try { - const payload = await requestJson$j(endpoints.preview, { + const payload = await requestJson$k(endpoints.preview, { method: "POST", body: { program_key: previewForm.program_key, @@ -79840,7 +80398,7 @@ function CollectionStaffProgramming() { setBusy(kind); setNotice(""); try { - const payload = await requestJson$j(url, { + const payload = await requestJson$k(url, { method: "POST", body: { collection_id: selectedCollectionId ? Number(selectedCollectionId) : null @@ -79879,7 +80437,7 @@ function CollectionStaffProgramming() { } setQueueBusy((current) => ({ ...current, [item.id]: kind })); try { - const payload = await requestJson$j(url, { + const payload = await requestJson$k(url, { method: "POST", body: { source_collection_id: Number(sourceId), @@ -79908,7 +80466,7 @@ function CollectionStaffProgramming() { setBusy("hooks"); setNotice(""); try { - const payload = await requestJson$j(endpoints.metadataUpdate, { + const payload = await requestJson$k(endpoints.metadataUpdate, { method: "POST", body: { collection_id: Number(selectedCollectionId), @@ -79955,11 +80513,11 @@ function CollectionStaffProgramming() { duration: toast.variant === "error" ? 3200 : 2200, onHide: () => setToast((current) => ({ ...current, visible: false })) } - ), /* @__PURE__ */ React.createElement("div", { className: "relative min-h-screen overflow-hidden pb-16" }, /* @__PURE__ */ React.createElement("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-x-0 top-0 -z-10 h-[34rem] opacity-95", style: { background: "radial-gradient(circle at 18% 15%, rgba(56,189,248,0.18), transparent 28%), radial-gradient(circle at 85% 14%, rgba(132,204,22,0.16), transparent 26%), linear-gradient(180deg, #07101d 0%, #0a1220 42%, #08111f 100%)" } }), /* @__PURE__ */ React.createElement("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 -z-10 opacity-[0.05]", style: { backgroundImage: "url(/gfx/noise.png)", backgroundSize: "180px" } }), /* @__PURE__ */ React.createElement("div", { className: "mx-auto max-w-7xl px-4 pt-8 md:px-6" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[34px] border border-white/10 bg-white/[0.04] p-6 shadow-[0_30px_90px_rgba(2,6,23,0.28)] backdrop-blur-sm md:p-8" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-start justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-lime-200/80" }, "Staff Programming"), /* @__PURE__ */ React.createElement("h1", { className: "mt-3 text-4xl font-semibold tracking-[-0.05em] text-white md:text-5xl" }, "Collections programming studio"), /* @__PURE__ */ React.createElement("p", { className: "mt-4 max-w-3xl text-sm leading-relaxed text-slate-300 md:text-[15px]" }, "Manage program assignments, preview live pools, and run targeted diagnostics before collections hit discovery surfaces."), notice ? /* @__PURE__ */ React.createElement("p", { className: "mt-4 text-sm text-sky-100" }, notice) : null), endpoints.surfaces ? /* @__PURE__ */ React.createElement("a", { href: endpoints.surfaces, className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-thumbtack fa-fw text-[11px]" }), "Open placement studio") : null)), /* @__PURE__ */ React.createElement("section", { className: "mt-8 grid gap-5 md:grid-cols-3" }, /* @__PURE__ */ React.createElement(StatCard$5, { label: "Assignments", value: assignments.length, tone: "sky" }), /* @__PURE__ */ React.createElement(StatCard$5, { label: "Program Keys", value: totalPrograms, tone: "amber" }), /* @__PURE__ */ React.createElement(StatCard$5, { label: "Eligible", value: eligibleAssignments, tone: "emerald" })), /* @__PURE__ */ React.createElement("section", { className: "mt-8 rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-start justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-rose-200/80" }, "Merge Queue"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Pending duplicates and recent decisions"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 max-w-3xl text-sm leading-relaxed text-slate-300" }, "Review what still needs merge attention and what staff already resolved. Each row links back into the collection studio for full compare-and-confirm actions.")), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 sm:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(StatCard$5, { label: "Pending", value: mergeQueue?.summary?.pending || 0, tone: "amber" }), /* @__PURE__ */ React.createElement(StatCard$5, { label: "Approved", value: mergeQueue?.summary?.approved || 0, tone: "sky" }), /* @__PURE__ */ React.createElement(StatCard$5, { label: "Rejected", value: mergeQueue?.summary?.rejected || 0, tone: "emerald" }), /* @__PURE__ */ React.createElement(StatCard$5, { label: "Merged", value: mergeQueue?.summary?.completed || 0, tone: "sky" }))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-6 xl:grid-cols-2" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/10 bg-slate-950/40 p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-rose-200/80" }, "Needs Review"), /* @__PURE__ */ React.createElement("h3", { className: "mt-2 text-xl font-semibold text-white" }, "Suggested duplicate pairs")), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold text-slate-300" }, mergeQueue?.pending?.length || 0)), /* @__PURE__ */ React.createElement("div", { className: "mt-5 space-y-4" }, (mergeQueue?.pending || []).length ? mergeQueue.pending.map((item) => { + ), /* @__PURE__ */ React.createElement("div", { className: "relative min-h-screen overflow-hidden pb-16" }, /* @__PURE__ */ React.createElement("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-x-0 top-0 -z-10 h-[34rem] opacity-95", style: { background: "radial-gradient(circle at 18% 15%, rgba(56,189,248,0.18), transparent 28%), radial-gradient(circle at 85% 14%, rgba(132,204,22,0.16), transparent 26%), linear-gradient(180deg, #07101d 0%, #0a1220 42%, #08111f 100%)" } }), /* @__PURE__ */ React.createElement("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 -z-10 opacity-[0.05]", style: { backgroundImage: "url(/gfx/noise.png)", backgroundSize: "180px" } }), /* @__PURE__ */ React.createElement("div", { className: "mx-auto max-w-7xl px-4 pt-8 md:px-6" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[34px] border border-white/10 bg-white/[0.04] p-6 shadow-[0_30px_90px_rgba(2,6,23,0.28)] backdrop-blur-sm md:p-8" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-start justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-lime-200/80" }, "Staff Programming"), /* @__PURE__ */ React.createElement("h1", { className: "mt-3 text-4xl font-semibold tracking-[-0.05em] text-white md:text-5xl" }, "Collections programming studio"), /* @__PURE__ */ React.createElement("p", { className: "mt-4 max-w-3xl text-sm leading-relaxed text-slate-300 md:text-[15px]" }, "Manage program assignments, preview live pools, and run targeted diagnostics before collections hit discovery surfaces."), notice ? /* @__PURE__ */ React.createElement("p", { className: "mt-4 text-sm text-sky-100" }, notice) : null), endpoints.surfaces ? /* @__PURE__ */ React.createElement("a", { href: endpoints.surfaces, className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-thumbtack fa-fw text-[11px]" }), "Open placement studio") : null)), /* @__PURE__ */ React.createElement("section", { className: "mt-8 grid gap-5 md:grid-cols-3" }, /* @__PURE__ */ React.createElement(StatCard$8, { label: "Assignments", value: assignments.length, tone: "sky" }), /* @__PURE__ */ React.createElement(StatCard$8, { label: "Program Keys", value: totalPrograms, tone: "amber" }), /* @__PURE__ */ React.createElement(StatCard$8, { label: "Eligible", value: eligibleAssignments, tone: "emerald" })), /* @__PURE__ */ React.createElement("section", { className: "mt-8 rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-start justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-rose-200/80" }, "Merge Queue"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Pending duplicates and recent decisions"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 max-w-3xl text-sm leading-relaxed text-slate-300" }, "Review what still needs merge attention and what staff already resolved. Each row links back into the collection studio for full compare-and-confirm actions.")), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 sm:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(StatCard$8, { label: "Pending", value: mergeQueue?.summary?.pending || 0, tone: "amber" }), /* @__PURE__ */ React.createElement(StatCard$8, { label: "Approved", value: mergeQueue?.summary?.approved || 0, tone: "sky" }), /* @__PURE__ */ React.createElement(StatCard$8, { label: "Rejected", value: mergeQueue?.summary?.rejected || 0, tone: "emerald" }), /* @__PURE__ */ React.createElement(StatCard$8, { label: "Merged", value: mergeQueue?.summary?.completed || 0, tone: "sky" }))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-6 xl:grid-cols-2" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/10 bg-slate-950/40 p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-rose-200/80" }, "Needs Review"), /* @__PURE__ */ React.createElement("h3", { className: "mt-2 text-xl font-semibold text-white" }, "Suggested duplicate pairs")), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold text-slate-300" }, mergeQueue?.pending?.length || 0)), /* @__PURE__ */ React.createElement("div", { className: "mt-5 space-y-4" }, (mergeQueue?.pending || []).length ? mergeQueue.pending.map((item) => { const activeQueueAction = queueBusy[item.id] || ""; const cardBusy = Boolean(activeQueueAction); return /* @__PURE__ */ React.createElement("div", { key: `merge-pending-${item.id}`, className: `rounded-[24px] border border-white/10 bg-white/[0.04] p-4 transition ${cardBusy ? "ring-1 ring-sky-300/25" : ""}` }, /* @__PURE__ */ React.createElement("div", { className: "mb-4 flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, (item.comparison?.match_reasons || []).map((reason) => /* @__PURE__ */ React.createElement("span", { key: reason, className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[10px] font-semibold uppercase tracking-[0.14em] text-slate-300" }, titleize(reason)))), cardBusy ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center gap-2 rounded-full border border-sky-300/20 bg-sky-400/10 px-3 py-1 text-[10px] font-semibold uppercase tracking-[0.14em] text-sky-100" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-circle-notch fa-spin fa-fw text-[10px]" }), "Processing ", titleize(activeQueueAction)) : null), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 lg:grid-cols-2" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "mb-2 text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Source"), item.source ? /* @__PURE__ */ React.createElement(CollectionCard, { collection: item.source, isOwner: true }) : null), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "mb-2 text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Candidate"), item.target ? /* @__PURE__ */ React.createElement(CollectionCard, { collection: item.target, isOwner: true }) : null)), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-2 md:grid-cols-3 text-xs text-slate-400" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-xl border border-white/10 bg-white/[0.04] px-3 py-2" }, "Shared artworks: ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, item.comparison?.shared_artworks_count ?? 0)), /* @__PURE__ */ React.createElement("div", { className: "rounded-xl border border-white/10 bg-white/[0.04] px-3 py-2" }, "Source count: ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, item.comparison?.source_artworks_count ?? 0)), /* @__PURE__ */ React.createElement("div", { className: "rounded-xl border border-white/10 bg-white/[0.04] px-3 py-2" }, "Target count: ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, item.comparison?.target_artworks_count ?? 0))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2" }, item.source?.manage_url ? /* @__PURE__ */ React.createElement("a", { href: item.source.manage_url, className: "inline-flex items-center gap-2 rounded-full border border-rose-300/20 bg-rose-400/10 px-4 py-2 text-xs font-semibold text-rose-100 transition hover:bg-rose-400/15" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-code-compare fa-fw text-[10px]" }), "Review source") : null, item.target?.manage_url ? /* @__PURE__ */ React.createElement("a", { href: item.target.manage_url, className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-xs font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-up-right-from-square fa-fw text-[10px]" }), "Open target") : null, item.source?.id && historyPattern ? /* @__PURE__ */ React.createElement("a", { href: historyPattern.replace("__COLLECTION__", String(item.source.id)), className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-xs font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-timeline fa-fw text-[10px]" }), "History") : null, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => handleQueueAction("canonicalize", item), disabled: cardBusy, className: "inline-flex items-center gap-2 rounded-full border border-sky-300/20 bg-sky-400/10 px-4 py-2 text-xs font-semibold text-sky-100 transition hover:bg-sky-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${activeQueueAction === "canonicalize" ? "fa-circle-notch fa-spin" : "fa-badge-check"} fa-fw text-[10px]` }), activeQueueAction === "canonicalize" ? "Canonicalizing..." : "Canonicalize"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => handleQueueAction("merge", item), disabled: cardBusy, className: "inline-flex items-center gap-2 rounded-full border border-emerald-300/20 bg-emerald-400/10 px-4 py-2 text-xs font-semibold text-emerald-100 transition hover:bg-emerald-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${activeQueueAction === "merge" ? "fa-circle-notch fa-spin" : "fa-code-merge"} fa-fw text-[10px]` }), activeQueueAction === "merge" ? "Merging..." : "Merge now"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => handleQueueAction("reject", item), disabled: cardBusy, className: "inline-flex items-center gap-2 rounded-full border border-amber-300/20 bg-amber-400/10 px-4 py-2 text-xs font-semibold text-amber-100 transition hover:bg-amber-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${activeQueueAction === "reject" ? "fa-circle-notch fa-spin" : "fa-ban"} fa-fw text-[10px]` }), activeQueueAction === "reject" ? "Rejecting..." : "Reject"))); - }) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-dashed border-white/12 bg-white/[0.03] px-5 py-10 text-sm text-slate-300" }, "No pending merge candidates right now."))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/10 bg-slate-950/40 p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-sky-200/80" }, "Recent Decisions"), /* @__PURE__ */ React.createElement("h3", { className: "mt-2 text-xl font-semibold text-white" }, "Canonical, reject, and merge history")), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold text-slate-300" }, mergeQueue?.recent?.length || 0)), /* @__PURE__ */ React.createElement("div", { className: "mt-5 space-y-4" }, (mergeQueue?.recent || []).length ? mergeQueue.recent.map((item) => /* @__PURE__ */ React.createElement("div", { key: `merge-recent-${item.id}`, className: "rounded-[24px] border border-white/10 bg-white/[0.04] p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-start justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", { className: "inline-flex rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-300" }, titleize(item.action_type)), item.summary ? /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-slate-300" }, item.summary) : null, /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-xs text-slate-500" }, item.updated_at ? new Date(item.updated_at).toLocaleString() : "Unknown time", item.actor?.username ? ` • @${item.actor.username}` : ""))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 lg:grid-cols-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Source"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, item.source?.title || "Collection")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Target"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, item.target?.title || "Collection"))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2" }, item.source?.manage_url ? /* @__PURE__ */ React.createElement("a", { href: item.source.manage_url, className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-xs font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-pen-to-square fa-fw text-[10px]" }), "Open source") : null, item.target?.manage_url ? /* @__PURE__ */ React.createElement("a", { href: item.target.manage_url, className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-xs font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-up-right-from-square fa-fw text-[10px]" }), "Open target") : null))) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-dashed border-white/12 bg-white/[0.03] px-5 py-10 text-sm text-slate-300" }, "No recent merge decisions yet."))))), /* @__PURE__ */ React.createElement("section", { className: "mt-8 grid gap-6 xl:grid-cols-[minmax(0,0.95fr)_minmax(0,1.05fr)]" }, /* @__PURE__ */ React.createElement("form", { onSubmit: handleAssignmentSubmit, className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Assignment"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Program key and scope")), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$2, { label: "Collection" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: String(assignmentForm.collection_id || ""), onChange: (val) => setAssignmentForm((current) => ({ ...current, collection_id: val })), options: collectionOptions.map((o) => ({ value: String(o.id), label: o.title })) })), /* @__PURE__ */ React.createElement(Field$2, { label: "Program Key", help: "Use stable internal names like discover-spring or homepage-hero." }, /* @__PURE__ */ React.createElement("input", { list: "program-key-options", value: assignmentForm.program_key, onChange: (event) => setAssignmentForm((current) => ({ ...current, program_key: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$2, { label: "Placement Scope", help: "Optional placement scope such as homepage.hero or discover.rail." }, /* @__PURE__ */ React.createElement("input", { value: assignmentForm.placement_scope, onChange: (event) => setAssignmentForm((current) => ({ ...current, placement_scope: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$2, { label: "Campaign Key" }, /* @__PURE__ */ React.createElement("input", { value: assignmentForm.campaign_key, onChange: (event) => setAssignmentForm((current) => ({ ...current, campaign_key: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$2, { label: "Priority" }, /* @__PURE__ */ React.createElement("input", { type: "number", min: "-100", max: "100", value: assignmentForm.priority, onChange: (event) => setAssignmentForm((current) => ({ ...current, priority: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement(Field$2, { label: "Starts At" }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: assignmentForm.starts_at, onChange: (nextValue) => setAssignmentForm((current) => ({ ...current, starts_at: nextValue })), placeholder: "Start time", clearable: true, className: "bg-white/[0.04]" })), /* @__PURE__ */ React.createElement(Field$2, { label: "Ends At" }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: assignmentForm.ends_at, onChange: (nextValue) => setAssignmentForm((current) => ({ ...current, ends_at: nextValue })), placeholder: "End time", clearable: true, className: "bg-white/[0.04]" }))), /* @__PURE__ */ React.createElement(Field$2, { label: "Notes", help: "Operational note for launch timing, overrides, or review context." }, /* @__PURE__ */ React.createElement("textarea", { value: assignmentForm.notes, onChange: (event) => setAssignmentForm((current) => ({ ...current, notes: event.target.value })), className: "mt-4 min-h-[120px] w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 1e3 })), /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-wrap gap-3" }, /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: busy === "assignment", className: "inline-flex items-center gap-2 rounded-2xl border border-sky-300/20 bg-sky-400/10 px-5 py-3 text-sm font-semibold text-sky-100 transition hover:bg-sky-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === "assignment" ? "fa-circle-notch fa-spin" : "fa-sliders"} fa-fw` }), assignmentForm.id ? "Update Assignment" : "Save Assignment"), assignmentForm.id ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: resetAssignmentForm, className: "inline-flex items-center gap-2 rounded-2xl border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-rotate-left fa-fw" }), "Cancel Edit") : null), /* @__PURE__ */ React.createElement("datalist", { id: "program-key-options" }, programKeyOptions.map((option) => /* @__PURE__ */ React.createElement("option", { key: option, value: option })))), /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement("form", { onSubmit: handlePreview, className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-amber-200/80" }, "Preview"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Inspect a live program pool")), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 md:grid-cols-[minmax(0,1fr)_140px_auto]" }, /* @__PURE__ */ React.createElement(Field$2, { label: "Program Key" }, /* @__PURE__ */ React.createElement("input", { list: "program-key-options", value: previewForm.program_key, onChange: (event) => setPreviewForm((current) => ({ ...current, program_key: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$2, { label: "Limit" }, /* @__PURE__ */ React.createElement("input", { type: "number", min: "1", max: "24", value: previewForm.limit, onChange: (event) => setPreviewForm((current) => ({ ...current, limit: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement("div", { className: "flex items-end" }, /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: busy === "preview", className: "inline-flex h-[50px] w-full items-center justify-center gap-2 rounded-2xl border border-amber-300/20 bg-amber-400/10 px-5 text-sm font-semibold text-amber-100 transition hover:bg-amber-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === "preview" ? "fa-circle-notch fa-spin" : "fa-binoculars"} fa-fw` }), "Preview"))), previewCollections.length ? /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 xl:grid-cols-2" }, previewCollections.map((collection) => /* @__PURE__ */ React.createElement("div", { key: collection.id, className: "rounded-[24px] border border-white/10 bg-slate-950/40 p-4" }, /* @__PURE__ */ React.createElement(CollectionCard, { collection, isOwner: true })))) : /* @__PURE__ */ React.createElement("div", { className: "mt-6 rounded-[24px] border border-dashed border-white/12 bg-white/[0.03] px-5 py-8 text-sm text-slate-300" }, "Run a preview to inspect which collections currently qualify for a given program key.")), /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-lime-200/80" }, "Diagnostics"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Eligibility, duplicate risk, and ranking refresh")), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 xl:grid-cols-[320px_minmax(0,1fr)]" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-slate-950/40 p-4 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-white" }, "Operations summary"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 sm:grid-cols-2 xl:grid-cols-1" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.16em] text-slate-400" }, "Stale health"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-lg font-semibold text-white" }, Number(observabilitySummary?.counts?.stale_health || 0))), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.16em] text-slate-400" }, "Stale recommendations"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-lg font-semibold text-white" }, Number(observabilitySummary?.counts?.stale_recommendations || 0))), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.16em] text-slate-400" }, "Placement blocked"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-lg font-semibold text-white" }, Number(observabilitySummary?.counts?.placement_blocked || 0))), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.16em] text-slate-400" }, "Duplicate risk"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-lg font-semibold text-white" }, Number(observabilitySummary?.counts?.duplicate_risk || 0)))), observabilitySummary?.generated_at ? /* @__PURE__ */ React.createElement("p", { className: "mt-4 text-xs text-slate-400" }, "Generated ", new Date(observabilitySummary.generated_at).toLocaleString()) : null), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-slate-950/40 p-4 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-white" }, "Watchlist"), Array.isArray(observabilitySummary?.watchlist) && observabilitySummary.watchlist.length ? /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-4 xl:grid-cols-2" }, observabilitySummary.watchlist.map((collection) => /* @__PURE__ */ React.createElement("div", { key: `watch-${collection.id}`, className: "rounded-[20px] border border-white/10 bg-white/[0.04] p-3" }, /* @__PURE__ */ React.createElement(CollectionCard, { collection, isOwner: true })))) : /* @__PURE__ */ React.createElement("p", { className: "mt-3" }, "No watchlist items are currently flagged."))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 md:grid-cols-[minmax(0,1fr)_auto]" }, /* @__PURE__ */ React.createElement(Field$2, { label: "Target Collection", help: "Leave a selection in place to inspect one collection. Change it any time before running a diagnostic." }, /* @__PURE__ */ React.createElement(NovaSelect, { value: String(selectedCollectionId || ""), onChange: (val) => setSelectedCollectionId(val), options: collectionOptions.map((o) => ({ value: String(o.id), label: o.title })) })), /* @__PURE__ */ React.createElement("div", { className: "flex items-end gap-3" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => runDiagnostic("eligibility"), disabled: busy !== "", className: "inline-flex items-center gap-2 rounded-2xl border border-lime-300/20 bg-lime-400/10 px-4 py-3 text-sm font-semibold text-lime-100 transition hover:bg-lime-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === "eligibility" ? "fa-circle-notch fa-spin" : "fa-shield-check"} fa-fw` }), "Eligibility"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => runDiagnostic("duplicates"), disabled: busy !== "", className: "inline-flex items-center gap-2 rounded-2xl border border-rose-300/20 bg-rose-400/10 px-4 py-3 text-sm font-semibold text-rose-100 transition hover:bg-rose-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === "duplicates" ? "fa-circle-notch fa-spin" : "fa-id-card"} fa-fw` }), "Duplicates"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => runDiagnostic("recommendations"), disabled: busy !== "", className: "inline-flex items-center gap-2 rounded-2xl border border-sky-300/20 bg-sky-400/10 px-4 py-3 text-sm font-semibold text-sky-100 transition hover:bg-sky-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === "recommendations" ? "fa-circle-notch fa-spin" : "fa-arrows-rotate"} fa-fw` }), "Refresh"))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 lg:grid-cols-3" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-slate-950/40 p-4 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-white" }, "Eligibility"), diagnostics.eligibility ? /* @__PURE__ */ React.createElement("div", { className: "mt-3 space-y-2" }, /* @__PURE__ */ React.createElement("p", null, diagnostics.eligibility.status === "queued" ? `${diagnostics.eligibility.count} collection(s) queued.` : `${diagnostics.eligibility.count} collection(s) evaluated.`), diagnostics.eligibility.message ? /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2" }, diagnostics.eligibility.message) : null, (diagnostics.eligibility.items || []).map((item) => /* @__PURE__ */ React.createElement("div", { key: item.collection_id, className: "rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2" }, item.health_state || "unknown", " · ", item.readiness_state || "unknown", " · ", item.placement_eligibility ? "eligible" : "blocked"))) : /* @__PURE__ */ React.createElement("p", { className: "mt-3" }, "Run an eligibility refresh to verify readiness and public placement safety.")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-slate-950/40 p-4 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-white" }, "Duplicate candidates"), diagnostics.duplicates ? /* @__PURE__ */ React.createElement("div", { className: "mt-3 space-y-2" }, /* @__PURE__ */ React.createElement("p", null, diagnostics.duplicates.status === "queued" ? `${diagnostics.duplicates.count} collection(s) queued.` : `${diagnostics.duplicates.count} collection(s) with candidates.`), diagnostics.duplicates.message ? /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2" }, diagnostics.duplicates.message) : null, (diagnostics.duplicates.items || []).map((item) => /* @__PURE__ */ React.createElement("div", { key: item.collection_id, className: "rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2" }, item.candidates?.length ? item.candidates.map((candidate) => candidate.title).join(", ") : "No candidates"))) : /* @__PURE__ */ React.createElement("p", { className: "mt-3" }, "Run duplicate scan to surface overlap before programming a collection widely.")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-slate-950/40 p-4 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-white" }, "Recommendation refresh"), diagnostics.recommendations ? /* @__PURE__ */ React.createElement("div", { className: "mt-3 space-y-2" }, /* @__PURE__ */ React.createElement("p", null, diagnostics.recommendations.status === "queued" ? `${diagnostics.recommendations.count} collection(s) queued.` : `${diagnostics.recommendations.count} collection(s) refreshed.`), diagnostics.recommendations.message ? /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2" }, diagnostics.recommendations.message) : null, (diagnostics.recommendations.items || []).map((item) => /* @__PURE__ */ React.createElement("div", { key: item.collection_id, className: "rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2" }, titleize(item.recommendation_tier || "unknown"), " · ", titleize(item.ranking_bucket || "unknown"), " · ", titleize(item.search_boost_tier || "unknown")))) : /* @__PURE__ */ React.createElement("p", { className: "mt-3" }, "Run a recommendation refresh to update ranking and search tiers for this collection.")))), /* @__PURE__ */ React.createElement("form", { onSubmit: handleHooksSubmit, className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-start justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-fuchsia-200/80" }, "Hooks"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Experiment and program governance"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-relaxed text-slate-300" }, "Control experiment keys, promotion tiers, and staff-only program governance hooks for the selected collection without leaving the programming studio.")), selectedCollection?.program_key && endpoints.publicProgramPattern ? /* @__PURE__ */ React.createElement("a", { href: buildProgramUrl(endpoints.publicProgramPattern, selectedCollection.program_key), className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-up-right-from-square fa-fw text-[11px]" }), "Open public program landing") : null), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 md:grid-cols-2 xl:grid-cols-3" }, /* @__PURE__ */ React.createElement(Field$2, { label: "Experiment Key", help: "Internal test or treatment key for cross-surface collection experiments." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.experiment_key, onChange: (event) => setHooksForm((current) => ({ ...current, experiment_key: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$2, { label: "Treatment", help: "Variant or treatment label tied to the experiment key." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.experiment_treatment, onChange: (event) => setHooksForm((current) => ({ ...current, experiment_treatment: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$2, { label: "Placement Variant", help: "Surface-specific placement variant such as homepage_a or search_dense." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.placement_variant, onChange: (event) => setHooksForm((current) => ({ ...current, placement_variant: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$2, { label: "Ranking Variant", help: "Override or annotate ranking mode experiments without changing the live pool logic." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.ranking_mode_variant, onChange: (event) => setHooksForm((current) => ({ ...current, ranking_mode_variant: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$2, { label: "Pool Version", help: "Snapshot or rollout version for the collection pool definition." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.collection_pool_version, onChange: (event) => setHooksForm((current) => ({ ...current, collection_pool_version: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$2, { label: "Test Label", help: "Human-readable campaign or experiment label for operations and diagnostics." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.test_label, onChange: (event) => setHooksForm((current) => ({ ...current, test_label: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 120 })), /* @__PURE__ */ React.createElement(Field$2, { label: "Promotion Tier", help: "Optional internal tier for elevated or restrained programming treatment." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.promotion_tier, onChange: (event) => setHooksForm((current) => ({ ...current, promotion_tier: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 40 })), viewer.isAdmin ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Field$2, { label: "Partner Key", help: "Admin-only internal key for trusted partner or program ownership." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.partner_key, onChange: (event) => setHooksForm((current) => ({ ...current, partner_key: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$2, { label: "Trust Tier", help: "Admin-only trust marker used for internal partner/program review logic." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.trust_tier, onChange: (event) => setHooksForm((current) => ({ ...current, trust_tier: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 40 })), /* @__PURE__ */ React.createElement(Field$2, { label: "Sponsorship State", help: "Admin-only state for sponsored, pending, or cleared program treatment." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.sponsorship_state, onChange: (event) => setHooksForm((current) => ({ ...current, sponsorship_state: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 40 })), /* @__PURE__ */ React.createElement(Field$2, { label: "Ownership Domain", help: "Admin-only internal ownership domain such as editorial, partner, creator_program, or events." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.ownership_domain, onChange: (event) => setHooksForm((current) => ({ ...current, ownership_domain: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$2, { label: "Commercial Review", help: "Admin-only commercial review status for future partner and sponsor programs." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.commercial_review_state, onChange: (event) => setHooksForm((current) => ({ ...current, commercial_review_state: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 40 })), /* @__PURE__ */ React.createElement(Field$2, { label: "Legal Review", help: "Admin-only legal review status when collections need compliance approval before wider promotion." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.legal_review_state, onChange: (event) => setHooksForm((current) => ({ ...current, legal_review_state: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 40 }))) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-dashed border-white/12 bg-white/[0.03] px-4 py-4 text-sm text-slate-300 md:col-span-2 xl:col-span-3" }, "Partner, sponsorship, ownership, and review metadata remain admin-only. Moderators can still manage experiment and promotion hooks here.")), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex items-center gap-3 rounded-[20px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement(Checkbox, { checked: hooksForm.placement_eligibility, onChange: (event) => setHooksForm((current) => ({ ...current, placement_eligibility: event.target.checked })), label: "Placement eligible override" })), /* @__PURE__ */ React.createElement("div", { className: "mt-5 grid gap-3 sm:grid-cols-2 xl:grid-cols-3" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Experiment"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.experiment_key || selectedCollection?.experiment_key || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Treatment"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.experiment_treatment || selectedCollection?.experiment_treatment || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Placement Variant"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.placement_variant || selectedCollection?.placement_variant || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Workflow"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, titleize(hooksDiagnostics?.workflow_state || selectedCollection?.workflow_state || "unknown"))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Health"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, titleize(hooksDiagnostics?.health_state || selectedCollection?.health_state || "unknown"))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Recommendation Tier"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, titleize(hooksDiagnostics?.recommendation_tier || selectedCollection?.recommendation_tier || "unknown"))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Ranking Bucket"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, titleize(hooksDiagnostics?.ranking_bucket || selectedCollection?.ranking_bucket || "unknown"))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Ranking Variant"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.ranking_mode_variant || selectedCollection?.ranking_mode_variant || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Pool Version"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.collection_pool_version || selectedCollection?.collection_pool_version || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Test Label"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.test_label || selectedCollection?.test_label || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Promotion Tier"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.promotion_tier || selectedCollection?.promotion_tier || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Partner Key"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.partner_key || selectedCollection?.partner_key || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Trust Tier"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.trust_tier || selectedCollection?.trust_tier || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Sponsorship State"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.sponsorship_state || selectedCollection?.sponsorship_state || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Ownership Domain"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.ownership_domain || selectedCollection?.ownership_domain || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Commercial Review"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.commercial_review_state || selectedCollection?.commercial_review_state || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Legal Review"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.legal_review_state || selectedCollection?.legal_review_state || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Last Health Check"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.last_health_check_at ? new Date(hooksDiagnostics.last_health_check_at).toLocaleString() : "Not yet")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Last Recommendation Refresh"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.last_recommendation_refresh_at ? new Date(hooksDiagnostics.last_recommendation_refresh_at).toLocaleString() : "Not yet"))), /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-wrap gap-3" }, /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: busy === "hooks" || !selectedCollectionId, className: "inline-flex items-center gap-2 rounded-2xl border border-fuchsia-300/20 bg-fuchsia-400/10 px-5 py-3 text-sm font-semibold text-fuchsia-100 transition hover:bg-fuchsia-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === "hooks" ? "fa-circle-notch fa-spin" : "fa-flask-vial"} fa-fw` }), "Save Hooks"), selectedCollection?.manage_url ? /* @__PURE__ */ React.createElement("a", { href: selectedCollection.manage_url, className: "inline-flex items-center gap-2 rounded-2xl border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-up-right-from-square fa-fw" }), "Open collection") : null)))), /* @__PURE__ */ React.createElement("section", { className: "mt-8 rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Assignments"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Current programming inventory")), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold text-slate-300" }, assignments.length)), /* @__PURE__ */ React.createElement("div", { className: "mt-6 space-y-5" }, assignments.length ? assignments.map((assignment) => /* @__PURE__ */ React.createElement("div", { key: assignment.id, className: "rounded-[28px] border border-white/10 bg-slate-950/40 p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2" }, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-sky-300/20 bg-sky-400/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-sky-100" }, assignment.program_key), assignment.placement_scope ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-300" }, assignment.placement_scope) : null, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-300" }, "priority ", assignment.priority)), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => hydrateAssignment(assignment), className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-xs font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-pen fa-fw text-[10px]" }), "Edit"), endpoints.managePattern ? /* @__PURE__ */ React.createElement("a", { href: endpoints.managePattern.replace("__COLLECTION__", String(assignment.collection?.id || "")), className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-xs font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-up-right-from-square fa-fw text-[10px]" }), "Manage") : null)), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-5 xl:grid-cols-[minmax(0,1fr)_280px]" }, /* @__PURE__ */ React.createElement("div", null, assignment.collection ? /* @__PURE__ */ React.createElement(CollectionCard, { collection: assignment.collection, isOwner: true }) : null), /* @__PURE__ */ React.createElement("div", { className: "space-y-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-white/[0.04] px-4 py-3" }, "Campaign: ", assignment.campaign_key || "None"), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-white/[0.04] px-4 py-3" }, "Starts: ", assignment.starts_at ? new Date(assignment.starts_at).toLocaleString() : "Immediate"), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-white/[0.04] px-4 py-3" }, "Ends: ", assignment.ends_at ? new Date(assignment.ends_at).toLocaleString() : "Open-ended"), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-white/[0.04] px-4 py-3" }, "Placement: ", assignment.collection?.placement_eligibility ? "Eligible" : "Blocked"), assignment.notes ? /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-white/[0.04] px-4 py-3" }, assignment.notes) : null)))) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[26px] border border-dashed border-white/12 bg-white/[0.03] px-6 py-12 text-sm text-slate-300" }, "No programming assignments yet. Create the first one above.")))))); + }) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-dashed border-white/12 bg-white/[0.03] px-5 py-10 text-sm text-slate-300" }, "No pending merge candidates right now."))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/10 bg-slate-950/40 p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-sky-200/80" }, "Recent Decisions"), /* @__PURE__ */ React.createElement("h3", { className: "mt-2 text-xl font-semibold text-white" }, "Canonical, reject, and merge history")), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold text-slate-300" }, mergeQueue?.recent?.length || 0)), /* @__PURE__ */ React.createElement("div", { className: "mt-5 space-y-4" }, (mergeQueue?.recent || []).length ? mergeQueue.recent.map((item) => /* @__PURE__ */ React.createElement("div", { key: `merge-recent-${item.id}`, className: "rounded-[24px] border border-white/10 bg-white/[0.04] p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-start justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", { className: "inline-flex rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-300" }, titleize(item.action_type)), item.summary ? /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-slate-300" }, item.summary) : null, /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-xs text-slate-500" }, item.updated_at ? new Date(item.updated_at).toLocaleString() : "Unknown time", item.actor?.username ? ` • @${item.actor.username}` : ""))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 lg:grid-cols-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Source"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, item.source?.title || "Collection")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Target"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, item.target?.title || "Collection"))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2" }, item.source?.manage_url ? /* @__PURE__ */ React.createElement("a", { href: item.source.manage_url, className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-xs font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-pen-to-square fa-fw text-[10px]" }), "Open source") : null, item.target?.manage_url ? /* @__PURE__ */ React.createElement("a", { href: item.target.manage_url, className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-xs font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-up-right-from-square fa-fw text-[10px]" }), "Open target") : null))) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-dashed border-white/12 bg-white/[0.03] px-5 py-10 text-sm text-slate-300" }, "No recent merge decisions yet."))))), /* @__PURE__ */ React.createElement("section", { className: "mt-8 grid gap-6 xl:grid-cols-[minmax(0,0.95fr)_minmax(0,1.05fr)]" }, /* @__PURE__ */ React.createElement("form", { onSubmit: handleAssignmentSubmit, className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Assignment"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Program key and scope")), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$3, { label: "Collection" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: String(assignmentForm.collection_id || ""), onChange: (val) => setAssignmentForm((current) => ({ ...current, collection_id: val })), options: collectionOptions.map((o) => ({ value: String(o.id), label: o.title })) })), /* @__PURE__ */ React.createElement(Field$3, { label: "Program Key", help: "Use stable internal names like discover-spring or homepage-hero." }, /* @__PURE__ */ React.createElement("input", { list: "program-key-options", value: assignmentForm.program_key, onChange: (event) => setAssignmentForm((current) => ({ ...current, program_key: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Placement Scope", help: "Optional placement scope such as homepage.hero or discover.rail." }, /* @__PURE__ */ React.createElement("input", { value: assignmentForm.placement_scope, onChange: (event) => setAssignmentForm((current) => ({ ...current, placement_scope: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Campaign Key" }, /* @__PURE__ */ React.createElement("input", { value: assignmentForm.campaign_key, onChange: (event) => setAssignmentForm((current) => ({ ...current, campaign_key: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Priority" }, /* @__PURE__ */ React.createElement("input", { type: "number", min: "-100", max: "100", value: assignmentForm.priority, onChange: (event) => setAssignmentForm((current) => ({ ...current, priority: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement(Field$3, { label: "Starts At" }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: assignmentForm.starts_at, onChange: (nextValue) => setAssignmentForm((current) => ({ ...current, starts_at: nextValue })), placeholder: "Start time", clearable: true, className: "bg-white/[0.04]" })), /* @__PURE__ */ React.createElement(Field$3, { label: "Ends At" }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: assignmentForm.ends_at, onChange: (nextValue) => setAssignmentForm((current) => ({ ...current, ends_at: nextValue })), placeholder: "End time", clearable: true, className: "bg-white/[0.04]" }))), /* @__PURE__ */ React.createElement(Field$3, { label: "Notes", help: "Operational note for launch timing, overrides, or review context." }, /* @__PURE__ */ React.createElement("textarea", { value: assignmentForm.notes, onChange: (event) => setAssignmentForm((current) => ({ ...current, notes: event.target.value })), className: "mt-4 min-h-[120px] w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 1e3 })), /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-wrap gap-3" }, /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: busy === "assignment", className: "inline-flex items-center gap-2 rounded-2xl border border-sky-300/20 bg-sky-400/10 px-5 py-3 text-sm font-semibold text-sky-100 transition hover:bg-sky-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === "assignment" ? "fa-circle-notch fa-spin" : "fa-sliders"} fa-fw` }), assignmentForm.id ? "Update Assignment" : "Save Assignment"), assignmentForm.id ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: resetAssignmentForm, className: "inline-flex items-center gap-2 rounded-2xl border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-rotate-left fa-fw" }), "Cancel Edit") : null), /* @__PURE__ */ React.createElement("datalist", { id: "program-key-options" }, programKeyOptions.map((option) => /* @__PURE__ */ React.createElement("option", { key: option, value: option })))), /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement("form", { onSubmit: handlePreview, className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-amber-200/80" }, "Preview"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Inspect a live program pool")), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 md:grid-cols-[minmax(0,1fr)_140px_auto]" }, /* @__PURE__ */ React.createElement(Field$3, { label: "Program Key" }, /* @__PURE__ */ React.createElement("input", { list: "program-key-options", value: previewForm.program_key, onChange: (event) => setPreviewForm((current) => ({ ...current, program_key: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Limit" }, /* @__PURE__ */ React.createElement("input", { type: "number", min: "1", max: "24", value: previewForm.limit, onChange: (event) => setPreviewForm((current) => ({ ...current, limit: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement("div", { className: "flex items-end" }, /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: busy === "preview", className: "inline-flex h-[50px] w-full items-center justify-center gap-2 rounded-2xl border border-amber-300/20 bg-amber-400/10 px-5 text-sm font-semibold text-amber-100 transition hover:bg-amber-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === "preview" ? "fa-circle-notch fa-spin" : "fa-binoculars"} fa-fw` }), "Preview"))), previewCollections.length ? /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 xl:grid-cols-2" }, previewCollections.map((collection) => /* @__PURE__ */ React.createElement("div", { key: collection.id, className: "rounded-[24px] border border-white/10 bg-slate-950/40 p-4" }, /* @__PURE__ */ React.createElement(CollectionCard, { collection, isOwner: true })))) : /* @__PURE__ */ React.createElement("div", { className: "mt-6 rounded-[24px] border border-dashed border-white/12 bg-white/[0.03] px-5 py-8 text-sm text-slate-300" }, "Run a preview to inspect which collections currently qualify for a given program key.")), /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-lime-200/80" }, "Diagnostics"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Eligibility, duplicate risk, and ranking refresh")), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 xl:grid-cols-[320px_minmax(0,1fr)]" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-slate-950/40 p-4 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-white" }, "Operations summary"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 sm:grid-cols-2 xl:grid-cols-1" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.16em] text-slate-400" }, "Stale health"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-lg font-semibold text-white" }, Number(observabilitySummary?.counts?.stale_health || 0))), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.16em] text-slate-400" }, "Stale recommendations"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-lg font-semibold text-white" }, Number(observabilitySummary?.counts?.stale_recommendations || 0))), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.16em] text-slate-400" }, "Placement blocked"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-lg font-semibold text-white" }, Number(observabilitySummary?.counts?.placement_blocked || 0))), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.16em] text-slate-400" }, "Duplicate risk"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-lg font-semibold text-white" }, Number(observabilitySummary?.counts?.duplicate_risk || 0)))), observabilitySummary?.generated_at ? /* @__PURE__ */ React.createElement("p", { className: "mt-4 text-xs text-slate-400" }, "Generated ", new Date(observabilitySummary.generated_at).toLocaleString()) : null), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-slate-950/40 p-4 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-white" }, "Watchlist"), Array.isArray(observabilitySummary?.watchlist) && observabilitySummary.watchlist.length ? /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-4 xl:grid-cols-2" }, observabilitySummary.watchlist.map((collection) => /* @__PURE__ */ React.createElement("div", { key: `watch-${collection.id}`, className: "rounded-[20px] border border-white/10 bg-white/[0.04] p-3" }, /* @__PURE__ */ React.createElement(CollectionCard, { collection, isOwner: true })))) : /* @__PURE__ */ React.createElement("p", { className: "mt-3" }, "No watchlist items are currently flagged."))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 md:grid-cols-[minmax(0,1fr)_auto]" }, /* @__PURE__ */ React.createElement(Field$3, { label: "Target Collection", help: "Leave a selection in place to inspect one collection. Change it any time before running a diagnostic." }, /* @__PURE__ */ React.createElement(NovaSelect, { value: String(selectedCollectionId || ""), onChange: (val) => setSelectedCollectionId(val), options: collectionOptions.map((o) => ({ value: String(o.id), label: o.title })) })), /* @__PURE__ */ React.createElement("div", { className: "flex items-end gap-3" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => runDiagnostic("eligibility"), disabled: busy !== "", className: "inline-flex items-center gap-2 rounded-2xl border border-lime-300/20 bg-lime-400/10 px-4 py-3 text-sm font-semibold text-lime-100 transition hover:bg-lime-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === "eligibility" ? "fa-circle-notch fa-spin" : "fa-shield-check"} fa-fw` }), "Eligibility"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => runDiagnostic("duplicates"), disabled: busy !== "", className: "inline-flex items-center gap-2 rounded-2xl border border-rose-300/20 bg-rose-400/10 px-4 py-3 text-sm font-semibold text-rose-100 transition hover:bg-rose-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === "duplicates" ? "fa-circle-notch fa-spin" : "fa-id-card"} fa-fw` }), "Duplicates"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => runDiagnostic("recommendations"), disabled: busy !== "", className: "inline-flex items-center gap-2 rounded-2xl border border-sky-300/20 bg-sky-400/10 px-4 py-3 text-sm font-semibold text-sky-100 transition hover:bg-sky-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === "recommendations" ? "fa-circle-notch fa-spin" : "fa-arrows-rotate"} fa-fw` }), "Refresh"))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 lg:grid-cols-3" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-slate-950/40 p-4 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-white" }, "Eligibility"), diagnostics.eligibility ? /* @__PURE__ */ React.createElement("div", { className: "mt-3 space-y-2" }, /* @__PURE__ */ React.createElement("p", null, diagnostics.eligibility.status === "queued" ? `${diagnostics.eligibility.count} collection(s) queued.` : `${diagnostics.eligibility.count} collection(s) evaluated.`), diagnostics.eligibility.message ? /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2" }, diagnostics.eligibility.message) : null, (diagnostics.eligibility.items || []).map((item) => /* @__PURE__ */ React.createElement("div", { key: item.collection_id, className: "rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2" }, item.health_state || "unknown", " · ", item.readiness_state || "unknown", " · ", item.placement_eligibility ? "eligible" : "blocked"))) : /* @__PURE__ */ React.createElement("p", { className: "mt-3" }, "Run an eligibility refresh to verify readiness and public placement safety.")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-slate-950/40 p-4 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-white" }, "Duplicate candidates"), diagnostics.duplicates ? /* @__PURE__ */ React.createElement("div", { className: "mt-3 space-y-2" }, /* @__PURE__ */ React.createElement("p", null, diagnostics.duplicates.status === "queued" ? `${diagnostics.duplicates.count} collection(s) queued.` : `${diagnostics.duplicates.count} collection(s) with candidates.`), diagnostics.duplicates.message ? /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2" }, diagnostics.duplicates.message) : null, (diagnostics.duplicates.items || []).map((item) => /* @__PURE__ */ React.createElement("div", { key: item.collection_id, className: "rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2" }, item.candidates?.length ? item.candidates.map((candidate) => candidate.title).join(", ") : "No candidates"))) : /* @__PURE__ */ React.createElement("p", { className: "mt-3" }, "Run duplicate scan to surface overlap before programming a collection widely.")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-slate-950/40 p-4 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-white" }, "Recommendation refresh"), diagnostics.recommendations ? /* @__PURE__ */ React.createElement("div", { className: "mt-3 space-y-2" }, /* @__PURE__ */ React.createElement("p", null, diagnostics.recommendations.status === "queued" ? `${diagnostics.recommendations.count} collection(s) queued.` : `${diagnostics.recommendations.count} collection(s) refreshed.`), diagnostics.recommendations.message ? /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2" }, diagnostics.recommendations.message) : null, (diagnostics.recommendations.items || []).map((item) => /* @__PURE__ */ React.createElement("div", { key: item.collection_id, className: "rounded-2xl border border-white/10 bg-white/[0.04] px-3 py-2" }, titleize(item.recommendation_tier || "unknown"), " · ", titleize(item.ranking_bucket || "unknown"), " · ", titleize(item.search_boost_tier || "unknown")))) : /* @__PURE__ */ React.createElement("p", { className: "mt-3" }, "Run a recommendation refresh to update ranking and search tiers for this collection.")))), /* @__PURE__ */ React.createElement("form", { onSubmit: handleHooksSubmit, className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-start justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-fuchsia-200/80" }, "Hooks"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Experiment and program governance"), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-relaxed text-slate-300" }, "Control experiment keys, promotion tiers, and staff-only program governance hooks for the selected collection without leaving the programming studio.")), selectedCollection?.program_key && endpoints.publicProgramPattern ? /* @__PURE__ */ React.createElement("a", { href: buildProgramUrl(endpoints.publicProgramPattern, selectedCollection.program_key), className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-up-right-from-square fa-fw text-[11px]" }), "Open public program landing") : null), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 md:grid-cols-2 xl:grid-cols-3" }, /* @__PURE__ */ React.createElement(Field$3, { label: "Experiment Key", help: "Internal test or treatment key for cross-surface collection experiments." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.experiment_key, onChange: (event) => setHooksForm((current) => ({ ...current, experiment_key: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Treatment", help: "Variant or treatment label tied to the experiment key." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.experiment_treatment, onChange: (event) => setHooksForm((current) => ({ ...current, experiment_treatment: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Placement Variant", help: "Surface-specific placement variant such as homepage_a or search_dense." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.placement_variant, onChange: (event) => setHooksForm((current) => ({ ...current, placement_variant: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Ranking Variant", help: "Override or annotate ranking mode experiments without changing the live pool logic." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.ranking_mode_variant, onChange: (event) => setHooksForm((current) => ({ ...current, ranking_mode_variant: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Pool Version", help: "Snapshot or rollout version for the collection pool definition." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.collection_pool_version, onChange: (event) => setHooksForm((current) => ({ ...current, collection_pool_version: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Test Label", help: "Human-readable campaign or experiment label for operations and diagnostics." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.test_label, onChange: (event) => setHooksForm((current) => ({ ...current, test_label: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 120 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Promotion Tier", help: "Optional internal tier for elevated or restrained programming treatment." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.promotion_tier, onChange: (event) => setHooksForm((current) => ({ ...current, promotion_tier: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 40 })), viewer.isAdmin ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Field$3, { label: "Partner Key", help: "Admin-only internal key for trusted partner or program ownership." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.partner_key, onChange: (event) => setHooksForm((current) => ({ ...current, partner_key: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Trust Tier", help: "Admin-only trust marker used for internal partner/program review logic." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.trust_tier, onChange: (event) => setHooksForm((current) => ({ ...current, trust_tier: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 40 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Sponsorship State", help: "Admin-only state for sponsored, pending, or cleared program treatment." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.sponsorship_state, onChange: (event) => setHooksForm((current) => ({ ...current, sponsorship_state: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 40 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Ownership Domain", help: "Admin-only internal ownership domain such as editorial, partner, creator_program, or events." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.ownership_domain, onChange: (event) => setHooksForm((current) => ({ ...current, ownership_domain: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Commercial Review", help: "Admin-only commercial review status for future partner and sponsor programs." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.commercial_review_state, onChange: (event) => setHooksForm((current) => ({ ...current, commercial_review_state: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 40 })), /* @__PURE__ */ React.createElement(Field$3, { label: "Legal Review", help: "Admin-only legal review status when collections need compliance approval before wider promotion." }, /* @__PURE__ */ React.createElement("input", { value: hooksForm.legal_review_state, onChange: (event) => setHooksForm((current) => ({ ...current, legal_review_state: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 40 }))) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-dashed border-white/12 bg-white/[0.03] px-4 py-4 text-sm text-slate-300 md:col-span-2 xl:col-span-3" }, "Partner, sponsorship, ownership, and review metadata remain admin-only. Moderators can still manage experiment and promotion hooks here.")), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex items-center gap-3 rounded-[20px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement(Checkbox, { checked: hooksForm.placement_eligibility, onChange: (event) => setHooksForm((current) => ({ ...current, placement_eligibility: event.target.checked })), label: "Placement eligible override" })), /* @__PURE__ */ React.createElement("div", { className: "mt-5 grid gap-3 sm:grid-cols-2 xl:grid-cols-3" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Experiment"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.experiment_key || selectedCollection?.experiment_key || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Treatment"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.experiment_treatment || selectedCollection?.experiment_treatment || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Placement Variant"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.placement_variant || selectedCollection?.placement_variant || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Workflow"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, titleize(hooksDiagnostics?.workflow_state || selectedCollection?.workflow_state || "unknown"))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Health"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, titleize(hooksDiagnostics?.health_state || selectedCollection?.health_state || "unknown"))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Recommendation Tier"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, titleize(hooksDiagnostics?.recommendation_tier || selectedCollection?.recommendation_tier || "unknown"))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Ranking Bucket"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, titleize(hooksDiagnostics?.ranking_bucket || selectedCollection?.ranking_bucket || "unknown"))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Ranking Variant"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.ranking_mode_variant || selectedCollection?.ranking_mode_variant || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Pool Version"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.collection_pool_version || selectedCollection?.collection_pool_version || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Test Label"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.test_label || selectedCollection?.test_label || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Promotion Tier"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.promotion_tier || selectedCollection?.promotion_tier || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Partner Key"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.partner_key || selectedCollection?.partner_key || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Trust Tier"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.trust_tier || selectedCollection?.trust_tier || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Sponsorship State"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.sponsorship_state || selectedCollection?.sponsorship_state || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Ownership Domain"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.ownership_domain || selectedCollection?.ownership_domain || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Commercial Review"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.commercial_review_state || selectedCollection?.commercial_review_state || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Legal Review"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.legal_review_state || selectedCollection?.legal_review_state || "Not set")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Last Health Check"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.last_health_check_at ? new Date(hooksDiagnostics.last_health_check_at).toLocaleString() : "Not yet")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-slate-950/40 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400" }, "Last Recommendation Refresh"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, hooksDiagnostics?.last_recommendation_refresh_at ? new Date(hooksDiagnostics.last_recommendation_refresh_at).toLocaleString() : "Not yet"))), /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-wrap gap-3" }, /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: busy === "hooks" || !selectedCollectionId, className: "inline-flex items-center gap-2 rounded-2xl border border-fuchsia-300/20 bg-fuchsia-400/10 px-5 py-3 text-sm font-semibold text-fuchsia-100 transition hover:bg-fuchsia-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === "hooks" ? "fa-circle-notch fa-spin" : "fa-flask-vial"} fa-fw` }), "Save Hooks"), selectedCollection?.manage_url ? /* @__PURE__ */ React.createElement("a", { href: selectedCollection.manage_url, className: "inline-flex items-center gap-2 rounded-2xl border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-up-right-from-square fa-fw" }), "Open collection") : null)))), /* @__PURE__ */ React.createElement("section", { className: "mt-8 rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Assignments"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Current programming inventory")), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold text-slate-300" }, assignments.length)), /* @__PURE__ */ React.createElement("div", { className: "mt-6 space-y-5" }, assignments.length ? assignments.map((assignment) => /* @__PURE__ */ React.createElement("div", { key: assignment.id, className: "rounded-[28px] border border-white/10 bg-slate-950/40 p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2" }, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-sky-300/20 bg-sky-400/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-sky-100" }, assignment.program_key), assignment.placement_scope ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-300" }, assignment.placement_scope) : null, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-300" }, "priority ", assignment.priority)), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => hydrateAssignment(assignment), className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-xs font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-pen fa-fw text-[10px]" }), "Edit"), endpoints.managePattern ? /* @__PURE__ */ React.createElement("a", { href: endpoints.managePattern.replace("__COLLECTION__", String(assignment.collection?.id || "")), className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-xs font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-up-right-from-square fa-fw text-[10px]" }), "Manage") : null)), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-5 xl:grid-cols-[minmax(0,1fr)_280px]" }, /* @__PURE__ */ React.createElement("div", null, assignment.collection ? /* @__PURE__ */ React.createElement(CollectionCard, { collection: assignment.collection, isOwner: true }) : null), /* @__PURE__ */ React.createElement("div", { className: "space-y-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-white/[0.04] px-4 py-3" }, "Campaign: ", assignment.campaign_key || "None"), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-white/[0.04] px-4 py-3" }, "Starts: ", assignment.starts_at ? new Date(assignment.starts_at).toLocaleString() : "Immediate"), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-white/[0.04] px-4 py-3" }, "Ends: ", assignment.ends_at ? new Date(assignment.ends_at).toLocaleString() : "Open-ended"), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-white/[0.04] px-4 py-3" }, "Placement: ", assignment.collection?.placement_eligibility ? "Eligible" : "Blocked"), assignment.notes ? /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-white/[0.04] px-4 py-3" }, assignment.notes) : null)))) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[26px] border border-dashed border-white/12 bg-white/[0.03] px-6 py-12 text-sm text-slate-300" }, "No programming assignments yet. Create the first one above.")))))); } const __vite_glob_0_48 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, @@ -79969,7 +80527,7 @@ function getCsrfToken$7() { if (typeof document === "undefined") return ""; return document.querySelector('meta[name="csrf-token"]')?.getAttribute("content") || ""; } -async function requestJson$i(url, { method = "POST", body: body2 } = {}) { +async function requestJson$j(url, { method = "POST", body: body2 } = {}) { const response = await fetch(url, { method, credentials: "same-origin", @@ -80002,7 +80560,7 @@ function rulesJsonToText(rulesJson) { return '{\n "campaign_key": "",\n "owner_username": "",\n "presentation_style": "hero_grid",\n "min_quality_score": 80\n}'; } } -function Field$1({ label, help, children }) { +function Field$2({ label, help, children }) { return /* @__PURE__ */ React.createElement("label", { className: "block space-y-2" }, /* @__PURE__ */ React.createElement("span", { className: "text-sm font-semibold text-white" }, label), children, help ? /* @__PURE__ */ React.createElement("span", { className: "block text-xs leading-relaxed text-slate-400" }, help) : null); } function CollectionStaffSurfaces() { @@ -80130,7 +80688,7 @@ function CollectionStaffSurfaces() { try { const rulesJson = definitionForm.rules_json.trim() ? JSON.parse(definitionForm.rules_json) : null; const url = definitionForm.id ? props.endpoints?.definitionsUpdatePattern?.replace("__DEFINITION__", String(definitionForm.id)) : props.endpoints?.definitionsStore; - const payload = await requestJson$i(url, { + const payload = await requestJson$j(url, { method: definitionForm.id ? "PATCH" : "POST", body: { ...definitionForm, @@ -80159,7 +80717,7 @@ function CollectionStaffSurfaces() { setNotice(""); try { const url = placementForm.id ? props.endpoints?.placementsUpdatePattern?.replace("__PLACEMENT__", String(placementForm.id)) : props.endpoints?.placementsStore; - const payload = await requestJson$i(url, { + const payload = await requestJson$j(url, { method: placementForm.id ? "PATCH" : "POST", body: { ...placementForm, @@ -80189,7 +80747,7 @@ function CollectionStaffSurfaces() { setBusy(`batch-${mode}`); setNotice(""); try { - const payload = await requestJson$i(props.endpoints?.batchEditorial, { + const payload = await requestJson$j(props.endpoints?.batchEditorial, { method: "POST", body: { ...batchForm, @@ -80251,7 +80809,7 @@ function CollectionStaffSurfaces() { setNotice(""); try { const url = props.endpoints?.definitionsDeletePattern?.replace("__DEFINITION__", String(definition2.id)); - await requestJson$i(url, { method: "DELETE" }); + await requestJson$j(url, { method: "DELETE" }); setDefinitions((current) => current.filter((item) => item.id !== definition2.id)); if (definitionForm.id === definition2.id) { resetDefinitionForm(); @@ -80269,7 +80827,7 @@ function CollectionStaffSurfaces() { setNotice(""); try { const url = props.endpoints?.placementsDeletePattern?.replace("__PLACEMENT__", String(placement.id)); - const payload = await requestJson$i(url, { method: "DELETE" }); + const payload = await requestJson$j(url, { method: "DELETE" }); setPlacements((current) => current.filter((item) => item.id !== placement.id)); setConflicts(Array.isArray(payload.conflicts) ? payload.conflicts : []); if (placementForm.id === placement.id) { @@ -80282,10 +80840,10 @@ function CollectionStaffSurfaces() { setBusy(""); } } - return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Se$1, null, /* @__PURE__ */ React.createElement("title", null, seo.title || "Collection Surfaces — Skinbase"), /* @__PURE__ */ React.createElement("meta", { name: "description", content: seo.description || "Staff tools for collection surfaces." }), seo.canonical ? /* @__PURE__ */ React.createElement("link", { rel: "canonical", href: seo.canonical }) : null, /* @__PURE__ */ React.createElement("meta", { name: "robots", content: seo.robots || "noindex,follow" })), /* @__PURE__ */ React.createElement("div", { className: "relative min-h-screen overflow-hidden pb-16" }, /* @__PURE__ */ React.createElement("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-x-0 top-0 -z-10 h-[34rem] opacity-95", style: { background: "radial-gradient(circle at 15% 14%, rgba(245,158,11,0.16), transparent 26%), radial-gradient(circle at 82% 18%, rgba(56,189,248,0.16), transparent 24%), linear-gradient(180deg, #07101d 0%, #0a1220 42%, #08111f 100%)" } }), /* @__PURE__ */ React.createElement("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 -z-10 opacity-[0.05]", style: { backgroundImage: "url(/gfx/noise.png)", backgroundSize: "180px" } }), /* @__PURE__ */ React.createElement("div", { className: "mx-auto max-w-7xl px-4 pt-8 md:px-6" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[34px] border border-white/10 bg-white/[0.04] p-6 shadow-[0_30px_90px_rgba(2,6,23,0.28)] backdrop-blur-sm md:p-8" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-amber-200/80" }, "Staff Surfaces"), /* @__PURE__ */ React.createElement("h1", { className: "mt-3 text-4xl font-semibold tracking-[-0.05em] text-white md:text-5xl" }, "Collections placement studio"), /* @__PURE__ */ React.createElement("p", { className: "mt-4 max-w-3xl text-sm leading-relaxed text-slate-300 md:text-[15px]" }, "Define reusable discovery surfaces, then place eligible public collections into manual or campaign-specific slots with clear timing and notes."), notice ? /* @__PURE__ */ React.createElement("p", { className: "mt-4 text-sm text-sky-100" }, notice) : null), /* @__PURE__ */ React.createElement("section", { className: "mt-8 grid gap-6 xl:grid-cols-[minmax(0,0.95fr)_minmax(0,1.05fr)]" }, /* @__PURE__ */ React.createElement("form", { onSubmit: handleDefinitionSubmit, className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Surface Definition"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Rules and ranking")), definitionForm.id ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm text-slate-300" }, "Editing ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, definitionForm.surface_key)) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$1, { label: "Surface Key", help: definitionForm.id ? "Surface keys stay stable during edits so existing placements remain attached." : null }, /* @__PURE__ */ React.createElement("input", { value: definitionForm.surface_key, onChange: (event) => setDefinitionForm((current) => ({ ...current, surface_key: event.target.value })), disabled: Boolean(definitionForm.id), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none disabled:cursor-not-allowed disabled:opacity-60", maxLength: 120 })), /* @__PURE__ */ React.createElement(Field$1, { label: "Title" }, /* @__PURE__ */ React.createElement("input", { value: definitionForm.title, onChange: (event) => setDefinitionForm((current) => ({ ...current, title: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 160 })), /* @__PURE__ */ React.createElement(Field$1, { label: "Mode" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: definitionForm.mode, onChange: (val) => setDefinitionForm((current) => ({ ...current, mode: val })), searchable: false, options: [{ value: "manual", label: "Manual" }, { value: "automatic", label: "Automatic" }, { value: "hybrid", label: "Hybrid" }] })), /* @__PURE__ */ React.createElement(Field$1, { label: "Ranking" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: definitionForm.ranking_mode, onChange: (val) => setDefinitionForm((current) => ({ ...current, ranking_mode: val })), searchable: false, options: [{ value: "ranking_score", label: "Ranking score" }, { value: "recent_activity", label: "Recent activity" }, { value: "quality_score", label: "Quality score" }] })), /* @__PURE__ */ React.createElement(Field$1, { label: "Max Items" }, /* @__PURE__ */ React.createElement("input", { type: "number", min: "1", max: "24", value: definitionForm.max_items, onChange: (event) => setDefinitionForm((current) => ({ ...current, max_items: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement(Field$1, { label: "Starts At", help: "Optional activation window for the full surface definition." }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: definitionForm.starts_at, onChange: (nextValue) => setDefinitionForm((current) => ({ ...current, starts_at: nextValue })), placeholder: "Start time", clearable: true, className: "bg-white/[0.04]" })), /* @__PURE__ */ React.createElement(Field$1, { label: "Ends At", help: "Leave blank when the surface should stay live until staff changes it." }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: definitionForm.ends_at, onChange: (nextValue) => setDefinitionForm((current) => ({ ...current, ends_at: nextValue })), placeholder: "End time", clearable: true, className: "bg-white/[0.04]" })), /* @__PURE__ */ React.createElement(Field$1, { label: "Fallback Surface Key", help: "Optional fallback when this definition is inactive, scheduled out, or resolves no items." }, /* @__PURE__ */ React.createElement("input", { value: definitionForm.fallback_surface_key, onChange: (event) => setDefinitionForm((current) => ({ ...current, fallback_surface_key: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 120 })), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-white" }, /* @__PURE__ */ React.createElement(Checkbox, { checked: definitionForm.is_active, onChange: (event) => setDefinitionForm((current) => ({ ...current, is_active: event.target.checked })), label: "Active" }))), /* @__PURE__ */ React.createElement(Field$1, { label: "Description", help: "Operational note for staff browsing this surface later." }, /* @__PURE__ */ React.createElement("textarea", { value: definitionForm.description, onChange: (event) => setDefinitionForm((current) => ({ ...current, description: event.target.value })), className: "mt-4 min-h-[96px] w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 400 })), /* @__PURE__ */ React.createElement(Field$1, { label: "Rules JSON", help: "Supported filters include campaign, event, season, type, presentation_style, theme_token, collaboration_mode, owner_username or owner_usernames, commercial_eligible_only, analytics_enabled_only, min_quality_score, min_ranking_score, include_collection_ids, exclude_collection_ids, and featured_only." }, /* @__PURE__ */ React.createElement("textarea", { value: definitionForm.rules_json, onChange: (event) => setDefinitionForm((current) => ({ ...current, rules_json: event.target.value })), className: "mt-4 min-h-[160px] w-full rounded-2xl border border-white/10 bg-slate-950/50 px-4 py-3 font-mono text-sm text-white outline-none", spellCheck: false })), /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-wrap items-center gap-3" }, /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: busy === "definition", className: "inline-flex items-center gap-2 rounded-2xl border border-sky-300/20 bg-sky-400/10 px-5 py-3 text-sm font-semibold text-sky-100 transition hover:bg-sky-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === "definition" ? "fa-circle-notch fa-spin" : "fa-layer-group"} fa-fw` }), definitionForm.id ? "Update Definition" : "Save Definition"), definitionForm.id ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: resetDefinitionForm, className: "inline-flex items-center gap-2 rounded-2xl border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-rotate-left fa-fw" }), "Cancel Edit") : null)), /* @__PURE__ */ React.createElement("form", { onSubmit: handlePlacementSubmit, className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-amber-200/80" }, "Surface Placement"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Manual and campaign slots")), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$1, { label: "Surface" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: placementForm.surface_key, onChange: (val) => setPlacementForm((current) => ({ ...current, surface_key: val })), options: surfaceKeyOptions.map((o) => ({ value: o, label: o })) })), /* @__PURE__ */ React.createElement(Field$1, { label: "Collection" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: String(placementForm.collection_id || ""), onChange: (val) => setPlacementForm((current) => ({ ...current, collection_id: val })), options: collectionOptions.map((o) => ({ value: String(o.id), label: o.title })) })), /* @__PURE__ */ React.createElement(Field$1, { label: "Placement Type" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: placementForm.placement_type, onChange: (val) => setPlacementForm((current) => ({ ...current, placement_type: val })), searchable: false, options: [{ value: "manual", label: "Manual" }, { value: "campaign", label: "Campaign" }, { value: "scheduled_override", label: "Scheduled override" }] })), /* @__PURE__ */ React.createElement(Field$1, { label: "Priority" }, /* @__PURE__ */ React.createElement("input", { type: "number", min: "-100", max: "100", value: placementForm.priority, onChange: (event) => setPlacementForm((current) => ({ ...current, priority: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement(Field$1, { label: "Starts At" }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: placementForm.starts_at, onChange: (nextValue) => setPlacementForm((current) => ({ ...current, starts_at: nextValue })), placeholder: "Start time", clearable: true, className: "bg-white/[0.04]" })), /* @__PURE__ */ React.createElement(Field$1, { label: "Ends At" }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: placementForm.ends_at, onChange: (nextValue) => setPlacementForm((current) => ({ ...current, ends_at: nextValue })), placeholder: "End time", clearable: true, className: "bg-white/[0.04]" })), /* @__PURE__ */ React.createElement(Field$1, { label: "Campaign Key", help: "Optional campaign label for reporting and grouped overrides." }, /* @__PURE__ */ React.createElement("input", { value: placementForm.campaign_key, onChange: (event) => setPlacementForm((current) => ({ ...current, campaign_key: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-white" }, /* @__PURE__ */ React.createElement(Checkbox, { checked: placementForm.is_active, onChange: (event) => setPlacementForm((current) => ({ ...current, is_active: event.target.checked })), label: "Active placement" }))), /* @__PURE__ */ React.createElement(Field$1, { label: "Notes", help: "Internal note for why this collection owns the slot." }, /* @__PURE__ */ React.createElement("textarea", { value: placementForm.notes, onChange: (event) => setPlacementForm((current) => ({ ...current, notes: event.target.value })), className: "mt-4 min-h-[110px] w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 1e3 })), /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-wrap items-center gap-3" }, /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: busy === "placement", className: "inline-flex items-center gap-2 rounded-2xl border border-amber-300/20 bg-amber-400/10 px-5 py-3 text-sm font-semibold text-amber-100 transition hover:bg-amber-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === "placement" ? "fa-circle-notch fa-spin" : "fa-thumbtack"} fa-fw` }), placementForm.id ? "Update Placement" : "Save Placement"), placementForm.id ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: resetPlacementForm, className: "inline-flex items-center gap-2 rounded-2xl border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-rotate-left fa-fw" }), "Cancel Edit") : null))), /* @__PURE__ */ React.createElement("section", { className: "mt-8 rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-lime-200/80" }, "Batch Editorial Tools"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Campaign planning in one pass")), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold text-slate-300" }, batchForm.collection_ids.length, " selected")), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-6 xl:grid-cols-[minmax(0,0.95fr)_minmax(0,1.05fr)]" }, /* @__PURE__ */ React.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[26px] border border-white/10 bg-slate-950/40 p-5" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm font-semibold text-white" }, "Choose collections"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-slate-300" }, "The selector uses current public discovery candidates so staff can quickly prepare a seasonal or editorial run."), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 md:grid-cols-2" }, collectionOptions.map((option) => { + return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Se$1, null, /* @__PURE__ */ React.createElement("title", null, seo.title || "Collection Surfaces — Skinbase"), /* @__PURE__ */ React.createElement("meta", { name: "description", content: seo.description || "Staff tools for collection surfaces." }), seo.canonical ? /* @__PURE__ */ React.createElement("link", { rel: "canonical", href: seo.canonical }) : null, /* @__PURE__ */ React.createElement("meta", { name: "robots", content: seo.robots || "noindex,follow" })), /* @__PURE__ */ React.createElement("div", { className: "relative min-h-screen overflow-hidden pb-16" }, /* @__PURE__ */ React.createElement("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-x-0 top-0 -z-10 h-[34rem] opacity-95", style: { background: "radial-gradient(circle at 15% 14%, rgba(245,158,11,0.16), transparent 26%), radial-gradient(circle at 82% 18%, rgba(56,189,248,0.16), transparent 24%), linear-gradient(180deg, #07101d 0%, #0a1220 42%, #08111f 100%)" } }), /* @__PURE__ */ React.createElement("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 -z-10 opacity-[0.05]", style: { backgroundImage: "url(/gfx/noise.png)", backgroundSize: "180px" } }), /* @__PURE__ */ React.createElement("div", { className: "mx-auto max-w-7xl px-4 pt-8 md:px-6" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[34px] border border-white/10 bg-white/[0.04] p-6 shadow-[0_30px_90px_rgba(2,6,23,0.28)] backdrop-blur-sm md:p-8" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-amber-200/80" }, "Staff Surfaces"), /* @__PURE__ */ React.createElement("h1", { className: "mt-3 text-4xl font-semibold tracking-[-0.05em] text-white md:text-5xl" }, "Collections placement studio"), /* @__PURE__ */ React.createElement("p", { className: "mt-4 max-w-3xl text-sm leading-relaxed text-slate-300 md:text-[15px]" }, "Define reusable discovery surfaces, then place eligible public collections into manual or campaign-specific slots with clear timing and notes."), notice ? /* @__PURE__ */ React.createElement("p", { className: "mt-4 text-sm text-sky-100" }, notice) : null), /* @__PURE__ */ React.createElement("section", { className: "mt-8 grid gap-6 xl:grid-cols-[minmax(0,0.95fr)_minmax(0,1.05fr)]" }, /* @__PURE__ */ React.createElement("form", { onSubmit: handleDefinitionSubmit, className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Surface Definition"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Rules and ranking")), definitionForm.id ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm text-slate-300" }, "Editing ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, definitionForm.surface_key)) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$2, { label: "Surface Key", help: definitionForm.id ? "Surface keys stay stable during edits so existing placements remain attached." : null }, /* @__PURE__ */ React.createElement("input", { value: definitionForm.surface_key, onChange: (event) => setDefinitionForm((current) => ({ ...current, surface_key: event.target.value })), disabled: Boolean(definitionForm.id), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none disabled:cursor-not-allowed disabled:opacity-60", maxLength: 120 })), /* @__PURE__ */ React.createElement(Field$2, { label: "Title" }, /* @__PURE__ */ React.createElement("input", { value: definitionForm.title, onChange: (event) => setDefinitionForm((current) => ({ ...current, title: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 160 })), /* @__PURE__ */ React.createElement(Field$2, { label: "Mode" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: definitionForm.mode, onChange: (val) => setDefinitionForm((current) => ({ ...current, mode: val })), searchable: false, options: [{ value: "manual", label: "Manual" }, { value: "automatic", label: "Automatic" }, { value: "hybrid", label: "Hybrid" }] })), /* @__PURE__ */ React.createElement(Field$2, { label: "Ranking" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: definitionForm.ranking_mode, onChange: (val) => setDefinitionForm((current) => ({ ...current, ranking_mode: val })), searchable: false, options: [{ value: "ranking_score", label: "Ranking score" }, { value: "recent_activity", label: "Recent activity" }, { value: "quality_score", label: "Quality score" }] })), /* @__PURE__ */ React.createElement(Field$2, { label: "Max Items" }, /* @__PURE__ */ React.createElement("input", { type: "number", min: "1", max: "24", value: definitionForm.max_items, onChange: (event) => setDefinitionForm((current) => ({ ...current, max_items: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement(Field$2, { label: "Starts At", help: "Optional activation window for the full surface definition." }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: definitionForm.starts_at, onChange: (nextValue) => setDefinitionForm((current) => ({ ...current, starts_at: nextValue })), placeholder: "Start time", clearable: true, className: "bg-white/[0.04]" })), /* @__PURE__ */ React.createElement(Field$2, { label: "Ends At", help: "Leave blank when the surface should stay live until staff changes it." }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: definitionForm.ends_at, onChange: (nextValue) => setDefinitionForm((current) => ({ ...current, ends_at: nextValue })), placeholder: "End time", clearable: true, className: "bg-white/[0.04]" })), /* @__PURE__ */ React.createElement(Field$2, { label: "Fallback Surface Key", help: "Optional fallback when this definition is inactive, scheduled out, or resolves no items." }, /* @__PURE__ */ React.createElement("input", { value: definitionForm.fallback_surface_key, onChange: (event) => setDefinitionForm((current) => ({ ...current, fallback_surface_key: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 120 })), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-white" }, /* @__PURE__ */ React.createElement(Checkbox, { checked: definitionForm.is_active, onChange: (event) => setDefinitionForm((current) => ({ ...current, is_active: event.target.checked })), label: "Active" }))), /* @__PURE__ */ React.createElement(Field$2, { label: "Description", help: "Operational note for staff browsing this surface later." }, /* @__PURE__ */ React.createElement("textarea", { value: definitionForm.description, onChange: (event) => setDefinitionForm((current) => ({ ...current, description: event.target.value })), className: "mt-4 min-h-[96px] w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 400 })), /* @__PURE__ */ React.createElement(Field$2, { label: "Rules JSON", help: "Supported filters include campaign, event, season, type, presentation_style, theme_token, collaboration_mode, owner_username or owner_usernames, commercial_eligible_only, analytics_enabled_only, min_quality_score, min_ranking_score, include_collection_ids, exclude_collection_ids, and featured_only." }, /* @__PURE__ */ React.createElement("textarea", { value: definitionForm.rules_json, onChange: (event) => setDefinitionForm((current) => ({ ...current, rules_json: event.target.value })), className: "mt-4 min-h-[160px] w-full rounded-2xl border border-white/10 bg-slate-950/50 px-4 py-3 font-mono text-sm text-white outline-none", spellCheck: false })), /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-wrap items-center gap-3" }, /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: busy === "definition", className: "inline-flex items-center gap-2 rounded-2xl border border-sky-300/20 bg-sky-400/10 px-5 py-3 text-sm font-semibold text-sky-100 transition hover:bg-sky-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === "definition" ? "fa-circle-notch fa-spin" : "fa-layer-group"} fa-fw` }), definitionForm.id ? "Update Definition" : "Save Definition"), definitionForm.id ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: resetDefinitionForm, className: "inline-flex items-center gap-2 rounded-2xl border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-rotate-left fa-fw" }), "Cancel Edit") : null)), /* @__PURE__ */ React.createElement("form", { onSubmit: handlePlacementSubmit, className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-amber-200/80" }, "Surface Placement"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Manual and campaign slots")), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$2, { label: "Surface" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: placementForm.surface_key, onChange: (val) => setPlacementForm((current) => ({ ...current, surface_key: val })), options: surfaceKeyOptions.map((o) => ({ value: o, label: o })) })), /* @__PURE__ */ React.createElement(Field$2, { label: "Collection" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: String(placementForm.collection_id || ""), onChange: (val) => setPlacementForm((current) => ({ ...current, collection_id: val })), options: collectionOptions.map((o) => ({ value: String(o.id), label: o.title })) })), /* @__PURE__ */ React.createElement(Field$2, { label: "Placement Type" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: placementForm.placement_type, onChange: (val) => setPlacementForm((current) => ({ ...current, placement_type: val })), searchable: false, options: [{ value: "manual", label: "Manual" }, { value: "campaign", label: "Campaign" }, { value: "scheduled_override", label: "Scheduled override" }] })), /* @__PURE__ */ React.createElement(Field$2, { label: "Priority" }, /* @__PURE__ */ React.createElement("input", { type: "number", min: "-100", max: "100", value: placementForm.priority, onChange: (event) => setPlacementForm((current) => ({ ...current, priority: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement(Field$2, { label: "Starts At" }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: placementForm.starts_at, onChange: (nextValue) => setPlacementForm((current) => ({ ...current, starts_at: nextValue })), placeholder: "Start time", clearable: true, className: "bg-white/[0.04]" })), /* @__PURE__ */ React.createElement(Field$2, { label: "Ends At" }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: placementForm.ends_at, onChange: (nextValue) => setPlacementForm((current) => ({ ...current, ends_at: nextValue })), placeholder: "End time", clearable: true, className: "bg-white/[0.04]" })), /* @__PURE__ */ React.createElement(Field$2, { label: "Campaign Key", help: "Optional campaign label for reporting and grouped overrides." }, /* @__PURE__ */ React.createElement("input", { value: placementForm.campaign_key, onChange: (event) => setPlacementForm((current) => ({ ...current, campaign_key: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-white" }, /* @__PURE__ */ React.createElement(Checkbox, { checked: placementForm.is_active, onChange: (event) => setPlacementForm((current) => ({ ...current, is_active: event.target.checked })), label: "Active placement" }))), /* @__PURE__ */ React.createElement(Field$2, { label: "Notes", help: "Internal note for why this collection owns the slot." }, /* @__PURE__ */ React.createElement("textarea", { value: placementForm.notes, onChange: (event) => setPlacementForm((current) => ({ ...current, notes: event.target.value })), className: "mt-4 min-h-[110px] w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 1e3 })), /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-wrap items-center gap-3" }, /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: busy === "placement", className: "inline-flex items-center gap-2 rounded-2xl border border-amber-300/20 bg-amber-400/10 px-5 py-3 text-sm font-semibold text-amber-100 transition hover:bg-amber-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === "placement" ? "fa-circle-notch fa-spin" : "fa-thumbtack"} fa-fw` }), placementForm.id ? "Update Placement" : "Save Placement"), placementForm.id ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: resetPlacementForm, className: "inline-flex items-center gap-2 rounded-2xl border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-rotate-left fa-fw" }), "Cancel Edit") : null))), /* @__PURE__ */ React.createElement("section", { className: "mt-8 rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-lime-200/80" }, "Batch Editorial Tools"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Campaign planning in one pass")), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold text-slate-300" }, batchForm.collection_ids.length, " selected")), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-6 xl:grid-cols-[minmax(0,0.95fr)_minmax(0,1.05fr)]" }, /* @__PURE__ */ React.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[26px] border border-white/10 bg-slate-950/40 p-5" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm font-semibold text-white" }, "Choose collections"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-slate-300" }, "The selector uses current public discovery candidates so staff can quickly prepare a seasonal or editorial run."), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 md:grid-cols-2" }, collectionOptions.map((option) => { const checked = batchForm.collection_ids.includes(option.id); return /* @__PURE__ */ React.createElement("label", { key: option.id, className: `flex cursor-pointer items-start gap-3 rounded-[22px] border px-4 py-3 transition ${checked ? "border-lime-300/30 bg-lime-400/10" : "border-white/10 bg-white/[0.04] hover:bg-white/[0.07]"}` }, /* @__PURE__ */ React.createElement(Checkbox, { checked, onChange: () => toggleBatchCollection(option.id) }), /* @__PURE__ */ React.createElement("span", { className: "min-w-0" }, /* @__PURE__ */ React.createElement("span", { className: "block truncate text-sm font-semibold text-white" }, option.title), /* @__PURE__ */ React.createElement("span", { className: "mt-1 block text-xs text-slate-400" }, option.type || "collection", " · ", option.visibility || "public"))); - }))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[26px] border border-white/10 bg-slate-950/40 p-5" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm font-semibold text-white" }, "Campaign metadata"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-4 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$1, { label: "Campaign Key" }, /* @__PURE__ */ React.createElement("input", { value: batchForm.campaign_key, onChange: (event) => setBatchForm((current) => ({ ...current, campaign_key: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$1, { label: "Campaign Label" }, /* @__PURE__ */ React.createElement("input", { value: batchForm.campaign_label, onChange: (event) => setBatchForm((current) => ({ ...current, campaign_label: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 120 })), /* @__PURE__ */ React.createElement(Field$1, { label: "Event Label" }, /* @__PURE__ */ React.createElement("input", { value: batchForm.event_label, onChange: (event) => setBatchForm((current) => ({ ...current, event_label: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 120 })), /* @__PURE__ */ React.createElement(Field$1, { label: "Season Key" }, /* @__PURE__ */ React.createElement("input", { value: batchForm.season_key, onChange: (event) => setBatchForm((current) => ({ ...current, season_key: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 }))), /* @__PURE__ */ React.createElement(Field$1, { label: "Editorial Notes", help: "Shared context recorded on each selected collection." }, /* @__PURE__ */ React.createElement("textarea", { value: batchForm.editorial_notes, onChange: (event) => setBatchForm((current) => ({ ...current, editorial_notes: event.target.value })), className: "mt-4 min-h-[120px] w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 4e3 })))), /* @__PURE__ */ React.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[26px] border border-white/10 bg-slate-950/40 p-5" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm font-semibold text-white" }, "Optional placement plan"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-slate-300" }, "If you set a surface, the preview shows which collections can safely be placed and which ones will be skipped."), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-4 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$1, { label: "Surface" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: batchForm.surface_key, onChange: (val) => setBatchForm((current) => ({ ...current, surface_key: val })), placeholder: "No placement", options: surfaceKeyOptions.map((o) => ({ value: o, label: o })) })), /* @__PURE__ */ React.createElement(Field$1, { label: "Placement Type" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: batchForm.placement_type, onChange: (val) => setBatchForm((current) => ({ ...current, placement_type: val })), searchable: false, options: [{ value: "campaign", label: "Campaign" }, { value: "manual", label: "Manual" }, { value: "scheduled_override", label: "Scheduled override" }] })), /* @__PURE__ */ React.createElement(Field$1, { label: "Priority" }, /* @__PURE__ */ React.createElement("input", { type: "number", min: "-100", max: "100", value: batchForm.priority, onChange: (event) => setBatchForm((current) => ({ ...current, priority: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-white" }, /* @__PURE__ */ React.createElement(Checkbox, { checked: batchForm.is_active, onChange: (event) => setBatchForm((current) => ({ ...current, is_active: event.target.checked })), label: "Active placement" })), /* @__PURE__ */ React.createElement(Field$1, { label: "Starts At" }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: batchForm.starts_at, onChange: (nextValue) => setBatchForm((current) => ({ ...current, starts_at: nextValue })), placeholder: "Start time", clearable: true, className: "bg-white/[0.04]" })), /* @__PURE__ */ React.createElement(Field$1, { label: "Ends At" }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: batchForm.ends_at, onChange: (nextValue) => setBatchForm((current) => ({ ...current, ends_at: nextValue })), placeholder: "End time", clearable: true, className: "bg-white/[0.04]" }))), /* @__PURE__ */ React.createElement(Field$1, { label: "Placement Notes" }, /* @__PURE__ */ React.createElement("textarea", { value: batchForm.notes, onChange: (event) => setBatchForm((current) => ({ ...current, notes: event.target.value })), className: "mt-4 min-h-[110px] w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 1e3 })), /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-wrap gap-3" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => handleBatchEditorial("preview"), disabled: busy === "batch-preview", className: "inline-flex items-center gap-2 rounded-2xl border border-lime-300/20 bg-lime-400/10 px-5 py-3 text-sm font-semibold text-lime-100 transition hover:bg-lime-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === "batch-preview" ? "fa-circle-notch fa-spin" : "fa-flask"} fa-fw` }), "Preview Batch"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => handleBatchEditorial("apply"), disabled: busy === "batch-apply", className: "inline-flex items-center gap-2 rounded-2xl border border-amber-300/20 bg-amber-400/10 px-5 py-3 text-sm font-semibold text-amber-100 transition hover:bg-amber-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === "batch-apply" ? "fa-circle-notch fa-spin" : "fa-wand-magic-sparkles"} fa-fw` }), "Apply Batch"))), batchResult ? /* @__PURE__ */ React.createElement("div", { className: "rounded-[26px] border border-white/10 bg-slate-950/40 p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-sm font-semibold text-white" }, "Preview results"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-300" }, batchResult.collections_count, " collections reviewed, ", batchResult.placement_eligible_count, " placement-ready."))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, (batchResult.items || []).map((item) => /* @__PURE__ */ React.createElement("div", { key: item.collection?.id, className: "rounded-[22px] border border-white/10 bg-white/[0.04] p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-sm font-semibold text-white" }, item.collection?.title), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-xs text-slate-400" }, item.collection?.visibility, " · ", item.collection?.lifecycle_state, " · ", item.collection?.moderation_status)), item.placement ? /* @__PURE__ */ React.createElement("span", { className: `rounded-full border px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] ${item.placement.eligible ? "border-lime-300/20 bg-lime-400/10 text-lime-100" : "border-rose-300/20 bg-rose-400/10 text-rose-100"}` }, item.placement.eligible ? `ready for ${item.placement.surface_key}` : "placement skipped") : /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-300" }, "metadata only")), item.eligibility?.reasons?.length ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-xs text-amber-100/80" }, "Campaign readiness: ", item.eligibility.reasons.join(" ")) : null, item.placement?.reasons?.length ? /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-xs text-rose-100/80" }, "Placement: ", item.placement.reasons.join(" ")) : null)))) : null))), /* @__PURE__ */ React.createElement("section", { className: "mt-8 rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Definitions"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Registered surfaces")), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold text-slate-300" }, definitions.length)), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 lg:grid-cols-2" }, definitions.map((definition2) => /* @__PURE__ */ React.createElement("div", { key: definition2.id, className: "rounded-[24px] border border-white/10 bg-slate-950/40 p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2" }, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-sky-300/20 bg-sky-400/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-sky-100" }, definition2.surface_key), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-300" }, definition2.mode)), /* @__PURE__ */ React.createElement("h3", { className: "mt-4 text-lg font-semibold text-white" }, definition2.title), definition2.description ? /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-slate-300" }, definition2.description) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2 text-xs text-slate-400" }, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1" }, definition2.ranking_mode), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1" }, "max ", definition2.max_items), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1" }, definition2.is_active ? "active" : "inactive"), definition2.starts_at ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1" }, "starts ", new Date(definition2.starts_at).toLocaleString()) : null, definition2.ends_at ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1" }, "ends ", new Date(definition2.ends_at).toLocaleString()) : null, definition2.fallback_surface_key ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1" }, "fallback ", definition2.fallback_surface_key) : null), /* @__PURE__ */ React.createElement("div", { className: "mt-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => hydrateDefinition(definition2), className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-xs font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-pen fa-fw text-[10px]" }), "Edit Definition"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => handleDeleteDefinition(definition2), disabled: busy === `delete-definition-${definition2.id}`, className: "inline-flex items-center gap-2 rounded-full border border-rose-300/20 bg-rose-400/10 px-4 py-2 text-xs font-semibold text-rose-100 transition hover:bg-rose-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === `delete-definition-${definition2.id}` ? "fa-circle-notch fa-spin" : "fa-trash"} fa-fw text-[10px]` }), "Delete"))))))), conflicts.length ? /* @__PURE__ */ React.createElement("section", { className: "mt-8 rounded-[32px] border border-rose-300/20 bg-rose-500/10 p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-rose-100/80" }, "Conflicts"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Schedule overlaps need review")), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-rose-300/20 bg-rose-400/10 px-3 py-1 text-xs font-semibold text-rose-100" }, conflicts.length)), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 lg:grid-cols-2" }, conflicts.map((conflict, index2) => /* @__PURE__ */ React.createElement("div", { key: `${conflict.surface_key}-${index2}`, className: "rounded-[24px] border border-rose-300/20 bg-slate-950/40 p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2" }, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-rose-300/20 bg-rose-400/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-rose-100" }, conflict.surface_key)), /* @__PURE__ */ React.createElement("p", { className: "mt-4 text-sm text-rose-50" }, conflict.summary), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-xs text-rose-100/70" }, "Window: ", conflict.window?.starts_at ? new Date(conflict.window.starts_at).toLocaleString() : "Immediate", " to ", conflict.window?.ends_at ? new Date(conflict.window.ends_at).toLocaleString() : "Open-ended"))))) : null, /* @__PURE__ */ React.createElement("section", { className: "mt-8 rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-amber-200/80" }, "Placements"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Active and scheduled slots")), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold text-slate-300" }, placements.length)), /* @__PURE__ */ React.createElement("div", { className: "mt-6 space-y-5" }, placements.map((placement) => /* @__PURE__ */ React.createElement("div", { key: placement.id, className: "rounded-[28px] border border-white/10 bg-slate-950/40 p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2" }, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-amber-300/20 bg-amber-400/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-amber-100" }, placement.surface_key), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-300" }, placement.placement_type), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-300" }, "priority ", placement.priority), conflictPlacementIds.has(placement.id) || placement.has_conflict ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-rose-300/20 bg-rose-400/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-rose-100" }, "conflict") : null), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => hydratePlacement(placement), className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-xs font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-pen fa-fw text-[10px]" }), "Edit"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => handleDeletePlacement(placement), disabled: busy === `delete-placement-${placement.id}`, className: "inline-flex items-center gap-2 rounded-full border border-rose-300/20 bg-rose-400/10 px-4 py-2 text-xs font-semibold text-rose-100 transition hover:bg-rose-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === `delete-placement-${placement.id}` ? "fa-circle-notch fa-spin" : "fa-trash"} fa-fw text-[10px]` }), "Delete"))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-5 xl:grid-cols-[minmax(0,1fr)_280px]" }, /* @__PURE__ */ React.createElement("div", null, placement.collection ? /* @__PURE__ */ React.createElement(CollectionCard, { collection: placement.collection, isOwner: true }) : null), /* @__PURE__ */ React.createElement("div", { className: "space-y-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-white/[0.04] px-4 py-3" }, "Starts: ", placement.starts_at ? new Date(placement.starts_at).toLocaleString() : "Immediate"), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-white/[0.04] px-4 py-3" }, "Ends: ", placement.ends_at ? new Date(placement.ends_at).toLocaleString() : "Open-ended"), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-white/[0.04] px-4 py-3" }, "Campaign: ", placement.campaign_key || "None"), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-white/[0.04] px-4 py-3" }, "Status: ", placement.is_active ? "Active" : "Inactive"), placement.notes ? /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-white/[0.04] px-4 py-3 text-slate-300" }, placement.notes) : null))))))))); + }))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[26px] border border-white/10 bg-slate-950/40 p-5" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm font-semibold text-white" }, "Campaign metadata"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-4 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$2, { label: "Campaign Key" }, /* @__PURE__ */ React.createElement("input", { value: batchForm.campaign_key, onChange: (event) => setBatchForm((current) => ({ ...current, campaign_key: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 })), /* @__PURE__ */ React.createElement(Field$2, { label: "Campaign Label" }, /* @__PURE__ */ React.createElement("input", { value: batchForm.campaign_label, onChange: (event) => setBatchForm((current) => ({ ...current, campaign_label: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 120 })), /* @__PURE__ */ React.createElement(Field$2, { label: "Event Label" }, /* @__PURE__ */ React.createElement("input", { value: batchForm.event_label, onChange: (event) => setBatchForm((current) => ({ ...current, event_label: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 120 })), /* @__PURE__ */ React.createElement(Field$2, { label: "Season Key" }, /* @__PURE__ */ React.createElement("input", { value: batchForm.season_key, onChange: (event) => setBatchForm((current) => ({ ...current, season_key: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 80 }))), /* @__PURE__ */ React.createElement(Field$2, { label: "Editorial Notes", help: "Shared context recorded on each selected collection." }, /* @__PURE__ */ React.createElement("textarea", { value: batchForm.editorial_notes, onChange: (event) => setBatchForm((current) => ({ ...current, editorial_notes: event.target.value })), className: "mt-4 min-h-[120px] w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 4e3 })))), /* @__PURE__ */ React.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[26px] border border-white/10 bg-slate-950/40 p-5" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm font-semibold text-white" }, "Optional placement plan"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-slate-300" }, "If you set a surface, the preview shows which collections can safely be placed and which ones will be skipped."), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-4 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$2, { label: "Surface" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: batchForm.surface_key, onChange: (val) => setBatchForm((current) => ({ ...current, surface_key: val })), placeholder: "No placement", options: surfaceKeyOptions.map((o) => ({ value: o, label: o })) })), /* @__PURE__ */ React.createElement(Field$2, { label: "Placement Type" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: batchForm.placement_type, onChange: (val) => setBatchForm((current) => ({ ...current, placement_type: val })), searchable: false, options: [{ value: "campaign", label: "Campaign" }, { value: "manual", label: "Manual" }, { value: "scheduled_override", label: "Scheduled override" }] })), /* @__PURE__ */ React.createElement(Field$2, { label: "Priority" }, /* @__PURE__ */ React.createElement("input", { type: "number", min: "-100", max: "100", value: batchForm.priority, onChange: (event) => setBatchForm((current) => ({ ...current, priority: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-white" }, /* @__PURE__ */ React.createElement(Checkbox, { checked: batchForm.is_active, onChange: (event) => setBatchForm((current) => ({ ...current, is_active: event.target.checked })), label: "Active placement" })), /* @__PURE__ */ React.createElement(Field$2, { label: "Starts At" }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: batchForm.starts_at, onChange: (nextValue) => setBatchForm((current) => ({ ...current, starts_at: nextValue })), placeholder: "Start time", clearable: true, className: "bg-white/[0.04]" })), /* @__PURE__ */ React.createElement(Field$2, { label: "Ends At" }, /* @__PURE__ */ React.createElement(DateTimePicker, { value: batchForm.ends_at, onChange: (nextValue) => setBatchForm((current) => ({ ...current, ends_at: nextValue })), placeholder: "End time", clearable: true, className: "bg-white/[0.04]" }))), /* @__PURE__ */ React.createElement(Field$2, { label: "Placement Notes" }, /* @__PURE__ */ React.createElement("textarea", { value: batchForm.notes, onChange: (event) => setBatchForm((current) => ({ ...current, notes: event.target.value })), className: "mt-4 min-h-[110px] w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none", maxLength: 1e3 })), /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-wrap gap-3" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => handleBatchEditorial("preview"), disabled: busy === "batch-preview", className: "inline-flex items-center gap-2 rounded-2xl border border-lime-300/20 bg-lime-400/10 px-5 py-3 text-sm font-semibold text-lime-100 transition hover:bg-lime-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === "batch-preview" ? "fa-circle-notch fa-spin" : "fa-flask"} fa-fw` }), "Preview Batch"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => handleBatchEditorial("apply"), disabled: busy === "batch-apply", className: "inline-flex items-center gap-2 rounded-2xl border border-amber-300/20 bg-amber-400/10 px-5 py-3 text-sm font-semibold text-amber-100 transition hover:bg-amber-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === "batch-apply" ? "fa-circle-notch fa-spin" : "fa-wand-magic-sparkles"} fa-fw` }), "Apply Batch"))), batchResult ? /* @__PURE__ */ React.createElement("div", { className: "rounded-[26px] border border-white/10 bg-slate-950/40 p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-sm font-semibold text-white" }, "Preview results"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-300" }, batchResult.collections_count, " collections reviewed, ", batchResult.placement_eligible_count, " placement-ready."))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, (batchResult.items || []).map((item) => /* @__PURE__ */ React.createElement("div", { key: item.collection?.id, className: "rounded-[22px] border border-white/10 bg-white/[0.04] p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-sm font-semibold text-white" }, item.collection?.title), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-xs text-slate-400" }, item.collection?.visibility, " · ", item.collection?.lifecycle_state, " · ", item.collection?.moderation_status)), item.placement ? /* @__PURE__ */ React.createElement("span", { className: `rounded-full border px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] ${item.placement.eligible ? "border-lime-300/20 bg-lime-400/10 text-lime-100" : "border-rose-300/20 bg-rose-400/10 text-rose-100"}` }, item.placement.eligible ? `ready for ${item.placement.surface_key}` : "placement skipped") : /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-300" }, "metadata only")), item.eligibility?.reasons?.length ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-xs text-amber-100/80" }, "Campaign readiness: ", item.eligibility.reasons.join(" ")) : null, item.placement?.reasons?.length ? /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-xs text-rose-100/80" }, "Placement: ", item.placement.reasons.join(" ")) : null)))) : null))), /* @__PURE__ */ React.createElement("section", { className: "mt-8 rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Definitions"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Registered surfaces")), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold text-slate-300" }, definitions.length)), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 lg:grid-cols-2" }, definitions.map((definition2) => /* @__PURE__ */ React.createElement("div", { key: definition2.id, className: "rounded-[24px] border border-white/10 bg-slate-950/40 p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2" }, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-sky-300/20 bg-sky-400/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-sky-100" }, definition2.surface_key), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-300" }, definition2.mode)), /* @__PURE__ */ React.createElement("h3", { className: "mt-4 text-lg font-semibold text-white" }, definition2.title), definition2.description ? /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-slate-300" }, definition2.description) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2 text-xs text-slate-400" }, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1" }, definition2.ranking_mode), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1" }, "max ", definition2.max_items), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1" }, definition2.is_active ? "active" : "inactive"), definition2.starts_at ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1" }, "starts ", new Date(definition2.starts_at).toLocaleString()) : null, definition2.ends_at ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1" }, "ends ", new Date(definition2.ends_at).toLocaleString()) : null, definition2.fallback_surface_key ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1" }, "fallback ", definition2.fallback_surface_key) : null), /* @__PURE__ */ React.createElement("div", { className: "mt-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => hydrateDefinition(definition2), className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-xs font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-pen fa-fw text-[10px]" }), "Edit Definition"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => handleDeleteDefinition(definition2), disabled: busy === `delete-definition-${definition2.id}`, className: "inline-flex items-center gap-2 rounded-full border border-rose-300/20 bg-rose-400/10 px-4 py-2 text-xs font-semibold text-rose-100 transition hover:bg-rose-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === `delete-definition-${definition2.id}` ? "fa-circle-notch fa-spin" : "fa-trash"} fa-fw text-[10px]` }), "Delete"))))))), conflicts.length ? /* @__PURE__ */ React.createElement("section", { className: "mt-8 rounded-[32px] border border-rose-300/20 bg-rose-500/10 p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-rose-100/80" }, "Conflicts"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Schedule overlaps need review")), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-rose-300/20 bg-rose-400/10 px-3 py-1 text-xs font-semibold text-rose-100" }, conflicts.length)), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 lg:grid-cols-2" }, conflicts.map((conflict, index2) => /* @__PURE__ */ React.createElement("div", { key: `${conflict.surface_key}-${index2}`, className: "rounded-[24px] border border-rose-300/20 bg-slate-950/40 p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2" }, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-rose-300/20 bg-rose-400/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-rose-100" }, conflict.surface_key)), /* @__PURE__ */ React.createElement("p", { className: "mt-4 text-sm text-rose-50" }, conflict.summary), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-xs text-rose-100/70" }, "Window: ", conflict.window?.starts_at ? new Date(conflict.window.starts_at).toLocaleString() : "Immediate", " to ", conflict.window?.ends_at ? new Date(conflict.window.ends_at).toLocaleString() : "Open-ended"))))) : null, /* @__PURE__ */ React.createElement("section", { className: "mt-8 rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-amber-200/80" }, "Placements"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Active and scheduled slots")), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold text-slate-300" }, placements.length)), /* @__PURE__ */ React.createElement("div", { className: "mt-6 space-y-5" }, placements.map((placement) => /* @__PURE__ */ React.createElement("div", { key: placement.id, className: "rounded-[28px] border border-white/10 bg-slate-950/40 p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2" }, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-amber-300/20 bg-amber-400/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-amber-100" }, placement.surface_key), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-300" }, placement.placement_type), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-300" }, "priority ", placement.priority), conflictPlacementIds.has(placement.id) || placement.has_conflict ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-rose-300/20 bg-rose-400/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-rose-100" }, "conflict") : null), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => hydratePlacement(placement), className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-xs font-semibold text-white transition hover:bg-white/[0.07]" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-pen fa-fw text-[10px]" }), "Edit"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => handleDeletePlacement(placement), disabled: busy === `delete-placement-${placement.id}`, className: "inline-flex items-center gap-2 rounded-full border border-rose-300/20 bg-rose-400/10 px-4 py-2 text-xs font-semibold text-rose-100 transition hover:bg-rose-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === `delete-placement-${placement.id}` ? "fa-circle-notch fa-spin" : "fa-trash"} fa-fw text-[10px]` }), "Delete"))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-5 xl:grid-cols-[minmax(0,1fr)_280px]" }, /* @__PURE__ */ React.createElement("div", null, placement.collection ? /* @__PURE__ */ React.createElement(CollectionCard, { collection: placement.collection, isOwner: true }) : null), /* @__PURE__ */ React.createElement("div", { className: "space-y-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-white/[0.04] px-4 py-3" }, "Starts: ", placement.starts_at ? new Date(placement.starts_at).toLocaleString() : "Immediate"), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-white/[0.04] px-4 py-3" }, "Ends: ", placement.ends_at ? new Date(placement.ends_at).toLocaleString() : "Open-ended"), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-white/[0.04] px-4 py-3" }, "Campaign: ", placement.campaign_key || "None"), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-white/[0.04] px-4 py-3" }, "Status: ", placement.is_active ? "Active" : "Inactive"), placement.notes ? /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-white/[0.04] px-4 py-3 text-slate-300" }, placement.notes) : null))))))))); } const __vite_glob_0_49 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, @@ -80577,7 +81135,7 @@ function NovaCardCanvasPreview({ card, fonts = [], className = "", editable = fa ); })); } -function requestJson$h(url, { method = "GET", body: body2 } = {}) { +function requestJson$i(url, { method = "GET", body: body2 } = {}) { return fetch(url, { method, credentials: "same-origin", @@ -80637,7 +81195,7 @@ function NovaCardsAdminIndex() { setReportsError(""); try { const separator = String(endpoints.reportsQueue).includes("?") ? "&" : "?"; - const response = await requestJson$h(`${endpoints.reportsQueue}${separator}status=${reportStatus}`); + const response = await requestJson$i(`${endpoints.reportsQueue}${separator}status=${reportStatus}`); if (!active) return; setReports(response.data || []); setReportsMeta(response.meta || { total: 0 }); @@ -80676,7 +81234,7 @@ function NovaCardsAdminIndex() { }; }, [endpoints.reportsQueue, reportStatus]); async function updateCard(cardId, patch2) { - const response = await requestJson$h(String(endpoints.updateCardPattern || "").replace("__CARD__", String(cardId)), { + const response = await requestJson$i(String(endpoints.updateCardPattern || "").replace("__CARD__", String(cardId)), { method: "PATCH", body: patch2 }); @@ -80687,7 +81245,7 @@ function NovaCardsAdminIndex() { setCards((current) => current.map((card) => card.id === cardId ? response.card : card)); } async function updateCreator(creatorId, patch2) { - const response = await requestJson$h(String(endpoints.updateCreatorPattern || "").replace("__CREATOR__", String(creatorId)), { + const response = await requestJson$i(String(endpoints.updateCreatorPattern || "").replace("__CREATOR__", String(creatorId)), { method: "PATCH", body: patch2 }); @@ -80722,7 +81280,7 @@ function NovaCardsAdminIndex() { async function saveCategory(category) { const isExisting = Boolean(category.id); const url = isExisting ? String(endpoints.updateCategoryPattern || "").replace("__CATEGORY__", String(category.id)) : endpoints.storeCategory; - const response = await requestJson$h(url, { + const response = await requestJson$i(url, { method: isExisting ? "PATCH" : "POST", body: category }); @@ -80743,7 +81301,7 @@ function NovaCardsAdminIndex() { } setReportBusy((current) => ({ ...current, [reportId]: true })); try { - const response = await requestJson$h(String(endpoints.updateReportPattern || "").replace("__REPORT__", String(reportId)), { + const response = await requestJson$i(String(endpoints.updateReportPattern || "").replace("__REPORT__", String(reportId)), { method: "PATCH", body: patch2 }); @@ -80759,7 +81317,7 @@ function NovaCardsAdminIndex() { } setReportBusy((current) => ({ ...current, [reportId]: true })); try { - const response = await requestJson$h(String(endpoints.moderateReportTargetPattern || "").replace("__REPORT__", String(reportId)), { + const response = await requestJson$i(String(endpoints.moderateReportTargetPattern || "").replace("__REPORT__", String(reportId)), { method: "POST", body: { action, disposition: reportDispositions[reportId] || null } }); @@ -80860,7 +81418,7 @@ const __vite_glob_0_51 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de __proto__: null, default: NovaCardsAdminIndex }, Symbol.toStringTag, { value: "Module" })); -function requestJson$g(url, { method = "GET", body: body2 } = {}) { +function requestJson$h(url, { method = "GET", body: body2 } = {}) { return fetch(url, { method, credentials: "same-origin", @@ -80904,7 +81462,7 @@ function NovaCardsAssetPackAdmin() { async function savePack() { const isExisting = Boolean(selectedId); const url = isExisting ? String(endpoints.updatePattern || "").replace("__PACK__", String(selectedId)) : endpoints.store; - const response = await requestJson$g(url, { method: isExisting ? "PATCH" : "POST", body: form }); + const response = await requestJson$h(url, { method: isExisting ? "PATCH" : "POST", body: form }); if (isExisting) { setPacks((current) => current.map((pack) => pack.id === selectedId ? response.pack : pack)); } else { @@ -80923,7 +81481,7 @@ const __vite_glob_0_52 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de __proto__: null, default: NovaCardsAssetPackAdmin }, Symbol.toStringTag, { value: "Module" })); -function requestJson$f(url, { method = "GET", body: body2 } = {}) { +function requestJson$g(url, { method = "GET", body: body2 } = {}) { return fetch(url, { method, credentials: "same-origin", @@ -80970,7 +81528,7 @@ function NovaCardsChallengeAdmin() { async function saveChallenge() { const isExisting = Boolean(selectedId); const url = isExisting ? String(endpoints.updatePattern || "").replace("__CHALLENGE__", String(selectedId)) : endpoints.store; - const response = await requestJson$f(url, { method: isExisting ? "PATCH" : "POST", body: { ...form, winner_card_id: form.winner_card_id || null } }); + const response = await requestJson$g(url, { method: isExisting ? "PATCH" : "POST", body: { ...form, winner_card_id: form.winner_card_id || null } }); if (isExisting) { setChallenges((current) => current.map((challenge) => challenge.id === selectedId ? response.challenge : challenge)); } else { @@ -80989,7 +81547,7 @@ const __vite_glob_0_53 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de __proto__: null, default: NovaCardsChallengeAdmin }, Symbol.toStringTag, { value: "Module" })); -function requestJson$e(url, { method = "GET", body: body2 } = {}) { +function requestJson$f(url, { method = "GET", body: body2 } = {}) { return fetch(url, { method, credentials: "same-origin", @@ -81043,7 +81601,7 @@ function NovaCardsCollectionAdmin() { async function saveCollection() { const isExisting = Boolean(selectedId); const url = isExisting ? String(endpoints.updatePattern || "").replace("__COLLECTION__", String(selectedId)) : endpoints.store; - const response = await requestJson$e(url, { method: isExisting ? "PATCH" : "POST", body: form }); + const response = await requestJson$f(url, { method: isExisting ? "PATCH" : "POST", body: form }); if (isExisting) { setCollections((current) => current.map((entry) => entry.id === selectedId ? response.collection : entry)); } else { @@ -81053,7 +81611,7 @@ function NovaCardsCollectionAdmin() { } async function attachCard() { if (!selectedId || !cardId) return; - const response = await requestJson$e(String(endpoints.attachCardPattern || "").replace("__COLLECTION__", String(selectedId)), { + const response = await requestJson$f(String(endpoints.attachCardPattern || "").replace("__COLLECTION__", String(selectedId)), { method: "POST", body: { card_id: Number(cardId), note: cardNote || null } }); @@ -81062,7 +81620,7 @@ function NovaCardsCollectionAdmin() { setCardNote(""); } async function detachCard(collectionId, currentCardId) { - const response = await requestJson$e( + const response = await requestJson$f( String(endpoints.detachCardPattern || "").replace("__COLLECTION__", String(collectionId)).replace("__CARD__", String(currentCardId)), { method: "DELETE" } ); @@ -81074,7 +81632,7 @@ const __vite_glob_0_54 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de __proto__: null, default: NovaCardsCollectionAdmin }, Symbol.toStringTag, { value: "Module" })); -function requestJson$d(url, { method = "GET", body: body2 } = {}) { +function requestJson$e(url, { method = "GET", body: body2 } = {}) { return fetch(url, { method, credentials: "same-origin", @@ -81153,7 +81711,7 @@ function NovaCardsTemplateAdmin() { async function saveTemplate() { const isExisting = Boolean(selectedId); const url = isExisting ? String(endpoints.updatePattern || "").replace("__TEMPLATE__", String(selectedId)) : endpoints.store; - const response = await requestJson$d(url, { + const response = await requestJson$e(url, { method: isExisting ? "PATCH" : "POST", body: form }); @@ -81183,7 +81741,7 @@ function getCsrfToken$6() { if (typeof document === "undefined") return ""; return document.querySelector('meta[name="csrf-token"]')?.getAttribute("content") || ""; } -async function requestJson$c(url, { method = "POST", body: body2 } = {}) { +async function requestJson$d(url, { method = "POST", body: body2 } = {}) { const response = await fetch(url, { method, credentials: "same-origin", @@ -81234,7 +81792,7 @@ function EmptyState$3({ browseUrl }) { function FilterLink({ href, active, children, count }) { return /* @__PURE__ */ React.createElement("a", { href, className: `flex items-center justify-between rounded-2xl border px-4 py-3 text-sm transition ${active ? "border-sky-300/20 bg-sky-400/10 text-sky-100" : "border-white/10 bg-white/[0.04] text-white hover:bg-white/[0.07]"}` }, /* @__PURE__ */ React.createElement("span", null, children), typeof count === "number" ? /* @__PURE__ */ React.createElement("span", { className: `text-xs ${active ? "text-sky-100/80" : "text-slate-400"}` }, count) : null); } -function formatDateTime$1(value) { +function formatDateTime$4(value) { if (!value) return null; const date = new Date(value); if (Number.isNaN(date.getTime())) return null; @@ -81294,7 +81852,7 @@ function SavedCollections() { setBusy("create-list"); setNotice(""); try { - const payload = await requestJson$c(props.endpoints.createList, { + const payload = await requestJson$d(props.endpoints.createList, { method: "POST", body: { title: newListTitle.trim() } }); @@ -81313,7 +81871,7 @@ function SavedCollections() { setBusy(`list-${collectionId}`); setNotice(""); try { - const payload = await requestJson$c(props.endpoints.addToListPattern.replace("__COLLECTION__", String(collectionId)), { + const payload = await requestJson$d(props.endpoints.addToListPattern.replace("__COLLECTION__", String(collectionId)), { method: "POST", body: { saved_list_id: Number(listId) } }); @@ -81330,7 +81888,7 @@ function SavedCollections() { setBusy(`unsave-${collection.id}`); setNotice(""); try { - await requestJson$c(props.endpoints.unsavePattern.replace("__COLLECTION__", String(collection.id)), { + await requestJson$d(props.endpoints.unsavePattern.replace("__COLLECTION__", String(collection.id)), { method: "DELETE" }); setCollections((current) => current.filter((item) => Number(item.id) !== Number(collection.id))); @@ -81350,7 +81908,7 @@ function SavedCollections() { setBusy(`remove-${collection.id}`); setNotice(""); try { - const payload = await requestJson$c( + const payload = await requestJson$d( props.endpoints.removeFromListPattern.replace("__LIST__", String(activeList.id)).replace("__COLLECTION__", String(collection.id)), { method: "DELETE" } ); @@ -81370,7 +81928,7 @@ function SavedCollections() { setBusy(`reorder-${collectionId}`); setNotice(""); try { - await requestJson$c( + await requestJson$d( props.endpoints.reorderItemsPattern.replace("__LIST__", String(activeList.id)), { method: "POST", @@ -81397,7 +81955,7 @@ function SavedCollections() { setBusy(`note-${collectionId}`); setNotice(""); try { - const payload = await requestJson$c( + const payload = await requestJson$d( props.endpoints.updateNotePattern.replace("__COLLECTION__", String(collectionId)), { method: "PATCH", @@ -81429,7 +81987,7 @@ function SavedCollections() { active: activeFilters.sort === option.key }, option.label - )))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-white/[0.04] p-5 backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Saved Lists"), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold text-slate-300" }, savedLists.length)), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, /* @__PURE__ */ React.createElement(FilterLink, { href: buildFilterUrl({ q: activeFilters.q, filter: activeFilters.filter, sort: activeFilters.sort }, libraryUrl), active: !activeFilters.list }, "All saved collections"), savedLists.map((list2) => /* @__PURE__ */ React.createElement(FilterLink, { key: list2.id, href: buildFilterUrl({ q: activeFilters.q, filter: activeFilters.filter, sort: activeFilters.sort }, list2.url || libraryUrl), active: Number(activeFilters.list) === Number(list2.id), count: list2.items_count }, list2.title))), /* @__PURE__ */ React.createElement("form", { onSubmit: handleCreateList, className: "mt-5 space-y-3" }, /* @__PURE__ */ React.createElement("input", { value: newListTitle, onChange: (event) => setNewListTitle(event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35", placeholder: "Create a saved list", maxLength: 120 }), /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: busy === "create-list", className: "inline-flex w-full items-center justify-center gap-2 rounded-2xl border border-sky-300/20 bg-sky-400/10 px-4 py-3 text-sm font-semibold text-sky-100 transition hover:bg-sky-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === "create-list" ? "fa-circle-notch fa-spin" : "fa-folder-plus"} fa-fw` }), "Create list")))), /* @__PURE__ */ React.createElement("div", { className: "space-y-8" }, recentlyRevisited.length ? /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Recently Revisited"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Jump back into active references")), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold text-slate-300" }, recentlyRevisited.length)), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid grid-cols-1 gap-5 xl:grid-cols-3" }, recentlyRevisited.map((collection) => /* @__PURE__ */ React.createElement(CollectionCard, { key: `revisited-${collection.id}`, collection, isOwner: false })))) : null, /* @__PURE__ */ React.createElement("section", null, collections.length ? /* @__PURE__ */ React.createElement("div", { className: "grid grid-cols-1 gap-5 xl:grid-cols-2" }, collections.map((collection, index2) => /* @__PURE__ */ React.createElement("div", { key: collection.id, className: "space-y-3" }, /* @__PURE__ */ React.createElement(CollectionCard, { collection, isOwner: false }), collection.saved_because || collection.last_viewed_at ? /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-3 rounded-[24px] border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-slate-300" }, collection.saved_because ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center gap-2 rounded-full border border-amber-300/20 bg-amber-400/10 px-3 py-2 text-xs font-semibold text-amber-100" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-lightbulb fa-fw" }), collection.saved_because) : null, collection.last_viewed_at ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-[#0d1726] px-3 py-2 text-xs font-semibold text-slate-200" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-clock-rotate-left fa-fw" }), "Last revisited ", formatDateTime$1(collection.last_viewed_at)) : null) : null, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-3 rounded-[24px] border border-white/10 bg-white/[0.04] px-4 py-3" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => handleUnsave(collection), disabled: busy === `unsave-${collection.id}`, className: "inline-flex items-center gap-2 rounded-2xl border border-rose-300/20 bg-rose-400/10 px-4 py-2.5 text-sm font-semibold text-rose-100 transition hover:bg-rose-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === `unsave-${collection.id}` ? "fa-circle-notch fa-spin" : "fa-bookmark-slash"} fa-fw` }), "Remove from saved"), activeList ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => handleRemoveFromList(collection), disabled: busy === `remove-${collection.id}`, className: "inline-flex items-center gap-2 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-2.5 text-sm font-semibold text-white transition hover:bg-white/[0.07] disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === `remove-${collection.id}` ? "fa-circle-notch fa-spin" : "fa-folder-minus"} fa-fw` }), "Remove from list") : null, activeList && collections.length > 1 ? /* @__PURE__ */ React.createElement("div", { className: "ml-auto inline-flex items-center gap-2 rounded-2xl border border-white/10 bg-[#0d1726] p-1" }, /* @__PURE__ */ React.createElement( + )))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-white/[0.04] p-5 backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Saved Lists"), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold text-slate-300" }, savedLists.length)), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, /* @__PURE__ */ React.createElement(FilterLink, { href: buildFilterUrl({ q: activeFilters.q, filter: activeFilters.filter, sort: activeFilters.sort }, libraryUrl), active: !activeFilters.list }, "All saved collections"), savedLists.map((list2) => /* @__PURE__ */ React.createElement(FilterLink, { key: list2.id, href: buildFilterUrl({ q: activeFilters.q, filter: activeFilters.filter, sort: activeFilters.sort }, list2.url || libraryUrl), active: Number(activeFilters.list) === Number(list2.id), count: list2.items_count }, list2.title))), /* @__PURE__ */ React.createElement("form", { onSubmit: handleCreateList, className: "mt-5 space-y-3" }, /* @__PURE__ */ React.createElement("input", { value: newListTitle, onChange: (event) => setNewListTitle(event.target.value), className: "w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35", placeholder: "Create a saved list", maxLength: 120 }), /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: busy === "create-list", className: "inline-flex w-full items-center justify-center gap-2 rounded-2xl border border-sky-300/20 bg-sky-400/10 px-4 py-3 text-sm font-semibold text-sky-100 transition hover:bg-sky-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === "create-list" ? "fa-circle-notch fa-spin" : "fa-folder-plus"} fa-fw` }), "Create list")))), /* @__PURE__ */ React.createElement("div", { className: "space-y-8" }, recentlyRevisited.length ? /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-white/[0.04] p-6 backdrop-blur-sm md:p-7" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Recently Revisited"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Jump back into active references")), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold text-slate-300" }, recentlyRevisited.length)), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid grid-cols-1 gap-5 xl:grid-cols-3" }, recentlyRevisited.map((collection) => /* @__PURE__ */ React.createElement(CollectionCard, { key: `revisited-${collection.id}`, collection, isOwner: false })))) : null, /* @__PURE__ */ React.createElement("section", null, collections.length ? /* @__PURE__ */ React.createElement("div", { className: "grid grid-cols-1 gap-5 xl:grid-cols-2" }, collections.map((collection, index2) => /* @__PURE__ */ React.createElement("div", { key: collection.id, className: "space-y-3" }, /* @__PURE__ */ React.createElement(CollectionCard, { collection, isOwner: false }), collection.saved_because || collection.last_viewed_at ? /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-3 rounded-[24px] border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-slate-300" }, collection.saved_because ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center gap-2 rounded-full border border-amber-300/20 bg-amber-400/10 px-3 py-2 text-xs font-semibold text-amber-100" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-lightbulb fa-fw" }), collection.saved_because) : null, collection.last_viewed_at ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-[#0d1726] px-3 py-2 text-xs font-semibold text-slate-200" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-clock-rotate-left fa-fw" }), "Last revisited ", formatDateTime$4(collection.last_viewed_at)) : null) : null, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-3 rounded-[24px] border border-white/10 bg-white/[0.04] px-4 py-3" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => handleUnsave(collection), disabled: busy === `unsave-${collection.id}`, className: "inline-flex items-center gap-2 rounded-2xl border border-rose-300/20 bg-rose-400/10 px-4 py-2.5 text-sm font-semibold text-rose-100 transition hover:bg-rose-400/15 disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === `unsave-${collection.id}` ? "fa-circle-notch fa-spin" : "fa-bookmark-slash"} fa-fw` }), "Remove from saved"), activeList ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => handleRemoveFromList(collection), disabled: busy === `remove-${collection.id}`, className: "inline-flex items-center gap-2 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-2.5 text-sm font-semibold text-white transition hover:bg-white/[0.07] disabled:opacity-60" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${busy === `remove-${collection.id}` ? "fa-circle-notch fa-spin" : "fa-folder-minus"} fa-fw` }), "Remove from list") : null, activeList && collections.length > 1 ? /* @__PURE__ */ React.createElement("div", { className: "ml-auto inline-flex items-center gap-2 rounded-2xl border border-white/10 bg-[#0d1726] p-1" }, /* @__PURE__ */ React.createElement( "button", { type: "button", @@ -83710,7 +84268,7 @@ function ForumIndex({ categories = [], trendingTopics = [], latestTopics = [], s return bTime - aTime; }); const latestActive = sortedByActivity[0] ?? null; - return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(SeoHead, { seo }), /* @__PURE__ */ React.createElement("div", { className: "pb-20" }, /* @__PURE__ */ React.createElement("section", { className: "relative overflow-hidden border-b border-white/10 bg-[radial-gradient(circle_at_15%_20%,rgba(34,211,238,0.24),transparent_40%),radial-gradient(circle_at_80%_0%,rgba(56,189,248,0.16),transparent_42%),linear-gradient(180deg,rgba(10,14,26,0.96),rgba(8,12,22,0.92))]" }, /* @__PURE__ */ React.createElement("div", { className: "pointer-events-none absolute inset-0 opacity-40 [background-image:linear-gradient(rgba(255,255,255,0.06)_1px,transparent_1px),linear-gradient(90deg,rgba(255,255,255,0.06)_1px,transparent_1px)] [background-size:40px_40px]" }), /* @__PURE__ */ React.createElement("div", { className: "relative mx-auto w-full max-w-[1400px] px-4 py-10 sm:px-6 lg:px-10 lg:py-14" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-8 lg:grid-cols-[1.2fr_0.8fr] lg:items-end" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "mb-2 inline-flex items-center gap-2 rounded-full border border-cyan-300/30 bg-cyan-300/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-cyan-100" }, "Community Hub"), /* @__PURE__ */ React.createElement("h1", { className: "text-4xl font-black leading-[0.95] tracking-[-0.02em] text-white sm:text-5xl lg:text-6xl" }, "Skinbase Forum"), /* @__PURE__ */ React.createElement("p", { className: "mt-4 max-w-2xl text-sm leading-relaxed text-slate-200/80 sm:text-base" }, "Ask questions, share progress, and join focused conversations across every part of Skinbase. This page is your launch point to active topics and community knowledge."), /* @__PURE__ */ React.createElement("div", { className: "mt-6 flex flex-wrap items-center gap-3" }, /* @__PURE__ */ React.createElement("a", { href: "/forum", className: "inline-flex items-center gap-2 rounded-xl bg-cyan-400 px-4 py-2.5 text-sm font-semibold text-slate-950 transition hover:bg-cyan-300" }, "Explore Categories", /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, "→")))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 sm:grid-cols-3 lg:grid-cols-1" }, /* @__PURE__ */ React.createElement(StatCard$4, { label: "Sections", value: number$1(categories.length) }), /* @__PURE__ */ React.createElement(StatCard$4, { label: "Topics", value: number$1(totalThreads) }), /* @__PURE__ */ React.createElement(StatCard$4, { label: "Posts", value: number$1(totalPosts) }))), latestActive && /* @__PURE__ */ React.createElement("div", { className: "mt-7 rounded-2xl border border-white/15 bg-white/[0.04] p-4 backdrop-blur" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-white/50" }, "Latest Activity"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 flex flex-wrap items-center gap-x-3 gap-y-1" }, /* @__PURE__ */ React.createElement( + return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(SeoHead, { seo }), /* @__PURE__ */ React.createElement("div", { className: "pb-20" }, /* @__PURE__ */ React.createElement("section", { className: "relative overflow-hidden border-b border-white/10 bg-[radial-gradient(circle_at_15%_20%,rgba(34,211,238,0.24),transparent_40%),radial-gradient(circle_at_80%_0%,rgba(56,189,248,0.16),transparent_42%),linear-gradient(180deg,rgba(10,14,26,0.96),rgba(8,12,22,0.92))]" }, /* @__PURE__ */ React.createElement("div", { className: "pointer-events-none absolute inset-0 opacity-40 [background-image:linear-gradient(rgba(255,255,255,0.06)_1px,transparent_1px),linear-gradient(90deg,rgba(255,255,255,0.06)_1px,transparent_1px)] [background-size:40px_40px]" }), /* @__PURE__ */ React.createElement("div", { className: "relative mx-auto w-full max-w-[1400px] px-4 py-10 sm:px-6 lg:px-10 lg:py-14" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-8 lg:grid-cols-[1.2fr_0.8fr] lg:items-end" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "mb-2 inline-flex items-center gap-2 rounded-full border border-cyan-300/30 bg-cyan-300/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-cyan-100" }, "Community Hub"), /* @__PURE__ */ React.createElement("h1", { className: "text-4xl font-black leading-[0.95] tracking-[-0.02em] text-white sm:text-5xl lg:text-6xl" }, "Skinbase Forum"), /* @__PURE__ */ React.createElement("p", { className: "mt-4 max-w-2xl text-sm leading-relaxed text-slate-200/80 sm:text-base" }, "Ask questions, share progress, and join focused conversations across every part of Skinbase. This page is your launch point to active topics and community knowledge."), /* @__PURE__ */ React.createElement("div", { className: "mt-6 flex flex-wrap items-center gap-3" }, /* @__PURE__ */ React.createElement("a", { href: "/forum", className: "inline-flex items-center gap-2 rounded-xl bg-cyan-400 px-4 py-2.5 text-sm font-semibold text-slate-950 transition hover:bg-cyan-300" }, "Explore Categories", /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, "→")))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 sm:grid-cols-3 lg:grid-cols-1" }, /* @__PURE__ */ React.createElement(StatCard$7, { label: "Sections", value: number$1(categories.length) }), /* @__PURE__ */ React.createElement(StatCard$7, { label: "Topics", value: number$1(totalThreads) }), /* @__PURE__ */ React.createElement(StatCard$7, { label: "Posts", value: number$1(totalPosts) }))), latestActive && /* @__PURE__ */ React.createElement("div", { className: "mt-7 rounded-2xl border border-white/15 bg-white/[0.04] p-4 backdrop-blur" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-white/50" }, "Latest Activity"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 flex flex-wrap items-center gap-x-3 gap-y-1" }, /* @__PURE__ */ React.createElement( "a", { href: `/forum/${latestActive.board_slug ?? latestActive.slug}`, @@ -83722,7 +84280,7 @@ function ForumIndex({ categories = [], trendingTopics = [], latestTopics = [], s function Panel({ title, items, emptyLabel }) { return /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/[0.08] bg-nova-800/50 p-5 backdrop-blur" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, title), items.length === 0 ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm text-white/45" }, emptyLabel) : /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, items.map((item) => /* @__PURE__ */ React.createElement("a", { key: item.slug, href: `/forum/topic/${item.slug}`, className: "block rounded-xl border border-white/6 px-4 py-3 transition hover:border-cyan-400/20 hover:bg-white/[0.03]" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold text-white" }, item.title), /* @__PURE__ */ React.createElement("div", { className: "mt-1 flex flex-wrap gap-3 text-xs text-white/45" }, item.board && /* @__PURE__ */ React.createElement("span", null, item.board), item.author && /* @__PURE__ */ React.createElement("span", null, "by ", item.author), typeof item.replies_count === "number" && /* @__PURE__ */ React.createElement("span", null, item.replies_count, " replies"), item.score !== void 0 && /* @__PURE__ */ React.createElement("span", null, "score ", item.score), item.last_post_at && /* @__PURE__ */ React.createElement("span", null, formatLastActivity(item.last_post_at))))))); } -function StatCard$4({ label, value }) { +function StatCard$7({ label, value }) { return /* @__PURE__ */ React.createElement("div", { className: "rounded-xl border border-white/15 bg-white/[0.04] px-4 py-3 backdrop-blur" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-[0.14em] text-white/50" }, label), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-2xl font-bold text-white" }, value)); } function number$1(n) { @@ -85710,7 +86268,7 @@ const __vite_glob_0_76 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de }, Symbol.toStringTag, { value: "Module" })); function GroupPromoCard({ group, eyebrow = "Groups spotlight", title, description, ctaLabel = "Open group" }) { if (!group) return null; - return /* @__PURE__ */ React.createElement("section", { className: "overflow-hidden rounded-[34px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(56,189,248,0.18),transparent_28%),radial-gradient(circle_at_80%_20%,rgba(16,185,129,0.12),transparent_26%),linear-gradient(180deg,rgba(7,16,29,0.98),rgba(2,6,23,0.94))] shadow-[0_30px_90px_rgba(2,6,23,0.45)]" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-6 p-6 lg:grid-cols-[minmax(0,1.3fr)_320px] lg:p-8" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.28em] text-sky-200/80" }, eyebrow), /* @__PURE__ */ React.createElement("h2", { className: "mt-3 text-3xl font-semibold tracking-[-0.03em] text-white sm:text-4xl" }, title || group.name), /* @__PURE__ */ React.createElement("p", { className: "mt-4 max-w-2xl text-sm leading-7 text-slate-300" }, description || group.headline || group.bio_excerpt || "Collective publishing for shared releases, artwork credits, and collaborative identity."), /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-wrap gap-2" }, (Array.isArray(group.trust_signals) ? group.trust_signals : []).slice(0, 3).map((signal) => /* @__PURE__ */ React.createElement(GroupBadgePill, { key: signal.key, label: signal.label, tone: signal.tone })), group.is_recruiting ? /* @__PURE__ */ React.createElement(GroupBadgePill, { label: "Actively recruiting", tone: "emerald" }) : null), /* @__PURE__ */ React.createElement("div", { className: "mt-6 flex flex-wrap gap-3" }, /* @__PURE__ */ React.createElement("a", { href: group.urls?.public || "/groups", className: "inline-flex items-center gap-2 rounded-full border border-sky-300/20 bg-sky-300/10 px-4 py-2 text-sm font-semibold text-sky-100 transition hover:border-sky-300/40 hover:bg-sky-300/15" }, ctaLabel, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-right text-xs" })), /* @__PURE__ */ React.createElement("a", { href: "/groups", className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white transition hover:border-white/20 hover:bg-white/[0.07]" }, "Browse all groups"))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/10 bg-black/25 p-5 backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex h-16 w-16 shrink-0 items-center justify-center overflow-hidden rounded-2xl border border-white/10 bg-white/[0.04]" }, group.avatar_url ? /* @__PURE__ */ React.createElement("img", { src: group.avatar_url, alt: "", className: "h-full w-full object-cover", loading: "lazy" }) : /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-people-group text-slate-300" })), /* @__PURE__ */ React.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React.createElement("div", { className: "truncate text-lg font-semibold text-white" }, group.name), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-sm text-slate-400" }, group.owner?.username || group.owner?.name ? `Led by ${group.owner.username || group.owner.name}` : "Shared publishing identity"))), /* @__PURE__ */ React.createElement("div", { className: "mt-5 grid grid-cols-2 gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.03] p-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-2xl font-semibold text-white" }, Number(group.counts?.artworks || 0).toLocaleString()), /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.18em] text-slate-500" }, "Published artworks")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.03] p-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-2xl font-semibold text-white" }, Number(group.counts?.followers || 0).toLocaleString()), /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.18em] text-slate-500" }, "Followers")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.03] p-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-2xl font-semibold text-white" }, Number(group.counts?.members || 0).toLocaleString()), /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.18em] text-slate-500" }, "Members")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.03] p-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-2xl font-semibold text-white" }, Number(group.counts?.collections || 0).toLocaleString()), /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.18em] text-slate-500" }, "Collections")))))); + return /* @__PURE__ */ React.createElement("section", { className: "group-promo-card overflow-hidden rounded-[34px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(56,189,248,0.18),transparent_28%),radial-gradient(circle_at_80%_20%,rgba(16,185,129,0.12),transparent_26%),linear-gradient(180deg,rgba(7,16,29,0.98),rgba(2,6,23,0.94))] shadow-[0_30px_90px_rgba(2,6,23,0.45)]" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-6 p-6 lg:grid-cols-[minmax(0,1.3fr)_320px] lg:p-8" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.28em] text-sky-200/80" }, eyebrow), /* @__PURE__ */ React.createElement("h2", { className: "mt-3 text-3xl font-semibold tracking-[-0.03em] text-white sm:text-4xl" }, title || group.name), /* @__PURE__ */ React.createElement("p", { className: "mt-4 max-w-2xl text-sm leading-7 text-slate-300" }, description || group.headline || group.bio_excerpt || "Collective publishing for shared releases, artwork credits, and collaborative identity."), /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-wrap gap-2" }, (Array.isArray(group.trust_signals) ? group.trust_signals : []).slice(0, 3).map((signal) => /* @__PURE__ */ React.createElement(GroupBadgePill, { key: signal.key, label: signal.label, tone: signal.tone })), group.is_recruiting ? /* @__PURE__ */ React.createElement(GroupBadgePill, { label: "Actively recruiting", tone: "emerald" }) : null), /* @__PURE__ */ React.createElement("div", { className: "mt-6 flex flex-wrap gap-3" }, /* @__PURE__ */ React.createElement("a", { href: group.urls?.public || "/groups", className: "inline-flex items-center gap-2 rounded-full border border-sky-300/20 bg-sky-300/10 px-4 py-2 text-sm font-semibold text-sky-100 transition hover:border-sky-300/40 hover:bg-sky-300/15" }, ctaLabel, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-right text-xs" })), /* @__PURE__ */ React.createElement("a", { href: "/groups", className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white transition hover:border-white/20 hover:bg-white/[0.07]" }, "Browse all groups"))), /* @__PURE__ */ React.createElement("div", { className: "group-promo-card__summary rounded-[28px] border border-white/10 bg-black/25 p-5 backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex h-16 w-16 shrink-0 items-center justify-center overflow-hidden rounded-2xl border border-white/10 bg-white/[0.04]" }, group.avatar_url ? /* @__PURE__ */ React.createElement("img", { src: group.avatar_url, alt: "", className: "h-full w-full object-cover", loading: "lazy" }) : /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-people-group text-slate-300" })), /* @__PURE__ */ React.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React.createElement("div", { className: "truncate text-lg font-semibold text-white" }, group.name), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-sm text-slate-400" }, group.owner?.username || group.owner?.name ? `Led by ${group.owner.username || group.owner.name}` : "Shared publishing identity"))), /* @__PURE__ */ React.createElement("div", { className: "mt-5 grid grid-cols-2 gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.03] p-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-2xl font-semibold text-white" }, Number(group.counts?.artworks || 0).toLocaleString()), /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.18em] text-slate-500" }, "Published artworks")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.03] p-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-2xl font-semibold text-white" }, Number(group.counts?.followers || 0).toLocaleString()), /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.18em] text-slate-500" }, "Followers")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.03] p-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-2xl font-semibold text-white" }, Number(group.counts?.members || 0).toLocaleString()), /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.18em] text-slate-500" }, "Members")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.03] p-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-2xl font-semibold text-white" }, Number(group.counts?.collections || 0).toLocaleString()), /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.18em] text-slate-500" }, "Collections")))))); } function GroupDiscoveryCard({ group, className = "", compact = false }) { if (!group) return null; @@ -85720,7 +86278,7 @@ function GroupDiscoveryCard({ group, className = "", compact = false }) { { href: group.urls?.public || "/groups", className: cx$7( - "group block overflow-hidden rounded-[30px] border border-white/10 bg-[linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.92))] p-5 shadow-[0_24px_70px_rgba(2,6,23,0.34)] transition duration-200 hover:-translate-y-1 hover:border-white/20", + "group-discovery-card group block overflow-hidden rounded-[30px] border border-white/10 bg-[linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.92))] p-5 shadow-[0_24px_70px_rgba(2,6,23,0.34)] transition duration-200 hover:-translate-y-1 hover:border-white/20", className ) }, @@ -85750,7 +86308,7 @@ function GroupBrowseFilters({ surfaces = [], currentSurface = "featured" }) { function GroupLeaderboardCard({ item }) { if (!item?.entity) return null; const entity = item.entity; - return /* @__PURE__ */ React.createElement("article", { className: "rounded-[26px] border border-white/10 bg-white/[0.03] p-4 shadow-[0_18px_50px_rgba(2,6,23,0.3)]" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start gap-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex h-12 w-12 shrink-0 items-center justify-center rounded-2xl border border-white/10 bg-slate-950/70 text-lg font-black text-white" }, "#", item.rank), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React.createElement("a", { href: entity.url || "/groups", className: "block truncate text-lg font-semibold text-white transition hover:text-sky-300" }, entity.name), entity.headline ? /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, entity.headline) : null), /* @__PURE__ */ React.createElement("div", { className: "text-right" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.18em] text-slate-500" }, "Score"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xl font-black text-white" }, Number(item.score || 0).toLocaleString()))), /* @__PURE__ */ React.createElement("div", { className: "mt-3 flex flex-wrap gap-2" }, (Array.isArray(entity.trust_signals) ? entity.trust_signals : []).slice(0, 2).map((signal) => /* @__PURE__ */ React.createElement(GroupBadgePill, { key: signal.key, label: signal.label, tone: signal.tone })), entity.is_recruiting ? /* @__PURE__ */ React.createElement(GroupBadgePill, { label: "Recruiting", tone: "emerald" }) : null), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-4 text-xs text-slate-400" }, /* @__PURE__ */ React.createElement("span", null, Number(entity.artworks_count || 0).toLocaleString(), " artworks"), /* @__PURE__ */ React.createElement("span", null, Number(entity.members_count || 0).toLocaleString(), " members"), /* @__PURE__ */ React.createElement("span", null, Number(entity.followers_count || 0).toLocaleString(), " followers"))))); + return /* @__PURE__ */ React.createElement("article", { className: "group-leaderboard-card rounded-[26px] border border-white/10 bg-white/[0.03] p-4 shadow-[0_18px_50px_rgba(2,6,23,0.3)]" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start gap-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex h-12 w-12 shrink-0 items-center justify-center rounded-2xl border border-white/10 bg-slate-950/70 text-lg font-black text-white" }, "#", item.rank), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React.createElement("a", { href: entity.url || "/groups", className: "block truncate text-lg font-semibold text-white transition hover:text-sky-300" }, entity.name), entity.headline ? /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, entity.headline) : null), /* @__PURE__ */ React.createElement("div", { className: "text-right" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] uppercase tracking-[0.18em] text-slate-500" }, "Score"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xl font-black text-white" }, Number(item.score || 0).toLocaleString()))), /* @__PURE__ */ React.createElement("div", { className: "mt-3 flex flex-wrap gap-2" }, (Array.isArray(entity.trust_signals) ? entity.trust_signals : []).slice(0, 2).map((signal) => /* @__PURE__ */ React.createElement(GroupBadgePill, { key: signal.key, label: signal.label, tone: signal.tone })), entity.is_recruiting ? /* @__PURE__ */ React.createElement(GroupBadgePill, { label: "Recruiting", tone: "emerald" }) : null), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-4 text-xs text-slate-400" }, /* @__PURE__ */ React.createElement("span", null, Number(entity.artworks_count || 0).toLocaleString(), " artworks"), /* @__PURE__ */ React.createElement("span", null, Number(entity.members_count || 0).toLocaleString(), " members"), /* @__PURE__ */ React.createElement("span", null, Number(entity.followers_count || 0).toLocaleString(), " followers"))))); } function GroupIndex() { const { props } = X$1(); @@ -85759,7 +86317,7 @@ function GroupIndex() { const currentSurface = props.currentSurface || "featured"; const highlightSections = Array.isArray(props.highlightSections) ? props.highlightSections : []; const leaderboardItems = Array.isArray(props.leaderboard?.items) ? props.leaderboard.items : []; - return /* @__PURE__ */ React.createElement("main", { className: "min-h-screen bg-[radial-gradient(circle_at_top_left,_rgba(56,189,248,0.16),_transparent_28%),linear-gradient(180deg,_#020617_0%,_#02040a_100%)] px-4 py-10 sm:px-6 lg:px-8" }, /* @__PURE__ */ React.createElement(SeoHead, { title: "Groups - Skinbase", description: props.description }), /* @__PURE__ */ React.createElement("div", { className: "mx-auto max-w-6xl" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Groups"), /* @__PURE__ */ React.createElement("h1", { className: "mt-2 text-4xl font-semibold text-white" }, "Collective publishing identities"), /* @__PURE__ */ React.createElement("p", { className: "mt-4 max-w-3xl text-sm leading-6 text-slate-300" }, "Discover collaborative studios, follow shared creative brands, and browse the artworks, releases, and collections published under each group identity."), /* @__PURE__ */ React.createElement(GroupBrowseFilters, { surfaces, currentSurface })), /* @__PURE__ */ React.createElement("div", { className: "mt-8" }, /* @__PURE__ */ React.createElement( + return /* @__PURE__ */ React.createElement("main", { className: "groups-directory-page min-h-screen bg-[radial-gradient(circle_at_top_left,_rgba(56,189,248,0.16),_transparent_28%),linear-gradient(180deg,_#020617_0%,_#02040a_100%)] px-4 py-10 sm:px-6 lg:px-8" }, /* @__PURE__ */ React.createElement(SeoHead, { title: "Groups - Skinbase", description: props.description }), /* @__PURE__ */ React.createElement("div", { className: "mx-auto max-w-6xl" }, /* @__PURE__ */ React.createElement("section", { className: "groups-directory-page__hero rounded-[32px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80" }, "Groups"), /* @__PURE__ */ React.createElement("h1", { className: "mt-2 text-4xl font-semibold text-white" }, "Collective publishing identities"), /* @__PURE__ */ React.createElement("p", { className: "mt-4 max-w-3xl text-sm leading-6 text-slate-300" }, "Discover collaborative studios, follow shared creative brands, and browse the artworks, releases, and collections published under each group identity."), /* @__PURE__ */ React.createElement(GroupBrowseFilters, { surfaces, currentSurface })), /* @__PURE__ */ React.createElement("div", { className: "mt-8" }, /* @__PURE__ */ React.createElement( GroupPromoCard, { group: props.spotlightGroup, @@ -90494,7 +91052,7 @@ function cx$4(...parts) { return parts.filter(Boolean).join(" "); } function LeaderboardTabs({ items, active, onChange, sticky = false, label }) { - return /* @__PURE__ */ React.createElement("div", { className: cx$4(sticky ? "sticky top-16 z-20" : "", "rounded-2xl border border-white/10 bg-slate-950/85 p-2 backdrop-blur") }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2", role: "tablist", "aria-label": label || "Leaderboard tabs" }, items.map((item) => { + return /* @__PURE__ */ React.createElement("div", { className: cx$4(sticky ? "sticky top-16 z-20" : "", "leaderboard-tabs rounded-2xl border border-white/10 bg-slate-950/85 p-2 backdrop-blur") }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2", role: "tablist", "aria-label": label || "Leaderboard tabs" }, items.map((item) => { const isActive = item.value === active; return /* @__PURE__ */ React.createElement( "button", @@ -90505,7 +91063,7 @@ function LeaderboardTabs({ items, active, onChange, sticky = false, label }) { "aria-selected": isActive, onClick: () => onChange(item.value), className: cx$4( - "rounded-full px-4 py-2 text-sm font-semibold transition", + "leaderboard-tabs__tab rounded-full px-4 py-2 text-sm font-semibold transition", isActive ? "bg-sky-400 text-slate-950 shadow-[0_12px_30px_rgba(56,189,248,0.28)]" : "bg-white/5 text-slate-300 hover:bg-white/10 hover:text-white" ) }, @@ -90530,7 +91088,7 @@ function LeaderboardItem({ item, type: type2, highlight = false }) { const tone = highlight ? PODIUM_STYLES[rank] || PODIUM_STYLES[3] : "border-white/10 bg-white/[0.03]"; const image2 = entity.avatar || entity.image || null; const groupSignals = Array.isArray(entity.trust_signals) ? entity.trust_signals.slice(0, 2) : []; - return /* @__PURE__ */ React.createElement("article", { className: cx$3("rounded-3xl border p-4 shadow-lg transition", tone) }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start gap-4" }, /* @__PURE__ */ React.createElement("div", { className: cx$3("flex shrink-0 items-center justify-center rounded-2xl border font-black", highlight ? "h-14 w-14 text-xl" : "h-11 w-11 text-base", "border-white/10 bg-slate-950/70 text-white") }, "#", rank), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React.createElement("a", { href: entity.url || "#", className: "block text-lg font-semibold text-white hover:text-sky-300 transition" }, entity.name || "Unknown"), entity.creator_name ? /* @__PURE__ */ React.createElement("a", { href: entity.creator_url || "#", className: "mt-1 block text-sm text-slate-400 hover:text-sky-300 transition" }, "by ", entity.creator_name) : null, type2 === "group" && entity.headline ? /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, entity.headline) : null, type2 === "world" && entity.summary ? /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, entity.summary) : null, entity.username ? /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-500" }, "@", entity.username) : null), /* @__PURE__ */ React.createElement("div", { className: "text-right" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-[0.24em] text-slate-500" }, "Score"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-2xl font-black text-white" }, formatScore(item?.score)))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap items-center gap-3" }, type2 === "creator" ? /* @__PURE__ */ React.createElement(LevelBadge, { level: entity.level, rank: entity.rank, compact: true }) : null, type2 === "group" ? groupSignals.map((signal) => /* @__PURE__ */ React.createElement(GroupBadgePill, { key: signal.key, label: signal.label, tone: signal.tone })) : null, type2 === "group" && entity.is_recruiting ? /* @__PURE__ */ React.createElement(GroupBadgePill, { label: "Recruiting", tone: "emerald" }) : null, type2 !== "creator" && entity.creator_name ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/5 px-3 py-1 text-[11px] uppercase tracking-[0.14em] text-slate-300" }, type2) : null, type2 === "group" ? /* @__PURE__ */ React.createElement("span", { className: "text-xs text-slate-400" }, Number(entity.artworks_count || 0).toLocaleString(), " artworks, ", Number(entity.members_count || 0).toLocaleString(), " members, ", Number(entity.followers_count || 0).toLocaleString(), " followers") : null, type2 === "world" ? /* @__PURE__ */ React.createElement("span", { className: "text-xs text-slate-400" }, Number(entity.relations_count || 0).toLocaleString(), " curated links, ", Number(entity.approved_submissions_count || 0).toLocaleString(), " approved submissions", entity.timeframe_label ? `, ${entity.timeframe_label}` : "") : null, type2 === "world" && entity.badge_label ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/5 px-3 py-1 text-[11px] uppercase tracking-[0.14em] text-slate-300" }, entity.badge_label) : null, type2 === "world" && entity.theme_label ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/5 px-3 py-1 text-[11px] uppercase tracking-[0.14em] text-slate-300" }, entity.theme_label) : null)), image2 ? /* @__PURE__ */ React.createElement("a", { href: entity.url || "#", className: cx$3("block shrink-0 overflow-hidden rounded-2xl border border-white/10 bg-slate-900", type2 === "creator" ? "h-16 w-16" : "h-20 w-24") }, /* @__PURE__ */ React.createElement("img", { src: image2, alt: entity.name || "Leaderboard item", className: "h-full w-full object-cover", loading: "lazy" })) : null)); + return /* @__PURE__ */ React.createElement("article", { className: cx$3("leaderboard-item rounded-3xl border p-4 shadow-lg transition", tone) }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start gap-4" }, /* @__PURE__ */ React.createElement("div", { className: cx$3("leaderboard-item__rank flex shrink-0 items-center justify-center rounded-2xl border font-black", highlight ? "h-14 w-14 text-xl" : "h-11 w-11 text-base", "border-white/10 bg-slate-950/70 text-white") }, "#", rank), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React.createElement("a", { href: entity.url || "#", className: "block text-lg font-semibold text-white hover:text-sky-300 transition" }, entity.name || "Unknown"), entity.creator_name ? /* @__PURE__ */ React.createElement("a", { href: entity.creator_url || "#", className: "mt-1 block text-sm text-slate-400 hover:text-sky-300 transition" }, "by ", entity.creator_name) : null, type2 === "group" && entity.headline ? /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, entity.headline) : null, type2 === "world" && entity.summary ? /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, entity.summary) : null, entity.username ? /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-500" }, "@", entity.username) : null), /* @__PURE__ */ React.createElement("div", { className: "text-right" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-[0.24em] text-slate-500" }, "Score"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-2xl font-black text-white" }, formatScore(item?.score)))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap items-center gap-3" }, type2 === "creator" ? /* @__PURE__ */ React.createElement(LevelBadge, { level: entity.level, rank: entity.rank, compact: true }) : null, type2 === "group" ? groupSignals.map((signal) => /* @__PURE__ */ React.createElement(GroupBadgePill, { key: signal.key, label: signal.label, tone: signal.tone })) : null, type2 === "group" && entity.is_recruiting ? /* @__PURE__ */ React.createElement(GroupBadgePill, { label: "Recruiting", tone: "emerald" }) : null, type2 !== "creator" && entity.creator_name ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/5 px-3 py-1 text-[11px] uppercase tracking-[0.14em] text-slate-300" }, type2) : null, type2 === "group" ? /* @__PURE__ */ React.createElement("span", { className: "text-xs text-slate-400" }, Number(entity.artworks_count || 0).toLocaleString(), " artworks, ", Number(entity.members_count || 0).toLocaleString(), " members, ", Number(entity.followers_count || 0).toLocaleString(), " followers") : null, type2 === "world" ? /* @__PURE__ */ React.createElement("span", { className: "text-xs text-slate-400" }, Number(entity.relations_count || 0).toLocaleString(), " curated links, ", Number(entity.approved_submissions_count || 0).toLocaleString(), " approved submissions", entity.timeframe_label ? `, ${entity.timeframe_label}` : "") : null, type2 === "world" && entity.badge_label ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/5 px-3 py-1 text-[11px] uppercase tracking-[0.14em] text-slate-300" }, entity.badge_label) : null, type2 === "world" && entity.theme_label ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/5 px-3 py-1 text-[11px] uppercase tracking-[0.14em] text-slate-300" }, entity.theme_label) : null)), image2 ? /* @__PURE__ */ React.createElement("a", { href: entity.url || "#", className: cx$3("block shrink-0 overflow-hidden rounded-2xl border border-white/10 bg-slate-900", type2 === "creator" ? "h-16 w-16" : "h-20 w-24") }, /* @__PURE__ */ React.createElement("img", { src: image2, alt: entity.name || "Leaderboard item", className: "h-full w-full object-cover", loading: "lazy" })) : null)); } function LeaderboardList({ items = [], type: type2 }) { const podium = items.slice(0, 3); @@ -90595,7 +91153,7 @@ function LeaderboardPage() { }; }, [type2, period, initialType, initialPeriod]); const items = Array.isArray(data?.items) ? data.items : []; - return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(SeoHead, { seo, title: seo?.title || "Leaderboard — Skinbase", description: seo?.description || "Top creators, groups, artworks, stories, and Worlds on Skinbase." }), /* @__PURE__ */ React.createElement("div", { className: "min-h-screen bg-[radial-gradient(circle_at_top,rgba(14,165,233,0.14),transparent_34%),linear-gradient(180deg,#020617_0%,#0f172a_48%,#020617_100%)] pb-16 text-slate-100" }, /* @__PURE__ */ React.createElement("div", { className: "mx-auto w-full max-w-7xl px-4 py-8 sm:px-6 lg:px-8" }, /* @__PURE__ */ React.createElement("header", { className: "rounded-[2rem] border border-white/10 bg-slate-950/70 px-6 py-8 shadow-[0_35px_120px_rgba(2,6,23,0.75)] backdrop-blur" }, /* @__PURE__ */ React.createElement("p", { className: "text-xs font-semibold uppercase tracking-[0.28em] text-sky-300" }, "Skinbase Competition Board"), /* @__PURE__ */ React.createElement("h1", { className: "mt-4 max-w-3xl text-4xl font-black tracking-tight text-white sm:text-5xl" }, "Top creators, groups, standout artworks, stories, and Worlds with momentum."), /* @__PURE__ */ React.createElement("p", { className: "mt-4 max-w-2xl text-sm leading-6 text-slate-300 sm:text-base" }, "Switch between creators, groups, artworks, stories, and Worlds, then filter by daily, weekly, monthly, or all-time performance.")), /* @__PURE__ */ React.createElement("div", { className: "mt-6 space-y-4" }, /* @__PURE__ */ React.createElement(LeaderboardTabs, { items: TYPE_TABS, active: type2, onChange: setType, sticky: true, label: "Leaderboard type" }), /* @__PURE__ */ React.createElement(LeaderboardTabs, { items: PERIOD_TABS, active: period, onChange: setPeriod, label: "Leaderboard period" })), loading ? /* @__PURE__ */ React.createElement("div", { className: "mt-6 rounded-3xl border border-white/10 bg-white/[0.03] px-6 py-5 text-sm text-slate-400" }, "Refreshing leaderboard...") : null, /* @__PURE__ */ React.createElement("div", { className: "mt-8" }, /* @__PURE__ */ React.createElement(LeaderboardList, { items, type: type2 }))))); + return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(SeoHead, { seo, title: seo?.title || "Leaderboard — Skinbase", description: seo?.description || "Top creators, groups, artworks, stories, and Worlds on Skinbase." }), /* @__PURE__ */ React.createElement("div", { className: "leaderboard-page min-h-screen bg-[radial-gradient(circle_at_top,rgba(14,165,233,0.14),transparent_34%),linear-gradient(180deg,#020617_0%,#0f172a_48%,#020617_100%)] pb-16 text-slate-100" }, /* @__PURE__ */ React.createElement("div", { className: "mx-auto w-full max-w-7xl px-4 py-8 sm:px-6 lg:px-8" }, /* @__PURE__ */ React.createElement("header", { className: "leaderboard-page__hero rounded-[2rem] border border-white/10 bg-slate-950/70 px-6 py-8 shadow-[0_35px_120px_rgba(2,6,23,0.75)] backdrop-blur" }, /* @__PURE__ */ React.createElement("p", { className: "text-xs font-semibold uppercase tracking-[0.28em] text-sky-300" }, "Skinbase Competition Board"), /* @__PURE__ */ React.createElement("h1", { className: "mt-4 max-w-3xl text-4xl font-black tracking-tight text-white sm:text-5xl" }, "Top creators, groups, standout artworks, stories, and Worlds with momentum."), /* @__PURE__ */ React.createElement("p", { className: "mt-4 max-w-2xl text-sm leading-6 text-slate-300 sm:text-base" }, "Switch between creators, groups, artworks, stories, and Worlds, then filter by daily, weekly, monthly, or all-time performance.")), /* @__PURE__ */ React.createElement("div", { className: "mt-6 space-y-4" }, /* @__PURE__ */ React.createElement(LeaderboardTabs, { items: TYPE_TABS, active: type2, onChange: setType, sticky: true, label: "Leaderboard type" }), /* @__PURE__ */ React.createElement(LeaderboardTabs, { items: PERIOD_TABS, active: period, onChange: setPeriod, label: "Leaderboard period" })), loading ? /* @__PURE__ */ React.createElement("div", { className: "mt-6 rounded-3xl border border-white/10 bg-white/[0.03] px-6 py-5 text-sm text-slate-400" }, "Refreshing leaderboard...") : null, /* @__PURE__ */ React.createElement("div", { className: "mt-8" }, /* @__PURE__ */ React.createElement(LeaderboardList, { items, type: type2 }))))); } const __vite_glob_0_92 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, @@ -92539,7 +93097,7 @@ const __vite_glob_0_93 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de __proto__: null, default: MessagesPage }, Symbol.toStringTag, { value: "Module" })); -function requestJson$b(url, { method = "GET", body: body2 } = {}) { +function requestJson$c(url, { method = "GET", body: body2 } = {}) { return fetch(url, { method, credentials: "same-origin", @@ -92596,7 +93154,7 @@ function ArtworkMaturityQueue() { ai_action: nextAiAction, ai_status: nextAiStatus }); - const payload = await requestJson$b(`${endpoints.list}?${query.toString()}`); + const payload = await requestJson$c(`${endpoints.list}?${query.toString()}`); setItems(payload.data || []); setStats(payload.meta?.stats || {}); } catch (loadError) { @@ -92607,7 +93165,7 @@ function ArtworkMaturityQueue() { setBusyId(itemId); setError(""); try { - const payload = await requestJson$b(String(endpoints.reviewPattern || "").replace("__ARTWORK__", String(itemId)), { + const payload = await requestJson$c(String(endpoints.reviewPattern || "").replace("__ARTWORK__", String(itemId)), { method: "POST", body: { action, @@ -92747,6 +93305,247 @@ const __vite_glob_0_97 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de __proto__: null, default: ModerationEnhanceShow }, Symbol.toStringTag, { value: "Module" })); +function ModerationFeaturedArtworks() { + return /* @__PURE__ */ React.createElement(AdminLayout, null, /* @__PURE__ */ React.createElement(FeaturedArtworksAdmin, null)); +} +const __vite_glob_0_98 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ + __proto__: null, + default: ModerationFeaturedArtworks +}, Symbol.toStringTag, { value: "Module" })); +function formatDateTime$3(value) { + if (!value) return "—"; + const date = new Date(value); + if (Number.isNaN(date.getTime())) return "—"; + return new Intl.DateTimeFormat("en", { dateStyle: "medium", timeStyle: "short" }).format(date); +} +function StatCard$6({ label, value }) { + return /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-white/[0.04] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400" }, label), /* @__PURE__ */ React.createElement("div", { className: "mt-4 text-3xl font-semibold tracking-[-0.04em] text-white" }, Number(value || 0).toLocaleString())); +} +function StaffApplicationsIndex({ title, items, stats, filters, topics, endpoints }) { + const [state, setState] = React.useState(filters || { q: "", topic: "all" }); + React.useEffect(() => { + setState(filters || { q: "", topic: "all" }); + }, [filters]); + function update(key, value) { + setState((current) => ({ ...current, [key]: value })); + } + function applyFilters(event) { + event.preventDefault(); + At.get(endpoints.index, state, { preserveState: true, replace: true, preserveScroll: true }); + } + const rows = items?.data || []; + return /* @__PURE__ */ React.createElement(AdminLayout, { title: title || "Staff Applications", subtitle: "Review staff and contact submissions without leaving moderation." }, /* @__PURE__ */ React.createElement(Se$1, { title: "Moderation · Staff Applications" }), /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(56,189,248,0.12),transparent_34%),linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.92))] p-6 shadow-[0_24px_70px_rgba(2,6,23,0.32)]" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.28em] text-sky-200/80" }, "Moderation surface"), /* @__PURE__ */ React.createElement("h1", { className: "mt-3 text-3xl font-semibold tracking-[-0.04em] text-white" }, "Staff Applications"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 max-w-3xl text-sm leading-relaxed text-slate-300" }, "Review staff and contact submissions in the same moderation workspace as the rest of Skinbase.")), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-3 text-xs uppercase tracking-[0.16em] text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.05] px-4 py-2" }, "Page ", items?.current_page || 1, " / ", items?.last_page || 1), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.05] px-4 py-2" }, Number(items?.total || 0).toLocaleString(), " submissions"))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 md:grid-cols-2 xl:grid-cols-5" }, /* @__PURE__ */ React.createElement(StatCard$6, { label: "Total", value: stats?.total }), /* @__PURE__ */ React.createElement(StatCard$6, { label: "Applications", value: stats?.applications }), /* @__PURE__ */ React.createElement(StatCard$6, { label: "Bug reports", value: stats?.bug }), /* @__PURE__ */ React.createElement(StatCard$6, { label: "Contact", value: stats?.contact }), /* @__PURE__ */ React.createElement(StatCard$6, { label: "Other", value: stats?.other })), /* @__PURE__ */ React.createElement("form", { onSubmit: applyFilters, className: "mt-6 grid gap-3 lg:grid-cols-[2fr_1fr_auto]" }, /* @__PURE__ */ React.createElement( + "input", + { + value: state.q || "", + onChange: (event) => update("q", event.target.value), + placeholder: "Search name, email, role, or message", + className: "rounded-2xl border border-white/10 bg-slate-950/70 px-4 py-3 text-sm text-white outline-none" + } + ), /* @__PURE__ */ React.createElement( + "select", + { + value: state.topic || "all", + onChange: (event) => update("topic", event.target.value), + className: "rounded-2xl border border-white/10 bg-slate-950/70 px-4 py-3 text-sm text-white outline-none" + }, + /* @__PURE__ */ React.createElement("option", { value: "all" }, "All topics"), + (topics || []).map((topic) => /* @__PURE__ */ React.createElement("option", { key: topic, value: topic }, String(topic).replaceAll("_", " "))) + ), /* @__PURE__ */ React.createElement("button", { type: "submit", className: "rounded-2xl border border-white/10 bg-white/[0.06] px-5 py-3 text-xs font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.1]" }, "Apply"))), /* @__PURE__ */ React.createElement("div", { className: "mt-8 overflow-hidden rounded-[28px] border border-white/10 bg-[#08111d] shadow-[0_18px_48px_rgba(2,6,23,0.2)]" }, /* @__PURE__ */ React.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React.createElement("table", { className: "min-w-full text-sm" }, /* @__PURE__ */ React.createElement("thead", { className: "bg-white/[0.03] text-left text-xs uppercase tracking-[0.18em] text-slate-400" }, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", { className: "px-5 py-4 font-medium" }, "Received"), /* @__PURE__ */ React.createElement("th", { className: "px-5 py-4 font-medium" }, "Topic"), /* @__PURE__ */ React.createElement("th", { className: "px-5 py-4 font-medium" }, "Name"), /* @__PURE__ */ React.createElement("th", { className: "px-5 py-4 font-medium" }, "Email"), /* @__PURE__ */ React.createElement("th", { className: "px-5 py-4 font-medium" }, "Role"), /* @__PURE__ */ React.createElement("th", { className: "px-5 py-4 font-medium" }, "Action"))), /* @__PURE__ */ React.createElement("tbody", { className: "divide-y divide-white/5 text-slate-200" }, rows.length === 0 ? /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("td", { colSpan: 6, className: "px-5 py-14 text-center text-slate-400" }, "No staff applications matched the current filters.")) : rows.map((item) => /* @__PURE__ */ React.createElement("tr", { key: item.id, className: "hover:bg-white/[0.02]" }, /* @__PURE__ */ React.createElement("td", { className: "px-5 py-4 text-slate-400" }, formatDateTime$3(item.created_at)), /* @__PURE__ */ React.createElement("td", { className: "px-5 py-4" }, /* @__PURE__ */ React.createElement("span", { className: "inline-flex rounded-full border border-white/10 bg-white/[0.06] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-200" }, String(item.topic || "contact").replaceAll("_", " "))), /* @__PURE__ */ React.createElement("td", { className: "px-5 py-4 font-medium text-white" }, item.name), /* @__PURE__ */ React.createElement("td", { className: "px-5 py-4 text-slate-300" }, item.email), /* @__PURE__ */ React.createElement("td", { className: "px-5 py-4 text-slate-300" }, item.role || "—"), /* @__PURE__ */ React.createElement("td", { className: "px-5 py-4" }, /* @__PURE__ */ React.createElement(xe, { href: item.show_url, className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.09]" }, "View")))))))), items?.prev_page_url || items?.next_page_url ? /* @__PURE__ */ React.createElement("div", { className: "mt-8 flex items-center justify-between gap-3 rounded-[24px] border border-white/10 bg-black/20 px-5 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-xs uppercase tracking-[0.16em] text-slate-400" }, "Showing page ", items?.current_page || 1, " of ", items?.last_page || 1), /* @__PURE__ */ React.createElement("div", { className: "flex gap-2" }, items?.prev_page_url ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => At.get(items.prev_page_url, {}, { preserveScroll: true }), className: "rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.09]" }, "Previous") : null, items?.next_page_url ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => At.get(items.next_page_url, {}, { preserveScroll: true }), className: "rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.09]" }, "Next") : null)) : null); +} +const __vite_glob_0_99 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ + __proto__: null, + default: StaffApplicationsIndex +}, Symbol.toStringTag, { value: "Module" })); +function formatDateTime$2(value) { + if (!value) return "—"; + const date = new Date(value); + if (Number.isNaN(date.getTime())) return "—"; + return new Intl.DateTimeFormat("en", { dateStyle: "medium", timeStyle: "short" }).format(date); +} +function Field$1({ label, children }) { + return /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400" }, label), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-sm leading-7 text-slate-100" }, children)); +} +function StaffApplicationShow({ title, item, backUrl }) { + const payload = item?.payload || {}; + return /* @__PURE__ */ React.createElement(AdminLayout, { title: title || "Staff Application", subtitle: "Read the full submission in a moderation-friendly layout." }, /* @__PURE__ */ React.createElement(Se$1, { title: "Moderation · Staff Application" }), /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(34,211,238,0.12),transparent_34%),linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.92))] p-6 shadow-[0_24px_70px_rgba(2,6,23,0.32)]" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.28em] text-sky-200/80" }, "Moderation surface"), /* @__PURE__ */ React.createElement("h1", { className: "mt-3 text-3xl font-semibold tracking-[-0.04em] text-white" }, item?.name || "Staff application"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 max-w-3xl text-sm leading-relaxed text-slate-300" }, "Topic: ", String(item?.topic || "contact").replaceAll("_", " "), " • Received ", formatDateTime$2(item?.created_at))), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-3" }, /* @__PURE__ */ React.createElement(xe, { href: backUrl, className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.05] px-5 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.09]" }, "Back"), item?.email ? /* @__PURE__ */ React.createElement("a", { href: `mailto:${item.email}`, className: "inline-flex items-center gap-2 rounded-full border border-sky-300/20 bg-sky-400/12 px-5 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-sky-50 transition hover:bg-sky-400/18" }, "Email") : null))), /* @__PURE__ */ React.createElement("div", { className: "mt-8 grid gap-6 xl:grid-cols-[1.35fr_0.85fr]" }, /* @__PURE__ */ React.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field$1, { label: "Name" }, item?.name || "—"), /* @__PURE__ */ React.createElement(Field$1, { label: "Email" }, item?.email || "—"), /* @__PURE__ */ React.createElement(Field$1, { label: "Role" }, item?.role || "—"), /* @__PURE__ */ React.createElement(Field$1, { label: "Portfolio" }, item?.portfolio ? /* @__PURE__ */ React.createElement("a", { href: item.portfolio, className: "text-sky-300 hover:text-sky-200", target: "_blank", rel: "noreferrer" }, item.portfolio) : "—")), /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/10 bg-[#08111d] p-5 shadow-[0_18px_48px_rgba(2,6,23,0.2)]" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400" }, "Message"), /* @__PURE__ */ React.createElement("div", { className: "mt-3 whitespace-pre-wrap rounded-2xl border border-white/10 bg-slate-950/60 px-4 py-4 text-sm leading-7 text-slate-100" }, item?.message || "No message included."))), /* @__PURE__ */ React.createElement("aside", { className: "space-y-4" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/10 bg-[#08111d] p-5 shadow-[0_18px_48px_rgba(2,6,23,0.2)]" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400" }, "Metadata"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, "Received:"), " ", formatDateTime$2(item?.created_at)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, "IP:"), " ", item?.ip || "—"), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, "User agent:"), " ", item?.user_agent || "—"), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, "ID:"), " ", item?.id || "—"))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/10 bg-[#08111d] p-5 shadow-[0_18px_48px_rgba(2,6,23,0.2)]" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400" }, "Payload"), /* @__PURE__ */ React.createElement("pre", { className: "mt-3 max-h-[420px] overflow-auto rounded-2xl border border-white/10 bg-slate-950/70 px-4 py-4 text-xs leading-6 text-slate-200" }, JSON.stringify(payload, null, 2)))))); +} +const __vite_glob_0_100 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ + __proto__: null, + default: StaffApplicationShow +}, Symbol.toStringTag, { value: "Module" })); +function badgeTone$1(status2) { + if (status2 === "published") return "border-emerald-300/20 bg-emerald-400/12 text-emerald-100"; + if (status2 === "scheduled") return "border-sky-300/20 bg-sky-400/12 text-sky-100"; + if (status2 === "pending_review") return "border-amber-300/20 bg-amber-400/12 text-amber-100"; + if (status2 === "archived" || status2 === "rejected") return "border-rose-300/20 bg-rose-400/12 text-rose-100"; + return "border-white/10 bg-white/[0.06] text-slate-200"; +} +function StatCard$5({ label, value }) { + return /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-white/[0.04] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400" }, label), /* @__PURE__ */ React.createElement("div", { className: "mt-4 text-3xl font-semibold tracking-[-0.04em] text-white" }, Number(value || 0).toLocaleString())); +} +function Stories({ title, stories, filters, stats, endpoints }) { + const [state, setState] = React.useState(filters || { q: "", status: "all" }); + React.useEffect(() => { + setState(filters || { q: "", status: "all" }); + }, [filters]); + function update(key, value) { + setState((current) => ({ ...current, [key]: value })); + } + function applyFilters(event) { + event.preventDefault(); + At.get(endpoints.index, state, { preserveState: true, replace: true, preserveScroll: true }); + } + const items = stories?.data || []; + return /* @__PURE__ */ React.createElement(AdminLayout, { title: title || "Stories", subtitle: "Review creator stories from the moderation surface, without jumping back to the old CP layout." }, /* @__PURE__ */ React.createElement(Se$1, { title: "Moderation · Stories" }), /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(56,189,248,0.14),transparent_34%),linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.92))] p-6 shadow-[0_24px_70px_rgba(2,6,23,0.32)]" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.28em] text-sky-200/80" }, "Moderation surface"), /* @__PURE__ */ React.createElement("h1", { className: "mt-3 text-3xl font-semibold tracking-[-0.04em] text-white" }, "Stories"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 max-w-3xl text-sm leading-relaxed text-slate-300" }, "Browse creator stories, filter by status, and jump straight to the public view when it exists.")), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-3 text-xs uppercase tracking-[0.16em] text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.05] px-4 py-2" }, "Page ", stories?.current_page || 1, " / ", stories?.last_page || 1), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.05] px-4 py-2" }, Number(stories?.total || 0).toLocaleString(), " stories"))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 md:grid-cols-2 xl:grid-cols-6" }, /* @__PURE__ */ React.createElement(StatCard$5, { label: "Total", value: stats?.total }), /* @__PURE__ */ React.createElement(StatCard$5, { label: "Published", value: stats?.published }), /* @__PURE__ */ React.createElement(StatCard$5, { label: "Draft", value: stats?.draft }), /* @__PURE__ */ React.createElement(StatCard$5, { label: "Scheduled", value: stats?.scheduled }), /* @__PURE__ */ React.createElement(StatCard$5, { label: "Pending review", value: stats?.pending_review }), /* @__PURE__ */ React.createElement(StatCard$5, { label: "Archived", value: stats?.archived })), /* @__PURE__ */ React.createElement("form", { onSubmit: applyFilters, className: "mt-6 grid gap-3 lg:grid-cols-[2fr_1fr_auto]" }, /* @__PURE__ */ React.createElement( + "input", + { + value: state.q || "", + onChange: (event) => update("q", event.target.value), + placeholder: "Search title, slug, or creator", + className: "rounded-2xl border border-white/10 bg-slate-950/70 px-4 py-3 text-sm text-white outline-none" + } + ), /* @__PURE__ */ React.createElement( + "select", + { + value: state.status || "all", + onChange: (event) => update("status", event.target.value), + className: "rounded-2xl border border-white/10 bg-slate-950/70 px-4 py-3 text-sm text-white outline-none" + }, + /* @__PURE__ */ React.createElement("option", { value: "all" }, "All statuses"), + /* @__PURE__ */ React.createElement("option", { value: "draft" }, "Draft"), + /* @__PURE__ */ React.createElement("option", { value: "pending_review" }, "Pending review"), + /* @__PURE__ */ React.createElement("option", { value: "scheduled" }, "Scheduled"), + /* @__PURE__ */ React.createElement("option", { value: "published" }, "Published"), + /* @__PURE__ */ React.createElement("option", { value: "archived" }, "Archived"), + /* @__PURE__ */ React.createElement("option", { value: "rejected" }, "Rejected") + ), /* @__PURE__ */ React.createElement("button", { type: "submit", className: "rounded-2xl border border-white/10 bg-white/[0.06] px-5 py-3 text-xs font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.1]" }, "Apply"))), /* @__PURE__ */ React.createElement("div", { className: "mt-8 grid gap-4 xl:grid-cols-2" }, items.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/10 bg-white/[0.04] px-6 py-12 text-center text-slate-300 xl:col-span-2" }, "No stories matched the current filters.") : items.map((story) => /* @__PURE__ */ React.createElement("article", { key: story.id, className: "overflow-hidden rounded-[28px] border border-white/10 bg-[#08111d] shadow-[0_18px_48px_rgba(2,6,23,0.2)]" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 md:grid-cols-[180px_1fr]" }, /* @__PURE__ */ React.createElement("div", { className: "aspect-[3/4] bg-black/30" }, story.cover_url ? /* @__PURE__ */ React.createElement("img", { src: story.cover_url, alt: story.title, className: "h-full w-full object-cover" }) : /* @__PURE__ */ React.createElement("div", { className: "flex h-full items-center justify-center text-white/20" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-feather-pointed text-4xl" }))), /* @__PURE__ */ React.createElement("div", { className: "p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2" }, /* @__PURE__ */ React.createElement("span", { className: `inline-flex rounded-full border px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] ${badgeTone$1(story.status)}` }, String(story.status || "draft").replaceAll("_", " ")), story.creator ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex rounded-full border border-white/10 bg-white/[0.06] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-200" }, "@", story.creator.username) : null), /* @__PURE__ */ React.createElement("h2", { className: "mt-3 text-2xl font-semibold tracking-[-0.03em] text-white" }, story.title), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-slate-300" }, "/", story.slug, story.creator ? ` • ${story.creator.name}` : ""), story.excerpt ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-6 text-slate-300" }, story.excerpt) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2 text-xs uppercase tracking-[0.16em] text-slate-400" }, /* @__PURE__ */ React.createElement("span", null, story.published_at ? new Date(story.published_at).toLocaleDateString() : "Unpublished"), /* @__PURE__ */ React.createElement("span", null, story.created_at ? new Date(story.created_at).toLocaleDateString() : "—")), /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-wrap gap-2" }, story.open_url ? /* @__PURE__ */ React.createElement("a", { href: story.open_url, className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.09]" }, "Open") : /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.03] px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-slate-400" }, "No public view"))))))), stories?.prev_page_url || stories?.next_page_url ? /* @__PURE__ */ React.createElement("div", { className: "mt-8 flex items-center justify-between gap-3 rounded-[24px] border border-white/10 bg-black/20 px-5 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-xs uppercase tracking-[0.16em] text-slate-400" }, "Showing page ", stories?.current_page || 1, " of ", stories?.last_page || 1), /* @__PURE__ */ React.createElement("div", { className: "flex gap-2" }, stories?.prev_page_url ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => At.get(stories.prev_page_url, {}, { preserveScroll: true }), className: "rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.09]" }, "Previous") : null, stories?.next_page_url ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => At.get(stories.next_page_url, {}, { preserveScroll: true }), className: "rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.09]" }, "Next") : null)) : null); +} +const __vite_glob_0_101 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ + __proto__: null, + default: Stories +}, Symbol.toStringTag, { value: "Module" })); +function formatDateTime$1(value) { + if (!value) return "—"; + const date = new Date(value); + if (Number.isNaN(date.getTime())) return "—"; + return new Intl.DateTimeFormat("en", { dateStyle: "medium", timeStyle: "short" }).format(date); +} +function StatCard$4({ label, value, tone = "sky" }) { + const tones2 = { + sky: "border-sky-300/15 bg-sky-400/10 text-sky-100", + amber: "border-amber-300/15 bg-amber-400/10 text-amber-100", + emerald: "border-emerald-300/15 bg-emerald-400/10 text-emerald-100", + rose: "border-rose-300/15 bg-rose-400/10 text-rose-100" + }; + return /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-white/[0.04] p-5 backdrop-blur-sm" }, /* @__PURE__ */ React.createElement("div", { className: `inline-flex rounded-full border px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] ${tones2[tone] || tones2.sky}` }, label), /* @__PURE__ */ React.createElement("div", { className: "mt-4 text-3xl font-semibold tracking-[-0.04em] text-white" }, Number(value || 0).toLocaleString())); +} +function badgeTone(status2) { + if (status2 === "approved") return "border-emerald-300/20 bg-emerald-400/12 text-emerald-100"; + if (status2 === "rejected") return "border-rose-300/20 bg-rose-400/12 text-rose-100"; + return "border-amber-300/20 bg-amber-400/12 text-amber-100"; +} +async function requestJson$b(url, body2) { + const response = await fetch(url, { + method: "POST", + credentials: "same-origin", + headers: { + Accept: "application/json", + "Content-Type": "application/json", + "X-CSRF-TOKEN": document.querySelector('meta[name="csrf-token"]')?.getAttribute("content") || "", + "X-Requested-With": "XMLHttpRequest" + }, + body: JSON.stringify(body2 || {}) + }); + const payload = await response.json().catch(() => ({})); + if (!response.ok) { + throw new Error(payload?.message || "Request failed."); + } + return payload; +} +function UsernameQueue({ title, requests, stats, filters, options, endpoints }) { + const [state, setState] = React.useState(filters || { q: "", status: "pending" }); + const [notes, setNotes] = React.useState({}); + const [busy, setBusy] = React.useState(""); + const [notice, setNotice] = React.useState(""); + const [error, setError] = React.useState(""); + React.useEffect(() => { + setState(filters || { q: "", status: "pending" }); + }, [filters]); + function update(key, value) { + setState((current) => ({ ...current, [key]: value })); + } + function applyFilters(event) { + event.preventDefault(); + At.get(endpoints.index, state, { preserveState: true, replace: true, preserveScroll: true }); + } + async function moderate(item, action) { + const actionKey = `${action}-${item.id}`; + setBusy(actionKey); + setError(""); + setNotice(""); + try { + const payload = await requestJson$b(action === "approve" ? item.approve_url : item.reject_url, { + note: String(notes[item.id] || "") + }); + setNotice(payload.message || `Request ${action}d.`); + At.reload({ only: ["requests", "stats"], preserveScroll: true }); + } catch (requestError) { + setError(requestError.message || "Request failed."); + } finally { + setBusy(""); + } + } + const items = requests?.data || []; + return /* @__PURE__ */ React.createElement(AdminLayout, { title: title || "Username Queue", subtitle: "Review username changes in the same moderation surface as the rest of Skinbase." }, /* @__PURE__ */ React.createElement(Se$1, { title: "Moderation · Username Queue" }), /* @__PURE__ */ React.createElement("section", { className: "rounded-[32px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(244,114,182,0.12),transparent_34%),linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.92))] p-6 shadow-[0_24px_70px_rgba(2,6,23,0.32)]" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.28em] text-rose-200/80" }, "Moderation surface"), /* @__PURE__ */ React.createElement("h1", { className: "mt-3 text-3xl font-semibold tracking-[-0.04em] text-white" }, "Username Queue"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 max-w-3xl text-sm leading-relaxed text-slate-300" }, "Review pending username requests before they are applied to the account or history trail.")), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-3 text-xs uppercase tracking-[0.16em] text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.05] px-4 py-2" }, "Page ", requests?.current_page || 1, " / ", requests?.last_page || 1), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.05] px-4 py-2" }, Number(requests?.total || 0).toLocaleString(), " requests"))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 md:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement(StatCard$4, { label: "Total", value: stats?.total }), /* @__PURE__ */ React.createElement(StatCard$4, { label: "Pending", value: stats?.pending, tone: "amber" }), /* @__PURE__ */ React.createElement(StatCard$4, { label: "Approved", value: stats?.approved, tone: "emerald" }), /* @__PURE__ */ React.createElement(StatCard$4, { label: "Rejected", value: stats?.rejected, tone: "rose" })), /* @__PURE__ */ React.createElement("form", { onSubmit: applyFilters, className: "mt-6 grid gap-3 lg:grid-cols-[2fr_1fr_auto]" }, /* @__PURE__ */ React.createElement( + "input", + { + value: state.q || "", + onChange: (event) => update("q", event.target.value), + placeholder: "Search requested or current username", + className: "rounded-2xl border border-white/10 bg-slate-950/70 px-4 py-3 text-sm text-white outline-none" + } + ), /* @__PURE__ */ React.createElement( + "select", + { + value: state.status || "pending", + onChange: (event) => update("status", event.target.value), + className: "rounded-2xl border border-white/10 bg-slate-950/70 px-4 py-3 text-sm text-white outline-none" + }, + (options?.statuses || []).map((option) => /* @__PURE__ */ React.createElement("option", { key: option.value, value: option.value }, option.label)) + ), /* @__PURE__ */ React.createElement("button", { type: "submit", className: "rounded-2xl border border-white/10 bg-white/[0.06] px-5 py-3 text-xs font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.1]" }, "Apply"))), notice ? /* @__PURE__ */ React.createElement("div", { className: "mt-6 rounded-2xl border border-emerald-300/20 bg-emerald-400/10 px-4 py-3 text-sm text-emerald-50" }, notice) : null, error ? /* @__PURE__ */ React.createElement("div", { className: "mt-6 rounded-2xl border border-rose-300/20 bg-rose-400/10 px-4 py-3 text-sm text-rose-100" }, error) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-8 space-y-4" }, items.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-white/10 bg-white/[0.04] px-6 py-12 text-center text-slate-300" }, "No username requests matched the current filters.") : items.map((item) => /* @__PURE__ */ React.createElement("article", { key: item.id, className: "rounded-[28px] border border-white/10 bg-[#08111d] p-5 shadow-[0_18px_48px_rgba(2,6,23,0.2)]" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement("span", { className: `inline-flex rounded-full border px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] ${badgeTone(item.status)}` }, String(item.status || "pending").replaceAll("_", " ")), item.context ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex rounded-full border border-white/10 bg-white/[0.06] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-200" }, item.context.replaceAll("_", " ")) : null, item.similar_to ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex rounded-full border border-amber-300/20 bg-amber-400/12 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-amber-100" }, "Similar to ", item.similar_to) : null), /* @__PURE__ */ React.createElement("h2", { className: "mt-3 text-2xl font-semibold tracking-[-0.03em] text-white" }, item.requested_username), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-slate-300" }, item.current_username ? `Current: @${item.current_username}` : "No current username", item.current_name ? ` • ${item.current_name}` : ""), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-xs uppercase tracking-[0.16em] text-slate-400" }, "Requested ", formatDateTime$1(item.created_at), item.reviewed_at ? ` • reviewed ${formatDateTime$1(item.reviewed_at)}` : "")), /* @__PURE__ */ React.createElement("div", { className: "w-full max-w-xl space-y-3" }, /* @__PURE__ */ React.createElement( + "textarea", + { + value: notes[item.id] || "", + onChange: (event) => setNotes((current) => ({ ...current, [item.id]: event.target.value })), + placeholder: "Optional moderation note", + rows: 3, + className: "w-full rounded-2xl border border-white/10 bg-slate-950/70 px-4 py-3 text-sm text-white outline-none" + } + ), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement( + "button", + { + type: "button", + onClick: () => moderate(item, "approve"), + disabled: busy === `approve-${item.id}`, + className: "inline-flex items-center gap-2 rounded-full border border-emerald-300/20 bg-emerald-400/12 px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-emerald-100 transition hover:bg-emerald-400/18 disabled:opacity-60" + }, + busy === `approve-${item.id}` ? "Saving…" : "Approve" + ), /* @__PURE__ */ React.createElement( + "button", + { + type: "button", + onClick: () => moderate(item, "reject"), + disabled: busy === `reject-${item.id}`, + className: "inline-flex items-center gap-2 rounded-full border border-rose-300/20 bg-rose-400/12 px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-rose-100 transition hover:bg-rose-400/18 disabled:opacity-60" + }, + busy === `reject-${item.id}` ? "Saving…" : "Reject" + ))))))), requests?.prev_page_url || requests?.next_page_url ? /* @__PURE__ */ React.createElement("div", { className: "mt-8 flex items-center justify-between gap-3 rounded-[24px] border border-white/10 bg-black/20 px-5 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-xs uppercase tracking-[0.16em] text-slate-400" }, "Showing page ", requests?.current_page || 1, " of ", requests?.last_page || 1), /* @__PURE__ */ React.createElement("div", { className: "flex gap-2" }, requests?.prev_page_url ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => At.get(requests.prev_page_url, {}, { preserveScroll: true }), className: "rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.09]" }, "Previous") : null, requests?.next_page_url ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => At.get(requests.next_page_url, {}, { preserveScroll: true }), className: "rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.09]" }, "Next") : null)) : null); +} +const __vite_glob_0_102 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ + __proto__: null, + default: UsernameQueue +}, Symbol.toStringTag, { value: "Module" })); function getCsrfToken$5() { if (typeof document === "undefined") return ""; return document.querySelector('meta[name="csrf-token"]')?.getAttribute("content") || ""; @@ -92878,7 +93677,7 @@ function WorldWebStoriesIndex() { } ), /* @__PURE__ */ React.createElement("label", { className: "flex items-center gap-2 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("input", { type: "checkbox", checked: generator.force, onChange: (event) => setGenerator((current) => ({ ...current, force: event.target.checked })) }), "Force"), /* @__PURE__ */ React.createElement("label", { className: "flex items-center gap-2 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("input", { type: "checkbox", checked: generator.publish, onChange: (event) => setGenerator((current) => ({ ...current, publish: event.target.checked })) }), "Publish"), /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: busyKey === "generate", className: "rounded-2xl border border-sky-300/20 bg-sky-400/12 px-5 py-3 text-xs font-semibold uppercase tracking-[0.14em] text-sky-50 transition hover:bg-sky-400/20 disabled:opacity-60" }, "Generate")))), notice ? /* @__PURE__ */ React.createElement("div", { className: "mt-6 rounded-2xl border border-emerald-300/20 bg-emerald-400/10 px-4 py-3 text-sm text-emerald-50" }, notice) : null, error ? /* @__PURE__ */ React.createElement("div", { className: "mt-6 rounded-2xl border border-rose-300/20 bg-rose-400/10 px-4 py-3 text-sm text-rose-100" }, error) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-8 grid gap-4 xl:grid-cols-2" }, (stories.data || []).map((story) => /* @__PURE__ */ React.createElement("article", { key: story.id, className: "overflow-hidden rounded-[28px] border border-white/10 bg-[#08111d] shadow-[0_18px_48px_rgba(2,6,23,0.2)]" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 md:grid-cols-[180px_1fr]" }, /* @__PURE__ */ React.createElement("div", { className: "aspect-[3/4] bg-black/30" }, story.poster_portrait_url ? /* @__PURE__ */ React.createElement("img", { src: story.poster_portrait_url, alt: story.title, className: "h-full w-full object-cover" }) : /* @__PURE__ */ React.createElement("div", { className: "flex h-full items-center justify-center text-white/20" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-book-open-reader text-4xl" }))), /* @__PURE__ */ React.createElement("div", { className: "p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2" }, /* @__PURE__ */ React.createElement(StatusBadge, { story }), !story.active ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex rounded-full border border-amber-300/20 bg-amber-400/12 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-amber-100" }, "inactive") : null, story.noindex ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex rounded-full border border-rose-300/20 bg-rose-400/12 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-rose-100" }, "noindex") : null), /* @__PURE__ */ React.createElement("h2", { className: "mt-3 text-2xl font-semibold tracking-[-0.03em] text-white" }, story.title), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-slate-300" }, "/", story.slug, story.world ? ` • ${story.world.title}` : ""), story.excerpt ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-6 text-slate-300" }, story.excerpt) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2 text-xs uppercase tracking-[0.16em] text-slate-400" }, /* @__PURE__ */ React.createElement("span", null, story.page_count, " pages"), story.published_at ? /* @__PURE__ */ React.createElement("span", null, new Date(story.published_at).toLocaleDateString()) : null), /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement(xe, { href: replacePattern$1(endpoints.editPattern, story.id), className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.09]" }, "Edit"), /* @__PURE__ */ React.createElement("a", { href: story.public_url, className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.09]" }, "Open"), story.status === "published" ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => performAction(`unpublish-${story.id}`, replacePattern$1(endpoints.unpublishPattern, story.id)), className: "inline-flex items-center gap-2 rounded-full border border-amber-300/20 bg-amber-400/12 px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-amber-100 transition hover:bg-amber-400/18" }, "Unpublish") : /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => performAction(`publish-${story.id}`, replacePattern$1(endpoints.publishPattern, story.id)), className: "inline-flex items-center gap-2 rounded-full border border-emerald-300/20 bg-emerald-400/12 px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-emerald-100 transition hover:bg-emerald-400/18" }, "Publish"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => performAction(`delete-${story.id}`, replacePattern$1(endpoints.destroyPattern, story.id), "DELETE"), className: "inline-flex items-center gap-2 rounded-full border border-rose-300/20 bg-rose-400/12 px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-rose-100 transition hover:bg-rose-400/18" }, "Delete")))))))); } -const __vite_glob_0_98 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_103 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: WorldWebStoriesIndex }, Symbol.toStringTag, { value: "Module" })); @@ -93100,7 +93899,7 @@ function WorldWebStoryEditor() { } return /* @__PURE__ */ React.createElement("div", { className: "w-full pb-16 pt-8" }, /* @__PURE__ */ React.createElement(Se$1, { title: isNew ? "New World Web Story" : `Edit ${story.title}` }), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.28em] text-sky-200/80" }, "Moderation surface"), /* @__PURE__ */ React.createElement("h1", { className: "mt-2 text-3xl font-semibold tracking-[-0.04em] text-white" }, isNew ? "Create World Web Story" : story.title), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm leading-relaxed text-slate-300" }, "Build a standalone AMP story companion for a Skinbase World without changing the canonical World route.")), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement(xe, { href: endpoints.index, className: "rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.09]" }, "Back"), !isNew && story.public_url ? /* @__PURE__ */ React.createElement("a", { href: story.public_url, className: "rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.09]" }, "Open story") : null, !isNew ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => performStoryAction(endpoints.publish), className: "rounded-full border border-emerald-300/20 bg-emerald-400/12 px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-emerald-100 transition hover:bg-emerald-400/18" }, "Publish") : null, !isNew ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => performStoryAction(endpoints.unpublish), className: "rounded-full border border-amber-300/20 bg-amber-400/12 px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-amber-100 transition hover:bg-amber-400/18" }, "Unpublish") : null)), notice ? /* @__PURE__ */ React.createElement("div", { className: "mt-6 rounded-2xl border border-emerald-300/20 bg-emerald-400/10 px-4 py-3 text-sm text-emerald-50" }, notice) : null, error ? /* @__PURE__ */ React.createElement("div", { className: "mt-6 rounded-2xl border border-rose-300/20 bg-rose-400/10 px-4 py-3 text-sm text-rose-100" }, error) : null, /* @__PURE__ */ React.createElement("form", { onSubmit: submit, className: "mt-6 rounded-[32px] border border-white/10 bg-[linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.92))] p-6 shadow-[0_24px_70px_rgba(2,6,23,0.32)]" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 xl:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field, { label: "Related World" }, /* @__PURE__ */ React.createElement("select", { value: form.data.world_id, onChange: (event) => form.setData("world_id", event.target.value), className: "w-full rounded-xl border border-white/10 bg-slate-950/70 px-3 py-2 text-white outline-none" }, /* @__PURE__ */ React.createElement("option", { value: "" }, "No related World"), worldOptions.map((world) => /* @__PURE__ */ React.createElement("option", { key: world.value, value: world.value }, world.label)))), /* @__PURE__ */ React.createElement(Field, { label: "Slug" }, /* @__PURE__ */ React.createElement("input", { value: form.data.slug, onChange: (event) => form.setData("slug", event.target.value), className: "w-full rounded-xl border border-white/10 bg-slate-950/70 px-3 py-2 text-white outline-none" })), /* @__PURE__ */ React.createElement(Field, { label: "Title" }, /* @__PURE__ */ React.createElement("input", { value: form.data.title, onChange: (event) => form.setData("title", event.target.value), className: "w-full rounded-xl border border-white/10 bg-slate-950/70 px-3 py-2 text-white outline-none" })), /* @__PURE__ */ React.createElement(Field, { label: "Subtitle" }, /* @__PURE__ */ React.createElement("input", { value: form.data.subtitle, onChange: (event) => form.setData("subtitle", event.target.value), className: "w-full rounded-xl border border-white/10 bg-slate-950/70 px-3 py-2 text-white outline-none" })), /* @__PURE__ */ React.createElement(Field, { label: "Excerpt" }, /* @__PURE__ */ React.createElement("textarea", { value: form.data.excerpt, onChange: (event) => form.setData("excerpt", event.target.value), rows: 3, className: "w-full rounded-xl border border-white/10 bg-slate-950/70 px-3 py-2 text-white outline-none" })), /* @__PURE__ */ React.createElement(Field, { label: "Description" }, /* @__PURE__ */ React.createElement("textarea", { value: form.data.description, onChange: (event) => form.setData("description", event.target.value), rows: 3, className: "w-full rounded-xl border border-white/10 bg-slate-950/70 px-3 py-2 text-white outline-none" })), /* @__PURE__ */ React.createElement(Field, { label: "SEO title" }, /* @__PURE__ */ React.createElement("input", { value: form.data.seo_title, onChange: (event) => form.setData("seo_title", event.target.value), className: "w-full rounded-xl border border-white/10 bg-slate-950/70 px-3 py-2 text-white outline-none" })), /* @__PURE__ */ React.createElement(Field, { label: "SEO description" }, /* @__PURE__ */ React.createElement("textarea", { value: form.data.seo_description, onChange: (event) => form.setData("seo_description", event.target.value), rows: 3, className: "w-full rounded-xl border border-white/10 bg-slate-950/70 px-3 py-2 text-white outline-none" })), /* @__PURE__ */ React.createElement(Field, { label: "Poster portrait path" }, /* @__PURE__ */ React.createElement("input", { value: form.data.poster_portrait_path, onChange: (event) => form.setData("poster_portrait_path", event.target.value), className: "w-full rounded-xl border border-white/10 bg-slate-950/70 px-3 py-2 text-white outline-none" })), /* @__PURE__ */ React.createElement(Field, { label: "Poster square path" }, /* @__PURE__ */ React.createElement("input", { value: form.data.poster_square_path, onChange: (event) => form.setData("poster_square_path", event.target.value), className: "w-full rounded-xl border border-white/10 bg-slate-950/70 px-3 py-2 text-white outline-none" })), /* @__PURE__ */ React.createElement(Field, { label: "Publisher logo path" }, /* @__PURE__ */ React.createElement("input", { value: form.data.publisher_logo_path, onChange: (event) => form.setData("publisher_logo_path", event.target.value), className: "w-full rounded-xl border border-white/10 bg-slate-950/70 px-3 py-2 text-white outline-none" })), /* @__PURE__ */ React.createElement(Field, { label: "Status" }, /* @__PURE__ */ React.createElement("select", { value: form.data.status, onChange: (event) => form.setData("status", event.target.value), className: "w-full rounded-xl border border-white/10 bg-slate-950/70 px-3 py-2 text-white outline-none" }, ["draft", "published", "archived"].map((value) => /* @__PURE__ */ React.createElement("option", { key: value, value }, value)))), /* @__PURE__ */ React.createElement(Field, { label: "Starts at" }, /* @__PURE__ */ React.createElement("input", { type: "datetime-local", value: form.data.starts_at ? form.data.starts_at.slice(0, 16) : "", onChange: (event) => form.setData("starts_at", event.target.value), className: "w-full rounded-xl border border-white/10 bg-slate-950/70 px-3 py-2 text-white outline-none" })), /* @__PURE__ */ React.createElement(Field, { label: "Ends at" }, /* @__PURE__ */ React.createElement("input", { type: "datetime-local", value: form.data.ends_at ? form.data.ends_at.slice(0, 16) : "", onChange: (event) => form.setData("ends_at", event.target.value), className: "w-full rounded-xl border border-white/10 bg-slate-950/70 px-3 py-2 text-white outline-none" }))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-4" }, /* @__PURE__ */ React.createElement("label", { className: "flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("input", { type: "checkbox", checked: Boolean(form.data.featured), onChange: (event) => form.setData("featured", event.target.checked) }), " Featured"), /* @__PURE__ */ React.createElement("label", { className: "flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("input", { type: "checkbox", checked: Boolean(form.data.active), onChange: (event) => form.setData("active", event.target.checked) }), " Active"), /* @__PURE__ */ React.createElement("label", { className: "flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("input", { type: "checkbox", checked: Boolean(form.data.noindex), onChange: (event) => form.setData("noindex", event.target.checked) }), " Noindex")), /* @__PURE__ */ React.createElement("div", { className: "mt-6 flex flex-wrap gap-3" }, /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: form.processing, className: "rounded-full border border-sky-300/20 bg-sky-400/12 px-5 py-2.5 text-xs font-semibold uppercase tracking-[0.14em] text-sky-50 transition hover:bg-sky-400/20 disabled:opacity-60" }, "Save story"), !isNew ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: generateFromWorld, className: "rounded-full border border-white/10 bg-white/[0.05] px-5 py-2.5 text-xs font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.09]" }, "Regenerate from World") : null)), !isNew ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("section", { className: "mt-8 rounded-[32px] border border-white/10 bg-[linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.92))] p-6 shadow-[0_24px_70px_rgba(2,6,23,0.32)]" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-2xl font-semibold tracking-[-0.03em] text-white" }, "Validation"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm leading-6 text-slate-300" }, "Publish only when poster, logo, page count, alt text, and CTA rules are satisfied.")), /* @__PURE__ */ React.createElement("div", { className: `rounded-full border px-4 py-2 text-xs font-semibold uppercase tracking-[0.14em] ${story.validation?.valid ? "border-emerald-300/20 bg-emerald-400/12 text-emerald-100" : "border-amber-300/20 bg-amber-400/12 text-amber-100"}` }, story.validation?.valid ? "Ready to publish" : "Needs fixes")), (story.validation?.errors || []).length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "mt-4 rounded-2xl border border-amber-300/20 bg-amber-400/10 px-4 py-4 text-sm text-amber-50" }, /* @__PURE__ */ React.createElement("ul", { className: "space-y-2" }, (story.validation.errors || []).map((item) => /* @__PURE__ */ React.createElement("li", { key: item }, item)))) : null), /* @__PURE__ */ React.createElement("section", { className: "mt-8 rounded-[32px] border border-white/10 bg-[linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.92))] p-6 shadow-[0_24px_70px_rgba(2,6,23,0.32)]" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-2xl font-semibold tracking-[-0.03em] text-white" }, "Story pages"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm leading-6 text-slate-300" }, "Keep each page short, visual, and clearly tied back to the World narrative."))), /* @__PURE__ */ React.createElement("form", { onSubmit: createPage, className: "mt-6 grid gap-3 xl:grid-cols-2" }, /* @__PURE__ */ React.createElement(Field, { label: "New page headline" }, /* @__PURE__ */ React.createElement("input", { value: newPage.headline, onChange: (event) => setNewPage((current) => ({ ...current, headline: event.target.value })), className: "w-full rounded-xl border border-white/10 bg-slate-950/70 px-3 py-2 text-white outline-none" })), /* @__PURE__ */ React.createElement(Field, { label: "New page caption" }, /* @__PURE__ */ React.createElement("input", { value: newPage.caption, onChange: (event) => setNewPage((current) => ({ ...current, caption: event.target.value })), className: "w-full rounded-xl border border-white/10 bg-slate-950/70 px-3 py-2 text-white outline-none" })), /* @__PURE__ */ React.createElement(Field, { label: "New page body" }, /* @__PURE__ */ React.createElement("textarea", { value: newPage.body, onChange: (event) => setNewPage((current) => ({ ...current, body: event.target.value })), rows: 3, className: "w-full rounded-xl border border-white/10 bg-slate-950/70 px-3 py-2 text-white outline-none" })), /* @__PURE__ */ React.createElement(Field, { label: "Alt text" }, /* @__PURE__ */ React.createElement("input", { value: newPage.alt_text, onChange: (event) => setNewPage((current) => ({ ...current, alt_text: event.target.value })), className: "w-full rounded-xl border border-white/10 bg-slate-950/70 px-3 py-2 text-white outline-none" })), /* @__PURE__ */ React.createElement(Field, { label: "Layout" }, /* @__PURE__ */ React.createElement("select", { value: newPage.layout, onChange: (event) => setNewPage((current) => ({ ...current, layout: event.target.value })), className: "w-full rounded-xl border border-white/10 bg-slate-950/70 px-3 py-2 text-white outline-none" }, ["cover", "artwork", "creator", "mood", "collection", "cta"].map((value) => /* @__PURE__ */ React.createElement("option", { key: value, value }, value)))), /* @__PURE__ */ React.createElement(Field, { label: "Background type" }, /* @__PURE__ */ React.createElement("select", { value: newPage.background_type, onChange: (event) => setNewPage((current) => ({ ...current, background_type: event.target.value })), className: "w-full rounded-xl border border-white/10 bg-slate-950/70 px-3 py-2 text-white outline-none" }, ["image", "video", "gradient"].map((value) => /* @__PURE__ */ React.createElement("option", { key: value, value }, value)))), /* @__PURE__ */ React.createElement(Field, { label: "Background path" }, /* @__PURE__ */ React.createElement("input", { value: newPage.background_path, onChange: (event) => setNewPage((current) => ({ ...current, background_path: event.target.value })), className: "w-full rounded-xl border border-white/10 bg-slate-950/70 px-3 py-2 text-white outline-none" })), /* @__PURE__ */ React.createElement(Field, { label: "Mobile background path" }, /* @__PURE__ */ React.createElement("input", { value: newPage.background_mobile_path, onChange: (event) => setNewPage((current) => ({ ...current, background_mobile_path: event.target.value })), className: "w-full rounded-xl border border-white/10 bg-slate-950/70 px-3 py-2 text-white outline-none" })), /* @__PURE__ */ React.createElement("div", { className: "xl:col-span-2 flex justify-end" }, /* @__PURE__ */ React.createElement("button", { type: "submit", className: "rounded-full border border-sky-300/20 bg-sky-400/12 px-5 py-2.5 text-xs font-semibold uppercase tracking-[0.14em] text-sky-50 transition hover:bg-sky-400/20" }, "Add page"))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 space-y-4" }, pages2.sort((left, right) => left.position - right.position).map((page) => /* @__PURE__ */ React.createElement("div", { key: page.id }, /* @__PURE__ */ React.createElement("div", { className: "mb-2 flex justify-end gap-2" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => reorder(page.id, -1), className: "rounded-full border border-white/10 bg-white/[0.05] px-3 py-1.5 text-[11px] font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.09]" }, "Move up"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => reorder(page.id, 1), className: "rounded-full border border-white/10 bg-white/[0.05] px-3 py-1.5 text-[11px] font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.09]" }, "Move down")), /* @__PURE__ */ React.createElement(StoryPageCard, { page, endpoints, onChanged: reloadEditor })))))) : null); } -const __vite_glob_0_99 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_104 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: WorldWebStoryEditor }, Symbol.toStringTag, { value: "Module" })); @@ -93521,7 +94320,7 @@ if (typeof document !== "undefined") { clientExports.createRoot(mountEl).render(/* @__PURE__ */ React.createElement(NewsComments, { ...props })); } } -const __vite_glob_0_100 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_105 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: NewsComments }, Symbol.toStringTag, { value: "Module" })); @@ -93608,7 +94407,7 @@ if (typeof document !== "undefined") { document.addEventListener("keydown", handleKeyDown); } const NewsImagePreview = null; -const __vite_glob_0_101 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_106 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: NewsImagePreview }, Symbol.toStringTag, { value: "Module" })); @@ -93883,7 +94682,7 @@ function ProfileHero({ user, profile, isOwner, viewerIsFollowing, followerCount, { label: "Followers", value: formatCompactNumber$1(count) }, { label: "Level", value: `Lv ${formatCompactNumber$1(user?.level ?? 1)}` } ]; - return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "relative mx-auto max-w-7xl px-4 pt-4 md:pt-6" }, /* @__PURE__ */ React.createElement( + return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "profile-hero relative mx-auto max-w-7xl px-4 pt-4 md:pt-6" }, /* @__PURE__ */ React.createElement( "div", { "aria-hidden": "true", @@ -94572,7 +95371,7 @@ function ProfileGallery() { } )))); } -const __vite_glob_0_102 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_107 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: ProfileGallery }, Symbol.toStringTag, { value: "Module" })); @@ -94596,7 +95395,7 @@ function ProfileTabs({ activeTab, onTabChange }) { activeRef.current.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" }); } }, [activeTab]); - return /* @__PURE__ */ React.createElement("div", { className: "sticky top-0 z-30 border-b border-white/10 bg-[#08111f]/80 backdrop-blur-2xl" }, /* @__PURE__ */ React.createElement( + return /* @__PURE__ */ React.createElement("div", { className: "profile-tabs-shell sticky top-0 z-30 border-b border-white/10 bg-[#08111f]/80 backdrop-blur-2xl" }, /* @__PURE__ */ React.createElement( "nav", { ref: navRef, @@ -97288,7 +98087,7 @@ function ProfileShow() { return acc; }, {}) : socialLinks ?? {}; const contentShellClassName = activeTab === "artworks" ? "w-full px-4 md:px-6" : activeTab === "posts" || activeTab === "about" ? "mx-auto max-w-7xl px-4 md:px-6" : "max-w-6xl mx-auto px-4"; - return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(SeoHead, { seo }), /* @__PURE__ */ React.createElement("div", { className: "relative min-h-screen overflow-hidden pb-16" }, /* @__PURE__ */ React.createElement( + return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(SeoHead, { seo }), /* @__PURE__ */ React.createElement("div", { className: "profile-page relative min-h-screen overflow-hidden pb-16" }, /* @__PURE__ */ React.createElement( "div", { "aria-hidden": "true", @@ -97422,7 +98221,7 @@ function ProfileShow() { } )))); } -const __vite_glob_0_103 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_108 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: ProfileShow }, Symbol.toStringTag, { value: "Module" })); @@ -98850,7 +99649,7 @@ function ProfileEdit() { ) ); } -const __vite_glob_0_104 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_109 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: ProfileEdit }, Symbol.toStringTag, { value: "Module" })); @@ -99363,7 +100162,7 @@ function StudioActivity() { /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement("section", { className: "grid gap-4 md:grid-cols-3" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "New since last read"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-3xl font-semibold text-white" }, Number(summary.new_items || 0).toLocaleString())), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Unread notifications"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-3xl font-semibold text-white" }, Number(summary.unread_notifications || 0).toLocaleString())), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Last inbox reset"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-base font-semibold text-white" }, summary.last_read_at ? formatDate$7(summary.last_read_at) : "Not yet"))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-[radial-gradient(circle_at_top_left,_rgba(56,189,248,0.14),_transparent_35%),linear-gradient(135deg,_rgba(15,23,42,0.86),_rgba(2,6,23,0.96))] p-5 lg:p-6" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 md:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement("label", { className: "space-y-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Search activity"), /* @__PURE__ */ React.createElement("input", { value: filters.q || "", onChange: (event) => updateFilters({ q: event.target.value }), className: "w-full rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white", placeholder: "Message, actor, or module" })), /* @__PURE__ */ React.createElement("div", { className: "space-y-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Type"), /* @__PURE__ */ React.createElement(NovaSelect, { value: filters.type || "all", onChange: (val) => updateFilters({ type: val }), options: typeOptions, searchable: false })), /* @__PURE__ */ React.createElement("div", { className: "space-y-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Content type"), /* @__PURE__ */ React.createElement(NovaSelect, { value: filters.module || "all", onChange: (val) => updateFilters({ module: val }), options: moduleOptions, searchable: false })), /* @__PURE__ */ React.createElement("div", { className: "flex items-end" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => updateFilters({ q: "", type: "all", module: "all" }), className: "w-full rounded-2xl border border-white/10 px-4 py-3 text-sm text-slate-200" }, "Reset")))), /* @__PURE__ */ React.createElement("section", { className: "space-y-4" }, items.length > 0 ? items.map((item) => /* @__PURE__ */ React.createElement("article", { key: item.id, className: `rounded-[28px] border p-5 ${item.is_new ? "border-sky-300/25 bg-sky-300/10" : "border-white/10 bg-white/[0.03]"}` }, /* @__PURE__ */ React.createElement("div", { className: "flex gap-4" }, item.actor?.avatar_url ? /* @__PURE__ */ React.createElement("img", { src: item.actor.avatar_url, alt: item.actor.name || "Activity actor", className: "h-12 w-12 rounded-2xl object-cover" }) : /* @__PURE__ */ React.createElement("div", { className: "flex h-12 w-12 items-center justify-center rounded-2xl bg-black/20 text-slate-400" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-bell" })), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-3 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, /* @__PURE__ */ React.createElement("span", null, item.module_label), /* @__PURE__ */ React.createElement("span", null, formatDate$7(item.created_at)), item.is_new && /* @__PURE__ */ React.createElement("span", { className: "rounded-full bg-sky-300/20 px-2 py-1 text-sky-100" }, "New")), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-lg font-semibold text-white" }, item.title), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm leading-6 text-slate-400" }, item.body), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap items-center gap-3 text-sm text-slate-400" }, item.actor?.name && /* @__PURE__ */ React.createElement("span", null, item.actor.name), /* @__PURE__ */ React.createElement("a", { href: item.url, className: "inline-flex items-center gap-2 rounded-full border border-white/10 px-3 py-1.5 text-slate-200" }, "Open")))))) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-dashed border-white/15 px-6 py-16 text-center text-slate-400" }, "No activity matches this filter.")), /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between rounded-[24px] border border-white/10 bg-white/[0.03] px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("button", { type: "button", disabled: (meta.current_page || 1) <= 1, onClick: () => updateFilters({ page: Math.max(1, (meta.current_page || 1) - 1) }), className: "rounded-full border border-white/10 px-4 py-2 disabled:opacity-40" }, "Previous"), /* @__PURE__ */ React.createElement("span", { className: "text-xs uppercase tracking-[0.18em] text-slate-500" }, "Page ", meta.current_page || 1, " of ", meta.last_page || 1), /* @__PURE__ */ React.createElement("button", { type: "button", disabled: (meta.current_page || 1) >= (meta.last_page || 1), onClick: () => updateFilters({ page: (meta.current_page || 1) + 1 }), className: "rounded-full border border-white/10 px-4 py-2 disabled:opacity-40" }, "Next"))) ); } -const __vite_glob_0_105 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_110 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioActivity }, Symbol.toStringTag, { value: "Module" })); @@ -99454,7 +100253,7 @@ function StudioAnalytics() { return /* @__PURE__ */ React.createElement("div", { key: item.key, className: "rounded-[22px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 text-slate-200" }, /* @__PURE__ */ React.createElement("i", { className: item.icon }), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "font-semibold text-white" }, item.label), /* @__PURE__ */ React.createElement("div", { className: "text-xs text-slate-400" }, Number(item.published_count || 0).toLocaleString(), " published"))), /* @__PURE__ */ React.createElement("a", { href: moduleBreakdown?.find((entry) => entry.key === item.key)?.index_url, className: "text-xs font-semibold uppercase tracking-[0.18em] text-sky-100" }, "Open")), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-4 md:grid-cols-2" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between text-xs text-slate-400" }, /* @__PURE__ */ React.createElement("span", null, "Views"), /* @__PURE__ */ React.createElement("span", null, Number(item.views || 0).toLocaleString())), /* @__PURE__ */ React.createElement("div", { className: "mt-2 h-2 overflow-hidden rounded-full bg-white/5" }, /* @__PURE__ */ React.createElement("div", { className: "h-full rounded-full bg-emerald-400/60", style: { width: `${Math.max(4, Math.round(Number(item.views || 0) / viewMax * 100))}%` } }))), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between text-xs text-slate-400" }, /* @__PURE__ */ React.createElement("span", null, "Engagement"), /* @__PURE__ */ React.createElement("span", null, Number(item.engagement || 0).toLocaleString())), /* @__PURE__ */ React.createElement("div", { className: "mt-2 h-2 overflow-hidden rounded-full bg-white/5" }, /* @__PURE__ */ React.createElement("div", { className: "h-full rounded-full bg-pink-400/60", style: { width: `${Math.max(4, Math.round(Number(item.engagement || 0) / engagementMax * 100))}%` } }))))); }))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Readable insights"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3 text-sm text-slate-400" }, (insightBlocks || []).map((item) => /* @__PURE__ */ React.createElement("a", { key: item.key, href: item.href, className: "block rounded-[22px] border border-white/10 bg-black/20 p-4 transition hover:border-white/20" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "flex h-10 w-10 items-center justify-center rounded-2xl bg-white/[0.04] text-sky-100" }, /* @__PURE__ */ React.createElement("i", { className: item.icon })), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h3", { className: "text-sm font-semibold text-white" }, item.title), /* @__PURE__ */ React.createElement("p", { className: "mt-2 leading-6 text-slate-400" }, item.body), /* @__PURE__ */ React.createElement("span", { className: "mt-3 inline-flex items-center gap-2 text-sm font-medium text-sky-100" }, item.cta, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-right" }))))))))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-6 xl:grid-cols-[minmax(0,1fr)_360px]" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Top content"), /* @__PURE__ */ React.createElement("div", { className: "mt-5 overflow-x-auto" }, /* @__PURE__ */ React.createElement("table", { className: "w-full text-sm" }, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", { className: "border-b border-white/5 text-left text-[11px] uppercase tracking-[0.18em] text-slate-500" }, /* @__PURE__ */ React.createElement("th", { className: "pb-3 pr-4" }, "Module"), /* @__PURE__ */ React.createElement("th", { className: "pb-3 pr-4" }, "Title"), /* @__PURE__ */ React.createElement("th", { className: "pb-3 pr-4 text-right" }, "Views"), /* @__PURE__ */ React.createElement("th", { className: "pb-3 pr-4 text-right" }, "Reactions"), /* @__PURE__ */ React.createElement("th", { className: "pb-3 pr-4 text-right" }, "Comments"), /* @__PURE__ */ React.createElement("th", { className: "pb-3 text-right" }, "Open"))), /* @__PURE__ */ React.createElement("tbody", { className: "divide-y divide-white/5" }, (topContent || []).map((item) => /* @__PURE__ */ React.createElement("tr", { key: item.id }, /* @__PURE__ */ React.createElement("td", { className: "py-3 pr-4 text-slate-300" }, item.module_label), /* @__PURE__ */ React.createElement("td", { className: "py-3 pr-4 text-white" }, item.title), /* @__PURE__ */ React.createElement("td", { className: "py-3 pr-4 text-right text-slate-300" }, Number(item.metrics?.views || 0).toLocaleString()), /* @__PURE__ */ React.createElement("td", { className: "py-3 pr-4 text-right text-slate-300" }, Number(item.metrics?.appreciation || 0).toLocaleString()), /* @__PURE__ */ React.createElement("td", { className: "py-3 pr-4 text-right text-slate-300" }, Number(item.metrics?.comments || 0).toLocaleString()), /* @__PURE__ */ React.createElement("td", { className: "py-3 text-right" }, /* @__PURE__ */ React.createElement("a", { href: item.analytics_url || item.view_url, className: "text-sky-100" }, "Open")))))))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Recent comments"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, (recentComments || []).map((comment) => /* @__PURE__ */ React.createElement("article", { key: comment.id, className: "rounded-[22px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-sky-200/70" }, comment.module_label), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-white" }, comment.author_name, " on ", comment.item_title), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm leading-6 text-slate-400" }, comment.body))))))); } -const __vite_glob_0_106 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_111 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioAnalytics }, Symbol.toStringTag, { value: "Module" })); @@ -100299,7 +101098,7 @@ function StudioArchived() { } )); } -const __vite_glob_0_107 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_112 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioArchived }, Symbol.toStringTag, { value: "Module" })); @@ -100335,7 +101134,7 @@ function StudioArtworkAnalytics() { } ), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-bold text-white" }, artwork?.title), /* @__PURE__ */ React.createElement("p", { className: "text-xs text-slate-500 mt-1" }, "/", artwork?.slug))), /* @__PURE__ */ React.createElement("div", { className: "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-4 mb-8" }, kpiItems$1.map((item) => /* @__PURE__ */ React.createElement("div", { key: item.key, className: "bg-nova-900/60 border border-white/10 rounded-2xl p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${item.icon} ${item.color}` }), /* @__PURE__ */ React.createElement("span", { className: "text-xs font-medium text-slate-400 uppercase tracking-wider" }, item.label)), /* @__PURE__ */ React.createElement("p", { className: "text-2xl font-bold text-white tabular-nums" }, (analytics?.[item.key] ?? 0).toLocaleString())))), /* @__PURE__ */ React.createElement("h3", { className: "text-base font-bold text-white mb-4" }, "Performance Metrics"), /* @__PURE__ */ React.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-3 gap-4 mb-8" }, metricCards.map((item) => /* @__PURE__ */ React.createElement("div", { key: item.key, className: "bg-nova-900/60 border border-white/10 rounded-2xl p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 mb-3" }, /* @__PURE__ */ React.createElement("div", { className: `w-10 h-10 rounded-xl bg-white/5 flex items-center justify-center ${item.color}` }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${item.icon} text-lg` })), /* @__PURE__ */ React.createElement("span", { className: "text-sm font-medium text-slate-300" }, item.label)), /* @__PURE__ */ React.createElement("p", { className: "text-3xl font-bold text-white tabular-nums" }, (analytics?.[item.key] ?? 0).toFixed(1))))), /* @__PURE__ */ React.createElement("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-4" }, /* @__PURE__ */ React.createElement("div", { className: "bg-nova-900/40 border border-white/10 rounded-2xl p-6" }, /* @__PURE__ */ React.createElement("h4", { className: "text-sm font-semibold text-white mb-3" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-chart-line mr-2 text-slate-500" }), "Traffic Sources"), /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-center py-8" }, /* @__PURE__ */ React.createElement("div", { className: "text-center" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-chart-pie text-3xl text-slate-700 mb-3" }), /* @__PURE__ */ React.createElement("p", { className: "text-xs text-slate-500" }, "Coming soon"), /* @__PURE__ */ React.createElement("p", { className: "text-[10px] text-slate-600 mt-1" }, "Traffic source tracking is on the roadmap")))), /* @__PURE__ */ React.createElement("div", { className: "bg-nova-900/40 border border-white/10 rounded-2xl p-6" }, /* @__PURE__ */ React.createElement("h4", { className: "text-sm font-semibold text-white mb-3" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-share-from-square mr-2 text-slate-500" }), "Shares by Platform"), /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-center py-8" }, /* @__PURE__ */ React.createElement("div", { className: "text-center" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-share-nodes text-3xl text-slate-700 mb-3" }), /* @__PURE__ */ React.createElement("p", { className: "text-xs text-slate-500" }, "Coming soon"), /* @__PURE__ */ React.createElement("p", { className: "text-[10px] text-slate-600 mt-1" }, "Per-platform breakdown coming in a future update")))), /* @__PURE__ */ React.createElement("div", { className: "bg-nova-900/40 border border-white/10 rounded-2xl p-6 lg:col-span-2" }, /* @__PURE__ */ React.createElement("h4", { className: "text-sm font-semibold text-white mb-3" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-trophy mr-2 text-slate-500" }), "Ranking History"), /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-center py-8" }, /* @__PURE__ */ React.createElement("div", { className: "text-center" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-chart-area text-3xl text-slate-700 mb-3" }), /* @__PURE__ */ React.createElement("p", { className: "text-xs text-slate-500" }, "Coming soon"), /* @__PURE__ */ React.createElement("p", { className: "text-[10px] text-slate-600 mt-1" }, "Historical ranking data will be tracked in a future update")))))); } -const __vite_glob_0_108 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_113 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioArtworkAnalytics }, Symbol.toStringTag, { value: "Module" })); @@ -101530,8 +102329,7 @@ const TABS = [ { id: "tags", label: "Tags", icon: "fa-solid fa-tags" }, { id: "worlds", label: "Worlds", icon: "fa-solid fa-globe" }, { id: "taxonomy", label: "Category", icon: "fa-solid fa-palette" }, - { id: "visibility", label: "Visibility", icon: "fa-solid fa-eye" }, - { id: "ai", label: "AI Assist", icon: "fa-solid fa-wand-magic-sparkles" } + { id: "visibility", label: "Visibility", icon: "fa-solid fa-eye" } ]; function getCsrfToken() { return document.querySelector('meta[name="csrf-token"]')?.getAttribute("content") || ""; @@ -102423,8 +103221,7 @@ function StudioArtworkEdit() { }, /* @__PURE__ */ React.createElement("i", { className: `${tab2.icon} text-[11px]`, "aria-hidden": "true" }), tab2.label, - tab2.id === "evolution" && evolutionTarget && /* @__PURE__ */ React.createElement("span", { className: "h-1.5 w-1.5 rounded-full bg-sky-400" }), - tab2.id === "ai" && aiStatus !== "not_analyzed" && /* @__PURE__ */ React.createElement("span", { className: `h-1.5 w-1.5 rounded-full ${aiStatus === "ready" ? "bg-emerald-400" : aiStatus === "failed" ? "bg-red-400" : "bg-sky-400"}` }) + tab2.id === "evolution" && evolutionTarget && /* @__PURE__ */ React.createElement("span", { className: "h-1.5 w-1.5 rounded-full bg-sky-400" }) ))), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 px-3 flex-shrink-0" }, saved && /* @__PURE__ */ React.createElement("span", { className: "text-xs text-emerald-400 flex items-center gap-1" }, /* @__PURE__ */ React.createElement("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("path", { fillRule: "evenodd", d: "M8 16A8 8 0 108 0a8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L7 8.586 5.707 7.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z", clipRule: "evenodd" })), "Saved"), /* @__PURE__ */ React.createElement(Button$1, { variant: "accent", size: "xs", loading: saving, onClick: handleSave }, "Save"))), activeTab === "taxonomy" && /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement(Section, { id: "taxonomy", className: "space-y-6" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-3 lg:flex-row lg:items-end lg:justify-between" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(SectionTitle, { icon: "fa-solid fa-palette" }, "Category"), /* @__PURE__ */ React.createElement("p", { className: "-mt-2 text-sm text-slate-400" }, "Pick a content type from the left, then choose the best category path on the right. The layout keeps the hierarchy visible instead of stretching into one long wall of chips.")), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2 text-xs" }, /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.03] px-3 py-1.5 text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "text-slate-500" }, "Type"), /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, selectedCT?.name || "Unset")), /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.03] px-3 py-1.5 text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "text-slate-500" }, "Path"), /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, selectedRoot?.name || "Choose category")))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 xl:grid-cols-[280px_minmax(0,1fr)]" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-[linear-gradient(180deg,rgba(255,255,255,0.04),rgba(255,255,255,0.02))] p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h4", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400" }, "Content types"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-xs text-slate-500" }, "Start here")), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-2 py-1 text-[11px] text-slate-400" }, contentTypes.length)), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-2" }, contentTypes.map((ct) => { const isActive = contentTypeId === ct.id; const visualKey = getContentTypeVisualKey$1(ct.slug); @@ -103112,7 +103909,7 @@ function StudioArtworkEdit() { )), historyData.versions.length === 0 && /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-500 text-center py-8" }, "No version history yet.")) )); } -const __vite_glob_0_109 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_114 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioArtworkEdit }, Symbol.toStringTag, { value: "Module" })); @@ -103133,7 +103930,7 @@ function StudioArtworks() { } )); } -const __vite_glob_0_110 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_115 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioArtworks }, Symbol.toStringTag, { value: "Module" })); @@ -103259,7 +104056,7 @@ function StudioAssets() { /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-right" }) )))); } -const __vite_glob_0_111 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_116 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioAssets }, Symbol.toStringTag, { value: "Module" })); @@ -103431,7 +104228,7 @@ function StudioCalendar() { }, [selectedDay]); return /* @__PURE__ */ React.createElement(StudioLayout, { title: props.title, subtitle: props.description }, /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement("section", { className: "grid gap-4 md:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Scheduled"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-3xl font-semibold text-white" }, Number(summary.scheduled_total || 0).toLocaleString())), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Unscheduled"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-3xl font-semibold text-white" }, Number(summary.unscheduled_total || 0).toLocaleString())), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Overloaded days"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-3xl font-semibold text-white" }, Number(summary.overloaded_days || 0).toLocaleString())), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Next publish"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-base font-semibold text-white" }, formatReleaseCountdown(summary.next_publish_at, nowMs)), summary.next_publish_at && /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-sm text-slate-400" }, formatScheduledDate(summary.next_publish_at)))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-[radial-gradient(circle_at_top_left,_rgba(56,189,248,0.14),_transparent_35%),linear-gradient(135deg,_rgba(15,23,42,0.86),_rgba(2,6,23,0.96))] p-5 lg:p-6" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 md:grid-cols-2 xl:grid-cols-5" }, /* @__PURE__ */ React.createElement("label", { className: "space-y-2 text-sm text-slate-300 xl:col-span-2" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Search planning queue"), /* @__PURE__ */ React.createElement("input", { value: filters.q || "", onChange: (event) => updateFilters({ q: event.target.value }), className: "w-full rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white", placeholder: "Title or module" })), /* @__PURE__ */ React.createElement("div", { className: "space-y-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "View"), /* @__PURE__ */ React.createElement(NovaSelect, { value: filters.view || "month", onChange: (val) => updateFilters({ view: val }), options: calendar.view_options || [], searchable: false })), /* @__PURE__ */ React.createElement("div", { className: "space-y-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Module"), /* @__PURE__ */ React.createElement(NovaSelect, { value: filters.module || "all", onChange: (val) => updateFilters({ module: val }), options: calendar.module_options || [], searchable: false })), /* @__PURE__ */ React.createElement("div", { className: "space-y-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Queue"), /* @__PURE__ */ React.createElement(NovaSelect, { value: filters.status || "scheduled", onChange: (val) => updateFilters({ status: val }), options: calendar.status_options || [], searchable: false })))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-6 xl:grid-cols-[minmax(0,1fr)_340px]" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-white/[0.03] p-5" }, filters.view === "week" ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, calendar.week?.label), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs uppercase tracking-[0.18em] text-slate-500" }, "Week planning")), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => shiftCalendar(-1), className: "rounded-full border border-white/10 px-3 py-1.5 text-sm text-slate-200 transition hover:border-sky-300/20 hover:bg-sky-300/10 hover:text-sky-100" }, "Prev week"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: resetCalendarFocus, className: "rounded-full border border-white/10 px-3 py-1.5 text-sm text-slate-200 transition hover:border-sky-300/20 hover:bg-sky-300/10 hover:text-sky-100" }, "Today"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => shiftCalendar(1), className: "rounded-full border border-white/10 px-3 py-1.5 text-sm text-slate-200 transition hover:border-sky-300/20 hover:bg-sky-300/10 hover:text-sky-100" }, "Next week"))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 md:grid-cols-2 xl:grid-cols-7" }, (calendar.week?.days || []).map((day) => /* @__PURE__ */ React.createElement("div", { key: day.date, className: "rounded-[22px] border border-white/10 bg-black/20 p-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold text-white" }, day.label), /* @__PURE__ */ React.createElement("div", { className: "mt-3 space-y-2" }, day.items.length > 0 ? day.items.map((item) => /* @__PURE__ */ React.createElement(CalendarInlineItem, { key: item.id, item })) : /* @__PURE__ */ React.createElement("div", { className: "text-xs text-slate-500" }, "No scheduled items")))))) : filters.view === "agenda" ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Agenda"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-4" }, (calendar.agenda || []).map((group) => /* @__PURE__ */ React.createElement("div", { key: group.date, className: "rounded-[22px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-base font-semibold text-white" }, group.label), /* @__PURE__ */ React.createElement("div", { className: "text-xs uppercase tracking-[0.18em] text-slate-500" }, group.count, " items")), /* @__PURE__ */ React.createElement("div", { className: "mt-3 space-y-2" }, group.items.map((item) => /* @__PURE__ */ React.createElement(CalendarInlineItem, { key: item.id, item }))))))) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, calendar.month?.label), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs uppercase tracking-[0.18em] text-slate-500" }, "Month planning")), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => shiftCalendar(-1), className: "rounded-full border border-white/10 px-3 py-1.5 text-sm text-slate-200 transition hover:border-sky-300/20 hover:bg-sky-300/10 hover:text-sky-100" }, "Prev month"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: resetCalendarFocus, className: "rounded-full border border-white/10 px-3 py-1.5 text-sm text-slate-200 transition hover:border-sky-300/20 hover:bg-sky-300/10 hover:text-sky-100" }, "Today"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => shiftCalendar(1), className: "rounded-full border border-white/10 px-3 py-1.5 text-sm text-slate-200 transition hover:border-sky-300/20 hover:bg-sky-300/10 hover:text-sky-100" }, "Next month"))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid grid-cols-7 gap-2 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].map((label) => /* @__PURE__ */ React.createElement("div", { key: label, className: "px-2 py-1" }, label))), /* @__PURE__ */ React.createElement("div", { className: "mt-2 grid grid-cols-7 gap-2" }, (calendar.month?.days || []).map((day) => /* @__PURE__ */ React.createElement(CalendarMonthDay, { key: day.date, day, onOpenDetail: setSelectedDay }))))), /* @__PURE__ */ React.createElement("aside", { className: "space-y-6" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Coverage gaps"), /* @__PURE__ */ React.createElement("a", { href: "/studio/drafts", className: "text-sm font-medium text-sky-100" }, "Open drafts")), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, (calendar.gaps || []).length > 0 ? (calendar.gaps || []).map((gap) => /* @__PURE__ */ React.createElement("div", { key: gap.date, className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-sm text-slate-200" }, gap.label)) : /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-dashed border-white/15 px-4 py-8 text-sm text-slate-500" }, "No empty days in the next two weeks."))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Unscheduled queue"), /* @__PURE__ */ React.createElement("span", { className: "text-xs uppercase tracking-[0.18em] text-slate-500" }, (calendar.unscheduled_items || []).length)), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, (calendar.unscheduled_items || []).map((item) => /* @__PURE__ */ React.createElement("a", { key: item.id, href: item.edit_url || item.manage_url, className: "block rounded-2xl border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold text-white" }, item.title), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs text-slate-500" }, item.module_label, " · ", item.workflow?.readiness?.label || "Needs review"))))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Upcoming actions"), /* @__PURE__ */ React.createElement("a", { href: "/studio/scheduled", className: "text-sm font-medium text-sky-100" }, "Open list")), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, (calendar.scheduled_items || []).slice(0, 5).map((item) => /* @__PURE__ */ React.createElement("div", { key: item.id, className: "rounded-2xl border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold text-white" }, item.title), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs font-medium text-sky-200" }, formatReleaseCountdown(item.scheduled_at, nowMs)), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs text-slate-500" }, formatScheduledDate(item.scheduled_at)), /* @__PURE__ */ React.createElement("div", { className: "mt-3 flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement("button", { type: "button", disabled: busyKey === `publish:${item.id}`, onClick: () => runAction(props.endpoints.publishNowPattern, item, "publish"), className: "rounded-full border border-sky-300/20 bg-sky-300/10 px-3 py-1.5 text-xs text-sky-100 disabled:opacity-50" }, "Publish now"), /* @__PURE__ */ React.createElement("button", { type: "button", disabled: busyKey === `unschedule:${item.id}`, onClick: () => runAction(props.endpoints.unschedulePattern, item, "unschedule"), className: "rounded-full border border-white/10 px-3 py-1.5 text-xs text-slate-200 disabled:opacity-50" }, "Unschedule")))))))), /* @__PURE__ */ React.createElement(CalendarDayModal, { day: selectedDay, busyKey, endpoints: props.endpoints, onAction: runAction, onClose: () => setSelectedDay(null), nowMs }))); } -const __vite_glob_0_112 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_117 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioCalendar }, Symbol.toStringTag, { value: "Module" })); @@ -103453,7 +104250,7 @@ function StudioCardAnalytics() { const { card, analytics } = props; return /* @__PURE__ */ React.createElement(StudioLayout, { title: `Analytics: ${card?.title || "Nova Card"}` }, /* @__PURE__ */ React.createElement(xe, { href: "/studio/cards", className: "mb-6 inline-flex items-center gap-2 text-sm text-slate-400 transition-colors hover:text-white" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-left" }), "Back to Cards"), /* @__PURE__ */ React.createElement("div", { className: "mb-8 flex items-center gap-4 rounded-2xl border border-white/10 bg-nova-900/60 p-4" }, card?.preview_url ? /* @__PURE__ */ React.createElement("img", { src: card.preview_url, alt: card.title, className: "h-20 w-20 rounded-xl object-cover bg-nova-800" }) : null, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-bold text-white" }, card?.title), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-xs text-slate-500" }, "/", card?.slug), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-xs uppercase tracking-[0.18em] text-slate-400" }, card?.status, " • ", card?.visibility))), /* @__PURE__ */ React.createElement("div", { className: "mb-8 grid grid-cols-2 gap-4 sm:grid-cols-3 xl:grid-cols-6" }, kpiItems.map((item) => /* @__PURE__ */ React.createElement("div", { key: item.key, className: "rounded-2xl border border-white/10 bg-nova-900/60 p-5" }, /* @__PURE__ */ React.createElement("div", { className: "mb-2 flex items-center gap-2" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${item.icon} ${item.color}` }), /* @__PURE__ */ React.createElement("span", { className: "text-xs font-medium uppercase tracking-wider text-slate-400" }, item.label)), /* @__PURE__ */ React.createElement("p", { className: "text-2xl font-bold tabular-nums text-white" }, (analytics?.[item.key] ?? 0).toLocaleString())))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 lg:grid-cols-[minmax(0,1.1fr)_minmax(0,0.9fr)]" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-nova-900/40 p-6" }, /* @__PURE__ */ React.createElement("h3", { className: "mb-4 text-sm font-semibold uppercase tracking-[0.18em] text-slate-300" }, "Ranking signals"), /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 sm:grid-cols-2" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.03] p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-xs uppercase tracking-[0.18em] text-slate-400" }, "Trending score"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-3xl font-bold tabular-nums text-white" }, Number(analytics?.trending_score ?? 0).toFixed(2))), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/[0.03] p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-xs uppercase tracking-[0.18em] text-slate-400" }, "Last engaged"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-sm text-white" }, analytics?.last_engaged_at || "No activity yet")))), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-nova-900/40 p-6" }, /* @__PURE__ */ React.createElement("h3", { className: "mb-4 text-sm font-semibold uppercase tracking-[0.18em] text-slate-300" }, "Secondary metrics"), /* @__PURE__ */ React.createElement("div", { className: "space-y-3" }, secondaryItems.map((item) => /* @__PURE__ */ React.createElement("div", { key: item.key, className: "flex items-center justify-between rounded-2xl border border-white/10 bg-white/[0.03] px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${item.icon} text-slate-500` }), item.label), /* @__PURE__ */ React.createElement("div", { className: "text-base font-semibold tabular-nums text-white" }, (analytics?.[item.key] ?? 0).toLocaleString()))))))); } -const __vite_glob_0_113 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_118 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioCardAnalytics }, Symbol.toStringTag, { value: "Module" })); @@ -104597,7 +105394,7 @@ function StudioCardEditor() { fmt2.label ))), exportStatus && /* @__PURE__ */ React.createElement("div", { className: "mt-3 flex items-center gap-3 rounded-[18px] border border-white/10 bg-white/[0.03] p-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "text-xs font-semibold uppercase tracking-[0.18em] text-slate-400" }, "Export status"), /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold capitalize text-white" }, exportStatus.status)), exportStatus.status === "ready" && exportStatus.output_url && /* @__PURE__ */ React.createElement("a", { href: exportStatus.output_url, download: true, className: "ml-auto rounded-full border border-emerald-300/20 bg-emerald-400/10 px-3 py-1.5 text-xs font-semibold text-emerald-200 transition hover:bg-emerald-400/15" }, "Download"))))), /* @__PURE__ */ React.createElement("nav", { className: "sticky bottom-0 z-20 mt-6 border-t border-white/10 bg-[rgba(2,6,23,0.92)] px-4 py-3 backdrop-blur xl:hidden" }, /* @__PURE__ */ React.createElement("div", { className: "mx-auto flex max-w-7xl items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => goToNextTab(-1), disabled: tabIndex === 0, className: "rounded-2xl border border-white/10 bg-white/[0.05] px-4 py-2.5 text-sm font-semibold text-white transition hover:bg-white/[0.08] disabled:opacity-50" }, "Back"), /* @__PURE__ */ React.createElement("div", { className: "text-center" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Step ", tabIndex + 1, " / ", editorTabs.length), /* @__PURE__ */ React.createElement("div", { className: "mt-0.5 text-sm font-semibold text-white" }, editorTabs[tabIndex]?.label)), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => goToNextTab(1), disabled: tabIndex >= editorTabs.length - 1, className: "rounded-2xl border border-sky-300/20 bg-sky-400/10 px-4 py-2.5 text-sm font-semibold text-sky-100 transition hover:bg-sky-400/15 disabled:opacity-50" }, "Next")))); } -const __vite_glob_0_114 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_119 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioCardEditor }, Symbol.toStringTag, { value: "Module" })); @@ -104619,7 +105416,7 @@ function StudioCardsIndex() { } ))); } -const __vite_glob_0_115 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_120 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioCardsIndex }, Symbol.toStringTag, { value: "Module" })); @@ -104692,7 +105489,7 @@ function StudioChallenges() { "Challenge" ), /* @__PURE__ */ React.createElement("a", { href: entry.card.edit_url, className: "text-slate-300" }, "Edit card"), /* @__PURE__ */ React.createElement("a", { href: entry.card.analytics_url, className: "text-slate-300" }, "Analytics")))))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Cards with challenge traction"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, (cardLeaders || []).map((card) => /* @__PURE__ */ React.createElement("div", { key: card.id, className: "rounded-[22px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold text-white" }, card.title), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs uppercase tracking-[0.16em] text-slate-500" }, card.status, " • ", card.challenge_entries_count, " challenge entries")), /* @__PURE__ */ React.createElement("a", { href: card.edit_url, className: "text-xs font-semibold uppercase tracking-[0.16em] text-sky-100" }, "Open")), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid grid-cols-2 gap-3 text-sm text-slate-400" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", null, "Views"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, Number(card.views_count || 0).toLocaleString())), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", null, "Comments"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, Number(card.comments_count || 0).toLocaleString()))))))))); } -const __vite_glob_0_116 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_121 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioChallenges }, Symbol.toStringTag, { value: "Module" })); @@ -104712,7 +105509,7 @@ function StudioCollections() { } )); } -const __vite_glob_0_117 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_122 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioCollections }, Symbol.toStringTag, { value: "Module" })); @@ -104999,7 +105796,7 @@ function StudioComments() { /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-right" }) ))))); } -const __vite_glob_0_118 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_123 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioComments }, Symbol.toStringTag, { value: "Module" })); @@ -105015,7 +105812,7 @@ function StudioContentIndex() { } )); } -const __vite_glob_0_119 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_124 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioContentIndex }, Symbol.toStringTag, { value: "Module" })); @@ -105141,7 +105938,7 @@ function StudioDashboard() { ["Comments", analytics.totals?.comments] ].map(([label, value]) => /* @__PURE__ */ React.createElement("div", { key: label, className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", null, label), /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, Number(value || 0).toLocaleString()))))))), showWidget("stale_drafts") && /* @__PURE__ */ React.createElement("div", { className: "mt-6 rounded-[30px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Stale drafts"), /* @__PURE__ */ React.createElement("a", { href: "/studio/content?bucket=drafts&stale=only&module=stories", className: "text-sm font-medium text-sky-100" }, "Filter stale work")), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 md:grid-cols-4" }, (overview.stale_drafts || []).map((item) => /* @__PURE__ */ React.createElement(ContinueWorkingCard, { key: item.id, item }))))); } -const __vite_glob_0_120 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_125 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioDashboard }, Symbol.toStringTag, { value: "Module" })); @@ -105158,7 +105955,7 @@ function StudioDrafts() { } )); } -const __vite_glob_0_121 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_126 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioDrafts }, Symbol.toStringTag, { value: "Module" })); @@ -105241,7 +106038,7 @@ function StudioFeatured() { })) : /* @__PURE__ */ React.createElement("div", { className: "mt-5 rounded-[24px] border border-dashed border-white/15 px-6 py-10 text-center text-sm text-slate-400" }, "No published ", module.label.toLowerCase(), " candidates yet.")); }))); } -const __vite_glob_0_122 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_127 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioFeatured }, Symbol.toStringTag, { value: "Module" })); @@ -105267,7 +106064,7 @@ function StudioFollowers() { }; return /* @__PURE__ */ React.createElement(StudioLayout, { title: props.title, subtitle: props.description }, /* @__PURE__ */ React.createElement("div", { className: "mb-6 grid gap-4 md:grid-cols-3" }, /* @__PURE__ */ React.createElement(SummaryCard$1, { label: "Total followers", value: summary.total_followers, icon: "fa-solid fa-user-group" }), /* @__PURE__ */ React.createElement(SummaryCard$1, { label: "Following back", value: summary.following_back, icon: "fa-solid fa-arrows-rotate" }), /* @__PURE__ */ React.createElement(SummaryCard$1, { label: "Not followed yet", value: summary.not_followed, icon: "fa-solid fa-user-plus" })), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 lg:grid-cols-[minmax(0,1fr)_220px_220px]" }, /* @__PURE__ */ React.createElement("label", { className: "space-y-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Search"), /* @__PURE__ */ React.createElement("input", { value: filters.q || "", onChange: (event) => updateQuery({ q: event.target.value, page: 1 }), placeholder: "Search followers", className: "w-full rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-sm text-white" })), /* @__PURE__ */ React.createElement("div", { className: "space-y-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Sort"), /* @__PURE__ */ React.createElement(NovaSelect, { value: filters.sort || "recent", onChange: (val) => updateQuery({ sort: val, page: 1 }), options: listing.sort_options || [], searchable: false })), /* @__PURE__ */ React.createElement("div", { className: "space-y-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500" }, "Relationship"), /* @__PURE__ */ React.createElement(NovaSelect, { value: filters.relationship || "all", onChange: (val) => updateQuery({ relationship: val, page: 1 }), options: listing.relationship_options || [], searchable: false }))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 space-y-3" }, items.map((item) => /* @__PURE__ */ React.createElement("article", { key: item.id, className: "flex flex-col gap-4 rounded-[24px] border border-white/10 bg-black/20 p-4 md:flex-row md:items-center md:justify-between" }, /* @__PURE__ */ React.createElement("a", { href: item.profile_url, className: "flex min-w-0 items-center gap-4" }, item.avatar_url ? /* @__PURE__ */ React.createElement("img", { src: item.avatar_url, alt: item.username, className: "h-14 w-14 rounded-[18px] object-cover" }) : /* @__PURE__ */ React.createElement("div", { className: "flex h-14 w-14 items-center justify-center rounded-[18px] bg-white/5 text-slate-400" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-user" })), /* @__PURE__ */ React.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React.createElement("div", { className: "truncate text-base font-semibold text-white" }, item.name), /* @__PURE__ */ React.createElement("div", { className: "text-sm text-slate-400" }, "@", item.username))), /* @__PURE__ */ React.createElement("div", { className: "grid grid-cols-2 gap-4 text-sm text-slate-400 md:grid-cols-4 md:text-right" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", null, "Uploads"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, Number(item.uploads_count || 0).toLocaleString())), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", null, "Followers"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, Number(item.followers_count || 0).toLocaleString())), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", null, "Followed"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, item.followed_at ? new Date(item.followed_at).toLocaleDateString() : "—")), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", null, "Status"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-semibold text-white" }, item.is_following_back ? "Following back" : "Not followed")))))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 flex items-center justify-between rounded-[24px] border border-white/10 bg-white/[0.03] px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("button", { type: "button", disabled: (meta.current_page || 1) <= 1, onClick: () => updateQuery({ page: Math.max(1, (meta.current_page || 1) - 1) }), className: "inline-flex items-center gap-2 rounded-full border border-white/10 px-4 py-2 disabled:opacity-40" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-left" }), "Previous"), /* @__PURE__ */ React.createElement("span", null, "Page ", meta.current_page || 1, " of ", meta.last_page || 1), /* @__PURE__ */ React.createElement("button", { type: "button", disabled: (meta.current_page || 1) >= (meta.last_page || 1), onClick: () => updateQuery({ page: (meta.current_page || 1) + 1 }), className: "inline-flex items-center gap-2 rounded-full border border-white/10 px-4 py-2 disabled:opacity-40" }, "Next", /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-right" }))))); } -const __vite_glob_0_123 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_128 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioFollowers }, Symbol.toStringTag, { value: "Module" })); @@ -105276,7 +106073,7 @@ function StudioGroupActivity() { const items = Array.isArray(props.activity) ? props.activity : []; return /* @__PURE__ */ React.createElement(StudioLayout, { title: props.title, subtitle: props.description }, /* @__PURE__ */ React.createElement("div", { className: "space-y-4" }, items.length > 0 ? items.map((item) => /* @__PURE__ */ React.createElement("div", { key: item.id, className: "rounded-[24px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2" }, /* @__PURE__ */ React.createElement("h2", { className: "text-base font-semibold text-white" }, item.headline), item.is_pinned ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-amber-300/20 bg-amber-300/10 px-2 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-amber-100" }, "Pinned") : null, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-2 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-300" }, item.visibility)), item.summary ? /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm leading-6 text-slate-400" }, item.summary) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-3 text-xs text-slate-500" }, item.actor?.name || item.actor?.username || "System", " • ", item.occurred_at ? new Date(item.occurred_at).toLocaleString() : "Recently"), item.subject?.url ? /* @__PURE__ */ React.createElement("a", { href: item.subject.url, className: "mt-3 inline-flex text-sm font-semibold text-sky-200" }, "Open subject") : null), props.pinPattern ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => At.post(props.pinPattern.replace("__ITEM__", String(item.id)), { is_pinned: !item.is_pinned }), className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1.5 text-sm font-semibold text-white" }, item.is_pinned ? "Unpin" : "Pin") : null))) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-dashed border-white/10 bg-white/[0.02] p-6 text-sm text-slate-400" }, "No activity yet."))); } -const __vite_glob_0_124 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_129 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupActivity }, Symbol.toStringTag, { value: "Module" })); @@ -105284,7 +106081,7 @@ function StudioGroupArtworks() { const { props } = X$1(); return /* @__PURE__ */ React.createElement(StudioLayout, { title: props.title, subtitle: props.description }, /* @__PURE__ */ React.createElement("div", { className: "mb-6 rounded-[28px] border border-sky-300/20 bg-sky-300/10 p-5 text-sky-100" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.18em]" }, "Group publish flow"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-xl font-semibold" }, "Upload into ", props.studioGroup?.name), /* @__PURE__ */ React.createElement("a", { href: props.uploadUrl, className: "mt-4 inline-flex rounded-full border border-sky-200/20 bg-sky-200/10 px-4 py-2 text-sm font-semibold text-sky-50" }, "New group artwork")), /* @__PURE__ */ React.createElement(StudioContentBrowser, { listing: props.listing, quickCreate: [{ key: "artworks", label: "Artwork", icon: "fa-solid fa-cloud-arrow-up", url: props.uploadUrl }], hideModuleFilter: true })); } -const __vite_glob_0_125 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_130 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupArtworks }, Symbol.toStringTag, { value: "Module" })); @@ -105325,7 +106122,7 @@ function StudioGroupAssets() { }; return /* @__PURE__ */ React.createElement(StudioLayout, { title: props.title, subtitle: props.description }, props.storeUrl ? /* @__PURE__ */ React.createElement("form", { onSubmit: submit, className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 lg:grid-cols-6" }, /* @__PURE__ */ React.createElement("input", { value: form.data.title, onChange: (event) => form.setData("title", event.target.value), placeholder: "Asset title", className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none lg:col-span-2" }), /* @__PURE__ */ React.createElement(NovaSelect, { value: form.data.category, onChange: (val) => form.setData("category", val), options: props.categoryOptions || [], searchable: false }), /* @__PURE__ */ React.createElement(NovaSelect, { value: form.data.visibility, onChange: (val) => form.setData("visibility", val), options: props.visibilityOptions || [], searchable: false }), /* @__PURE__ */ React.createElement(NovaSelect, { value: form.data.status, onChange: (val) => form.setData("status", val), options: props.statusOptions || [], searchable: false }), /* @__PURE__ */ React.createElement("input", { type: "file", onChange: (event) => form.setData("file", event.target.files?.[0] || null), className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement("textarea", { value: form.data.description, onChange: (event) => form.setData("description", event.target.value), placeholder: "What is this asset for?", rows: 3, className: "mt-4 w-full rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" }), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-4 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: String(form.data.linked_project_id || ""), onChange: (val) => form.setData("linked_project_id", val), placeholder: "No linked project", options: (props.projectOptions || []).map((o) => ({ value: String(o.id), label: o.title })) }), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-sm text-white" }, /* @__PURE__ */ React.createElement(Checkbox, { checked: form.data.is_featured, onChange: (event) => form.setData("is_featured", event.target.checked), label: "Featured asset" }))), /* @__PURE__ */ React.createElement("button", { type: "submit", className: "mt-4 rounded-full border border-white/10 bg-white/[0.05] px-5 py-2.5 text-sm font-semibold text-white" }, "Upload asset")) : null, /* @__PURE__ */ React.createElement("form", { onSubmit: applyFilters, className: "mt-6 rounded-[28px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-end justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Browse library"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "Search and filter shared assets by visibility and category.")), /* @__PURE__ */ React.createElement("button", { type: "submit", className: "rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-sm font-semibold text-white" }, "Apply filters")), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-4 lg:grid-cols-3" }, /* @__PURE__ */ React.createElement("input", { value: filters.data.q, onChange: (event) => filters.setData("q", event.target.value), placeholder: "Search title, description, or filename", className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" }), /* @__PURE__ */ React.createElement(NovaSelect, { value: filters.data.category, onChange: (val) => filters.setData("category", val), options: [{ value: "all", label: "All categories" }, ...props.categoryOptions || []], searchable: false }), /* @__PURE__ */ React.createElement(NovaSelect, { value: filters.data.bucket, onChange: (val) => filters.setData("bucket", val), options: [{ value: "all", label: "All visibility levels" }, ...(props.listing?.bucket_options || []).filter((option) => option.value !== "all")], searchable: false }))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 lg:grid-cols-2" }, items.length > 0 ? items.map((asset) => /* @__PURE__ */ React.createElement("div", { key: asset.id, className: "rounded-[24px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, asset.title), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-xs uppercase tracking-[0.16em] text-slate-500" }, asset.category, " • ", asset.visibility, " • ", asset.status)), /* @__PURE__ */ React.createElement("a", { href: asset.download_url, className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1.5 text-sm font-semibold text-white" }, "Download")), asset.description ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-6 text-slate-400" }, asset.description) : null, props.updatePattern ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => At.patch(props.updatePattern.replace("__ASSET__", String(asset.id)), { title: asset.title, description: asset.description || "", category: asset.category, visibility: asset.visibility, status: asset.status === "active" ? "archived" : "active", linked_project_id: asset.linked_project?.id || "", is_featured: asset.is_featured }), className: "mt-4 rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white" }, asset.status === "active" ? "Archive" : "Reactivate") : null)) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-dashed border-white/10 bg-white/[0.02] p-6 text-sm text-slate-400" }, "No assets yet."))); } -const __vite_glob_0_126 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_131 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupAssets }, Symbol.toStringTag, { value: "Module" })); @@ -105397,7 +106194,7 @@ function StudioGroupChallengeEditor() { attachForm.post(props.attachArtworkUrl, { preserveScroll: true }); }, className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Attach artwork"), /* @__PURE__ */ React.createElement(NovaSelect, { value: String(attachForm.data.artwork_id || ""), onChange: (val) => attachForm.setData("artwork_id", val), placeholder: "Choose artwork", options: (props.artworkOptions || []).map((o) => ({ value: String(o.id), label: o.title })) }), /* @__PURE__ */ React.createElement("button", { type: "submit", className: "mt-4 rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-sm font-semibold text-white" }, "Attach")) : null))); } -const __vite_glob_0_127 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_132 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupChallengeEditor }, Symbol.toStringTag, { value: "Module" })); @@ -105406,7 +106203,7 @@ function StudioGroupChallenges() { const items = Array.isArray(props.listing?.items) ? props.listing.items : []; return /* @__PURE__ */ React.createElement(StudioLayout, { title: props.title, subtitle: props.description }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm text-slate-400" }, "Challenges keep the group active between releases and give members a focused creative prompt."), props.createUrl ? /* @__PURE__ */ React.createElement("a", { href: props.createUrl, className: "rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white" }, "Create challenge") : null), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 lg:grid-cols-2" }, items.length > 0 ? items.map((challenge) => /* @__PURE__ */ React.createElement("a", { key: challenge.id, href: challenge.urls?.edit || challenge.url, className: "rounded-[24px] border border-white/10 bg-white/[0.03] p-5 transition hover:border-white/20" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, challenge.title), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-300" }, challenge.status)), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-6 text-slate-400" }, challenge.summary || "Challenge page"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 text-xs text-slate-500" }, challenge.entry_count || 0, " linked entries"))) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-dashed border-white/10 bg-white/[0.02] p-6 text-sm text-slate-400" }, "No challenges yet."))); } -const __vite_glob_0_128 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_133 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupChallenges }, Symbol.toStringTag, { value: "Module" })); @@ -105414,7 +106211,7 @@ function StudioGroupCollections() { const { props } = X$1(); return /* @__PURE__ */ React.createElement(StudioLayout, { title: props.title, subtitle: props.description }, /* @__PURE__ */ React.createElement("div", { className: "mb-6 rounded-[28px] border border-sky-300/20 bg-sky-300/10 p-5 text-sky-100" }, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.18em]" }, "Shared curation"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-xl font-semibold" }, "Create collections for ", props.studioGroup?.name), /* @__PURE__ */ React.createElement("a", { href: props.createUrl, className: "mt-4 inline-flex rounded-full border border-sky-200/20 bg-sky-200/10 px-4 py-2 text-sm font-semibold text-sky-50" }, "New group collection")), /* @__PURE__ */ React.createElement(StudioContentBrowser, { listing: props.listing, quickCreate: [{ key: "collections", label: "Collection", icon: "fa-solid fa-layer-group", url: props.createUrl }], hideModuleFilter: true })); } -const __vite_glob_0_129 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_134 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupCollections }, Symbol.toStringTag, { value: "Module" })); @@ -105528,7 +106325,7 @@ function StudioGroupCreate() { } )), /* @__PURE__ */ React.createElement("section", { className: "mx-auto max-w-3xl rounded-[30px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-5" }, /* @__PURE__ */ React.createElement("label", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "Name"), /* @__PURE__ */ React.createElement("input", { value: form.name, onChange: handleNameChange, className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement("label", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "Slug"), /* @__PURE__ */ React.createElement("input", { value: form.slug, onChange: handleSlugChange, className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement("label", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "Short description"), /* @__PURE__ */ React.createElement("input", { value: form.headline, onChange: (event) => setForm((current) => ({ ...current, headline: event.target.value })), className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement("label", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "About"), /* @__PURE__ */ React.createElement("textarea", { value: form.bio, onChange: (event) => setForm((current) => ({ ...current, bio: event.target.value })), rows: 6, className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2" }, /* @__PURE__ */ React.createElement("label", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "Type / category"), /* @__PURE__ */ React.createElement("input", { value: form.type, onChange: (event) => setForm((current) => ({ ...current, type: event.target.value })), className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement("div", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "Founded date"), /* @__PURE__ */ React.createElement(DateTimePicker, { value: form.founded_at, onChange: (nextValue) => setForm((current) => ({ ...current, founded_at: nextValue })), mode: "date", placeholder: "Pick the founding date", clearable: true, className: "bg-black/20" }))), /* @__PURE__ */ React.createElement("label", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "Website"), /* @__PURE__ */ React.createElement("input", { value: form.website_url, onChange: (event) => setForm((current) => ({ ...current, website_url: event.target.value })), className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 rounded-[24px] border border-white/10 bg-black/20 p-4 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", { className: "text-sm font-semibold text-white" }, "Avatar / logo"), /* @__PURE__ */ React.createElement("div", { className: "flex h-28 w-28 items-center justify-center overflow-hidden rounded-[24px] border border-white/10 bg-white/[0.04]" }, resolvedAvatarPreview ? /* @__PURE__ */ React.createElement("img", { src: resolvedAvatarPreview, alt: "Avatar preview", className: "h-full w-full object-cover" }) : /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-image text-slate-500" })), /* @__PURE__ */ React.createElement("input", { ref: avatarInputRef, type: "file", accept: "image/png,image/jpeg,image/webp", onChange: handleFileSelected("avatar_file", setAvatarPreview), className: "hidden" }), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => avatarInputRef.current?.click(), className: "rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white" }, "Upload avatar"), form.avatar_file ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => clearSelectedFile("avatar_file", setAvatarPreview, avatarInputRef), className: "rounded-full border border-white/10 bg-transparent px-4 py-2 text-sm font-semibold text-slate-300" }, "Use URL instead") : null), /* @__PURE__ */ React.createElement("label", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "Or paste an image URL"), /* @__PURE__ */ React.createElement("input", { value: form.avatar_path, onChange: (event) => setForm((current) => ({ ...current, avatar_path: event.target.value })), placeholder: "https://", className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" }))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 rounded-[24px] border border-white/10 bg-black/20 p-4 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", { className: "text-sm font-semibold text-white" }, "Cover image"), /* @__PURE__ */ React.createElement("div", { className: "flex h-28 w-full items-center justify-center overflow-hidden rounded-[24px] border border-white/10 bg-white/[0.04]" }, resolvedBannerPreview ? /* @__PURE__ */ React.createElement("img", { src: resolvedBannerPreview, alt: "Cover preview", className: "h-full w-full object-cover" }) : /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-panorama text-slate-500" })), /* @__PURE__ */ React.createElement("input", { ref: bannerInputRef, type: "file", accept: "image/png,image/jpeg,image/webp", onChange: handleFileSelected("banner_file", setBannerPreview), className: "hidden" }), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => bannerInputRef.current?.click(), className: "rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white" }, "Upload cover"), form.banner_file ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => clearSelectedFile("banner_file", setBannerPreview, bannerInputRef), className: "rounded-full border border-white/10 bg-transparent px-4 py-2 text-sm font-semibold text-slate-300" }, "Use URL instead") : null), /* @__PURE__ */ React.createElement("label", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "Or paste an image URL"), /* @__PURE__ */ React.createElement("input", { value: form.banner_path, onChange: (event) => setForm((current) => ({ ...current, banner_path: event.target.value })), placeholder: "https://", className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" })))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "Visibility"), /* @__PURE__ */ React.createElement(NovaSelect, { value: form.visibility, onChange: (val) => setForm((current) => ({ ...current, visibility: val })), options: props.visibilityOptions || [], searchable: false })), /* @__PURE__ */ React.createElement("div", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "Membership policy"), /* @__PURE__ */ React.createElement(NovaSelect, { value: form.membership_policy, onChange: (val) => setForm((current) => ({ ...current, membership_policy: val })), options: props.membershipPolicyOptions || [], searchable: false })), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("span", { className: "text-sm text-slate-200" }, "Links"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: addLink, className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1.5 text-xs font-semibold text-white" }, "Add link")), form.links_json.map((item, index2) => /* @__PURE__ */ React.createElement("div", { key: `link-${index2}`, className: "grid gap-3 md:grid-cols-[0.8fr_1.2fr_auto]" }, /* @__PURE__ */ React.createElement("input", { value: item.label, onChange: (event) => updateLink(index2, "label", event.target.value), placeholder: "Label", className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" }), /* @__PURE__ */ React.createElement("input", { value: item.url, onChange: (event) => updateLink(index2, "url", event.target.value), placeholder: "https://", className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" }), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => removeLink(index2), className: "rounded-full border border-rose-300/20 bg-rose-400/10 px-4 py-2 text-sm font-semibold text-rose-100" }, "Remove")))), /* @__PURE__ */ React.createElement("div", { className: "flex justify-end gap-3" }, /* @__PURE__ */ React.createElement("a", { href: "/studio/groups", className: "rounded-full border border-white/10 bg-white/[0.03] px-4 py-2 text-sm font-semibold text-white" }, "Cancel"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: submit, className: "rounded-full border border-sky-300/20 bg-sky-300/10 px-4 py-2 text-sm font-semibold text-sky-100" }, "Create group"))))); } -const __vite_glob_0_130 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_135 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupCreate }, Symbol.toStringTag, { value: "Module" })); @@ -105593,7 +106390,7 @@ function StudioGroupDashboard() { }; return /* @__PURE__ */ React.createElement(StudioLayout, { title: props.title, subtitle: props.description }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 md:grid-cols-3 xl:grid-cols-6" }, /* @__PURE__ */ React.createElement(StatCard, { label: "Artworks", value: group?.counts?.artworks, icon: "fa-solid fa-images" }), /* @__PURE__ */ React.createElement(StatCard, { label: "Collections", value: group?.counts?.collections, icon: "fa-solid fa-layer-group" }), /* @__PURE__ */ React.createElement(StatCard, { label: "Followers", value: group?.counts?.followers, icon: "fa-solid fa-user-group" }), /* @__PURE__ */ React.createElement(StatCard, { label: "Active members", value: dashboard?.active_members_count || group?.counts?.members, icon: "fa-solid fa-people-group" }), /* @__PURE__ */ React.createElement(StatCard, { label: "Projects", value: dashboard?.projects_count, icon: "fa-solid fa-diagram-project" }), /* @__PURE__ */ React.createElement(StatCard, { label: "Releases", value: dashboard?.published_releases_count || dashboard?.releases_count, icon: "fa-solid fa-rocket" }), /* @__PURE__ */ React.createElement(StatCard, { label: "Assets", value: dashboard?.assets_count, icon: "fa-solid fa-box-archive" })), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-6 xl:grid-cols-[minmax(0,1.2fr)_minmax(0,0.8fr)]" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Quick actions"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "Run the most common group tasks without leaving the dashboard."))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 md:grid-cols-2" }, quickActions.map((action) => /* @__PURE__ */ React.createElement("a", { key: action.label, href: action.href, className: `rounded-[24px] border px-4 py-4 transition hover:translate-y-[-1px] hover:border-white/20 ${toneClasses2[action.tone] || toneClasses2.sky}` }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ React.createElement("span", { className: "inline-flex h-11 w-11 items-center justify-center rounded-2xl border border-current/20 bg-black/10" }, /* @__PURE__ */ React.createElement("i", { className: action.icon })), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold" }, action.label), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs opacity-80" }, action.detail)))))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h3", { className: "text-lg font-semibold text-white" }, "Pending action"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "Drafts and scheduled items that still need a publishing decision.")), /* @__PURE__ */ React.createElement("div", { className: "text-right text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", null, Number(dashboard?.draft_artworks_count || 0), " drafts"), /* @__PURE__ */ React.createElement("div", null, Number(dashboard?.scheduled_artworks_count || 0), " scheduled"))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 md:grid-cols-2" }, draftsPendingAction.length > 0 ? draftsPendingAction.map((artwork) => /* @__PURE__ */ React.createElement(ContentCard, { key: artwork.id, item: artwork, fallbackLabel: "Draft" })) : /* @__PURE__ */ React.createElement(EmptyCard, { title: "No drafts waiting", description: "This group has no draft artworks waiting for review or completion right now." }))), pendingJoinRequests.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "mt-6 rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h3", { className: "text-lg font-semibold text-white" }, "Pending join requests"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "Applicants waiting for a review decision.")), group?.urls?.studio_join_requests ? /* @__PURE__ */ React.createElement("a", { href: group.urls.studio_join_requests, className: "text-sm font-semibold text-sky-200" }, "Open queue") : null), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, pendingJoinRequests.map((item) => /* @__PURE__ */ React.createElement("div", { key: item.id, className: "rounded-2xl border border-white/10 bg-white/[0.03] px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "font-semibold text-white" }, item.user?.name || item.user?.username), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-sm text-slate-400" }, item.desired_role_label || item.desired_role || "Contributor", " • ", item.created_at ? new Date(item.created_at).toLocaleDateString() : "New"))))) : null), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Members"), /* @__PURE__ */ React.createElement("a", { href: group?.urls?.studio_members, className: "text-sm font-semibold text-sky-200" }, "Manage")), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-2 sm:grid-cols-2" }, Object.entries(roleSummary).map(([role, count]) => /* @__PURE__ */ React.createElement("div", { key: role, className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, role), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xl font-semibold text-white" }, Number(count))))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, members.slice(0, 6).map((member) => /* @__PURE__ */ React.createElement("div", { key: member.id, className: "flex items-center gap-3 rounded-2xl border border-white/10 bg-black/20 px-4 py-3" }, member.user?.avatar_url ? /* @__PURE__ */ React.createElement("img", { src: member.user.avatar_url, alt: member.user.name || member.user.username, className: "h-11 w-11 rounded-2xl object-cover" }) : /* @__PURE__ */ React.createElement("div", { className: "flex h-11 w-11 items-center justify-center rounded-2xl border border-white/10 bg-white/[0.03] text-slate-400" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-user" })), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React.createElement("div", { className: "truncate font-semibold text-white" }, member.user?.name || member.user?.username), /* @__PURE__ */ React.createElement("div", { className: "text-xs uppercase tracking-[0.16em] text-slate-400" }, member.role))))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Recruitment"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-lg font-semibold text-white" }, recruitment?.is_recruiting ? recruitment.headline || "Recruiting is active" : "Recruitment is off"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-slate-400" }, recruitment?.description || "Set open roles, skills, and contact instructions from the recruitment page.")))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-6 xl:grid-cols-2" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Releases"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "Track featured drops and current release pipelines.")), group?.urls?.studio_releases ? /* @__PURE__ */ React.createElement("a", { href: group.urls.studio_releases, className: "text-sm font-semibold text-sky-200" }, "Manage") : null), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 md:grid-cols-2" }, recentReleases.length > 0 ? recentReleases.map((release) => /* @__PURE__ */ React.createElement(ContentCard, { key: release.id, item: release, fallbackLabel: "Release" })) : /* @__PURE__ */ React.createElement(EmptyCard, { title: "No releases yet", description: "Create a release to track milestones, contributors, and publication status." }))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Projects"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "Recent structured releases and collaboration hubs.")), group?.urls?.studio_projects ? /* @__PURE__ */ React.createElement("a", { href: group.urls.studio_projects, className: "text-sm font-semibold text-sky-200" }, "Manage") : null), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 md:grid-cols-2" }, recentProjects.length > 0 ? recentProjects.map((project) => /* @__PURE__ */ React.createElement(ContentCard, { key: project.id, item: project, fallbackLabel: "Project" })) : /* @__PURE__ */ React.createElement(EmptyCard, { title: "No projects yet", description: "Create a project to bundle shared assets, linked artworks, and a release state." }))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Challenges"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "Current creative prompts and challenge arcs.")), group?.urls?.studio_challenges ? /* @__PURE__ */ React.createElement("a", { href: group.urls.studio_challenges, className: "text-sm font-semibold text-sky-200" }, "Manage") : null), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 md:grid-cols-2" }, recentChallenges.length > 0 ? recentChallenges.map((challenge) => /* @__PURE__ */ React.createElement(ContentCard, { key: challenge.id, item: challenge, fallbackLabel: "Challenge" })) : /* @__PURE__ */ React.createElement(EmptyCard, { title: "No challenges yet", description: "Launch a challenge to keep the group active between major releases." })))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-6 xl:grid-cols-2" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Trust summary"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "Public-facing trust labels and internal contributor health snapshot.")), group?.urls?.studio_reputation ? /* @__PURE__ */ React.createElement("a", { href: group.urls.studio_reputation, className: "text-sm font-semibold text-sky-200" }, "Open dashboard") : null), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2" }, trustSignals.map((signal) => /* @__PURE__ */ React.createElement("span", { key: signal.key, className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-2 text-sm font-semibold text-white" }, signal.label))), /* @__PURE__ */ React.createElement("div", { className: "mt-5 grid gap-3 md:grid-cols-2" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-4 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Contributors"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-2xl font-semibold text-white" }, Number(reputationSummary?.counts?.contributors || 0))), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-4 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Group badges"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-2xl font-semibold text-white" }, Number(reputationSummary?.counts?.group_badges || 0))))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Contributor highlights"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "Recent high-trust contributors and badge unlocks."))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, Array.isArray(reputationSummary?.top_contributors) && reputationSummary.top_contributors.length > 0 ? reputationSummary.top_contributors.slice(0, 4).map((entry) => /* @__PURE__ */ React.createElement("div", { key: entry.user?.id, className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "font-semibold text-white" }, entry.user?.name || entry.user?.username), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-sm text-slate-400" }, entry.summary || "Contributor"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-xs text-slate-500" }, entry.counts?.releases || 0, " releases • ", entry.counts?.credited_artworks || 0, " artworks"))) : /* @__PURE__ */ React.createElement(EmptyCard, { title: "No contributor signals yet", description: "Release and milestone activity will populate contributor reputation here." })))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-6 xl:grid-cols-2" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Recent artworks"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "Latest published work released under this group identity.")), /* @__PURE__ */ React.createElement("a", { href: group?.urls?.studio_artworks, className: "text-sm font-semibold text-sky-200" }, "View all")), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 md:grid-cols-2" }, recentArtworks.length > 0 ? recentArtworks.map((artwork) => /* @__PURE__ */ React.createElement(ContentCard, { key: artwork.id, item: artwork, fallbackLabel: "Published" })) : /* @__PURE__ */ React.createElement(EmptyCard, { title: "No published artworks yet", description: "Publish the first group artwork to start building this feed." }))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Events"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "Upcoming or recently updated moments on the group timeline.")), group?.urls?.studio_events ? /* @__PURE__ */ React.createElement("a", { href: group.urls.studio_events, className: "text-sm font-semibold text-sky-200" }, "Manage") : null), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 md:grid-cols-2" }, recentEvents.length > 0 ? recentEvents.map((event) => /* @__PURE__ */ React.createElement(ContentCard, { key: event.id, item: event, fallbackLabel: "Event" })) : /* @__PURE__ */ React.createElement(EmptyCard, { title: "No events yet", description: "Schedule a launch, stream, or milestone to start the group timeline." })))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-6 xl:grid-cols-2" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Recent collections"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "Collections most recently updated in this group workspace.")), /* @__PURE__ */ React.createElement("a", { href: group?.urls?.studio_collections, className: "text-sm font-semibold text-sky-200" }, "View all")), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 md:grid-cols-2" }, recentCollections.length > 0 ? recentCollections.map((collection) => /* @__PURE__ */ React.createElement(ContentCard, { key: collection.id, item: collection, fallbackLabel: "Collection" })) : /* @__PURE__ */ React.createElement(EmptyCard, { title: "No collections yet", description: "Create a collection to organize group work into campaigns, series, or themed sets." }))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Activity feed"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "Pinned and recent internal or public timeline items.")), group?.urls?.studio_activity ? /* @__PURE__ */ React.createElement("a", { href: group.urls.studio_activity, className: "text-sm font-semibold text-sky-200" }, "Open feed") : null), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, recentActivity.length > 0 ? recentActivity.map((item) => /* @__PURE__ */ React.createElement(ActivityCard, { key: item.id, item })) : /* @__PURE__ */ React.createElement(EmptyCard, { title: "No activity items yet", description: "Publishing projects, events, posts, and member milestones will populate this feed." })))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-6 xl:grid-cols-2" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Review queue"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "Latest artwork submissions waiting for moderation.")), group?.urls?.studio_review ? /* @__PURE__ */ React.createElement("a", { href: group.urls.studio_review, className: "text-sm font-semibold text-sky-200" }, "Open queue") : null), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 md:grid-cols-2" }, reviewQueuePreview.length > 0 ? reviewQueuePreview.map((item) => /* @__PURE__ */ React.createElement("a", { key: item.id, href: item.urls?.edit, className: "rounded-[24px] border border-white/10 bg-black/20 p-4 transition hover:border-white/20" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold text-white" }, item.title), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs uppercase tracking-[0.16em] text-slate-500" }, item.group_review_status))) : /* @__PURE__ */ React.createElement(EmptyCard, { title: "No pending reviews", description: "Contributor submissions will appear here when they are sent for review." }))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Recent posts"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "Announcements and updates published from the group.")), group?.urls?.studio_posts ? /* @__PURE__ */ React.createElement("a", { href: group.urls.studio_posts, className: "text-sm font-semibold text-sky-200" }, "Manage posts") : null), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 md:grid-cols-2" }, recentPosts.length > 0 ? recentPosts.map((post2) => /* @__PURE__ */ React.createElement("a", { key: post2.id, href: post2.url, className: "rounded-[24px] border border-white/10 bg-black/20 p-4 transition hover:border-white/20" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, post2.type), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-base font-semibold text-white" }, post2.title), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-slate-400" }, post2.excerpt || "Open post"))) : /* @__PURE__ */ React.createElement(EmptyCard, { title: "No posts yet", description: "Create the first group announcement to add a public news feed." })))), /* @__PURE__ */ React.createElement("section", { className: "mt-6 rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Recent history"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 md:grid-cols-2 xl:grid-cols-3" }, recentHistory.length > 0 ? recentHistory.map((item) => /* @__PURE__ */ React.createElement("div", { key: item.id, className: "rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold text-white" }, item.summary || item.action_type), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-xs text-slate-400" }, item.actor?.name || item.actor?.username || "System", " • ", item.created_at ? new Date(item.created_at).toLocaleString() : "Recently"))) : /* @__PURE__ */ React.createElement(EmptyCard, { title: "No history yet", description: "Audit events will appear here as members review requests, posts, and submissions." })))); } -const __vite_glob_0_131 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_136 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupDashboard }, Symbol.toStringTag, { value: "Module" })); @@ -105632,7 +106429,7 @@ function StudioGroupEventEditor() { form.post(props.publishUrl, { preserveScroll: true }); }, className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("button", { type: "submit", className: "rounded-full border border-white/10 bg-white/[0.05] px-5 py-2.5 text-sm font-semibold text-white" }, "Publish event")) : null)); } -const __vite_glob_0_132 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_137 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupEventEditor }, Symbol.toStringTag, { value: "Module" })); @@ -105641,7 +106438,7 @@ function StudioGroupEvents() { const items = Array.isArray(props.listing?.items) ? props.listing.items : []; return /* @__PURE__ */ React.createElement(StudioLayout, { title: props.title, subtitle: props.description }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm text-slate-400" }, "Events let the group announce launches, sessions, milestones, and time-based updates."), props.createUrl ? /* @__PURE__ */ React.createElement("a", { href: props.createUrl, className: "rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white" }, "Create event") : null), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 lg:grid-cols-2" }, items.length > 0 ? items.map((event) => /* @__PURE__ */ React.createElement("a", { key: event.id, href: event.urls?.edit || event.url, className: "rounded-[24px] border border-white/10 bg-white/[0.03] p-5 transition hover:border-white/20" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, event.title), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-300" }, event.status)), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-6 text-slate-400" }, event.summary || "Event page"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 text-xs text-slate-500" }, event.start_at ? new Date(event.start_at).toLocaleString() : "Unscheduled", " • ", event.event_type))) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-dashed border-white/10 bg-white/[0.02] p-6 text-sm text-slate-400" }, "No events yet."))); } -const __vite_glob_0_133 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_138 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupEvents }, Symbol.toStringTag, { value: "Module" })); @@ -105668,7 +106465,7 @@ function StudioGroupInvitations() { ); return /* @__PURE__ */ React.createElement(StudioLayout, { title: props.title, subtitle: props.description }, /* @__PURE__ */ React.createElement("section", { className: "mb-6 rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-start justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-sky-200/75" }, "Group invitations"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-xl font-semibold text-white" }, "Invite collaborators into ", props.studioGroup?.name), /* @__PURE__ */ React.createElement("p", { className: "mt-2 max-w-2xl text-sm leading-6 text-slate-300" }, "Pending invites stay separate from active members here, so owners and admins can review who was invited, when the invite expires, and revoke access before acceptance.")), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement(xe, { href: props.studioGroup?.urls?.studio_members, className: "rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white" }, "Members"), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-sky-300/20 bg-sky-300/10 px-4 py-2 text-sm font-semibold text-sky-100" }, pendingInvites.length, " pending"))), /* @__PURE__ */ React.createElement("div", { className: "mt-5 grid gap-3 md:grid-cols-[1.1fr_0.8fr_1fr_0.7fr_auto]" }, /* @__PURE__ */ React.createElement("input", { value: invite.username, onChange: (event) => setInvite((current) => ({ ...current, username: event.target.value })), placeholder: "Username", className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" }), /* @__PURE__ */ React.createElement(NovaSelect, { value: invite.role, onChange: (val) => setInvite((current) => ({ ...current, role: val })), searchable: false, options: [{ value: "contributor", label: "Contributor" }, { value: "editor", label: "Editor" }, { value: "admin", label: "Admin" }] }), /* @__PURE__ */ React.createElement("input", { value: invite.note, onChange: (event) => setInvite((current) => ({ ...current, note: event.target.value })), placeholder: "Optional note", className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" }), /* @__PURE__ */ React.createElement("input", { value: invite.expires_in_days, onChange: (event) => setInvite((current) => ({ ...current, expires_in_days: event.target.value })), type: "number", min: "1", max: "30", className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" }), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => At.post(props.endpoints?.invite, { ...invite, expires_in_days: Number(invite.expires_in_days || 7) || 7 }), className: "rounded-full border border-sky-300/20 bg-sky-300/10 px-4 py-2 text-sm font-semibold text-sky-100" }, "Send invite"))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-6 xl:grid-cols-[minmax(0,1.15fr)_minmax(0,0.85fr)]" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Pending invitations"), /* @__PURE__ */ React.createElement("span", { className: "text-sm text-slate-400" }, pendingInvites.length, " outstanding")), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, pendingInvites.length > 0 ? pendingInvites.map((inviteRow) => /* @__PURE__ */ React.createElement("article", { key: inviteRow.id, className: "rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-4 md:flex-row md:items-center" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3" }, inviteRow.user?.avatar_url ? /* @__PURE__ */ React.createElement("img", { src: inviteRow.user.avatar_url, alt: inviteRow.user.name || inviteRow.user.username, className: "h-12 w-12 rounded-2xl object-cover" }) : /* @__PURE__ */ React.createElement("div", { className: "flex h-12 w-12 items-center justify-center rounded-2xl border border-white/10 bg-white/[0.03] text-slate-400" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-user" })), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "font-semibold text-white" }, inviteRow.user?.name || inviteRow.user?.username), /* @__PURE__ */ React.createElement("div", { className: "text-xs uppercase tracking-[0.16em] text-slate-400" }, inviteRow.role_label || inviteRow.role))), /* @__PURE__ */ React.createElement("div", { className: "md:ml-auto flex flex-wrap items-center gap-3 text-xs text-slate-400" }, inviteRow.invited_by ? /* @__PURE__ */ React.createElement("span", null, "Invited by ", inviteRow.invited_by.name || inviteRow.invited_by.username) : null, inviteRow.invited_at ? /* @__PURE__ */ React.createElement("span", null, "Sent ", formatInviteTimestamp(inviteRow.invited_at)) : null, inviteRow.expires_at ? /* @__PURE__ */ React.createElement("span", null, "Expires ", formatInviteTimestamp(inviteRow.expires_at)) : null)), inviteRow.note ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm text-slate-300" }, inviteRow.note) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2" }, inviteRow.can_revoke && inviteRow.revoke_url ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => At.delete(inviteRow.revoke_url), className: "rounded-full border border-rose-300/20 bg-rose-400/10 px-3 py-2 text-sm font-semibold text-rose-100" }, "Revoke invite") : null))) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-dashed border-white/10 px-6 py-12 text-center text-slate-400" }, "No pending invites for this group."))), /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Recent invite history"), /* @__PURE__ */ React.createElement("span", { className: "text-sm text-slate-400" }, revokedInvites.length, " revoked or expired")), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, revokedInvites.length > 0 ? revokedInvites.map((inviteRow) => /* @__PURE__ */ React.createElement("article", { key: inviteRow.id, className: "rounded-2xl border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "font-semibold text-white" }, inviteRow.user?.name || inviteRow.user?.username), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs uppercase tracking-[0.16em] text-slate-400" }, inviteRow.is_expired ? "Expired" : "Revoked", " • ", inviteRow.role_label || inviteRow.role), inviteRow.invited_at ? /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-slate-400" }, "Originally sent ", formatInviteTimestamp(inviteRow.invited_at)) : null)) : /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-dashed border-white/10 px-4 py-8 text-center text-slate-400" }, "No recent invite history yet."))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Active members"), /* @__PURE__ */ React.createElement("span", { className: "text-sm text-slate-400" }, activeMembers.length, " active")), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, activeMembers.slice(0, 6).map((member) => /* @__PURE__ */ React.createElement("div", { key: member.id, className: "flex items-center gap-3 rounded-2xl border border-white/10 bg-black/20 px-4 py-3" }, member.user?.avatar_url ? /* @__PURE__ */ React.createElement("img", { src: member.user.avatar_url, alt: member.user.name || member.user.username, className: "h-11 w-11 rounded-2xl object-cover" }) : /* @__PURE__ */ React.createElement("div", { className: "flex h-11 w-11 items-center justify-center rounded-2xl border border-white/10 bg-white/[0.03] text-slate-400" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-user" })), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React.createElement("div", { className: "truncate font-semibold text-white" }, member.user?.name || member.user?.username), /* @__PURE__ */ React.createElement("div", { className: "text-xs uppercase tracking-[0.16em] text-slate-400" }, member.role_label || member.role))))))))); } -const __vite_glob_0_134 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_139 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupInvitations }, Symbol.toStringTag, { value: "Module" })); @@ -105697,7 +106494,7 @@ function routeUrl(baseUrl, id, action) { if (!baseUrl) return ""; return `${String(baseUrl).replace(/\/$/, "")}/${id}/${action}`; } -const __vite_glob_0_135 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_140 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupJoinRequests }, Symbol.toStringTag, { value: "Module" })); @@ -105764,7 +106561,7 @@ function StudioGroupMembers() { return /* @__PURE__ */ React.createElement("div", { key: option.value, className: "rounded-2xl border border-white/10 bg-white/[0.03] p-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold text-white" }, option.label), /* @__PURE__ */ React.createElement("div", { className: "mt-3 flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => setPermissionState(member.id, option.value, "inherit"), className: `rounded-full border px-3 py-1.5 text-xs font-semibold ${current === "inherit" ? "border-white/20 bg-white/[0.08] text-white" : "border-white/10 bg-transparent text-slate-300"}` }, "Inherit"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => setPermissionState(member.id, option.value, "allow"), className: `rounded-full border px-3 py-1.5 text-xs font-semibold ${current === "allow" ? "border-emerald-300/20 bg-emerald-400/10 text-emerald-100" : "border-white/10 bg-transparent text-slate-300"}` }, "Allow"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => setPermissionState(member.id, option.value, "deny"), className: `rounded-full border px-3 py-1.5 text-xs font-semibold ${current === "deny" ? "border-rose-300/20 bg-rose-400/10 text-rose-100" : "border-white/10 bg-transparent text-slate-300"}` }, "Deny"))); }))) : null)), filteredMembers.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "px-4 py-8 text-sm text-slate-400" }, "No members match the current search.") : null)))); } -const __vite_glob_0_136 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_141 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupMembers }, Symbol.toStringTag, { value: "Module" })); @@ -105788,7 +106585,7 @@ function StudioGroupPostEditor() { }; return /* @__PURE__ */ React.createElement(StudioLayout, { title: props.title, subtitle: props.description }, /* @__PURE__ */ React.createElement("form", { onSubmit: submit, className: "grid gap-6 xl:grid-cols-[minmax(0,1.1fr)_minmax(0,0.9fr)]" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-4" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Type"), /* @__PURE__ */ React.createElement(NovaSelect, { value: form.data.type, onChange: (val) => form.setData("type", val), options: Array.isArray(props.typeOptions) ? props.typeOptions : [], searchable: false })), /* @__PURE__ */ React.createElement("label", { className: "grid gap-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Title"), /* @__PURE__ */ React.createElement("input", { value: form.data.title, onChange: (event) => form.setData("title", event.target.value), className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement("label", { className: "grid gap-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Excerpt"), /* @__PURE__ */ React.createElement("textarea", { value: form.data.excerpt, onChange: (event) => form.setData("excerpt", event.target.value), rows: 3, className: "rounded-[24px] border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement("label", { className: "grid gap-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Content"), /* @__PURE__ */ React.createElement("textarea", { value: form.data.content, onChange: (event) => form.setData("content", event.target.value), rows: 12, className: "rounded-[24px] border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" })))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Post controls"), /* @__PURE__ */ React.createElement("div", { className: "mt-5 space-y-3" }, /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: form.processing, className: "w-full rounded-full border border-sky-300/20 bg-sky-300/10 px-4 py-3 text-sm font-semibold text-sky-100 disabled:opacity-60" }, "Save"), props.publishUrl ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => At.post(props.publishUrl), className: "w-full rounded-full border border-emerald-300/20 bg-emerald-400/10 px-4 py-3 text-sm font-semibold text-emerald-100" }, "Publish") : null, props.pinUrl ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => At.post(props.pinUrl), className: "w-full rounded-full border border-amber-300/20 bg-amber-400/10 px-4 py-3 text-sm font-semibold text-amber-100" }, "Toggle pinned") : null, props.archiveUrl ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => At.post(props.archiveUrl), className: "w-full rounded-full border border-rose-300/20 bg-rose-400/10 px-4 py-3 text-sm font-semibold text-rose-100" }, "Archive") : null)))); } -const __vite_glob_0_137 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_142 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupPostEditor }, Symbol.toStringTag, { value: "Module" })); @@ -105797,7 +106594,7 @@ function StudioGroupPosts() { const items = Array.isArray(props.listing?.items) ? props.listing.items : []; return /* @__PURE__ */ React.createElement(StudioLayout, { title: props.title, subtitle: props.description }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Post library"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "Draft, publish, pin, and archive public group posts.")), props.createUrl ? /* @__PURE__ */ React.createElement("a", { href: props.createUrl, className: "rounded-full border border-sky-300/20 bg-sky-300/10 px-4 py-2 text-sm font-semibold text-sky-100" }, "New post") : null), /* @__PURE__ */ React.createElement("div", { className: "mt-5 grid gap-4 md:grid-cols-2" }, items.length > 0 ? items.map((item) => /* @__PURE__ */ React.createElement("article", { key: item.id, className: "rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, item.type), /* @__PURE__ */ React.createElement("h3", { className: "mt-2 text-lg font-semibold text-white" }, item.title)), /* @__PURE__ */ React.createElement("div", { className: "flex flex-col items-end gap-2" }, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold uppercase tracking-[0.16em] text-slate-300" }, item.status), item.is_pinned ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-amber-300/20 bg-amber-400/10 px-3 py-1 text-xs font-semibold uppercase tracking-[0.16em] text-amber-100" }, "Pinned") : null)), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-6 text-slate-300" }, item.excerpt || item.content || "No excerpt yet."), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement("a", { href: item.urls?.edit, className: "rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white" }, "Edit"), item.urls?.public ? /* @__PURE__ */ React.createElement("a", { href: item.urls.public, className: "rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white" }, "View") : null))) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-dashed border-white/10 bg-white/[0.02] p-5 text-sm text-slate-400" }, "No posts yet.")))); } -const __vite_glob_0_138 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_143 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupPosts }, Symbol.toStringTag, { value: "Module" })); @@ -105846,7 +106643,7 @@ function StudioGroupProjectEditor() { milestoneForm.post(props.storeMilestoneUrl, { preserveScroll: true, onSuccess: () => milestoneForm.reset("title", "summary", "due_date", "owner_user_id", "notes") }); }, className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Milestones"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, /* @__PURE__ */ React.createElement("input", { value: milestoneForm.data.title, onChange: (event) => milestoneForm.setData("title", event.target.value), placeholder: "Milestone title", className: "w-full rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" }), /* @__PURE__ */ React.createElement("textarea", { value: milestoneForm.data.summary, onChange: (event) => milestoneForm.setData("summary", event.target.value), placeholder: "Summary", rows: 3, className: "w-full rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" }), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: milestoneForm.data.status, onChange: (val) => milestoneForm.setData("status", val), searchable: false, options: ["pending", "active", "blocked", "completed", "cancelled"].map((s2) => ({ value: s2, label: s2 })) }), /* @__PURE__ */ React.createElement(DateTimePicker, { value: milestoneForm.data.due_date, onChange: (nextValue) => milestoneForm.setData("due_date", nextValue), mode: "date", placeholder: "Due date", clearable: true, className: "bg-black/20" })), /* @__PURE__ */ React.createElement(NovaSelect, { value: String(milestoneForm.data.owner_user_id || ""), onChange: (val) => milestoneForm.setData("owner_user_id", val), placeholder: "No owner", options: (props.memberOptions || []).map((o) => ({ value: String(o.id), label: o.name || o.username })) }), /* @__PURE__ */ React.createElement("textarea", { value: milestoneForm.data.notes, onChange: (event) => milestoneForm.setData("notes", event.target.value), placeholder: "Notes", rows: 3, className: "w-full rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" }), /* @__PURE__ */ React.createElement("button", { type: "submit", className: "rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-sm font-semibold text-white" }, "Add milestone")), Array.isArray(project?.milestones) && project.milestones.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "mt-6 space-y-3" }, project.milestones.map((milestone) => /* @__PURE__ */ React.createElement("div", { key: milestone.id, className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "font-semibold text-white" }, milestone.title), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs text-slate-500" }, milestone.owner?.name || milestone.owner?.username || "No owner", milestone.due_date ? ` • due ${milestone.due_date}` : "")), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => At.patch(props.updateMilestonePattern.replace("__MILESTONE__", String(milestone.id)), { title: milestone.title, summary: milestone.summary || "", status: milestone.status === "completed" ? "active" : "completed", due_date: milestone.due_date || "", owner_user_id: milestone.owner?.id || "", notes: milestone.notes || "" }, { preserveScroll: true }), className: "rounded-full border border-white/10 bg-white/[0.05] px-3 py-1 text-xs font-semibold text-white" }, "Mark ", milestone.status === "completed" ? "active" : "complete")), milestone.summary ? /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-slate-400" }, milestone.summary) : null))) : null) : null))); } -const __vite_glob_0_139 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_144 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupProjectEditor }, Symbol.toStringTag, { value: "Module" })); @@ -105856,7 +106653,7 @@ function StudioGroupProjects() { const items = Array.isArray(listing.items) ? listing.items : []; return /* @__PURE__ */ React.createElement(StudioLayout, { title: props.title, subtitle: props.description }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm text-slate-400" }, "Projects give the group a structured place for releases, teams, and linked outputs."), props.createUrl ? /* @__PURE__ */ React.createElement("a", { href: props.createUrl, className: "rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white" }, "Create project") : null), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 lg:grid-cols-2" }, items.length > 0 ? items.map((project) => /* @__PURE__ */ React.createElement("a", { key: project.id, href: project.urls?.edit || project.url, className: "rounded-[24px] border border-white/10 bg-white/[0.03] p-5 transition hover:border-white/20" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, project.title), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-300" }, project.status)), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-6 text-slate-400" }, project.summary || "Project page"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 text-xs text-slate-500" }, project.counts?.artworks || 0, " artworks • ", project.counts?.assets || 0, " assets • ", project.counts?.team || 0, " team • ", project.counts?.milestones || 0, " milestones • ", project.counts?.releases || 0, " releases"))) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-dashed border-white/10 bg-white/[0.02] p-6 text-sm text-slate-400" }, "No projects yet."))); } -const __vite_glob_0_140 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_145 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupProjects }, Symbol.toStringTag, { value: "Module" })); @@ -105887,7 +106684,7 @@ function StudioGroupRecruitment() { return /* @__PURE__ */ React.createElement("button", { key: option.value, type: "button", onClick: () => form.setData("skills_json", toggleItem(form.data.skills_json, option.value)), className: `rounded-full border px-3 py-1.5 text-xs font-semibold ${selected ? "border-sky-300/20 bg-sky-300/10 text-sky-100" : "border-white/10 bg-white/[0.03] text-slate-300"}` }, option.label); }))))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Application settings"), /* @__PURE__ */ React.createElement("div", { className: "mt-5 grid gap-4" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Contact mode"), /* @__PURE__ */ React.createElement(NovaSelect, { value: form.data.contact_mode, onChange: (val) => form.setData("contact_mode", val), options: Array.isArray(props.contactModes) ? props.contactModes : [], searchable: false })), /* @__PURE__ */ React.createElement("div", { className: "grid gap-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Visibility"), /* @__PURE__ */ React.createElement(NovaSelect, { value: form.data.visibility, onChange: (val) => form.setData("visibility", val), options: Array.isArray(props.visibilityOptions) ? props.visibilityOptions : [], searchable: false })), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 p-4 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("p", { className: "font-semibold text-white" }, "Public preview"), /* @__PURE__ */ React.createElement("p", { className: "mt-2" }, form.data.headline || "No headline yet."), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-slate-400" }, form.data.description || "Recruitment copy will show here once you add it."), form.data.roles_json.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "mt-3 flex flex-wrap gap-2" }, form.data.roles_json.map((role) => /* @__PURE__ */ React.createElement("span", { key: role, className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold text-white" }, role))) : null), /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: form.processing, className: "rounded-full border border-sky-300/20 bg-sky-300/10 px-4 py-3 text-sm font-semibold text-sky-100 disabled:opacity-60" }, "Save recruitment profile"))))); } -const __vite_glob_0_141 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_146 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupRecruitment }, Symbol.toStringTag, { value: "Module" })); @@ -105940,7 +106737,7 @@ function StudioGroupReleaseEditor() { milestoneForm.post(props.storeMilestoneUrl, { preserveScroll: true, onSuccess: () => milestoneForm.reset("title", "summary", "due_date", "owner_user_id", "notes") }); }, className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Milestones"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, /* @__PURE__ */ React.createElement("input", { value: milestoneForm.data.title, onChange: (event) => milestoneForm.setData("title", event.target.value), placeholder: "Milestone title", className: "w-full rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" }), /* @__PURE__ */ React.createElement("textarea", { value: milestoneForm.data.summary, onChange: (event) => milestoneForm.setData("summary", event.target.value), placeholder: "Summary", rows: 3, className: "w-full rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" }), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 md:grid-cols-2" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: milestoneForm.data.status, onChange: (val) => milestoneForm.setData("status", val), searchable: false, options: ["pending", "active", "blocked", "completed", "cancelled"].map((s2) => ({ value: s2, label: s2 })) }), /* @__PURE__ */ React.createElement(DateTimePicker, { value: milestoneForm.data.due_date, onChange: (nextValue) => milestoneForm.setData("due_date", nextValue), mode: "date", placeholder: "Due date", clearable: true, className: "bg-black/20" })), /* @__PURE__ */ React.createElement(NovaSelect, { value: String(milestoneForm.data.owner_user_id || ""), onChange: (val) => milestoneForm.setData("owner_user_id", val), placeholder: "No owner", options: (props.memberOptions || []).map((o) => ({ value: String(o.id), label: o.name || o.username })) }), /* @__PURE__ */ React.createElement("textarea", { value: milestoneForm.data.notes, onChange: (event) => milestoneForm.setData("notes", event.target.value), placeholder: "Notes", rows: 3, className: "w-full rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" }), /* @__PURE__ */ React.createElement("button", { type: "submit", className: "rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-sm font-semibold text-white" }, "Add milestone")), Array.isArray(release?.milestones) && release.milestones.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "mt-6 space-y-3" }, release.milestones.map((milestone) => /* @__PURE__ */ React.createElement("div", { key: milestone.id, className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "font-semibold text-white" }, milestone.title), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs text-slate-500" }, milestone.owner?.name || milestone.owner?.username || "No owner", milestone.due_date ? ` • due ${milestone.due_date}` : "")), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => At.patch(props.updateMilestonePattern.replace("__MILESTONE__", String(milestone.id)), { title: milestone.title, summary: milestone.summary || "", status: milestone.status === "completed" ? "active" : "completed", due_date: milestone.due_date || "", owner_user_id: milestone.owner?.id || "", notes: milestone.notes || "" }, { preserveScroll: true }), className: "rounded-full border border-white/10 bg-white/[0.05] px-3 py-1 text-xs font-semibold text-white" }, "Mark ", milestone.status === "completed" ? "active" : "complete")), milestone.summary ? /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-slate-400" }, milestone.summary) : null))) : null) : null))); } -const __vite_glob_0_142 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_147 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupReleaseEditor }, Symbol.toStringTag, { value: "Module" })); @@ -105952,7 +106749,7 @@ function StudioGroupReleases() { const currentBucket = listing.filters?.bucket || "all"; return /* @__PURE__ */ React.createElement(StudioLayout, { title: props.title, subtitle: props.description }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm text-slate-400" }, "Track the release pipeline from draft through public launch, with milestones and contributor credits."), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ React.createElement(NovaSelect, { value: currentBucket, onChange: (val) => At.get(window.location.pathname, { bucket: val }, { preserveScroll: true, preserveState: true }), options: bucketOptions, searchable: false }), props.createUrl ? /* @__PURE__ */ React.createElement("a", { href: props.createUrl, className: "rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white" }, "Create release") : null)), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-4 lg:grid-cols-2" }, items.length > 0 ? items.map((release) => /* @__PURE__ */ React.createElement("div", { key: release.id, className: "overflow-hidden rounded-[24px] border border-white/10 bg-white/[0.03]" }, release.cover_url ? /* @__PURE__ */ React.createElement("img", { src: release.cover_url, alt: release.title, className: "aspect-[4/3] w-full object-cover" }) : /* @__PURE__ */ React.createElement("div", { className: "flex aspect-[4/3] items-center justify-center bg-white/[0.03] text-slate-500" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-rocket text-2xl" })), /* @__PURE__ */ React.createElement("div", { className: "p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2" }, /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-300" }, release.status), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-300" }, release.current_stage), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-300" }, release.visibility)), /* @__PURE__ */ React.createElement("h2", { className: "mt-3 text-xl font-semibold text-white" }, release.title), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm leading-6 text-slate-400" }, release.summary || "Release page"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 text-xs text-slate-500" }, release.counts?.artworks || 0, " artworks • ", release.counts?.contributors || 0, " contributors • ", release.counts?.milestones || 0, " milestones"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement("a", { href: release.urls?.edit || release.url, className: "rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white" }, "Manage"), release.urls?.public ? /* @__PURE__ */ React.createElement("a", { href: release.urls.public, className: "rounded-full border border-white/10 bg-black/20 px-4 py-2 text-sm font-semibold text-white" }, "View public") : null)))) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-dashed border-white/10 bg-white/[0.02] p-6 text-sm text-slate-400" }, "No releases yet."))); } -const __vite_glob_0_143 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_148 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupReleases }, Symbol.toStringTag, { value: "Module" })); @@ -105969,7 +106766,7 @@ function StudioGroupReputation() { const memberBadgeUnlocks = Array.isArray(reputation.member_badge_unlocks) ? reputation.member_badge_unlocks : []; return /* @__PURE__ */ React.createElement(StudioLayout, { title: props.title, subtitle: props.description }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 md:grid-cols-2 xl:grid-cols-5" }, /* @__PURE__ */ React.createElement(MetricCard, { label: "Freshness", value: metrics.freshness_score }), /* @__PURE__ */ React.createElement(MetricCard, { label: "Activity", value: metrics.activity_score }), /* @__PURE__ */ React.createElement(MetricCard, { label: "Release", value: metrics.release_score }), /* @__PURE__ */ React.createElement(MetricCard, { label: "Trust", value: metrics.trust_score }), /* @__PURE__ */ React.createElement(MetricCard, { label: "Collaboration", value: metrics.collaboration_score })), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-6 xl:grid-cols-[minmax(0,0.9fr)_minmax(0,1.1fr)]" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Trust signals"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "Public-safe labels that shape discovery and confidence."))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2" }, trustSignals.map((signal) => /* @__PURE__ */ React.createElement("span", { key: signal.key, className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-2 text-sm font-semibold text-white" }, signal.label))), /* @__PURE__ */ React.createElement("div", { className: "mt-5 grid gap-3 md:grid-cols-2" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Contributors"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-2xl font-semibold text-white" }, Number(reputation.counts?.contributors || 0))), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Member badges"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-2xl font-semibold text-white" }, Number(reputation.counts?.member_badges || 0)))), metrics.last_calculated_at ? /* @__PURE__ */ React.createElement("div", { className: "mt-4 text-xs text-slate-500" }, "Last calculated ", new Date(metrics.last_calculated_at).toLocaleString()) : null), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Top contributors"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "Reputation summaries derived from visible collaboration history."))), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, topContributors.length > 0 ? topContributors.map((entry) => /* @__PURE__ */ React.createElement("div", { key: entry.user?.id, className: "rounded-[24px] border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3" }, entry.user?.avatar_url ? /* @__PURE__ */ React.createElement("img", { src: entry.user.avatar_url, alt: entry.user?.name || entry.user?.username, className: "h-11 w-11 rounded-2xl object-cover" }) : /* @__PURE__ */ React.createElement("div", { className: "flex h-11 w-11 items-center justify-center rounded-2xl border border-white/10 bg-white/[0.03] text-slate-400" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-user" })), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2" }, /* @__PURE__ */ React.createElement("div", { className: "truncate font-semibold text-white" }, entry.user?.name || entry.user?.username), entry.trusted_indicator ? /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-emerald-300/20 bg-emerald-300/10 px-2 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-emerald-100" }, "Trusted") : null), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-sm text-slate-400" }, entry.summary || "Contributor"))), /* @__PURE__ */ React.createElement("div", { className: "mt-3 text-xs text-slate-500" }, entry.counts?.releases || 0, " releases • ", entry.counts?.projects || 0, " projects • ", entry.counts?.credited_artworks || 0, " artworks • ", entry.counts?.review_actions || 0, " reviews"), Array.isArray(entry.badges) && entry.badges.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "mt-3 flex flex-wrap gap-2" }, entry.badges.map((badge) => /* @__PURE__ */ React.createElement("span", { key: `${entry.user?.id}-${badge.key}`, className: "rounded-full border border-white/10 bg-white/[0.04] px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-300" }, badge.label))) : null)) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-dashed border-white/10 bg-white/[0.02] p-6 text-sm text-slate-400" }, "No contributor reputation signals yet.")))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-6 xl:grid-cols-2" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Group badges"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, recentBadges.length > 0 ? recentBadges.map((badge) => /* @__PURE__ */ React.createElement("div", { key: badge.key, className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "font-semibold text-white" }, badge.label), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-sm text-slate-400" }, badge.reason))) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-dashed border-white/10 bg-white/[0.02] p-6 text-sm text-slate-400" }, "No group badges awarded yet."))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Recent member badge unlocks"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, memberBadgeUnlocks.length > 0 ? memberBadgeUnlocks.map((entry) => /* @__PURE__ */ React.createElement("div", { key: `${entry.user?.id}-${entry.badge?.key}`, className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "font-semibold text-white" }, entry.user?.name || entry.user?.username), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-sm text-sky-200" }, entry.badge?.label), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-sm text-slate-400" }, entry.badge?.reason))) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-dashed border-white/10 bg-white/[0.02] p-6 text-sm text-slate-400" }, "No member badge unlocks yet."))))); } -const __vite_glob_0_144 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_149 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupReputation }, Symbol.toStringTag, { value: "Module" })); @@ -105986,7 +106783,7 @@ function StudioGroupReviewQueue() { }; return /* @__PURE__ */ React.createElement(StudioLayout, { title: props.title, subtitle: props.description }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-6 xl:grid-cols-[minmax(0,1.2fr)_minmax(0,0.8fr)]" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Submission queue"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "Review artwork drafts before they publish under the group identity.")), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold uppercase tracking-[0.16em] text-slate-300" }, listing.filters?.bucket || "submitted")), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-4" }, items.length > 0 ? items.map((item) => /* @__PURE__ */ React.createElement("article", { key: item.id, className: "rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-start gap-4" }, item.thumb ? /* @__PURE__ */ React.createElement("img", { src: item.thumb, alt: item.title, className: "h-24 w-24 rounded-2xl object-cover" }) : /* @__PURE__ */ React.createElement("div", { className: "flex h-24 w-24 items-center justify-center rounded-2xl border border-white/10 bg-white/[0.03] text-slate-400" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-image" })), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("h3", { className: "text-lg font-semibold text-white" }, item.title), /* @__PURE__ */ React.createElement("span", { className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 text-xs font-semibold uppercase tracking-[0.16em] text-slate-300" }, item.group_review_status)), /* @__PURE__ */ React.createElement("div", { className: "mt-2 flex flex-wrap gap-3 text-xs text-slate-400" }, item.primary_author ? /* @__PURE__ */ React.createElement("span", null, "Author: ", item.primary_author.name || item.primary_author.username) : null, item.uploader ? /* @__PURE__ */ React.createElement("span", null, "Uploader: ", item.uploader.name || item.uploader.username) : null, item.submitted_at ? /* @__PURE__ */ React.createElement("span", null, "Submitted ", new Date(item.submitted_at).toLocaleString()) : null), item.group_review_notes ? /* @__PURE__ */ React.createElement("p", { className: "mt-3 rounded-2xl border border-white/10 bg-white/[0.03] px-3 py-2 text-sm text-slate-300" }, item.group_review_notes) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement("a", { href: item.urls?.edit, className: "rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white" }, "Open draft"), item.can_review ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => sendAction(item, "approve"), className: "rounded-full border border-emerald-300/20 bg-emerald-400/10 px-4 py-2 text-sm font-semibold text-emerald-100" }, "Approve") : null, item.can_review ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => sendAction(item, "needs_changes"), className: "rounded-full border border-amber-300/20 bg-amber-400/10 px-4 py-2 text-sm font-semibold text-amber-100" }, "Needs changes") : null, item.can_review ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => sendAction(item, "reject"), className: "rounded-full border border-rose-300/20 bg-rose-400/10 px-4 py-2 text-sm font-semibold text-rose-100" }, "Reject") : null))))) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-dashed border-white/10 bg-white/[0.02] p-5 text-sm text-slate-400" }, "No submissions in this bucket."))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-white" }, "Recent history"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, (Array.isArray(props.recentHistory) ? props.recentHistory : []).map((item) => /* @__PURE__ */ React.createElement("div", { key: item.id, className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold text-white" }, item.summary || item.action_type), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs text-slate-400" }, item.actor?.name || item.actor?.username || "System", " • ", item.created_at ? new Date(item.created_at).toLocaleString() : "Recently"))))))); } -const __vite_glob_0_145 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_150 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupReviewQueue }, Symbol.toStringTag, { value: "Module" })); @@ -106079,7 +106876,7 @@ function StudioGroupSettings() { }; return /* @__PURE__ */ React.createElement(StudioLayout, { title: props.title, subtitle: props.description }, /* @__PURE__ */ React.createElement("section", { className: "mx-auto max-w-3xl rounded-[30px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-5" }, /* @__PURE__ */ React.createElement("label", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "Name"), /* @__PURE__ */ React.createElement("input", { value: form.name, onChange: (event) => setForm((current) => ({ ...current, name: event.target.value })), className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement("label", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "Slug"), /* @__PURE__ */ React.createElement("input", { value: form.slug, onChange: (event) => setForm((current) => ({ ...current, slug: event.target.value })), className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement("label", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "Short description"), /* @__PURE__ */ React.createElement("input", { value: form.headline, onChange: (event) => setForm((current) => ({ ...current, headline: event.target.value })), className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement("label", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "About"), /* @__PURE__ */ React.createElement("textarea", { value: form.bio, onChange: (event) => setForm((current) => ({ ...current, bio: event.target.value })), rows: 6, className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2" }, /* @__PURE__ */ React.createElement("label", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "Type / category"), /* @__PURE__ */ React.createElement("input", { value: form.type, onChange: (event) => setForm((current) => ({ ...current, type: event.target.value })), className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement("div", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "Founded date"), /* @__PURE__ */ React.createElement(DateTimePicker, { value: form.founded_at, onChange: (nextValue) => setForm((current) => ({ ...current, founded_at: nextValue })), mode: "date", placeholder: "Pick the founding date", clearable: true, className: "bg-black/20" }))), /* @__PURE__ */ React.createElement("label", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "Website"), /* @__PURE__ */ React.createElement("input", { value: form.website_url, onChange: (event) => setForm((current) => ({ ...current, website_url: event.target.value })), className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement("div", { className: "grid gap-5 md:grid-cols-2" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 rounded-[24px] border border-white/10 bg-black/20 p-4 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", { className: "text-sm font-semibold text-white" }, "Avatar / logo"), /* @__PURE__ */ React.createElement("div", { className: "flex h-28 w-28 items-center justify-center overflow-hidden rounded-[24px] border border-white/10 bg-white/[0.04]" }, resolvedAvatarPreview ? /* @__PURE__ */ React.createElement("img", { src: resolvedAvatarPreview, alt: "Avatar preview", className: "h-full w-full object-cover" }) : /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-image text-slate-500" })), /* @__PURE__ */ React.createElement("input", { ref: avatarInputRef, type: "file", accept: "image/png,image/jpeg,image/webp", onChange: handleFileSelected("avatar_file", setAvatarPreview), className: "hidden" }), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => avatarInputRef.current?.click(), className: "rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white" }, "Upload avatar"), form.avatar_file ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => clearSelectedFile("avatar_file", setAvatarPreview, avatarInputRef), className: "rounded-full border border-white/10 bg-transparent px-4 py-2 text-sm font-semibold text-slate-300" }, "Use current path") : null), /* @__PURE__ */ React.createElement("label", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "Or paste an image URL"), /* @__PURE__ */ React.createElement("input", { value: form.avatar_path, onChange: (event) => setForm((current) => ({ ...current, avatar_path: event.target.value })), placeholder: "https://", className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" }))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 rounded-[24px] border border-white/10 bg-black/20 p-4 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", { className: "text-sm font-semibold text-white" }, "Cover image"), /* @__PURE__ */ React.createElement("div", { className: "flex h-28 w-full items-center justify-center overflow-hidden rounded-[24px] border border-white/10 bg-white/[0.04]" }, resolvedBannerPreview ? /* @__PURE__ */ React.createElement("img", { src: resolvedBannerPreview, alt: "Cover preview", className: "h-full w-full object-cover" }) : /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-panorama text-slate-500" })), /* @__PURE__ */ React.createElement("input", { ref: bannerInputRef, type: "file", accept: "image/png,image/jpeg,image/webp", onChange: handleFileSelected("banner_file", setBannerPreview), className: "hidden" }), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => bannerInputRef.current?.click(), className: "rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm font-semibold text-white" }, "Upload cover"), form.banner_file ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => clearSelectedFile("banner_file", setBannerPreview, bannerInputRef), className: "rounded-full border border-white/10 bg-transparent px-4 py-2 text-sm font-semibold text-slate-300" }, "Use current path") : null), /* @__PURE__ */ React.createElement("label", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "Or paste an image URL"), /* @__PURE__ */ React.createElement("input", { value: form.banner_path, onChange: (event) => setForm((current) => ({ ...current, banner_path: event.target.value })), placeholder: "https://", className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" })))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "Featured artwork"), /* @__PURE__ */ React.createElement(NovaSelect, { value: String(form.featured_artwork_id || ""), onChange: (val) => setForm((current) => ({ ...current, featured_artwork_id: val })), placeholder: "Use latest published artwork", options: featuredArtworkOptions.map((item) => ({ value: String(item.id), label: item.title })) })), selectedFeaturedArtwork ? /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 rounded-[20px] border border-white/10 bg-white/[0.04] p-3" }, selectedFeaturedArtwork.thumb ? /* @__PURE__ */ React.createElement("img", { src: selectedFeaturedArtwork.thumb, alt: selectedFeaturedArtwork.title, className: "h-16 w-16 rounded-2xl object-cover" }) : null, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "font-semibold text-white" }, selectedFeaturedArtwork.title), /* @__PURE__ */ React.createElement("div", { className: "text-sm text-slate-400" }, selectedFeaturedArtwork.author || "Group member"))) : /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-400" }, "When this is empty, the public overview falls back to the latest published works automatically.")), /* @__PURE__ */ React.createElement("div", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "Visibility"), /* @__PURE__ */ React.createElement(NovaSelect, { value: form.visibility, onChange: (val) => setForm((current) => ({ ...current, visibility: val })), options: props.visibilityOptions || [], searchable: false })), /* @__PURE__ */ React.createElement("div", { className: "grid gap-2 text-sm text-slate-200" }, /* @__PURE__ */ React.createElement("span", null, "Membership policy"), /* @__PURE__ */ React.createElement(NovaSelect, { value: form.membership_policy, onChange: (val) => setForm((current) => ({ ...current, membership_policy: val })), options: props.membershipPolicyOptions || [], searchable: false })), /* @__PURE__ */ React.createElement("div", { className: "grid gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("span", { className: "text-sm text-slate-200" }, "Links"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: addLink, className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-1.5 text-xs font-semibold text-white" }, "Add link")), form.links_json.map((item, index2) => /* @__PURE__ */ React.createElement("div", { key: `link-${index2}`, className: "grid gap-3 md:grid-cols-[0.8fr_1.2fr_auto]" }, /* @__PURE__ */ React.createElement("input", { value: item.label, onChange: (event) => updateLink(index2, "label", event.target.value), placeholder: "Label", className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" }), /* @__PURE__ */ React.createElement("input", { value: item.url, onChange: (event) => updateLink(index2, "url", event.target.value), placeholder: "https://", className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" }), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => removeLink(index2), className: "rounded-full border border-rose-300/20 bg-rose-400/10 px-4 py-2 text-sm font-semibold text-rose-100" }, "Remove")))), /* @__PURE__ */ React.createElement("div", { className: "flex justify-between gap-3" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: archiveGroup, className: "rounded-full border border-rose-300/20 bg-rose-400/10 px-4 py-2 text-sm font-semibold text-rose-100" }, "Archive group"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: submit, className: "rounded-full border border-sky-300/20 bg-sky-300/10 px-4 py-2 text-sm font-semibold text-sky-100" }, "Save settings"))))); } -const __vite_glob_0_146 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_151 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupSettings }, Symbol.toStringTag, { value: "Module" })); @@ -106107,7 +106904,7 @@ function StudioGroupsIndex() { } ), /* @__PURE__ */ React.createElement("div", { className: "mb-6 flex items-center justify-between gap-3 rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("p", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-sky-200/80" }, "Collective publishing"), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-2xl font-semibold text-white" }, "Launch and manage shared identities")), /* @__PURE__ */ React.createElement(xe, { href: props.endpoints?.create, className: "rounded-full border border-sky-300/20 bg-sky-300/10 px-4 py-2 text-sm font-semibold text-sky-100 transition hover:border-sky-300/35 hover:bg-sky-300/15" }, "Create group")), pendingInvites.length > 0 ? /* @__PURE__ */ React.createElement("section", { className: "mb-6 rounded-[28px] border border-amber-300/20 bg-amber-400/10 p-5" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-amber-50" }, "Pending invites"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 md:grid-cols-2" }, pendingInvites.map((invite) => /* @__PURE__ */ React.createElement("article", { key: invite.id, className: "rounded-2xl border border-white/10 bg-black/20 p-4 text-white" }, /* @__PURE__ */ React.createElement("h3", { className: "text-base font-semibold" }, invite.group?.name), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-amber-50/80" }, "Role: ", invite.role), invite.invited_by ? /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-amber-50/70" }, "Invited by ", invite.invited_by.name || invite.invited_by.username) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex gap-2" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => At.post(invite.accept_url), className: "rounded-full border border-emerald-300/20 bg-emerald-400/10 px-3 py-2 text-sm font-semibold text-emerald-100" }, "Accept"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => At.post(invite.decline_url), className: "rounded-full border border-white/10 bg-white/[0.04] px-3 py-2 text-sm font-semibold text-white" }, "Decline")))))) : null, /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 xl:grid-cols-2" }, groups.length > 0 ? groups.map((group) => /* @__PURE__ */ React.createElement(GroupCard, { key: group.slug, group })) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-dashed border-white/10 px-6 py-16 text-center text-slate-400" }, "No groups yet. Create one to start publishing collaboratively."))); } -const __vite_glob_0_147 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_152 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGroupsIndex }, Symbol.toStringTag, { value: "Module" })); @@ -106208,7 +107005,7 @@ function StudioGrowth() { /* @__PURE__ */ React.createElement("div", { className: "mt-3 grid grid-cols-3 gap-3 text-xs text-slate-400" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", null, "Views"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-sm font-semibold text-white" }, Number(item.metrics?.views || 0).toLocaleString())), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", null, "Reactions"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-sm font-semibold text-white" }, Number(item.metrics?.appreciation || 0).toLocaleString())), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", null, "Comments"), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-sm font-semibold text-white" }, Number(item.metrics?.comments || 0).toLocaleString()))) )))))); } -const __vite_glob_0_148 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_153 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioGrowth }, Symbol.toStringTag, { value: "Module" })); @@ -106268,7 +107065,7 @@ function StudioInbox() { }; return /* @__PURE__ */ React.createElement(StudioLayout, { title: props.title, subtitle: props.description, actions: /* @__PURE__ */ React.createElement("button", { type: "button", onClick: markAllRead, disabled: marking, className: "inline-flex items-center gap-2 rounded-full border border-white/10 px-4 py-2 text-sm text-slate-100 disabled:opacity-50" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-check-double" }), marking ? "Updating..." : "Mark all read") }, /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement("section", { className: "grid gap-4 md:grid-cols-2 xl:grid-cols-4" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Unread"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-3xl font-semibold text-white" }, Number(summary.unread_count || 0).toLocaleString())), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "High priority"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-3xl font-semibold text-white" }, Number(summary.high_priority_count || 0).toLocaleString())), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Comments"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-3xl font-semibold text-white" }, Number(summary.comment_count || 0).toLocaleString())), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Followers"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-3xl font-semibold text-white" }, Number(summary.follower_count || 0).toLocaleString()))), /* @__PURE__ */ React.createElement("div", { className: "grid gap-6 xl:grid-cols-[320px_minmax(0,1fr)]" }, /* @__PURE__ */ React.createElement("aside", { className: "space-y-6" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Filters"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, /* @__PURE__ */ React.createElement("label", { className: "space-y-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Search"), /* @__PURE__ */ React.createElement("input", { value: filters.q || "", onChange: (event) => updateFilters({ q: event.target.value }), className: "w-full rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white", placeholder: "Actor, title, or module" })), /* @__PURE__ */ React.createElement("div", { className: "space-y-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Type"), /* @__PURE__ */ React.createElement(NovaSelect, { value: filters.type || "all", onChange: (val) => updateFilters({ type: val }), options: inbox.type_options || [], searchable: false })), /* @__PURE__ */ React.createElement("div", { className: "space-y-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Module"), /* @__PURE__ */ React.createElement(NovaSelect, { value: filters.module || "all", onChange: (val) => updateFilters({ module: val }), options: inbox.module_options || [], searchable: false })), /* @__PURE__ */ React.createElement("div", { className: "space-y-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Read state"), /* @__PURE__ */ React.createElement(NovaSelect, { value: filters.read_state || "all", onChange: (val) => updateFilters({ read_state: val }), options: inbox.read_state_options || [], searchable: false })), /* @__PURE__ */ React.createElement("div", { className: "space-y-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Priority"), /* @__PURE__ */ React.createElement(NovaSelect, { value: filters.priority || "all", onChange: (val) => updateFilters({ priority: val }), options: inbox.priority_options || [], searchable: false })))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Attention now"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, (inbox.panels?.attention_now || []).map((item) => /* @__PURE__ */ React.createElement("a", { key: item.id, href: item.url, className: "block rounded-2xl border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold text-white" }, item.title), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs text-slate-500" }, item.module_label)))))), /* @__PURE__ */ React.createElement("section", { className: "space-y-4" }, items.length > 0 ? items.map((item) => /* @__PURE__ */ React.createElement("article", { key: item.id, className: `rounded-[28px] border p-5 ${item.is_new ? "border-sky-300/20 bg-sky-300/10" : "border-white/10 bg-white/[0.03]"}` }, /* @__PURE__ */ React.createElement("div", { className: "flex gap-4" }, item.actor?.avatar_url ? /* @__PURE__ */ React.createElement("img", { src: item.actor.avatar_url, alt: item.actor.name || "Actor", className: "h-12 w-12 rounded-2xl object-cover" }) : /* @__PURE__ */ React.createElement("div", { className: "flex h-12 w-12 items-center justify-center rounded-2xl bg-black/20 text-slate-400" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-bell" })), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, /* @__PURE__ */ React.createElement("span", null, item.module_label), /* @__PURE__ */ React.createElement("span", { className: `inline-flex items-center rounded-full border px-2 py-1 ${priorityClasses[item.priority] || priorityClasses.low}` }, item.priority), item.is_new && /* @__PURE__ */ React.createElement("span", { className: "rounded-full bg-sky-300/20 px-2 py-1 text-sky-100" }, "Unread")), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-lg font-semibold text-white" }, item.title), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm leading-6 text-slate-400" }, item.body), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap items-center gap-3 text-sm text-slate-400" }, /* @__PURE__ */ React.createElement("span", null, formatDate$2(item.created_at)), item.actor?.name && /* @__PURE__ */ React.createElement("span", null, item.actor.name), /* @__PURE__ */ React.createElement("a", { href: item.url, className: "inline-flex items-center gap-2 rounded-full border border-white/10 px-3 py-1.5 text-slate-200" }, "Open")))))) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-dashed border-white/15 px-6 py-16 text-center text-slate-400" }, "No inbox items match this filter."), /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between rounded-[24px] border border-white/10 bg-white/[0.03] px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("button", { type: "button", disabled: (meta.current_page || 1) <= 1, onClick: () => updateFilters({ page: Math.max(1, (meta.current_page || 1) - 1) }), className: "rounded-full border border-white/10 px-4 py-2 disabled:opacity-40" }, "Previous"), /* @__PURE__ */ React.createElement("span", { className: "text-xs uppercase tracking-[0.18em] text-slate-500" }, "Page ", meta.current_page || 1, " of ", meta.last_page || 1), /* @__PURE__ */ React.createElement("button", { type: "button", disabled: (meta.current_page || 1) >= (meta.last_page || 1), onClick: () => updateFilters({ page: (meta.current_page || 1) + 1 }), className: "rounded-full border border-white/10 px-4 py-2 disabled:opacity-40" }, "Next")))))); } -const __vite_glob_0_149 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_154 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioInbox }, Symbol.toStringTag, { value: "Module" })); @@ -108025,7 +108822,7 @@ function StudioNewsEditor() { } )); } -const __vite_glob_0_150 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_155 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioNewsEditor }, Symbol.toStringTag, { value: "Module" })); @@ -108211,7 +109008,7 @@ function StudioNewsIndex() { "Next" ))) : null); } -const __vite_glob_0_151 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_156 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioNewsIndex }, Symbol.toStringTag, { value: "Module" })); @@ -108244,7 +109041,7 @@ function StudioNewsTaxonomies() { tagForm.post(props.storeTagUrl); }, className: "mt-5 grid gap-3 md:grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto] md:items-center" }, /* @__PURE__ */ React.createElement("input", { value: tagForm.data.name, onChange: (event) => tagForm.setData("name", event.target.value), placeholder: "Tag name", className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" }), /* @__PURE__ */ React.createElement("input", { value: tagForm.data.slug, onChange: (event) => tagForm.setData("slug", event.target.value), placeholder: "optional slug", className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" }), /* @__PURE__ */ React.createElement("button", { type: "submit", className: "rounded-full border border-sky-300/20 bg-sky-400/10 px-4 py-3 text-sm font-semibold text-sky-100" }, "Create tag")), /* @__PURE__ */ React.createElement("div", { className: "mt-6 grid gap-3" }, tags.map((tag, index2) => /* @__PURE__ */ React.createElement("div", { key: tag.id, className: "rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 md:grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto_auto] md:items-center" }, /* @__PURE__ */ React.createElement("input", { value: tag.name, onChange: (event) => updateTag(index2, "name", event.target.value), className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" }), /* @__PURE__ */ React.createElement("input", { value: tag.slug, onChange: (event) => updateTag(index2, "slug", event.target.value), className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" }), /* @__PURE__ */ React.createElement("span", { className: "text-xs uppercase tracking-[0.14em] text-slate-500" }, Number(tag.published_count || 0).toLocaleString(), " published"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => saveTag(tag), className: "rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-sm font-semibold text-white" }, "Save")))))))); } -const __vite_glob_0_152 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_157 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioNewsTaxonomies }, Symbol.toStringTag, { value: "Module" })); @@ -108394,7 +109191,7 @@ function StudioPreferences() { return /* @__PURE__ */ React.createElement("div", { key: widgetKey, className: "flex flex-col gap-3 rounded-[22px] border border-white/10 bg-black/20 p-4 md:flex-row md:items-center md:justify-between" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold text-white" }, option.label), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs uppercase tracking-[0.16em] text-slate-500" }, "Position ", index2 + 1)), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => toggleWidget(widgetKey), className: `rounded-full border px-3 py-1.5 text-xs ${enabled ? "border-sky-300/25 bg-sky-300/10 text-sky-100" : "border-white/10 text-slate-300"}` }, enabled ? "Visible" : "Hidden"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => moveWidget(widgetKey, "up"), className: "rounded-full border border-white/10 px-3 py-1.5 text-xs text-slate-300" }, "Up"), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => moveWidget(widgetKey, "down"), className: "rounded-full border border-white/10 px-3 py-1.5 text-xs text-slate-300" }, "Down"))); })))), /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Related surfaces"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, (props.links || []).map((link2) => /* @__PURE__ */ React.createElement("a", { key: link2.url, href: link2.url, className: "block rounded-[22px] border border-white/10 bg-black/20 p-4 transition hover:border-white/20" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 text-sky-100" }, /* @__PURE__ */ React.createElement("i", { className: link2.icon }), /* @__PURE__ */ React.createElement("span", { className: "text-base font-semibold text-white" }, link2.label)))))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Preference notes"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3 text-sm text-slate-400" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-black/20 p-4" }, "Landing page and widget order are stored in the shared Studio preference record, so new Creator Studio surfaces can plug into the same contract without another migration."), /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-white/10 bg-black/20 p-4" }, "Analytics range and card density stay here so Analytics, Growth, and the main dashboard can stay visually consistent.")))))); } -const __vite_glob_0_153 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_158 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioPreferences }, Symbol.toStringTag, { value: "Module" })); @@ -108574,7 +109371,7 @@ function StudioProfile() { /* @__PURE__ */ React.createElement("div", { className: "p-6 pt-0" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-5 lg:flex-row lg:items-end lg:justify-between" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-end gap-4" }, /* @__PURE__ */ React.createElement("div", { className: "relative" }, profile.avatar_url ? /* @__PURE__ */ React.createElement("img", { src: profile.avatar_url, alt: profile.username, className: "h-24 w-24 rounded-[28px] border border-white/10 object-cover shadow-lg" }) : /* @__PURE__ */ React.createElement("div", { className: "flex h-24 w-24 items-center justify-center rounded-[28px] border border-white/10 bg-black/30 text-slate-400 shadow-lg" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-user text-2xl" })), /* @__PURE__ */ React.createElement("input", { ref: avatarInputRef, type: "file", accept: "image/png,image/jpeg,image/webp", onChange: handleAvatarSelected, className: "hidden" }), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => avatarInputRef.current?.click(), disabled: uploadingAvatar, className: "absolute -bottom-2 -right-2 inline-flex h-10 w-10 items-center justify-center rounded-full border border-sky-300/25 bg-sky-300/15 text-sky-100 disabled:opacity-50" }, /* @__PURE__ */ React.createElement("i", { className: `fa-solid ${uploadingAvatar ? "fa-spinner fa-spin" : "fa-camera"}` }))), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-3xl font-semibold text-white" }, profile.name), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-300" }, "@", profile.username), /* @__PURE__ */ React.createElement("div", { className: "mt-2 flex flex-wrap gap-4 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", null, Number(profile.followers || 0).toLocaleString(), " followers"), profile.location && /* @__PURE__ */ React.createElement("span", null, profile.location)))), profile.cover_url && /* @__PURE__ */ React.createElement("div", { className: "w-full max-w-sm rounded-[24px] border border-white/10 bg-black/30 p-4" }, /* @__PURE__ */ React.createElement("label", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400" }, "Banner position"), /* @__PURE__ */ React.createElement("input", { type: "range", min: "0", max: "100", value: coverPosition, onChange: (event) => setCoverPosition(Number(event.target.value)), className: "mt-3 w-full" }), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: saveCoverPosition, disabled: savingCoverPosition, className: "mt-3 inline-flex items-center gap-2 rounded-full border border-white/10 px-4 py-2 text-sm text-white disabled:opacity-50" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrows-up-down" }), savingCoverPosition ? "Saving..." : "Save banner position")))) )), /* @__PURE__ */ React.createElement("div", { className: "grid gap-6 xl:grid-cols-[minmax(0,1fr)_360px]" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Public profile details"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "Update the creator information that supports your public presence across Nova.")), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: saveProfile, disabled: savingProfile, className: "inline-flex items-center gap-2 rounded-full border border-sky-300/20 bg-sky-300/10 px-4 py-2 text-sm font-semibold text-sky-100 disabled:opacity-50" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-floppy-disk" }), savingProfile ? "Saving..." : "Save profile")), /* @__PURE__ */ React.createElement("div", { className: "mt-5 grid gap-4 md:grid-cols-2" }, /* @__PURE__ */ React.createElement("label", { className: "space-y-2 text-sm text-slate-300 md:col-span-2" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Display name"), /* @__PURE__ */ React.createElement("input", { value: form.display_name, onChange: (event) => setForm((current) => ({ ...current, display_name: event.target.value })), className: "w-full rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-sm text-white outline-none" })), /* @__PURE__ */ React.createElement("label", { className: "space-y-2 text-sm text-slate-300 md:col-span-2" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Tagline"), /* @__PURE__ */ React.createElement("input", { value: form.tagline, onChange: (event) => setForm((current) => ({ ...current, tagline: event.target.value })), placeholder: "One-line creator summary", className: "w-full rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-sm text-white outline-none placeholder:text-slate-500" })), /* @__PURE__ */ React.createElement("label", { className: "space-y-2 text-sm text-slate-300 md:col-span-2" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Bio"), /* @__PURE__ */ React.createElement("textarea", { value: form.bio, onChange: (event) => setForm((current) => ({ ...current, bio: event.target.value })), rows: 5, placeholder: "Tell visitors what you create and what makes your work distinct.", className: "w-full rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-sm text-white outline-none placeholder:text-slate-500" })), /* @__PURE__ */ React.createElement("label", { className: "space-y-2 text-sm text-slate-300 md:col-span-2" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Website"), /* @__PURE__ */ React.createElement("input", { value: form.website, onChange: (event) => setForm((current) => ({ ...current, website: event.target.value })), placeholder: "https://example.com", className: "w-full rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-sm text-white outline-none placeholder:text-slate-500" }))), /* @__PURE__ */ React.createElement("div", { className: "mt-6" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-4" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h3", { className: "text-base font-semibold text-white" }, "Social links"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 text-sm text-slate-400" }, "Add the channels that matter for your creator identity.")), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: addSocialLink, className: "inline-flex items-center gap-2 rounded-full border border-white/10 px-4 py-2 text-sm text-white" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-plus" }), "Add link")), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, form.social_links.map((link2, index2) => /* @__PURE__ */ React.createElement("div", { key: `${index2}-${link2.platform}`, className: "grid gap-3 rounded-[24px] border border-white/10 bg-black/20 p-4 md:grid-cols-[180px_minmax(0,1fr)_auto]" }, /* @__PURE__ */ React.createElement("input", { value: link2.platform, onChange: (event) => updateSocialLink(index2, "platform", event.target.value), placeholder: "instagram", className: "rounded-2xl border border-white/10 bg-black/30 px-4 py-3 text-sm text-white outline-none placeholder:text-slate-500" }), /* @__PURE__ */ React.createElement("input", { value: link2.url, onChange: (event) => updateSocialLink(index2, "url", event.target.value), placeholder: "https://...", className: "rounded-2xl border border-white/10 bg-black/30 px-4 py-3 text-sm text-white outline-none placeholder:text-slate-500" }), /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => removeSocialLink(index2), className: "inline-flex items-center justify-center rounded-2xl border border-rose-300/20 bg-rose-300/10 px-4 py-3 text-sm text-rose-100" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-trash" }))))))), /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Publishing footprint"), /* @__PURE__ */ React.createElement("div", { className: "mt-5 grid gap-4" }, (props.moduleSummaries || []).map((item) => /* @__PURE__ */ React.createElement("div", { key: item.key, className: "rounded-[22px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 text-slate-200" }, /* @__PURE__ */ React.createElement("i", { className: item.icon }), /* @__PURE__ */ React.createElement("span", null, item.label)), /* @__PURE__ */ React.createElement("div", { className: "mt-3 text-3xl font-semibold text-white" }, Number(item.count || 0).toLocaleString()), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-slate-400" }, Number(item.published_count || 0).toLocaleString(), " published, ", Number(item.draft_count || 0).toLocaleString(), " drafts"))))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-4" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Featured identity"), /* @__PURE__ */ React.createElement("a", { href: "/studio/featured", className: "text-sm font-medium text-sky-100" }, "Manage featured")), /* @__PURE__ */ React.createElement("div", { className: "mt-4 flex flex-wrap gap-2" }, featuredModules.length > 0 ? featuredModules.map((module) => /* @__PURE__ */ React.createElement("span", { key: module, className: "inline-flex items-center rounded-full border border-sky-300/20 bg-sky-300/10 px-3 py-1 text-xs font-semibold uppercase tracking-[0.16em] text-sky-100" }, socialPlatformLabel(module))) : /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-400" }, "No featured modules selected yet.")), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, Object.entries(featuredContent).map(([module, item]) => item ? /* @__PURE__ */ React.createElement("a", { key: module, href: item.view_url || item.preview_url || "/studio/featured", className: "flex items-center gap-3 rounded-2xl border border-white/10 bg-black/20 p-3" }, item.image_url ? /* @__PURE__ */ React.createElement("img", { src: item.image_url, alt: item.title, className: "h-14 w-14 rounded-2xl object-cover" }) : /* @__PURE__ */ React.createElement("div", { className: "flex h-14 w-14 items-center justify-center rounded-2xl bg-white/5 text-slate-400" }, /* @__PURE__ */ React.createElement("i", { className: item.module_icon || "fa-solid fa-star" })), /* @__PURE__ */ React.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, socialPlatformLabel(module)), /* @__PURE__ */ React.createElement("div", { className: "truncate text-sm font-semibold text-white" }, item.title))) : null))))))); } -const __vite_glob_0_154 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_159 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioProfile }, Symbol.toStringTag, { value: "Module" })); @@ -108651,7 +109448,7 @@ function StudioScheduled() { }, [items, summary.next_publish_at]); return /* @__PURE__ */ React.createElement(StudioLayout, { title: props.title, subtitle: props.description }, /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement("section", { className: "grid gap-4 xl:grid-cols-[minmax(0,1fr)_340px]" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[30px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 md:grid-cols-3" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Scheduled total"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-3xl font-semibold text-white" }, Number(summary.total || 0).toLocaleString())), /* @__PURE__ */ React.createElement("div", { className: "rounded-[24px] border border-white/10 bg-black/20 p-4 md:col-span-2" }, /* @__PURE__ */ React.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Next publish slot"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-xl font-semibold text-white" }, formatReleaseCountdown(summary.next_publish_at, nowMs)), summary.next_publish_at && /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-sm text-slate-400" }, formatScheduledDate(summary.next_publish_at)))), /* @__PURE__ */ React.createElement("div", { className: "mt-5 grid gap-3 md:grid-cols-2 xl:grid-cols-4" }, (summary.by_module || []).map((entry) => /* @__PURE__ */ React.createElement("div", { key: entry.key, className: "rounded-[22px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 text-slate-300" }, /* @__PURE__ */ React.createElement("i", { className: entry.icon }), /* @__PURE__ */ React.createElement("span", { className: "text-sm font-medium text-white" }, entry.label)), /* @__PURE__ */ React.createElement("div", { className: "mt-3 text-2xl font-semibold text-white" }, Number(entry.count || 0).toLocaleString()))))), /* @__PURE__ */ React.createElement("div", { className: "rounded-[30px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Agenda"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, agenda.length > 0 ? agenda.slice(0, 6).map((day) => /* @__PURE__ */ React.createElement("div", { key: day.date, className: "rounded-[22px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("span", { className: "text-sm font-semibold text-white" }, day.label), /* @__PURE__ */ React.createElement("span", { className: "text-xs uppercase tracking-[0.18em] text-slate-500" }, day.count, " items")), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-sm text-slate-400" }, day.items.slice(0, 2).map((item) => item.title).join(" • ")))) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[22px] border border-dashed border-white/15 px-4 py-8 text-sm text-slate-400" }, "No scheduled items yet.")))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-[radial-gradient(circle_at_top_left,_rgba(56,189,248,0.14),_transparent_35%),linear-gradient(135deg,_rgba(15,23,42,0.86),_rgba(2,6,23,0.96))] p-5 lg:p-6" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 md:grid-cols-2 xl:grid-cols-5" }, /* @__PURE__ */ React.createElement("label", { className: "space-y-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Search scheduled work"), /* @__PURE__ */ React.createElement("input", { value: filters.q || "", onChange: (event) => updateFilters({ q: event.target.value }), className: "w-full rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white", placeholder: "Title or module" })), /* @__PURE__ */ React.createElement("div", { className: "space-y-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Module"), /* @__PURE__ */ React.createElement(NovaSelect, { value: filters.module || "all", onChange: (val) => updateFilters({ module: val }), options: listing.module_options || [], searchable: false })), /* @__PURE__ */ React.createElement("div", { className: "space-y-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Date range"), /* @__PURE__ */ React.createElement(NovaSelect, { value: filters.range || "upcoming", onChange: (val) => updateFilters({ range: val }), options: rangeOptions2, searchable: false })), /* @__PURE__ */ React.createElement("div", { className: "space-y-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Start date"), /* @__PURE__ */ React.createElement(DateTimePicker, { value: filters.start_date || "", onChange: (nextValue) => updateFilters({ range: "custom", start_date: nextValue }), mode: "date", placeholder: "Start date", clearable: true, className: "bg-black/20" })), /* @__PURE__ */ React.createElement("div", { className: "space-y-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "End date"), /* @__PURE__ */ React.createElement(DateTimePicker, { value: filters.end_date || "", onChange: (nextValue) => updateFilters({ range: "custom", end_date: nextValue }), mode: "date", placeholder: "End date", clearable: true, className: "bg-black/20" })), /* @__PURE__ */ React.createElement("div", { className: "flex items-end" }, /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => updateFilters({ q: "", module: "all", range: "upcoming", start_date: "", end_date: "" }), className: "w-full rounded-2xl border border-white/10 px-4 py-3 text-sm text-slate-200" }, "Reset")))), /* @__PURE__ */ React.createElement("section", { className: "space-y-4" }, items.length > 0 ? items.map((item) => /* @__PURE__ */ React.createElement("article", { key: item.id, className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between" }, /* @__PURE__ */ React.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-3 text-[11px] font-semibold uppercase tracking-[0.18em] text-sky-200/70" }, /* @__PURE__ */ React.createElement("span", null, item.module_label), /* @__PURE__ */ React.createElement("span", null, item.status)), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 text-xl font-semibold text-white" }, item.title), /* @__PURE__ */ React.createElement("div", { className: "mt-2 flex flex-wrap items-center gap-4 text-sm text-slate-400" }, /* @__PURE__ */ React.createElement("span", null, formatReleaseCountdown(item.scheduled_at || item.published_at, nowMs)), /* @__PURE__ */ React.createElement("span", null, formatScheduledDate(item.scheduled_at || item.published_at)), item.visibility && /* @__PURE__ */ React.createElement("span", null, "Visibility: ", item.visibility), item.updated_at && /* @__PURE__ */ React.createElement("span", null, "Last edited ", formatScheduledDate(item.updated_at)), item.schedule_timezone && /* @__PURE__ */ React.createElement("span", null, item.schedule_timezone))), /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, /* @__PURE__ */ React.createElement("a", { href: item.edit_url || item.manage_url, className: "inline-flex items-center gap-2 rounded-full border border-white/10 px-4 py-2 text-sm text-slate-200" }, "Edit"), /* @__PURE__ */ React.createElement("a", { href: item.edit_url || item.manage_url, className: "inline-flex items-center gap-2 rounded-full border border-white/10 px-4 py-2 text-sm text-slate-200" }, "Reschedule"), item.preview_url && /* @__PURE__ */ React.createElement("a", { href: item.preview_url, className: "inline-flex items-center gap-2 rounded-full border border-white/10 px-4 py-2 text-sm text-slate-200" }, "Preview"), /* @__PURE__ */ React.createElement("button", { type: "button", disabled: busyId === `publish:${item.id}`, onClick: () => runAction(item, "publish"), className: "inline-flex items-center gap-2 rounded-full border border-sky-300/20 bg-sky-300/10 px-4 py-2 text-sm text-sky-100 disabled:opacity-50" }, "Publish now"), /* @__PURE__ */ React.createElement("button", { type: "button", disabled: busyId === `unschedule:${item.id}`, onClick: () => runAction(item, "unschedule"), className: "inline-flex items-center gap-2 rounded-full border border-white/10 px-4 py-2 text-sm text-slate-200 disabled:opacity-50" }, "Unschedule"))))) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-dashed border-white/15 px-6 py-16 text-center text-slate-400" }, "No scheduled content matches this view.")), /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between rounded-[24px] border border-white/10 bg-white/[0.03] px-4 py-3 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("button", { type: "button", disabled: (meta.current_page || 1) <= 1, onClick: () => updateFilters({ page: Math.max(1, (meta.current_page || 1) - 1) }), className: "rounded-full border border-white/10 px-4 py-2 disabled:opacity-40" }, "Previous"), /* @__PURE__ */ React.createElement("span", { className: "text-xs uppercase tracking-[0.18em] text-slate-500" }, "Page ", meta.current_page || 1, " of ", meta.last_page || 1), /* @__PURE__ */ React.createElement("button", { type: "button", disabled: (meta.current_page || 1) >= (meta.last_page || 1), onClick: () => updateFilters({ page: (meta.current_page || 1) + 1 }), className: "rounded-full border border-white/10 px-4 py-2 disabled:opacity-40" }, "Next")))); } -const __vite_glob_0_155 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_160 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioScheduled }, Symbol.toStringTag, { value: "Module" })); @@ -108669,7 +109466,7 @@ function StudioSearch() { }; return /* @__PURE__ */ React.createElement(StudioLayout, { title: props.title, subtitle: props.description }, /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-[radial-gradient(circle_at_top_left,_rgba(56,189,248,0.14),_transparent_35%),linear-gradient(135deg,_rgba(15,23,42,0.86),_rgba(2,6,23,0.96))] p-5 lg:p-6" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-3 md:grid-cols-2 xl:grid-cols-5" }, /* @__PURE__ */ React.createElement("label", { className: "space-y-2 text-sm text-slate-300 xl:col-span-3" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Search Studio"), /* @__PURE__ */ React.createElement("input", { value: filters.q || "", onChange: (event) => updateFilters({ q: event.target.value }), className: "w-full rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white", placeholder: "Search content, comments, inbox, or assets" })), /* @__PURE__ */ React.createElement("div", { className: "space-y-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Surface"), /* @__PURE__ */ React.createElement(NovaSelect, { value: filters.type || "all", onChange: (val) => updateFilters({ type: val }), options: search2.type_options || [], searchable: false })), /* @__PURE__ */ React.createElement("div", { className: "space-y-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "block text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, "Module"), /* @__PURE__ */ React.createElement(NovaSelect, { value: filters.module || "all", onChange: (val) => updateFilters({ module: val }), options: search2.module_options || [], searchable: false })))), filters.q ? /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm text-slate-400" }, "Found ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, Number(search2.summary?.total || 0).toLocaleString()), " matches for ", /* @__PURE__ */ React.createElement("span", { className: "font-semibold text-white" }, search2.summary?.query)), sections.length > 0 ? sections.map((section) => /* @__PURE__ */ React.createElement("section", { key: section.key, className: "rounded-[30px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, section.label), /* @__PURE__ */ React.createElement("span", { className: "text-xs uppercase tracking-[0.18em] text-slate-500" }, section.count, " matches")), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 md:grid-cols-2 xl:grid-cols-3" }, section.items.map((item) => /* @__PURE__ */ React.createElement("a", { key: item.id, href: item.href, className: "block rounded-[24px] border border-white/10 bg-black/20 p-4 transition hover:border-white/20" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-start gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "flex h-10 w-10 items-center justify-center rounded-2xl bg-white/[0.04] text-sky-100" }, /* @__PURE__ */ React.createElement("i", { className: item.icon })), /* @__PURE__ */ React.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React.createElement("div", { className: "truncate text-base font-semibold text-white" }, item.title), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs uppercase tracking-[0.18em] text-slate-500" }, item.subtitle), /* @__PURE__ */ React.createElement("p", { className: "mt-3 line-clamp-3 text-sm leading-6 text-slate-400" }, item.description)))))))) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-dashed border-white/15 px-6 py-16 text-center text-slate-400" }, "No results matched this search yet.")) : /* @__PURE__ */ React.createElement("div", { className: "grid gap-6 xl:grid-cols-[minmax(0,1fr)_320px]" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Continue working"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3 md:grid-cols-2" }, (search2.empty_state?.continue_working || []).map((item) => /* @__PURE__ */ React.createElement("a", { key: item.id, href: item.edit_url || item.manage_url, className: "block rounded-[24px] border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold text-white" }, item.title), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs text-slate-500" }, item.module_label, " · ", item.workflow?.readiness?.label))))), /* @__PURE__ */ React.createElement("aside", { className: "space-y-6" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Stale drafts"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 space-y-3" }, (search2.empty_state?.stale_drafts || []).map((item) => /* @__PURE__ */ React.createElement("a", { key: item.id, href: item.edit_url || item.manage_url, className: "block rounded-2xl border border-white/10 bg-black/20 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold text-white" }, item.title), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs text-slate-500" }, item.module_label))))), /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "Quick create"), /* @__PURE__ */ React.createElement("div", { className: "mt-4 grid gap-3" }, (props.quickCreate || []).map((item) => /* @__PURE__ */ React.createElement("a", { key: item.key, href: item.url, className: "inline-flex items-center gap-3 rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-sm text-slate-100" }, /* @__PURE__ */ React.createElement("i", { className: item.icon }), /* @__PURE__ */ React.createElement("span", null, "New ", item.label))))))))); } -const __vite_glob_0_156 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_161 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioSearch }, Symbol.toStringTag, { value: "Module" })); @@ -108677,7 +109474,7 @@ function StudioSettings() { const { props } = X$1(); return /* @__PURE__ */ React.createElement(StudioLayout, { title: props.title, subtitle: props.description }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-6 xl:grid-cols-[minmax(0,1fr)_360px]" }, /* @__PURE__ */ React.createElement("section", { className: "rounded-[30px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, "System handoff"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 max-w-2xl text-sm leading-6 text-slate-400" }, "Studio now keeps creator workflow preferences in their own surface. This page stays focused on links out to adjacent dashboards and the control points that do not belong in the day-to-day workflow UI."), /* @__PURE__ */ React.createElement("div", { className: "mt-5 grid gap-3 md:grid-cols-2" }, (props.links || []).map((link2) => /* @__PURE__ */ React.createElement("a", { key: link2.url, href: link2.url, className: "rounded-[22px] border border-white/10 bg-black/20 p-4 transition hover:border-white/20 hover:bg-black/30" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 text-sky-100" }, /* @__PURE__ */ React.createElement("i", { className: link2.icon }), /* @__PURE__ */ React.createElement("span", { className: "text-base font-semibold text-white" }, link2.label)), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-6 text-slate-400" }, "Open the linked dashboard or settings surface without losing the Studio navigation shell as the default control plane."))))), /* @__PURE__ */ React.createElement("section", { className: "space-y-6" }, (props.sections || []).map((section) => /* @__PURE__ */ React.createElement("div", { key: section.title, className: "rounded-[30px] border border-white/10 bg-white/[0.03] p-6" }, /* @__PURE__ */ React.createElement("h2", { className: "text-lg font-semibold text-white" }, section.title), /* @__PURE__ */ React.createElement("p", { className: "mt-3 text-sm leading-6 text-slate-400" }, section.body), /* @__PURE__ */ React.createElement("a", { href: section.href, className: "mt-4 inline-flex items-center gap-2 rounded-full border border-sky-300/20 bg-sky-300/10 px-4 py-2 text-sm font-semibold text-sky-100 transition hover:border-sky-300/35 hover:bg-sky-300/15" }, section.cta, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-arrow-right" }))))))); } -const __vite_glob_0_157 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_162 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioSettings }, Symbol.toStringTag, { value: "Module" })); @@ -108698,7 +109495,7 @@ function StudioStories() { } )); } -const __vite_glob_0_158 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_163 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioStories }, Symbol.toStringTag, { value: "Module" })); @@ -109361,7 +110158,7 @@ function StudioUploadQueue() { ))))); }))))); } -const __vite_glob_0_159 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_164 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioUploadQueue }, Symbol.toStringTag, { value: "Module" })); @@ -111093,7 +111890,7 @@ function StudioWorldEditor() { )) : null )); } -const __vite_glob_0_160 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_165 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioWorldEditor }, Symbol.toStringTag, { value: "Module" })); @@ -111179,7 +111976,7 @@ function StudioWorldsIndex() { }; return /* @__PURE__ */ React.createElement(StudioLayout, { title: props.title, subtitle: props.description }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-6" }, /* @__PURE__ */ React.createElement(WorldAnalyticsPortfolioPanel, { analytics: props.analytics }), /* @__PURE__ */ React.createElement("section", { className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5" }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 lg:grid-cols-[minmax(0,1fr)_12rem_12rem_auto] lg:items-end" }, /* @__PURE__ */ React.createElement("label", { className: "grid gap-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Search"), /* @__PURE__ */ React.createElement("input", { value: filters.q || "", onChange: (event) => updateFilter("q", event.target.value), placeholder: "Search title, slug, or summary", className: "rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" })), /* @__PURE__ */ React.createElement("div", { className: "grid gap-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Status"), /* @__PURE__ */ React.createElement(NovaSelect, { value: filters.status || "", onChange: (val) => updateFilter("status", val), options: [{ value: "", label: "All statuses" }, ...props.statusOptions || []], searchable: false })), /* @__PURE__ */ React.createElement("div", { className: "grid gap-2 text-sm text-slate-300" }, /* @__PURE__ */ React.createElement("span", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500" }, "Type"), /* @__PURE__ */ React.createElement(NovaSelect, { value: filters.type || "", onChange: (val) => updateFilter("type", val), options: [{ value: "", label: "All types" }, ...props.typeOptions || []], searchable: false })), /* @__PURE__ */ React.createElement("a", { href: props.createUrl, className: "inline-flex items-center justify-center gap-2 rounded-2xl border border-sky-300/20 bg-sky-400/10 px-5 py-3 text-sm font-semibold text-sky-100 transition hover:bg-sky-400/15" }, /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-plus" }), "New world"))), /* @__PURE__ */ React.createElement("section", { className: "grid gap-4 xl:grid-cols-2" }, items.length > 0 ? items.map((world) => /* @__PURE__ */ React.createElement("a", { key: world.id, href: world.edit_url, className: "rounded-[28px] border border-white/10 bg-white/[0.03] p-5 transition hover:-translate-y-1 hover:border-white/20 hover:bg-white/[0.05]" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500" }, /* @__PURE__ */ React.createElement(WorldStatusBadge, { badge: { label: world.status, tone: "slate" } }), /* @__PURE__ */ React.createElement(WorldStatusBadge, { badge: { label: world.type, tone: "slate" } }), (Array.isArray(world.status_badges) ? world.status_badges : []).map((badge) => /* @__PURE__ */ React.createElement(WorldStatusBadge, { key: `${world.id}-${badge.label}`, badge }))), /* @__PURE__ */ React.createElement("h2", { className: "mt-4 text-2xl font-semibold tracking-[-0.03em] text-white" }, world.title), /* @__PURE__ */ React.createElement("div", { className: "mt-2 text-sm text-slate-500" }, "/", world.slug), world.summary ? /* @__PURE__ */ React.createElement("p", { className: "mt-4 text-sm leading-6 text-slate-300" }, world.summary) : null, /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-wrap gap-4 text-sm text-slate-400" }, world.timeframe_label ? /* @__PURE__ */ React.createElement("span", null, world.timeframe_label) : null, world.promotion_window_label ? /* @__PURE__ */ React.createElement("span", null, world.promotion_window_label) : null, /* @__PURE__ */ React.createElement("span", null, world.relation_count, " relations"), world.live_submission_count > 0 ? /* @__PURE__ */ React.createElement("span", null, world.live_submission_count, " live submissions") : null, world.theme_key ? /* @__PURE__ */ React.createElement("span", null, world.theme_key) : null), /* @__PURE__ */ React.createElement("div", { className: "mt-5 flex flex-wrap gap-3 text-sm font-semibold" }, /* @__PURE__ */ React.createElement("span", { className: "text-sky-100" }, "Edit"), /* @__PURE__ */ React.createElement("span", { className: "text-slate-500" }, "Preview"), world.public_url ? /* @__PURE__ */ React.createElement("span", { className: "text-slate-500" }, "Public") : null))) : /* @__PURE__ */ React.createElement("div", { className: "rounded-[28px] border border-dashed border-white/10 bg-white/[0.02] p-6 text-sm text-slate-400" }, "No worlds match this filter yet.")))); } -const __vite_glob_0_161 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_166 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: StudioWorldsIndex }, Symbol.toStringTag, { value: "Module" })); @@ -115813,7 +116610,7 @@ function UploadPage({ draftId = null, filesCdnUrl = "", chunkSize, chunkRequestT "Reset" )))), /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/5 p-6" }, /* @__PURE__ */ React.createElement("h3", { className: "text-lg font-semibold" }, "Pipeline status"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-white/60" }, "Stage: ", /* @__PURE__ */ React.createElement("span", { className: "text-white" }, statusLabel2)), /* @__PURE__ */ React.createElement("div", { className: "mt-4 h-2 w-full overflow-hidden rounded-full bg-white/10" }, /* @__PURE__ */ React.createElement("div", { className: "h-full bg-sky-400 transition-all", style: { width: `${state.progress}%` } })), state.failureReason && /* @__PURE__ */ React.createElement("div", { className: "mt-3 text-sm text-red-200" }, "Failure: ", state.failureReason), state.previewUrl && state.phase === phases.success && /* @__PURE__ */ React.createElement("div", { className: "mt-6" }, /* @__PURE__ */ React.createElement("h4", { className: "text-sm font-semibold text-white/80" }, "CDN preview"), /* @__PURE__ */ React.createElement("div", { className: "mt-2 overflow-hidden rounded-xl border border-white/10" }, /* @__PURE__ */ React.createElement("img", { src: state.previewUrl, alt: "CDN preview", className: "h-56 w-full object-cover" }))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 rounded-xl border border-white/10 bg-white/5 px-4 py-3 text-xs text-white/60" }, "Session: ", state.sessionId ?? "—")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-white/10 bg-white/5 p-6" }, /* @__PURE__ */ React.createElement("h3", { className: "text-lg font-semibold" }, "Draft resume"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-white/60" }, "Use the draft link to resume an interrupted upload."), draftId ? /* @__PURE__ */ React.createElement("div", { className: "mt-4 text-sm text-white/80" }, "Draft ID: ", draftId) : /* @__PURE__ */ React.createElement("div", { className: "mt-4 text-sm text-white/50" }, "No draft loaded.")))))); } -const __vite_glob_0_162 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_167 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: UploadPage }, Symbol.toStringTag, { value: "Module" })); @@ -116057,7 +116854,7 @@ function WorldIndex() { } ))); } -const __vite_glob_0_163 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_168 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: WorldIndex }, Symbol.toStringTag, { value: "Module" })); @@ -116358,7 +117155,7 @@ function WorldShow() { } ))); } -const __vite_glob_0_164 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ +const __vite_glob_0_169 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: WorldShow }, Symbol.toStringTag, { value: "Module" })); @@ -142047,73 +142844,78 @@ const pages = /* @__PURE__ */ Object.assign({ "./Pages/Moderation/ArtworkMaturityQueue.jsx": __vite_glob_0_95, "./Pages/Moderation/Enhance/Index.jsx": __vite_glob_0_96, "./Pages/Moderation/Enhance/Show.jsx": __vite_glob_0_97, - "./Pages/Moderation/WorldWebStoriesIndex.jsx": __vite_glob_0_98, - "./Pages/Moderation/WorldWebStoryEditor.jsx": __vite_glob_0_99, - "./Pages/News/NewsComments.jsx": __vite_glob_0_100, - "./Pages/News/NewsImagePreview.jsx": __vite_glob_0_101, - "./Pages/Profile/ProfileGallery.jsx": __vite_glob_0_102, - "./Pages/Profile/ProfileShow.jsx": __vite_glob_0_103, - "./Pages/Settings/ProfileEdit.jsx": __vite_glob_0_104, - "./Pages/Studio/StudioActivity.jsx": __vite_glob_0_105, - "./Pages/Studio/StudioAnalytics.jsx": __vite_glob_0_106, - "./Pages/Studio/StudioArchived.jsx": __vite_glob_0_107, - "./Pages/Studio/StudioArtworkAnalytics.jsx": __vite_glob_0_108, - "./Pages/Studio/StudioArtworkEdit.jsx": __vite_glob_0_109, - "./Pages/Studio/StudioArtworks.jsx": __vite_glob_0_110, - "./Pages/Studio/StudioAssets.jsx": __vite_glob_0_111, - "./Pages/Studio/StudioCalendar.jsx": __vite_glob_0_112, - "./Pages/Studio/StudioCardAnalytics.jsx": __vite_glob_0_113, - "./Pages/Studio/StudioCardEditor.jsx": __vite_glob_0_114, - "./Pages/Studio/StudioCardsIndex.jsx": __vite_glob_0_115, - "./Pages/Studio/StudioChallenges.jsx": __vite_glob_0_116, - "./Pages/Studio/StudioCollections.jsx": __vite_glob_0_117, - "./Pages/Studio/StudioComments.jsx": __vite_glob_0_118, - "./Pages/Studio/StudioContentIndex.jsx": __vite_glob_0_119, - "./Pages/Studio/StudioDashboard.jsx": __vite_glob_0_120, - "./Pages/Studio/StudioDrafts.jsx": __vite_glob_0_121, - "./Pages/Studio/StudioFeatured.jsx": __vite_glob_0_122, - "./Pages/Studio/StudioFollowers.jsx": __vite_glob_0_123, - "./Pages/Studio/StudioGroupActivity.jsx": __vite_glob_0_124, - "./Pages/Studio/StudioGroupArtworks.jsx": __vite_glob_0_125, - "./Pages/Studio/StudioGroupAssets.jsx": __vite_glob_0_126, - "./Pages/Studio/StudioGroupChallengeEditor.jsx": __vite_glob_0_127, - "./Pages/Studio/StudioGroupChallenges.jsx": __vite_glob_0_128, - "./Pages/Studio/StudioGroupCollections.jsx": __vite_glob_0_129, - "./Pages/Studio/StudioGroupCreate.jsx": __vite_glob_0_130, - "./Pages/Studio/StudioGroupDashboard.jsx": __vite_glob_0_131, - "./Pages/Studio/StudioGroupEventEditor.jsx": __vite_glob_0_132, - "./Pages/Studio/StudioGroupEvents.jsx": __vite_glob_0_133, - "./Pages/Studio/StudioGroupInvitations.jsx": __vite_glob_0_134, - "./Pages/Studio/StudioGroupJoinRequests.jsx": __vite_glob_0_135, - "./Pages/Studio/StudioGroupMembers.jsx": __vite_glob_0_136, - "./Pages/Studio/StudioGroupPostEditor.jsx": __vite_glob_0_137, - "./Pages/Studio/StudioGroupPosts.jsx": __vite_glob_0_138, - "./Pages/Studio/StudioGroupProjectEditor.jsx": __vite_glob_0_139, - "./Pages/Studio/StudioGroupProjects.jsx": __vite_glob_0_140, - "./Pages/Studio/StudioGroupRecruitment.jsx": __vite_glob_0_141, - "./Pages/Studio/StudioGroupReleaseEditor.jsx": __vite_glob_0_142, - "./Pages/Studio/StudioGroupReleases.jsx": __vite_glob_0_143, - "./Pages/Studio/StudioGroupReputation.jsx": __vite_glob_0_144, - "./Pages/Studio/StudioGroupReviewQueue.jsx": __vite_glob_0_145, - "./Pages/Studio/StudioGroupSettings.jsx": __vite_glob_0_146, - "./Pages/Studio/StudioGroupsIndex.jsx": __vite_glob_0_147, - "./Pages/Studio/StudioGrowth.jsx": __vite_glob_0_148, - "./Pages/Studio/StudioInbox.jsx": __vite_glob_0_149, - "./Pages/Studio/StudioNewsEditor.jsx": __vite_glob_0_150, - "./Pages/Studio/StudioNewsIndex.jsx": __vite_glob_0_151, - "./Pages/Studio/StudioNewsTaxonomies.jsx": __vite_glob_0_152, - "./Pages/Studio/StudioPreferences.jsx": __vite_glob_0_153, - "./Pages/Studio/StudioProfile.jsx": __vite_glob_0_154, - "./Pages/Studio/StudioScheduled.jsx": __vite_glob_0_155, - "./Pages/Studio/StudioSearch.jsx": __vite_glob_0_156, - "./Pages/Studio/StudioSettings.jsx": __vite_glob_0_157, - "./Pages/Studio/StudioStories.jsx": __vite_glob_0_158, - "./Pages/Studio/StudioUploadQueue.jsx": __vite_glob_0_159, - "./Pages/Studio/StudioWorldEditor.jsx": __vite_glob_0_160, - "./Pages/Studio/StudioWorldsIndex.jsx": __vite_glob_0_161, - "./Pages/Upload/Index.jsx": __vite_glob_0_162, - "./Pages/World/WorldIndex.jsx": __vite_glob_0_163, - "./Pages/World/WorldShow.jsx": __vite_glob_0_164 + "./Pages/Moderation/FeaturedArtworks.jsx": __vite_glob_0_98, + "./Pages/Moderation/StaffApplications/Index.jsx": __vite_glob_0_99, + "./Pages/Moderation/StaffApplications/Show.jsx": __vite_glob_0_100, + "./Pages/Moderation/Stories.jsx": __vite_glob_0_101, + "./Pages/Moderation/UsernameQueue.jsx": __vite_glob_0_102, + "./Pages/Moderation/WorldWebStoriesIndex.jsx": __vite_glob_0_103, + "./Pages/Moderation/WorldWebStoryEditor.jsx": __vite_glob_0_104, + "./Pages/News/NewsComments.jsx": __vite_glob_0_105, + "./Pages/News/NewsImagePreview.jsx": __vite_glob_0_106, + "./Pages/Profile/ProfileGallery.jsx": __vite_glob_0_107, + "./Pages/Profile/ProfileShow.jsx": __vite_glob_0_108, + "./Pages/Settings/ProfileEdit.jsx": __vite_glob_0_109, + "./Pages/Studio/StudioActivity.jsx": __vite_glob_0_110, + "./Pages/Studio/StudioAnalytics.jsx": __vite_glob_0_111, + "./Pages/Studio/StudioArchived.jsx": __vite_glob_0_112, + "./Pages/Studio/StudioArtworkAnalytics.jsx": __vite_glob_0_113, + "./Pages/Studio/StudioArtworkEdit.jsx": __vite_glob_0_114, + "./Pages/Studio/StudioArtworks.jsx": __vite_glob_0_115, + "./Pages/Studio/StudioAssets.jsx": __vite_glob_0_116, + "./Pages/Studio/StudioCalendar.jsx": __vite_glob_0_117, + "./Pages/Studio/StudioCardAnalytics.jsx": __vite_glob_0_118, + "./Pages/Studio/StudioCardEditor.jsx": __vite_glob_0_119, + "./Pages/Studio/StudioCardsIndex.jsx": __vite_glob_0_120, + "./Pages/Studio/StudioChallenges.jsx": __vite_glob_0_121, + "./Pages/Studio/StudioCollections.jsx": __vite_glob_0_122, + "./Pages/Studio/StudioComments.jsx": __vite_glob_0_123, + "./Pages/Studio/StudioContentIndex.jsx": __vite_glob_0_124, + "./Pages/Studio/StudioDashboard.jsx": __vite_glob_0_125, + "./Pages/Studio/StudioDrafts.jsx": __vite_glob_0_126, + "./Pages/Studio/StudioFeatured.jsx": __vite_glob_0_127, + "./Pages/Studio/StudioFollowers.jsx": __vite_glob_0_128, + "./Pages/Studio/StudioGroupActivity.jsx": __vite_glob_0_129, + "./Pages/Studio/StudioGroupArtworks.jsx": __vite_glob_0_130, + "./Pages/Studio/StudioGroupAssets.jsx": __vite_glob_0_131, + "./Pages/Studio/StudioGroupChallengeEditor.jsx": __vite_glob_0_132, + "./Pages/Studio/StudioGroupChallenges.jsx": __vite_glob_0_133, + "./Pages/Studio/StudioGroupCollections.jsx": __vite_glob_0_134, + "./Pages/Studio/StudioGroupCreate.jsx": __vite_glob_0_135, + "./Pages/Studio/StudioGroupDashboard.jsx": __vite_glob_0_136, + "./Pages/Studio/StudioGroupEventEditor.jsx": __vite_glob_0_137, + "./Pages/Studio/StudioGroupEvents.jsx": __vite_glob_0_138, + "./Pages/Studio/StudioGroupInvitations.jsx": __vite_glob_0_139, + "./Pages/Studio/StudioGroupJoinRequests.jsx": __vite_glob_0_140, + "./Pages/Studio/StudioGroupMembers.jsx": __vite_glob_0_141, + "./Pages/Studio/StudioGroupPostEditor.jsx": __vite_glob_0_142, + "./Pages/Studio/StudioGroupPosts.jsx": __vite_glob_0_143, + "./Pages/Studio/StudioGroupProjectEditor.jsx": __vite_glob_0_144, + "./Pages/Studio/StudioGroupProjects.jsx": __vite_glob_0_145, + "./Pages/Studio/StudioGroupRecruitment.jsx": __vite_glob_0_146, + "./Pages/Studio/StudioGroupReleaseEditor.jsx": __vite_glob_0_147, + "./Pages/Studio/StudioGroupReleases.jsx": __vite_glob_0_148, + "./Pages/Studio/StudioGroupReputation.jsx": __vite_glob_0_149, + "./Pages/Studio/StudioGroupReviewQueue.jsx": __vite_glob_0_150, + "./Pages/Studio/StudioGroupSettings.jsx": __vite_glob_0_151, + "./Pages/Studio/StudioGroupsIndex.jsx": __vite_glob_0_152, + "./Pages/Studio/StudioGrowth.jsx": __vite_glob_0_153, + "./Pages/Studio/StudioInbox.jsx": __vite_glob_0_154, + "./Pages/Studio/StudioNewsEditor.jsx": __vite_glob_0_155, + "./Pages/Studio/StudioNewsIndex.jsx": __vite_glob_0_156, + "./Pages/Studio/StudioNewsTaxonomies.jsx": __vite_glob_0_157, + "./Pages/Studio/StudioPreferences.jsx": __vite_glob_0_158, + "./Pages/Studio/StudioProfile.jsx": __vite_glob_0_159, + "./Pages/Studio/StudioScheduled.jsx": __vite_glob_0_160, + "./Pages/Studio/StudioSearch.jsx": __vite_glob_0_161, + "./Pages/Studio/StudioSettings.jsx": __vite_glob_0_162, + "./Pages/Studio/StudioStories.jsx": __vite_glob_0_163, + "./Pages/Studio/StudioUploadQueue.jsx": __vite_glob_0_164, + "./Pages/Studio/StudioWorldEditor.jsx": __vite_glob_0_165, + "./Pages/Studio/StudioWorldsIndex.jsx": __vite_glob_0_166, + "./Pages/Upload/Index.jsx": __vite_glob_0_167, + "./Pages/World/WorldIndex.jsx": __vite_glob_0_168, + "./Pages/World/WorldShow.jsx": __vite_glob_0_169 }); const ClientOnlyPlaceholder = () => null; d( diff --git a/config/theme.php b/config/theme.php new file mode 100644 index 00000000..9a539b86 --- /dev/null +++ b/config/theme.php @@ -0,0 +1,12 @@ + env('LIGHT_THEME_ENABLED', false), + + // Whether the toolbar should render the light-theme switch. This is + // controlled separately so you can enable the theme without showing the + // global switch to visitors/admins. + 'show_toolbar_switch' => env('LIGHT_THEME_SHOW_SWITCH', false), +]; diff --git a/config/vision.php b/config/vision.php index 7bc57800..30e8b5fd 100644 --- a/config/vision.php +++ b/config/vision.php @@ -5,6 +5,22 @@ declare(strict_types=1); return [ 'enabled' => env('VISION_ENABLED', true), + 'auto_tagging' => [ + 'enabled' => env('VISION_AUTO_TAGGING_ENABLED', false), + ], + + 'upload' => [ + 'embeddings' => [ + 'enabled' => env('VISION_UPLOAD_EMBEDDINGS_ENABLED', true), + ], + 'maturity' => [ + 'enabled' => env('VISION_UPLOAD_MATURITY_ENABLED', false), + ], + 'ai_assist' => [ + 'enabled' => env('VISION_UPLOAD_AI_ASSIST_ENABLED', false), + ], + ], + 'queue' => env('VISION_QUEUE', 'default'), 'clip' => [ diff --git a/database/migrations/2026_06_06_120000_add_filled_examples_to_academy_prompt_templates_table.php b/database/migrations/2026_06_06_120000_add_filled_examples_to_academy_prompt_templates_table.php new file mode 100644 index 00000000..f8cba4c0 --- /dev/null +++ b/database/migrations/2026_06_06_120000_add_filled_examples_to_academy_prompt_templates_table.php @@ -0,0 +1,28 @@ +json('filled_examples')->nullable()->after('prompt_variants'); + } + }); + } + + public function down(): void + { + Schema::table('academy_prompt_templates', function (Blueprint $table): void { + if (Schema::hasColumn('academy_prompt_templates', 'filled_examples')) { + $table->dropColumn('filled_examples'); + } + }); + } +}; diff --git a/env b/env new file mode 100644 index 00000000..f1532ca1 --- /dev/null +++ b/env @@ -0,0 +1,275 @@ +APP_NAME=SkinbaseNova +APP_ENV=local +APP_KEY=base64:TAMmcAnL05vnhSV7wBoDoSc/Pv42LNQtX6B6lGc3HBk= +APP_DEBUG=true +APP_URL=http://skinbase26.test + +DEBUGBAR_ENABLED=true + +APP_LOCALE=en +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=en_US + +APP_MAINTENANCE_DRIVER=file +# APP_MAINTENANCE_STORE=database + +# PHP_CLI_SERVER_WORKERS=4 + +BCRYPT_ROUNDS=12 + +LOG_CHANNEL=stack +LOG_STACK=single +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=mysql +#DB_HOST=10.255.255.254 +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_DATABASE=projekti_2026_skinbase +DB_USERNAME=projekti +DB_PASSWORD=2Xf5TM3P1IeNTfhs + +LEGACY_DB_HOST=127.0.0.1 +LEGACY_DB_DATABASE=projekti_old_skinbase + +SESSION_DRIVER=database +SESSION_LIFETIME=3600 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=null + +BROADCAST_CONNECTION=reverb +FILESYSTEM_DISK=local +QUEUE_CONNECTION=redis + +CACHE_STORE=database +# CACHE_PREFIX= + +MEMCACHED_HOST=127.0.0.1 + +REDIS_CLIENT=predis +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=smtp +MAIL_HOST=smtp-pulse.com +MAIL_PORT=587 +MAIL_USERNAME=info@skinbase.org +MAIL_PASSWORD=ML2BBL958fdCMMc +MAIL_ENCRYPTION=tls +MAIL_FROM_ADDRESS='info@skinbase.org' +MAIL_FROM_NAME="Skinbase" + +AWS_ACCESS_KEY_ID=9d9292110fb4f68b2e4bc1fa55d6b2a3 +AWS_SECRET_ACCESS_KEY=0a1d8d8a38eb9a15ff23eac0c5e993c1 +AWS_DEFAULT_REGION=eu2 +AWS_BUCKET=skinbase +AWS_USE_PATH_STYLE_ENDPOINT=true +AWS_ENDPOINT=https://eu2.contabostorage.com + +VISION_VECTOR_GATEWAY_ENABLED=true +VISION_VECTOR_GATEWAY_URL=https://vision.klevze.net +VISION_VECTOR_GATEWAY_API_KEY=jQZ96c2B2QRjsFZiPZXMYCid6lVdsyxF +VISION_VECTOR_GATEWAY_COLLECTION=images +VISION_VECTOR_GATEWAY_TIMEOUT=20 +VISION_VECTOR_GATEWAY_CONNECT_TIMEOUT=5 +VISION_VECTOR_GATEWAY_RETRIES=1 +VISION_VECTOR_GATEWAY_RETRY_DELAY_MS=250 + +VITE_APP_NAME="${APP_NAME}" + +SKINBASE_STORAGE_ROOT=D:/Sites/Skinbase26/public/files/thumb +ARTWORKS_LOCAL_ORIGINALS_ROOT=D:/Sites/Skinbase26/public/files/originals + +SCOUT_DRIVER=meilisearch + +MEILISEARCH_HOST=https://meili.klevze.si +MEILISEARCH_KEY=0d0df27b-dd25-4855-b6a6-3786755475c6 +#MEILISEARCH_KEY=a474f24de92941aac24441b4d7ee71ce4feb8e7a3157d4f6e6a42877cb2a563c + +MEILI_PREFIX=skinbase_prod_ + + +# Discovery rollout profile (Phase 8 lock) +DISCOVERY_ALGO_VERSION=clip-cosine-v1 +DISCOVERY_V2_ENABLED=true +DISCOVERY_V2_ALGO_VERSION=clip-cosine-v2-adaptive +DISCOVERY_V2_CACHE_VERSION=cache-v2 +DISCOVERY_V2_CACHE_TTL_MINUTES=15 +DISCOVERY_V2_ROLLOUT_PERCENTAGE=10 +DISCOVERY_RANKING_WEIGHTS_VERSION_CLIP_COSINE_V2=rank-w-v2-prod-1 +DISCOVERY_RANKING_W1_CLIP_COSINE_V2=0.52 +DISCOVERY_RANKING_W2_CLIP_COSINE_V2=0.23 +DISCOVERY_RANKING_W3_CLIP_COSINE_V2=0.15 +DISCOVERY_RANKING_W4_CLIP_COSINE_V2=0.10 +DISCOVERY_ROLLOUT_ENABLED=true +DISCOVERY_ROLLOUT_BASELINE_ALGO_VERSION=clip-cosine-v1 +DISCOVERY_ROLLOUT_CANDIDATE_ALGO_VERSION=clip-cosine-v2 +DISCOVERY_ROLLOUT_ACTIVE_GATE=g10 +DISCOVERY_ROLLOUT_GATE_10_PERCENT=10 +DISCOVERY_ROLLOUT_GATE_50_PERCENT=50 +DISCOVERY_ROLLOUT_GATE_100_PERCENT=100 +DISCOVERY_FORCE_ALGO_VERSION= +DISCOVERY_EVAL_SAVE_RATE_INFORMATIONAL=true + +# Emergency rollback preset (uncomment to force baseline immediately) +# DISCOVERY_FORCE_ALGO_VERSION=clip-cosine-v1 +# DISCOVERY_ROLLOUT_ACTIVE_GATE=g10 +# DISCOVERY_ROLLOUT_ENABLED=true +UPLOAD_SCAN_ENABLED=false +UPLOAD_SCAN_COMMAND=clamscan +IMAGE_DRIVER=gd +SKINBASE_UPLOADS_V2=true + +# Vision / AI auto-tagging (local defaults) +VISION_ENABLED=true +VISION_QUEUE=default +VISION_IMAGE_VARIANT=lg +VISION_API_KEY=${VISION_VECTOR_GATEWAY_API_KEY} +CLIP_BASE_URL=https://vision.klevze.net +CLIP_ANALYZE_ENDPOINT=/analyze/clip +YOLO_BASE_URL=https://vision.klevze.net +YOLO_ANALYZE_ENDPOINT=/analyze/yolo + +VISION_GATEWAY_URL=https://vision.klevze.net +VISION_GATEWAY_API_KEY=${VISION_VECTOR_GATEWAY_API_KEY} +VISION_GATEWAY_TIMEOUT=60 +VISION_GATEWAY_CONNECT_TIMEOUT=5 + +SCOUT_QUEUE_CONNECTION=database +SCOUT_QUEUE_NAME=default + +# ─── Early-Stage Growth System ─────────────────────────────────────────────── +# Set NOVA_EARLY_GROWTH_ENABLED=false to instantly revert to normal behaviour. +# NOVA_EARLY_GROWTH_MODE: off | light | aggressive +NOVA_EARLY_GROWTH_ENABLED=true +NOVA_EARLY_GROWTH_MODE=aggressive + +# Module toggles (only active when NOVA_EARLY_GROWTH_ENABLED=true) +NOVA_EGS_ADAPTIVE_WINDOW=true +NOVA_EGS_GRID_FILLER=true +NOVA_EGS_SPOTLIGHT=true +NOVA_EGS_ACTIVITY_LAYER=false + +# AdaptiveTimeWindow thresholds +NOVA_EGS_UPLOADS_PER_DAY_NARROW=10 +NOVA_EGS_UPLOADS_PER_DAY_WIDE=3 +NOVA_EGS_WINDOW_NARROW_DAYS=7 +NOVA_EGS_WINDOW_MEDIUM_DAYS=30 +NOVA_EGS_WINDOW_WIDE_DAYS=90 + +# GridFiller minimum items per page +NOVA_EGS_GRID_MIN_RESULTS=12 + +# Auto-disable when site reaches organic scale +NOVA_EGS_AUTO_DISABLE=false +NOVA_EGS_AUTO_DISABLE_UPLOADS=50 +NOVA_EGS_AUTO_DISABLE_USERS=500 + +# Cache TTLs (seconds) +NOVA_EGS_SPOTLIGHT_TTL=3600 +NOVA_EGS_BLEND_TTL=300 +NOVA_EGS_WINDOW_TTL=600 +NOVA_EGS_ACTIVITY_TTL=1800 + +GOOGLE_CLIENT_ID="252720311278-fgjgrv3bue9upgqfp91ihbpunoqlpjvf.apps.googleusercontent.com" +GOOGLE_CLIENT_SECRET="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" +GOOGLE_REDIRECT_URI=https://skinbase26.test/auth/google/callback + +# Discord — https://discord.com/developers/applications +DISCORD_CLIENT_ID=1478852108869570731 +DISCORD_CLIENT_SECRET=k9OgyZrwNqT_UwZgwvHTRdEw8DXStKLN +DISCORD_REDIRECT_URI=https://skinbase26.test/auth/discord/callback + +CP_ENABLE_CORS=false + +BROADCAST_CONNECTION=reverb + +REVERB_APP_ID=376489 +REVERB_APP_KEY=jm0pq3ikcu3yequsbioc +REVERB_APP_SECRET=68sq4tc5lqhxuavxgqlt + +# internal Reverb server bind +REVERB_SERVER_HOST=127.0.0.1 +REVERB_SERVER_PORT=8080 + +# public host behind Cloudflare / Apache +REVERB_HOST=ws.skinbase.org +REVERB_PORT=443 +REVERB_SCHEME=https + +VITE_REVERB_APP_KEY="${REVERB_APP_KEY}" +VITE_REVERB_HOST="${REVERB_HOST}" +VITE_REVERB_PORT="${REVERB_PORT}" +VITE_REVERB_SCHEME="${REVERB_SCHEME}" + +MESSAGING_REALTIME=true + +CLOUDFLARE_ZONE_ID=2fead03fca715d3f44f567c671dc554d +CLOUDFLARE_API_TOKEN=cfut_Bd7THUxtJTHvOb66xDXhzp4uEm8IoaHZAkkOBnbVbcda524d +NOVA_CARDS_PUBLIC_DISK=s3 +NOVA_CARDS_PLAYWRIGHT_RENDER=true +AWS_URL=https://cdn.skinbase.org + +SEO_META_KEYWORDS=false + +#SENTRY_LARAVEL_DSN=https://f3774714982b12b53cfc3e70e1883595@o106088.ingest.us.sentry.io/4511307411816448 +#SENTRY_SEND_DEFAULT_PII=true +#SENTRY_TRACES_SAMPLE_RATE=1.0 + +SKINBASE_ACADEMY_ENABLED=true +SKINBASE_ACADEMY_PAYMENTS_ENABLED=true +SKINBASE_ACADEMY_CHALLENGES_ENABLED=true +SKINBASE_ACADEMY_BADGES_ENABLED=true + +# Stripe / Cashier +STRIPE_KEY=pk_test_51TYk1SBlXOyRoJYFUxa4PsycgqcfajPUMKCAFwXle5edjB2dIg7CvwO3upI6P83ya5blD4CvhSiStY0kP8jyJbAp00zn9cPlii +STRIPE_SECRET=sk_test_51TYk1SBlXOyRoJYFn0PvoXYvRa5KkGh5Q9PkMD3SgTKiBEibjnZsnZmKH098y38tQU8n14Fy1WyLrsuUAkgz1DtZ00MaOIwWBt +STRIPE_WEBHOOK_SECRET=whsec_IeFGaq7AK27RWwXXchyaWyPqSJ08cBsW +CASHIER_CURRENCY=eur +CASHIER_CURRENCY_LOCALE=sl_SI + +# Academy billing price IDs +ACADEMY_CREATOR_MONTHLY_PRICE_ID=price_xxx +ACADEMY_PRO_MONTHLY_PRICE_ID=price_1TYmkTBlXOyRoJYFfY8al4j2 + +ACADEMY_BILLING_ENABLED=true +ACADEMY_STRIPE_SUBSCRIPTION_NAME=academy + +# Registration anti-spam +REGISTRATION_IP_PER_MINUTE_LIMIT=3 +REGISTRATION_IP_PER_DAY_LIMIT=20 +REGISTRATION_EMAIL_PER_MINUTE_LIMIT=6 +REGISTRATION_EMAIL_COOLDOWN_MINUTES=30 +REGISTRATION_VERIFY_TOKEN_TTL_HOURS=24 +REGISTRATION_ENABLE_TURNSTILE=true +REGISTRATION_DISPOSABLE_DOMAINS_ENABLED=true +REGISTRATION_TURNSTILE_SUSPICIOUS_ATTEMPTS=2 +REGISTRATION_TURNSTILE_ATTEMPT_WINDOW_MINUTES=30 +REGISTRATION_EMAIL_GLOBAL_SEND_PER_MINUTE=30 +REGISTRATION_MONTHLY_EMAIL_LIMIT=10000 +TURNSTILE_SITE_KEY=0x4AAAAAADI6Ruu4X2IpmLrF +TURNSTILE_SECRET_KEY=0x4AAAAAADI6RlHFGscerV8DhIUwykRcbgE +TURNSTILE_VERIFY_URL=https://challenges.cloudflare.com/turnstile/v0/siteverify +TURNSTILE_TIMEOUT=5 + +TURNSTILE_ENABLED=true +TURNSTILE_FAIL_OPEN=false + +ENHANCE_DISK=public +ENHANCE_SOURCE_PREFIX=enhance/sources +ENHANCE_OUTPUT_PREFIX=enhance/outputs +ENHANCE_PREVIEW_PREFIX=enhance/previews +ENHANCE_ENGINE=stub +ENHANCE_MAX_UPLOAD_MB=20 +ENHANCE_MAX_INPUT_WIDTH=4096 +ENHANCE_MAX_INPUT_HEIGHT=4096 +ENHANCE_MAX_OUTPUT_WIDTH=8192 +ENHANCE_MAX_OUTPUT_HEIGHT=8192 +ENHANCE_DAILY_LIMIT=10 +ENHANCE_QUEUE=default +ENHANCE_WORKER_URL= +ENHANCE_WORKER_TIMEOUT=300 +ENHANCE_WORKER_TOKEN= diff --git a/resources/js/Layouts/AdminLayout.jsx b/resources/js/Layouts/AdminLayout.jsx index c845d237..5694895c 100644 --- a/resources/js/Layouts/AdminLayout.jsx +++ b/resources/js/Layouts/AdminLayout.jsx @@ -16,6 +16,7 @@ const buildAdminNavGroups = (isAdmin) => [ { label: 'All Users', href: '/moderation/users', icon: 'fa-solid fa-users' }, { label: 'Staff', href: '/moderation/users?role=admin', icon: 'fa-solid fa-shield-halved' }, { label: 'Moderators', href: '/moderation/users?role=moderator', icon: 'fa-solid fa-user-shield' }, + { label: 'Staff Applications', href: '/moderation/staff-applications', icon: 'fa-solid fa-user-check' }, ], }, { @@ -29,7 +30,6 @@ const buildAdminNavGroups = (isAdmin) => [ { label: 'Homepage Announcements', href: '/moderation/homepage/announcements', icon: 'fa-solid fa-bullhorn' }, { label: 'Upload Queue', href: '/moderation/uploads', icon: 'fa-solid fa-cloud-arrow-up' }, { label: 'Username Queue', href: '/moderation/usernames/moderation', icon: 'fa-solid fa-id-badge' }, - { label: 'AI Biography', href: '/moderation/ai-biography', icon: 'fa-solid fa-wand-magic-sparkles' }, ], }, { diff --git a/resources/js/Pages/Academy/Billing/Account.jsx b/resources/js/Pages/Academy/Billing/Account.jsx index 2aef09ae..6d797f39 100644 --- a/resources/js/Pages/Academy/Billing/Account.jsx +++ b/resources/js/Pages/Academy/Billing/Account.jsx @@ -1,5 +1,5 @@ -import React from 'react' -import { Head, Link } from '@inertiajs/react' +import React, { useState, useRef, useEffect } from 'react' +import { Head, Link, useForm, usePage } from '@inertiajs/react' import AccessBadge from '../../../components/academy/billing/AccessBadge' function formatDate(iso) { @@ -12,6 +12,72 @@ function formatDate(iso) { } export default function AcademyBillingAccount({ currentTier, isSubscribed, subscription, activePlan = null, links = {} }) { + const { flash, auth } = usePage().props + const { data, setData, post, processing } = useForm({ + issue_type: 'billing', + contact_email: auth?.user?.email || '', + message: '', + session_id: null, + }) + + function IssueTypeDropdown({ value, onChange }) { + const options = [ + { value: 'billing', label: 'Billing question' }, + { value: 'payment', label: 'Payment problem' }, + { value: 'upgrade', label: 'Upgrade problem' }, + { value: 'downgrade', label: 'Downgrade problem' }, + { value: 'cancel', label: 'Cancellation problem' }, + { value: 'access', label: 'Access not updated' }, + { value: 'other', label: 'Other' }, + ] + const [open, setOpen] = useState(false) + const ref = useRef(null) + + useEffect(() => { + function onDoc(e) { + if (ref.current && !ref.current.contains(e.target)) setOpen(false) + } + document.addEventListener('mousedown', onDoc) + return () => document.removeEventListener('mousedown', onDoc) + }, []) + + const current = options.find((o) => o.value === value) || options[0] + + return ( +
+ + + {open ? ( +
+ {options.map((opt) => ( + + ))} +
+ ) : null} +
+ ) + } + + function getCsrfToken() { + return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '' + } const endsAt = formatDate(subscription?.endsAt) const onGracePeriod = subscription?.onGracePeriod === true const subscriptionActive = subscription?.active === true @@ -21,6 +87,16 @@ export default function AcademyBillingAccount({ currentTier, isSubscribed, subsc
+ {flash?.error ? ( +
+

{flash.error}

+
+ ) : null} + {flash?.success ? ( +
+

{flash.success}

+
+ ) : null} {/* Header */}
@@ -42,12 +118,12 @@ export default function AcademyBillingAccount({ currentTier, isSubscribed, subsc

Your subscription was cancelled and will end on {endsAt}.

You still have full access until that date. Open the subscription portal to resume your plan if you change your mind.

- Resume subscription - +
) : null} @@ -123,20 +199,75 @@ export default function AcademyBillingAccount({ currentTier, isSubscribed, subsc