Skip to content

Commit

Permalink
feat(dashboard): add table support to dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
square-li committed Mar 15, 2023
1 parent 996858c commit 1d8d44e
Show file tree
Hide file tree
Showing 15 changed files with 222 additions and 266 deletions.
3 changes: 1 addition & 2 deletions packages/dashboard/src/components/sidePanel/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { FC } from 'react';
import React from 'react';
import { Box, Header, SpaceBetween } from '@cloudscape-design/components';
import { useSelector } from 'react-redux';
Expand All @@ -7,8 +8,6 @@ import { BaseSettings } from './sections/baseSettingSection';
import AxisSetting, { isAxisSettingsSupported } from './sections/axisSettingSection';
import ThresholdsSection, { isThresholdsSupported } from './sections/thresholdsSection/thresholdsSection';
import PropertiesAlarmsSection, { isPropertiesAndAlarmsSupported } from './sections/propertiesAlarmSection';

import type { FC } from 'react';
import type { DashboardState } from '~/store/state';
import type { DashboardMessages } from '~/messages';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,58 @@
import type { FC } from 'react';
import React from 'react';
import { ExpandableSection, SpaceBetween } from '@cloudscape-design/components';
import { useAssetDescriptionMapAsync } from '~/hooks/useAssetDescriptionMapAsync';
import ExpandableSectionHeader from '../../shared/expandableSectionHeader';
import { PropertyComponent } from './propertyComponent';
import { useWidgetLense } from '../../utils/useWidgetLense';
import { mapAssetDescriptionToAssetSummary } from '~/components/resourceExplorer/components/mapper';
import type { FC } from 'react';
import type { SiteWiseAssetQuery } from '@iot-app-kit/source-iotsitewise';
import type { DashboardMessages } from '~/messages';
import type { QueryWidget } from '~/customization/widgets/types';
import { toId } from '@iot-app-kit/source-iotsitewise';
import type { QueryWidget, TableProperties } from '~/customization/widgets/types';
import type { StyleSettingsMap } from '@iot-app-kit/core';
import type { Widget } from '~/types';
import type { TableItemRef } from '@iot-app-kit/react-components';
import { TableItem } from '@iot-app-kit/react-components';

function isTableItemRef(value: TableItem[string]): value is TableItemRef {
return typeof value === 'object' && value?.$cellRef !== undefined;
}

export type PropertiesAlarmsSectionProps = QueryWidget & {
onDeleteAssetQuery?: (params: {
assetId: string;
propertyId: string;
siteWiseAssetQuery: SiteWiseAssetQuery;
updateSiteWiseAssetQuery: (newQuery: SiteWiseAssetQuery) => void;
}) => () => void;
};

const defaultOnDeleteQuery: PropertiesAlarmsSectionProps['onDeleteAssetQuery'] =
({ assetId, propertyId, siteWiseAssetQuery, updateSiteWiseAssetQuery }) =>
() => {
const assets =
siteWiseAssetQuery?.assets
.map((asset) => {
if (assetId === asset.assetId) {
const { properties } = asset;
return {
assetId,
properties: properties.filter((p) => p.propertyId !== propertyId),
};
}
return asset;
})
.filter((asset) => asset.properties.length > 0) ?? [];

updateSiteWiseAssetQuery({ assets });
};
export const isPropertiesAndAlarmsSupported = (widget: Widget): widget is QueryWidget =>
['iot-line', 'iot-scatter', 'iot-bar', 'iot-table', 'iot-kpi', 'iot-status'].some((t) => t === widget.type);

export type PropertiesAlarmsSectionProps = {
messageOverrides: DashboardMessages;
};
const PropertiesAlarmsSection: FC<QueryWidget> = (widget) => {
const GeneralPropertiesAlarmsSection: FC<PropertiesAlarmsSectionProps> = ({
onDeleteAssetQuery = defaultOnDeleteQuery,
...widget
}) => {
const [siteWiseAssetQuery, updateSiteWiseAssetQuery] = useWidgetLense<QueryWidget, SiteWiseAssetQuery | undefined>(
widget,
(w) => w.properties.queryConfig.query,
Expand Down Expand Up @@ -48,24 +82,6 @@ const PropertiesAlarmsSection: FC<QueryWidget> = (widget) => {

const describedAssetsMap = useAssetDescriptionMapAsync(siteWiseAssetQuery);

const onDeleteAssetQuery = (assetId: string, propertyId: string) => () => {
const assets =
siteWiseAssetQuery?.assets
.map((asset) => {
if (assetId === asset.assetId) {
const { properties } = asset;
return {
assetId,
properties: properties.filter((p) => p.propertyId !== propertyId),
};
}
return asset;
})
.filter((asset) => asset.properties.length > 0) ?? [];

updateSiteWiseAssetQuery({ assets });
};

const onUpdatePropertyColor = (refId: string) => (color: string) => {
updateStyleSettings({
...styleSettings,
Expand All @@ -85,7 +101,7 @@ const PropertiesAlarmsSection: FC<QueryWidget> = (widget) => {
refId={refId}
assetSummary={mapAssetDescriptionToAssetSummary(describedAssetsMap[assetId])}
styleSettings={styleSettings}
onDeleteAssetQuery={onDeleteAssetQuery(assetId, propertyId)}
onDeleteAssetQuery={onDeleteAssetQuery({ assetId, propertyId, siteWiseAssetQuery, updateSiteWiseAssetQuery })}
onUpdatePropertyColor={onUpdatePropertyColor(refId)}
/>
) : null
Expand All @@ -103,5 +119,54 @@ const PropertiesAlarmsSection: FC<QueryWidget> = (widget) => {
</ExpandableSection>
);
};
export const TablePropertiesAlarmsSection: FC<QueryWidget> = (widget) => {
const [properties, updateTableProperties] = useWidgetLense<QueryWidget, TableProperties>(
widget,
(w) => w.properties,
(w, properties) => ({
...w,
properties,
})
);

const deleteQuery: PropertiesAlarmsSectionProps['onDeleteAssetQuery'] =
({ assetId, propertyId, siteWiseAssetQuery }) =>
() => {
const assets =
siteWiseAssetQuery?.assets
.map((asset) => {
if (assetId === asset.assetId) {
const { properties } = asset;
return {
assetId,
properties: properties.filter((p) => p.propertyId !== propertyId),
};
}
return asset;
})
.filter((asset) => asset.properties.length > 0) ?? [];

const newItems = properties.items?.filter((item) => {
const value = item.value;
return isTableItemRef(value) && value.$cellRef.id !== toId({ assetId, propertyId });
});
updateTableProperties({
...properties,
queryConfig: {
...properties.queryConfig,
query: {
assets,
},
},
items: newItems || [],
});
};

return <GeneralPropertiesAlarmsSection {...widget} onDeleteAssetQuery={deleteQuery} />;
};

export const PropertiesAlarmsSection: React.FC<PropertiesAlarmsSectionProps> = (props) => {
const isTableWidget = props.type === 'iot-table';
return isTableWidget ? <TablePropertiesAlarmsSection {...props} /> : <GeneralPropertiesAlarmsSection {...props} />;
};
export default PropertiesAlarmsSection;
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ReactNode } from 'react';
import React from 'react';
import { useDrop } from 'react-dnd';

Expand All @@ -6,44 +7,51 @@ import { ItemTypes } from '~/components/dragLayer/itemTypes';
import { assignDefaultStyles } from '../utils/assignDefaultStyleSettings';
import { mergeAssetQueries } from '~/util/mergeAssetQueries';
import './queryWidget.css';
import type { ReactNode } from 'react';
import type { QueryWidget } from '../types';
import type { ResourcePanelItem } from '~/components/resourceExplorer/components/panel';

export type onDropHandler<W extends QueryWidget = QueryWidget> = (item: ResourcePanelItem, widget: W) => W;
export const defaultOnDropHandler: onDropHandler = (item, widget) => {
const { assetSummary } = item;
const currentAssets = widget.properties.queryConfig.query?.assets ?? [];

const mergedAssets = mergeAssetQueries(currentAssets, {
assetId: assetSummary.assetId || '',
properties: assetSummary.properties.map(({ propertyId }) => ({
propertyId: propertyId || '',
})),
});

return {
...widget,
properties: {
...widget.properties,
queryConfig: {
...widget.properties.queryConfig,
query: {
assets: mergedAssets,
},
},
},
};
};
/**
*
* HOC widget component for handling drag and drop of a widget that can have multiple assets per query
*
*/
const MultiQueryWidgetComponent: React.FC<QueryWidget & { children: ReactNode }> = ({ children, ...widget }) => {
const MultiQueryWidgetComponent: React.FC<QueryWidget & { children: ReactNode; onDropHandler?: onDropHandler }> = ({
children,
onDropHandler = defaultOnDropHandler,
...widget
}) => {
const { update } = useWidgetActions<QueryWidget>();

const [, drop] = useDrop(
() => ({
accept: [ItemTypes.ResourceExplorerAssetProperty, ItemTypes.ResourceExplorerAlarm],
drop: ({ assetSummary }: ResourcePanelItem) => {
const currentAssets = widget.properties.queryConfig.query?.assets ?? [];

const mergedAssets = mergeAssetQueries(currentAssets, {
assetId: assetSummary.assetId || '',
properties: assetSummary.properties.map(({ propertyId }) => ({
propertyId: propertyId || '',
})),
});

const updatedWidget = {
...widget,
properties: {
...widget.properties,
queryConfig: {
...widget.properties.queryConfig,
query: {
assets: mergedAssets,
},
},
},
};

drop: (item: ResourcePanelItem) => {
const updatedWidget = onDropHandler(item, widget);
update(assignDefaultStyles(updatedWidget));
},
}),
Expand Down
19 changes: 12 additions & 7 deletions packages/dashboard/src/customization/widgets/table/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,29 @@ import { Table } from '@iot-app-kit/react-components';

import { useDataSource } from '../../hooks/useDataSource';
import { computeQueryConfigKey } from '../utils/computeQueryConfigKey';
import { useAssetDescriptionMapAsync } from '~/hooks/useAssetDescriptionMapAsync';
import { getTableDefinitions } from './helper';
import { DEFAULT_TABLE_COLUMN_DEFINITIONS } from '~/customization/widgets';
import type { DashboardState } from '~/store/state';
import type { TableWidget } from '../types';

const TableWidgetComponent: React.FC<TableWidget> = (widget) => {
const viewport = useSelector((state: DashboardState) => state.dashboardConfiguration.viewport);

const { queryConfig } = widget.properties;
const { queryConfig, columnDefinitions = DEFAULT_TABLE_COLUMN_DEFINITIONS, items = [] } = widget.properties;

const { dataSource } = useDataSource();
const queries = dataSource.query && queryConfig.query ? [dataSource.query?.timeSeriesData(queryConfig.query)] : [];
const key = computeQueryConfigKey(viewport, widget.properties.queryConfig);
const siteWiseQueries = widget.properties.queryConfig.query || { assets: [] };
const descriptionMap = useAssetDescriptionMapAsync(siteWiseQueries);
const { items, columnDefinitions } = getTableDefinitions(siteWiseQueries, descriptionMap);

return <Table key={key} queries={queries} viewport={viewport} columnDefinitions={columnDefinitions} items={items} />;
return (
<Table
resizableColumns
key={key}
queries={queries}
viewport={viewport}
columnDefinitions={columnDefinitions}
items={items}
/>
);
};

export default TableWidgetComponent;

0 comments on commit 1d8d44e

Please sign in to comment.