Wire admin studio SSR and search infrastructure
This commit is contained in:
@@ -130,7 +130,6 @@ export default function UploadWizard({
|
||||
const [publishMode, setPublishMode] = useState('now') // 'now' | 'schedule'
|
||||
const [scheduledAt, setScheduledAt] = useState(null) // UTC ISO or null
|
||||
const [visibility, setVisibility] = useState('public') // 'public'|'unlisted'|'private'
|
||||
const [showMobilePublishPanel, setShowMobilePublishPanel] = useState(false)
|
||||
const userTimezone = useMemo(() => {
|
||||
try { return Intl.DateTimeFormat().resolvedOptions().timeZone } catch { return 'UTC' }
|
||||
}, [])
|
||||
@@ -393,6 +392,8 @@ export default function UploadWizard({
|
||||
return true
|
||||
}, [canPublish, reviewSubmissionMode, publishMode, scheduledAt])
|
||||
|
||||
const publishActionEnabled = activeStep === 3 && canScheduleSubmit
|
||||
|
||||
// ── Validation surface for parent ────────────────────────────────────────
|
||||
const validationErrors = useMemo(
|
||||
() => [...primaryErrors, ...screenshotErrors],
|
||||
@@ -437,13 +438,6 @@ export default function UploadWizard({
|
||||
clearPolling()
|
||||
}
|
||||
}, [abortAllRequests, clearPolling])
|
||||
// ── ESC key closes mobile drawer (spec §7) ─────────────────────────────
|
||||
useEffect(() => {
|
||||
if (!showMobilePublishPanel) return
|
||||
const handler = (e) => { if (e.key === 'Escape') setShowMobilePublishPanel(false) }
|
||||
document.addEventListener('keydown', handler)
|
||||
return () => document.removeEventListener('keydown', handler)
|
||||
}, [showMobilePublishPanel])
|
||||
// ── Metadata helpers ──────────────────────────────────────────────────────
|
||||
const setMeta = useCallback((patch) => setMetadata((prev) => ({ ...prev, ...patch })), [])
|
||||
|
||||
@@ -459,7 +453,6 @@ export default function UploadWizard({
|
||||
setPublishMode('now')
|
||||
setScheduledAt(null)
|
||||
setVisibility('public')
|
||||
setShowMobilePublishPanel(false)
|
||||
setResolvedArtworkId(() => {
|
||||
const parsed = Number(initialDraftId)
|
||||
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : null
|
||||
@@ -705,11 +698,15 @@ export default function UploadWizard({
|
||||
return machine.error || 'Publish is available when upload is ready and rights are confirmed.'
|
||||
})()
|
||||
|
||||
const publishActionTitle = activeStep < 3
|
||||
? 'Continue to the final publish step to choose Worlds and publish.'
|
||||
: disableReason
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
return (
|
||||
<section
|
||||
ref={stepContentRef}
|
||||
className="space-y-5 pb-32 text-white lg:pb-8"
|
||||
className="space-y-5 pb-40 text-white lg:pb-40"
|
||||
data-is-archive={isArchive ? 'true' : 'false'}
|
||||
>
|
||||
{notices.length > 0 && (
|
||||
@@ -796,7 +793,7 @@ export default function UploadWizard({
|
||||
step={activeStep}
|
||||
canStart={canStartUpload && [machineStates.idle, machineStates.error, machineStates.cancelled].includes(machine.state)}
|
||||
canContinue={detailsValid}
|
||||
canPublish={canScheduleSubmit}
|
||||
canPublish={publishActionEnabled}
|
||||
canGoBack={activeStep > 1 && machine.state !== machineStates.complete}
|
||||
canReset={Boolean(primaryFile || screenshots.length || metadata.title || metadata.description || metadata.tags.length)}
|
||||
canCancel={activeStep === 1 && [
|
||||
@@ -813,7 +810,7 @@ export default function UploadWizard({
|
||||
disableReason={disableReason}
|
||||
onStart={runUploadFlow}
|
||||
onContinue={() => detailsValid && setActiveStep(3)}
|
||||
onPublish={() => handlePublish(canPublish, { mode: reviewSubmissionMode ? 'now' : publishMode, publishAt: reviewSubmissionMode ? null : scheduledAt, timezone: userTimezone, visibility, action: reviewSubmissionMode ? 'submit_review' : 'publish' })}
|
||||
onPublish={() => publishActionEnabled && handlePublish(canPublish, { mode: reviewSubmissionMode ? 'now' : publishMode, publishAt: reviewSubmissionMode ? null : scheduledAt, timezone: userTimezone, visibility, action: reviewSubmissionMode ? 'submit_review' : 'publish' })}
|
||||
onBack={() => setActiveStep((s) => Math.max(1, s - 1))}
|
||||
onCancel={handleCancel}
|
||||
onReset={handleReset}
|
||||
@@ -841,6 +838,8 @@ export default function UploadWizard({
|
||||
machineState={machine.state}
|
||||
uploadReady={uploadReady}
|
||||
canPublish={canPublish}
|
||||
publishActionEnabled={publishActionEnabled}
|
||||
publishActionTitle={publishActionTitle}
|
||||
isPublishing={machine.state === machineStates.publishing}
|
||||
isArchiveRequiresScreenshot={isArchive}
|
||||
publishMode={publishMode}
|
||||
@@ -864,101 +863,6 @@ export default function UploadWizard({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* ── Mobile: floating "Publish" button that opens bottom sheet ────── */}
|
||||
{(primaryFile || resolvedArtworkId) && machine.state !== machineStates.complete && activeStep > 1 && (
|
||||
<div className="fixed bottom-4 right-4 z-30 lg:hidden">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Open publish panel"
|
||||
onClick={() => setShowMobilePublishPanel((v) => !v)}
|
||||
className="flex items-center gap-2 rounded-full border border-sky-300/20 bg-sky-500 px-4 py-2.5 text-sm font-semibold text-white shadow-[0_18px_50px_rgba(14,165,233,0.35)] transition hover:bg-sky-400 active:scale-95"
|
||||
>
|
||||
<svg className="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
{reviewSubmissionMode ? 'Review' : 'Publish'}
|
||||
{!canPublish && (
|
||||
<span className="ml-1 rounded-full bg-white/20 px-1.5 text-[10px]">
|
||||
{[
|
||||
...(!uploadReady ? [1] : []),
|
||||
...(hasTitle ? [] : [1]),
|
||||
...(hasCompleteCategory ? [] : [1]),
|
||||
...(hasTag ? [] : [1]),
|
||||
...(hasRequiredScreenshot ? [] : [1]),
|
||||
...(metadata.rightsAccepted ? [] : [1]),
|
||||
].length}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ── Mobile Publish panel bottom-sheet overlay ────────────────────── */}
|
||||
<AnimatePresence>
|
||||
{showMobilePublishPanel && (
|
||||
<>
|
||||
{/* Backdrop */}
|
||||
<motion.div
|
||||
key="mobile-panel-backdrop"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className="fixed inset-0 z-40 bg-black/60 backdrop-blur-sm lg:hidden"
|
||||
onClick={() => setShowMobilePublishPanel(false)}
|
||||
/>
|
||||
{/* Sheet */}
|
||||
<motion.div
|
||||
key="mobile-panel-sheet"
|
||||
initial={{ y: '100%' }}
|
||||
animate={{ y: 0 }}
|
||||
exit={{ y: '100%' }}
|
||||
transition={prefersReducedMotion ? { duration: 0 } : { type: 'spring', damping: 30, stiffness: 300 }}
|
||||
className="fixed bottom-0 left-0 right-0 z-50 max-h-[80vh] overflow-y-auto rounded-t-2xl bg-slate-900 ring-1 ring-white/10 p-5 pb-8 lg:hidden"
|
||||
>
|
||||
<div className="mx-auto mb-4 h-1 w-12 rounded-full bg-white/20" aria-hidden="true" />
|
||||
<PublishPanel
|
||||
primaryPreviewUrl={primaryPreviewUrl}
|
||||
isArchive={isArchive}
|
||||
screenshots={screenshots}
|
||||
selectedScreenshotIndex={selectedScreenshotIndex}
|
||||
onSelectedScreenshotChange={setSelectedScreenshotIndex}
|
||||
metadata={metadata}
|
||||
machineState={machine.state}
|
||||
uploadReady={uploadReady}
|
||||
canPublish={canPublish}
|
||||
isPublishing={machine.state === machineStates.publishing}
|
||||
isArchiveRequiresScreenshot={isArchive}
|
||||
publishMode={publishMode}
|
||||
scheduledAt={scheduledAt}
|
||||
timezone={userTimezone}
|
||||
visibility={visibility}
|
||||
actionLabel={publishActionLabel}
|
||||
showScheduleControls={!reviewSubmissionMode}
|
||||
showRightsConfirmation={activeStep === 3}
|
||||
showVisibility={false}
|
||||
onPublishModeChange={setPublishMode}
|
||||
onScheduleAt={setScheduledAt}
|
||||
onVisibilityChange={setVisibility}
|
||||
onToggleRights={(checked) => setMeta({ rightsAccepted: Boolean(checked) })}
|
||||
onPublish={() => {
|
||||
setShowMobilePublishPanel(false)
|
||||
handlePublish(canPublish, { mode: reviewSubmissionMode ? 'now' : publishMode, publishAt: reviewSubmissionMode ? null : scheduledAt, timezone: userTimezone, visibility, action: reviewSubmissionMode ? 'submit_review' : 'publish' })
|
||||
}}
|
||||
onCancel={() => {
|
||||
setShowMobilePublishPanel(false)
|
||||
handleCancel()
|
||||
}}
|
||||
onGoToStep={(s) => {
|
||||
setShowMobilePublishPanel(false)
|
||||
goToStep(s)
|
||||
}}
|
||||
allRootCategoryOptions={allRootCategoryOptions}
|
||||
/>
|
||||
</motion.div>
|
||||
</>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user