feat: modal row
parent
3c4da03575
commit
b9f223d9d4
@ -0,0 +1,65 @@
|
|||||||
|
'use client'
|
||||||
|
import type { FC } from 'react'
|
||||||
|
import React from 'react'
|
||||||
|
import { type MetadataItemWithEdit, UpdateType } from '../types'
|
||||||
|
import Input from '../../create/website/base/input'
|
||||||
|
import Label from './label'
|
||||||
|
import { RiDeleteBinLine } from '@remixicon/react'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
import InputHasSetMultipleValue from './input-has-set-multiple-value'
|
||||||
|
import InputCombined from './input-combined'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
payload: MetadataItemWithEdit
|
||||||
|
onChange: (payload: MetadataItemWithEdit) => void
|
||||||
|
onRemove: (id: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const labelClassName = ''
|
||||||
|
|
||||||
|
export const AddedMetadataItem: FC<Props> = ({
|
||||||
|
payload,
|
||||||
|
onChange,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className='flex'>
|
||||||
|
<div className={labelClassName}>{payload.name}</div>
|
||||||
|
<Input
|
||||||
|
value={payload.value}
|
||||||
|
onChange={v => onChange({ ...payload, value: v as string })
|
||||||
|
} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditMetadatabatchItem: FC<Props> = ({
|
||||||
|
payload,
|
||||||
|
onChange,
|
||||||
|
onRemove,
|
||||||
|
}) => {
|
||||||
|
const isDeleted = payload.updateType === UpdateType.delete
|
||||||
|
return (
|
||||||
|
<div className='flex h-6 items-center space-x-0.5'>
|
||||||
|
<Label text={payload.name} isDeleted={isDeleted} />
|
||||||
|
{payload.isMultipleValue
|
||||||
|
? <InputHasSetMultipleValue onClear={() => onChange({ ...payload, isMultipleValue: false })} />
|
||||||
|
: <InputCombined
|
||||||
|
type={payload.type}
|
||||||
|
value={payload.value}
|
||||||
|
onChange={v => onChange({ ...payload, value: v as string })
|
||||||
|
} />}
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
cn(
|
||||||
|
'p-1 rounded-md text-text-tertiary hover:bg-state-destructive-hover hover:text-text-destructive cursor-pointer',
|
||||||
|
isDeleted && 'cursor-default bg-state-destructive-hover text-text-quaternary ')
|
||||||
|
}
|
||||||
|
onClick={() => onRemove(payload.id)}
|
||||||
|
>
|
||||||
|
<RiDeleteBinLine className='size-4' />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default React.memo(EditMetadatabatchItem)
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
'use client'
|
||||||
|
import type { FC } from 'react'
|
||||||
|
import React from 'react'
|
||||||
|
import { DataType } from '../types'
|
||||||
|
import Input from '@/app/components/base/input'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
type: DataType
|
||||||
|
value: any
|
||||||
|
onChange: (value: any) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const InputCombined: FC<Props> = ({
|
||||||
|
type,
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
}) => {
|
||||||
|
if (type === DataType.time)
|
||||||
|
return <div className='grow text-xs'>Datepicker placeholder</div>
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
className='p-0.5 h-6 rounded-md text-xs'
|
||||||
|
value={value}
|
||||||
|
onChange={e => onChange(e.target.value)}
|
||||||
|
>
|
||||||
|
</Input>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default React.memo(InputCombined)
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
'use client'
|
||||||
|
import { RiCloseLine } from '@remixicon/react'
|
||||||
|
import type { FC } from 'react'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onClear: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const InputHasSetMultipleValue: FC<Props> = ({
|
||||||
|
onClear,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className='grow h-6 p-0.5 rounded-md bg-components-input-bg-normal text-[0]'>
|
||||||
|
<div className='inline-flex rounded-[5px] items-center h-5 pl-1.5 pr-0.5 bg-components-badge-white-to-dark border-[0.5px] border-components-panel-border shadow-xs space-x-0.5'>
|
||||||
|
<div className='system-xs-regular text-text-secondary'>Multiple Value</div>
|
||||||
|
<div className='p-0.5rounded-sm text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary cursor-pointer'>
|
||||||
|
<RiCloseLine
|
||||||
|
className='size-3.5 '
|
||||||
|
onClick={onClear}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default React.memo(InputHasSetMultipleValue)
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
'use client'
|
||||||
|
import type { FC } from 'react'
|
||||||
|
import React from 'react'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
isDeleted: boolean,
|
||||||
|
className?: string,
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const Label: FC<Props> = ({
|
||||||
|
isDeleted,
|
||||||
|
className,
|
||||||
|
text,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className={cn(
|
||||||
|
'shrink-0 w-[136px] system-xs-medium text-text-tertiary truncate',
|
||||||
|
isDeleted && 'line-through text-text-quaternary',
|
||||||
|
className,
|
||||||
|
)}>
|
||||||
|
{text}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default React.memo(Label)
|
||||||
@ -0,0 +1,103 @@
|
|||||||
|
'use client'
|
||||||
|
import type { FC } from 'react'
|
||||||
|
import React, { useCallback, useState } from 'react'
|
||||||
|
import Modal from '../../../base/modal'
|
||||||
|
import type { MetadataItemWithEdit } from '../types'
|
||||||
|
import EditMetadataBatchItem, { AddedMetadataItem } from './edit-row'
|
||||||
|
import Button from '../../../base/button'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import Checkbox from '../../../base/checkbox'
|
||||||
|
import Tooltip from '../../../base/tooltip'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
documentNum: number
|
||||||
|
list: MetadataItemWithEdit[]
|
||||||
|
onChange: (list: MetadataItemWithEdit[], addedList: MetadataItemWithEdit[], isApplyToAllSelectDocument: boolean) => void
|
||||||
|
onHide: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditMetadataBatchModal: FC<Props> = ({
|
||||||
|
documentNum,
|
||||||
|
list,
|
||||||
|
onChange,
|
||||||
|
onHide,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const [templeList, setTempleList] = useState<MetadataItemWithEdit[]>(list)
|
||||||
|
const handleTemplesChange = useCallback((payload: MetadataItemWithEdit) => {
|
||||||
|
const newTempleList = templeList.map(i => i.id === payload.id ? payload : i)
|
||||||
|
setTempleList(newTempleList)
|
||||||
|
}, [templeList])
|
||||||
|
const handleTempleItemRemove = useCallback((id: string) => {
|
||||||
|
const newTempleList = templeList.filter(i => i.id !== id)
|
||||||
|
setTempleList(newTempleList)
|
||||||
|
}, [templeList])
|
||||||
|
|
||||||
|
const [addedList, setAddedList] = useState<MetadataItemWithEdit[]>([])
|
||||||
|
const handleAddedListChange = useCallback((payload: MetadataItemWithEdit) => {
|
||||||
|
const newAddedList = addedList.map(i => i.id === payload.id ? payload : i)
|
||||||
|
setAddedList(newAddedList)
|
||||||
|
}, [addedList])
|
||||||
|
const handleAddedItemRemove = useCallback((id: string) => {
|
||||||
|
const newAddedList = addedList.filter(i => i.id !== id)
|
||||||
|
setAddedList(newAddedList)
|
||||||
|
}, [addedList])
|
||||||
|
|
||||||
|
const [isApplyToAllSelectDocument, setIsApplyToAllSelectDocument] = useState(false)
|
||||||
|
|
||||||
|
const handleSave = useCallback(() => {
|
||||||
|
onChange(templeList, addedList, isApplyToAllSelectDocument)
|
||||||
|
}, [templeList, addedList, isApplyToAllSelectDocument, onChange])
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title='Edit Metadata'
|
||||||
|
isShow
|
||||||
|
closable
|
||||||
|
onClose={onHide}
|
||||||
|
className='!max-w-[640px]'
|
||||||
|
>
|
||||||
|
<div className='system-xs-medium text-text-accent'>Editing {documentNum} documents</div>
|
||||||
|
{/* TODO handle list scroll. There is two list. */}
|
||||||
|
<div className='mt-4 space-y-2'>
|
||||||
|
{templeList.map(item => (
|
||||||
|
<EditMetadataBatchItem
|
||||||
|
key={item.id}
|
||||||
|
payload={item}
|
||||||
|
onChange={handleTemplesChange}
|
||||||
|
onRemove={handleTempleItemRemove}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{addedList.map(item => (
|
||||||
|
<AddedMetadataItem
|
||||||
|
key={item.id}
|
||||||
|
payload={item}
|
||||||
|
onChange={handleAddedListChange}
|
||||||
|
onRemove={handleAddedItemRemove}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mt-4 flex items-center justify-between'>
|
||||||
|
<div className='flex items-center'>
|
||||||
|
<Checkbox checked={isApplyToAllSelectDocument} onCheck={() => setIsApplyToAllSelectDocument(!isApplyToAllSelectDocument)} />
|
||||||
|
<div className='ml-2 mr-1'> Apply to all selected documents</div>
|
||||||
|
<Tooltip popupContent={
|
||||||
|
<div className='max-w-[240px]'>Automatically create all the above edited and new metadata for all selected documents, otherwise editing metadata will only apply to documents with it.</div>
|
||||||
|
} />
|
||||||
|
</div>
|
||||||
|
<div className='flex items-center space-x-2'>
|
||||||
|
<Button
|
||||||
|
onClick={onHide}>{t('common.operation.cancel')}</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleSave}
|
||||||
|
variant='primary'
|
||||||
|
>{t('common.operation.save')}</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default React.memo(EditMetadataBatchModal)
|
||||||
Loading…
Reference in New Issue