Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,106 +26,81 @@ import { Table, Column } from '@gpa-gemstone/react-table';
import * as _ from 'lodash';
import { Application, SystemCenter } from '@gpa-gemstone/application-typings';
import ExternalDBUpdate from '../CommonComponents/ExternalDBUpdate';
import { Search, Modal, LoadingScreen } from '@gpa-gemstone/react-interactive';
import { DefaultSearch } from '@gpa-gemstone/common-pages';
import { ByMeterSlice } from '../Store/Store';
import { useAppDispatch, useAppSelector } from '../hooks';
import { Search, Modal, LoadingScreen, GenericController, SearchBar } from '@gpa-gemstone/react-interactive';
import { useNavigate } from "react-router-dom";
import { Paging } from '@gpa-gemstone/react-table';

declare var homePath: string;

const ByMeter: Application.Types.iByComponent = (props) => {
let navigate = useNavigate();
const dispatch = useAppDispatch();

const data = useAppSelector(ByMeterSlice.SearchResults);
const [filters, setFilters] = React.useState<Search.IFilter<SystemCenter.Types.DetailedMeter>[]>([]);
const [data, setData] = React.useState<SystemCenter.Types.DetailedMeter[]>([]);
const [ascending, setAscending] = React.useState<boolean>(true);
const [sortKey, setSortKey] = React.useState<keyof SystemCenter.Types.DetailedMeter>("Name");

const cState = useAppSelector(ByMeterSlice.PagedStatus);
const allPages = useAppSelector(ByMeterSlice.TotalPages);
const currentPage = useAppSelector(ByMeterSlice.CurrentPage);
const [page, setPage] = React.useState<number>(currentPage);
const [status, setStatus] = React.useState<Application.Types.Status>('uninitiated');
const [allPages, setAllPages] = React.useState<number>(0);
const [totalRecords, setTotalRecords] = React.useState<number>(0);
const [recordsPerPage, setRecordsPerPage] = React.useState<number>(0);
const [addlFields, setAddlFields] = React.useState<Search.IField<SystemCenter.Types.DetailedMeter>[]>([])
const [page, setPage] = React.useState<number>(0);

const [showEXTModal, setShowExtModal] = React.useState<boolean>(false);
const extDbUpdateAll = React.useRef<() => (() => void)>(undefined);

React.useEffect(() => {
dispatch(ByMeterSlice.PagedSearch({ sortField: sortKey, ascending, page }));
}, [sortKey, ascending, page]);

React.useEffect(() => {
if (cState === 'uninitiated' || cState === 'changed')
dispatch(ByMeterSlice.PagedSearch({ sortField: sortKey, ascending, page }));
}, [cState]);
setStatus('loading');
const handle = new GenericController<SystemCenter.Types.DetailedMeter>(`${homePath}api/OpenXDA/ByMeter`, "Name", true).PagedSearch(filters, sortKey, ascending, page);
handle.done((d) => {
setStatus('idle');
setData(JSON.parse(d.Data as unknown as string));
setAllPages(d.NumberOfPages);
setTotalRecords(d.TotalRecords);
setRecordsPerPage(d.RecordsPerPage);
if (page >= d.NumberOfPages)
setPage(Math.max(d.NumberOfPages - 1, 0));
})
handle.fail(() => setStatus('error'))
return () => { if (handle != null && handle.abort != null) handle.abort() }
}, [filters, sortKey, ascending, page])

function handleSelect(item) {
navigate(`${homePath}index.cshtml?name=Meter&MeterID=${item.row.ID}`);
}

function goNewMeterWizard() {
navigate(`${homePath}index.cshtml?name=NewMeterWizard`);
}

function getAdditionalFields(setFields) {

let handle = $.ajax({
type: "GET",
url: `${homePath}api/SystemCenter/AdditionalFieldView/ParentTable/Meter/FieldName/0`,
contentType: "application/json; charset=utf-8",
cache: false,
async: true
});

function ConvertType(type: string) {
if (type == 'string' || type == 'integer' || type == 'number' || type == 'datetime' || type == 'boolean')
return { type: type }
return {
type: 'enum', enum: [{ Label: type, Value: type }]
}
}

handle.done((d: Array<SystemCenter.Types.AdditionalFieldView>) => {
let ordered = _.orderBy(d.filter(item => item.Searchable).map(item => (
{ label: `[AF${item.ExternalDB != undefined ? " " + item.ExternalDB : ''}] ${item.FieldName}`, key: item.FieldName, ...ConvertType(item.Type), isPivotField: true } as Search.IField<SystemCenter.Types.DetailedMeter>
)), ['label'], ["asc"]);
setFields(ordered)
});

return () => {
if (handle != null && handle.abort == null) {
handle.abort()
};
};
}

function getEnum(setOptions, field) {
let handle = null;
if (field.type != 'enum' || field.enum == undefined || field.enum.length != 1)
return () => { };

handle = $.ajax({
type: "GET",
url: `${homePath}api/ValueList/Group/${field.enum[0].Value}`,
contentType: "application/json; charset=utf-8",
dataType: 'json',
cache: true,
async: true
});

handle.done(d => setOptions(d.map(item => ({ Value: item.ID, Label: item.Value }))))
return () => {
if (handle != null && handle.abort == null)
handle.abort();
}
}
React.useEffect(() => {
return getAdditionalFields(setAddlFields);
}, [])

return (
<div style={{ width: '100%', height: '100%' }}>
<LoadingScreen Show={cState === 'loading'} />
<LoadingScreen Show={status === 'loading'} />
<div className="container-fluid d-flex h-100 flex-column">
<div className="row">
<DefaultSearch.Meter Slice={ByMeterSlice} GetEnum={getEnum} GetAddlFields={getAdditionalFields} StorageID="MetersFilter">
<SearchBar<SystemCenter.Types.DetailedMeter>
CollumnList={[
{ label: 'Name', key: 'Name', type: 'string', isPivotField: false },
{ label: 'Key', key: 'LocationKey', type: 'string', isPivotField: false },
{ label: 'Asset Key', key: 'Asset', type: 'string', isPivotField: false },
{ label: 'Meter Key', key: 'Meter', type: 'string', isPivotField: false },
{ label: 'Number of Assets', key: 'Assets', type: 'integer', isPivotField: false },
{ label: 'Number of Meters', key: 'Meters', type: 'integer', isPivotField: false },
{ label: 'Description', key: 'Description', type: 'string', isPivotField: false },
...addlFields]}
SetFilter={setFilters}
Direction={'left'}
defaultCollumn={{ label: 'Name', key: 'Name', type: 'string', isPivotField: false }}
Width={'50%'}
Label={'Search'}
ShowLoading={status === 'loading'}
ResultNote={status === 'error' ? 'Could not complete Search' : `Displaying Meter(s) ${totalRecords > 0 ? (recordsPerPage * page + 1) : 0} - ${recordsPerPage * page + data.length} out of ${totalRecords}`}
GetEnum={getEnum}
StorageID={"MetersFilter"}
>
<li className="nav-item" style={{ width: '15%', paddingRight: 10 }}>
<fieldset className="border" style={{ padding: '10px', height: '100%' }}>
<legend className="w-auto" style={{ fontSize: 'large' }}>Wizards:</legend>
Expand All @@ -143,7 +118,7 @@ const ByMeter: Application.Types.iByComponent = (props) => {
</form>
</fieldset>
</li>
</DefaultSearch.Meter>
</SearchBar>
</div>
<div className="row" style={{ flex: 1, overflow: 'hidden' }}>
<Table<SystemCenter.Types.DetailedMeter>
Expand Down Expand Up @@ -232,4 +207,57 @@ const ByMeter: Application.Types.iByComponent = (props) => {
)
}

export default ByMeter;
export default ByMeter;

function getAdditionalFields(setFields) {

let handle = $.ajax({
type: "GET",
url: `${homePath}api/SystemCenter/AdditionalFieldView/ParentTable/Meter/FieldName/0`,
contentType: "application/json; charset=utf-8",
cache: false,
async: true
});

function ConvertType(type: string) {
if (type == 'string' || type == 'integer' || type == 'number' || type == 'datetime' || type == 'boolean')
return { type: type }
return {
type: 'enum', enum: [{ Label: type, Value: type }]
}
}

handle.done((d: Array<SystemCenter.Types.AdditionalFieldView>) => {
let ordered = _.orderBy(d.filter(item => item.Searchable).map(item => (
{ label: `[AF${item.ExternalDB != undefined ? " " + item.ExternalDB : ''}] ${item.FieldName}`, key: item.FieldName, ...ConvertType(item.Type), isPivotField: true } as Search.IField<SystemCenter.Types.DetailedMeter>
)), ['label'], ["asc"]);
setFields(ordered)
});

return () => {
if (handle != null && handle.abort == null) {
handle.abort()
};
};
}

function getEnum(setOptions, field) {
let handle = null;
if (field.type != 'enum' || field.enum == undefined || field.enum.length != 1)
return () => { };

handle = $.ajax({
type: "GET",
url: `${homePath}api/ValueList/Group/${field.enum[0].Value}`,
contentType: "application/json; charset=utf-8",
dataType: 'json',
cache: true,
async: true
});

handle.done(d => setOptions(d.map(item => ({ Value: item.ID, Label: item.Value }))))
return () => {
if (handle != null && handle.abort == null)
handle.abort();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,12 @@ import TransformerAttributes from '../AssetAttribute/Transformer';
import { useNavigate } from 'react-router-dom';
import { AssetAttributes } from '../AssetAttribute/Asset';
import { getAssetTypes, getAssetWithAdditionalFields } from '../../../TS/Services/Asset';
import { DBActionAsset, DBMeterAction, SelectAssetStatus } from '../Store/AssetSlice'
import { ConfigurableTable, ConfigurableColumn, Column, Paging } from '@gpa-gemstone/react-table';
import { ReactIcons } from '@gpa-gemstone/gpa-symbols';
import { Warning, Modal, LoadingScreen, GenericController, ServerErrorIcon } from '@gpa-gemstone/react-interactive';
import { ToolTip } from '@gpa-gemstone/react-forms';
import DERAttributes from '../AssetAttribute/DER';
import { useAppDispatch, useAppSelector } from '../hooks';
import { useAppSelector } from '../hooks';
import AssetSelect from '../Asset/AssetSelect';
import { SelectRoles } from '../Store/UserSettings';
import GenerationAttributes from '../AssetAttribute/Generation';
Expand All @@ -53,7 +52,7 @@ interface IProps { Meter: OpenXDA.Types.Meter }
const MeterAssetWindow = (props: IProps) => {
const navigate = useNavigate();

const MeterAssetController = new GenericController<OpenXDA.Types.MeterAsset>(`${homePath}api/OpenXDA/DetailedMeterAsset/${props.Meter.ID}`, "AssetName", true);
const MeterAssetController = React.useMemo(() => new GenericController<OpenXDA.Types.MeterAsset>(`${homePath}api/OpenXDA/DetailedMeterAsset/${props.Meter.ID}`, "AssetName", true), [props.Meter.ID]);
const PagingID = 'MeterAssetPage'

const [assetTypes, setAssetTypes] = React.useState<OpenXDA.Types.AssetType[]>([]);
Expand All @@ -65,12 +64,10 @@ const MeterAssetWindow = (props: IProps) => {

const [showSelect, setShowSelect] = React.useState<boolean>(false);

// Asset Slice Consts
const [data, setData] = React.useState<OpenXDA.Types.MeterAsset[]>([]);
const dispatch = useAppDispatch();
const status = useAppSelector(SelectAssetStatus) as Application.Types.Status;
const [sortKey, setSortKey] = React.useState<keyof OpenXDA.Types.MeterAsset>('AssetName');
const [ascending, setAscending] = React.useState<boolean>(true);
const [refreshTrigger, setRefreshTrigger] = React.useState<boolean>(false);

const [hover, setHover] = React.useState<('submit' | 'clear' | 'none')>('none');
const roles = useAppSelector(SelectRoles);
Expand Down Expand Up @@ -113,7 +110,7 @@ const MeterAssetWindow = (props: IProps) => {
setPageState('idle');
}).fail(() => setPageState('error'));
return () => { if (handle != null && handle?.abort != null) handle.abort(); }
}, [sortKey, ascending, page, status])
}, [sortKey, ascending, page, refreshTrigger])

function setActiveAsset(assetID: number, assetType: OpenXDA.Types.AssetTypeName) {
if (assetID == 0) {
Expand Down Expand Up @@ -145,6 +142,15 @@ const MeterAssetWindow = (props: IProps) => {
<h4>Assets:</h4>
</div>
</div>
<div className="row">
<div className="col">
<p style={{ marginTop: 2, marginBottom: 2 }}>
{pageState === 'error' ? 'Could not complete Search' :
pageState === 'loading' ? 'Loading...' :
`Displaying Asset(s) ${pageInfo.TotalRecords > 0 ? (pageInfo.RecordsPerPage * page + 1) : 0} - ${pageInfo.RecordsPerPage * page + data.length} out of ${pageInfo.TotalRecords}`}
</p>
</div>
</div>
</div>
<div className="card-body" style={{ paddingBottom: 0, display: 'flex', flexDirection: 'column', flex: 1, overflow: 'hidden' }}>
<div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column' }}>
Expand Down Expand Up @@ -258,7 +264,10 @@ const MeterAssetWindow = (props: IProps) => {
> <p></p>
</Column>
</ConfigurableTable>
<Warning Show={showDeleteWarning} CallBack={(confirmed) => { if (confirmed) dispatch(DBMeterAction({ verb: 'DELETE', assetID: activeAsset.ID, meterID: props.Meter.ID, locationID: props.Meter.LocationID })); setShowDeleteWarning(false); }} Title={'Remove ' + (activeAsset?.AssetName ?? 'Asset') + ' from ' + (props.Meter?.Name ?? 'Meter')} Message={'This will permanently remove the Asset from this Meter.'} />
<Warning Show={showDeleteWarning}
CallBack={(confirmed) => { if (confirmed) removeAsset(activeAsset.ID, props.Meter.ID, props.Meter.LocationID).then(() => setRefreshTrigger(val => !val)); setShowDeleteWarning(false); }}
Title={'Remove ' + (activeAsset?.AssetName ?? 'Asset') + ' from ' + (props.Meter?.Name ?? 'Meter')}
Message={'This will permanently remove the Asset from this Meter.'} />
<LoadingScreen Show={pageState == 'loading'} />
<ServerErrorIcon Show={pageState == 'error'} Size={40} Label={'A Server Error Occurred. Please Reload the Application.'} />
<div className="row">
Expand All @@ -275,7 +284,7 @@ const MeterAssetWindow = (props: IProps) => {
CallBack={(confirm) => {
setShoweditNew(false);
if (confirm) {
dispatch(DBActionAsset({ verb: 'POST', record: activeAsset, meterID: props.Meter.ID, locationID: props.Meter.LocationID }));
addNewAsset(activeAsset, props.Meter.ID, props.Meter.LocationID).then(() => setRefreshTrigger(val => !val));
}
}}
ConfirmShowToolTip={AssetAttributes.AttributeError(activeAsset).length > 0}
Expand Down Expand Up @@ -324,7 +333,7 @@ const MeterAssetWindow = (props: IProps) => {
OnCloseFunction={(selected, confirm) => {
setShowSelect(false);
if (confirm)
dispatch(DBMeterAction({ verb: 'POST', assetID: selected[0].ID, meterID: props.Meter.ID, locationID: props.Meter.LocationID }));
addExistingAsset(selected[0].ID, props.Meter.ID, props.Meter.LocationID).then(() => setRefreshTrigger(val => !val));
}}
/>
</>
Expand Down Expand Up @@ -355,4 +364,41 @@ const MeterAssetWindow = (props: IProps) => {
}
}

export default MeterAssetWindow;
export default MeterAssetWindow;

function addExistingAsset(assetID: number, meterID: number, locationID: number) {
return $.ajax({
type: "POST",
url: `${homePath}api/OpenXDA/Meter/${meterID}/Asset/${assetID}/${locationID}`,
contentType: "application/json; charset=utf-8",
dataType: 'json',
data: JSON.stringify(''),
cache: false,
async: true
});
}

function addNewAsset(asset: OpenXDA.Types.Asset, meterID: number, locationID: number) {
const route = `${homePath}api/OpenXDA/Asset/New/Meter/${meterID}/${locationID}`;
return $.ajax({
type: "POST",
url: route,
contentType: "application/json; charset=utf-8",
dataType: 'json',
data: JSON.stringify({ Asset: asset }),
cache: false,
async: true
})
}

function removeAsset(assetID: number, meterID: number, locationID: number) {
return $.ajax({
type: "DELETE",
url: `${homePath}api/OpenXDA/Meter/${meterID}/Asset/${assetID}/${locationID}`,
contentType: "application/json; charset=utf-8",
dataType: 'json',
data: JSON.stringify(''),
cache: false,
async: true
});
}
Loading