Files
SkinbaseNova/resources/js/Pages/Enhance/Show.jsx

144 lines
9.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React from 'react'
import { Head, Link, router, usePage } from '@inertiajs/react'
import BeforeAfterSlider from '../../components/enhance/BeforeAfterSlider'
import EnhanceStatusBadge from '../../components/enhance/EnhanceStatusBadge'
import EnhanceStubWarning from '../../components/enhance/EnhanceStubWarning'
import { formatEnhanceDate } from '../../utils/enhanceFormatting'
function formatDate(value) {
return formatEnhanceDate(value)
}
function DetailRow({ label, value }) {
return (
<div className="flex items-start justify-between gap-4 border-b border-white/[0.06] py-3 last:border-b-0 last:pb-0">
<dt className="text-sm text-slate-400">{label}</dt>
<dd className="text-right text-sm text-white">{value}</dd>
</div>
)
}
export default function EnhanceShow() {
const { props } = usePage()
const job = props.job || {}
const flash = props.flash || {}
const errors = props.errors || {}
const statusKey = String(job.status || '').toLowerCase()
const statusCopy = {
pending: 'Waiting to be queued.',
queued: 'Waiting for processor.',
processing: 'Enhancing image.',
completed: 'Enhanced image ready.',
failed: 'Enhancement failed.',
cancelled: 'Cancelled.',
expired: 'Enhanced output expired and cleaned files were removed.',
}[statusKey] || 'Unknown status.'
React.useEffect(() => {
if (!['pending', 'queued', 'processing'].includes(statusKey)) {
return undefined
}
const timer = window.setTimeout(() => {
router.reload({ only: ['job', 'flash'], preserveScroll: true })
}, 8000)
return () => window.clearTimeout(timer)
}, [statusKey])
const canCompare = Boolean(job.source_url && job.output_url && job.status === 'completed')
return (
<div className="w-full pb-16 pt-8">
<Head title={`Enhance Job #${job.id || ''}`} />
<section className="rounded-[32px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(14,165,233,0.18),transparent_36%),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)]">
<div className="flex flex-col gap-4 xl:flex-row xl:items-end xl:justify-between">
<div>
<div className="flex flex-wrap items-center gap-2">
<EnhanceStatusBadge status={job.status} />
<span className="rounded-full border border-white/10 bg-white/[0.05] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-200">{job.scale}x</span>
<span className="rounded-full border border-white/10 bg-white/[0.05] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-200">{job.mode}</span>
<span className="rounded-full border border-white/10 bg-white/[0.05] px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-200">{job.engine}</span>
</div>
<h1 className="mt-3 text-3xl font-semibold tracking-[-0.04em] text-white">Enhance job #{job.id}</h1>
<p className="mt-2 max-w-3xl text-sm leading-relaxed text-slate-300">{statusCopy}</p>
</div>
<div className="flex flex-wrap gap-3">
<Link href={props.indexUrl} className="inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.05] px-5 py-3 text-xs font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.09]">
<i className="fa-solid fa-arrow-left text-[10px]" />
Back to jobs
</Link>
<Link href={props.createUrl} className="inline-flex items-center gap-2 rounded-full 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">
<i className="fa-solid fa-plus text-[10px]" />
New enhance
</Link>
{job.download_url ? <a href={job.download_url} className="inline-flex items-center gap-2 rounded-full border border-emerald-300/20 bg-emerald-400/12 px-5 py-3 text-xs font-semibold uppercase tracking-[0.14em] text-emerald-50 transition hover:bg-emerald-400/20">Download enhanced</a> : null}
{job.can_retry ? <button type="button" onClick={() => router.post(job.retry_url, {}, { preserveScroll: true })} className="inline-flex items-center gap-2 rounded-full border border-amber-300/20 bg-amber-400/12 px-5 py-3 text-xs font-semibold uppercase tracking-[0.14em] text-amber-50 transition hover:bg-amber-400/20">Retry</button> : null}
{job.can_delete ? <button type="button" onClick={() => {
if (!window.confirm('Delete this enhance job and its generated files?')) return
router.delete(job.delete_url)
}} className="inline-flex items-center gap-2 rounded-full border border-rose-300/20 bg-rose-400/12 px-5 py-3 text-xs font-semibold uppercase tracking-[0.14em] text-rose-100 transition hover:bg-rose-400/20">Delete</button> : null}
</div>
</div>
</section>
<EnhanceStubWarning config={props.enhanceConfig} className="mt-6" />
{flash.success ? <div className="mt-6 rounded-2xl border border-emerald-300/20 bg-emerald-400/10 px-4 py-3 text-sm text-emerald-50">{flash.success}</div> : null}
{flash.error ? <div className="mt-6 rounded-2xl border border-rose-300/20 bg-rose-400/10 px-4 py-3 text-sm text-rose-100">{flash.error}</div> : null}
{errors.job ? <div className="mt-6 rounded-2xl border border-rose-300/20 bg-rose-400/10 px-4 py-3 text-sm text-rose-100">{errors.job}</div> : null}
{job.error_message ? <div className="mt-6 rounded-2xl border border-rose-300/20 bg-rose-400/10 px-4 py-3 text-sm text-rose-100">{job.error_message}</div> : null}
{canCompare ? <div className="mt-8"><BeforeAfterSlider beforeUrl={job.source_url} afterUrl={job.output_url} /></div> : null}
<div className="mt-8 grid gap-6 xl:grid-cols-[minmax(0,1.2fr)_380px]">
<div className="space-y-6">
<section className="rounded-[30px] border border-white/10 bg-[#08111d] p-6 shadow-[0_18px_48px_rgba(2,6,23,0.2)]">
<div className="grid gap-6 lg:grid-cols-2">
<div>
<div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400">Original source</div>
<div className="overflow-hidden rounded-[24px] border border-white/10 bg-black/20">
{job.source_url ? <img src={job.source_url} alt="Original source" className="w-full object-cover" /> : <div className="flex min-h-[280px] items-center justify-center text-white/20"><i className="fa-solid fa-image text-4xl" /></div>}
</div>
</div>
<div>
<div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400">Enhanced result</div>
<div className="overflow-hidden rounded-[24px] border border-white/10 bg-black/20">
{job.output_url ? <img src={job.output_url} alt="Enhanced output" className="w-full object-cover" /> : <div className="flex min-h-[280px] items-center justify-center text-white/20"><i className="fa-solid fa-hourglass-half text-4xl" /></div>}
</div>
</div>
</div>
</section>
<section className="rounded-[30px] border border-white/10 bg-[#08111d] p-6 shadow-[0_18px_48px_rgba(2,6,23,0.2)]">
<div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400">Metadata</div>
<div className="mt-4 rounded-[24px] border border-white/10 bg-black/20 p-4 text-sm text-slate-200">
<pre className="overflow-x-auto whitespace-pre-wrap break-words">{JSON.stringify(job.metadata || {}, null, 2)}</pre>
</div>
</section>
</div>
<aside className="rounded-[30px] border border-white/10 bg-[#08111d] p-6 shadow-[0_18px_48px_rgba(2,6,23,0.2)]">
<div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400">Job details</div>
<dl className="mt-4">
<DetailRow label="Created" value={formatDate(job.created_at)} />
<DetailRow label="Queued" value={formatDate(job.queued_at)} />
<DetailRow label="Started" value={formatDate(job.started_at)} />
<DetailRow label="Finished" value={formatDate(job.finished_at)} />
<DetailRow label="Expires" value={formatDate(job.expires_at)} />
<DetailRow label="Input size" value={job.input_filesize ? `${(job.input_filesize / 1024 / 1024).toFixed(2)} MB` : '—'} />
<DetailRow label="Input mime" value={job.input_mime || '—'} />
<DetailRow label="Input dimensions" value={job.input_width && job.input_height ? `${job.input_width} × ${job.input_height}` : '—'} />
<DetailRow label="Output size" value={job.output_filesize ? `${(job.output_filesize / 1024 / 1024).toFixed(2)} MB` : '—'} />
<DetailRow label="Output mime" value={job.output_mime || '—'} />
<DetailRow label="Output dimensions" value={job.output_width && job.output_height ? `${job.output_width} × ${job.output_height}` : '—'} />
<DetailRow label="Processing seconds" value={job.processing_seconds ?? '—'} />
<DetailRow label="Artwork" value={job.artwork?.title ? <a href={job.artwork.url} className="text-sky-300 hover:text-sky-200">{job.artwork.title}</a> : 'Standalone upload'} />
</dl>
</aside>
</div>
</div>
)
}