import React, { useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { type TFunction } from 'i18next'; import { Button, DataTable, DataTableSkeleton, Dropdown, IconButton, InlineLoading, InlineNotification, Table, TableBody, TableCell, TableContainer, TableHead, TableHeader, TableRow, TableToolbar, TableToolbarContent, TableToolbarSearch, Tag, Tile, } from '@carbon/react'; import { Add, DocumentImport, Download, Edit, TrashCan } from '@carbon/react/icons'; import { type KeyedMutator, preload } from 'swr'; import { ConfigurableLink, navigate, openmrsFetch, restBaseUrl, showModal, showSnackbar, useConfig, useLayoutType, usePagination, } from '@openmrs/esm-framework'; import EmptyState from '../empty-state/empty-state.component'; import ErrorState from '../error-state/error-state.component'; import Header from '../header/header.component'; import { deleteForm } from '@resources/forms.resource'; import { FormBuilderPagination } from '../pagination'; import { useClobdata } from '@hooks/useClobdata'; import { useForms } from '@hooks/useForms'; import type { ConfigObject } from '../../config-schema'; import type { Form as TypedForm } from '@types'; import styles from './dashboard.scss'; type Mutator = KeyedMutator<{ data: { results: Array; }; }>; interface ActionButtonsProps { form: TypedForm; mutate: Mutator; responsiveSize: 'sm' | 'lg'; t: TFunction; } interface FormsListProps { forms: Array; isValidating: boolean; mutate: Mutator; t: TFunction; } function CustomTag({ condition }: { condition?: boolean }) { const { t } = useTranslation(); if (condition) { return ( {t('yes', 'Yes')} ); } return ( {t('no', 'No')} ); } function ActionButtons({ form, mutate, responsiveSize, t }: ActionButtonsProps) { const defaultEnterDelayInMs = 300; const { clobdata } = useClobdata(form); const formResources = form?.resources; const [isDeletingForm, setIsDeletingForm] = useState(false); const downloadableSchema = useMemo( () => new Blob([JSON.stringify(clobdata, null, 2)], { type: 'application/json', }), [clobdata], ); const handleDeleteForm = useCallback( async (formUuid: string) => { try { const res = await deleteForm(formUuid); if (res.status === 204) { showSnackbar({ title: t('formDeleted', 'Form deleted'), kind: 'success', isLowContrast: true, subtitle: t('formDeletedMessage', 'The form "{{- formName}}" has been deleted successfully', { formName: form.name, }), }); await mutate(); } } catch (e: unknown) { if (e instanceof Error) { showSnackbar({ title: t('errorDeletingForm', 'Error deleting form'), kind: 'error', subtitle: e?.message, }); } } finally { setIsDeletingForm(false); } }, [form.name, mutate, t], ); const launchDeleteFormModal = useCallback(() => { const dispose = showModal('delete-form-modal', { closeModal: () => dispose(), isDeletingForm, onDeleteForm: () => handleDeleteForm(form.uuid), }); }, [form.uuid, handleDeleteForm, isDeletingForm]); const ImportButton = () => { return ( navigate({ to: `${window.spaBase}/form-builder/edit/${form.uuid}` })} size={responsiveSize} > ); }; const EditButton = () => { return ( navigate({ to: `${window.spaBase}/form-builder/edit/${form.uuid}`, }) } size={responsiveSize} > ); }; const DownloadSchemaButton = () => { return ( ); }; const DeleteButton = () => { return ( ); }; return ( <> {formResources.length == 0 || !form?.resources[0] ? ( ) : ( <> )} ); } function FormsList({ forms, isValidating, mutate, t }: FormsListProps) { const pageSize = 10; const isTablet = useLayoutType() === 'tablet'; const responsiveSize: 'sm' | 'lg' = isTablet ? 'lg' : 'sm'; const [filter, setFilter] = useState(''); const [searchString, setSearchString] = useState(''); const filteredRows = useMemo(() => { if (!filter) { return forms; } if (filter === 'Published') { return forms.filter((form) => form.published); } if (filter === 'Unpublished') { return forms.filter((form) => !form.published); } if (filter === 'Retired') { return forms.filter((form) => form.retired); } return forms; }, [filter, forms]); const tableHeaders = [ { header: t('name', 'Name'), key: 'name', }, { header: t('version', 'Version'), key: 'version', }, { header: t('published', 'Published'), key: 'published', }, { header: t('retired', 'Retired'), key: 'retired', }, { header: t('schemaActions', 'Schema actions'), key: 'actions', }, ]; const editSchemaUrl = '${openmrsSpaBase}/form-builder/edit/${formUuid}'; const searchResults = useMemo(() => { if (searchString && searchString.trim() !== '') { return filteredRows.filter((form) => form.name.toLowerCase().includes(searchString.toLowerCase())); } return filteredRows; }, [searchString, filteredRows]); const { paginated, goTo, results, currentPage } = usePagination(searchResults, pageSize); const tableRows = results?.map((form: TypedForm) => ({ ...form, id: form?.uuid, name: ( void preload(`${restBaseUrl}/form/${form?.uuid}?v=full`, openmrsFetch)} > {form.name} ), published: , retired: , actions: , })); const handleFilter = ({ selectedItem }: { selectedItem: string }) => setFilter(selectedItem); const handleSearch = useCallback( (e: React.ChangeEvent) => { goTo(1); setSearchString(e.target.value); }, [goTo, setSearchString], ); return ( <>
{isValidating ? : null}
{({ rows, headers, getTableProps, getHeaderProps, getRowProps }) => ( <>
{headers.map((header) => ( {header.header} ))} {rows.map((row) => ( {row.cells.map((cell) => ( {cell.value} ))} ))}
{rows.length === 0 ? (

{t('noMatchingFormsToDisplay', 'No matching forms to display')}

{t('checkFilters', 'Check the filters above')}

) : null} )}
{paginated && ( { goTo(page); }} pageNumber={currentPage} pageSize={pageSize} /> )} ); } const Dashboard: React.FC = () => { const { t } = useTranslation(); const { forms, error, isLoading, isValidating, mutate } = useForms(); const { showSchemaSaveWarning } = useConfig(); return (
{(() => { if (error) { return ; } if (isLoading) { return ; } if (forms.length === 0) { return ; } return ( <> {showSchemaSaveWarning && ( )} ); })()}
); }; export default Dashboard;