import { useFieldArray, UseFieldArrayReturn, useFormContext, } from 'react-hook-form'; import {NormalizedModel} from '@common/datatable/filters/normalized-model'; import {Trans} from '@common/i18n/trans'; import {Table} from '@common/ui/tables/table'; import {RowElementProps} from '@common/ui/tables/table-row'; import {useIsTouchDevice} from '@common/utils/hooks/is-touch-device'; import React, { cloneElement, ReactElement, ReactNode, useContext, useRef, useState, } from 'react'; import {TableContext} from '@common/ui/tables/table-context'; import {DragPreviewRenderer} from '@common/ui/interactions/dnd/use-draggable'; import { DropPosition, useSortable, } from '@common/ui/interactions/dnd/use-sortable'; import clsx from 'clsx'; import {mergeProps} from '@react-aria/utils'; import {ColumnConfig} from '@common/datatable/column-config'; import {DragHandleIcon} from '@common/icons/material/DragHandle'; import {NameWithAvatar} from '@common/datatable/column-templates/name-with-avatar'; import {IconButton} from '@common/ui/buttons/icon-button'; import {CloseIcon} from '@common/icons/material/Close'; import {DragPreview} from '@common/ui/interactions/dnd/drag-preview'; import {WarningIcon} from '@common/icons/material/Warning'; import {IllustratedMessage} from '@common/ui/images/illustrated-message'; import playlist from '../playlist.svg'; import {SvgImage} from '@common/ui/images/svg-image/svg-image'; import {useParams} from 'react-router-dom'; import {Button} from '@common/ui/buttons/button'; import {RefreshIcon} from '@common/icons/material/Refresh'; import {UpdateChannelPayload} from '@common/admin/channels/requests/use-update-channel'; import {useUpdateChannelContent} from '@common/admin/channels/requests/use-update-channel-content'; import {ChannelContentSearchFieldProps} from '@common/admin/channels/channel-editor/channel-content-search-field'; const columnConfig: ColumnConfig[] = [ { key: 'dragHandle', width: 'w-42 flex-shrink-0', header: () => , hideHeader: true, body: () => ( ), }, { key: 'name', header: () => , visibleInMode: 'all', body: item => ( ), }, { key: 'type', header: () => , width: 'w-100 flex-shrink-0', body: item => {item.model_type}, }, { key: 'actions', header: () => , hideHeader: true, align: 'end', width: 'w-42 flex-shrink-0', visibleInMode: 'all', body: (item, {index}) => , }, ]; interface Props { searchField: ReactElement; title?: ReactNode; noResultsMessage?: ReactNode; } export function ChannelContentEditor({ searchField, title, noResultsMessage, }: Props) { const {watch, getValues} = useFormContext(); const contentType = watch('config.contentType'); const fieldArray = useFieldArray({ name: 'content.data', }); // need to watch this and use it in table, otherwise content will not update when using "update content now" button const content = watch('content'); // only show delete and drag buttons when channel content is managed manually const filteredColumns = columnConfig.filter(col => { return !( contentType !== 'manual' && (col.key === 'actions' || col.key === 'dragHandle') ); }); return (

{title || }

{contentType === 'manual' ? cloneElement(searchField, { onResultSelected: result => { const alreadyAttached = getValues('content.data').find( x => x.id === result.id && x.model_type === result.model_type, ); if (!alreadyAttached) { fieldArray.prepend(result); } }, }) : null}
{!fieldArray.fields.length && contentType === 'manual' ? noResultsMessage || ( } description={ } image={} /> ) : null} ); } function ContentTableRow({ item, children, className, ...domProps }: RowElementProps) { const isTouchDevice = useIsTouchDevice(); const {data, meta} = useContext(TableContext); const domRef = useRef(null); const previewRef = useRef(null); const [dropPosition, setDropPosition] = useState(null); const fieldArray = meta as UseFieldArrayReturn; const {sortableProps} = useSortable({ ref: domRef, disabled: isTouchDevice ?? false, item, items: data, type: 'channelContentItem', preview: previewRef, previewVariant: 'line', onDropPositionChange: position => { setDropPosition(position); }, onSortEnd: (oldIndex, newIndex) => { fieldArray.move(oldIndex, newIndex); }, }); return (
{children} {!item.isPlaceholder && }
); } interface RowDragPreviewProps { item: NormalizedModel; } const RowDragPreview = React.forwardRef< DragPreviewRenderer, RowDragPreviewProps >(({item}, ref) => { return ( {() => (
{item.name}
)}
); }); interface RemoveItemColumnProps { index: number; } function RemoveItemColumn({index}: RemoveItemColumnProps) { const {meta} = useContext(TableContext); const fieldArray = meta as UseFieldArrayReturn; return ( { fieldArray.remove(index); }} > ); } function ContentNotEditableWarning() { const {watch} = useFormContext(); const contentType = watch('config.contentType'); if (contentType === 'manual') { return null; } return (
{contentType === 'listAll' ? ( ) : null} {contentType === 'autoUpdate' ? ( ) : null}
); } function UpdateContentButton() { const {slugOrId} = useParams(); const updateContent = useUpdateChannelContent(slugOrId!); const {setValue, watch, getValues} = useFormContext(); if (watch('config.contentType') !== 'autoUpdate') { return null; } return ( ); }