2021-09-21 13:48:28 +00:00
|
|
|
/* eslint-disable no-unused-vars */
|
|
|
|
|
/* eslint-disable react-hooks/exhaustive-deps */
|
2021-09-27 03:42:22 +00:00
|
|
|
import React, { useMemo, useState, useEffect, useCallback } from 'react';
|
2021-04-30 06:31:32 +00:00
|
|
|
import {
|
|
|
|
|
useTable,
|
|
|
|
|
useFilters,
|
|
|
|
|
useSortBy,
|
|
|
|
|
useGlobalFilter,
|
|
|
|
|
useAsyncDebounce,
|
|
|
|
|
usePagination,
|
|
|
|
|
useBlockLayout,
|
2021-09-21 13:48:28 +00:00
|
|
|
useResizeColumns,
|
2021-10-05 07:07:17 +00:00
|
|
|
useRowSelect,
|
2021-04-30 06:31:32 +00:00
|
|
|
} from 'react-table';
|
2021-09-27 12:57:06 +00:00
|
|
|
import cx from 'classnames';
|
2021-09-06 08:50:38 +00:00
|
|
|
import { resolveReferences, resolveWidgetFieldValue, validateWidget } from '@/_helpers/utils';
|
2021-04-24 16:32:14 +00:00
|
|
|
import SelectSearch, { fuzzySearch } from 'react-select-search';
|
2021-04-30 06:31:32 +00:00
|
|
|
import { useExportData } from 'react-table-plugins';
|
|
|
|
|
import Papa from 'papaparse';
|
2021-05-02 13:45:13 +00:00
|
|
|
import { Pagination } from './Pagination';
|
2021-05-09 07:26:54 +00:00
|
|
|
import { CustomSelect } from './CustomSelect';
|
2021-05-09 10:51:26 +00:00
|
|
|
import { Tags } from './Tags';
|
2021-08-02 04:46:59 +00:00
|
|
|
import { Radio } from './Radio';
|
2021-09-21 13:48:28 +00:00
|
|
|
import { Toggle } from './Toggle';
|
2021-08-25 16:37:29 +00:00
|
|
|
import { Datepicker } from './Datepicker';
|
2021-09-27 03:38:34 +00:00
|
|
|
import { GlobalFilter } from './GlobalFilter';
|
2021-04-24 18:57:18 +00:00
|
|
|
var _ = require('lodash');
|
2021-04-30 06:31:32 +00:00
|
|
|
export function Table({
|
|
|
|
|
id,
|
|
|
|
|
width,
|
|
|
|
|
height,
|
|
|
|
|
component,
|
|
|
|
|
onComponentClick,
|
|
|
|
|
currentState = { components: {} },
|
|
|
|
|
onEvent,
|
|
|
|
|
paramUpdated,
|
|
|
|
|
changeCanDrag,
|
|
|
|
|
onComponentOptionChanged,
|
2021-07-03 14:17:47 +00:00
|
|
|
onComponentOptionsChanged,
|
2021-09-21 13:48:28 +00:00
|
|
|
darkMode,
|
2021-11-08 07:28:05 +00:00
|
|
|
fireEvent,
|
2022-01-24 10:07:12 +00:00
|
|
|
setExposedVariable,
|
2022-01-20 13:35:34 +00:00
|
|
|
registerAction,
|
2021-04-30 06:31:32 +00:00
|
|
|
}) {
|
2022-02-07 03:43:12 +00:00
|
|
|
const color =
|
|
|
|
|
component.definition.styles.textColor.value !== '#000'
|
|
|
|
|
? component.definition.styles.textColor.value
|
|
|
|
|
: darkMode && '#fff';
|
|
|
|
|
|
2021-04-30 06:31:32 +00:00
|
|
|
const actions = component.definition.properties.actions || { value: [] };
|
2021-05-02 13:45:13 +00:00
|
|
|
const serverSidePaginationProperty = component.definition.properties.serverSidePagination;
|
2022-02-01 14:16:21 +00:00
|
|
|
const serverSidePagination = serverSidePaginationProperty
|
|
|
|
|
? resolveWidgetFieldValue(serverSidePaginationProperty.value, currentState)
|
|
|
|
|
: false;
|
2021-04-30 06:31:32 +00:00
|
|
|
|
2021-06-01 11:24:37 +00:00
|
|
|
const serverSideSearchProperty = component.definition.properties.serverSideSearch;
|
2022-02-01 14:16:21 +00:00
|
|
|
const serverSideSearch = serverSideSearchProperty
|
|
|
|
|
? resolveWidgetFieldValue(serverSideSearchProperty.value, currentState)
|
|
|
|
|
: false;
|
2021-06-01 11:24:37 +00:00
|
|
|
|
2021-08-23 11:30:33 +00:00
|
|
|
const displaySearchBoxProperty = component.definition.properties.displaySearchBox;
|
2022-02-01 14:16:21 +00:00
|
|
|
const displaySearchBox = displaySearchBoxProperty
|
|
|
|
|
? resolveWidgetFieldValue(displaySearchBoxProperty.value, currentState)
|
|
|
|
|
: true;
|
2021-08-23 11:30:33 +00:00
|
|
|
|
2021-09-07 05:31:10 +00:00
|
|
|
const showDownloadButtonProperty = component.definition.properties.showDownloadButton?.value;
|
|
|
|
|
const showDownloadButton = resolveWidgetFieldValue(showDownloadButtonProperty, currentState) ?? true; // default is true for backward compatibility
|
|
|
|
|
|
|
|
|
|
const showFilterButtonProperty = component.definition.properties.showFilterButton?.value;
|
|
|
|
|
const showFilterButton = resolveWidgetFieldValue(showFilterButtonProperty, currentState) ?? true; // default is true for backward compatibility
|
|
|
|
|
|
2021-09-13 10:07:07 +00:00
|
|
|
const showBulkUpdateActionsProperty = component.definition.properties.showBulkUpdateActions?.value;
|
|
|
|
|
const showBulkUpdateActions = resolveWidgetFieldValue(showBulkUpdateActionsProperty, currentState) ?? true; // default is true for backward compatibility
|
|
|
|
|
|
2021-10-05 07:07:17 +00:00
|
|
|
const showBulkSelectorProperty = component.definition.properties.showBulkSelector?.value;
|
|
|
|
|
const showBulkSelector = resolveWidgetFieldValue(showBulkSelectorProperty, currentState) ?? false; // default is false for backward compatibility
|
|
|
|
|
|
2021-10-05 14:06:30 +00:00
|
|
|
const highlightSelectedRowProperty = component.definition.properties.highlightSelectedRow?.value;
|
|
|
|
|
const highlightSelectedRow = resolveWidgetFieldValue(highlightSelectedRowProperty, currentState) ?? false; // default is false for backward compatibility
|
|
|
|
|
|
2021-09-07 05:31:10 +00:00
|
|
|
const clientSidePaginationProperty = component.definition.properties.clientSidePagination?.value;
|
2021-09-21 13:48:28 +00:00
|
|
|
const clientSidePagination =
|
|
|
|
|
resolveWidgetFieldValue(clientSidePaginationProperty, currentState) ?? !serverSidePagination; // default is true for backward compatibility
|
2021-09-07 05:31:10 +00:00
|
|
|
|
2021-08-28 16:10:45 +00:00
|
|
|
const tableTypeProperty = component.definition.styles.tableType;
|
|
|
|
|
let tableType = tableTypeProperty ? tableTypeProperty.value : 'table-bordered';
|
|
|
|
|
tableType = tableType === '' ? 'table-bordered' : tableType;
|
|
|
|
|
|
2021-10-22 02:19:51 +00:00
|
|
|
const cellSizeType = component.definition.styles.cellSize?.value;
|
2022-02-01 11:36:41 +00:00
|
|
|
const borderRadius = component.definition.styles.borderRadius?.value;
|
2021-10-22 02:19:51 +00:00
|
|
|
|
2021-09-02 14:11:59 +00:00
|
|
|
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
|
|
|
|
|
const disabledState = component.definition.styles?.disabledState?.value ?? false;
|
|
|
|
|
|
2021-09-21 13:48:28 +00:00
|
|
|
const parsedDisabledState =
|
|
|
|
|
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
2021-08-30 11:43:05 +00:00
|
|
|
let parsedWidgetVisibility = widgetVisibility;
|
2021-09-21 13:48:28 +00:00
|
|
|
|
2021-08-30 11:43:05 +00:00
|
|
|
try {
|
|
|
|
|
parsedWidgetVisibility = resolveReferences(parsedWidgetVisibility, currentState, []);
|
2021-09-21 13:48:28 +00:00
|
|
|
} catch (err) {
|
|
|
|
|
console.log(err);
|
|
|
|
|
}
|
2021-08-30 11:43:05 +00:00
|
|
|
|
2021-04-30 06:31:32 +00:00
|
|
|
const [loadingState, setLoadingState] = useState(false);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const loadingStateProperty = component.definition.properties.loadingState;
|
|
|
|
|
if (loadingStateProperty && currentState) {
|
2021-04-30 08:10:57 +00:00
|
|
|
const newState = resolveReferences(loadingStateProperty.value, currentState, false);
|
2021-04-30 06:31:32 +00:00
|
|
|
setLoadingState(newState);
|
2021-04-08 07:49:38 +00:00
|
|
|
}
|
2021-04-30 06:31:32 +00:00
|
|
|
}, [currentState]);
|
|
|
|
|
|
|
|
|
|
const [componentState, setcomponentState] = useState(currentState.components[component.component] || {});
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
setcomponentState(currentState.components[component.name] || {});
|
|
|
|
|
}, [currentState.components[component.name]]);
|
|
|
|
|
|
|
|
|
|
const [isFiltersVisible, setFiltersVisibility] = useState(false);
|
|
|
|
|
const [filters, setFilters] = useState([]);
|
|
|
|
|
|
|
|
|
|
function showFilters() {
|
|
|
|
|
setFiltersVisibility(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function hideFilters() {
|
|
|
|
|
setFiltersVisibility(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function filterColumnChanged(index, value) {
|
|
|
|
|
const newFilters = filters;
|
2021-04-30 08:10:57 +00:00
|
|
|
newFilters[index].id = value;
|
2021-04-30 06:31:32 +00:00
|
|
|
setFilters(newFilters);
|
2021-04-30 08:10:57 +00:00
|
|
|
setAllFilters(newFilters.filter((filter) => filter.id !== ''));
|
2021-04-30 06:31:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function filterOperationChanged(index, value) {
|
|
|
|
|
const newFilters = filters;
|
2021-04-30 08:10:57 +00:00
|
|
|
newFilters[index].value = {
|
|
|
|
|
...newFilters[index].value,
|
2021-09-21 13:48:28 +00:00
|
|
|
operation: value,
|
2021-04-30 06:31:32 +00:00
|
|
|
};
|
|
|
|
|
setFilters(newFilters);
|
2021-04-30 08:10:57 +00:00
|
|
|
setAllFilters(newFilters.filter((filter) => filter.id !== ''));
|
2021-04-30 06:31:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function filterValueChanged(index, value) {
|
|
|
|
|
const newFilters = filters;
|
2021-04-30 08:10:57 +00:00
|
|
|
newFilters[index].value = {
|
|
|
|
|
...newFilters[index].value,
|
2021-09-21 13:48:28 +00:00
|
|
|
value: value,
|
2021-04-30 06:31:32 +00:00
|
|
|
};
|
|
|
|
|
setFilters(newFilters);
|
2021-04-30 08:10:57 +00:00
|
|
|
setAllFilters(newFilters.filter((filter) => filter.id !== ''));
|
2021-04-30 06:31:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function addFilter() {
|
|
|
|
|
setFilters([...filters, { id: '', value: { operation: 'contains', value: '' } }]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function removeFilter(index) {
|
|
|
|
|
let newFilters = filters;
|
|
|
|
|
newFilters.splice(index, 1);
|
|
|
|
|
setFilters(newFilters);
|
2021-10-24 14:24:00 +00:00
|
|
|
setAllFilters(newFilters.filter((filter) => filter.id !== ''));
|
2021-04-30 06:31:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function clearFilters() {
|
|
|
|
|
setFilters([]);
|
|
|
|
|
setAllFilters([]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const defaultColumn = React.useMemo(
|
|
|
|
|
() => ({
|
|
|
|
|
minWidth: 60,
|
2021-09-21 13:48:28 +00:00
|
|
|
width: 268,
|
2021-04-30 06:31:32 +00:00
|
|
|
}),
|
|
|
|
|
[]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const columnSizes = component.definition.properties.columnSizes || {};
|
|
|
|
|
|
|
|
|
|
function handleCellValueChange(index, key, value, rowData) {
|
|
|
|
|
const changeSet = componentState.changeSet;
|
|
|
|
|
const dataUpdates = componentState.dataUpdates || [];
|
|
|
|
|
|
|
|
|
|
let obj = changeSet ? changeSet[index] || {} : {};
|
|
|
|
|
obj = _.set(obj, key, value);
|
|
|
|
|
|
|
|
|
|
let newChangeset = {
|
|
|
|
|
...changeSet,
|
|
|
|
|
[index]: {
|
2021-09-21 13:48:28 +00:00
|
|
|
...obj,
|
|
|
|
|
},
|
2021-04-30 06:31:32 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
obj = _.set(rowData, key, value);
|
|
|
|
|
|
2021-08-02 14:35:21 +00:00
|
|
|
let newDataUpdates = {
|
2021-09-21 13:48:28 +00:00
|
|
|
...dataUpdates,
|
|
|
|
|
[index]: { ...obj },
|
2021-08-02 14:35:21 +00:00
|
|
|
};
|
2021-04-30 06:31:32 +00:00
|
|
|
|
2021-11-08 07:28:05 +00:00
|
|
|
return onComponentOptionsChanged(component, [
|
2021-04-30 06:31:32 +00:00
|
|
|
['dataUpdates', newDataUpdates],
|
2021-09-21 13:48:28 +00:00
|
|
|
['changeSet', newChangeset],
|
2021-04-30 06:31:32 +00:00
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-21 13:48:28 +00:00
|
|
|
function getExportFileBlob({ columns, data }) {
|
2021-04-30 06:31:32 +00:00
|
|
|
const headerNames = columns.map((col) => col.exportValue);
|
|
|
|
|
const csvString = Papa.unparse({ fields: headerNames, data });
|
|
|
|
|
return new Blob([csvString], { type: 'text/csv' });
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-02 13:45:13 +00:00
|
|
|
function onPageIndexChanged(page) {
|
|
|
|
|
onComponentOptionChanged(component, 'pageIndex', page).then(() => {
|
2021-09-21 13:48:28 +00:00
|
|
|
onEvent('onPageChanged', { component, data: {} });
|
2021-05-02 13:45:13 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-30 06:31:32 +00:00
|
|
|
function handleChangesSaved() {
|
2021-04-30 08:10:57 +00:00
|
|
|
Object.keys(changeSet).forEach((key) => {
|
2021-04-30 06:31:32 +00:00
|
|
|
tableData[key] = {
|
2021-09-21 13:48:28 +00:00
|
|
|
..._.merge(tableData[key], changeSet[key]),
|
2021-04-30 06:31:32 +00:00
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
onComponentOptionChanged(component, 'changeSet', {});
|
|
|
|
|
onComponentOptionChanged(component, 'dataUpdates', []);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleChangesDiscarded() {
|
|
|
|
|
onComponentOptionChanged(component, 'changeSet', {});
|
|
|
|
|
onComponentOptionChanged(component, 'dataUpdates', []);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function customFilter(rows, columnIds, filterValue) {
|
|
|
|
|
try {
|
|
|
|
|
if (filterValue.operation === 'equals') {
|
|
|
|
|
return rows.filter((row) => row.values[columnIds[0]] === filterValue.value);
|
|
|
|
|
}
|
2022-02-19 18:01:12 +00:00
|
|
|
|
|
|
|
|
if (filterValue.operation === 'ne') {
|
|
|
|
|
return rows.filter((row) => row.values[columnIds[0]] !== filterValue.value);
|
|
|
|
|
}
|
2021-04-30 06:31:32 +00:00
|
|
|
|
2021-05-09 12:03:03 +00:00
|
|
|
if (filterValue.operation === 'matches') {
|
2021-09-21 13:48:28 +00:00
|
|
|
return rows.filter((row) =>
|
|
|
|
|
row.values[columnIds[0]].toString().toLowerCase().includes(filterValue.value.toLowerCase())
|
|
|
|
|
);
|
2021-05-09 12:03:03 +00:00
|
|
|
}
|
2022-02-19 18:01:12 +00:00
|
|
|
|
|
|
|
|
if (filterValue.operation === 'nl') {
|
|
|
|
|
return rows.filter((row) =>
|
|
|
|
|
!row.values[columnIds[0]].toString().toLowerCase().includes(filterValue.value.toLowerCase())
|
|
|
|
|
);
|
|
|
|
|
}
|
2021-05-09 12:03:03 +00:00
|
|
|
|
2021-04-30 06:31:32 +00:00
|
|
|
if (filterValue.operation === 'gt') {
|
|
|
|
|
return rows.filter((row) => row.values[columnIds[0]] > filterValue.value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (filterValue.operation === 'lt') {
|
|
|
|
|
return rows.filter((row) => row.values[columnIds[0]] < filterValue.value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (filterValue.operation === 'gte') {
|
|
|
|
|
return rows.filter((row) => row.values[columnIds[0]] >= filterValue.value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (filterValue.operation === 'lte') {
|
|
|
|
|
return rows.filter((row) => row.values[columnIds[0]] <= filterValue.value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let value = filterValue.value;
|
|
|
|
|
if (typeof value === 'string') {
|
|
|
|
|
value = value.toLowerCase();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rows.filter((row) => {
|
|
|
|
|
let rowValue = row.values[columnIds[0]];
|
|
|
|
|
if (typeof rowValue === 'string') {
|
|
|
|
|
rowValue = rowValue.toLowerCase();
|
|
|
|
|
}
|
|
|
|
|
return rowValue.includes(value);
|
|
|
|
|
});
|
|
|
|
|
} catch {
|
|
|
|
|
return rows;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-04 06:26:46 +00:00
|
|
|
|
2021-04-30 06:31:32 +00:00
|
|
|
const changeSet = componentState ? componentState.changeSet : {};
|
2021-04-09 04:56:17 +00:00
|
|
|
|
2021-09-27 03:42:22 +00:00
|
|
|
const computeFontColor = useCallback(() => {
|
|
|
|
|
if (color !== undefined) {
|
|
|
|
|
return color;
|
|
|
|
|
} else {
|
|
|
|
|
return darkMode ? '#ffffff' : '#000000';
|
|
|
|
|
}
|
|
|
|
|
}, [color, darkMode]);
|
|
|
|
|
|
2021-04-30 06:31:32 +00:00
|
|
|
const columnData = component.definition.properties.columns.value.map((column) => {
|
2021-08-20 17:07:21 +00:00
|
|
|
const columnSize = columnSizes[column.id] || columnSizes[column.name];
|
2021-04-30 06:31:32 +00:00
|
|
|
const columnType = column.columnType;
|
2021-04-09 04:56:17 +00:00
|
|
|
|
2021-04-30 06:31:32 +00:00
|
|
|
const columnOptions = {};
|
2021-09-21 13:48:28 +00:00
|
|
|
if (
|
|
|
|
|
columnType === 'dropdown' ||
|
|
|
|
|
columnType === 'multiselect' ||
|
|
|
|
|
columnType === 'badge' ||
|
|
|
|
|
columnType === 'badges' ||
|
|
|
|
|
columnType === 'radio'
|
|
|
|
|
) {
|
2021-04-30 08:10:57 +00:00
|
|
|
const values = resolveReferences(column.values, currentState) || [];
|
|
|
|
|
const labels = resolveReferences(column.labels, currentState, []) || [];
|
2021-04-30 06:31:32 +00:00
|
|
|
|
2021-09-01 07:36:14 +00:00
|
|
|
if (Array.isArray(labels)) {
|
2021-04-30 08:10:57 +00:00
|
|
|
columnOptions.selectOptions = labels.map((label, index) => {
|
2021-09-21 13:48:28 +00:00
|
|
|
return { name: label, value: values[index] };
|
2021-04-30 06:31:32 +00:00
|
|
|
});
|
|
|
|
|
}
|
2021-04-03 05:25:41 +00:00
|
|
|
}
|
2021-08-25 16:37:29 +00:00
|
|
|
if (columnType === 'datepicker') {
|
2021-09-21 13:48:28 +00:00
|
|
|
column.isTimeChecked = column.isTimeChecked ? column.isTimeChecked : false;
|
|
|
|
|
column.dateFormat = column.dateFormat ? column.dateFormat : 'DD/MM/YYYY';
|
2021-12-03 08:22:14 +00:00
|
|
|
column.parseDateFormat = column.parseDateFormat ?? column.dateFormat; //backwards compatibility
|
2021-08-25 16:37:29 +00:00
|
|
|
}
|
2021-04-03 05:25:41 +00:00
|
|
|
|
2021-05-10 10:52:35 +00:00
|
|
|
const width = columnSize || defaultColumn.width;
|
|
|
|
|
|
2021-04-30 06:31:32 +00:00
|
|
|
return {
|
2021-08-19 05:02:46 +00:00
|
|
|
id: column.id,
|
2021-04-30 06:31:32 +00:00
|
|
|
Header: column.name,
|
|
|
|
|
accessor: column.key || column.name,
|
|
|
|
|
filter: customFilter,
|
2021-05-10 10:52:35 +00:00
|
|
|
width: width,
|
2021-09-01 07:36:14 +00:00
|
|
|
columnOptions,
|
2021-09-27 12:57:06 +00:00
|
|
|
columnType,
|
|
|
|
|
isEditable: column.isEditable,
|
2021-04-30 06:31:32 +00:00
|
|
|
Cell: function (cell) {
|
|
|
|
|
const rowChangeSet = changeSet ? changeSet[cell.row.index] : null;
|
|
|
|
|
const cellValue = rowChangeSet ? rowChangeSet[column.name] || cell.value : cell.value;
|
|
|
|
|
|
2021-09-27 12:57:06 +00:00
|
|
|
switch (columnType) {
|
|
|
|
|
case 'string':
|
|
|
|
|
case undefined:
|
|
|
|
|
case 'default': {
|
2021-11-17 11:37:06 +00:00
|
|
|
const textColor = resolveReferences(column.textColor, currentState, '', { cellValue });
|
2021-09-27 12:57:06 +00:00
|
|
|
|
|
|
|
|
const cellStyles = {
|
2021-09-29 16:48:08 +00:00
|
|
|
color: textColor ?? '',
|
2021-09-27 12:57:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (column.isEditable) {
|
|
|
|
|
const validationData = validateWidget({
|
|
|
|
|
validationObject: {
|
|
|
|
|
regex: {
|
|
|
|
|
value: column.regex,
|
|
|
|
|
},
|
|
|
|
|
minLength: {
|
|
|
|
|
value: column.minLength,
|
|
|
|
|
},
|
|
|
|
|
maxLength: {
|
|
|
|
|
value: column.maxLength,
|
|
|
|
|
},
|
|
|
|
|
customRule: {
|
|
|
|
|
value: column.customRule,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
widgetValue: cellValue,
|
|
|
|
|
currentState,
|
|
|
|
|
customResolveObjects: { cellValue },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const { isValid, validationError } = validationData;
|
|
|
|
|
const cellStyles = {
|
|
|
|
|
color: textColor ?? '',
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
2021-12-03 09:44:04 +00:00
|
|
|
style={{ ...cellStyles, maxWidth: width, minWidth: width - 10 }}
|
2021-09-27 12:57:06 +00:00
|
|
|
onKeyDown={(e) => {
|
|
|
|
|
if (e.key === 'Enter') {
|
|
|
|
|
if (e.target.defaultValue !== e.target.value) {
|
|
|
|
|
handleCellValueChange(
|
|
|
|
|
cell.row.index,
|
|
|
|
|
column.key || column.name,
|
|
|
|
|
e.target.value,
|
|
|
|
|
cell.row.original
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
onBlur={(e) => {
|
|
|
|
|
if (e.target.defaultValue !== e.target.value) {
|
|
|
|
|
handleCellValueChange(
|
|
|
|
|
cell.row.index,
|
|
|
|
|
column.key || column.name,
|
|
|
|
|
e.target.value,
|
|
|
|
|
cell.row.original
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
className={`form-control-plaintext form-control-plaintext-sm ${!isValid ? 'is-invalid' : ''}`}
|
|
|
|
|
defaultValue={cellValue}
|
|
|
|
|
/>
|
|
|
|
|
<div className="invalid-feedback">{validationError}</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return <span style={cellStyles}>{cellValue}</span>;
|
|
|
|
|
}
|
|
|
|
|
case 'text': {
|
|
|
|
|
return (
|
|
|
|
|
<textarea
|
|
|
|
|
rows="1"
|
|
|
|
|
className="form-control-plaintext text-container text-muted"
|
|
|
|
|
readOnly={!column.isEditable}
|
|
|
|
|
style={{ maxWidth: width, minWidth: width - 10 }}
|
|
|
|
|
onBlur={(e) => {
|
2021-12-03 10:36:24 +00:00
|
|
|
if (column.isEditable) {
|
|
|
|
|
handleCellValueChange(cell.row.index, column.key || column.name, e.target.value, cell.row.original);
|
|
|
|
|
}
|
2021-09-27 12:57:06 +00:00
|
|
|
}}
|
2022-01-04 06:01:04 +00:00
|
|
|
onChange={(e) => {
|
|
|
|
|
if (column.isEditable) {
|
|
|
|
|
handleCellValueChange(cell.row.index, column.key || column.name, e.target.value, cell.row.original);
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
value={cellValue}
|
2021-09-27 12:57:06 +00:00
|
|
|
defaultValue={cellValue}
|
|
|
|
|
></textarea>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
case 'dropdown': {
|
2021-09-06 08:50:38 +00:00
|
|
|
const validationData = validateWidget({
|
|
|
|
|
validationObject: {
|
|
|
|
|
regex: {
|
2021-09-21 13:48:28 +00:00
|
|
|
value: column.regex,
|
2021-09-06 08:50:38 +00:00
|
|
|
},
|
|
|
|
|
minLength: {
|
2021-09-21 13:48:28 +00:00
|
|
|
value: column.minLength,
|
2021-09-06 08:50:38 +00:00
|
|
|
},
|
|
|
|
|
maxLength: {
|
2021-09-21 13:48:28 +00:00
|
|
|
value: column.maxLength,
|
2021-09-06 08:50:38 +00:00
|
|
|
},
|
|
|
|
|
customRule: {
|
2021-09-21 13:48:28 +00:00
|
|
|
value: column.customRule,
|
|
|
|
|
},
|
2021-09-06 08:50:38 +00:00
|
|
|
},
|
|
|
|
|
widgetValue: cellValue,
|
2021-09-06 14:32:09 +00:00
|
|
|
currentState,
|
2021-09-21 13:48:28 +00:00
|
|
|
customResolveObjects: { cellValue },
|
|
|
|
|
});
|
|
|
|
|
|
2021-09-06 08:50:38 +00:00
|
|
|
const { isValid, validationError } = validationData;
|
|
|
|
|
|
2021-04-30 06:31:32 +00:00
|
|
|
return (
|
2021-09-06 08:50:38 +00:00
|
|
|
<div>
|
2021-09-27 12:57:06 +00:00
|
|
|
<SelectSearch
|
|
|
|
|
options={columnOptions.selectOptions}
|
|
|
|
|
value={cellValue}
|
|
|
|
|
search={true}
|
|
|
|
|
onChange={(value) => {
|
|
|
|
|
handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original);
|
2021-09-06 08:50:38 +00:00
|
|
|
}}
|
2021-09-27 12:57:06 +00:00
|
|
|
filterOptions={fuzzySearch}
|
|
|
|
|
placeholder="Select.."
|
|
|
|
|
/>
|
|
|
|
|
<div className={`invalid-feedback ${isValid ? '' : 'd-flex'}`}>{validationError}</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
case 'multiselect': {
|
|
|
|
|
return (
|
|
|
|
|
<div>
|
|
|
|
|
<SelectSearch
|
|
|
|
|
printOptions="on-focus"
|
|
|
|
|
multiple
|
|
|
|
|
search={true}
|
|
|
|
|
placeholder="Select.."
|
|
|
|
|
options={columnOptions.selectOptions}
|
|
|
|
|
value={cellValue}
|
|
|
|
|
onChange={(value) => {
|
|
|
|
|
handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
case 'badge':
|
|
|
|
|
case 'badges': {
|
|
|
|
|
return (
|
|
|
|
|
<div>
|
|
|
|
|
<CustomSelect
|
|
|
|
|
options={columnOptions.selectOptions}
|
|
|
|
|
value={cellValue}
|
|
|
|
|
multiple={columnType === 'badges'}
|
|
|
|
|
onChange={(value) => {
|
|
|
|
|
handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
case 'tags': {
|
|
|
|
|
return (
|
|
|
|
|
<div>
|
|
|
|
|
<Tags
|
|
|
|
|
value={cellValue}
|
|
|
|
|
onChange={(value) => {
|
|
|
|
|
handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
case 'radio': {
|
|
|
|
|
return (
|
|
|
|
|
<div>
|
|
|
|
|
<Radio
|
|
|
|
|
options={columnOptions.selectOptions}
|
|
|
|
|
value={cellValue}
|
|
|
|
|
readOnly={!column.isEditable}
|
|
|
|
|
onChange={(value) => {
|
|
|
|
|
handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
case 'toggle': {
|
|
|
|
|
return (
|
|
|
|
|
<div>
|
|
|
|
|
<Toggle
|
|
|
|
|
value={cellValue}
|
|
|
|
|
readOnly={!column.isEditable}
|
|
|
|
|
activeColor={column.activeColor}
|
|
|
|
|
onChange={(value) => {
|
2021-11-08 07:28:05 +00:00
|
|
|
handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original).then(
|
|
|
|
|
() => {
|
|
|
|
|
fireEvent('OnTableToggleCellChanged', {
|
|
|
|
|
column: column,
|
|
|
|
|
rowId: cell.row.id,
|
|
|
|
|
row: cell.row.original,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
);
|
2021-09-27 12:57:06 +00:00
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
case 'datepicker': {
|
|
|
|
|
return (
|
|
|
|
|
<div>
|
|
|
|
|
<Datepicker
|
2021-12-03 08:22:14 +00:00
|
|
|
dateDisplayFormat={column.dateFormat}
|
2021-09-27 12:57:06 +00:00
|
|
|
isTimeChecked={column.isTimeChecked}
|
|
|
|
|
value={cellValue}
|
|
|
|
|
readOnly={column.isEditable}
|
2021-12-03 08:22:14 +00:00
|
|
|
parseDateFormat={column.parseDateFormat}
|
2021-09-27 12:57:06 +00:00
|
|
|
onChange={(value) => {
|
|
|
|
|
handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original);
|
2021-09-06 08:50:38 +00:00
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2021-04-30 06:31:32 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-30 08:10:57 +00:00
|
|
|
return cellValue || '';
|
2021-09-21 13:48:28 +00:00
|
|
|
},
|
2021-04-30 06:31:32 +00:00
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let tableData = [];
|
|
|
|
|
if (currentState) {
|
2021-04-30 08:10:57 +00:00
|
|
|
tableData = resolveReferences(component.definition.properties.data.value, currentState, []);
|
2021-04-30 06:31:32 +00:00
|
|
|
if (!Array.isArray(tableData)) tableData = [];
|
|
|
|
|
console.log('resolved param', tableData);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-30 08:10:57 +00:00
|
|
|
tableData = tableData || [];
|
|
|
|
|
|
2021-09-21 13:48:28 +00:00
|
|
|
const leftActions = () => actions.value.filter((action) => action.position === 'left');
|
|
|
|
|
const rightActions = () => actions.value.filter((action) => [undefined, 'right'].includes(action.position));
|
|
|
|
|
|
|
|
|
|
const leftActionsCellData =
|
|
|
|
|
leftActions().length > 0
|
|
|
|
|
? [
|
2021-12-03 09:44:04 +00:00
|
|
|
{
|
|
|
|
|
id: 'leftActions',
|
|
|
|
|
Header: 'Actions',
|
|
|
|
|
accessor: 'edit',
|
|
|
|
|
width: columnSizes.leftActions || defaultColumn.width,
|
|
|
|
|
Cell: (cell) => {
|
|
|
|
|
return leftActions().map((action) => (
|
|
|
|
|
<button
|
|
|
|
|
key={action.name}
|
|
|
|
|
className="btn btn-sm m-1 btn-light"
|
2022-01-12 11:21:14 +00:00
|
|
|
style={{
|
|
|
|
|
background: action.backgroundColor,
|
|
|
|
|
color: action.textColor,
|
|
|
|
|
borderRadius: component.definition.styles.actionButtonRadius?.value
|
|
|
|
|
? parseFloat(component.definition.styles.actionButtonRadius?.value)
|
|
|
|
|
: 0,
|
|
|
|
|
}}
|
2021-12-03 09:44:04 +00:00
|
|
|
onClick={(e) => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
onEvent('onTableActionButtonClicked', {
|
|
|
|
|
component,
|
|
|
|
|
data: cell.row.original,
|
|
|
|
|
rowId: cell.row.id,
|
|
|
|
|
action,
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{action.buttonText}
|
|
|
|
|
</button>
|
|
|
|
|
));
|
|
|
|
|
},
|
2021-09-21 13:48:28 +00:00
|
|
|
},
|
2021-12-03 09:44:04 +00:00
|
|
|
]
|
2021-09-21 13:48:28 +00:00
|
|
|
: [];
|
|
|
|
|
|
|
|
|
|
const rightActionsCellData =
|
|
|
|
|
rightActions().length > 0
|
|
|
|
|
? [
|
2021-12-03 09:44:04 +00:00
|
|
|
{
|
|
|
|
|
id: 'rightActions',
|
|
|
|
|
Header: 'Actions',
|
|
|
|
|
accessor: 'edit',
|
|
|
|
|
width: columnSizes.rightActions || defaultColumn.width,
|
|
|
|
|
Cell: (cell) => {
|
|
|
|
|
return rightActions().map((action) => (
|
|
|
|
|
<button
|
|
|
|
|
key={action.name}
|
|
|
|
|
className="btn btn-sm m-1 btn-light"
|
2022-01-12 11:21:14 +00:00
|
|
|
style={{
|
|
|
|
|
background: action.backgroundColor,
|
|
|
|
|
color: action.textColor,
|
|
|
|
|
borderRadius: component.definition.styles.actionButtonRadius?.value
|
|
|
|
|
? parseFloat(component.definition.styles.actionButtonRadius?.value)
|
|
|
|
|
: 0,
|
|
|
|
|
}}
|
2021-12-03 09:44:04 +00:00
|
|
|
onClick={(e) => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
onEvent('onTableActionButtonClicked', {
|
|
|
|
|
component,
|
|
|
|
|
data: cell.row.original,
|
|
|
|
|
rowId: cell.row.id,
|
|
|
|
|
action,
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{action.buttonText}
|
|
|
|
|
</button>
|
|
|
|
|
));
|
|
|
|
|
},
|
2021-09-21 13:48:28 +00:00
|
|
|
},
|
2021-12-03 09:44:04 +00:00
|
|
|
]
|
2021-09-21 13:48:28 +00:00
|
|
|
: [];
|
2021-09-01 07:36:14 +00:00
|
|
|
|
2021-10-05 07:07:17 +00:00
|
|
|
const IndeterminateCheckbox = React.forwardRef(({ indeterminate, ...rest }, ref) => {
|
|
|
|
|
const defaultRef = React.useRef();
|
|
|
|
|
const resolvedRef = ref || defaultRef;
|
|
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
|
resolvedRef.current.indeterminate = indeterminate;
|
|
|
|
|
}, [resolvedRef, indeterminate]);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<input
|
|
|
|
|
type="checkbox"
|
|
|
|
|
ref={resolvedRef}
|
|
|
|
|
style={{
|
|
|
|
|
width: 15,
|
|
|
|
|
height: 15,
|
|
|
|
|
marginTop: 8,
|
|
|
|
|
marginLeft: 10,
|
|
|
|
|
}}
|
2021-10-07 08:35:36 +00:00
|
|
|
onClick={(event) => event.stopPropagation()}
|
2021-10-05 07:07:17 +00:00
|
|
|
{...rest}
|
|
|
|
|
/>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
2021-09-21 13:48:28 +00:00
|
|
|
const optionsData = columnData.map((column) => column.columnOptions?.selectOptions);
|
2021-09-01 07:36:14 +00:00
|
|
|
|
2021-04-30 06:31:32 +00:00
|
|
|
const columns = useMemo(
|
2021-08-31 10:37:38 +00:00
|
|
|
() => [...leftActionsCellData, ...columnData, ...rightActionsCellData],
|
2021-09-21 13:48:28 +00:00
|
|
|
[
|
|
|
|
|
JSON.stringify(columnData),
|
2021-08-31 11:33:45 +00:00
|
|
|
leftActionsCellData.length,
|
|
|
|
|
rightActionsCellData.length,
|
2021-08-29 06:15:57 +00:00
|
|
|
componentState.changeSet,
|
2021-09-01 07:36:14 +00:00
|
|
|
JSON.stringify(optionsData),
|
2021-09-21 13:48:28 +00:00
|
|
|
JSON.stringify(component.definition.properties.columns),
|
2021-10-05 07:07:17 +00:00
|
|
|
showBulkSelector,
|
2021-08-29 06:15:57 +00:00
|
|
|
] // Hack: need to fix
|
2021-04-30 06:31:32 +00:00
|
|
|
);
|
|
|
|
|
|
2021-12-03 08:22:50 +00:00
|
|
|
const data = useMemo(
|
|
|
|
|
() => tableData,
|
|
|
|
|
[tableData.length, componentState.changeSet, component.definition.properties.data.value]
|
|
|
|
|
);
|
2021-04-30 06:31:32 +00:00
|
|
|
|
|
|
|
|
const computedStyles = {
|
2021-11-16 11:44:09 +00:00
|
|
|
// width: `${width}px`,
|
2021-04-30 06:31:32 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const {
|
|
|
|
|
getTableProps,
|
|
|
|
|
getTableBodyProps,
|
|
|
|
|
headerGroups,
|
|
|
|
|
page,
|
|
|
|
|
canPreviousPage,
|
|
|
|
|
canNextPage,
|
|
|
|
|
pageOptions,
|
|
|
|
|
gotoPage,
|
|
|
|
|
pageCount,
|
|
|
|
|
nextPage,
|
|
|
|
|
previousPage,
|
|
|
|
|
setPageSize,
|
|
|
|
|
state,
|
2021-09-05 15:52:27 +00:00
|
|
|
rows,
|
2021-04-30 06:31:32 +00:00
|
|
|
prepareRow,
|
|
|
|
|
setAllFilters,
|
|
|
|
|
preGlobalFilteredRows,
|
|
|
|
|
setGlobalFilter,
|
|
|
|
|
state: { pageIndex, pageSize },
|
2021-09-21 13:48:28 +00:00
|
|
|
exportData,
|
2021-10-05 07:07:17 +00:00
|
|
|
selectedFlatRows,
|
2021-04-30 06:31:32 +00:00
|
|
|
} = useTable(
|
|
|
|
|
{
|
2021-09-21 11:22:39 +00:00
|
|
|
autoResetPage: false,
|
2021-04-30 06:31:32 +00:00
|
|
|
columns,
|
|
|
|
|
data,
|
|
|
|
|
defaultColumn,
|
2021-09-21 13:48:28 +00:00
|
|
|
initialState: { pageIndex: 0, pageSize: -1 },
|
|
|
|
|
pageCount: -1,
|
|
|
|
|
manualPagination: false,
|
|
|
|
|
getExportFileBlob,
|
2021-04-09 04:56:17 +00:00
|
|
|
},
|
2021-04-30 06:31:32 +00:00
|
|
|
useFilters,
|
|
|
|
|
useGlobalFilter,
|
|
|
|
|
useSortBy,
|
|
|
|
|
usePagination,
|
|
|
|
|
useBlockLayout,
|
|
|
|
|
useResizeColumns,
|
2021-10-05 07:07:17 +00:00
|
|
|
useExportData,
|
|
|
|
|
useRowSelect,
|
|
|
|
|
(hooks) => {
|
|
|
|
|
showBulkSelector &&
|
|
|
|
|
hooks.visibleColumns.push((columns) => [
|
|
|
|
|
{
|
|
|
|
|
id: 'selection',
|
|
|
|
|
Header: ({ getToggleAllPageRowsSelectedProps }) => (
|
|
|
|
|
<div className="d-flex flex-column align-items-center">
|
|
|
|
|
<IndeterminateCheckbox {...getToggleAllPageRowsSelectedProps()} />
|
|
|
|
|
</div>
|
|
|
|
|
),
|
|
|
|
|
Cell: ({ row }) => (
|
|
|
|
|
<div className="d-flex flex-column align-items-center">
|
|
|
|
|
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
|
|
|
|
|
</div>
|
|
|
|
|
),
|
|
|
|
|
width: 1,
|
|
|
|
|
columnType: 'selector',
|
|
|
|
|
},
|
|
|
|
|
...columns,
|
|
|
|
|
]);
|
|
|
|
|
}
|
2021-04-30 06:31:32 +00:00
|
|
|
);
|
|
|
|
|
|
2022-01-24 10:07:12 +00:00
|
|
|
const registerSetPageAction = () => {
|
2022-01-20 13:35:34 +00:00
|
|
|
registerAction('setPage', (targetPageIndex) => {
|
|
|
|
|
setPaginationInternalPageIndex(targetPageIndex);
|
2022-01-24 10:07:12 +00:00
|
|
|
setExposedVariable('pageIndex', targetPageIndex);
|
2022-01-20 13:35:34 +00:00
|
|
|
if (!serverSidePagination && clientSidePagination) gotoPage(targetPageIndex - 1);
|
|
|
|
|
});
|
2022-01-24 10:07:12 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
useEffect(registerSetPageAction, []);
|
|
|
|
|
useEffect(registerSetPageAction, [serverSidePagination, clientSidePagination]);
|
2022-01-20 13:35:34 +00:00
|
|
|
|
2021-10-05 07:07:17 +00:00
|
|
|
useEffect(() => {
|
|
|
|
|
const selectedRowsOriginalData = selectedFlatRows.map((row) => row.original);
|
|
|
|
|
onComponentOptionChanged(component, 'selectedRows', selectedRowsOriginalData);
|
|
|
|
|
}, [selectedFlatRows.length]);
|
|
|
|
|
|
2021-09-07 05:31:10 +00:00
|
|
|
React.useEffect(() => {
|
2021-09-21 13:48:28 +00:00
|
|
|
if (serverSidePagination || !clientSidePagination) {
|
2021-09-29 17:13:19 +00:00
|
|
|
setPageSize(rows?.length || 10);
|
2021-09-07 05:31:10 +00:00
|
|
|
}
|
2021-09-21 13:48:28 +00:00
|
|
|
if (!serverSidePagination && clientSidePagination) {
|
|
|
|
|
setPageSize(10);
|
|
|
|
|
}
|
2021-12-03 09:47:34 +00:00
|
|
|
}, [clientSidePagination, serverSidePagination, rows]);
|
2021-09-07 05:31:10 +00:00
|
|
|
|
2021-09-05 15:52:27 +00:00
|
|
|
useEffect(() => {
|
2021-09-21 13:48:28 +00:00
|
|
|
const pageData = page.map((row) => row.original);
|
|
|
|
|
const currentData = rows.map((row) => row.original);
|
2021-09-05 15:52:27 +00:00
|
|
|
onComponentOptionsChanged(component, [
|
|
|
|
|
['currentPageData', pageData],
|
2021-09-21 13:48:28 +00:00
|
|
|
['currentData', currentData],
|
2021-09-05 15:52:27 +00:00
|
|
|
]);
|
|
|
|
|
}, [tableData.length, componentState.changeSet]);
|
|
|
|
|
|
2021-04-30 06:31:32 +00:00
|
|
|
useEffect(() => {
|
|
|
|
|
if (!state.columnResizing.isResizingColumn) {
|
|
|
|
|
changeCanDrag(true);
|
2021-09-21 13:48:28 +00:00
|
|
|
paramUpdated(id, 'columnSizes', { ...columnSizes, ...state.columnResizing.columnWidths });
|
2021-04-30 06:31:32 +00:00
|
|
|
} else {
|
|
|
|
|
changeCanDrag(false);
|
|
|
|
|
}
|
2021-05-05 07:06:07 +00:00
|
|
|
}, [state.columnResizing.isResizingColumn]);
|
2021-04-30 06:31:32 +00:00
|
|
|
|
2022-01-20 13:35:34 +00:00
|
|
|
const [paginationInternalPageIndex, setPaginationInternalPageIndex] = useState(pageIndex ?? 1);
|
|
|
|
|
|
2021-10-25 08:30:52 +00:00
|
|
|
useEffect(() => {
|
|
|
|
|
if (pageCount <= pageIndex) gotoPage(pageCount - 1);
|
|
|
|
|
}, [pageCount]);
|
|
|
|
|
|
2021-04-30 06:31:32 +00:00
|
|
|
return (
|
|
|
|
|
<div
|
2021-09-02 14:11:59 +00:00
|
|
|
data-disabled={parsedDisabledState}
|
2021-07-03 14:17:47 +00:00
|
|
|
className="card jet-table"
|
2022-02-01 11:36:41 +00:00
|
|
|
style={{
|
|
|
|
|
width: `100%`,
|
|
|
|
|
height: `${height}px`,
|
|
|
|
|
display: parsedWidgetVisibility ? '' : 'none',
|
|
|
|
|
overflow: 'hidden',
|
|
|
|
|
borderRadius: Number.parseFloat(borderRadius),
|
|
|
|
|
}}
|
2021-09-21 13:48:28 +00:00
|
|
|
onClick={(event) => {
|
|
|
|
|
event.stopPropagation();
|
Feature: Collaboration ( realtime comments for canvas ) 🔥 (#810)
* feat: initial commit for collaboration feature
* add dnd to comments
* add positions endpoint
* feat: encapsulate all http common logic in http-client
* segregate sections and transfer responsibility of state
* feat: use-spring to add fade effect :zap:
* fix: open in right
* fix: left-right position css
* add footer for message
* integrate getcomment endpoint
* use fromnow for date ago
* add dnd
* - Add data trasfer object for comment
- Add class-validator package to check the response type from client
- Add comment repository class for persistance layer
- Add comment service with std. http methods
- Update controller with all http methods
- Update comment module
- Fix http-client bug when error is thrown
* fix http client bug when error is thrown
* feat: add entity thread
* feat: add migrations for thread and comment
* update entitites
* add tid to migration
* filter comments by tid(thread_id)
* fix: comment migration, add missing column comment
* feat: integrate in ui
* feat: split comments based on app_id
* fix: dnd to correct position
* package json engines
* engines update
* update npm
* npm 6 to 7
* fix: add user initials to thread
* fix: add firtname lastname to the comments
* - Return user object when save thread called
- Hide password field from user response
- Fix created_at date typo
- Instead of fetch all threads on new thread added, add the response to array of existing threads
* feat: update ui components
* change icon on comments view
* ui fixes
* fix: close icon close the popover
* temp: comment select: false
* use currentUser from localStorage
* fix: on click outside if comment is open, dont hit addThread
* fix: auth token issue in http-client
* on drag hide the comment if open
* add jwt auth
* spec: add test for comment & thread
* cleanup: remove console.log
* feat: add comment actions
* feat: add edit, delete, resolve options
* feat: add mentions component
* feat: add nestjs websockets
* temp
* websocket: establish client-server communication
* ws: add message listner to comments module in ui
* feat: add broadcast method to broadcast new events to all clients :bomb:
* ws: cleanup :call_me_hand:
* fix: remove max height from comment actions
* feat: add user mentions, emoji support
* fix: add static list of users - temp
* update and delete iterations
* - Rename comment, thread to comments, threads
- Add conditional actions
- Show edit, delete only if he is comment owner
- Show resolve only if he is thread owner
* reset engines
* move svgr webpack to deps
* fix: ui issues
* remove log stmt
* refactor: move resolved icon to comment-header
* feat: allow comments to be added on top of widgets
* feat: add keyboard shortcut
* scroll to bottom on comment add
* ui fixes
* feat: add react toast for notification display
* feat: add comment badge
* fix: ws connection
* fix: ws
* remove rvrse
* feat: add comment sidebar
* feat: add comment right sidebar
* fix: add missing foreign key elements
* - upgrade typeorm to 0.2.38
- comment sidebar ui
- added filter ui
* feat: on click of right sidebar notificaiton open the comment box
* reset engines
* fix: add organization id to the comment and thread module
* fix: add current version id
* add currentversion id
* disable comments if no id present
* temp:checking for heroku deploy
* fetch app on edit and deploy version
* rename current_version_id to app_versions_id
* ui fixes
* show mentioned user in blue color
* add ui changes
* add authorization for create thread
* change color to blue on click of comment, add auth for other endpoints of thread
* update threads, notifications using socket
* add auth for comments
* remove events spec file
* fix duplicate key error
* fix notificaitons updation on edit, delete, resolve buttons clicked
* update notifications for edit
* feature toggle changes for frontend
* add check for comments server
* add emoji mart package for emoji
* add reply count in comment sidebar
* subtract 1 from count in comment sidebar
* change empty text when no comments available
2021-11-01 07:28:03 +00:00
|
|
|
onComponentClick(id, component, event);
|
2021-09-21 13:48:28 +00:00
|
|
|
}}
|
2021-04-30 06:31:32 +00:00
|
|
|
>
|
2021-08-23 11:30:33 +00:00
|
|
|
{/* Show top bar unless search box is disabled and server pagination is enabled */}
|
2021-09-21 13:48:28 +00:00
|
|
|
{displaySearchBox && (
|
2021-08-23 11:30:33 +00:00
|
|
|
<div className="card-body border-bottom py-3 jet-data-table-header">
|
|
|
|
|
<div className="d-flex">
|
2021-09-27 03:38:34 +00:00
|
|
|
<div className="ms-auto text-muted">
|
|
|
|
|
<GlobalFilter
|
|
|
|
|
preGlobalFilteredRows={preGlobalFilteredRows}
|
|
|
|
|
globalFilter={state.globalFilter}
|
|
|
|
|
useAsyncDebounce={useAsyncDebounce}
|
|
|
|
|
setGlobalFilter={setGlobalFilter}
|
|
|
|
|
onComponentOptionChanged={onComponentOptionChanged}
|
|
|
|
|
component={component}
|
|
|
|
|
serverSideSearch={serverSideSearch}
|
|
|
|
|
onEvent={onEvent}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2021-04-30 06:31:32 +00:00
|
|
|
</div>
|
|
|
|
|
</div>
|
2021-09-21 13:48:28 +00:00
|
|
|
)}
|
2021-04-30 06:31:32 +00:00
|
|
|
<div className="table-responsive jet-data-table">
|
2021-08-28 16:10:45 +00:00
|
|
|
<table {...getTableProps()} className={`table table-vcenter table-nowrap ${tableType}`} style={computedStyles}>
|
2021-04-30 06:31:32 +00:00
|
|
|
<thead>
|
2021-09-21 13:48:28 +00:00
|
|
|
{headerGroups.map((headerGroup, index) => (
|
|
|
|
|
<tr key={index} {...headerGroup.getHeaderGroupProps()} tabIndex="0" className="tr">
|
|
|
|
|
{headerGroup.headers.map((column, index) => (
|
2021-04-30 06:31:32 +00:00
|
|
|
<th
|
2021-09-21 13:48:28 +00:00
|
|
|
key={index}
|
2021-04-30 06:31:32 +00:00
|
|
|
{...column.getHeaderProps(column.getSortByToggleProps())}
|
|
|
|
|
className={column.isSorted ? (column.isSortedDesc ? 'sort-desc th' : 'sort-asc th') : 'th'}
|
|
|
|
|
>
|
|
|
|
|
{column.render('Header')}
|
|
|
|
|
<div
|
|
|
|
|
draggable="true"
|
|
|
|
|
{...column.getResizerProps()}
|
|
|
|
|
className={`resizer ${column.isResizing ? 'isResizing' : ''}`}
|
|
|
|
|
/>
|
|
|
|
|
</th>
|
|
|
|
|
))}
|
|
|
|
|
</tr>
|
|
|
|
|
))}
|
|
|
|
|
</thead>
|
2021-05-26 12:31:33 +00:00
|
|
|
|
2021-09-21 13:48:28 +00:00
|
|
|
{!loadingState && page.length === 0 && (
|
|
|
|
|
<center className="w-100">
|
|
|
|
|
<div className="py-5"> no data </div>
|
|
|
|
|
</center>
|
|
|
|
|
)}
|
2021-05-26 12:31:33 +00:00
|
|
|
|
2021-04-30 06:31:32 +00:00
|
|
|
{!loadingState && (
|
2021-09-27 03:42:22 +00:00
|
|
|
<tbody {...getTableBodyProps()} style={{ color: computeFontColor() }}>
|
2021-04-30 06:31:32 +00:00
|
|
|
{console.log('page', page)}
|
2021-09-21 13:48:28 +00:00
|
|
|
{page.map((row, index) => {
|
2021-04-30 06:31:32 +00:00
|
|
|
prepareRow(row);
|
|
|
|
|
return (
|
|
|
|
|
<tr
|
2021-09-21 13:48:28 +00:00
|
|
|
key={index}
|
2021-12-03 09:44:04 +00:00
|
|
|
className={`table-row ${
|
|
|
|
|
highlightSelectedRow && row.id === componentState.selectedRowId ? 'selected' : ''
|
|
|
|
|
}`}
|
2021-04-30 06:31:32 +00:00
|
|
|
{...row.getRowProps()}
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.stopPropagation();
|
2021-10-05 14:06:30 +00:00
|
|
|
onEvent('onRowClicked', { component, data: row.original, rowId: row.id });
|
2021-04-30 06:31:32 +00:00
|
|
|
}}
|
|
|
|
|
>
|
2021-09-21 13:48:28 +00:00
|
|
|
{row.cells.map((cell, index) => {
|
2021-04-30 06:31:32 +00:00
|
|
|
let cellProps = cell.getCellProps();
|
|
|
|
|
if (componentState.changeSet) {
|
|
|
|
|
if (componentState.changeSet[cell.row.index]) {
|
2021-09-21 13:48:28 +00:00
|
|
|
const currentColumn = columnData.find((column) => column.id === cell.column.id);
|
2021-08-20 12:19:46 +00:00
|
|
|
|
2021-09-21 13:48:28 +00:00
|
|
|
if (
|
|
|
|
|
_.get(componentState.changeSet[cell.row.index], currentColumn?.accessor, undefined) !==
|
|
|
|
|
undefined
|
|
|
|
|
) {
|
2021-04-30 06:31:32 +00:00
|
|
|
console.log('componentState.changeSet', componentState.changeSet);
|
2021-10-21 17:31:21 +00:00
|
|
|
cellProps.style.backgroundColor = darkMode ? '#1c252f' : '#ffffde';
|
|
|
|
|
cellProps.style['--tblr-table-accent-bg'] = darkMode ? '#1c252f' : '#ffffde';
|
2021-04-30 06:31:32 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-09-27 12:57:06 +00:00
|
|
|
|
2021-09-21 13:48:28 +00:00
|
|
|
return (
|
2021-09-28 09:39:37 +00:00
|
|
|
// Does not require key as its already being passed by react-table via cellProps
|
|
|
|
|
// eslint-disable-next-line react/jsx-key
|
2021-09-27 12:57:06 +00:00
|
|
|
<td
|
|
|
|
|
className={cx({
|
|
|
|
|
'has-actions': cell.column.id === 'rightActions' || cell.column.id === 'leftActions',
|
|
|
|
|
'has-text': cell.column.columnType === 'text' || cell.column.isEditable,
|
|
|
|
|
'has-dropdown': cell.column.columnType === 'dropdown',
|
|
|
|
|
'has-multiselect': cell.column.columnType === 'multiselect',
|
|
|
|
|
'has-datepicker': cell.column.columnType === 'datepicker',
|
2021-10-05 07:07:17 +00:00
|
|
|
'align-items-center flex-column': cell.column.columnType === 'selector',
|
2021-10-22 02:19:51 +00:00
|
|
|
[cellSizeType]: true,
|
2021-09-27 12:57:06 +00:00
|
|
|
})}
|
|
|
|
|
{...cellProps}
|
|
|
|
|
>
|
2021-10-05 07:07:17 +00:00
|
|
|
<div className="td-container">{cell.render('Cell')}</div>
|
2021-09-21 13:48:28 +00:00
|
|
|
</td>
|
|
|
|
|
);
|
2021-04-30 06:31:32 +00:00
|
|
|
})}
|
|
|
|
|
</tr>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</tbody>
|
|
|
|
|
)}
|
|
|
|
|
</table>
|
|
|
|
|
{loadingState === true && (
|
|
|
|
|
<div style={{ width: '100%' }} className="p-2">
|
2021-07-03 18:07:51 +00:00
|
|
|
<center>
|
|
|
|
|
<div className="spinner-border mt-5" role="status"></div>
|
|
|
|
|
</center>
|
2021-04-30 06:31:32 +00:00
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2021-09-21 13:48:28 +00:00
|
|
|
{(clientSidePagination ||
|
|
|
|
|
serverSidePagination ||
|
|
|
|
|
Object.keys(componentState.changeSet || {}).length > 0 ||
|
|
|
|
|
showFilterButton ||
|
|
|
|
|
showDownloadButton) && (
|
2021-12-03 09:44:04 +00:00
|
|
|
<div className="card-footer d-flex align-items-center jet-table-footer">
|
|
|
|
|
<div className="table-footer row">
|
|
|
|
|
<div className="col">
|
|
|
|
|
{(clientSidePagination || serverSidePagination) && (
|
|
|
|
|
<Pagination
|
|
|
|
|
lastActivePageIndex={pageIndex}
|
|
|
|
|
serverSide={serverSidePagination}
|
|
|
|
|
autoGotoPage={gotoPage}
|
|
|
|
|
autoCanNextPage={canNextPage}
|
|
|
|
|
autoPageCount={pageCount}
|
|
|
|
|
autoPageOptions={pageOptions}
|
|
|
|
|
onPageIndexChanged={onPageIndexChanged}
|
2022-01-20 13:35:34 +00:00
|
|
|
pageIndex={paginationInternalPageIndex}
|
|
|
|
|
setPageIndex={setPaginationInternalPageIndex}
|
2021-12-03 09:44:04 +00:00
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{showBulkUpdateActions && Object.keys(componentState.changeSet || {}).length > 0 && (
|
2021-09-07 05:31:10 +00:00
|
|
|
<div className="col">
|
2021-12-03 09:44:04 +00:00
|
|
|
<button
|
|
|
|
|
className={`btn btn-primary btn-sm ${componentState.isSavingChanges ? 'btn-loading' : ''}`}
|
|
|
|
|
onClick={() =>
|
|
|
|
|
onEvent('onBulkUpdate', { component }).then(() => {
|
|
|
|
|
handleChangesSaved();
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
Save Changes
|
|
|
|
|
</button>
|
|
|
|
|
<button className="btn btn-light btn-sm mx-2" onClick={() => handleChangesDiscarded()}>
|
|
|
|
|
Discard changes
|
|
|
|
|
</button>
|
2021-09-07 05:31:10 +00:00
|
|
|
</div>
|
2021-12-03 09:44:04 +00:00
|
|
|
)}
|
2021-09-07 05:31:10 +00:00
|
|
|
|
2021-12-03 09:44:04 +00:00
|
|
|
<div className="col-auto">
|
|
|
|
|
{showFilterButton && (
|
|
|
|
|
<span data-tip="Filter data" className="btn btn-light btn-sm p-1 mx-2" onClick={() => showFilters()}>
|
|
|
|
|
<img src="/assets/images/icons/filter.svg" width="13" height="13" />
|
|
|
|
|
{filters.length > 0 && (
|
|
|
|
|
<a className="badge bg-azure" style={{ width: '4px', height: '4px', marginTop: '5px' }}></a>
|
|
|
|
|
)}
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
{showDownloadButton && (
|
|
|
|
|
<span
|
|
|
|
|
data-tip="Download as CSV"
|
|
|
|
|
className="btn btn-light btn-sm p-1"
|
|
|
|
|
onClick={() => exportData('csv', true)}
|
|
|
|
|
>
|
|
|
|
|
<img src="/assets/images/icons/download.svg" width="13" height="13" />
|
|
|
|
|
</span>
|
2021-09-21 13:48:28 +00:00
|
|
|
)}
|
2021-09-07 05:31:10 +00:00
|
|
|
</div>
|
2021-04-30 06:31:32 +00:00
|
|
|
</div>
|
2021-12-03 09:44:04 +00:00
|
|
|
</div>
|
|
|
|
|
)}
|
2021-04-30 06:31:32 +00:00
|
|
|
{isFiltersVisible && (
|
|
|
|
|
<div className="table-filters card">
|
2021-04-30 08:10:57 +00:00
|
|
|
<div className="card-header row">
|
2021-04-30 06:31:32 +00:00
|
|
|
<div className="col">
|
2021-10-26 12:25:53 +00:00
|
|
|
<h4 className="font-weight-normal">Filters</h4>
|
2021-04-30 06:31:32 +00:00
|
|
|
</div>
|
|
|
|
|
<div className="col-auto">
|
|
|
|
|
<button onClick={() => hideFilters()} className="btn btn-light btn-sm">
|
|
|
|
|
x
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="card-body">
|
|
|
|
|
{filters.map((filter, index) => (
|
|
|
|
|
<div className="row mb-2" key={index}>
|
|
|
|
|
<div className="col p-2" style={{ maxWidth: '70px' }}>
|
|
|
|
|
<small>{index > 0 ? 'and' : 'where'}</small>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="col">
|
|
|
|
|
<SelectSearch
|
|
|
|
|
options={columnData.map((column) => {
|
2021-08-23 13:24:28 +00:00
|
|
|
return { name: column.Header, value: column.id };
|
2021-04-30 06:31:32 +00:00
|
|
|
})}
|
|
|
|
|
value={filter.id}
|
|
|
|
|
search={true}
|
|
|
|
|
onChange={(value) => {
|
|
|
|
|
filterColumnChanged(index, value);
|
|
|
|
|
}}
|
|
|
|
|
filterOptions={fuzzySearch}
|
|
|
|
|
placeholder="Select.."
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="col" style={{ maxWidth: '180px' }}>
|
|
|
|
|
<SelectSearch
|
|
|
|
|
options={[
|
|
|
|
|
{ name: 'contains', value: 'contains' },
|
2021-05-09 12:03:03 +00:00
|
|
|
{ name: 'matches', value: 'matches' },
|
2022-02-19 18:01:12 +00:00
|
|
|
{ name: 'does not match', value: 'nl' },
|
2021-04-30 06:31:32 +00:00
|
|
|
{ name: 'equals', value: 'equals' },
|
2022-02-19 18:01:12 +00:00
|
|
|
{ name: 'does not equal', value: 'ne' },
|
2021-04-30 06:31:32 +00:00
|
|
|
{ name: 'greater than', value: 'gt' },
|
|
|
|
|
{ name: 'less than', value: 'lt' },
|
|
|
|
|
{ name: 'greater than or equals', value: 'gte' },
|
2021-09-21 13:48:28 +00:00
|
|
|
{ name: 'less than or equals', value: 'lte' },
|
2021-04-30 06:31:32 +00:00
|
|
|
]}
|
|
|
|
|
value={filter.value.operation}
|
|
|
|
|
search={true}
|
|
|
|
|
onChange={(value) => {
|
|
|
|
|
filterOperationChanged(index, value);
|
|
|
|
|
}}
|
|
|
|
|
filterOptions={fuzzySearch}
|
|
|
|
|
placeholder="Select.."
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="col">
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={filter.value.value}
|
|
|
|
|
placeholder="value"
|
|
|
|
|
className="form-control"
|
|
|
|
|
onChange={(e) => filterValueChanged(index, e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="col-auto">
|
2021-11-08 07:28:05 +00:00
|
|
|
<button
|
|
|
|
|
onClick={() => removeFilter(index)}
|
|
|
|
|
className={`btn ${darkMode ? 'btn-dark' : 'btn-light'} btn-sm p-2 text-danger font-weight-bold`}
|
|
|
|
|
>
|
2021-04-30 06:31:32 +00:00
|
|
|
x
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
2021-04-30 08:10:57 +00:00
|
|
|
{filters.length === 0 && (
|
2021-04-30 06:31:32 +00:00
|
|
|
<div>
|
|
|
|
|
<center>
|
2021-10-26 12:25:53 +00:00
|
|
|
<span>no filters yet.</span>
|
2021-04-30 06:31:32 +00:00
|
|
|
</center>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2021-04-30 08:10:57 +00:00
|
|
|
<div className="card-footer">
|
2021-10-26 12:25:53 +00:00
|
|
|
<button onClick={addFilter} className="btn btn-light btn-sm">
|
2021-04-30 06:31:32 +00:00
|
|
|
+ add filter
|
|
|
|
|
</button>
|
2021-10-26 12:25:53 +00:00
|
|
|
<button onClick={() => clearFilters()} className="btn btn-light btn-sm mx-2">
|
2021-04-30 06:31:32 +00:00
|
|
|
clear filters
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2021-04-12 13:35:48 +00:00
|
|
|
}
|