feat: ship creator journey v2 and profile updates

This commit is contained in:
2026-04-12 21:42:07 +02:00
parent a2457f4e49
commit d5cff21ea2
335 changed files with 20147 additions and 1545 deletions

View File

@@ -1,7 +1,8 @@
import React, { useState } from 'react'
import React, { useEffect, useState } from 'react'
import { router, usePage } from '@inertiajs/react'
import StudioLayout from '../../Layouts/StudioLayout'
import { studioSurface, trackStudioEvent } from '../../utils/studioEvents'
import { formatReleaseCountdown, formatScheduledDate } from '../../utils/scheduleCountdown'
async function requestJson(url, method = 'POST') {
const response = await fetch(url, {
@@ -24,13 +25,6 @@ async function requestJson(url, method = 'POST') {
return payload
}
function formatDate(value) {
if (!value) return 'Not scheduled'
const date = new Date(value)
if (Number.isNaN(date.getTime())) return 'Not scheduled'
return date.toLocaleString(undefined, { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit' })
}
export default function StudioScheduled() {
const { props } = usePage()
const listing = props.listing || {}
@@ -42,6 +36,7 @@ export default function StudioScheduled() {
const rangeOptions = listing.range_options || []
const endpoints = props.endpoints || {}
const [busyId, setBusyId] = useState(null)
const [nowMs, setNowMs] = useState(() => Date.now())
const updateFilters = (patch) => {
const next = { ...filters, ...patch }
@@ -84,6 +79,17 @@ export default function StudioScheduled() {
}
}
useEffect(() => {
const hasTimedEntries = Boolean(summary.next_publish_at) || items.some((item) => Boolean(item.scheduled_at || item.published_at))
if (!hasTimedEntries) return undefined
const timer = window.setInterval(() => {
setNowMs(Date.now())
}, 1000)
return () => window.clearInterval(timer)
}, [items, summary.next_publish_at])
return (
<StudioLayout title={props.title} subtitle={props.description}>
<div className="space-y-6">
@@ -96,7 +102,8 @@ export default function StudioScheduled() {
</div>
<div className="rounded-[24px] border border-white/10 bg-black/20 p-4 md:col-span-2">
<div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">Next publish slot</div>
<div className="mt-2 text-xl font-semibold text-white">{formatDate(summary.next_publish_at)}</div>
<div className="mt-2 text-xl font-semibold text-white">{formatReleaseCountdown(summary.next_publish_at, nowMs)}</div>
{summary.next_publish_at && <div className="mt-1 text-sm text-slate-400">{formatScheduledDate(summary.next_publish_at)}</div>}
</div>
</div>
@@ -172,9 +179,10 @@ export default function StudioScheduled() {
</div>
<h2 className="mt-2 text-xl font-semibold text-white">{item.title}</h2>
<div className="mt-2 flex flex-wrap items-center gap-4 text-sm text-slate-400">
<span>Scheduled for {formatDate(item.scheduled_at || item.published_at)}</span>
<span>{formatReleaseCountdown(item.scheduled_at || item.published_at, nowMs)}</span>
<span>{formatScheduledDate(item.scheduled_at || item.published_at)}</span>
{item.visibility && <span>Visibility: {item.visibility}</span>}
{item.updated_at && <span>Last edited {formatDate(item.updated_at)}</span>}
{item.updated_at && <span>Last edited {formatScheduledDate(item.updated_at)}</span>}
{item.schedule_timezone && <span>{item.schedule_timezone}</span>}
</div>
</div>