Remove dead admin UI code, redesign dashboard followers/following and upload experiences, and add schema audit tooling with repair migrations for forum and upload drift.
117 lines
4.2 KiB
JavaScript
117 lines
4.2 KiB
JavaScript
import React from 'react'
|
|
import UploadDropzone from '../UploadDropzone'
|
|
import ScreenshotUploader from '../ScreenshotUploader'
|
|
|
|
/**
|
|
* Step1FileUpload
|
|
*
|
|
* Step 1 of the upload wizard: file selection + live upload progress.
|
|
* Shows the dropzone, optional screenshot uploader (archives),
|
|
* and the progress panel once an upload is in flight.
|
|
*/
|
|
export default function Step1FileUpload({
|
|
headingRef,
|
|
// File state
|
|
primaryFile,
|
|
primaryPreviewUrl,
|
|
primaryErrors,
|
|
primaryWarnings,
|
|
fileMetadata,
|
|
fileSelectionLocked,
|
|
onPrimaryFileChange,
|
|
// Archive screenshots
|
|
isArchive,
|
|
screenshots,
|
|
screenshotErrors,
|
|
screenshotPerFileErrors,
|
|
onScreenshotsChange,
|
|
// Machine state (passed for potential future use)
|
|
machine,
|
|
}) {
|
|
return (
|
|
<div className="space-y-5 rounded-[28px] border border-white/10 bg-[linear-gradient(180deg,rgba(255,255,255,0.05),rgba(255,255,255,0.02))] p-6 shadow-[0_24px_90px_rgba(2,8,23,0.28)] backdrop-blur sm:p-7">
|
|
{/* Step header */}
|
|
<div className="rounded-2xl border border-white/8 bg-white/[0.04] px-5 py-4">
|
|
<h2
|
|
ref={headingRef}
|
|
tabIndex={-1}
|
|
className="text-lg font-semibold text-white focus:outline-none"
|
|
>
|
|
Upload your artwork
|
|
</h2>
|
|
<p className="mt-1 text-sm text-white/60">
|
|
Drop or browse a file. Validation runs immediately. Upload starts when you click
|
|
<span className="text-white/80">Start upload</span>.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="grid gap-3 sm:grid-cols-3">
|
|
{[
|
|
{
|
|
title: '1. Add the file',
|
|
body: 'Drop an image or archive pack into the upload area.',
|
|
},
|
|
{
|
|
title: '2. Check validation',
|
|
body: 'We flag unsupported formats, missing screenshots, and basic file issues immediately.',
|
|
},
|
|
{
|
|
title: '3. Start upload',
|
|
body: 'Once the file is clean, the secure processing pipeline takes over.',
|
|
},
|
|
].map((item) => (
|
|
<div key={item.title} className="rounded-2xl border border-white/8 bg-white/[0.03] p-4">
|
|
<p className="text-sm font-semibold text-white">{item.title}</p>
|
|
<p className="mt-2 text-sm leading-6 text-slate-300">{item.body}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Locked notice */}
|
|
{fileSelectionLocked && (
|
|
<div className="flex items-center gap-2 rounded-2xl bg-amber-500/10 px-4 py-3 text-xs text-amber-100 ring-1 ring-amber-300/30">
|
|
<svg className="h-3.5 w-3.5 shrink-0" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
<path fillRule="evenodd" d="M10 1a4.5 4.5 0 00-4.5 4.5V9H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-.5V5.5A4.5 4.5 0 0010 1zm3 8V5.5a3 3 0 10-6 0V9h6z" clipRule="evenodd" />
|
|
</svg>
|
|
File is locked after upload. Reset to change.
|
|
</div>
|
|
)}
|
|
|
|
{/* Primary dropzone */}
|
|
<UploadDropzone
|
|
title="Upload your artwork file"
|
|
description="Drag & drop or click to browse. Accepted: JPG, PNG, WEBP, ZIP, RAR, 7Z."
|
|
fileName={primaryFile?.name || ''}
|
|
previewUrl={primaryPreviewUrl}
|
|
fileMeta={fileMetadata}
|
|
fileHint="No file selected"
|
|
invalid={primaryErrors.length > 0}
|
|
errors={primaryErrors}
|
|
showLooksGood={Boolean(primaryFile) && primaryErrors.length === 0}
|
|
looksGoodText="Looks good"
|
|
locked={fileSelectionLocked}
|
|
onPrimaryFileChange={(file) => {
|
|
if (fileSelectionLocked) return
|
|
onPrimaryFileChange(file || null)
|
|
}}
|
|
/>
|
|
|
|
{/* Screenshots (archives only) */}
|
|
<ScreenshotUploader
|
|
title="Archive screenshots"
|
|
description="We need at least 1 screenshot to generate thumbnails and analyze content."
|
|
visible={isArchive}
|
|
files={screenshots}
|
|
min={1}
|
|
max={5}
|
|
perFileErrors={screenshotPerFileErrors}
|
|
errors={screenshotErrors}
|
|
invalid={isArchive && screenshotErrors.length > 0}
|
|
showLooksGood={isArchive && screenshots.length > 0 && screenshotErrors.length === 0}
|
|
looksGoodText="Looks good"
|
|
onFilesChange={onScreenshotsChange}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|