199 行
7.5 KiB
TypeScript
199 行
7.5 KiB
TypeScript
import FieldError from '@/components/common/FieldError'
|
|
import { Button } from '@/components/ui/button'
|
|
import PostImportThumbnailPreview from '@/components/posts/import/PostImportThumbnailPreview'
|
|
import PostImportStatusBadge from '@/components/posts/import/PostImportStatusBadge'
|
|
import { displayPostImportStatus } from '@/components/posts/import/postImportRowStatus'
|
|
import { hasVideoTag } from '@/lib/postImportSession'
|
|
import { canEditReviewRow, canRetryResultRow } from '@/lib/postImportSession'
|
|
import { cn, originalCreatedAtString } from '@/lib/utils'
|
|
|
|
import type { FC } from 'react'
|
|
|
|
import type { PostImportRow } from '@/lib/postImportSession'
|
|
|
|
type Props = {
|
|
row: PostImportRow
|
|
displayNumber?: number
|
|
onEdit?: () => void
|
|
onRetry?: () => void
|
|
onToggleSkip?: (checked: boolean) => void
|
|
rowMessages?: string[]
|
|
editDisabled?: boolean
|
|
retryDisabled?: boolean
|
|
skipDisabled?: boolean
|
|
showActions?: boolean
|
|
showSkipToggle?: boolean }
|
|
|
|
const summaryWarning = (row: PostImportRow): string | null =>
|
|
Object.values (row.fieldWarnings ?? { }).flat ()[0]
|
|
?? row.baseWarnings?.[0]
|
|
?? null
|
|
|
|
const summaryDate = (row: PostImportRow): string =>
|
|
originalCreatedAtString (
|
|
row.attributes.originalCreatedFrom?.toString () ?? null,
|
|
row.attributes.originalCreatedBefore?.toString () ?? null)
|
|
|
|
|
|
const PostImportRowSummary: FC<Props> = (
|
|
{ row,
|
|
displayNumber,
|
|
onEdit,
|
|
onRetry,
|
|
onToggleSkip,
|
|
rowMessages,
|
|
editDisabled,
|
|
retryDisabled,
|
|
skipDisabled,
|
|
showActions = true,
|
|
showSkipToggle = false },
|
|
) => {
|
|
const warning = summaryWarning (row)
|
|
const displayStatus = displayPostImportStatus (row)
|
|
const editVisible = onEdit != null
|
|
const editAllowed = editVisible && canEditReviewRow (row)
|
|
const retryAllowed = onRetry != null && canRetryResultRow (row)
|
|
const skipChecked = row.skipReason === 'manual'
|
|
const rowNumber = displayNumber ?? row.sourceRow
|
|
const duration = String (row.attributes.duration ?? '')
|
|
const showDuration = hasVideoTag (row.attributes.tags) && duration !== ''
|
|
const skipControl = showSkipToggle
|
|
? (
|
|
<label className="flex items-center gap-2 text-sm">
|
|
<input
|
|
type="checkbox"
|
|
checked={skipChecked}
|
|
onChange={event => onToggleSkip?.(event.target.checked)}
|
|
disabled={skipDisabled === true}/>
|
|
<span>スキップ</span>
|
|
</label>)
|
|
: null
|
|
|
|
return (
|
|
<>
|
|
<div
|
|
className={cn (
|
|
'hidden items-center gap-4 rounded-lg border p-4 md:grid',
|
|
'md:grid-cols-[4rem_5rem_minmax(0,1fr)_auto_auto]',
|
|
'transition-shadow hover:shadow-sm')}>
|
|
<div className="space-y-1">
|
|
<div className="text-sm font-medium">#{rowNumber}</div>
|
|
</div>
|
|
<PostImportThumbnailPreview
|
|
url={String (row.attributes.thumbnailBase ?? '')}
|
|
file={row.thumbnailFile}
|
|
className="h-16 w-16"/>
|
|
<div className="min-w-0 space-y-1">
|
|
<div className="line-clamp-2 text-sm font-medium">
|
|
{String (row.attributes.title ?? '')}
|
|
</div>
|
|
<div className="truncate text-xs text-neutral-600 dark:text-neutral-300">
|
|
{row.url}
|
|
</div>
|
|
{String (row.attributes.tags ?? '') && (
|
|
<div className="truncate text-xs text-neutral-500 dark:text-neutral-400">
|
|
{String (row.attributes.tags ?? '')}
|
|
</div>)}
|
|
<div className="text-xs text-neutral-500 dark:text-neutral-400">
|
|
{summaryDate (row)}
|
|
</div>
|
|
{showDuration && (
|
|
<div className="text-xs text-neutral-500 dark:text-neutral-400">
|
|
動画時間 {duration}
|
|
</div>)}
|
|
{warning && (
|
|
<div className="text-xs text-amber-700 dark:text-amber-200">
|
|
{warning}
|
|
</div>)}
|
|
<FieldError messages={rowMessages}/>
|
|
</div>
|
|
<div className="space-y-1">
|
|
{displayStatus != null && <PostImportStatusBadge value={displayStatus}/>}
|
|
</div>
|
|
<div className="flex justify-end">
|
|
<div className="flex items-center gap-2">
|
|
{skipControl}
|
|
{showActions && editVisible && (
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
onClick={onEdit}
|
|
disabled={editDisabled === true || !(editAllowed)}>
|
|
編輯
|
|
</Button>)}
|
|
{showActions && retryAllowed && (
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
onClick={onRetry}
|
|
disabled={retryDisabled === true}>
|
|
再試行
|
|
</Button>)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
className={cn (
|
|
'space-y-3 rounded-lg border p-4 md:hidden',
|
|
'transition-shadow hover:shadow-sm')}>
|
|
<div className="text-sm font-medium">#{rowNumber}</div>
|
|
<div className="flex items-start gap-3">
|
|
<PostImportThumbnailPreview
|
|
url={String (row.attributes.thumbnailBase ?? '')}
|
|
file={row.thumbnailFile}
|
|
className="h-20 w-20 shrink-0"/>
|
|
<div className="min-w-0 flex-1 space-y-2">
|
|
<div className="line-clamp-2 text-sm font-medium">
|
|
{String (row.attributes.title ?? '')}
|
|
</div>
|
|
<div className="truncate text-xs text-neutral-600 dark:text-neutral-300">
|
|
{row.url}
|
|
</div>
|
|
<div className="flex flex-wrap gap-2">
|
|
{displayStatus != null && <PostImportStatusBadge value={displayStatus}/>}
|
|
</div>
|
|
{String (row.attributes.tags ?? '') && (
|
|
<div className="text-xs text-neutral-500 dark:text-neutral-400">
|
|
{String (row.attributes.tags ?? '')}
|
|
</div>)}
|
|
<div className="text-xs text-neutral-500 dark:text-neutral-400">
|
|
{summaryDate (row)}
|
|
</div>
|
|
{showDuration && (
|
|
<div className="text-xs text-neutral-500 dark:text-neutral-400">
|
|
動画時間 {duration}
|
|
</div>)}
|
|
{warning && (
|
|
<div className="text-xs text-amber-700 dark:text-amber-200">
|
|
{warning}
|
|
</div>)}
|
|
<FieldError messages={rowMessages}/>
|
|
{skipControl}
|
|
</div>
|
|
</div>
|
|
{showActions && (editVisible || retryAllowed) && (
|
|
<div className="flex flex-col gap-2 sm:flex-row">
|
|
{editVisible && (
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
onClick={onEdit}
|
|
disabled={editDisabled === true || !(editAllowed)}>
|
|
編輯
|
|
</Button>)}
|
|
{retryAllowed && (
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
onClick={onRetry}
|
|
disabled={retryDisabled === true}>
|
|
再試行
|
|
</Button>)}
|
|
</div>)}
|
|
</div>
|
|
</>)
|
|
}
|
|
|
|
export default PostImportRowSummary
|