From 21d6c1408ad448e96dfa8ced4c93963e1bf68e0e Mon Sep 17 00:00:00 2001 From: miteruzo Date: Wed, 15 Jul 2026 19:43:39 +0900 Subject: [PATCH] #399 --- frontend/src/components/common/Form.tsx | 12 +- .../src/components/common/StatusBadge.tsx | 51 +++++++++ .../posts/import/PageActionFooter.tsx | 22 ++++ .../posts/import/PostImportRowDialog.tsx | 70 ++++++++++-- .../posts/import/PostImportRowSummary.tsx | 28 +---- .../posts/import/PostImportStatusBadge.tsx | 41 +++---- .../posts/import/PostImportSummaryChip.tsx | 19 ++++ .../posts/import/postImportRowStatus.ts | 37 ++++-- .../components/posts/import/postImportTone.ts | 36 ++++++ .../src/pages/posts/PostImportResultPage.tsx | 78 +++---------- .../src/pages/posts/PostImportReviewPage.tsx | 68 ++++++----- .../src/pages/posts/PostImportSourcePage.tsx | 106 +++++++++--------- 12 files changed, 350 insertions(+), 218 deletions(-) create mode 100644 frontend/src/components/common/StatusBadge.tsx create mode 100644 frontend/src/components/posts/import/PageActionFooter.tsx create mode 100644 frontend/src/components/posts/import/PostImportSummaryChip.tsx create mode 100644 frontend/src/components/posts/import/postImportTone.ts diff --git a/frontend/src/components/common/Form.tsx b/frontend/src/components/common/Form.tsx index 2a3c294..f97d669 100644 --- a/frontend/src/components/common/Form.tsx +++ b/frontend/src/components/common/Form.tsx @@ -1,11 +1,15 @@ +import { cn } from '@/lib/utils' + import type { FC, ReactNode } from 'react' -type Props = { children: ReactNode } +type Props = { + children: ReactNode + className?: string } -const Form: FC = ({ children }) => ( -
+const Form: FC = ({ children, className }) => ( +
{children}
) -export default Form \ No newline at end of file +export default Form diff --git a/frontend/src/components/common/StatusBadge.tsx b/frontend/src/components/common/StatusBadge.tsx new file mode 100644 index 0000000..bfc70c2 --- /dev/null +++ b/frontend/src/components/common/StatusBadge.tsx @@ -0,0 +1,51 @@ +import { cn } from '@/lib/utils' + +import type { FC, ReactNode } from 'react' + +export type StatusBadgeTone = + 'danger' + | 'default' + | 'info' + | 'manual' + | 'ready' + | 'skipped' + | 'warning' + +type Props = { + children: ReactNode + tone: StatusBadgeTone } + +const TONES: Record = { + default: [ + 'border-slate-300 bg-slate-50 text-slate-700', + 'dark:border-slate-700 dark:bg-slate-900 dark:text-slate-200'], + ready: [ + 'border-emerald-300 bg-emerald-50 text-emerald-700', + 'dark:border-emerald-900 dark:bg-emerald-950 dark:text-emerald-200'], + warning: [ + 'border-amber-300 bg-amber-50 text-amber-700', + 'dark:border-amber-900 dark:bg-amber-950 dark:text-amber-200'], + danger: [ + 'border-rose-300 bg-rose-50 text-rose-700', + 'dark:border-rose-900 dark:bg-rose-950 dark:text-rose-200'], + info: [ + 'border-sky-300 bg-sky-50 text-sky-700', + 'dark:border-sky-900 dark:bg-sky-950 dark:text-sky-200'], + skipped: [ + 'border-stone-300 bg-stone-50 text-stone-700', + 'dark:border-stone-700 dark:bg-stone-900 dark:text-stone-200'], + manual: [ + 'border-purple-300 bg-purple-50 text-purple-700', + 'dark:border-purple-900 dark:bg-purple-950 dark:text-purple-200'] } + + +const StatusBadge: FC = ({ children, tone }) => ( + + {children} + ) + +export default StatusBadge diff --git a/frontend/src/components/posts/import/PageActionFooter.tsx b/frontend/src/components/posts/import/PageActionFooter.tsx new file mode 100644 index 0000000..6cd149d --- /dev/null +++ b/frontend/src/components/posts/import/PageActionFooter.tsx @@ -0,0 +1,22 @@ +import type { FC, ReactNode } from 'react' + +type Props = { + summary: ReactNode + actions: ReactNode } + +const PageActionFooter: FC = ({ summary, actions }) => ( +
+
+
+ {summary} +
+
+ {actions} +
+
+
) + +export default PageActionFooter diff --git a/frontend/src/components/posts/import/PostImportRowDialog.tsx b/frontend/src/components/posts/import/PostImportRowDialog.tsx index 644b68d..41d915d 100644 --- a/frontend/src/components/posts/import/PostImportRowDialog.tsx +++ b/frontend/src/components/posts/import/PostImportRowDialog.tsx @@ -4,7 +4,7 @@ import FieldWarning from '@/components/common/FieldWarning' import FormField from '@/components/common/FormField' import PostImportStatusBadge from '@/components/posts/import/PostImportStatusBadge' import ThumbnailPreview from '@/components/posts/import/ThumbnailPreview' -import { displayPostImportStatus } from '@/components/posts/import/postImportRowStatus' +import { effectivePostImportStatus } from '@/components/posts/import/postImportRowStatus' import { Button } from '@/components/ui/button' import { Dialog, DialogContent, @@ -17,7 +17,8 @@ import { inputClass } from '@/lib/utils' import type { FC } from 'react' import { useEffect, useState } from 'react' -import type { PostImportRow } from '@/lib/postImportSession' +import type { PostImportOrigin, + PostImportRow } from '@/lib/postImportSession' type Draft = { url: string @@ -37,6 +38,36 @@ type Props = { onOpenChange: (open: boolean) => void onSave: (draft: Draft) => Promise } +const originOf = ( + row: PostImportRow, + field: string, +): PostImportOrigin => + row.provenance[field] ?? 'automatic' + +const changedOrigin = ( + changed: boolean, + row: PostImportRow, + field: string, +): PostImportOrigin => + changed ? 'manual' : originOf (row, field) + +const originalCreatedOrigin = ( + row: PostImportRow, + originalDraft: Draft, + draft: Draft, +): PostImportOrigin => { + const changed = + originalDraft.originalCreatedFrom !== draft.originalCreatedFrom + || originalDraft.originalCreatedBefore !== draft.originalCreatedBefore + if (changed) + return 'manual' + + const manualOrigin = + originOf (row, 'originalCreatedFrom') === 'manual' + || originOf (row, 'originalCreatedBefore') === 'manual' + return manualOrigin ? 'manual' : 'automatic' +} + const buildDraft = (row: PostImportRow): Draft => ({ url: row.url, title: String (row.attributes.title ?? ''), @@ -77,6 +108,9 @@ const PostImportRowDialog: FC = ( return null const displayRow = messageRow ?? row + const originalDraft = buildDraft (row) + const fieldOrigin = (field: keyof Draft): PostImportOrigin => + changedOrigin (draft[field] !== originalDraft[field], row, field) const update = ( key: Key, @@ -109,7 +143,7 @@ const PostImportRowDialog: FC = ( url={draft.thumbnailBase} className="h-28 w-28"/>
- +
@@ -117,6 +151,7 @@ const PostImportRowDialog: FC = ( = ( = ( = ( )} onChange={value => update ('thumbnailBase', value)}/> + } originalCreatedFrom={draft.originalCreatedFrom || null} setOriginalCreatedFrom={value => update ('originalCreatedFrom', value ?? '')} originalCreatedBefore={draft.originalCreatedBefore || null} @@ -153,6 +194,7 @@ const PostImportRowDialog: FC = ( = ( = ( = ( } const DialogTextField = ( - { label, value, warnings, errors, onChange }: { + { label, value, origin, warnings, errors, onChange }: { label: string value: string + origin: PostImportOrigin warnings?: string[] errors?: string[] onChange: (value: string) => void }, ) => ( - + } messages={errors}> {({ describedBy, invalid }) => ( <> ) const DialogAreaField = ( - { label, value, warnings, errors, onChange }: { + { label, value, origin, warnings, errors, onChange }: { label: string value: string + origin: PostImportOrigin warnings?: string[] errors?: string[] onChange: (value: string) => void }, ) => ( - + } messages={errors}> {({ describedBy, invalid }) => ( <>