Skip to main content

LocalDataGrid

Reference

  • The LocalDataGrid component is display data grid built using the Handsontable library.
  • It supports advanced features like custom cell renderers, filtering, sorting, and dynamic data handling, making it suitable for displaying and managing tabular data.

Key Features

  • Customizable Columns: Dynamically configure columns with custom renderers and attributes.
  • Advanced Filtering: Built-in filtering with callbacks for filtered row counts.
  • File Handling: Supports file preview and download using FileDownloader.
  • Dynamic Layout: Adjusts column widths and layout based on props like shrinkToFit.
  • State Management: Handles grid state (e.g., loading, filtering) with a reducer.

Props

  1. fields

    • Type: Array
    • Description: Defines the columns of the grid, including attributes like name, type, isVisible, and custom renderers.
  2. items

    • Type: Array
    • Description: The data to display in the grid.
  3. onReload

    • Type: function
    • Description: Callback triggered when the grid is reloaded.
  4. leftHeader

    • Type: function
    • Description: Renders custom content for the left header section.
  5. columnFilterEnabled

    • Type: boolean
    • Default: true
    • Description: Enables or disables column filtering.
  6. columnSortingEnabled

    • Type: boolean
    • Default: true
    • Description: Enables or disables column sorting.
  7. shrinkToFit

    • Type: boolean
    • Default: true
    • Description: Adjusts column widths to fit the grid.
  8. filteredRecordsCallback

    • Type: function
    • Description: Callback triggered after filtering, providing the count of filtered rows.
    • If want to do some action when filter is applied use this function.

Sample format for fields

[
{
fieldType: "Attribute",
attributeType: "Text",
label: name, // Used to show haeder labels
isVisible: true,
width: 200,
// If shrinkToFit is false then width given to column will get applied
attributeName: name,
customRenderer: getCustomRenderer(name),
// Here pass custom renderer if null then
it will render deafult renderer based on attribute type
}
]

Sample format for items

Minimum required values are :
[
attributes : [
{
"name": "Name",
"type": "Text",
"canUpdate": true,
"textValue": "TestStud"
},
{
"name": "StudBooleanAttribute",
"type": "Boolean",
"canUpdate": true,
"booleanValue": false
}
]
]

Usage Example

function GatOnboarding(pageContext) {
let skip = 0;
let take = 10000;
const getGatsPromise = React.useCallback(() => {
return {
endpoint: 'instances',
config: {
method: 'GET',
params: {
instanceTypeName: 'Gat',
'attributes[0]': 'Name',
'attributes[1]': 'TrackingId',
'attributes[2]': 'GatToVillageRef',
'attributes[3]': 'GatAdmin',
'attributes[4]': 'GatFarmerCount',
'attributes[5]': 'Created',
'attributes[6]': 'Modified',
'attributes[7]': 'Modifier',
'attributes[8]': 'CropTypeOnGat',
getAllowedOperations: true,
getAllowedActions: true,
getOnlyActionableInstances: false,
skip: skip,
take: take,
sortBy: 'Created',
IsSortByDescending: true,
},
},
};
}, [skip, take]);
const getVillagesPromise = React.useCallback(() => {
return {
endpoint: 'instances',
config: {
method: 'GET',
params: {
instanceTypeName: 'Village',
'attributes[0]': 'VillageToTalukaRef',
'attributes[1]': 'VillageToDistrictRef',
'attributes[2]': 'Name',
skip: 0,
take: 9999,
},
},
};
}, []);
const { protrakUtils, protrakComponents } =
React.useContext(customWidgetContext);
const { useProtrakApi, usePaginatedGet, getDate } = protrakUtils;
const {
state: responseForGats,
nextPage,
paginationState,
hasMoreItems,
} = usePaginatedGet(
{
requestConfig: getGatsPromise,
skip,
take,
},
{},
take
);
React.useEffect(() => {
if (!responseForGats.isLoading && responseForGats.data && hasMoreItems()) {
nextPage();
}
}, [responseForGats, hasMoreItems, nextPage]);
const {
Box,
Button,
ButtonEnums,
ExportCSVButton,
ShowDrillDownData,
Container,
Spinner,
ErrorModal,
} = protrakComponents;

if (responseForGats.isLoading || hasMoreItems()) {
return (
<Container>
<Spinner />
</Container>
);
}
if (!responseForGats.isLoading && responseForGats.data && !hasMoreItems()) {
if (responseForGats.isError) {
return <ErrorModal state={responseForGats} />;
}

if (responseForGats.data.items && responseForGats.data.items.length > 0) {
return <TabularData gats={responseForGats.data.items} />;
}
}

function TabularData({ gats }) {
const { protrakUtils, protrakComponents } =
React.useContext(customWidgetContext);
const { useProtrakApi } = protrakUtils;

const { state: villages, run } = useProtrakApi({
requestConfig: getVillagesPromise,
});

const { Container, Spinner, ErrorModal } = protrakComponents;
if (villages.isLoading || !villages.data) {
return (
<Container>
<Spinner />
</Container>
);
}

if (!villages.isLoading) {
if (villages.isError) {
return <ErrorModal state={villages} />;
}
return <Data gats={gats} villages={villages} />;
}
}

function Data({ gats, villages }) {
const { protrakComponents, protrakUtils } =
React.useContext(customWidgetContext);
const {} = protrakUtils;
const { Box, LocalDataGrid, Label, ExportCSVButton } = protrakComponents;
const [isDataChanged, setIsDataChanged] = React.useState(false);
const columns = [
'Name',
'TrackingId',
'CropTypeOnGat',
'GatToVillageRef',
'VillageToTalukaRef',
'VillageToDistrictRef',
'AdminName',
'AdminMobile',
'GatFarmerCount',
'Created',
'Modified',
'Modifier',
];
function GetColumnValue(data, column, villages) {
const { protrakUtils } = React.useContext(customWidgetContext);
const { formatDateTime } = protrakUtils;

let villageData;
const villageAttr =
data &&
data.attributes.find((attribute) => {
return attribute.name === 'GatToVillageRef';
});
if (
villageAttr &&
villageAttr.referenceValues &&
villageAttr.referenceValues[0]
) {
const villageId = villageAttr.referenceValues[0].id;

villageData = villages.data.items.find((village) => {
return village.id && village.id == villageId;
});
}

let adminData;
const adminAttr =
data &&
data.attributes.find((attribute) => {
return attribute.name === 'GatAdmin';
});
if (adminAttr && adminAttr.userValues && adminAttr.userValues[0]) {
adminData = adminAttr.userValues[0];
}

if (column == 'Name') {
return {
name: 'Name',
type: 'Text',
canUpdate: true,
textValue: data.name ? data.name : '-',
};
}
if (column == 'TrackingId') {
const attribute =
data &&
data.attributes.find((attribute) => {
return attribute.name === 'TrackingId';
});
if (typeof attribute == 'undefined') {
return {
name: 'TrackingId',
type: 'Text',
canUpdate: true,
};
} else {
return attribute;
}
}

if (column === 'CropTypeOnGat') {
const attribute =
data &&
data.attributes.find((attribute) => {
return attribute.name === 'CropTypeOnGat';
});
if (typeof attribute == 'undefined') {
return {
name: 'CropTypeOnGat',
type: 'Reference',
canUpdate: true,
};
} else {
return attribute;
}
}

if (column === 'GatToVillageRef') {
return {
name: 'GatToVillageRef',
type: 'Text',
canUpdate: true,
textValue: villageData && villageData.name ? villageData.name : '-',
};
}
if (column === 'VillageToTalukaRef') {
const attribute =
villageData &&
villageData.attributes.find((attribute) => {
return attribute.name === 'VillageToTalukaRef';
});
if (typeof attribute == 'undefined') {
return {
name: 'VillageToTalukaRef',
type: 'Reference',
canUpdate: true,
};
} else {
return attribute;
}
}

if (column === 'VillageToDistrictRef') {
const attribute =
villageData &&
villageData.attributes.find((attribute) => {
return attribute.name === 'VillageToDistrictRef';
});
if (typeof attribute == 'undefined') {
return {
name: 'VillageToDistrictRef',
type: 'Reference',
canUpdate: true,
};
} else {
return attribute;
}
}
if (column === 'AdminName') {
return {
name: 'AdminName',
type: 'Text',
canUpdate: true,
textValue: adminData && adminData.userName ? adminData.userName : '-',
};
}
if (column === 'AdminMobile') {
return {
name: 'AdminMobile',
type: 'Text',
canUpdate: true,
textValue: adminData && adminData.phone ? adminData.phone : '-',
};
}
if (column === 'GatFarmerCount') {
const attribute =
data &&
data.attributes.find((attribute) => {
return attribute.name === 'GatFarmerCount';
});

if (typeof attribute == 'undefined') {
return {
name: 'GatFarmerCount',
type: 'Numeric',
canUpdate: true,
};
} else {
return attribute;
}
}
if (column === 'Created') {
const attribute =
data &&
data.attributes.find((attribute) => {
return attribute.name === 'Created';
});

if (typeof attribute == 'undefined') {
return {
name: 'Created',
type: 'DateTime',
canUpdate: true,
};
} else {
return attribute;
}
}
if (column === 'Modified') {
const attribute =
data &&
data.attributes.find((attribute) => {
return attribute.name === 'Modified';
});

if (typeof attribute == 'undefined') {
return {
name: 'Modified',
type: 'DateTime',
canUpdate: true,
};
} else {
return attribute;
}
}
if (column === 'Modifier') {
const attribute =
data &&
data.attributes.find((attribute) => {
return attribute.name === 'Modifier';
});

if (typeof attribute == 'undefined') {
return {
name: 'Modifier',
type: 'User',
canUpdate: true,
};
} else {
return attribute;
}
}
}

function GetRowValue(data, column, villages) {
const { protrakUtils } = React.useContext(customWidgetContext);
const { formatDateTime } = protrakUtils;

let villageData;
const villageAttr =
data &&
data.attributes.find((attribute) => {
return attribute.name === 'GatToVillageRef';
});
if (
villageAttr &&
villageAttr.referenceValues &&
villageAttr.referenceValues[0]
) {
const villageId = villageAttr.referenceValues[0].id;

villageData = villages.data.items.find((village) => {
return village.id && village.id == villageId;
});
}

let adminData;
const adminAttr =
data &&
data.attributes.find((attribute) => {
return attribute.name === 'GatAdmin';
});
if (adminAttr && adminAttr.userValues && adminAttr.userValues[0]) {
adminData = adminAttr.userValues[0];
}

if (column == 'Name') {
return data.name;
}
if (column == 'TrackingId') {
const attribute =
data &&
data.attributes.find((attribute) => {
return attribute.name === 'TrackingId';
});
return attribute && attribute.textValue ? attribute.textValue : '-';
}
if (column === 'CropTypeOnGat') {
const attribute =
data &&
data.attributes.find((attribute) => {
return attribute.name === 'CropTypeOnGat';
});
return attribute &&
attribute.referenceValues &&
attribute.referenceValues[0]
? attribute.referenceValues[0].name
: '-';
}

if (column === 'GatToVillageRef') {
return villageData && villageData.name ? villageData.name : '-';
}
if (column === 'VillageToTalukaRef') {
const attribute =
villageData &&
villageData.attributes.find((attribute) => {
return attribute.name === 'VillageToTalukaRef';
});
return attribute &&
attribute.referenceValues &&
attribute.referenceValues[0]
? attribute.referenceValues[0].name
: '-';
}
if (column === 'VillageToDistrictRef') {
const attribute =
villageData &&
villageData.attributes.find((attribute) => {
return attribute.name === 'VillageToDistrictRef';
});
return attribute &&
attribute.referenceValues &&
attribute.referenceValues[0]
? attribute.referenceValues[0].name
: '-';
}
if (column === 'AdminName') {
return adminData && adminData.userName ? adminData.userName : '-';
}
if (column === 'AdminMobile') {
return adminData && adminData.phone ? adminData.phone : '-';
}
if (column === 'GatFarmerCount') {
const attribute =
data &&
data.attributes.find((attribute) => {
return attribute.name === 'GatFarmerCount';
});
return attribute && attribute.numericValue
? attribute.numericValue
: '-';
}
if (column === 'Created') {
const attribute =
data &&
data.attributes.find((attribute) => {
return attribute.name === 'Created';
});
return attribute && attribute.dateValue ? attribute.dateValue : '-';
}
if (column === 'Modified') {
const attribute =
data &&
data.attributes.find((attribute) => {
return attribute.name === 'Modified';
});
return attribute && attribute.dateValue ? attribute.dateValue : '-';
}
if (column === 'Modifier') {
const attribute =
data &&
data.attributes.find((attribute) => {
return attribute.name === 'Modifier';
});
return attribute && attribute.userValues && attribute.userValues[0]
? attribute.userValues[0].userName
: '-';
}
}

const renderName = ({ value, attribute, column, rowIndex }) => {
const appUrl = document.location.origin.includes('localhost')
? '/Protrak'
: '';
return (
<a
href={`${appUrl}/#view_${exportData[rowIndex]['Id']}`}
target="_blank"
style={{ color: 'var(--secondary)' }}
>
{value}
</a>
);
};
const renderGatNo = ({ value, attribute, column, rowIndex }) => {
const appUrl = document.location.origin.includes('localhost')
? '/Protrak'
: '';
return (
<a
href={`${appUrl}/#view_${exportData[rowIndex]['Id']}`}
target="_blank"
style={{ color: 'var(--secondary)' }}
>
{value}
</a>
);
};
const getCustomRenderer = (columnName) => {
if (columnName === 'Name') {
return renderName;
} else if (columnName === 'TrackingId') {
return renderGatNo;
}
return null;
};

const columnHeaders = [
{
fieldType: 'Attribute',
attributeType: 'Text',
label: 'Name',
isEditable: false,
isVisible: true,
width: 200,
attributeName: 'Name',
readOnly: true,
customRenderer: getCustomRenderer('Name'),
},
{
fieldType: 'Attribute',
attributeType: 'Text',
label: 'Gat No',
isEditable: false,
isVisible: true,
width: 200,
attributeName: 'TrackingId',
readOnly: true,
customRenderer: getCustomRenderer('TrackingId'),
},
{
fieldType: 'Attribute',
attributeType: 'Reference',
label: 'Crop',
isEditable: false,
isVisible: true,
width: 200,
attributeName: 'CropTypeOnGat',
readOnly: true,
},
{
fieldType: 'Attribute',
attributeType: 'Text',
label: 'Village',
isEditable: false,
isVisible: true,
width: 200,
attributeName: 'GatToVillageRef',
readOnly: true,
},
{
fieldType: 'Attribute',
attributeType: 'Reference',
label: 'Taluka',
isEditable: false,
isVisible: true,
width: 200,
attributeName: 'VillageToTalukaRef',
readOnly: true,
},
{
fieldType: 'Attribute',
attributeType: 'Reference',
label: 'District',
isEditable: false,
isVisible: true,
width: 200,
attributeName: 'VillageToDistrictRef',
readOnly: true,
},
{
fieldType: 'Attribute',
attributeType: 'Text',
label: 'Admin Name',
isEditable: false,
isVisible: true,
width: 200,
attributeName: 'AdminName',
readOnly: true,
},
{
fieldType: 'Attribute',
attributeType: 'Text',
label: 'Admin Mobile',
isEditable: false,
isVisible: true,
width: 200,
attributeName: 'AdminMobile',
readOnly: true,
},
{
fieldType: 'Attribute',
attributeType: 'Numeric',
label: 'No. Of Farmers',
isEditable: false,
isVisible: true,
width: 200,
attributeName: 'GatFarmerCount',
readOnly: true,
},
{
fieldType: 'Attribute',
attributeType: 'DateTime',
label: 'Created Date',
isEditable: false,
isVisible: true,
width: 200,
attributeName: 'Created',
readOnly: true,
},
{
fieldType: 'Attribute',
attributeType: 'DateTime',
label: 'Modified Date',
isEditable: false,
isVisible: true,
width: 200,
attributeName: 'Modified',
readOnly: true,
},
{
fieldType: 'Attribute',
attributeType: 'User',
label: 'Modified By',
isEditable: false,
isVisible: true,
width: 200,
attributeName: 'Modifier',
readOnly: true,
},
];

const headers = [
{ key: 'Name', attributeType: 'Text' },
{ key: 'TrackingId', attributeType: 'Text' },
{ key: 'CropTypeOnGat', attributeType: 'Reference' },
{ key: 'GatToVillageRef', attributeType: 'Text' },
{ key: 'VillageToTalukaRef', attributeType: 'Reference' },
{ key: 'VillageToDistrictRef', attributeType: 'Reference' },
{ key: 'AdminName', attributeType: 'Text' },
{ key: 'AdminMobile', attributeType: 'Text' },
{ key: 'GatFarmerCount', attributeType: 'Numeric' },
{ key: 'Created', attributeType: 'DateTime' },
{ key: 'Modified', attributeType: 'DateTime' },
{ key: 'Modifier', attributeType: 'User' },
];

let rowsData = [];
gats.forEach((gat) => {
let attributes = [];
columns.forEach((column) => {
attributes.push(GetColumnValue(gat, column, villages));
});
rowsData.push({ attributes });
});

let exportData = [];
gats.forEach((gat) => {
var rowData = { Id: gat.id };
columns.forEach((column) => {
rowData[column] = GetRowValue(gat, column, villages);
});
exportData.push(rowData);
});

const tableLeftHeader = () => {
return (
<Box
alignSelf="right"
justifyContent="space-between"
alignItems="center"
>
<Box alignSelf="baseline">
<ExportCSVButton
data={exportData}
headers={headers}
filename="GatOnboarding"
/>
</Box>
</Box>
);
};

return (
<LocalDataGrid
leftHeader={tableLeftHeader}
fields={columnHeaders}
items={rowsData}
shrinkToFit={true}
columnSorting={true}
colWidths={false}
showPagination={false}
totalCount={rowsData.length}
isDataChanged={isDataChanged}
pageSize={rowsData.length}
onDataChange={() => {
setIsDataChanged(false);
}}
/>
);
}
return null;
}

UI

LocalDataGrid