Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

6841 create/update paragraph extractor modal #7342

Merged
merged 10 commits into from
Oct 11, 2024
12 changes: 12 additions & 0 deletions app/react/App/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -1824,6 +1824,10 @@ input[type="range"]::-ms-fill-lower {
margin-top: 1rem;
}

.mt-5 {
margin-top: 1.25rem;
}

.mt-6 {
margin-top: 1.5rem;
}
Expand Down Expand Up @@ -1981,6 +1985,14 @@ input[type="range"]::-ms-fill-lower {
max-height: 100svh;
}

.min-h-\[300px\] {
min-height: 300px;
}

.min-h-\[327px\] {
min-height: 327px;
}

.min-h-fit {
min-height: -moz-fit-content;
min-height: fit-content;
Expand Down
11 changes: 11 additions & 0 deletions app/react/V2/Components/Forms/MultiselectList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import React, { useEffect, useState, useRef } from 'react';
import { Translate } from 'app/I18N';
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/24/outline';
import { isString } from 'lodash';
import { InputField, RadioSelect } from '.';
import { Pill } from '../UI/Pill';
import { Label } from './Label';
Expand All @@ -31,8 +32,12 @@ interface MultiselectListProps {
startOnSelected?: boolean;
search?: string;
suggestions?: boolean;
blankState?: string | React.ReactNode;
}

const renderChild = (child: string | React.ReactNode) =>
isString(child) ? <Translate>{child}</Translate> : child;

const MultiselectList = ({
items,
onChange,
Expand All @@ -47,6 +52,7 @@ const MultiselectList = ({
startOnSelected = false,
search = '',
suggestions = false,
blankState = <Translate>No items available</Translate>,
}: MultiselectListProps) => {
const [selectedItems, setSelectedItems] = useState<string[]>(value || []);
const [showAll, setShowAll] = useState<boolean>(!(startOnSelected && selectedItems.length));
Expand Down Expand Up @@ -345,6 +351,11 @@ const MultiselectList = ({
</div>
</div>

{items.length === 0 && (
<div className="flex w-full h-full items-center justify-center min-h-[300px]">
{renderChild(blankState)}
</div>
)}
<ul className="w-full px-2 pt-2 grow" ref={optionsRef}>
{filteredItems.map(renderItem)}
</ul>
Expand Down
43 changes: 43 additions & 0 deletions app/react/V2/Components/Forms/specs/MultiselectList.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -223,4 +223,47 @@ describe('MultiselectList.cy.tsx', () => {
cy.contains('Pepperoni').should('be.visible');
});
});

// add blank state test
describe('blank state property', () => {
it('should show blank state property if there is no items passed to the component', () => {
cy.viewport(450, 650);
mount(
<Provider store={createStore()}>
<div className="p-2 tw-content">
<MultiselectList onChange={() => {}} items={[]} />
</div>
</Provider>
);
cy.contains('No items available').should('be.visible');
});

it('should accept a blank state string', () => {
cy.viewport(450, 650);
mount(
<Provider store={createStore()}>
<div className="p-2 tw-content">
<MultiselectList onChange={() => {}} items={[]} blankState="nada" />
</div>
</Provider>
);
cy.contains('nada').should('be.visible');
});

it('should accept a blank state component', () => {
cy.viewport(450, 650);
mount(
<Provider store={createStore()}>
<div className="p-2 tw-content">
<MultiselectList
onChange={() => {}}
items={[]}
blankState={<div>no items string</div>}
/>
</div>
</Provider>
);
cy.contains('no items string').should('be.visible');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,36 +11,44 @@ import { Translate } from 'app/I18N';
import { useSetAtom } from 'jotai';
import { notificationAtom } from 'V2/atoms';
import { extractorsTableColumns } from './components/TableElements';
import { TableExtractor, Extractor } from './types';
import { TableParagraphExtractor, ParagraphExtractorApiResponse } from './types';
import { List } from './components/List';
import { ExtractorModal } from './components/ExtractorModal';

const getTemplateName = (templates: ClientTemplateSchema[], targetId: string) => {
const foundTemplate = templates.find(template => template._id === targetId);
return foundTemplate?.name || targetId;
};

const formatExtractors = (
extractors: Extractor[],
extractors: ParagraphExtractorApiResponse[],
templates: ClientTemplateSchema[]
): TableExtractor[] =>
(extractors || []).map(extractor => {
): TableParagraphExtractor[] =>
extractors.map(extractor => {
const targetTemplateName = getTemplateName(templates, extractor.templateTo);
const originTemplateNames = extractor.templateFrom.map(templateFrom =>
const originTemplateNames = (extractor.templatesFrom || []).map(templateFrom =>
getTemplateName(templates, templateFrom)
);

return { ...extractor, rowId: extractor._id, originTemplateNames, targetTemplateName };
return {
...extractor,
rowId: extractor._id || '',
originTemplateNames,
targetTemplateName,
};
});

const ParagraphExtractorDashboard = () => {
const { extractors = [], templates } = useLoaderData() as {
extractors: TableExtractor[];
extractors: ParagraphExtractorApiResponse[];
templates: ClientTemplateSchema[];
};
const [isSaving, setIsSaving] = useState(false);

const revalidator = useRevalidator();
const [selected, setSelected] = useState<TableExtractor[]>([]);
const [isSaving, setIsSaving] = useState(false);
const [selected, setSelected] = useState<TableParagraphExtractor[]>([]);
const [confirmModal, setConfirmModal] = useState(false);
const [extractorModal, setExtractorModal] = useState(false);
const setNotifications = useSetAtom(notificationAtom);

const deleteExtractors = async () => {
Expand All @@ -64,7 +72,15 @@ const ParagraphExtractorDashboard = () => {
setIsSaving(false);
}
};
// const handleSave = async (extractor: IXExtractorInfo) => {};

const handleSave = async () => {
revalidator.revalidate();
setNotifications({
type: 'success',
text: <Translate>Paragraph Extractor added</Translate>,
});
};

const paragraphExtractorData = useMemo(
() => formatExtractors(extractors, templates),
[extractors, templates]
Expand All @@ -73,14 +89,13 @@ const ParagraphExtractorDashboard = () => {
return (
<div
className="tw-content"
data-testid="settings-ix"
data-testid="settings-paragraph-extractor"
style={{ width: '100%', overflowY: 'auto' }}
>
<SettingsContent>
<SettingsContent.Header title="Paragraph extraction" />

<SettingsContent.Body>
{/* should create a component for empty data? */}
<Table
data={paragraphExtractorData}
columns={extractorsTableColumns}
Expand All @@ -100,7 +115,7 @@ const ParagraphExtractorDashboard = () => {

<SettingsContent.Footer className="flex gap-2">
{selected?.length === 1 ? (
<Button type="button" onClick={() => {}} disabled={isSaving}>
<Button type="button" onClick={() => setExtractorModal(true)} disabled={isSaving}>
<Translate>Edit Extractor</Translate>
</Button>
) : undefined}
Expand All @@ -115,7 +130,7 @@ const ParagraphExtractorDashboard = () => {
<Translate>Delete</Translate>
</Button>
) : (
<Button type="button" onClick={() => {}} disabled={isSaving}>
<Button type="button" onClick={() => setExtractorModal(true)} disabled={isSaving}>
<Translate>Create Extractor</Translate>
</Button>
)}
Expand All @@ -136,6 +151,16 @@ const ParagraphExtractorDashboard = () => {
dangerStyle
/>
)}

{extractorModal && (
<ExtractorModal
setShowModal={setExtractorModal}
onClose={() => setExtractorModal(false)}
onAccept={handleSave}
templates={templates}
extractor={selected?.length ? selected[0] : undefined}
/>
)}
</div>
);
};
Expand Down
Loading
Loading