-
Notifications
You must be signed in to change notification settings - Fork 81
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
1,919 additions
and
2,211 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
151 changes: 151 additions & 0 deletions
151
app/react/V2/Routes/Settings/ParagraphExtraction/ParagraphExtraction.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
/* eslint-disable max-statements */ | ||
import React, { useMemo, useState } from 'react'; | ||
import { IncomingHttpHeaders } from 'http'; | ||
import { LoaderFunction, useLoaderData, useRevalidator } from 'react-router-dom'; | ||
import * as extractorsAPI from 'app/V2/api/paragraphExtractor/extractors'; | ||
import * as templatesAPI from 'V2/api/templates'; | ||
import { SettingsContent } from 'V2/Components/Layouts/SettingsContent'; | ||
import { ClientTemplateSchema } from 'app/istore'; | ||
import { Button, Table, ConfirmationModal } from 'V2/Components/UI'; | ||
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 { List } from './components/List'; | ||
|
||
const getTemplateName = (templates: ClientTemplateSchema[], targetId: string) => { | ||
const foundTemplate = templates.find(template => template._id === targetId); | ||
return foundTemplate?.name || targetId; | ||
}; | ||
|
||
const formatExtractors = ( | ||
extractors: Extractor[], | ||
templates: ClientTemplateSchema[] | ||
): TableExtractor[] => | ||
(extractors || []).map(extractor => { | ||
const targetTemplateName = getTemplateName(templates, extractor.templateTo); | ||
const originTemplateNames = extractor.templateFrom.map(templateFrom => | ||
getTemplateName(templates, templateFrom) | ||
); | ||
|
||
return { ...extractor, rowId: extractor._id, originTemplateNames, targetTemplateName }; | ||
}); | ||
|
||
const ParagraphExtractorDashboard = () => { | ||
const { extractors = [], templates } = useLoaderData() as { | ||
extractors: TableExtractor[]; | ||
templates: ClientTemplateSchema[]; | ||
}; | ||
const [isSaving, setIsSaving] = useState(false); | ||
const revalidator = useRevalidator(); | ||
const [selected, setSelected] = useState<TableExtractor[]>([]); | ||
const [confirmModal, setConfirmModal] = useState(false); | ||
const setNotifications = useSetAtom(notificationAtom); | ||
|
||
const deleteExtractors = async () => { | ||
setIsSaving(true); | ||
const extractorIds = selected?.map(selection => selection._id) as string[]; | ||
|
||
try { | ||
await extractorsAPI.remove(extractorIds); | ||
revalidator.revalidate(); | ||
setNotifications({ | ||
type: 'success', | ||
text: <Translate>Extractor/s deleted</Translate>, | ||
}); | ||
} catch (error) { | ||
setNotifications({ | ||
type: 'error', | ||
text: <Translate>An error occurred</Translate>, | ||
details: error.json?.prettyMessage ? error.json.prettyMessage : undefined, | ||
}); | ||
} finally { | ||
setIsSaving(false); | ||
} | ||
}; | ||
// const handleSave = async (extractor: IXExtractorInfo) => {}; | ||
const paragraphExtractorData = useMemo( | ||
() => formatExtractors(extractors, templates), | ||
[extractors, templates] | ||
); | ||
|
||
return ( | ||
<div | ||
className="tw-content" | ||
data-testid="settings-ix" | ||
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} | ||
header={ | ||
<Translate className="text-base font-semibold text-left text-gray-900 bg-white"> | ||
Extractors | ||
</Translate> | ||
} | ||
enableSelections | ||
onChange={({ selectedRows }) => { | ||
setSelected(() => paragraphExtractorData.filter(ex => ex.rowId in selectedRows)); | ||
}} | ||
// what default sorting to use? | ||
defaultSorting={[{ id: 'status', desc: false }]} | ||
/> | ||
</SettingsContent.Body> | ||
|
||
<SettingsContent.Footer className="flex gap-2"> | ||
{selected?.length === 1 ? ( | ||
<Button type="button" onClick={() => {}} disabled={isSaving}> | ||
<Translate>Edit Extractor</Translate> | ||
</Button> | ||
) : undefined} | ||
|
||
{selected?.length ? ( | ||
<Button | ||
type="button" | ||
color="error" | ||
onClick={() => setConfirmModal(true)} | ||
disabled={isSaving} | ||
> | ||
<Translate>Delete</Translate> | ||
</Button> | ||
) : ( | ||
<Button type="button" onClick={() => {}} disabled={isSaving}> | ||
<Translate>Create Extractor</Translate> | ||
</Button> | ||
)} | ||
</SettingsContent.Footer> | ||
</SettingsContent> | ||
|
||
{confirmModal && ( | ||
<ConfirmationModal | ||
header="Delete extractors" | ||
warningText="Do you want to delete the following items?" | ||
body={<List items={selected || []} />} | ||
onAcceptClick={async () => { | ||
await deleteExtractors(); | ||
setConfirmModal(false); | ||
setSelected([]); | ||
}} | ||
onCancelClick={() => setConfirmModal(false)} | ||
dangerStyle | ||
/> | ||
)} | ||
</div> | ||
); | ||
}; | ||
|
||
const ParagraphExtractorLoader = | ||
(headers?: IncomingHttpHeaders): LoaderFunction => | ||
async () => { | ||
const extractors = await extractorsAPI.get(); | ||
const templates = await templatesAPI.get(headers); | ||
return { extractors, templates }; | ||
}; | ||
|
||
export { ParagraphExtractorDashboard, ParagraphExtractorLoader }; |
19 changes: 19 additions & 0 deletions
19
app/react/V2/Routes/Settings/ParagraphExtraction/components/List.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import React from 'react'; | ||
import { Translate } from 'app/I18N'; | ||
import { TableExtractor } from '../types'; | ||
|
||
const List = ({ items }: { items: TableExtractor[] }) => ( | ||
<ul className="flex flex-wrap gap-8 max-w-md list-disc"> | ||
{/* what should be displayed on the confirm modal? */} | ||
{items.map(item => ( | ||
<li key={item._id}> | ||
<Translate>Templates: </Translate> | ||
{item.originTemplateNames.join(', ')} | ||
<Translate>Target Template:</Translate> | ||
{item.targetTemplateName} | ||
</li> | ||
))} | ||
</ul> | ||
); | ||
|
||
export { List }; |
87 changes: 87 additions & 0 deletions
87
app/react/V2/Routes/Settings/ParagraphExtraction/components/TableElements.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
/* eslint-disable max-lines */ | ||
/* eslint-disable react/no-multi-comp */ | ||
import React from 'react'; | ||
import { CellContext, createColumnHelper } from '@tanstack/react-table'; | ||
import { Link } from 'react-router-dom'; | ||
import { Translate } from 'app/I18N'; | ||
import { Button, Pill } from 'V2/Components/UI'; | ||
import { TableExtractor } from '../types'; | ||
|
||
const extractorColumnHelper = createColumnHelper<TableExtractor>(); | ||
|
||
const TemplateFromHeader = () => <Translate>Template</Translate>; | ||
const TemplateToHeader = () => <Translate>Target Template</Translate>; | ||
const DocumentsHeader = () => <Translate>Documents</Translate>; | ||
const GeneratedEntitiesHeader = () => <Translate>Generated Entities</Translate>; | ||
const ActionHeader = () => <Translate className="">Action</Translate>; | ||
|
||
const NumericCell = ({ | ||
cell, | ||
}: CellContext< | ||
TableExtractor, | ||
TableExtractor['documents'] | TableExtractor['generatedEntities'] | ||
>) => <span className="text-sm font-normal text-gray-500">{cell.getValue()}</span>; | ||
|
||
const TemplatesCell = ({ cell }: CellContext<TableExtractor, TableExtractor['templateTo']>) => ( | ||
<div className="flex flex-wrap gap-2"> | ||
<div key={cell.getValue()} className="whitespace-nowrap"> | ||
<Pill color="gray">{cell.getValue()}</Pill> | ||
</div> | ||
</div> | ||
); | ||
|
||
const TemplateFromCell = ({ | ||
cell, | ||
}: CellContext<TableExtractor, TableExtractor['templateFrom']>) => ( | ||
<div className="flex flex-wrap gap-2"> | ||
{cell.getValue().map(value => ( | ||
<div key={value} className="whitespace-nowrap"> | ||
<Pill color="gray">{value}</Pill> | ||
</div> | ||
))} | ||
</div> | ||
); | ||
|
||
const LinkButton = ({ cell }: CellContext<TableExtractor, TableExtractor['_id']>) => ( | ||
<Link to={`suggestions/${cell.getValue()}`}> | ||
<Button className="leading-4" styling="outline"> | ||
<Translate>View</Translate> | ||
</Button> | ||
</Link> | ||
); | ||
|
||
// todo: fix width of each column | ||
const extractorsTableColumns = [ | ||
extractorColumnHelper.accessor('originTemplateNames', { | ||
header: TemplateFromHeader, | ||
enableSorting: true, | ||
cell: TemplateFromCell, | ||
meta: { headerClassName: 'w-4/6' }, | ||
}), | ||
extractorColumnHelper.accessor('targetTemplateName', { | ||
header: TemplateToHeader, | ||
enableSorting: true, | ||
cell: TemplatesCell, | ||
meta: { headerClassName: 'w-4/6' }, | ||
}), | ||
extractorColumnHelper.accessor('documents', { | ||
header: DocumentsHeader, | ||
enableSorting: true, | ||
cell: NumericCell, | ||
meta: { headerClassName: 'w-4/6' }, | ||
}), | ||
extractorColumnHelper.accessor('generatedEntities', { | ||
header: GeneratedEntitiesHeader, | ||
enableSorting: true, | ||
cell: NumericCell, | ||
meta: { headerClassName: 'w-4/6' }, | ||
}), | ||
extractorColumnHelper.accessor('_id', { | ||
header: ActionHeader, | ||
enableSorting: true, | ||
cell: LinkButton, | ||
meta: { headerClassName: '' }, | ||
}), | ||
]; | ||
|
||
export { extractorsTableColumns }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
export type Extractor = { | ||
_id: string; | ||
templateFrom: string[]; | ||
templateTo: string; | ||
documents: number; | ||
generatedEntities: number; | ||
}; | ||
|
||
export type TableExtractor = Extractor & { | ||
rowId: string; | ||
targetTemplateName: string; | ||
originTemplateNames: string[]; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import { IncomingHttpHeaders } from 'http'; | ||
import api from 'app/utils/api'; | ||
import { RequestParams } from 'app/utils/RequestParams'; | ||
import { IXExtractorInfo } from 'V2/shared/types'; | ||
|
||
let dummyData = [ | ||
{ | ||
_id: '1', | ||
templateFrom: ['66fbe4f28542cc5545e05a46', '66fbe4d28542cc5545e0599c'], | ||
templateTo: '5bfbb1a0471dd0fc16ada146', | ||
documents: 831, | ||
generatedEntities: 12000, | ||
rowId: '1', | ||
status: '', | ||
}, | ||
{ | ||
_id: '2', | ||
templateFrom: ['66fbe4d28542cc5545e0599c', 'Judge Documents'], | ||
templateTo: '66fbe4f28542cc5545e05a46', | ||
documents: 500, | ||
generatedEntities: 12001, | ||
rowId: '1', | ||
status: '', | ||
}, | ||
]; | ||
|
||
const apiEndpoint = 'paragraph-extractor'; | ||
|
||
const get = async () => | ||
new Promise(resolve => { | ||
setTimeout(() => resolve(dummyData)); | ||
}); | ||
|
||
const getById = async (extractorId: string, headers?: IncomingHttpHeaders) => { | ||
try { | ||
const requestParams = new RequestParams({ id: extractorId }, headers); | ||
const { json: response } = await api.get(apiEndpoint, requestParams); | ||
return response; | ||
} catch (e) { | ||
return e; | ||
} | ||
}; | ||
|
||
const save = async (extractor: IXExtractorInfo) => { | ||
const requestParams = new RequestParams(extractor); | ||
let response: IXExtractorInfo[]; | ||
if (extractor._id) { | ||
response = await api.put(apiEndpoint, requestParams); | ||
} else { | ||
response = await api.post(apiEndpoint, requestParams); | ||
} | ||
return response; | ||
}; | ||
|
||
const remove = async (ids: string[]) => { | ||
// const requestParams = new RequestParams({ ids }); | ||
// const response = await api.delete(apiEndpoint, requestParams); | ||
// return response; | ||
dummyData = dummyData.filter(data => !ids.includes(data._id)); | ||
console.log(dummyData); | ||
return true; | ||
}; | ||
|
||
export { get, save, remove, getById }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.