このコミットが含まれているのは:
@@ -73,7 +73,7 @@ const PostImportRowDialog: FC<Props> = (
|
||||
const [resetRequested, setResetRequested] = useState (false)
|
||||
|
||||
useEffect (() => {
|
||||
if (open && row)
|
||||
if (open && row != null)
|
||||
{
|
||||
setDraft (buildDraft (row))
|
||||
setResetRequested (false)
|
||||
@@ -90,7 +90,7 @@ const PostImportRowDialog: FC<Props> = (
|
||||
value: Draft[Key],
|
||||
) => {
|
||||
setDraft (current =>
|
||||
current ? { ...current, [key]: value } : current)
|
||||
current != null ? { ...current, [key]: value } : current)
|
||||
}
|
||||
|
||||
const save = async () => {
|
||||
|
||||
@@ -14,6 +14,9 @@ const buildResetSnapshot = (row: PostImportRow) => ({
|
||||
tagSources: {
|
||||
automatic: row.tagSources?.automatic ?? '',
|
||||
manual: row.tagSources?.manual ?? '' },
|
||||
fieldWarnings: Object.fromEntries (
|
||||
Object.entries (row.fieldWarnings).map (([key, values]) => [key, [...values]])),
|
||||
baseWarnings: [...row.baseWarnings],
|
||||
metadataUrl: row.metadataUrl })
|
||||
|
||||
|
||||
@@ -73,8 +76,7 @@ export const mergeValidatedImportRows = (
|
||||
if (
|
||||
previous.importStatus === 'created'
|
||||
|| previous.importStatus === 'skipped'
|
||||
|| previous.importStatus === 'failed'
|
||||
)
|
||||
|| previous.importStatus === 'failed')
|
||||
return previous
|
||||
|
||||
const row = validatedMap.get (previous.sourceRow)
|
||||
|
||||
@@ -21,6 +21,7 @@ const ATTRIBUTE_KEYS = [
|
||||
'parentPostIds'] as const
|
||||
const PROVENANCE_KEYS = [...ATTRIBUTE_KEYS, 'url'] as const
|
||||
const TAG_SOURCE_KEYS = ['automatic', 'manual'] as const
|
||||
const WARNING_KEYS = [...ATTRIBUTE_KEYS, 'url'] as const
|
||||
|
||||
|
||||
const isPlainObject = (value: unknown): value is Record<string, unknown> =>
|
||||
@@ -162,6 +163,14 @@ const sanitiseResetSnapshot = (value: unknown): PostImportResetSnapshot | null =
|
||||
return null
|
||||
if (!(Object.values (value.tagSources).every (_1 => typeof _1 === 'string')))
|
||||
return null
|
||||
const fieldWarnings = ensureStringListRecord (value.fieldWarnings)
|
||||
if (fieldWarnings == null)
|
||||
return null
|
||||
if (!(hasOnlyKeys (fieldWarnings, WARNING_KEYS)))
|
||||
return null
|
||||
if (!(Array.isArray (value.baseWarnings))
|
||||
|| !(value.baseWarnings.every (_1 => typeof _1 === 'string')))
|
||||
return null
|
||||
if (value.metadataUrl != null && typeof value.metadataUrl !== 'string')
|
||||
return null
|
||||
|
||||
@@ -170,6 +179,8 @@ const sanitiseResetSnapshot = (value: unknown): PostImportResetSnapshot | null =
|
||||
attributes: value.attributes as Record<string, string | number>,
|
||||
provenance: value.provenance as Record<string, PostImportOrigin>,
|
||||
tagSources: value.tagSources as Record<PostImportOrigin, string>,
|
||||
fieldWarnings,
|
||||
baseWarnings: value.baseWarnings,
|
||||
metadataUrl: value.metadataUrl as string | undefined }
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ export type PostImportResetSnapshot = {
|
||||
attributes: Record<string, PostImportAttributeValue>
|
||||
provenance: Record<string, PostImportOrigin>
|
||||
tagSources: Record<PostImportOrigin, string>
|
||||
fieldWarnings: Record<string, string[]>
|
||||
baseWarnings: string[]
|
||||
metadataUrl?: string }
|
||||
|
||||
export type PostImportRow = {
|
||||
@@ -48,7 +50,8 @@ export type PostImportResultRow =
|
||||
| {
|
||||
sourceRow: number
|
||||
status: 'failed'
|
||||
errors?: Record<string, string[]> }
|
||||
errors?: Record<string, string[]>
|
||||
recoverable?: boolean }
|
||||
|
||||
export type PostImportSession = {
|
||||
version: number
|
||||
|
||||
@@ -120,10 +120,36 @@ const PostImportResultPage: FC<Props> = ({ user }) => {
|
||||
provenance: target.provenance,
|
||||
tagSources: target.tagSources,
|
||||
metadataUrl: target.metadataUrl }] })
|
||||
setSession (current =>
|
||||
current
|
||||
? { ...current, rows: mergeImportResults (current.rows, result.rows) }
|
||||
: current)
|
||||
const mergedRows = mergeImportResults (nextSession.rows, result.rows)
|
||||
const recoverableRows = result.rows.filter (row =>
|
||||
row.status === 'failed'
|
||||
&& row.recoverable
|
||||
&& Object.keys (row.errors ?? { }).length > 0)
|
||||
const nextRows = mergedRows.map (row => {
|
||||
const recoverable = recoverableRows.find (_1 => _1.sourceRow === row.sourceRow)
|
||||
if (recoverable == null)
|
||||
return row
|
||||
return {
|
||||
...row,
|
||||
importStatus: 'pending',
|
||||
validationErrors: recoverable.errors ?? { },
|
||||
importErrors: undefined }
|
||||
})
|
||||
const recoverableTarget = nextRows.find (_1 => _1.sourceRow === sourceRow)
|
||||
const resultSession = {
|
||||
...nextSession,
|
||||
rows: nextRows,
|
||||
repairMode: recoverableTarget == null ? 'all' as const : 'failed' as const }
|
||||
setSession (resultSession)
|
||||
if (recoverableTarget != null
|
||||
&& Object.keys (recoverableTarget.validationErrors).length > 0)
|
||||
{
|
||||
const saved = savePostImportSession (sessionId, resultSession, message =>
|
||||
toast ({ title: '取込状態を保存できませんでした', description: message }))
|
||||
if (saved)
|
||||
navigate (`/posts/import/${ sessionId }/review?edit=${ sourceRow }`)
|
||||
return
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -115,7 +115,7 @@ const PostImportReviewPage: FC<Props> = ({ user }) => {
|
||||
|
||||
const updateSessionRows = (nextRows: PostImportRow[]) =>
|
||||
setSession (current =>
|
||||
current ? { ...current, rows: nextRows } : current)
|
||||
current != null ? { ...current, rows: nextRows } : current)
|
||||
|
||||
const saveDraft = async (
|
||||
{ draft, resetRequested, resetSnapshot }: {
|
||||
@@ -136,6 +136,10 @@ const PostImportReviewPage: FC<Props> = ({ user }) => {
|
||||
attributes: { ...resetSnapshot.attributes },
|
||||
provenance: { ...resetSnapshot.provenance },
|
||||
tagSources: { ...resetSnapshot.tagSources },
|
||||
fieldWarnings: Object.fromEntries (
|
||||
Object.entries (resetSnapshot.fieldWarnings)
|
||||
.map (([key, values]) => [key, [...values]])),
|
||||
baseWarnings: [...resetSnapshot.baseWarnings],
|
||||
metadataUrl: resetSnapshot.metadataUrl }
|
||||
: editingRow
|
||||
const urlChanged = draft.url !== baseRow.url
|
||||
@@ -223,7 +227,37 @@ const PostImportReviewPage: FC<Props> = ({ user }) => {
|
||||
provenance: row.provenance,
|
||||
tagSources: row.tagSources,
|
||||
metadataUrl: row.metadataUrl })) })
|
||||
const nextRows = mergeImportResults (mergedRows, result.rows)
|
||||
const mergedResults = mergeImportResults (mergedRows, result.rows)
|
||||
const recoverableRows = result.rows.filter (row =>
|
||||
row.status === 'failed'
|
||||
&& row.recoverable
|
||||
&& Object.keys (row.errors ?? { }).length > 0)
|
||||
const nextRows = mergedResults.map (row => {
|
||||
const recoverable = recoverableRows.find (_1 => _1.sourceRow === row.sourceRow)
|
||||
if (recoverable == null)
|
||||
return row
|
||||
return {
|
||||
...row,
|
||||
importStatus: 'pending',
|
||||
validationErrors: recoverable.errors ?? { },
|
||||
importErrors: undefined }
|
||||
})
|
||||
const firstRecoverable = nextRows.find (row =>
|
||||
Object.keys (row.validationErrors).length > 0
|
||||
&& row.importStatus !== 'created')
|
||||
if (firstRecoverable != null)
|
||||
{
|
||||
const repairSession = { ...session,
|
||||
rows: nextRows,
|
||||
repairMode: 'failed' as const }
|
||||
setSession (repairSession)
|
||||
setDialogMessageRow (firstRecoverable)
|
||||
const saved = savePostImportSession (sessionId, repairSession, message =>
|
||||
toast ({ title: '取込状態を保存できませんでした', description: message }))
|
||||
if (saved)
|
||||
setSearchParams ({ edit: String (firstRecoverable.sourceRow) })
|
||||
return
|
||||
}
|
||||
const nextSession = { ...session, rows: nextRows, repairMode: 'all' as const }
|
||||
setSession (nextSession)
|
||||
const saved = savePostImportSession (sessionId, nextSession, message =>
|
||||
|
||||
@@ -56,9 +56,9 @@ const PostImportSourcePage: FC<Props> = ({ user }) => {
|
||||
const editedRef = useRef (false)
|
||||
|
||||
const lineCount = countImportSourceLines (source)
|
||||
const messages = sourceError ? [sourceError] : []
|
||||
const messages = sourceError != null ? [sourceError] : []
|
||||
const sourceDescribedBy = [
|
||||
sourceError ? SOURCE_ERROR_ID : null,
|
||||
sourceError != null ? SOURCE_ERROR_ID : null,
|
||||
sourceIssues.length > 0 ? SOURCE_ISSUES_ID : null]
|
||||
.filter (_1 => _1 != null)
|
||||
.join (' ')
|
||||
|
||||
新しい課題から参照
ユーザをブロックする