From c31d84115db1fa8b025920ade3cf921db5255a5f Mon Sep 17 00:00:00 2001 From: miteruzo Date: Sat, 18 Jul 2026 00:31:02 +0900 Subject: [PATCH] #399 --- backend/app/controllers/posts_controller.rb | 2 +- .../src/pages/posts/PostImportReviewPage.tsx | 17 +++-- .../src/pages/posts/PostImportSourcePage.tsx | 63 +++++++++++++------ 3 files changed, 58 insertions(+), 24 deletions(-) diff --git a/backend/app/controllers/posts_controller.rb b/backend/app/controllers/posts_controller.rb index ac80b82..2ff9175 100644 --- a/backend/app/controllers/posts_controller.rb +++ b/backend/app/controllers/posts_controller.rb @@ -128,7 +128,6 @@ class PostsController < ApplicationController provenance: { }, tag_sources: { } }], fetch_metadata: true).first - return render_validation_error fields: preview[:validation_errors] if preview[:validation_errors].present? render json: { url: preview[:url], @@ -141,6 +140,7 @@ class PostsController < ApplicationController video_ms: preview[:attributes]['video_ms'], field_warnings: preview[:field_warnings], base_warnings: preview[:base_warnings], + validation_errors: preview[:validation_errors], existing_post: compact_post(preview[:existing_post_id]) } rescue ArgumentError => e render_bad_request e.message diff --git a/frontend/src/pages/posts/PostImportReviewPage.tsx b/frontend/src/pages/posts/PostImportReviewPage.tsx index 48be61c..d7eac2a 100644 --- a/frontend/src/pages/posts/PostImportReviewPage.tsx +++ b/frontend/src/pages/posts/PostImportReviewPage.tsx @@ -74,6 +74,7 @@ type PreviewResponse = { tags?: string fieldWarnings?: Record baseWarnings?: string[] + validationErrors?: Record existingPost?: { id: number title: string @@ -249,7 +250,13 @@ const mergePreviewRow = ( ): PostImportRow => { const fieldWarnings = preview.fieldWarnings ?? { } const baseWarnings = preview.baseWarnings ?? [] + const validationErrors = preview.validationErrors ?? { } const metadataChanged = currentRow.metadataUrl !== preview.url + const hasWarnings = + Object.values (fieldWarnings).some (messages => messages.length > 0) + || baseWarnings.length > 0 + const hasErrors = + Object.values (validationErrors).some (messages => messages.length > 0) const nextRow: PostImportRow = { ...currentRow, attributes: { ...currentRow.attributes }, @@ -260,6 +267,7 @@ const mergePreviewRow = ( url: preview.url, fieldWarnings, baseWarnings, + validationErrors, existingPostId: preview.existingPost?.id, existingPost: preview.existingPost ?? undefined, skipReason: @@ -274,10 +282,11 @@ const mergePreviewRow = ( ? buildPreviewResetSnapshot (preview) : currentRow.resetSnapshot, status: - Object.keys (fieldWarnings).length > 0 - || baseWarnings.length > 0 - ? 'warning' - : 'ready' } + hasErrors + ? 'error' + : hasWarnings + ? 'warning' + : 'ready' } if (currentRow.provenance.title !== 'manual') nextRow.attributes.title = preview.title ?? '' diff --git a/frontend/src/pages/posts/PostImportSourcePage.tsx b/frontend/src/pages/posts/PostImportSourcePage.tsx index 098e944..fc9840b 100644 --- a/frontend/src/pages/posts/PostImportSourcePage.tsx +++ b/frontend/src/pages/posts/PostImportSourcePage.tsx @@ -48,6 +48,7 @@ type PreviewResponse = { tags?: string fieldWarnings?: Record baseWarnings?: string[] + validationErrors?: Record existingPost?: { id: number title: string @@ -75,6 +76,12 @@ const previewRowFromResult = ( ): PostImportRow => { const fieldWarnings = result.fieldWarnings ?? { } const baseWarnings = result.baseWarnings ?? [] + const validationErrors = result.validationErrors ?? { } + const hasWarnings = + Object.values (fieldWarnings).some (messages => messages.length > 0) + || baseWarnings.length > 0 + const hasErrors = + Object.values (validationErrors).some (messages => messages.length > 0) const row: PostImportRow = { sourceRow, url: result.url, @@ -89,7 +96,7 @@ const previewRowFromResult = ( parentPostIds: '' }, fieldWarnings, baseWarnings, - validationErrors: { }, + validationErrors, provenance: { url: 'manual', title: 'automatic', @@ -104,9 +111,11 @@ const previewRowFromResult = ( automatic: result.tags ?? '', manual: '' }, status: - Object.keys (fieldWarnings).length > 0 || baseWarnings.length > 0 - ? 'warning' - : 'ready', + hasErrors + ? 'error' + : hasWarnings + ? 'warning' + : 'ready', skipReason: result.existingPost?.id != null ? 'existing' : undefined, existingPostId: result.existingPost?.id, existingPost: result.existingPost ?? undefined, @@ -155,6 +164,36 @@ const fetchPreviewRows = async ( message: string url: string }> = [] let nextIndex = 0 + + const previewIssues = ( + sourceRow: number, + url: string, + requestError: unknown, + ): Array<{ sourceRow: number + message: string + url: string }> => { + if (!(isApiError<{ + message?: string + errors?: Record + baseErrors?: string[] + }> (requestError))) + { + return [{ + sourceRow, + message: '入力を確認してください.', + url }] + } + + const fieldMessages = Object.values (requestError.response?.data?.errors ?? { }).flat () + const baseMessages = requestError.response?.data?.baseErrors ?? [] + const messages = [...new Set ([...fieldMessages, ...baseMessages])] + return (messages.length > 0 ? messages : [requestError.response?.data?.message ?? '入力を確認してください.']) + .map (message => ({ + sourceRow, + message, + url })) + } + const worker = async () => { while (nextIndex < rows.length) { @@ -172,21 +211,7 @@ const fetchPreviewRows = async ( { if (signal.aborted) return - - const apiMessage = - isApiError<{ - message?: string - errors?: Record - baseErrors?: string[] - }> (requestError) - ? (requestError.response?.data?.errors?.url?.[0] - ?? requestError.response?.data?.message - ?? requestError.response?.data?.baseErrors?.[0]) - : undefined - issues.push ({ - sourceRow: row.sourceRow, - message: apiMessage ?? '入力を確認してください.', - url: row.url }) + issues.push (...previewIssues (row.sourceRow, row.url, requestError)) } } }