feat: forum rich-text editor, emoji picker, mentions, discover nav, feed, uploads, profile
Forum: - TipTap WYSIWYG editor with full toolbar - @emoji-mart/react emoji picker (consistent with tweets) - @mention autocomplete with user search API - Fix PHP 8.4 parse errors in Blade templates - Fix thread data display (paginator items) - Align forum page widths to max-w-5xl Discover: - Extract shared _nav.blade.php partial - Add missing nav links to for-you page - Add Following link for authenticated users Feed/Posts: - Post model, controllers, policies, migrations - Feed page components (PostComposer, FeedCard, etc) - Post reactions, comments, saves, reports, sharing - Scheduled publishing support - Link preview controller Profile: - Profile page components (ProfileHero, ProfileTabs) - Profile API controller Uploads: - Upload wizard enhancements - Scheduled publish picker - Studio status bar and readiness checklist
This commit is contained in:
@@ -24,7 +24,11 @@ function PublishCheckBadge({ label, ok }) {
|
||||
* Step3Publish
|
||||
*
|
||||
* Step 3 of the upload wizard: review summary and publish action.
|
||||
* Shows a compact artwork preview, metadata summary, and readiness badges.
|
||||
* Shows a compact artwork preview, metadata summary, readiness badges,
|
||||
* and a summary of publish mode / schedule + visibility.
|
||||
*
|
||||
* Publish controls (mode/schedule picker) live in PublishPanel (sidebar).
|
||||
* This step serves as the final review before the user clicks Publish.
|
||||
*/
|
||||
export default function Step3Publish({
|
||||
headingRef,
|
||||
@@ -39,17 +43,36 @@ export default function Step3Publish({
|
||||
// Readiness
|
||||
canPublish,
|
||||
uploadReady,
|
||||
// Publish options (from wizard state, for summary display only)
|
||||
publishMode = 'now',
|
||||
scheduledAt = null,
|
||||
timezone = null,
|
||||
visibility = 'public',
|
||||
// Category tree (for label lookup)
|
||||
allRootCategoryOptions = [],
|
||||
filteredCategoryTree = [],
|
||||
}) {
|
||||
const prefersReducedMotion = useReducedMotion()
|
||||
const quickTransition = prefersReducedMotion ? { duration: 0 } : { duration: 0.2, ease: 'easeOut' }
|
||||
|
||||
const hasPreview = Boolean(primaryPreviewUrl && !isArchive)
|
||||
|
||||
// ── Category label lookup ────────────────────────────────────────────────
|
||||
const rootCategory = allRootCategoryOptions.find(
|
||||
(r) => String(r.id) === String(metadata.rootCategoryId)
|
||||
) ?? null
|
||||
const rootLabel = rootCategory?.name ?? null
|
||||
const subCategory = rootCategory?.children?.find(
|
||||
(c) => String(c.id) === String(metadata.subCategoryId)
|
||||
) ?? null
|
||||
const subLabel = subCategory?.name ?? null
|
||||
|
||||
const checks = [
|
||||
{ label: 'File uploaded', ok: uploadReady },
|
||||
{ label: 'Scan passed', ok: uploadReady },
|
||||
{ label: 'Preview ready', ok: hasPreview || (isArchive && screenshots.length > 0) },
|
||||
{ label: 'Rights confirmed', ok: Boolean(metadata.rightsAccepted) },
|
||||
{ label: 'Tags added', ok: Array.isArray(metadata.tags) && metadata.tags.length > 0 },
|
||||
]
|
||||
|
||||
return (
|
||||
@@ -104,11 +127,11 @@ export default function Step3Publish({
|
||||
{metadata.contentType && (
|
||||
<span className="capitalize">Type: <span className="text-white/75">{metadata.contentType}</span></span>
|
||||
)}
|
||||
{metadata.rootCategoryId && (
|
||||
<span>Category: <span className="text-white/75">{metadata.rootCategoryId}</span></span>
|
||||
{rootLabel && (
|
||||
<span>Category: <span className="text-white/75">{rootLabel}</span></span>
|
||||
)}
|
||||
{metadata.subCategoryId && (
|
||||
<span>Sub: <span className="text-white/75">{metadata.subCategoryId}</span></span>
|
||||
{subLabel && (
|
||||
<span>Sub: <span className="text-white/75">{subLabel}</span></span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -129,6 +152,32 @@ export default function Step3Publish({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Publish summary: visibility + schedule */}
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<span className="inline-flex items-center gap-1.5 rounded-full border border-white/15 bg-white/6 px-2.5 py-1 text-xs text-white/60">
|
||||
👁 {visibility === 'public' ? 'Public' : visibility === 'unlisted' ? 'Unlisted' : 'Private'}
|
||||
</span>
|
||||
{publishMode === 'schedule' && scheduledAt ? (
|
||||
<span className="inline-flex items-center gap-1.5 rounded-full border border-violet-300/30 bg-violet-500/15 px-2.5 py-1 text-xs text-violet-200">
|
||||
🕐 Scheduled
|
||||
{timezone && (
|
||||
<span className="text-violet-300/70">
|
||||
{' '}·{' '}
|
||||
{new Intl.DateTimeFormat('en-GB', {
|
||||
timeZone: timezone,
|
||||
weekday: 'short', day: 'numeric', month: 'short',
|
||||
hour: '2-digit', minute: '2-digit', hour12: false,
|
||||
}).format(new Date(scheduledAt))}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
) : (
|
||||
<span className="inline-flex items-center gap-1.5 rounded-full border border-emerald-300/30 bg-emerald-500/12 px-2.5 py-1 text-xs text-emerald-200">
|
||||
⚡ Publish immediately
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Readiness badges */}
|
||||
<div>
|
||||
<p className="mb-2.5 text-xs uppercase tracking-wide text-white/40">Readiness checks</p>
|
||||
|
||||
Reference in New Issue
Block a user