-
-
@@ -304,7 +325,7 @@ function AssetConnectionWindow(props: { Name: string, ID: number, TypeID: number
e.stopPropagation();
if (hasPermissions()) deleteAssetConnection(item);
}}>
{TrashCan}
- > }
+ >}
>
@@ -356,7 +377,6 @@ function AssetConnectionWindow(props: { Name: string, ID: number, TypeID: number
}
-
);
}
diff --git a/Source/Applications/SystemCenter/wwwroot/Scripts/TSX/SystemCenter/CommonComponents/LocationDrawingsButton.tsx b/Source/Applications/SystemCenter/wwwroot/Scripts/TSX/SystemCenter/CommonComponents/LocationDrawingsButton.tsx
new file mode 100644
index 000000000..67b0463f0
--- /dev/null
+++ b/Source/Applications/SystemCenter/wwwroot/Scripts/TSX/SystemCenter/CommonComponents/LocationDrawingsButton.tsx
@@ -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
= (props) => {
+ const [hover, setHover] = React.useState<'none' | 'drawings'>('none');
+ const [pageState, setPageState] = React.useState<"loading" | "error" | "idle">("idle");
+ const [selectedLocation, setSelectedLocation] = React.useState();
+ const multipleLocations = React.useMemo(() => props.Locations.length > 1, [props.Locations]);
+ const [showDrawingsModal, setShowDrawingsModal] = React.useState(false);
+ const [locationOptions, setLocationOptions] = React.useState([]);
+
+ 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: {CrossMark} {error}
,
+ 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 (
+ <>
+
+
+ {!multipleLocations
+ ? <>
+ locationOptions[0]?.Disabled ? null : handleClickDrawingsModal(props.Locations[0])}
+ data-tooltip={"DrawingsModal"}
+ onMouseEnter={() => setHover('drawings')}
+ onMouseLeave={() => setHover('none')}
+ >Open {props.Locations[0]?.Name} Drawings
+
+ {locationOptions[0]?.ToolTipContent}
+
+ >
+ : handleClickDrawingsModal(props.Locations[0])}
+ TooltipContent={
+ {CrossMark} {locationOptions[0]?.Disabled}
+ }
+ ShowToolTip={locationOptions[0]?.ShowToolTip}
+ Disabled={locationOptions[0]?.Disabled}
+ BtnClass={'btn-primary'}
+ Options={locationOptions.slice(1)}
+ />
+ }
+ setShowDrawingsModal(false)}
+ ShowCancel={false}
+ ShowConfirm={false}>
+
+
+ >
+ );
+};
+
+export default LocationDrawingsButton;
\ No newline at end of file
diff --git a/Source/Applications/SystemCenter/wwwroot/Scripts/TSX/SystemCenter/Location/AddEditDrawingsModal.tsx b/Source/Applications/SystemCenter/wwwroot/Scripts/TSX/SystemCenter/Location/AddEditDrawingsModal.tsx
new file mode 100644
index 000000000..2889bd513
--- /dev/null
+++ b/Source/Applications/SystemCenter/wwwroot/Scripts/TSX/SystemCenter/Location/AddEditDrawingsModal.tsx
@@ -0,0 +1,121 @@
+//******************************************************************************************************
+// 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:
+// ----------------------------------------------------------------------------------------------------
+// 11/06/2024 - Collins Self
+// Generated original version of source code.
+//
+//******************************************************************************************************
+import { SystemCenter } from "@gpa-gemstone/application-typings";
+import { Input, Select } from "@gpa-gemstone/react-forms";
+import { Modal } from "@gpa-gemstone/react-interactive";
+import React from "react";
+
+interface IProps {
+ Record: SystemCenter.Types.LocationDrawing,
+ Setter: (record) => void,
+ HandleSave: () => void,
+ Show: boolean,
+ SetShow: (show: boolean) => void,
+}
+
+const AddEditDrawingsModal = (props: IProps) => {
+ const [category, setCategory] = React.useState>([]);
+
+ function valid(field: keyof (SystemCenter.Types.LocationDrawing)): boolean {
+ if (field == 'Name')
+ return props.Record.Name != null && props.Record.Name.length > 0 && props.Record.Name.length <= 200;
+ else if (field == 'Link')
+ return props.Record.Link != null && props.Record.Link.length > 0;
+ else if (field == 'Number')
+ return props.Record.Number == null || props.Record.Number.length <= 50;
+ return true;
+ }
+
+ function getValueList(listName: string, setter: (value: Array) => void): JQuery.jqXHR> {
+ let h = $.ajax({
+ type: "GET",
+ url: `${homePath}api/ValueList/Group/${listName}`,
+ contentType: "application/json; charset=utf-8",
+ dataType: `json`,
+ cache: false,
+ async: true
+ });
+ h.done((dCat: Array) => {
+ setter(dCat);
+
+ });
+ return h;
+ }
+
+ React.useEffect(() => {
+ const categoryHandle = getValueList("Category", setCategory);
+
+ return () => {
+ if (categoryHandle != null && categoryHandle.abort != null) categoryHandle.abort();
+ }
+ }, [])
+
+ return <>
+ {
+ props.SetShow(false);
+ if (conf) props.HandleSave();
+ }}
+ ShowCancel={false}
+ DisableConfirm={
+ !(valid('Name') &&
+ valid('Link') &&
+ valid('Number'))}
+ ConfirmText={'Save'}>
+
+ Record={props.Record}
+ Field={'Name'}
+ Feedback={'A Name of less than 200 characters is required.'}
+ Valid={valid}
+ Setter={(r) => props.Setter(r)} />
+
+ Record={props.Record}
+ Field={'Link'}
+ Feedback={'A Link is required.'}
+ Valid={valid}
+ Setter={(r) => props.Setter(r)} />
+
+ Record={props.Record}
+ Field={'Description'}
+ Valid={valid}
+ Setter={(r) => props.Setter(r)} />
+
+ Record={props.Record}
+ Field={'Category'}
+ Options={category.map(item => { return { Value: item.Value, Label: item.AltValue ?? item.Value } })}
+ Label={'Category'}
+ Setter={(r) => props.Setter(r)} />
+
+ Record={props.Record}
+ Field={'Number'}
+ Feedback={'Number must be less than 50 characters.'}
+ Valid={valid}
+ AllowNull={true}
+ Setter={(r) => props.Setter(r)} />
+
+ >
+}
+export default AddEditDrawingsModal;
\ No newline at end of file
diff --git a/Source/Applications/SystemCenter/wwwroot/Scripts/TSX/SystemCenter/Location/Location.tsx b/Source/Applications/SystemCenter/wwwroot/Scripts/TSX/SystemCenter/Location/Location.tsx
index ff98a1194..cfb1cd55f 100644
--- a/Source/Applications/SystemCenter/wwwroot/Scripts/TSX/SystemCenter/Location/Location.tsx
+++ b/Source/Applications/SystemCenter/wwwroot/Scripts/TSX/SystemCenter/Location/Location.tsx
@@ -133,7 +133,7 @@ function Location(props: IProps) {
{tab === 'meters' ? : null}
{tab === 'assets' ? : null}
{tab === 'images' ? : null}
- {tab === 'drawings' ? : null}
+ {tab === 'drawings' ? : null}
{ if (conf) deleteLocation(); setShowDelete(false); }} />
diff --git a/Source/Applications/SystemCenter/wwwroot/Scripts/TSX/SystemCenter/Location/LocationDrawings.tsx b/Source/Applications/SystemCenter/wwwroot/Scripts/TSX/SystemCenter/Location/LocationDrawings.tsx
index 6f2a0e8d7..fab37e2b0 100644
--- a/Source/Applications/SystemCenter/wwwroot/Scripts/TSX/SystemCenter/Location/LocationDrawings.tsx
+++ b/Source/Applications/SystemCenter/wwwroot/Scripts/TSX/SystemCenter/Location/LocationDrawings.tsx
@@ -22,90 +22,25 @@
// Modified original source code; Incorporated 'Number' and 'Category' fields into
// 'Substation Drawings' table.
//******************************************************************************************************
-
-
-
-
import * as React from 'react';
import * as _ from 'lodash';
-import { Application, OpenXDA, SystemCenter } from '@gpa-gemstone/application-typings';
-import { SystemCenter as SCGlobal } from '../global';
-import { Table, Column, Paging } from '@gpa-gemstone/react-table';
-import { useAppSelector, useAppDispatch } from '../hooks';
-import { Input, Select } from '@gpa-gemstone/react-forms';
-import { Pencil, TrashCan } from '@gpa-gemstone/gpa-symbols';
+import { SystemCenter } from '@gpa-gemstone/application-typings';
+import { GenericController, ToolTip } from '@gpa-gemstone/react-interactive';
+import LocationDrawingsTable from './LocationDrawingsTable';
+import { useAppSelector } from '../hooks';
import { SelectRoles } from '../Store/UserSettings';
-import { ToolTip, GenericController, LoadingScreen, ServerErrorIcon } from '@gpa-gemstone/react-interactive';
-import { current } from '@reduxjs/toolkit';
+import AddEditDrawingsModal from './AddEditDrawingsModal';
-const LocationDrawingsWindow = (props: { Location: OpenXDA.Types.Location }) => {
- const LocationDrawingController = new GenericController(`${homePath}api/LocationDrawing`, "Name", true);
- const PagingID = 'LocationDrawingPage'
-
- const [links, setLinks] = React.useState([]);
- const [sortKey, setSortKey] = React.useState('Name');
- const [ascending, setAscending] = React.useState(true);
+const LocationDrawingsWindow = (props: { LocationID: number }) => {
+ const roles = useAppSelector(SelectRoles);
const emptyRecord: SystemCenter.Types.LocationDrawing = { ID: 0, LocationID: 0, Name: '', Link: '', Description: '', Number: '', Category: '' };
const [record, setRecord] = React.useState(emptyRecord);
- const [category, setCategory] = React.useState>([]);
const [hover, setHover] = React.useState<('Update' | 'Reset' | 'None')>('None');
- const roles = useAppSelector(SelectRoles);
-
- const [page, setPage] = React.useState(0);
- const [pageInfo, setPageInfo] = React.useState<{ RecordsPerPage: number, NumberOfPages: number, TotalRecords: number }>({ RecordsPerPage: 0, NumberOfPages: 0, TotalRecords: 0 });
- const [pageState, setPageState] = React.useState<'error' | 'idle' | 'loading'>('idle');
-
- React.useEffect(() => {
- const storedInfo = JSON.parse(localStorage.getItem(PagingID) as string);
- if (storedInfo != null) setPage(storedInfo);
- }, []);
-
- React.useEffect(() => {
- localStorage.setItem(PagingID, JSON.stringify(page));
- }, [page]);
-
- React.useEffect(() => {
- const handle = fetchDrawings(sortKey, ascending, page, props.Location.ID);
- return () => { if (handle != null && handle?.abort != null) handle.abort(); }
- }, [sortKey, ascending, page, props.Location.ID]);
-
- const fetchDrawings = (sortKey: keyof SystemCenter.Types.LocationDrawing, ascending: boolean, page: number, locationID: number) => {
- setPageState('loading');
- const handle = LocationDrawingController.PagedSearch([], sortKey, ascending, page, locationID)
- .done((result) => {
- setLinks(JSON.parse(result.Data as unknown as string));
- if (result.NumberOfPages === 0) result.NumberOfPages = 1;
- setPageInfo(result);
- setPageState('idle');
- })
- .fail(() => setPageState('error'));
- return handle;
- }
-
- React.useEffect(() => {
- let categoryHandle = getValueList("Category", setCategory);
-
- return () => {
- if (categoryHandle != null && categoryHandle.abort != null) categoryHandle.abort();
- }
- }, [])
-
- function getValueList(listName: string, setter: (value: Array) => void): JQuery.jqXHR> {
- let h = $.ajax({
- type: "GET",
- url: `${homePath}api/ValueList/Group/${listName}`,
- contentType: "application/json; charset=utf-8",
- dataType: `json`,
- cache: false,
- async: true
- });
- h.done((dCat: Array) => {
- setter(dCat);
-
- });
- return h;
- }
+ const [showModal, setShowModal] = React.useState(false);
+ const [editMode, setEditMode] = React.useState(false);
+ const [updateTable, setUpdateTable] = React.useState(0);
+ const LocationDrawingController = new GenericController(`${homePath}api/LocationDrawing`, "Name", true);
function hasPermissions(): boolean {
if (roles.indexOf('Administrator') < 0 && roles.indexOf('Engineer') < 0)
@@ -113,39 +48,16 @@ const LocationDrawingsWindow = (props: { Location: OpenXDA.Types.Location }) =>
return true;
}
- function valid(field: keyof (SystemCenter.Types.LocationDrawing)): boolean {
- if (field == 'Name')
- return record.Name != null && record.Name.length > 0 && record.Name.length <= 200;
- else if (field == 'Link')
- return record.Link != null && record.Link.length > 0;
- else if (field == 'Number')
- return record.Number == null || record.Number.length <=50;
- return true;
- }
-
const handleSave = () => {
- setPageState('loading');
- LocationDrawingController.DBAction('PATCH', record)
- .then(() => {
- fetchDrawings(sortKey, ascending, page, props.Location.ID);
- })
- .catch(() => {
- setPageState('error');
- });
- };
-
- const handleDelete = (item: SystemCenter.Types.LocationDrawing) => {
- setPageState('loading');
- LocationDrawingController.DBAction('DELETE', item)
- .then(() => {
- fetchDrawings(sortKey, ascending, page, props.Location.ID);
- })
- .catch(() => {
- setPageState('error');
- });
+ setShowModal(false);
+ setRecord(record);
+ editMode ? LocationDrawingController.DBAction('PATCH', record)
+ .done(() => {setUpdateTable(updateTable + 1)})
+ : LocationDrawingController.DBAction('POST', record)
+ .done(() => {setUpdateTable(updateTable + 1)});
};
- return (
+ return (<>
@@ -155,128 +67,44 @@ const LocationDrawingsWindow = (props: { Location: OpenXDA.Types.Location }) =>
-
-
- TableClass="table table-hover"
- Data={links}
- SortKey={sortKey}
- Ascending={ascending}
- OnSort={(d) => {
- if (d.colKey === 'EditDelete')
- return;
- if (d.colKey == sortKey)
- setAscending(!ascending);
- else {
- setAscending(true);
- setSortKey(d.colKey as keyof SystemCenter.Types.LocationDrawing);
- }
- }}
- TableStyle={{ padding: 0, width: '100%', tableLayout: 'fixed', display: 'flex', flexDirection: 'column', overflow: 'hidden', flex: 1 }}
- TheadStyle={{ fontSize: 'smaller', display: 'table', tableLayout: 'fixed', width: '100%' }}
- TbodyStyle={{ display: 'block', width: '100%', overflowY: 'auto', flex: 1 }}
- RowStyle={{ fontSize: 'smaller', display: 'table', tableLayout: 'fixed', width: '100%' }}
- Selected={(item) => false}
- KeySelector={(item) => item.ID}
- >
-
- Key={'Name'}
- AllowSort={true}
- Field={'Name'}
- HeaderStyle={{ width: 'auto' }}
- RowStyle={{ width: 'auto' }}
- > Name
-
-
- Key={'Link'}
- AllowSort={true}
- Field={'Link'}
- HeaderStyle={{ width: 'auto' }}
- RowStyle={{ width: 'auto' }}
- Content={({ item, key }) => {item[key]} }
- > Link
-
-
- Key={'Description'}
- AllowSort={true}
- Field={'Description'}
- HeaderStyle={{ width: 'auto' }}
- RowStyle={{ width: 'auto' }}
- > Description
-
-
- Key={'Number'}
- AllowSort={true}
- Field={'Number'}
- HeaderStyle={{ width: 'auto' }}
- RowStyle={{ width: 'auto' }}
- > Number
-
-
- Key={'Category'}
- AllowSort={true}
- Field={'Category'}
- HeaderStyle={{ width: 'auto' }}
- RowStyle={{ width: 'auto' }}
- > Category
-
-
- Key={'EditDelete'}
- AllowSort={false}
- HeaderStyle={{ width: 'auto' }}
- RowStyle={{ width: 'auto' }}
- Content={({ item }) =>
-
- {setRecord(item) }}>{Pencil}
- { if (hasPermissions()) handleDelete(item); }}>{TrashCan}
-
- }
- >
-
-
-
-
-
-
+
{
+ setRecord(record);
+ setEditMode(true);
+ setShowModal(true);
+ }}
+ RefreshDrawings={updateTable}
+ />
- setHover('Update')} onMouseLeave={() => setHover('None')}
- onClick={() => { setRecord({ ...emptyRecord, LocationID: props.Location.ID }) }}>Add Drawing
+ setHover('Update')}
+ onMouseLeave={() => setHover('None')}
+ onClick={() => {
+ setRecord({ ...emptyRecord, LocationID: props.LocationID });
+ setEditMode(false);
+ setShowModal(true);
+ }}
+ >Add Drawing
+
-
+
+
Your role does not have permission. Please contact your Administrator if you believe this to be in error.
-
-
- );
-
+ >);
}
export default LocationDrawingsWindow;
\ No newline at end of file
diff --git a/Source/Applications/SystemCenter/wwwroot/Scripts/TSX/SystemCenter/Location/LocationDrawingsTable.tsx b/Source/Applications/SystemCenter/wwwroot/Scripts/TSX/SystemCenter/Location/LocationDrawingsTable.tsx
new file mode 100644
index 000000000..35c1a8223
--- /dev/null
+++ b/Source/Applications/SystemCenter/wwwroot/Scripts/TSX/SystemCenter/Location/LocationDrawingsTable.tsx
@@ -0,0 +1,188 @@
+//******************************************************************************************************
+// 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:
+// ----------------------------------------------------------------------------------------------------
+// 11/06/2024 - Collins Self
+// Generated original version of source code.
+//
+//******************************************************************************************************
+import { SystemCenter } from "@gpa-gemstone/application-typings";
+import { Pencil, TrashCan } from "@gpa-gemstone/gpa-symbols";
+import { GenericController, LoadingScreen, ServerErrorIcon } from "@gpa-gemstone/react-interactive";
+import { Column, Table, Paging } from "@gpa-gemstone/react-table";
+import React from "react";
+import { useAppSelector } from "../hooks";
+import { SelectRoles } from "../Store/UserSettings";
+
+interface IProps {
+ LocationID: number,
+ Edit?: (record: SystemCenter.Types.LocationDrawing) => void,
+ /**
+ * @param RefreshDrawings Counter that triggers a fetchDrawings function
+ */
+ RefreshDrawings: number
+}
+
+const LocationDrawingsTable = (props: IProps) => {
+ const [links, setLinks] = React.useState([]);
+ const [sortKey, setSortKey] = React.useState('Name');
+ const [ascending, setAscending] = React.useState(true);
+ const [pageInfo, setPageInfo] = React.useState<{ RecordsPerPage: number, NumberOfPages: number, TotalRecords: number }>({ RecordsPerPage: 0, NumberOfPages: 0, TotalRecords: 0 });
+ const [pageState, setPageState] = React.useState<'error' | 'idle' | 'loading'>('idle');
+ const [page, setPage] = React.useState(0);
+
+ const roles = useAppSelector(SelectRoles);
+ const LocationDrawingController = new GenericController(`${homePath}api/LocationDrawing`, "Name", true);
+ const PagingID = 'LocationDrawingPage'
+
+ function hasPermissions(): boolean {
+ if (roles.indexOf('Administrator') < 0 && roles.indexOf('Engineer') < 0)
+ return false;
+ return true;
+ }
+
+ const fetchDrawings = (sortKey: keyof SystemCenter.Types.LocationDrawing, ascending: boolean, page: number, locationID: number) => {
+ setPageState('loading');
+ const handle = LocationDrawingController.PagedSearch([], sortKey, ascending, page, locationID)
+ .done((result) => {
+ setLinks(JSON.parse(result.Data as unknown as string));
+ if (result.NumberOfPages === 0) result.NumberOfPages = 1;
+ setPageInfo({RecordsPerPage: result.RecordsPerPage,
+ NumberOfPages: result.NumberOfPages,
+ TotalRecords: result.TotalRecords});
+ setPageState('idle');
+ })
+ .fail(() => setPageState('error'));
+ return handle;
+ }
+
+ const handleDelete = (item: SystemCenter.Types.LocationDrawing) => {
+ setPageState('loading');
+ LocationDrawingController.DBAction('DELETE', item)
+ .then(() => fetchDrawings(sortKey, ascending, page, props.LocationID),
+ () => setPageState('error'))
+ };
+
+ React.useEffect(() => {
+ const storedInfo = JSON.parse(localStorage.getItem(PagingID) as string);
+ if (storedInfo != null) setPage(storedInfo);
+ }, []);
+
+ React.useEffect(() => {
+ localStorage.setItem(PagingID, JSON.stringify(page));
+ }, [page]);
+
+ React.useEffect(() => {
+ const handle = fetchDrawings(sortKey, ascending, page, props.LocationID);
+ return () => { if (handle != null && handle?.abort != null) handle.abort(); }
+ }, [sortKey, ascending, page, props.LocationID, props.RefreshDrawings]);
+
+ return <>
+
+
+ TableClass="table table-hover"
+ Data={links}
+ SortKey={sortKey}
+ Ascending={ascending}
+ OnSort={(d) => {
+ if (d.colKey === 'EditDelete')
+ return;
+ if (d.colKey == sortKey)
+ setAscending(!ascending);
+ else {
+ setAscending(true);
+ setSortKey(d.colKey as keyof SystemCenter.Types.LocationDrawing);
+ }
+ }}
+ TableStyle={{ padding: 0, width: '100%', tableLayout: 'fixed', display: 'flex', flexDirection: 'column', overflow: 'hidden', flex: 1 }}
+ TheadStyle={{ fontSize: 'smaller', display: 'table', tableLayout: 'fixed', width: '100%' }}
+ TbodyStyle={{ display: 'block', width: '100%', overflowY: 'auto', flex: 1 }}
+ RowStyle={{ fontSize: 'smaller', display: 'table', tableLayout: 'fixed', width: '100%' }}
+ Selected={(item) => false}
+ KeySelector={(item) => item.ID}
+ >
+
+ Key={'Name'}
+ AllowSort={true}
+ Field={'Name'}
+ HeaderStyle={{ width: '20%' }}
+ RowStyle={{ width: '20%' }}
+ > Name
+
+
+ Key={'Link'}
+ AllowSort={true}
+ Field={'Link'}
+ HeaderStyle={{ width: '20%' }}
+ RowStyle={{ width: '20%' }}
+ Content={({ item, key }) => {item[key]} }
+ > Link
+
+
+ Key={'Number'}
+ AllowSort={true}
+ Field={'Number'}
+ HeaderStyle={{ width: '10%' }}
+ RowStyle={{ width: '10%' }}
+ > Number
+
+
+ Key={'Category'}
+ AllowSort={true}
+ Field={'Category'}
+ HeaderStyle={{ width: '10%' }}
+ RowStyle={{ width: '10%' }}
+ > Category
+
+
+ Key={'Description'}
+ AllowSort={true}
+ Field={'Description'}
+ HeaderStyle={{ width: 'auto' }}
+ RowStyle={{ width: 'auto' }}
+ > Description
+
+ {props.Edit ?
+
+ Key={'EditDelete'}
+ AllowSort={false}
+ HeaderStyle={{ width: '10%' }}
+ RowStyle={{ width: '10%' }}
+ Content={({ item }) =>
+
+ { props.Edit(item); }}>{Pencil}
+ { if (hasPermissions()) handleDelete(item); }}>{TrashCan}
+
+ }
+ >
+
+ : null}
+
+
+
+
+
+ >
+}
+
+export default LocationDrawingsTable;
\ No newline at end of file
diff --git a/Source/Applications/SystemCenter/wwwroot/Scripts/TSX/SystemCenter/Meter/MeterLocation.tsx b/Source/Applications/SystemCenter/wwwroot/Scripts/TSX/SystemCenter/Meter/MeterLocation.tsx
index 6298b5456..f02e2b446 100644
--- a/Source/Applications/SystemCenter/wwwroot/Scripts/TSX/SystemCenter/Meter/MeterLocation.tsx
+++ b/Source/Applications/SystemCenter/wwwroot/Scripts/TSX/SystemCenter/Meter/MeterLocation.tsx
@@ -168,13 +168,17 @@ const LocationWindow = (props: IProps) => {