import * as React from "react"; import IJitzSPContext from "../data/interfaces/IJitzSPContext"; import UtilityService from "../services/UtilityService"; import { buildColumns, CommandBar, DetailsList, IColumn, IconButton, Label, SelectionMode, TooltipHost, Selection, ActionButton, Spinner, } from "@fluentui/react"; import CommonRepository from "../data/context/CommonRepository"; import { IList } from "../data/interfaces/IList"; import { IModel } from "../common/IModels"; import IJitzContext from "../data/interfaces/IJitzContext"; export enum SortOrder { Asc = "asc", Desc = "desc", } export enum DisplayMode { Grid = "Grid", Tile = "Tile", } export interface IJitzGridState { list: IList; listForDownload: IList; items: any[]; showingItems: any[]; rawData: any[]; showingRawData: any[]; exportData: any[]; totalCount: number; columns?: IColumn[]; selectedItems?: {}; displayMode?: DisplayMode; filterQuery?: string; isItIndexedSearch?: boolean; isItIndexedDownload?: boolean; hasNextPage?: boolean; hasNextPageToDownload?: boolean; processing?: boolean; downloading?: boolean; } export interface IJitzGridProps { context: IJitzContext; list: IList; filterQuery?: string; orderBy?: string; displayFields?: string[]; exportFields?: string[]; hasExport?: boolean; pageSize?: number; renderCustomColumns?: ( content: any, fieldName: string, rowItem?: any ) => React.ReactElement<{}>; modifyColumns?: (columns: IColumn[]) => IColumn[]; onItemSelect?: (items: any) => void; onItemInvoked?: (item: any, index: number | undefined) => void; selectionMode?: SelectionMode; noRecordsMessage?: string; noRecordsMessageCssClass?: string; tileComponent?: (content: any) => React.ReactElement<{}>; refreshedOn?: Date; displayMode?: DisplayMode; downloadFileName?: string; outerGridClass?: string; fixedGridWidthCss?: string; } export class JitzGrid extends React.Component< IJitzGridProps, IJitzGridState > { private _selection: Selection; constructor(props: IJitzGridProps) { super(props); let item: any = {}; if (props.displayFields) { props.displayFields.map((field: any) => { item[field] = ""; }); } this.state = { list: this.props.list, listForDownload: this.props.list, items: [], showingItems: [], rawData: [], showingRawData: [], exportData: [], totalCount: 0, columns: [], displayMode: props.displayMode || DisplayMode.Tile, filterQuery: props.filterQuery, isItIndexedSearch: false, isItIndexedDownload: false, processing: false, downloading: false, hasNextPage: false, hasNextPageToDownload: false, }; this._selection = new Selection({ onSelectionChanged: () => this.setState({ selectedItems: this._getSelectedItems() }), }); } private _getSelectedItems = () => { if (this.props.onItemSelect != undefined) { this.props.onItemSelect(this._selection.getSelection()); } return {}; }; private getSortOrderString = ( sortOrder: SortOrder, isPreviousNavigation: boolean ): string => { if (sortOrder == SortOrder.Asc) { return isPreviousNavigation ? "desc" : "asc"; } else { return isPreviousNavigation ? "asc" : "desc"; } }; // private hasKey(obj: O, key: keyof any): key is keyof O { // return key in obj; // } private hasKey(obj: object, key: string): boolean { return key in obj; } private initalLoad = async () => { await this.setState({ items: [], showingItems: [], rawData: [], showingRawData: [], }); var list = this.props.list; var data: T[] = []; let items: any[] = []; await list .getItems( this.props.filterQuery, this.props.orderBy, this.props.pageSize || 100 ) .then((results: any) => { data = results; this.setState({ isItIndexedSearch: false }); }) .catch(async (err: any) => { data = await list.getItemsIndexed( this.props.filterQuery, this.props.orderBy, this.props.pageSize || 100 ); this.setState({ isItIndexedSearch: true }); }); if (this.props.displayFields != undefined) { let rawShowingData: any[] = []; data.map((record: any) => { let item: any = {}; if (this.props.displayFields != undefined) { this.props.displayFields.map((field) => { if (this.hasKey(record, field)) { item[field] = typeof record[field] == "object" ? JSON.stringify(record[field]) : record[field]; // works fine! } }); items.push(item); rawShowingData.push(record); } }); this.setState( { items: items, showingItems: items, rawData: items, showingRawData: rawShowingData, columns: this._buildColumns(items), list: list, hasNextPage: (list._nextPageLink != undefined && list._nextPageLink.length > 0) || list.lowerId > 0, }, () => {} ); } }; private loadMore = async () => { try { var list = this.state.list; var data: T[] = []; if (this.state.isItIndexedSearch) data = await list.loadMoreIndexed(); else { data = await list.loadMore(); } if (this.props.displayFields != undefined) { let items: any[] = []; let rawShowingData: any[] = []; data.map((record: any) => { let item: any = {}; if (this.props.displayFields != undefined) { this.props.displayFields.map((field) => { if (this.hasKey(record, field)) { item[field] = typeof record[field] == "object" ? JSON.stringify(record[field]) : record[field]; // works fine! } }); items.push(item); rawShowingData.push(record); } }); if (data.length == 0) { //TODO: No more records message } await this.setState( { items: [...this.state.items, ...items], showingItems: [...this.state.showingItems, ...items], rawData: items, showingRawData: [...this.state.showingRawData, ...rawShowingData], list: list, hasNextPage: (list._nextPageLink != undefined && list._nextPageLink.length > 0) || list.lowerId > 0, }, () => {} ); } } catch {} }; public componentDidMount(): void { this.initalLoad(); } public async componentWillReceiveProps(nextProps: IJitzGridProps) { if ( nextProps.filterQuery != this.state.filterQuery // || // nextProps.refreshedOn != this.props.refreshedOn ) { await this.setState({ filterQuery: nextProps.filterQuery }); this.initalLoad(); } } public downloadData = async () => { try { await this.setState({ downloading: true }); await this.initiateDownload(); let items: any[] = []; var data: any[] = this.state.exportData; data.map((record: any) => { let item: any = {}; if (this.props.exportFields != undefined) { this.props.exportFields.map((field: string) => { if (this.hasKey(record, field)) { if (typeof record[field] == "object") { if (record[field] == null || record[field] == undefined) { item[field] = ""; } else if (record[field].EMail != undefined) { if (record[field].Title != undefined) { item[field + "_Title"] = record[field].Title || ""; } item[field + "_Email"] = record[field].EMail || ""; } else if (record[field].Name != undefined) { item[field + "_Name"] = record[field].Name || ""; } } else { item[field] = record[field]; } // item[field] = // typeof record[field] == "object" // ? JSON.stringify(record[field]) // : record[field]; // works fine! } else if ((field as string).indexOf("/") > 0) { let lookUpField: string = (field as string).split("/")[0]; let lookUpValueColumn: string = (field as string).split("/")[1]; if (typeof record[lookUpField] == "object") { item[field] = record[lookUpField][lookUpValueColumn]; } } }); items.push(item); } }); UtilityService.exportJsonAsExcelSheet( items, this.props.downloadFileName || "export" ); } catch {} this.setState({ downloading: false }); }; initiateDownload = async () => { var list = this.props.list; var data: T[] = []; let items: any[] = []; await list .getItems(this.props.filterQuery, this.props.orderBy, 5000) .then((results: any) => { data = results; this.setState({ isItIndexedDownload: false }); }) .catch(async (err: any) => { data = await list.getItemsIndexed( this.props.filterQuery, this.props.orderBy, 5000 ); this.setState({ isItIndexedDownload: true }); }); await this.setState({ exportData: data, hasNextPageToDownload: (list._nextPageLink != undefined && list._nextPageLink.length > 0) || list.lowerId > 0, }); if (this.state.hasNextPageToDownload === true) { await this.continueDownload(list); } }; continueDownload = async (list: IList) => { try { var data: T[] = []; if (this.state.isItIndexedDownload) data = await list.loadMoreIndexed(); else { data = await list.loadMore(); } await this.setState({ exportData: [...this.state.exportData, ...data], hasNextPageToDownload: (list._nextPageLink != undefined && list._nextPageLink.length > 0) || list.lowerId > 0, }); if (this.state.hasNextPageToDownload === true) { await this.continueDownload(list); } } catch {} }; public render() { return (
{/*
{this.props.refreshedOn}
*/} {this.props.hasExport == true && this.state.showingItems != undefined && this.state.showingItems.length > 0 && (
{ this.setState({ displayMode: DisplayMode.Grid }); }, }, { key: "tile", text: "", title: "Tile View", disabled: this.state.displayMode === DisplayMode.Tile, iconProps: { iconName: "Tiles" }, onClick: () => { this.setState({ displayMode: DisplayMode.Tile }); }, }, { key: "export", text: "", title: "Download", iconProps: { iconName: "Download" }, onClick: () => { this.downloadData(); }, }, ]} items={[ { key: "total", text: `Showing ${this.state.items.length} records`, title: `Showing ${this.state.items.length} records`, onClick: () => {}, }, // { // key: "loadMore", // text: "Try to get more older records", // title: "Try to get more older records", // iconProps: { iconName: "DoubleChevronDown" }, // onClick: () => { // if (this.state.processing != true) // this.setState({ processing: true }, async () => { // await this.loadMore(); // this.setState({ processing: false }); // }); // }, // }, ]} /> {this.state.downloading && }
)} {(this.state.showingItems == undefined || this.state.showingItems.length > 0) && (
{this.state.displayMode === DisplayMode.Grid && (
false} onColumnHeaderContextMenu={ this._onColumnHeaderContextMenu } onActiveItemChanged={(item: any, index?: number) => { this.itemSelected(item, index); }} selectionMode={ this.props.selectionMode || SelectionMode.none } selection={this._selection} />
)} {this.state.displayMode === DisplayMode.Tile && this.state.showingItems != undefined && this.state.showingItems.length > 0 && this.state.showingItems.map((item: any) => { return this.props.tileComponent != undefined ? ( this.props.tileComponent(item) ) : ( <> ); })}
)} {(this.state.showingItems == undefined || this.state.showingItems.length == 0) && (
{this.props.noRecordsMessage || "No records found"}
)}
{this.state.processing !== true && this.state.hasNextPage && ( { this.setState({ processing: true }, async () => { await this.loadMore(); this.setState({ processing: false }); }); }} > Try to get more older records )} {this.state.processing && }
); } private itemSelected = (item: any, index?: number) => {}; private _buildColumns = (items: any[]) => { var columns: IColumn[] = []; if (items != null && items.length > 0) { columns = buildColumns(items); } else if ( this.state.items != null && this.state.items != undefined && this.state.items.length > 0 ) { columns = buildColumns(this.state.items); } var idIndex = 0; columns.map((raw, i) => { raw.name = raw.name.split("_").join(" "); raw.name = raw.name.replace(/\b\w/g, (l) => { return l.toUpperCase(); }); if (raw.name == "Id") { idIndex = i; } }); // columns = columns.filter(matchedColumns => matchedColumns.name != "Id" && matchedColumns.name !="ID"); if ( this.props.modifyColumns != undefined && this.props.modifyColumns != null ) { columns = this.props.modifyColumns(columns); } return columns; }; private _renderItemColumn = ( item?: any, index?: number, column?: IColumn ) => { if ( this.state.showingRawData != null && this.state.showingRawData != undefined && this.state.showingRawData.length > 0 ) { if (column != undefined && column.fieldName != undefined) { const fieldContent = item[column.fieldName]; if ( this.props.renderCustomColumns != undefined && this.props.renderCustomColumns != undefined ) { return this.props.renderCustomColumns( fieldContent, column.key, this.state.showingRawData[index || 0] ); } else { return {fieldContent}; } } } }; private _onColumnClick = ( event?: React.MouseEvent, column?: IColumn ): void => { const { columns } = this.state; let { items } = this.state; if (column != undefined && column.fieldName != undefined) { let isSortedDescending = column.isSortedDescending; // If we've sorted this column, flip it. if (column.isSorted) { isSortedDescending = !isSortedDescending; } // Sort the items. items = items!.concat([]).sort((a, b) => { const firstValue = column.fieldName != undefined ? a[column.fieldName] : undefined; const secondValue = column.fieldName != undefined ? b[column.fieldName] : undefined; if (isSortedDescending) { return firstValue > secondValue ? -1 : 1; } else { return firstValue > secondValue ? 1 : -1; } }); // Reset the items and columns to match the state. this.setState({ items: items, columns: columns!.map((col) => { col.isSorted = col.key === column.key; if (col.isSorted) { col.isSortedDescending = isSortedDescending; } return col; }), }); } }; private _onColumnHeaderContextMenu( column: IColumn | undefined, ev: React.MouseEvent | undefined ): void { // console.log(`column ${column!.key} contextmenu opened.`); } private _onItemInvoked = (item: any, index: number | undefined): void => { if ( this.props.onItemInvoked != undefined && this.props.onItemInvoked != null ) { this.props.onItemInvoked(item, index); } }; }