Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
4167285
Added LocationDrawing button to AssetConnection page
Aug 9, 2024
e34966d
Added to nmw and fixed styling
Aug 13, 2024
4d658af
Made the locations an array of locations and btndropdown
Aug 15, 2024
22a0e1b
Added getLocations()
Aug 15, 2024
a3e1974
Updated LocationDrawing functionality, does not work still
Oct 4, 2024
3b50146
functional
Oct 4, 2024
76567ff
Now updates properly w loading and alert
Oct 4, 2024
e813040
Alert and made sure was passing in correct format
Oct 7, 2024
c60618b
styling
Oct 7, 2024
b0ef4c4
Apply suggestions from code review AssetConnection
Oct 8, 2024
477cd2e
removed drowpdown type
Oct 9, 2024
d043b55
Apply suggestions from code review
Oct 9, 2024
bf281f8
added tooltip back for 0 Locations
Oct 9, 2024
5551cbd
Tooltip and button disable
Oct 21, 2024
746d02b
Button wording
Nov 1, 2024
228a6d4
structural changes to locationdrawings
Nov 6, 2024
cae137c
updated imports and fixed 'undefined' error
Nov 8, 2024
38cd223
simplified functionality, LocationDrawings.tsx wip
Nov 11, 2024
66e016e
moved some redundant functions
Nov 12, 2024
11b2ff7
added the SetRecord functionality
Nov 13, 2024
d96ec5b
Changed to just an ID prop
Nov 13, 2024
479be17
moved fetchDrawing up, does not get called properly
Nov 22, 2024
df67bfc
functional as single button
Nov 25, 2024
a86ed29
removed the button from modal comp
Nov 26, 2024
93cba9d
ToolTips and Error
Nov 27, 2024
0244e5f
fixed hover for AssetConnection
Nov 27, 2024
f88b395
map
Nov 27, 2024
574cbd6
modified disable
Dec 2, 2024
c4660f7
fixed disable on MeterLocationProperties
Dec 6, 2024
655ed6f
refactor: replace LocationDrawingsModal with LocationDrawingsButton i…
Dec 31, 2024
6db7f96
fix: update LocationDrawingsButton to use location IDs for error hand…
Jan 7, 2025
bd0bd10
quick fixes
Jan 15, 2025
4fc731a
optimize multipleLocations and error management in LocationDrawingsBu…
Jan 15, 2025
f93c3ae
improve error handling and cleanup in LocationDrawingsButton
Jan 16, 2025
7c45f36
enhance LocationDrawingsButton with dropdown options and error handling
Jan 17, 2025
27a4819
update LocationDrawingsModal and LocationDrawingsTable for improved s…
Jan 17, 2025
9a7eae7
Removed unintended (whitespace) changes: ConnectionPage, AssetPage, NMW
Jan 17, 2025
7d97c4f
update LocationDrawingsButton and related components for loading stat…
Jan 21, 2025
ab95603
Made prop optional
Jan 22, 2025
c537b31
updated LocationDrawingsButton props with memoized location arrays
Jan 24, 2025
2c5c685
fixed error handling and DBSearch, organized AssetConnection changes
Jan 27, 2025
4c66fcb
adjust table column styles and csproj
Jan 28, 2025
90f86c6
Updated style of component
Jan 29, 2025
34faba6
rename modal handler and improve tooltip error display
Jan 31, 2025
6581c96
updated props and imports
Jan 31, 2025
3b96c34
removed redundant button fixed tooltip
Jan 31, 2025
09bd2c7
added missing imports
Feb 5, 2025
ff331c0
cleaned up NMW/AssetPage
Feb 12, 2025
1f8a16c
quick fixes
Feb 17, 2025
fac8a0f
removed fieldset
Feb 17, 2025
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
4 changes: 3 additions & 1 deletion Source/Applications/SystemCenter/SystemCenter.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,9 @@
<TypeScriptCompile Include="wwwroot\Scripts\TSX\SystemCenter\MATLABAnalytics\MATLABAnalyticForm.tsx" />
<TypeScriptCompile Include="wwwroot\Scripts\TSX\SystemCenter\MATLABAnalytics\MATLABAnalyticInfo.tsx" />
<TypeScriptCompile Include="wwwroot\Scripts\TSX\SystemCenter\MATLABAnalytics\MATLABAnalyticSQLSetting.tsx" />
<TypeScriptCompile Include="wwwroot\Scripts\TSX\SystemCenter\Meter\PropertyUI\LocationDrawings.tsx" />
<TypeScriptCompile Include="wwwroot\Scripts\TSX\SystemCenter\CommonComponents\LocationDrawingsButton.tsx" />
<TypeScriptCompile Include="wwwroot\Scripts\TSX\SystemCenter\Location\AddEditDrawingsModal.tsx" />
<TypeScriptCompile Include="wwwroot\Scripts\TSX\SystemCenter\Location\LocationDrawingsTable.tsx" />
<Content Include="wwwroot\Scripts\TSX\SystemCenter\NewMeterWizard\TemplateWindow.tsx" />
<Content Include="wwwroot\Scripts\TSX\SystemCenter\NewMeterWizard\ChannelSelector.tsx" />
<TypeScriptCompile Include="wwwroot\Scripts\TSX\SystemCenter\NewMeterWizard\CustomerAssetGroupPage.tsx" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//******************************************************************************************************
//******************************************************************************************************
// LocationMeter.tsx - Gbtc
//
// Copyright © 2020, Grid Protection Alliance. All Rights Reserved.
Expand Down Expand Up @@ -31,6 +31,7 @@ import { OpenXDA } from '@gpa-gemstone/application-typings';
import { useAppSelector, useAppDispatch } from '../hooks';
import { AssetConnectionTypeSlice } from '../Store/Store';
import { SelectRoles } from '../Store/UserSettings';
import LocationDrawingsButton from '../CommonComponents/LocationDrawingsButton';

interface AssetConnection {
AssetRelationShipTypeID: number,
Expand All @@ -40,8 +41,7 @@ interface AssetConnection {
AssetName: string
}

function AssetConnectionWindow(props: { Name: string, ID: number, TypeID: number}): JSX.Element{

function AssetConnectionWindow(props: { Name: string, ID: number, TypeID: number }): JSX.Element {
let history = useHistory();
let dispatch = useAppDispatch();

Expand All @@ -52,38 +52,41 @@ function AssetConnectionWindow(props: { Name: string, ID: number, TypeID: number
const [selectedTypeID, setSelectedtypeID] = React.useState<number>(0);
const [localAssets, setLocalAssets] = React.useState<Array<OpenXDA.Types.Asset>>([]);

const [locations, setLocations] = React.useState<OpenXDA.Types.Location[]>([]);
const [isLoadingLocations, setIsLoadingLocations] = React.useState<boolean>(false);

const [sortKey, setSortKey] = React.useState<string>('AssetKey');
const [ascending, setAscending] = React.useState<boolean>(true);
const [showModal, setShowModal] = React.useState<boolean>(false);

const [status, setStatus] = React.useState<'idle' | 'loading' | 'error'>('idle');
const actStatus = useAppSelector(AssetConnectionTypeSlice.SearchStatus);
const [trigger, setTrigger] = React.useState<number>(0);
const actStatus = useAppSelector(AssetConnectionTypeSlice.SearchStatus);

const [hover, setHover] = React.useState<('Update' | 'Reset' | 'None')>('None');
const [hover, setHover] = React.useState<('Update' | 'Reset' | 'None' | 'Drawings')>('None');
const roles = useAppSelector(SelectRoles);

React.useEffect(() => {
let handle = getAssetConnections();
return () => { if (handle != null || handle.abort != null) handle.abort();}
const handle = getAssetConnections();
return () => { if (handle != null && handle.abort != null) handle.abort(); }
}, [props.ID, trigger])

React.useEffect(() => {
if (props.ID > 0) {
let sqlString = `(SELECT AssetRelationshipTypeID FROM AssetRelationshipTypeAssetType LEFT JOIN Asset ON `
sqlString = sqlString + `Asset.AssetTypeID <> ${props.TypeID} AND Asset.AssetTypeID = AssetRelationshipTypeAssetType.assetTypeID AND `
sqlString = sqlString + `Asset.ID IN (SELECT AssetID FROM AssetLocation WHERE LocationID IN (Select LocationID FROM AssetLocation WHERE AssetID = ${props.ID})) `
sqlString = sqlString + `GROUP BY AssetRelationshipTypeAssetType.AssetTypeID, AssetRelationshipTypeAssetType.AssetRelationshipTypeID `
sqlString = sqlString + `HAVING COUNT(Asset.ID) > 0)`
sqlString = sqlString + `Asset.AssetTypeID <> ${props.TypeID} AND Asset.AssetTypeID = AssetRelationshipTypeAssetType.assetTypeID AND `
sqlString = sqlString + `Asset.ID IN (SELECT AssetID FROM AssetLocation WHERE LocationID IN (Select LocationID FROM AssetLocation WHERE AssetID = ${props.ID})) `
sqlString = sqlString + `GROUP BY AssetRelationshipTypeAssetType.AssetTypeID, AssetRelationshipTypeAssetType.AssetRelationshipTypeID `
sqlString = sqlString + `HAVING COUNT(Asset.ID) > 0)`
const filter: Search.IFilter<OpenXDA.Types.AssetConnectionType>[] = [
{ FieldName: 'ID', SearchText: `(SELECT AssetRelationshipTypeID FROM AssetRelationshipTypeAssetType WHERE AssetTypeID = ${props.TypeID})`, Operator: 'IN', Type: 'query', IsPivotColumn: false },
{
FieldName: 'ID', SearchText: sqlString, Operator: 'IN', Type: 'query', IsPivotColumn: false
}
]
]
dispatch(AssetConnectionTypeSlice.DBSearch({ filter: filter }))
}
}, [props.TypeID])
}, [props.TypeID])

React.useEffect(() => {
if (selectedTypeID == 0) {
Expand All @@ -97,7 +100,7 @@ function AssetConnectionWindow(props: { Name: string, ID: number, TypeID: number

React.useEffect(() => {
let index = assetConnectionTypes.findIndex(t => t.ID == selectedTypeID);
if (index == -1 && assetConnectionTypes.length> 0)
if (index == -1 && assetConnectionTypes.length > 0)
setSelectedtypeID(assetConnectionTypes[0].ID)
}, [assetConnectionTypes])

Expand All @@ -106,7 +109,20 @@ function AssetConnectionWindow(props: { Name: string, ID: number, TypeID: number
if (index == -1 && localAssets.length > 0)
setSelectedAssetID(localAssets[0].ID)
}, [localAssets])


React.useEffect(() => {
setIsLoadingLocations(true);
const h = $.ajax({
type: "GET",
url: `${homePath}api/OpenXDA/Asset/${props.ID}/Locations`,
contentType: "application/json; charset=utf-8",
dataType: 'json',
cache: true,
async: true
}).done(data => { setLocations(data); setIsLoadingLocations(false); });
return () => { if (h!= null && h.abort != null) h.abort(); }
}, [assetConnections])

function getAssetConnections(): JQuery.jqXHR<OpenXDA.Types.AssetConnection> {
setStatus('loading');
return $.ajax({
Expand Down Expand Up @@ -171,7 +187,7 @@ function AssetConnectionWindow(props: { Name: string, ID: number, TypeID: number
url: `${homePath}api/OpenXDA/AssetConnection/Add`,
contentType: "application/json; charset=utf-8",
dataType: 'json',
data: JSON.stringify({ ID: 0, AssetRelationshipTypeID: selectedTypeID, ParentID: props.ID, ChildID: selectedAssetID}),
data: JSON.stringify({ ID: 0, AssetRelationshipTypeID: selectedTypeID, ParentID: props.ID, ChildID: selectedAssetID }),
cache: false,
async: true
}).done(() => {
Expand All @@ -181,9 +197,8 @@ function AssetConnectionWindow(props: { Name: string, ID: number, TypeID: number
});
}


function handleSelect(item) {
history.push({ pathname: homePath + 'index.cshtml', search: '?name=Asset&AssetID=' + item.row.AssetID})
history.push({ pathname: homePath + 'index.cshtml', search: '?name=Asset&AssetID=' + item.row.AssetID })
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gcsantos-gpa I bet this conflicts with #606 ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would yes

}

function hasPermissions(): boolean {
Expand All @@ -210,7 +225,7 @@ function AssetConnectionWindow(props: { Name: string, ID: number, TypeID: number
</div>
</div>

if (status == 'loading' || actStatus == 'loading' )
if (status == 'loading' || actStatus == 'loading')
return <div className="card" style={{ marginBottom: 10 }}>
<div className="card-header">
<div className="row">
Expand All @@ -232,10 +247,16 @@ function AssetConnectionWindow(props: { Name: string, ID: number, TypeID: number
return (
<div className="card" style={{ flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
<div className="card-header">
<div className="row">
<div className="col">
<div className="row justify-content-between">
<div className="col-6">
<h4>Connections:</h4>
</div>
<div className="pr-4">
<LocationDrawingsButton
Locations={locations}
IsLoadingLocations={isLoadingLocations}
/>
</div>
</div>
</div>
<div className="card-body" style={{ flex: 1, overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
Expand Down Expand Up @@ -304,7 +325,7 @@ function AssetConnectionWindow(props: { Name: string, ID: number, TypeID: number
e.stopPropagation();
if (hasPermissions()) deleteAssetConnection(item);
}}><span>{TrashCan}</span></button>
</> }
</>}
> <p></p>
</Column>
</Table>
Expand Down Expand Up @@ -356,7 +377,6 @@ function AssetConnectionWindow(props: { Name: string, ID: number, TypeID: number
</div>}
</Modal>
</div>

);

}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
//******************************************************************************************************
// LocationDrawings.tsx - Gbtc
//
// Copyright © 2024, Grid Protection Alliance. All Rights Reserved.
//
// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
// the NOTICE file distributed with this work for additional information regarding copyright ownership.
// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this
// file except in compliance with the License. You may obtain a copy of the License at:
//
// http://opensource.org/licenses/MIT
//
// Unless agreed to in writing, the subject software distributed under the License is distributed on an
// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
// License for the specific language governing permissions and limitations.
//
// Code Modification History:
// ----------------------------------------------------------------------------------------------------
// 01/06/2024 - Collins Self
// Generated original version of source code.
//
//******************************************************************************************************
import React from 'react';
import { BtnDropdown, GenericController, LoadingScreen, Modal, ServerErrorIcon, ToolTip } from '@gpa-gemstone/react-interactive';
import { OpenXDA } from '@gpa-gemstone/application-typings';
import { CrossMark } from '@gpa-gemstone/gpa-symbols';
import LocationDrawingsTable from '../Location/LocationDrawingsTable';

const LocationDrawingController = new GenericController(`${homePath}api/LocationDrawing`, "Name", true);
interface LocationDrawingsButtonProps {
Locations: OpenXDA.Types.Location[];
IsLoadingLocations?: boolean;
}

type DropDownOption = {
Label: JSX.Element | string,
Callback: () => void,
Disabled: boolean
ToolTipContent: JSX.Element,
ShowToolTip: boolean,
ToolTipLocation: ('top' | 'bottom' | 'left' | 'right'),
Key: string | number
}

const isValid = (location: OpenXDA.Types.Location, drawingData) => {
let e = "";

if (location == null
|| (location.Alias === ""
&& location.Description === ""
&& location.ID === 0
&& location.Latitude == null
&& location.LocationKey === ""
&& location.Longitude == null
&& location.Name === ""))
e = 'No location(s) have been set.';
else if (drawingData.length == 0)
e = 'No drawing(s) associated with location.';
return e;
}

const LocationDrawingsButton: React.FC<LocationDrawingsButtonProps> = (props) => {
const [hover, setHover] = React.useState<'none' | 'drawings'>('none');
const [pageState, setPageState] = React.useState<"loading" | "error" | "idle">("idle");
const [selectedLocation, setSelectedLocation] = React.useState<OpenXDA.Types.Location>();
const multipleLocations = React.useMemo(() => props.Locations.length > 1, [props.Locations]);
const [showDrawingsModal, setShowDrawingsModal] = React.useState<boolean>(false);
const [locationOptions, setLocationOptions] = React.useState<DropDownOption[]>([]);

React.useEffect(() => { // Generates the map of errors for each location
setPageState('loading');
setLocationOptions([]);

const handles = props.Locations.map((location, i) => {
if (location == null)
return null;
const handle = LocationDrawingController.DBSearch([], 'Name', true, location.ID)
.done((result) => {
const error = isValid(location, result);
const option: DropDownOption = {
Label: location.Name,
Callback: () => handleClickDrawingsModal(location),
Disabled: error != "",
ToolTipContent: <p>{CrossMark} {error}</p>,
ShowToolTip: error != "",
ToolTipLocation: "left",
Key: i
};
setLocationOptions(prev => [...prev, option]);
})
.fail(() => { throw new Error() });
return handle;
}).filter(handle => handle != null);

Promise.all(handles)
.then(() => setPageState('idle'),
() => setPageState('error'));

return () => handles.forEach(handle => () => { if (handle != null && handle?.abort != null) handle.abort() });
}, [props.Locations]);

const handleClickDrawingsModal = (loc?: OpenXDA.Types.Location) => {
if (showDrawingsModal) {
setShowDrawingsModal(false);
return;
}
if (loc == undefined) return;
setSelectedLocation(loc);
setShowDrawingsModal(true);
};

return (
<>
<LoadingScreen Show={pageState == 'loading' || props.IsLoadingLocations == true} />
<ServerErrorIcon Show={pageState == 'error'} Size={40} Label={'A Server Error Occurred. Please Reload the Application.'} />
{!multipleLocations
? <>
<button
className={locationOptions[0]?.Disabled ? "btn btn-primary disabled" : "btn btn-primary"}
onClick={() => locationOptions[0]?.Disabled ? null : handleClickDrawingsModal(props.Locations[0])}
data-tooltip={"DrawingsModal"}
onMouseEnter={() => setHover('drawings')}
onMouseLeave={() => setHover('none')}
>Open {props.Locations[0]?.Name} Drawings
</button>
<ToolTip
Show={locationOptions[0]?.Disabled && hover === 'drawings'}
Position={'left'}
Target={"DrawingsModal"}
Zindex={9999}
><p>{locationOptions[0]?.ToolTipContent}</p>
</ToolTip>
</>
: <BtnDropdown
Label={'Open ' + props.Locations[0]?.Name + ' Drawings'}
Callback={() => handleClickDrawingsModal(props.Locations[0])}
TooltipContent={
<p>{CrossMark} {locationOptions[0]?.Disabled}</p>
}
ShowToolTip={locationOptions[0]?.ShowToolTip}
Disabled={locationOptions[0]?.Disabled}
BtnClass={'btn-primary'}
Options={locationOptions.slice(1)}
/>
}
<Modal
Show={showDrawingsModal}
Title={'Drawings for ' + selectedLocation?.Name}
ShowX={true} Size={'xlg'}
CallBack={() => setShowDrawingsModal(false)}
ShowCancel={false}
ShowConfirm={false}>
<div className="row">
<div className="col-12">
<LocationDrawingsTable
LocationID={selectedLocation?.ID}
RefreshDrawings={0}
/>
</div>
</div>
</Modal>
</>
);
};

export default LocationDrawingsButton;
Loading