このコミットが含まれているのは:
@@ -0,0 +1,28 @@
|
||||
import PostTextField from '@/components/posts/PostTextField'
|
||||
|
||||
import type { FC } from 'react'
|
||||
|
||||
type Props = {
|
||||
value: string
|
||||
onChange: (value: string) => void
|
||||
errors?: string[]
|
||||
disabled?: boolean }
|
||||
|
||||
|
||||
const PostDurationField: FC<Props> = (
|
||||
{ value,
|
||||
onChange,
|
||||
errors,
|
||||
disabled },
|
||||
) => (
|
||||
<PostTextField
|
||||
label="動画時間"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
errors={errors}
|
||||
disabled={disabled}
|
||||
type="text"
|
||||
placeholder="例: 2 / 2.5 / 1:23"/>
|
||||
)
|
||||
|
||||
export default PostDurationField
|
||||
@@ -0,0 +1,29 @@
|
||||
import PostFormTagsArea from '@/components/PostFormTagsArea'
|
||||
import FieldWarning from '@/components/common/FieldWarning'
|
||||
|
||||
import type { ComponentPropsWithoutRef, FC } from 'react'
|
||||
|
||||
type Props = Omit<ComponentPropsWithoutRef<'textarea'>, 'value' | 'onChange'> & {
|
||||
tags: string
|
||||
setTags: (tags: string) => void
|
||||
warnings?: string[]
|
||||
errors?: string[] }
|
||||
|
||||
|
||||
const PostTagsField: FC<Props> = (
|
||||
{ tags,
|
||||
setTags,
|
||||
warnings,
|
||||
errors,
|
||||
...rest },
|
||||
) => (
|
||||
<div className="space-y-2">
|
||||
<PostFormTagsArea
|
||||
{...rest}
|
||||
tags={tags}
|
||||
setTags={setTags}
|
||||
errors={errors}/>
|
||||
<FieldWarning messages={warnings}/>
|
||||
</div>)
|
||||
|
||||
export default PostTagsField
|
||||
@@ -0,0 +1,49 @@
|
||||
import FieldWarning from '@/components/common/FieldWarning'
|
||||
import FormField from '@/components/common/FormField'
|
||||
import { inputClass } from '@/lib/utils'
|
||||
|
||||
import type { FC, ReactNode } from 'react'
|
||||
|
||||
type Props = {
|
||||
label: string
|
||||
value: string
|
||||
onChange: (value: string) => void
|
||||
warnings?: string[]
|
||||
errors?: string[]
|
||||
disabled?: boolean
|
||||
type?: string
|
||||
placeholder?: string
|
||||
className?: string
|
||||
after?: ReactNode }
|
||||
|
||||
|
||||
const PostTextField: FC<Props> = (
|
||||
{ label,
|
||||
value,
|
||||
onChange,
|
||||
warnings,
|
||||
errors,
|
||||
disabled,
|
||||
type = 'text',
|
||||
placeholder,
|
||||
className,
|
||||
after },
|
||||
) => (
|
||||
<FormField label={label} messages={errors}>
|
||||
{({ describedBy, invalid }) => (
|
||||
<>
|
||||
<input
|
||||
type={type}
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
placeholder={placeholder}
|
||||
onChange={ev => onChange (ev.target.value)}
|
||||
aria-describedby={describedBy}
|
||||
aria-invalid={invalid}
|
||||
className={inputClass (invalid, className)}/>
|
||||
<FieldWarning messages={warnings}/>
|
||||
{after}
|
||||
</>)}
|
||||
</FormField>)
|
||||
|
||||
export default PostTextField
|
||||
+7
-6
@@ -2,14 +2,15 @@ import { useEffect, useState } from 'react'
|
||||
|
||||
import type { FC } from 'react'
|
||||
|
||||
|
||||
type Props = {
|
||||
url: string
|
||||
alt?: string
|
||||
className?: string }
|
||||
url: string
|
||||
alt?: string
|
||||
className?: string }
|
||||
|
||||
|
||||
const ThumbnailPreview: FC<Props> = ({ url, alt = 'サムネール', className = 'h-16 w-16' }) => {
|
||||
const PostThumbnailPreview: FC<Props> = (
|
||||
{ url, alt = 'サムネール', className = 'h-16 w-16' },
|
||||
) => {
|
||||
const [failed, setFailed] = useState (false)
|
||||
|
||||
useEffect (() => {
|
||||
@@ -46,4 +47,4 @@ const ThumbnailPreview: FC<Props> = ({ url, alt = 'サムネール', className =
|
||||
onError={() => setFailed (true)}/>)
|
||||
}
|
||||
|
||||
export default ThumbnailPreview
|
||||
export default PostThumbnailPreview
|
||||
@@ -3,10 +3,10 @@ import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import PostOriginalCreatedTimeField from '@/components/PostOriginalCreatedTimeField'
|
||||
import FieldError from '@/components/common/FieldError'
|
||||
import FieldWarning from '@/components/common/FieldWarning'
|
||||
import FormField from '@/components/common/FormField'
|
||||
import TextArea from '@/components/common/TextArea'
|
||||
import ThumbnailPreview from '@/components/posts/import/ThumbnailPreview'
|
||||
import { inputClass } from '@/lib/utils'
|
||||
import PostDurationField from '@/components/posts/PostDurationField'
|
||||
import PostTagsField from '@/components/posts/PostTagsField'
|
||||
import PostTextField from '@/components/posts/PostTextField'
|
||||
import PostThumbnailPreview from '@/components/posts/PostThumbnailPreview'
|
||||
|
||||
import type { FC } from 'react'
|
||||
|
||||
@@ -153,13 +153,13 @@ const PostImportRowForm: FC<Props> = (
|
||||
<div className="space-y-4">
|
||||
<div className="grid gap-6 md:grid-cols-[7rem_minmax(0,1fr)]">
|
||||
<div className="space-y-3 md:sticky md:top-0 md:self-start">
|
||||
<ThumbnailPreview
|
||||
<PostThumbnailPreview
|
||||
url={draft.thumbnailBase}
|
||||
className="h-28 w-28"/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<PostImportTextField
|
||||
<PostTextField
|
||||
label="URL"
|
||||
value={draft.url}
|
||||
warnings={displayRow.fieldWarnings.url}
|
||||
@@ -167,7 +167,7 @@ const PostImportRowForm: FC<Props> = (
|
||||
displayRow.validationErrors.url,
|
||||
displayRow.importErrors?.url)}
|
||||
onChange={value => update ('url', value)}/>
|
||||
<PostImportTextField
|
||||
<PostTextField
|
||||
label="タイトル"
|
||||
value={draft.title}
|
||||
warnings={displayRow.fieldWarnings.title}
|
||||
@@ -175,7 +175,7 @@ const PostImportRowForm: FC<Props> = (
|
||||
displayRow.validationErrors.title,
|
||||
displayRow.importErrors?.title)}
|
||||
onChange={value => update ('title', value)}/>
|
||||
<PostImportTextField
|
||||
<PostTextField
|
||||
label="サムネール基底 URL"
|
||||
value={draft.thumbnailBase}
|
||||
warnings={displayRow.fieldWarnings.thumbnailBase}
|
||||
@@ -195,8 +195,7 @@ const PostImportRowForm: FC<Props> = (
|
||||
displayRow.importErrors?.originalCreatedAt,
|
||||
displayRow.importErrors?.originalCreatedFrom,
|
||||
displayRow.importErrors?.originalCreatedBefore)}/>
|
||||
<PostImportTextField
|
||||
label="動画時間"
|
||||
<PostDurationField
|
||||
value={draft.duration}
|
||||
errors={groupedMessages (
|
||||
displayRow.validationErrors.duration,
|
||||
@@ -204,15 +203,15 @@ const PostImportRowForm: FC<Props> = (
|
||||
displayRow.importErrors?.duration,
|
||||
displayRow.importErrors?.videoMs)}
|
||||
onChange={value => update ('duration', value)}/>
|
||||
<PostImportAreaField
|
||||
label="タグ"
|
||||
value={draft.tags}
|
||||
<PostTagsField
|
||||
tags={draft.tags}
|
||||
setTags={value => update ('tags', value)}
|
||||
warnings={displayRow.fieldWarnings.tags}
|
||||
errors={groupedMessages (
|
||||
displayRow.validationErrors.tags,
|
||||
displayRow.importErrors?.tags)}
|
||||
onChange={value => update ('tags', value)}/>
|
||||
<PostImportTextField
|
||||
rows={4}/>
|
||||
<PostTextField
|
||||
label="親投稿"
|
||||
value={draft.parentPostIds}
|
||||
errors={groupedMessages (
|
||||
@@ -229,49 +228,6 @@ const PostImportRowForm: FC<Props> = (
|
||||
</>)
|
||||
}
|
||||
|
||||
const PostImportTextField = (
|
||||
{ label, value, warnings, errors, onChange }: {
|
||||
label: string
|
||||
value: string
|
||||
warnings?: string[]
|
||||
errors?: string[]
|
||||
onChange: (value: string) => void },
|
||||
) => (
|
||||
<FormField label={label} messages={errors}>
|
||||
{({ describedBy, invalid }) => (
|
||||
<>
|
||||
<input
|
||||
value={value}
|
||||
onChange={ev => onChange (ev.target.value)}
|
||||
aria-describedby={describedBy}
|
||||
aria-invalid={invalid}
|
||||
className={inputClass (invalid)}/>
|
||||
<FieldWarning messages={warnings}/>
|
||||
</>)}
|
||||
</FormField>)
|
||||
|
||||
const PostImportAreaField = (
|
||||
{ label, value, warnings, errors, onChange }: {
|
||||
label: string
|
||||
value: string
|
||||
warnings?: string[]
|
||||
errors?: string[]
|
||||
onChange: (value: string) => void },
|
||||
) => (
|
||||
<FormField label={label} messages={errors}>
|
||||
{({ describedBy, invalid }) => (
|
||||
<>
|
||||
<TextArea
|
||||
value={value}
|
||||
rows={4}
|
||||
onChange={ev => onChange (ev.target.value)}
|
||||
aria-describedby={describedBy}
|
||||
invalid={invalid}
|
||||
className="h-auto"/>
|
||||
<FieldWarning messages={warnings}/>
|
||||
</>)}
|
||||
</FormField>)
|
||||
|
||||
export default PostImportRowForm
|
||||
export { buildDraft }
|
||||
export type { Draft as PostImportRowDraft }
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { cn, originalCreatedAtString } from '@/lib/utils'
|
||||
|
||||
import PostThumbnailPreview from '@/components/posts/PostThumbnailPreview'
|
||||
import PostImportStatusBadge from '@/components/posts/import/PostImportStatusBadge'
|
||||
import ThumbnailPreview from '@/components/posts/import/ThumbnailPreview'
|
||||
import { displayPostImportStatus } from '@/components/posts/import/postImportRowStatus'
|
||||
import { canEditReviewRow } from '@/lib/postImportSession'
|
||||
import { cn, originalCreatedAtString } from '@/lib/utils'
|
||||
|
||||
import type { FC } from 'react'
|
||||
|
||||
@@ -39,7 +39,7 @@ const PostImportRowSummary: FC<Props> = ({ row, onEdit, editDisabled }) => {
|
||||
<div className="space-y-1">
|
||||
<div className="text-sm font-medium">#{row.sourceRow}</div>
|
||||
</div>
|
||||
<ThumbnailPreview
|
||||
<PostThumbnailPreview
|
||||
url={String (row.attributes.thumbnailBase ?? '')}
|
||||
className="h-16 w-16"/>
|
||||
<div className="min-w-0 space-y-1">
|
||||
@@ -69,7 +69,7 @@ const PostImportRowSummary: FC<Props> = ({ row, onEdit, editDisabled }) => {
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={onEdit}
|
||||
disabled={row.importStatus === 'created' || editDisabled === true}>
|
||||
disabled={!(canEditReviewRow (row)) || editDisabled === true}>
|
||||
編輯
|
||||
</Button>
|
||||
</div>
|
||||
@@ -80,7 +80,7 @@ const PostImportRowSummary: FC<Props> = ({ row, onEdit, editDisabled }) => {
|
||||
'space-y-3 rounded-lg border p-4 md:hidden',
|
||||
'transition-shadow hover:shadow-sm')}>
|
||||
<div className="flex items-start gap-3">
|
||||
<ThumbnailPreview
|
||||
<PostThumbnailPreview
|
||||
url={String (row.attributes.thumbnailBase ?? '')}
|
||||
className="h-20 w-20 shrink-0"/>
|
||||
<div className="min-w-0 flex-1 space-y-2">
|
||||
@@ -110,7 +110,7 @@ const PostImportRowSummary: FC<Props> = ({ row, onEdit, editDisabled }) => {
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={onEdit}
|
||||
disabled={row.importStatus === 'created' || editDisabled === true}>
|
||||
disabled={!(canEditReviewRow (row)) || editDisabled === true}>
|
||||
編輯
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
新しい課題から参照
ユーザをブロックする