このコミットが含まれているのは:
2026-07-12 00:46:53 +09:00
コミット 4535a9d260
5個のファイルの変更54行の追加8行の削除
+19 -3
ファイルの表示
@@ -31,9 +31,11 @@ type ImportRow = {
tagSources?: Record<'automatic' | 'mapped' | 'fixed' | 'manual', string>
status: 'ready' | 'warning' | 'error'
existing: boolean
metadataUrl?: string
importStatus?: 'pending' | 'created' | 'skipped' | 'failed' }
type ParsedImport = { columns: Column[], rows: { sourceRow: number, values: string[] }[] }
type Phase = 'source' | 'mapping' | 'preview' | 'result'
type ImportResultStatus = 'created' | 'skipped' | 'failed'
const detectFormat = (source: string): 'csv' | 'tsv' =>
@@ -54,7 +56,7 @@ const PostImportPage: FC<Props> = ({ user }) => {
const [parsed, setParsed] = useState<ParsedImport | null> (null)
const [columns, setColumns] = useState<Column[]> ([])
const [rows, setRows] = useState<ImportRow[]> ([])
const [results, setResults] = useState<{ sourceRow: number, status: string, post?: { id: number }, errors?: Record<string, string[]> }[]> ([])
const [results, setResults] = useState<{ sourceRow: number, status: ImportResultStatus, post?: { id: number }, errors?: Record<string, string[]> }[]> ([])
const [mappings, setMappings] = useState<Record<string, { columns: string[] }>> ({ })
const [existing, setExisting] = useState<'skip' | 'error'> ('skip')
const [loading, setLoading] = useState (false)
@@ -181,7 +183,9 @@ const PostImportPage: FC<Props> = ({ user }) => {
try
{
const result = await apiPost<{ created: number, skipped: number, failed: number,
rows: { sourceRow: number, status: string, post?: { id: number }, errors?: Record<string, string[]> }[] }> ('/posts/import', { rows: targets, existing })
rows: { sourceRow: number, status: ImportResultStatus, post?: { id: number }, errors?: Record<string, string[]> }[] }> ('/posts/import', { rows: targets, existing })
if (result.rows.some (row => !(['created', 'skipped', 'failed'] as string[]).includes (row.status)))
throw new Error ('不正な登録結果です。')
toast ({ title: `登録完了: ${result.created}`,
description: `スキップ ${result.skipped} 件、失敗 ${result.failed}` })
setPhase ('result')
@@ -295,15 +299,17 @@ const PostImportPage: FC<Props> = ({ user }) => {
const RowCard = ({ row, index, update, updateURL }: { row: ImportRow, index: number, update: (index: number, field: string, value: string) => void, updateURL: (index: number, value: string) => void }) => (
<article className="space-y-2 rounded border bg-white p-3 text-neutral-900 dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-100">
<p> {row.sourceRow}{row.status}</p>
{row.importStatus === 'created' ? <p className="rounded bg-green-100 p-2 dark:bg-green-900"></p> : <>
<EditableValue label="URL" value={row.url} origin={row.provenance.url} onCommit={value => updateURL (index, value)}/>
<EditableValue label="タイトル" value={row.attributes.title ?? ''} origin={row.provenance.title} onCommit={value => update (index, 'title', value)}/>
<EditableValue label="サムネール基底 URL" value={row.attributes.thumbnailBase ?? ''} origin={row.provenance.thumbnailBase} onCommit={value => update (index, 'thumbnailBase', value)}/>
{row.attributes.thumbnailBase && <img src={row.attributes.thumbnailBase} alt="サムネール" className="max-h-32" onError={ev => { ev.currentTarget.hidden = true }}/>}
<ThumbnailPreview url={row.attributes.thumbnailBase ?? ''}/>
<EditableValue label="作成日時 From" value={row.attributes.originalCreatedFrom ?? ''} origin={row.provenance.originalCreatedFrom} onCommit={value => update (index, 'originalCreatedFrom', value)}/>
<EditableValue label="作成日時 Before" value={row.attributes.originalCreatedBefore ?? ''} origin={row.provenance.originalCreatedBefore} onCommit={value => update (index, 'originalCreatedBefore', value)}/>
<EditableValue label="動画時間" value={String (row.attributes.duration ?? '')} origin={row.provenance.duration} onCommit={value => update (index, 'duration', value)}/>
<EditableValue label="タグ" value={row.attributes.tags ?? ''} origin={row.provenance.tags} onCommit={value => update (index, 'tags', value)}/>
<EditableValue label="親投稿" value={row.attributes.parentPostIds ?? ''} origin={row.provenance.parentPostIds} onCommit={value => update (index, 'parentPostIds', value)}/>
</>}
<FieldError messages={[...row.warnings, ...Object.values (row.errors).flat ()]}/>
</article>)
@@ -324,4 +330,14 @@ const EditableValue = ({ label, value, origin, onCommit }: { label: string, valu
</div>
}
const ThumbnailPreview = ({ url }: { url: string }) => {
const [failed, setFailed] = useState (false)
useEffect (() => setFailed (false), [url])
if (!url)
return <p className="text-sm"></p>
if (failed)
return <p className="text-sm text-red-600 dark:text-red-300"></p>
return <img src={url} alt="サムネール" className="max-h-32" onError={() => setFailed (true)}/>
}
export default PostImportPage