node-red-contrib-chatbot
Version:
REDBot a Chat bot for a full featured chat bot for Telegram, Facebook Messenger and Slack. Almost no coding skills required
381 lines (356 loc) • 11 kB
JavaScript
import React, { useState, useRef, Fragment } from 'react';
import gql from 'graphql-tag';
import _ from 'lodash';
import { Table, Icon, SelectPicker, ButtonGroup, Button, ButtonToolbar } from 'rsuite';
const { Column, HeaderCell, Cell } = Table;
import PageContainer from '../../../../src/components/page-container';
import Breadcrumbs from '../../../../src/components/breadcrumbs';
import SmartDate from '../../../../src/components/smart-date';
import Language from '../../../../src/components/language';
import CustomTable from '../../../../src/components/table';
import LanguagePicker from '../../../../src/components/language-picker';
import { Input } from '../../../../src/components/table-filters';
import ExportButton from '../../../../src/components/export-button';
import useMCContext from '../../../../src/hooks/mc-context';
import useContents from '../hooks/content';
import ModalContent from '../views/modal-content';
import hasField from '../helpers/has-field';
import { ContentsType } from '../prop-types';
const CONTENTS = gql`
query(
$offset: Int,
$limit: Int,
$order: String,
$categoryId: Int,
$slug: String,
$language: String,
$namespace: String,
$search: String,
$chatbotId: String
) {
counters {
rows: contents {
count(
categoryId: $categoryId,
slug: $slug,
language: $language,
namespace: $namespace,
search: $search,
chatbotId: $chatbotId
)
}
}
categories(namespace: $namespace, chatbotId: $chatbotId) {
id,
name
}
rows: contents(
offset: $offset,
limit: $limit,
order: $order,
categoryId: $categoryId,
slug: $slug,
language: $language,
namespace: $namespace,
search: $search,
chatbotId: $chatbotId
) {
id,
slug,
title,
body,
categoryId,
language,
createdAt,
payload,
namespace,
chatbotId,
category {
id,
name
}
}
}
`;
const LABELS = {
createContent: 'Create content',
emptyContent: 'No Content',
saveContent: 'Save content',
title: 'Title',
slug: 'Slug'
};
const COLUMNS_SIZE = {
date: 140,
language: 50,
slug: 80,
categoryId: 80
};
const FILTERS_SCHEMA = [
{
name: 'search',
label: 'Search',
control: Input
},
{
name: 'categoryId',
placeholder: 'Filter by category',
cleanable: true,
block: true,
data: data => data.categories.map(category => ({ value: category.id, label: category.name })),
control: SelectPicker,
type: 'number'
},
{
name: 'slug',
label: 'Slug',
control: Input
},
{
name: 'language',
label: 'Language',
control: LanguagePicker,
block: true,
cleanable: true
}
];
const columnField = (columns = [], columnId, key, defaultValue) => {
const column = columns.find(({ id }) => id === columnId);
return column != null && !_.isEmpty(column[key]) ? column[key] : defaultValue;
}
const Contents = ({
namespace,
title,
labels,
breadcrumbs,
customFieldsSchema,
custom,
fields,
defaultContent,
columns,
plugins,
sortable
}) => {
const { state } = useMCContext();
const [filters, setFilters] = useState(null);
const [content, setContent] = useState(null);
const table = useRef();
const {
error,
saving,
deleteContent,
editContent,
createContent,
swapOrder
} = useContents();
labels = { ...LABELS, ...labels };
const columnsSize = {
...COLUMNS_SIZE,
...(columns || []).reduce((acc, { width, id }) => ({ ...acc, [id]: width }), {})
};
const disabled = saving;
// remove unwanted column
const filtersSchema = FILTERS_SCHEMA
.filter(({ name }) => hasField(fields, name))
.map(filter => ({ ...filter, label: columnField(columns, filter.name, 'label', filter.label)}));
return (
<PageContainer className="page-contents">
<Breadcrumbs pages={breadcrumbs != null ? breadcrumbs : [title]}/>
{content != null && (
<ModalContent
contentId={content.id}
content={content}
error={error}
disabled={disabled}
labels={labels}
customFieldsSchema={customFieldsSchema}
fields={fields}
namespace={namespace}
onCancel={() => setContent(null)}
plugins={plugins}
onSubmit={async content => {
if (content.id != null) {
await editContent({ variables: { id: content.id, content }})
} else {
await createContent({ variables: { content: { ...content, namespace, chatbotId: state.chatbotId } } });
}
setContent(null);
table.current.refetch();
}}
/>
)}
<CustomTable
ref={table}
query={CONTENTS}
variables={{ namespace, chatbotId: state.chatbotId }}
initialSortField={sortable ? 'order' : 'createdAt'}
initialSortDirection={sortable ? 'asc' : 'desc'}
toolbar={(
<ButtonToolbar>
{_.isFunction(custom) ? custom({
refetch: () => table.current.refetch(),
disabled
}) : custom}
<Button
appearance="primary"
disabled={disabled}
onClick={() => table.current.refetch()}>Refresh
</Button>
<Button
appearance="primary"
disabled={disabled}
onClick={() => {
const newContent = { title: '', body: '', fields: [], ...filters, namespace };
setContent(_.isFunction(defaultContent) ? defaultContent(newContent) : newContent);
}}>{labels.createContent}
</Button>
<ExportButton
table="contents"
namespace={namespace}
disabled={disabled}
/>
</ButtonToolbar>
)}
onFilters={setFilters}
filtersSchema={filtersSchema}
height={600}
labels={{
empty: labels.emptyContent
}}
autoHeight
>
<Column width={60} align="center">
<HeaderCell>Id</HeaderCell>
<Cell dataKey="id" />
</Column>
<Column width={columnsSize.date} resizable sortable={!sortable}>
<HeaderCell>Date</HeaderCell>
<Cell dataKey="createdAt">
{({ createdAt }) => <SmartDate date={createdAt} />}
</Cell>
</Column>
<Column width={260} align="left" sortable={!sortable} flexGrow={1}>
<HeaderCell>{labels.title}</HeaderCell>
<Cell dataKey="title" />
</Column>
{hasField(fields, 'slug') && (
<Column width={columnsSize.slug} align="left" sortable={!sortable} resizable>
<HeaderCell>{labels.slug}</HeaderCell>
<Cell dataKey="slug" />
</Column>
)}
{hasField(fields, 'language') && (
<Column width={columnsSize.language} resizable>
<HeaderCell>Language</HeaderCell>
<Cell>
{({ language }) => <Language>{language}</Language>}
</Cell>
</Column>
)}
{hasField(fields, 'categoryId') && (
<Column width={columnsSize.categoryId} align="left" resizable>
<HeaderCell>Category</HeaderCell>
<Cell dataKey="category">
{({ category }) => <span>{category != null ? category.name : ''}</span>}
</Cell>
</Column>
)}
{hasField(fields, 'body') && (
<Column width={300} flexGrow={1}>
<HeaderCell>Body</HeaderCell>
<Cell dataKey="body" />
</Column>
)}
{!_.isEmpty(columns) && (
columns
.filter(({ id }) => !['body', 'date', 'slug', 'category', 'language', 'title', 'createdAt'].includes(id))
.map(({ label, id, width = undefined, flex = undefined, cell }) => (
<Column
key={id}
width={width}
flexGrow={flex}
>
<HeaderCell>{label}</HeaderCell>
<Cell dataKey={id}>
{content => cell(content)}
</Cell>
</Column>
))
)}
<Column width={sortable ? 150 : 80} fixed>
<HeaderCell>Action</HeaderCell>
<Cell>
{function(content) {
return (
<ButtonGroup>
{sortable && (
<Fragment>
<Button
disabled={!table.current.getPrevious(content.id)}
size="xs"
onClick={async () => {
const previous = table.current.getPrevious(content.id);
if (previous) {
await swapOrder({
variables: {
id: content.id,
withId: previous
}
});
table.current.refetch();
}
}}
>
<Icon icon="up" />
</Button>
<Button
disabled={!table.current.getNext(content.id)}
size="xs"
onClick={async () => {
const next = table.current.getNext(content.id);
if (next) {
await swapOrder({
variables: {
id: content.id,
withId: next
}
});
table.current.refetch();
}}
}
>
<Icon icon="down" />
</Button>
</Fragment>
)}
<Button
disabled={disabled}
size="xs"
onClick={async () => {
if (confirm(`Delete "${content.title}"?`)) {
await deleteContent({ variables: { id: content.id }})
table.current.refetch();
}
}}
>
<Icon icon="trash" />
</Button>
<Button
disabled={disabled}
size="xs"
onClick={() => {
setContent(content)
}}
>
<Icon icon="edit2" />
</Button>
</ButtonGroup>
);
}}
</Cell>
</Column>
</CustomTable>
</PageContainer>
);
};
Contents.propTypes = ContentsType;
export default Contents;