META-INF.resources.js.data_set.visualization_modes.modes.Table.tsx Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com.liferay.frontend.data.set.admin.web
Show all versions of com.liferay.frontend.data.set.admin.web
Liferay Frontend Data Set Admin Web
/**
* SPDX-FileCopyrightText: (c) 2000 Liferay, Inc. https://liferay.com
* SPDX-License-Identifier: LGPL-2.1-or-later OR LicenseRef-Liferay-DXP-EULA-2.0.0-2023-06
*/
import ClayButton from '@clayui/button';
import ClayDropDown from '@clayui/drop-down';
import ClayForm, {ClayCheckbox, ClayInput} from '@clayui/form';
import ClayLabel from '@clayui/label';
import ClayLayout from '@clayui/layout';
import ClayLoadingIndicator from '@clayui/loading-indicator';
import ClayModal from '@clayui/modal';
import {
FDS_INTERNAL_CELL_RENDERERS,
IClientExtensionRenderer,
IInternalRenderer,
} from '@liferay/frontend-data-set-web';
import {InputLocalized} from 'frontend-js-components-web';
import {fetch, openModal} from 'frontend-js-web';
import fuzzy from 'fuzzy';
import React, {useEffect, useState} from 'react';
import FieldSelectModalContent, {
visit,
} from '../../../components/FieldSelectModalContent';
import OrderableTable from '../../../components/OrderableTable';
import {
API_URL,
DEFAULT_FETCH_HEADERS,
FUZZY_OPTIONS,
OBJECT_RELATIONSHIP,
} from '../../../utils/constants';
import openDefaultFailureToast from '../../../utils/openDefaultFailureToast';
import openDefaultSuccessToast from '../../../utils/openDefaultSuccessToast';
import {IDataSetSectionProps} from '../../DataSet';
import '../../../../css/TableVisualizationMode.scss';
import ClayAlert from '@clayui/alert';
import ClayIcon from '@clayui/icon';
import sortItems from '../../../utils/sortItems';
import {
EFieldType,
IFDSField,
IField,
IFieldTreeItem,
} from '../../../utils/types';
const defaultLanguageId = Liferay.ThemeDisplay.getDefaultLanguageId();
const getRendererLabel = ({
cetRenderers = [],
rendererName,
}: {
cetRenderers?: IClientExtensionRenderer[];
rendererName: string;
}): string => {
let clientExtensionRenderer;
const internalRenderer = FDS_INTERNAL_CELL_RENDERERS.find(
(renderer: IInternalRenderer) => {
return renderer.name === rendererName;
}
);
if (internalRenderer?.label) {
return internalRenderer.label;
}
else {
clientExtensionRenderer = cetRenderers.find(
(renderer: IClientExtensionRenderer) => {
return renderer.externalReferenceCode === rendererName;
}
);
if (clientExtensionRenderer?.name) {
return clientExtensionRenderer.name;
}
return rendererName;
}
};
interface IRendererLabelCellRendererComponentProps {
cetRenderers?: IClientExtensionRenderer[];
item: IFDSField;
query: string;
}
const RendererLabelCellRendererComponent = ({
cetRenderers = [],
item,
query,
}: IRendererLabelCellRendererComponentProps) => {
const itemFieldValue = getRendererLabel({
cetRenderers,
rendererName: item.renderer,
});
const fuzzyMatch = fuzzy.match(query, itemFieldValue, FUZZY_OPTIONS);
return (
{fuzzyMatch ? (
) : (
{itemFieldValue}
)}
);
};
interface IEditFDSFieldModalContentProps {
closeModal: Function;
fdsClientExtensionCellRenderers: IClientExtensionRenderer[];
fdsField: IFDSField;
namespace: string;
onSaveButtonClick: Function;
sortable: boolean;
}
const EditFDSFieldModalContent = ({
closeModal,
fdsClientExtensionCellRenderers,
fdsField,
namespace,
onSaveButtonClick,
sortable,
}: IEditFDSFieldModalContentProps) => {
const [selectedFDSFieldRenderer, setSelectedFDSFieldRenderer] = useState(
fdsField.renderer ?? 'default'
);
const [fdsFieldSortable, setFSDFieldSortable] = useState(
fdsField.sortable
);
const fdsInternalCellRendererNames = FDS_INTERNAL_CELL_RENDERERS.map(
(cellRenderer: IInternalRenderer) => cellRenderer.name
);
const fdsFieldTranslations = fdsField.label_i18n;
const [i18nFieldLabels, setI18nFieldLabels] =
useState(fdsFieldTranslations);
const editFDSField = async () => {
const body = {
label_i18n: i18nFieldLabels,
renderer: selectedFDSFieldRenderer,
rendererType: !fdsInternalCellRendererNames.includes(
selectedFDSFieldRenderer
)
? 'clientExtension'
: 'internal',
sortable: fdsFieldSortable,
};
const response = await fetch(
`${API_URL.TABLE_SECTIONS}/by-external-reference-code/${fdsField.externalReferenceCode}`,
{
body: JSON.stringify(body),
headers: DEFAULT_FETCH_HEADERS,
method: 'PATCH',
}
);
if (!response.ok) {
openDefaultFailureToast();
return;
}
const editedFDSField = await response.json();
closeModal();
onSaveButtonClick({editedFDSField});
openDefaultSuccessToast();
};
const fdsFieldNameInputId = `${namespace}fdsFieldNameInput`;
const fdsFieldLabelInputId = `${namespace}fdsFieldLabelInput`;
const fdsFieldRendererSelectId = `${namespace}fdsFieldRendererSelectId`;
const options = FDS_INTERNAL_CELL_RENDERERS.map(
(renderer: IInternalRenderer) => ({
label: renderer.label!,
value: renderer.name!,
})
);
options.push(
...fdsClientExtensionCellRenderers.map((item) => ({
label: item.name!,
value: item.externalReferenceCode!,
}))
);
const CellRendererDropdown = ({
cellRenderers,
namespace,
onItemClick,
}: {
cellRenderers: {
label: string;
value: string;
}[];
namespace: string;
onItemClick: Function;
}) => {
const fdsClientExtensionCellRenderersERCs =
fdsClientExtensionCellRenderers.map(
(cellRendererCET) => cellRendererCET.externalReferenceCode
);
return (
{selectedFDSFieldRenderer
? getRendererLabel({
cetRenderers:
fdsClientExtensionCellRenderers,
rendererName: selectedFDSFieldRenderer,
})
: Liferay.Language.get('choose-an-option')}
}
>
{cellRenderers.map((cellRenderer) => (
onItemClick(cellRenderer.value)}
roleItem="option"
>
{cellRenderer.label}
{fdsClientExtensionCellRenderersERCs.includes(
cellRenderer.value
) && (
{Liferay.Language.get('client-extension')}
)}
))}
);
};
return (
<>
{Liferay.Util.sub(
Liferay.Language.get('edit-x'),
fdsField.label_i18n[defaultLanguageId] ?? fdsField.name
)}
setSelectedFDSFieldRenderer(item)
}
/>
setFSDFieldSortable(checked)
}
/>
{fdsField.type !== EFieldType.OBJECT && (
)}
editFDSField()}>
{Liferay.Language.get('save')}
closeModal()}
>
{Liferay.Language.get('cancel')}
}
/>
>
);
};
function Table(props: IDataSetSectionProps & {title?: string}) {
const {
dataSet,
fdsClientExtensionCellRenderers,
fieldTreeItems,
namespace,
saveFDSFieldsURL,
title,
} = props;
const [fdsFields, setFDSFields] = useState | null>(null);
const [saveButtonDisabled, setSaveButtonDisabled] = useState(false);
const getFDSFields = async () => {
const response = await fetch(
`${API_URL.TABLE_SECTIONS}?filter=(${OBJECT_RELATIONSHIP.DATA_SET_TABLE_SECTION_ID} eq '${dataSet.id}')&nestedFields=${OBJECT_RELATIONSHIP.DATA_SET_TABLE_SECTION}&sort=dateCreated:asc`,
{
headers: DEFAULT_FETCH_HEADERS,
}
);
if (!response.ok) {
openDefaultFailureToast();
return null;
}
const responseJSON = await response.json();
const storedFDSFields: IFDSField[] = responseJSON?.items;
if (!storedFDSFields) {
openDefaultFailureToast();
return null;
}
const fdsFieldsOrder =
// @ts-ignore
storedFDSFields?.[0]?.[OBJECT_RELATIONSHIP.DATA_SET_TABLE_SECTION]
?.fdsFieldsOrder;
setFDSFields(sortItems(storedFDSFields, fdsFieldsOrder) as IFDSField[]);
};
const onDeleteButtonClick = ({item}: {item: IFDSField}) => {
openModal({
bodyHTML: Liferay.Language.get(
'are-you-sure-you-want-to-delete-this-field?-fragments-using-it-will-be-affected'
),
buttons: [
{
autoFocus: true,
displayType: 'secondary',
label: Liferay.Language.get('cancel'),
type: 'cancel',
},
{
displayType: 'warning',
label: Liferay.Language.get('delete'),
onClick: async ({
processClose,
}: {
processClose: Function;
}) => {
processClose();
const url = `${API_URL.TABLE_SECTIONS}/${item.id}`;
const response = await fetch(url, {method: 'DELETE'});
if (!response.ok) {
openDefaultFailureToast();
return;
}
openDefaultSuccessToast();
setFDSFields(
fdsFields?.filter(
(fdsField: IFDSField) => fdsField.id !== item.id
) || []
);
},
},
],
status: 'warning',
title: Liferay.Language.get('delete-filter'),
});
};
const saveFDSFields = async ({
closeModal,
fields,
}: {
closeModal: Function;
fields: Array;
}) => {
setSaveButtonDisabled(true);
const creationData: Array<{
name: string;
sortable: boolean;
type: string;
}> = [];
const deletionIds: Array = [];
fields.forEach((field) => {
if (!field.id) {
creationData.push({
name: field.name,
sortable: field.sortable || false,
type: field.type || 'string',
});
}
});
fdsFields?.forEach((fdsField) => {
if (!fields.find((field) => field.name === fdsField.name)) {
deletionIds.push(fdsField.id);
}
});
const formData = new FormData();
formData.append(
`${namespace}creationData`,
JSON.stringify(creationData)
);
deletionIds.forEach((id) => {
formData.append(`${namespace}deletionIds`, String(id));
});
formData.append(`${namespace}dataSetId`, dataSet.id);
const response = await fetch(saveFDSFieldsURL, {
body: formData,
method: 'POST',
});
setSaveButtonDisabled(false);
if (!response.ok) {
openDefaultFailureToast();
return;
}
const createdFDSFields: Array = await response.json();
closeModal();
const newFDSFields: Array = [];
fdsFields?.forEach((fdsField) => {
if (!deletionIds.includes(fdsField.id)) {
newFDSFields.push(fdsField);
}
});
createdFDSFields.forEach((fdsField) => {
newFDSFields.push(fdsField);
});
setFDSFields(newFDSFields);
openDefaultSuccessToast();
};
const updateFDSFieldsOrder = async ({
fdsFieldsOrder,
}: {
fdsFieldsOrder: string;
}) => {
const body = {
fdsFieldsOrder,
};
const response = await fetch(
`${API_URL.DATA_SETS}/by-external-reference-code/${dataSet.externalReferenceCode}`,
{
body: JSON.stringify(body),
headers: DEFAULT_FETCH_HEADERS,
method: 'PATCH',
}
);
if (!response.ok) {
openDefaultFailureToast();
return null;
}
const responseJSON = await response.json();
const storedFDSFieldsOrder = responseJSON?.fdsFieldsOrder;
if (
fdsFields &&
storedFDSFieldsOrder &&
storedFDSFieldsOrder === fdsFieldsOrder
) {
setFDSFields(
sortItems(fdsFields, storedFDSFieldsOrder) as IFDSField[]
);
openDefaultSuccessToast();
}
else {
openDefaultFailureToast();
}
};
useEffect(() => {
getFDSFields();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const onCreationButtonClick = () => {
openModal({
contentComponent: ({closeModal}: {closeModal: Function}) => (
;
}) => {
saveFDSFields({closeModal, fields: selectedFields});
}}
saveButtonDisabled={saveButtonDisabled}
selectedFields={
fdsFields
? fdsFields.map((fdsField) => ({
id: String(fdsField.id),
name: fdsField.name,
}))
: []
}
selectionMode="multiple"
/>
),
size: 'full-screen',
});
};
const onEditButtonClick = ({item}: {item: IFDSField}) => {
openModal({
className: 'overflow-auto',
contentComponent: ({closeModal}: {closeModal: Function}) => (
{
setFDSFields(
fdsFields?.map((fdsField) => {
if (fdsField.name === editedFDSField.name) {
return editedFDSField;
}
return fdsField;
}) || null
);
}}
sortable={isSortable(fieldTreeItems, item)}
/>
),
});
};
return fdsFields ? (
{Liferay.Language.get(
'this-visualization-mode-will-not-be-shown-until-you-assign-at-least-one-field'
)}
(
),
textMatch: (item: IFDSField) =>
getRendererLabel({
cetRenderers:
fdsClientExtensionCellRenderers,
rendererName: item.renderer,
}),
},
label: Liferay.Language.get('renderer'),
name: 'renderer',
},
{
label: Liferay.Language.get('sortable'),
name: 'sortable',
},
]}
items={fdsFields}
noItemsButtonLabel={Liferay.Language.get('add-fields')}
noItemsDescription={Liferay.Language.get(
'add-fields-to-show-in-your-view'
)}
noItemsTitle={Liferay.Language.get('no-fields-added-yet')}
onOrderChange={({order}: {order: string}) => {
updateFDSFieldsOrder({
fdsFieldsOrder: order,
});
}}
title={title}
/>
) : (
);
}
export function Fields(props: IDataSetSectionProps) {
return (
);
}
function isSortable(
fieldTreeItems: Array,
selectedItem: IFDSField
): boolean {
let isSortable = false;
visit(fieldTreeItems, (fieldTreeItem: IFieldTreeItem) => {
if (fieldTreeItem.name === selectedItem.name) {
isSortable = fieldTreeItem.sortable || false;
return;
}
});
return isSortable;
}
export default Table;