From 3788891ed0b514e261a0e0382ca29f6a30eb0ea9 Mon Sep 17 00:00:00 2001 From: natalie beatty Date: Mon, 10 Aug 2026 13:28:54 -0400 Subject: [PATCH 1/8] use controller in ByCellCarrier --- .../Scripts/TSX/CellCarrier/ByCellCarrier.tsx | 61 ++++++++++++------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/CellCarrier/ByCellCarrier.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/CellCarrier/ByCellCarrier.tsx index 039d8c115..1f36c2ac5 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/CellCarrier/ByCellCarrier.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/CellCarrier/ByCellCarrier.tsx @@ -22,16 +22,12 @@ //****************************************************************************************************** import * as React from 'react'; -import { LoadingScreen, Modal, Search, SearchBar } from '@gpa-gemstone/react-interactive' +import { LoadingScreen, Modal, Search, SearchBar, GenericController } from '@gpa-gemstone/react-interactive' import { ReactIcons } from '@gpa-gemstone/gpa-symbols'; import { Application } from '@gpa-gemstone/application-typings'; import { Table, Column, Paging } from '@gpa-gemstone/react-table'; -import moment from 'moment'; import { ICellCarrier } from '../global'; -import { CellCarrierSlice } from '../Store'; -import { useAppDispatch, useAppSelector } from '../hooks'; import { Input } from '@gpa-gemstone/react-forms'; -import { castArray } from 'lodash'; declare var homePath; @@ -43,37 +39,60 @@ const searchFields: Search.IField[] = [ { key: "Transform", label: "Transform", type: "string", isPivotField: false }, ]; +const cellCarrierController = new GenericController(`${homePath}api/OpenXDA/CellCarrier`, "Name", true); + interface IProps {} const ByCellCarrier = (props: IProps) => { - const dispatch = useAppDispatch(); - - const searchStatus: Application.Types.Status = useAppSelector(CellCarrierSlice.SearchStatus); - const status: Application.Types.Status = useAppSelector(CellCarrierSlice.Status); - const data: ICellCarrier[] = useAppSelector(CellCarrierSlice.SearchResults); - const allData: ICellCarrier[] = useAppSelector(CellCarrierSlice.Data); + const [searchStatus, setSearchStatus] = React.useState('uninitiated'); + const [fetchStatus, setFetchStatus] = React.useState('uninitiated'); + const [data, setData] = React.useState([]); + const [allData, setAllData] = React.useState([]); const [sortField, setSortField] = React.useState('Name'); const [ascending, setAscending] = React.useState(true); const [filters, setFilters] = React.useState[]>([]); const [showModal, setShowModal] = React.useState<'New' | 'Edit' | 'Hide'>('Hide'); const [carrier, setCarrier] = React.useState(emptyCarrier); const [page, setPage] = React.useState(0); - const totalPages = useAppSelector(CellCarrierSlice.TotalPages) - const recordsPerPage = useAppSelector(CellCarrierSlice.RecordsPerPage); - const totalRecords = useAppSelector(CellCarrierSlice.TotalRecords); + const [totalPages, setTotalPages] = React.useState(0); + const [recordsPerPage, setRecordsPerPage] = React.useState(0); + const [totalRecords, setTotalRecords] = React.useState(0); const [refreshTrigger, setRefreshTrigger] = React.useState(false); React.useEffect(() => { - dispatch(CellCarrierSlice.PagedSearch({ filter: filters, sortField, ascending: ascending, page: page })); + setSearchStatus('uninitiated') + const h = cellCarrierController.PagedSearch(filters, sortField, ascending, page); + h.done((d) => { + setData(JSON.parse(d.Data as unknown as string)); + setTotalPages(d.NumberOfPages); + setTotalRecords(d.TotalRecords); + setRecordsPerPage(d.RecordsPerPage); + setSearchStatus('idle'); + }) + h.fail(() => setSearchStatus('error')) + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } }, [page, filters, sortField, ascending, refreshTrigger]) React.useEffect(() => { - dispatch(CellCarrierSlice.Fetch()); - }, [refreshTrigger]) + setFetchStatus('uninitiated') + const h = cellCarrierController.Fetch(); + h.done((d) => { + setAllData(d) + setFetchStatus('idle') + }) + h.fail(() => setFetchStatus('error')) + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + }, [refreshTrigger, cellCarrierController.Fetch]) return (
- +
CollumnList={searchFields} @@ -161,11 +180,11 @@ const ByCellCarrier = (props: IProps) => { } ConfirmBtnClass={'btn-primary'} CallBack={(c, b) => { if (showModal == 'New' && c) - dispatch(CellCarrierSlice.DBAction({ verb: 'POST', record: carrier })).then(() => setRefreshTrigger((val) => !val)); + cellCarrierController.DBAction('POST', carrier).then(() => setRefreshTrigger((val) => !val)); if (showModal == 'Edit' && c) - dispatch(CellCarrierSlice.DBAction({ verb: 'PATCH', record: carrier })).then(() => setRefreshTrigger((val) => !val)); + cellCarrierController.DBAction('PATCH', carrier).then(() => setRefreshTrigger((val) => !val)); if (showModal == 'Edit' && b && !c) - dispatch(CellCarrierSlice.DBAction({ verb: 'DELETE', record: carrier })).then(() => setRefreshTrigger((val) => !val)); + cellCarrierController.DBAction('DELETE', carrier).then(() => setRefreshTrigger((val) => !val)); setShowModal('Hide'); }} From dfc45a1158e766c4874d80957605e133da64bc32 Mon Sep 17 00:00:00 2001 From: natalie beatty Date: Mon, 10 Aug 2026 13:34:43 -0400 Subject: [PATCH 2/8] use controller in EmailCategory components --- .../TSX/EmailCategory/ByEmailCategory.tsx | 51 ++++++++++++++----- .../TSX/EmailCategory/EmailCategoryForm.tsx | 25 ++++++--- .../TSX/EmailCategory/EmailCategoryPage.tsx | 46 ++++++++++++----- .../TSX/EmailCategory/EmailCategoryWindow.tsx | 29 ++++++++--- .../Scripts/TSX/EmailCategory/EmailList.tsx | 37 +++++++++----- 5 files changed, 133 insertions(+), 55 deletions(-) diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/EmailCategory/ByEmailCategory.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/EmailCategory/ByEmailCategory.tsx index 8aa58fdc0..d356df3d0 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/EmailCategory/ByEmailCategory.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/EmailCategory/ByEmailCategory.tsx @@ -21,46 +21,69 @@ // //****************************************************************************************************** -import { useAppDispatch, useAppSelector } from '../hooks'; import * as React from 'react'; -import { LoadingScreen, Modal, Search, SearchBar } from '@gpa-gemstone/react-interactive' +import { LoadingScreen, Modal, Search, SearchBar, GenericController } from '@gpa-gemstone/react-interactive' import { ReactIcons } from '@gpa-gemstone/gpa-symbols'; import { Application } from '@gpa-gemstone/application-typings'; import { EmailCategory } from '../global'; -import { EmailCategorySlice } from '../Store'; import { Table, Column, Paging } from '@gpa-gemstone/react-table'; import EmailCategoryForm from './EmailCategoryForm'; import { useNavigate } from 'react-router-dom'; import TestEmailButton from '../CommonComponents/TestEmailButton'; + interface IProps { } +const emailCategoryController = new GenericController(`${homePath}api/OpenXDA/EmailCategory`, "Name", true); + const ByEmailCategory = (props: IProps) => { const navigate = useNavigate(); - const dispatch = useAppDispatch(); const [search, setSearch] = React.useState[]>([]); - const status: Application.Types.Status = useAppSelector(EmailCategorySlice.Status); - const searchStatus: Application.Types.Status = useAppSelector(EmailCategorySlice.SearchStatus); - const data: EmailCategory[] = useAppSelector(EmailCategorySlice.SearchResults); - const allData: EmailCategory[] = useAppSelector(EmailCategorySlice.Data); + const [status, setStatus] = React.useState('uninitiated'); + const [searchStatus, setSearchStatus] = React.useState('uninitiated'); + const [data, setData] = React.useState([]); + const [allData, setAllData] = React.useState([]); const [showModal, setShowModal] = React.useState(false); const [errors, setErrors] = React.useState([]); const [newEmailCategory, setNewEmailCategory] = React.useState({ ID: -1, Name: '', SelfSubscribe: true }); const [sortField, setSortField] = React.useState('Name'); const [asc, setAsc] = React.useState(true); const [page, setPage] = React.useState(0); - const totalPages: number = useAppSelector(EmailCategorySlice.TotalPages); - const totalRecords: number = useAppSelector(EmailCategorySlice.TotalRecords); - const recordsPerPage: number = useAppSelector(EmailCategorySlice.RecordsPerPage); + const [totalPages, setTotalPages] = React.useState(0); + const [recordsPerPage, setRecordsPerPage] = React.useState(0); + const [totalRecords, setTotalRecords] = React.useState(0); const [refreshTrigger, setRefreshTrigger] = React.useState(false) React.useEffect(() => { - dispatch(EmailCategorySlice.Fetch()); + setStatus('uninitiated') + const h = emailCategoryController.Fetch(); + h.done((d) => { + setAllData(d) + setStatus('idle') + }) + h.fail(() => setStatus('error')) + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } }, [refreshTrigger]); React.useEffect(() => { - dispatch(EmailCategorySlice.PagedSearch({ filter: search, sortField, ascending: asc, page: page })); + setSearchStatus('uninitiated') + const h = emailCategoryController.PagedSearch(search, sortField, asc, page); + h.done((d) => { + setData(JSON.parse(d.Data as unknown as string)); + setTotalPages(d.NumberOfPages); + setTotalRecords(d.TotalRecords); + setRecordsPerPage(d.RecordsPerPage); + setSearchStatus('idle'); + }) + h.fail(() => setSearchStatus('error')) + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } }, [search, sortField, asc, page, refreshTrigger]) React.useEffect(() => { @@ -159,7 +182,7 @@ const ByEmailCategory = (props: IProps) => { Show={showModal} ShowX={true} Size={'lg'} ShowCancel={false} ConfirmText={'Add'} CallBack={(conf) => { if (conf) - dispatch(EmailCategorySlice.DBAction({ verb: "POST", record: newEmailCategory })).then(()=>setRefreshTrigger((val) => !val)); + emailCategoryController.DBAction("POST",newEmailCategory).then(()=>setRefreshTrigger((val) => !val)); setShowModal(false); }} DisableConfirm={errors.length > 0} diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/EmailCategory/EmailCategoryForm.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/EmailCategory/EmailCategoryForm.tsx index 32524c793..21830bbc2 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/EmailCategory/EmailCategoryForm.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/EmailCategory/EmailCategoryForm.tsx @@ -21,24 +21,33 @@ // //****************************************************************************************************** -import { useAppDispatch, useAppSelector } from '../hooks'; import * as React from 'react'; import { EmailCategory } from '../global'; -import { EmailCategorySlice } from '../Store'; import { CheckBox, Input } from '@gpa-gemstone/react-forms'; +import { GenericController } from '@gpa-gemstone/react-interactive'; +import { Application } from '@gpa-gemstone/application-typings'; interface IProps { record: EmailCategory, setRecord: (d: EmailCategory) => void } const EmailCategoryForm = (props: IProps) => { - const dispatch = useAppDispatch(); - const emailCategories = useAppSelector(EmailCategorySlice.Data); - const status = useAppSelector(EmailCategorySlice.Status); + const [emailCategories, setEmailCategories] = React.useState([]); + const [status, setStatus] = React.useState('uninitiated'); React.useEffect(() => { - if (status == 'uninitiated' || status == 'changed') - dispatch(EmailCategorySlice.Fetch()); - }, [status]); + setStatus('loading') + const h = new GenericController(`${homePath}api/OpenXDA/EmailCategory`, "Name", true).Fetch(); + h.done((d) => { + setEmailCategories(d) + setStatus('idle') + }); + h.fail(() => setStatus('error')); + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + }, []); return (
diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/EmailCategory/EmailCategoryPage.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/EmailCategory/EmailCategoryPage.tsx index ae2f3674e..6af4351d1 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/EmailCategory/EmailCategoryPage.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/EmailCategory/EmailCategoryPage.tsx @@ -21,12 +21,12 @@ // //****************************************************************************************************** -import { useAppDispatch, useAppSelector } from '../hooks'; +import * as $ from 'jquery' import * as React from 'react'; -import { LoadingScreen, TabSelector, Warning } from '@gpa-gemstone/react-interactive'; +import { LoadingScreen, TabSelector, Warning, GenericController } from '@gpa-gemstone/react-interactive'; import { Application } from '@gpa-gemstone/application-typings'; -import { EmailCategorySlice } from '../Store'; import EmailCategoryWindow from './EmailCategoryWindow'; +import { EmailCategory } from '../global'; import EmailList from './EmailList'; interface IProps { useParams: { id: string } } @@ -39,12 +39,10 @@ const Tabs = [ const EmailCategoryPage = (props: IProps) => { - const dispatch = useAppDispatch(); - const [showDelete, setShowDelete] = React.useState(false); - const category = useAppSelector((state) => EmailCategorySlice.Datum(state, parseInt(props.useParams.id))); - const status: Application.Types.Status = useAppSelector(EmailCategorySlice.Status); + const [category, setCategory] = React.useState(null); + const [status, setStatus] = React.useState('uninitiated'); const getTab = React.useCallback(() => { if (sessionStorage.hasOwnProperty('EmailCategory.Tab')) @@ -54,17 +52,35 @@ const EmailCategoryPage = (props: IProps) => { const [tab, setTab] = React.useState(getTab()); - React.useEffect(() => { - if (status == 'uninitiated' || status == 'changed') - dispatch(EmailCategorySlice.Fetch()); - }, [status]); - React.useEffect(() => { const saved = getTab(); if (saved !== tab) sessionStorage.setItem('EmailCategory.Tab', JSON.stringify(tab)); }, [tab]); + React.useEffect(() => { + setStatus('loading') + const h = $.ajax({ + type: "GET", + url: `${homePath}api/OpenXDA/EmailCategory/One/${props.useParams.id}`, + contentType: "application/json; charset=utf-8", + dataType: 'json', + cache: false, + async: true + }) + h.done((d) => { + setCategory(d); + setStatus('idle') + }) + h.fail(() => { + setStatus('error') + }) + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + }, [props.useParams.id]) + return (
@@ -104,8 +120,10 @@ const EmailCategoryPage = (props: IProps) => { Title={`Delete ${category?.Name ?? 'Email Category'}`} CallBack={(conf) => { if (conf) { - dispatch(EmailCategorySlice.DBAction({ verb: 'DELETE', record: category })); - window.location.href = `${homePath}/Categories`; + new GenericController(`${homePath}api/OpenXDA/EmailCategory`, "Name", true).DBAction('DELETE', category) + .then(() => { + window.location.href = `${homePath}/Categories`; + }) } setShowDelete(false); }} /> diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/EmailCategory/EmailCategoryWindow.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/EmailCategory/EmailCategoryWindow.tsx index 37d69c081..4fb0a9e0c 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/EmailCategory/EmailCategoryWindow.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/EmailCategory/EmailCategoryWindow.tsx @@ -21,29 +21,46 @@ // //****************************************************************************************************** -import { useAppDispatch, useAppSelector } from '../hooks'; import * as React from 'react'; -import { Warning } from '@gpa-gemstone/react-interactive'; +import { Warning, GenericController } from '@gpa-gemstone/react-interactive'; import { ToolTip } from '@gpa-gemstone/react-forms'; import { ReactIcons } from '@gpa-gemstone/gpa-symbols'; import { EmailCategory } from '../global'; -import { EmailCategorySlice } from '../Store'; import EmailCategoryForm from './EmailCategoryForm'; +import { Application } from '@gpa-gemstone/application-typings'; interface IProps { Category: EmailCategory } +const emailCategoryController = new GenericController(`${homePath}api/OpenXDA/EmailCategory`, "Name", true); + const EmailCategoryWindow = (props: IProps) => { - const dispatch = useAppDispatch(); const [category, setCategory] = React.useState(props.Category); const [hasChanged, setHasChanged] = React.useState(false); const [hover, setHover] = React.useState<('submit' | 'clear' | 'none')>('none'); - const categories = useAppSelector(EmailCategorySlice.Data); + const [categories, setCategories] = React.useState([]) + const [status, setStatus] = React.useState('uninitiated'); const [errors, setErrors] = React.useState([]); + React.useEffect(() => { + setStatus('loading') + const h = emailCategoryController.Fetch(); + h.done((d) => { + setCategories(d) + setStatus('idle') + }); + h.fail(() => setStatus('error')); + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + + }, []); + React.useEffect(() => { let e = []; if (category.Name == undefined || category.Name.length < 1) @@ -78,7 +95,7 @@ const EmailCategoryWindow = (props: IProps) => {
diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/EmailCategory/EmailList.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/EmailCategory/EmailList.tsx index e1273962b..95191eda6 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/EmailCategory/EmailList.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/EmailCategory/EmailList.tsx @@ -21,34 +21,45 @@ // //****************************************************************************************************** -import { useAppDispatch, useAppSelector } from '../hooks'; import * as React from 'react'; +import { Application } from '@gpa-gemstone/application-typings' +import { GenericController, Search } from '@gpa-gemstone/react-interactive' +import { Column, Paging, Table } from '@gpa-gemstone/react-table'; import { EmailType } from '../global'; -import { EmailTypeSlice } from '../Store'; -import { Table, Column, Paging } from '@gpa-gemstone/react-table'; -import { Search } from '@gpa-gemstone/react-interactive' interface IProps { CategoryID: number} const EmailList = (props: IProps) => { - const dispatch = useAppDispatch(); - const emails = useAppSelector(EmailTypeSlice.SearchResults); - const status = useAppSelector(EmailTypeSlice.SearchStatus); - - const parentID = useAppSelector(EmailTypeSlice.ParentID); + const [emails, setEmails] = React.useState([]); + const [status, setStatus] = React.useState('uninitiated'); const [sortField, setSortField] = React.useState('Name'); const [ascending, setAscending] = React.useState(false); const [page, setPage] = React.useState(0); - const totalPages = useAppSelector(EmailTypeSlice.TotalPages); + const [totalPages, setTotalPages] = React.useState(0); React.useEffect(() => { - const filters: Search.IFilter[] = [{ FieldName: "EmailCategoryID", Type: 'number', Operator: '=', SearchText: props.CategoryID.toString(), IsPivotColumn: false}] - dispatch(EmailTypeSlice.PagedSearch({ filter: filters, sortField: sortField, ascending: ascending, page: page })); - }, [parentID, props.CategoryID, sortField, ascending, page]) + setStatus('loading') + + const filters: Search.IFilter[] = [{ FieldName: "EmailCategoryID", Type: 'number', Operator: '=', SearchText: props.CategoryID.toString(), IsPivotColumn: false }]; + const h = new GenericController(`${homePath}api/OpenXDA/EmailType`, "Name", true).PagedSearch(filters, sortField, ascending, page); + + h.done((d) => { + setEmails(JSON.parse(d.Data)); + setTotalPages(d.NumberOfPages); + setStatus('idle') + }) + h.fail(() => setStatus('error')) + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + + }, [props.CategoryID, sortField, ascending, page]) return (
From 9aa09266b31581a4f04a31f5dd334399d9dadc08 Mon Sep 17 00:00:00 2001 From: natalie beatty Date: Mon, 10 Aug 2026 13:35:53 -0400 Subject: [PATCH 3/8] use controller in ByUserInformation --- .../TSX/UserInformation/ByUserInformation.tsx | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/UserInformation/ByUserInformation.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/UserInformation/ByUserInformation.tsx index 9da01348d..f1829a27b 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/UserInformation/ByUserInformation.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/UserInformation/ByUserInformation.tsx @@ -24,11 +24,13 @@ import * as React from 'react'; import { ReactIcons } from '@gpa-gemstone/gpa-symbols'; import { useAppDispatch, useAppSelector } from '../hooks'; -import { CellCarrierSlice, UserInfoSlice } from '../Store'; +import { UserInfoSlice } from '../Store'; import { IsInteger } from '@gpa-gemstone/helper-functions'; +import { Application } from '@gpa-gemstone/application-typings'; import EmailConfirm from '../Subscriptions/ConfirmEmail'; import { ICellCarrier } from '../global'; import { Select, Input, ToolTip } from '@gpa-gemstone/react-forms'; +import { GenericController } from '@gpa-gemstone/react-interactive'; declare var homePath; declare var version; @@ -44,19 +46,29 @@ const ByUserInformation = (props: IProps) => { const [hover, setHover] = React.useState<('submit' | 'clear' | 'none')>('none'); const [validPhone, setValidPhone] = React.useState(false); - - const carriers = useAppSelector(CellCarrierSlice.Data); - const carrierStatus = useAppSelector(CellCarrierSlice.Status); + + const [carriers, setCarriers] = React.useState([]); + const [carrierStatus, setCarrierStatus] = React.useState('uninitiated'); const userPhone = useAppSelector(UserInfoSlice.CellPhone); const userCarrier = useAppSelector(UserInfoSlice.CellCarrierID); const emailConfirmed = useAppSelector(UserInfoSlice.ConfirmedEmail); - - React.useEffect(() => { - if (carrierStatus == 'uninitiated' || carrierStatus == 'changed') - dispatch(CellCarrierSlice.Fetch()); - }, [carrierStatus]); + setCarrierStatus('loading'); + const h = new GenericController(`${homePath}api/OpenXDA/CellCarrier`, "Name", true).Fetch(); + h.done((d) => { + setCarrierStatus('idle'); + setCarriers(d); + }) + h.fail(() => { + setCarrierStatus('error'); + }) + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + }, []); React.useEffect(() => { if (userPhone != null && userPhone.length > 0) From 88062e7c9e52143d2d32b8a7803e5514d503c59a Mon Sep 17 00:00:00 2001 From: natalie beatty Date: Mon, 10 Aug 2026 13:50:27 -0400 Subject: [PATCH 4/8] use controller in Subscription components --- .../TSX/Subscriptions/AssetGroupSelection.tsx | 40 +++++---- .../TSX/Subscriptions/ConfirmCarrier.tsx | 25 ++++-- .../Event/AddAllSubscription.tsx | 15 ++-- .../Subscriptions/Event/ByAllSubscription.tsx | 81 ++++++++++++------- .../Subscriptions/Event/BySubscription.tsx | 43 ++++++---- .../TSX/Subscriptions/Event/EmailSelect.tsx | 30 ++++--- .../Event/NewEventSubscription.tsx | 40 +++++++-- .../TSX/Subscriptions/Event/Success.tsx | 57 ++++++++++--- .../Report/NewReportSubscription.tsx | 36 +++++++-- .../TSX/Subscriptions/Report/ReportSelect.tsx | 37 +++++---- .../TSX/Subscriptions/Report/Success.tsx | 60 +++++++++++--- .../Scripts/TSX/Subscriptions/UserSelect.tsx | 41 ++++++---- 12 files changed, 359 insertions(+), 146 deletions(-) diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/AssetGroupSelection.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/AssetGroupSelection.tsx index d99f25ccf..d80d83d26 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/AssetGroupSelection.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/AssetGroupSelection.tsx @@ -20,14 +20,13 @@ // Generated original version of source code. // //****************************************************************************************************** + +import * as $ from 'jquery'; import * as React from 'react'; -import { LoadingIcon, Search, Warning } from '@gpa-gemstone/react-interactive' import { Application, OpenXDA } from '@gpa-gemstone/application-typings'; -import { AssetGroupSlice } from '../Store'; -import { useAppDispatch, useAppSelector } from '../hooks'; -import * as $ from 'jquery'; -import { Table, Column, Paging } from '@gpa-gemstone/react-table'; import { Select } from '@gpa-gemstone/react-forms'; +import { GenericController, LoadingIcon, Search, Warning } from '@gpa-gemstone/react-interactive' +import { Column, Paging, Table } from '@gpa-gemstone/react-table'; declare var homePath; declare var version; @@ -49,19 +48,18 @@ interface IProps { } const AssetGroupSelection = (props: IProps) => { - const dispatch = useAppDispatch(); const [parentGroups, setParentGroups] = React.useState([]); const [parentGroupState, setParentGroupState] = React.useState('uninitiated'); const [selectedParent, setSelectedParent] = React.useState(otherParent); - const assetGrpStatus = useAppSelector(AssetGroupSlice.Status); - const assetGrps = useAppSelector(AssetGroupSlice.SearchResults); + const [assetGrpStatus, setAssetGrpStatus] = React.useState('uninitiated'); + const [assetGrps, setAssetGrps] = React.useState([]); const [asc, setAsc] = React.useState(true); const [sort, setSort] = React.useState('Name'); const [page, setPage] = React.useState(0); - const totalPages = useAppSelector(AssetGroupSlice.TotalPages); + const [totalPages, setTotalPages] = React.useState(0); const [showWarning, setShowWarning] = React.useState(false); @@ -70,13 +68,29 @@ const AssetGroupSelection = (props: IProps) => { return () => { if (handle != null && handle.abort != null) handle.abort(); } }, []) + // search for child asset groups. React.useEffect(() => { const flt: Search.IFilter = { FieldName: 'ID', IsPivotColumn: false, SearchText: `(SELECT ChildAssetGroupID FROM AssetGroupAssetGroup WHERE ParentAssetGroupID = ${selectedParent.ID})`, Type: 'query', Operator: 'IN' } if (selectedParent.ID == -1) { flt.SearchText = " (SELECT ChildAssetGroupID FROM AssetGroupAssetGroup X WHERE X.ParentAssetGroupID IN (SELECT ID FROM AssetGroup Y WHERE Y.DisplayEmail =1))"; flt.Operator = "NOT IN" } - dispatch(AssetGroupSlice.PagedSearch({ ascending: asc, sortField: sort, filter: [flt], page: page })); + + setAssetGrpStatus('loading'); + const h = new GenericController(`${homePath}api/OpenXDA/AssetGroup`, "Name", true).PagedSearch([flt], sort, asc, page); + + h.done((d) => { + setAssetGrps(JSON.parse(d.Data)); + setTotalPages(d.NumberOfPages); + setAssetGrpStatus('idle'); + }) + + h.fail(() => setAssetGrpStatus('error')); + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } }, [asc, sort, selectedParent, page]) React.useEffect(() => { @@ -94,12 +108,6 @@ const AssetGroupSelection = (props: IProps) => { localStorage.setItem("SystemCenter.Notifications.SelectedGroup", selectedParent.ID.toString()); }, [selectedParent]); - React.useEffect(() => { - if (assetGrpStatus == 'uninitiated' || assetGrpStatus == 'changed') { - dispatch(AssetGroupSlice.Fetch()); - } - }, [assetGrpStatus]); - function getParents() { setParentGroupState('loading'); return $.ajax({ diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/ConfirmCarrier.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/ConfirmCarrier.tsx index 6464c135a..6a18e7f59 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/ConfirmCarrier.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/ConfirmCarrier.tsx @@ -22,9 +22,11 @@ //****************************************************************************************************** import * as React from 'react'; -import { CellCarrierSlice, UserInfoSlice } from '../Store'; +import { UserInfoSlice } from '../Store'; import { useAppDispatch, useAppSelector } from '../hooks'; +import { Application } from '@gpa-gemstone/application-typings'; import { Select } from '@gpa-gemstone/react-forms'; +import { GenericController } from '@gpa-gemstone/react-interactive'; import { ICellCarrier } from '../global'; const emptyCarrier: ICellCarrier = { @@ -41,13 +43,24 @@ const ConfirmPhoneCarrier = (props: IProps) => { const [currentCarrier, setCurrentCarrier] = React.useState(emptyCarrier); const userCarrier = useAppSelector(UserInfoSlice.CellCarrierID); - const carriers = useAppSelector(CellCarrierSlice.Data); - const carrierStatus = useAppSelector(CellCarrierSlice.Status); + const [carriers, setCarriers] = React.useState([]); + const [carrierStatus, setCarrierStatus] = React.useState('uninitiated'); React.useEffect(() => { - if (carrierStatus == 'uninitiated' || carrierStatus == 'changed') - dispatch(CellCarrierSlice.Fetch()); - }, [carrierStatus]); + setCarrierStatus('loading'); + const h = new GenericController(`${homePath}api/OpenXDA/CellCarrier`, "Name", true).Fetch(); + h.done((d) => { + setCarrierStatus('idle'); + setCarriers(d); + }) + h.fail(() => { + setCarrierStatus('error'); + }) + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + }, []); React.useEffect(() => { if (userCarrier == null && carriers.length > 0) diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/AddAllSubscription.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/AddAllSubscription.tsx index eb861d816..eb3fe8a93 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/AddAllSubscription.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/AddAllSubscription.tsx @@ -22,13 +22,12 @@ //****************************************************************************************************** import * as React from 'react'; -import { Modal } from '@gpa-gemstone/react-interactive' import { ReactIcons } from '@gpa-gemstone/gpa-symbols'; -import EmailSelect from './EmailSelect'; +import { GenericController, Modal } from '@gpa-gemstone/react-interactive'; import AssetGroupSelection from '../AssetGroupSelection'; +import EmailSelect from './EmailSelect'; import UserSelect from '../UserSelect'; -import { ActiveSubscriptionSlice } from '../../Store'; -import { useAppDispatch } from '../../hooks'; +import { ActiveSubscription } from '../../global'; declare var homePath; declare var version; @@ -39,7 +38,6 @@ interface IProps { } const AddAllSubscription = (props: IProps) => { - const dispatch = useAppDispatch(); const [step, setStep] = React.useState<('Email'|'User')>('User'); const [error, setError] = React.useState([]); @@ -69,9 +67,10 @@ const AddAllSubscription = (props: IProps) => { }, [props.show]) function save() { + const activeSubscriptionController = new GenericController(`${homePath}api/ActiveSubscription`, 'LastSent'); assetGroupIDs.forEach((id) => { - dispatch(ActiveSubscriptionSlice.DBAction({ - verb: 'POST', record: { + activeSubscriptionController.DBAction( + 'POST', { ID: 0, UserAccountID: userAccountID, EmailTypeID: emailTypeID, @@ -88,7 +87,7 @@ const AddAllSubscription = (props: IProps) => { LastName: '', RequireApproval: false } - })); + ); }); props.OnClose(); } diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/ByAllSubscription.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/ByAllSubscription.tsx index 44d819fd6..f7f3e5e5d 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/ByAllSubscription.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/ByAllSubscription.tsx @@ -21,35 +21,33 @@ // //****************************************************************************************************** -import { useAppDispatch, useAppSelector } from '../../hooks'; +import * as $ from 'jquery'; +import moment from 'moment'; import * as React from 'react'; -import { LoadingScreen, SearchBar, Warning, Search } from '@gpa-gemstone/react-interactive' -import { ReactIcons } from '@gpa-gemstone/gpa-symbols'; import { Application } from '@gpa-gemstone/application-typings'; -import { Table, Column, Paging } from '@gpa-gemstone/react-table'; +import { ReactIcons } from '@gpa-gemstone/gpa-symbols'; import { ToolTip } from '@gpa-gemstone/react-forms'; -import { ActiveSubscriptionSlice } from '../../Store'; -import { ActiveSubscription } from '../../global'; -import moment from 'moment'; -import * as $ from 'jquery'; +import { GenericController, LoadingScreen, Search, SearchBar, Warning } from '@gpa-gemstone/react-interactive' +import { Column, Paging, Table } from '@gpa-gemstone/react-table'; import AddAllSubscription from './AddAllSubscription'; +import { ActiveSubscription } from '../../global'; declare var homePath; declare var version; interface IProps { } +const activeSubscriptionController = new GenericController(`${homePath}api/ActiveSubscription`, 'LastSent'); + const ByAllSubscription = (props: IProps) => { - const dispatch = useAppDispatch(); - const status: Application.Types.Status = useAppSelector(ActiveSubscriptionSlice.Status); - const data: ActiveSubscription[] = useAppSelector(ActiveSubscriptionSlice.SearchResults); - const allData: ActiveSubscription[] = useAppSelector(ActiveSubscriptionSlice.Data); - const parentID = useAppSelector(ActiveSubscriptionSlice.ParentID); + const [status, setStatus] = React.useState('uninitiated'); + const [data, setData] = React.useState([]); + const [allData, setAllData] = React.useState([]); const [sortField, setSortField] = React.useState('Category'); const [asc, setAsc] = React.useState(false); const [filters, setFilters] = React.useState[]>([]); - const searchStatus = useAppSelector(ActiveSubscriptionSlice.SearchStatus); + const [searchStatus, setSearchStatus] = React.useState('uninitiated'); const [showApproveWarning, setShowApproveWarning] = React.useState(false); const [showRemoveWarning, setShowRemoveWarning] = React.useState(false); const [showModal, setShowModal] = React.useState(false); @@ -57,15 +55,46 @@ const ByAllSubscription = (props: IProps) => { const [record, setRecord] = React.useState(); const [hover, setHover] = React.useState('none'); const [page, setPage] = React.useState(0); - const totalPages = useAppSelector(ActiveSubscriptionSlice.TotalPages); + const [totalPages, setTotalPages] = React.useState(0); + const [totalRecords, setTotalRecords] = React.useState(0); + const [recordsPerPage, setRecordsPerPage] = React.useState(0); const [refreshTrigger, setRefreshTrigger] = React.useState(false) React.useEffect(() => { - dispatch(ActiveSubscriptionSlice.Fetch()); - }, [parentID, refreshTrigger]); + setStatus('loading'); + const h = activeSubscriptionController.Fetch(); + + h.done((d) => { + setAllData(d); + setStatus('idle'); + }) + + h.fail(() => setStatus('error')) + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + }, [refreshTrigger]); React.useEffect(() => { - dispatch(ActiveSubscriptionSlice.PagedSearch({ filter: filters, sortField, ascending: asc, page: page })); + setSearchStatus('loading'); + const h = activeSubscriptionController.PagedSearch(filters, sortField, asc, page); + + h.done((d) => { + setData(JSON.parse(d.Data)); + setTotalPages(d.NumberOfPages); + setRecordsPerPage(d.RecordsPerPage); + setTotalRecords(d.TotalRecords); + setSearchStatus('idle'); + }) + + h.fail(() => setSearchStatus('error')) + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } }, [filters, sortField, asc, page, refreshTrigger]); React.useEffect(() => { @@ -80,9 +109,9 @@ const ByAllSubscription = (props: IProps) => { cache: false, async: true, }).then((d) => { - setRefreshTrigger((val) => !val) + setRefreshTrigger(val => !val) }, () => { - setRefreshTrigger((val) => !val) + setRefreshTrigger(val => !val) }); } @@ -94,16 +123,12 @@ const ByAllSubscription = (props: IProps) => { cache: false, async: true, }).then((d) => { - setRefreshTrigger((val) => !val) + setRefreshTrigger(val => !val) }, () => { - setRefreshTrigger((val) => !val) + setRefreshTrigger(val => !val) }); } - const removeSubscription = () => { - dispatch(ActiveSubscriptionSlice.DBAction({ verb: 'DELETE', record })).then(() => setRefreshTrigger((val) => !val)); - }; - return (
@@ -120,7 +145,7 @@ const ByAllSubscription = (props: IProps) => { { key: 'Approved', label: 'Approved', type: 'boolean', isPivotField: false }, ]} SetFilter={setFilters} Direction={'left'} defaultCollumn={{ key: 'EmailName', label: 'Notification', type: 'string', isPivotField: false }} Width={'50%'} Label={'Search'} - ShowLoading={searchStatus === 'loading'} ResultNote={searchStatus === 'error' ? 'Could not complete Search' : 'Found ' + data.length + ' Subscriptions'} + ShowLoading={searchStatus === 'loading'} ResultNote={searchStatus === 'error' ? 'Could not complete Search' : `Displaying Subscription(s) ${totalRecords > 0 ? recordsPerPage * page + 1 : 0}-${recordsPerPage * page + data.length} out of ${totalRecords}`} GetEnum={() => { return () => { } }} @@ -269,7 +294,7 @@ const ByAllSubscription = (props: IProps) => { { setShowApproveWarning(false); if (c) approveAll(); }} /> { setShowRemoveWarning(false); if (c) removeSubscription(); }} /> + CallBack={(c) => { setShowRemoveWarning(false); if (c) activeSubscriptionController.DBAction('DELETE', record).then(() => setRefreshTrigger(val => !val)); }} /> { setShowModal(false); setRefreshTrigger((val) => !val) }} show={showModal} /> Click to approve this subscription. diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/BySubscription.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/BySubscription.tsx index 51a93d3ba..9befb7137 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/BySubscription.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/BySubscription.tsx @@ -21,14 +21,13 @@ // //****************************************************************************************************** -import { useAppDispatch, useAppSelector } from '../../hooks'; import * as React from 'react'; -import { LoadingScreen, Warning, Search } from '@gpa-gemstone/react-interactive' -import { ReactIcons } from '@gpa-gemstone/gpa-symbols'; import { Application } from '@gpa-gemstone/application-typings'; +import { ReactIcons } from '@gpa-gemstone/gpa-symbols'; +import { GenericController, LoadingScreen, Search, Warning } from '@gpa-gemstone/react-interactive' import { Table, Column, Paging } from '@gpa-gemstone/react-table'; -import { ActiveSubscriptionSlice } from '../../Store'; import { ActiveSubscription } from '../../global'; +import { useAppSelector } from '../../hooks' import moment from 'moment'; import { UserInfoSlice } from '../../Store'; @@ -38,26 +37,40 @@ declare var version; interface IProps {} +const activeSubscriptionController = new GenericController(`${homePath}api/ActiveSubscription`, 'LastSent'); + const BySubscription = (props: IProps) => { - const dispatch = useAppDispatch(); const [showWarning, setShowWarning] = React.useState(false); const [subscription, setSubscription] = React.useState(null); - const status: Application.Types.Status = useAppSelector(ActiveSubscriptionSlice.SearchStatus); - const data: ActiveSubscription[] = useAppSelector(ActiveSubscriptionSlice.SearchResults); - const parentID = useAppSelector(ActiveSubscriptionSlice.ParentID); - const userID = useAppSelector(UserInfoSlice.UserAccountID) + const [status, setStatus] = React.useState('uninitiated'); + const [data, setData] = React.useState([]); + const userID = useAppSelector(UserInfoSlice.UserAccountID); const [sortField, setSortField] = React.useState('Category'); const [asc, setAsc] = React.useState(false); const [page, setPage] = React.useState(0); - const totalPages = useAppSelector(ActiveSubscriptionSlice.TotalPages); + const [totalPages, setTotalPages] = React.useState(0); const [refreshTrigger, setRefreshTrigger] = React.useState(false); - React.useEffect(() => { - const filters: Search.IFilter[] = [{FieldName: 'UserAccountID', SearchText: userID.toString(), IsPivotColumn: false, Operator: "=", Type:'string'}] - dispatch(ActiveSubscriptionSlice.PagedSearch({ filter: filters, sortField: sortField, ascending: asc, page: page })); - }, [parentID, userID, asc, sortField, page, refreshTrigger]) + const filters: Search.IFilter[] = [{ FieldName: 'UserAccountID', SearchText: userID.toString(), IsPivotColumn: false, Operator: "=", Type: 'string' }] + + const h = activeSubscriptionController.PagedSearch(filters, sortField, asc, page); + setStatus('loading'); + + h.done((d) => { + setStatus('idle'); + setData(JSON.parse(d.Data)); + setTotalPages(d.NumberOfPages); + }) + + h.fail(() => setStatus('error')); + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + }, [userID, asc, sortField, page, refreshTrigger]) return (
@@ -167,7 +180,7 @@ const BySubscription = (props: IProps) => { Message={'This will unsubscribe you from these notifications. You will no longer receive these notifications.'} CallBack={(c) => { if (c) - dispatch(ActiveSubscriptionSlice.DBAction({ record: subscription, verb: 'DELETE' })).then(() => setRefreshTrigger((val) => !val)); + activeSubscriptionController.DBAction('DELETE', subscription).then(() => setRefreshTrigger((val) => !val)); setShowWarning(false); }} /> diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/EmailSelect.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/EmailSelect.tsx index 348160065..fe4cbcc45 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/EmailSelect.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/EmailSelect.tsx @@ -21,14 +21,12 @@ // //****************************************************************************************************** -import { Select } from '@gpa-gemstone/react-forms'; -import { GenericController, LoadingIcon } from '@gpa-gemstone/react-interactive'; import * as $ from 'jquery'; import * as React from 'react'; -import { EmailTypeSlice } from '../../Store'; -import { EmailCategory, EmailType } from '../../global'; -import { useAppDispatch, useAppSelector } from '../../hooks'; import { Application } from '@gpa-gemstone/application-typings'; +import { Select } from '@gpa-gemstone/react-forms'; +import { GenericController, LoadingIcon } from '@gpa-gemstone/react-interactive'; +import { EmailCategory, EmailType } from '../../global'; const emptyEmailType: EmailType = { @@ -54,15 +52,13 @@ interface IProps { } const EmailSelect = (props: IProps) => { - const dispatch = useAppDispatch(); const [emailCategories, setEmailCategories] = React.useState([]); const [emailCategoryStatus, setEmailCategoryStatus] = React.useState('uninitiated'); const [selectedCategory, setSelectedCategory] = React.useState(emptyCategory); - const emailTypeStatus = useAppSelector(EmailTypeSlice.Status); - const emailTypes = useAppSelector(EmailTypeSlice.Data); - const emailTypeParentID = useAppSelector(EmailTypeSlice.ParentID); + const [emailTypeStatus, setEmailTypeStatus] = React.useState('uninitiated'); + const [emailTypes, setEmailTypes] = React.useState([]); const [selectedEmailType, setSelectedEmailType] = React.useState(emptyEmailType); @@ -95,9 +91,19 @@ const EmailSelect = (props: IProps) => { }, [emailCategories]); React.useEffect(() => { - if (selectedCategory.ID != emailTypeParentID || emailTypeStatus == 'uninitiated' || emailTypeStatus == 'changed') - dispatch(EmailTypeSlice.Fetch(selectedCategory.ID)); - }, [selectedCategory, emailTypeParentID, emailTypeStatus]) + setEmailTypeStatus('loading'); + const h = new GenericController(`${homePath}api/OpenXDA/EmailType`, "Name", true).Fetch(selectedCategory.ID); + h.done((d) => { + setEmailTypes(d); + setEmailTypeStatus('idle'); + }); + h.fail(() => setEmailTypeStatus('error')); + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + }, [selectedCategory.ID]) React.useEffect(() => { if (emailTypes.filter(e => e.ShowSubscription).length > 0) diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/NewEventSubscription.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/NewEventSubscription.tsx index 6c97514b6..3f1222ccc 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/NewEventSubscription.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/NewEventSubscription.tsx @@ -21,18 +21,21 @@ // //****************************************************************************************************** +import * as $ from 'jquery'; import * as React from 'react'; +import { Application } from '@gpa-gemstone/application-typings'; import { ProgressBar } from '@gpa-gemstone/react-interactive'; import { ToolTip } from '@gpa-gemstone/react-forms'; import { ReactIcons } from '@gpa-gemstone/gpa-symbols'; -import EmailSelect from './EmailSelect'; +import { useAppSelector } from '../../hooks'; +import { EmailType } from '../../global'; +import { UserInfoSlice } from '../../Store'; import AssetGroupSelection from '../AssetGroupSelection'; +import ConfirmPhoneCarrier from '../ConfirmCarrier'; import ConfirmEmail from '../ConfirmEmail'; import ConfirmPhone from '../ConfirmPhone'; -import { EmailTypeSlice, UserInfoSlice } from '../../Store'; -import { useAppSelector } from '../../hooks'; +import EmailSelect from './EmailSelect'; import Success from './Success'; -import ConfirmPhoneCarrier from '../ConfirmCarrier'; declare var homePath; declare var version; @@ -47,10 +50,35 @@ const NewEventSubscription = (props: {}) => { const [emailTypeID, setEmailTypeID] = React.useState(-1); const [assetGroupID, setAssetGroupID] = React.useState([]); - const isText = useAppSelector((state) => (EmailTypeSlice.Datum(state, emailTypeID) == null ? false : EmailTypeSlice.Datum(state, emailTypeID).SMS)); + const [isText, setIsText] = React.useState(false); + const [emailTypeStatus, setEmailTypeStatus] = React.useState('uninitiated'); const carrierID = useAppSelector(UserInfoSlice.CellCarrierID); + React.useEffect(() => { + if (emailTypeID < 0) return + setEmailTypeStatus('loading') + const h = $.ajax({ + type: "GET", + url: `${homePath}api/OpenXDA/EmailType/One/${emailTypeID}`, + contentType: "application/json; charset=utf-8", + dataType: 'json', + cache: false, + async: true + }) + h.done((d) => { + setIsText(d.SMS); + setEmailTypeStatus('idle'); + }); + h.fail(() => setEmailTypeStatus('error')); + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + + }, [emailTypeID]) + const PhoneSteps = [ { short: 'Notification', long: 'Select Notification', id: 'Selection' }, { short: 'Phone Provider', long: 'Confirm Phone Provider', id: 'Carrier' }, @@ -118,14 +146,12 @@ const NewEventSubscription = (props: {}) => { setCurrentStep('Selection'); } - function disableNext(): boolean { if (currentStep == 'Selection' || currentStep == 'Carrier') return error.length > 0 return true; } - return (
diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/Success.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/Success.tsx index 4418bbe43..6241bd20f 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/Success.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Event/Success.tsx @@ -21,9 +21,13 @@ // //****************************************************************************************************** +import * as $ from 'jquery'; import * as React from 'react'; -import { ActiveSubscriptionSlice, AssetGroupSlice, EmailTypeSlice, UserInfoSlice } from '../../Store'; -import { useAppDispatch, useAppSelector } from '../../hooks'; +import { Application, OpenXDA } from '@gpa-gemstone/application-typings'; +import { GenericController, Search } from '@gpa-gemstone/react-interactive'; +import { EmailType, ActiveSubscription } from '../../global'; +import { useAppSelector } from '../../hooks'; +import { UserInfoSlice } from '../../Store'; declare var homePath; declare var version; @@ -34,15 +38,31 @@ interface IProps { } const Success = (props: IProps) => { - const dispatch = useAppDispatch(); - const email = useAppSelector((state) => EmailTypeSlice.Datum(state, props.emailTypeID)); - const assetGrp = useAppSelector((state) => AssetGroupSlice.Data(state).filter(ag => props.assetGroupID.includes(ag.ID))); + const [email, setEmail] = React.useState(null); + const [assetGrps, setAssetGrps] = React.useState([]); const userID = useAppSelector(UserInfoSlice.UserAccountID); React.useEffect(() => { + const filters: Search.IFilter[] = [{ SearchText: props.assetGroupID.join(','), IsPivotColumn: false, FieldName: 'ID', Operator: 'IN', Type: 'string' }]; + + const h = new GenericController(`${homePath}api/OpenXDA/AssetGroup`, "Name", true).DBSearch(filters); + + h.done((d) => { + setAssetGrps(d); + }) + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + + }, [props.assetGroupID]) + + React.useEffect(() => { + const activeSubscriptionController = new GenericController(`${homePath}api/ActiveSubscription`, 'LastSent'); + props.assetGroupID.forEach((id) => { - dispatch(ActiveSubscriptionSlice.DBAction({ - verb: 'POST', record: { + activeSubscriptionController.DBAction( + 'POST', { ID: 0, UserAccountID: userID, EmailTypeID: props.emailTypeID, @@ -59,16 +79,35 @@ const Success = (props: IProps) => { LastName: '', RequireApproval: false } - })); + ); }); }, [props.assetGroupID, props.emailTypeID]) + React.useEffect(() => { + const h = $.ajax({ + type: "GET", + url: `${homePath}api/OpenXDA/EmailType/One/${props.emailTypeID}`, + contentType: "application/json; charset=utf-8", + dataType: 'json', + cache: false, + async: true + }) + h.done((d) => { + setEmail(d); + }); + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + }, [props.emailTypeID]) + return (
You have successfully subscribed to {email == null ? '' : email.Name + ' '} - for {assetGrp.length > 1 ? (assetGrp.length + " Asset groups") : (assetGrp[0]?.Name ?? null)}. + for {assetGrps.length > 1 ? (assetGrps.length + " Asset groups") : (assetGrps[0]?.Name ?? null)}. If approval is required, an Administrator will need to approve the subscription before you receive notifications.
); diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Report/NewReportSubscription.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Report/NewReportSubscription.tsx index 5569d0110..ccc54d02b 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Report/NewReportSubscription.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Report/NewReportSubscription.tsx @@ -21,18 +21,19 @@ // //****************************************************************************************************** +import * as $ from 'jquery'; import * as React from 'react'; -import { ProgressBar } from '@gpa-gemstone/react-interactive'; -import { ToolTip } from '@gpa-gemstone/react-forms'; import { ReactIcons } from '@gpa-gemstone/gpa-symbols'; -import ReportSelect from './ReportSelect'; +import { ToolTip } from '@gpa-gemstone/react-forms'; +import { ProgressBar } from '@gpa-gemstone/react-interactive'; +import { useAppSelector } from '../../hooks'; +import { UserInfoSlice } from '../../Store'; import AssetGroupSelection from '../AssetGroupSelection'; +import ConfirmPhoneCarrier from '../ConfirmCarrier'; import ConfirmEmail from '../ConfirmEmail'; import ConfirmPhone from '../ConfirmPhone'; -import { EmailTypeSlice, UserInfoSlice } from '../../Store'; -import { useAppSelector } from '../../hooks'; +import ReportSelect from './ReportSelect'; import Success from './Success'; -import ConfirmPhoneCarrier from '../ConfirmCarrier'; declare var homePath; declare var version; @@ -47,10 +48,31 @@ const NewReportSubscription = (props: {}) => { const [emailTypeID, setEmailTypeID] = React.useState(-1); const [assetGroupID, setAssetGroupID] = React.useState([]); - const isText = useAppSelector((state) => (EmailTypeSlice.Datum(state, emailTypeID) == null ? false : EmailTypeSlice.Datum(state, emailTypeID).SMS)); + const [isText, setIsText] = React.useState(false); const carrierID = useAppSelector(UserInfoSlice.CellCarrierID); + React.useEffect(() => { + if (emailTypeID < 0) return + const h = $.ajax({ + type: "GET", + url: `${homePath}api/OpenXDA/EmailType/One/${emailTypeID}`, + contentType: "application/json; charset=utf-8", + dataType: 'json', + cache: false, + async: true + }) + h.done((d) => { + setIsText(d.SMS); + }); + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + + }, [emailTypeID]) + const PhoneSteps = [ { short: 'Notification', long: 'Select Notification', id: 'Selection' }, { short: 'Phone Provider', long: 'Confirm Phone Provider', id: 'Carrier' }, diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Report/ReportSelect.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Report/ReportSelect.tsx index a71025334..6723b38ee 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Report/ReportSelect.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Report/ReportSelect.tsx @@ -22,12 +22,10 @@ //****************************************************************************************************** import { Select } from '@gpa-gemstone/react-forms'; -import { LoadingIcon } from '@gpa-gemstone/react-interactive'; +import { LoadingIcon, GenericController } from '@gpa-gemstone/react-interactive'; import * as $ from 'jquery'; import * as React from 'react'; import { EmailCategory, ScheduledEmailType } from '../../global'; -import { useAppDispatch, useAppSelector } from '../../hooks'; -import { ScheduledEmailTypeSlice } from '../../Store'; import { Application } from '@gpa-gemstone/application-typings'; const emptyReport: ScheduledEmailType = { @@ -51,15 +49,13 @@ interface IProps { } const ReportSelect = (props: IProps) => { - const dispatch = useAppDispatch(); const [emailCategories, setEmailCategories] = React.useState([]); const [emailCategoryStatus, setEmailCategoryStatus] = React.useState('uninitiated'); const [selectedCategory, setSelectedCategory] = React.useState(emptyCategory); - const reportTypeStatus = useAppSelector(ScheduledEmailTypeSlice.Status); - const reportTypes = useAppSelector(ScheduledEmailTypeSlice.Data); - const reportTypeParentID = useAppSelector(ScheduledEmailTypeSlice.ParentID); + const [scheduledEmailStatus, setScheduledEmailStatus] = React.useState('uninitiated'); + const [scheduledEmails, setScheduledEmails] = React.useState([]); const [selectedReport, setSelectedReport] = React.useState(emptyReport); @@ -91,16 +87,27 @@ const ReportSelect = (props: IProps) => { }, [emailCategories]); React.useEffect(() => { - if (selectedCategory.ID !== reportTypeParentID || reportTypeStatus == 'uninitiated' || reportTypeStatus == 'changed') - dispatch(ScheduledEmailTypeSlice.Fetch(selectedCategory.ID)); - }, [selectedCategory, reportTypeParentID, reportTypeStatus]) + setScheduledEmailStatus('loading') + const h = new GenericController(`${homePath}api/OpenXDA/ScheduledEmailType`, "Name", true).Fetch(selectedCategory.ID); + h.done((d) => { + setScheduledEmails(d) + setScheduledEmailStatus('idle') + }); + h.fail(() => setScheduledEmailStatus('error')); + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + + }, [selectedCategory]); React.useEffect(() => { - if (reportTypes.length > 0) - props.SetScheduledEmailTypeID(reportTypes[0].ID) + if (scheduledEmails.length > 0) + props.SetScheduledEmailTypeID(scheduledEmails[0].ID) else props.SetScheduledEmailTypeID(-1); - }, [reportTypes]) + }, [scheduledEmails]) React.useEffect(() => { if (selectedCategory.ID != -1) @@ -113,7 +120,7 @@ const ReportSelect = (props: IProps) => { return (<> - +
@@ -126,7 +133,7 @@ const ReportSelect = (props: IProps) => {
Record={selectedReport} Field={'ID'} Label='Notification Template' Setter={(record) => setSelectedReport({ ...record, ID: typeof record.ID == 'string' ? parseInt(record.ID) : record.ID })} - Options={reportTypes + Options={scheduledEmails .filter(e => e.ShowSubscription) .map((e) => ({ Label: e.Name, Value: e.ID.toString() })) } /> diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Report/Success.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Report/Success.tsx index d311f3010..5c1a628c7 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Report/Success.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/Report/Success.tsx @@ -21,9 +21,13 @@ // //****************************************************************************************************** +import * as $ from 'jquery'; import * as React from 'react'; -import { ActiveReportSubscriptionSlice, AssetGroupSlice, ScheduledEmailTypeSlice, UserInfoSlice } from '../../Store'; -import { useAppDispatch, useAppSelector } from '../../hooks'; +import { Application, OpenXDA } from '@gpa-gemstone/application-typings' +import { GenericController, Search } from '@gpa-gemstone/react-interactive'; +import { ActiveReportSubscription, ScheduledEmailType } from '../../global'; +import { useAppSelector } from '../../hooks'; +import { UserInfoSlice } from '../../Store'; declare var homePath; declare var version; @@ -34,15 +38,32 @@ interface IProps { } const Success = (props: IProps) => { - const dispatch = useAppDispatch(); - const email = useAppSelector((state) => ScheduledEmailTypeSlice.Datum(state, props.emailTypeID)); - const assetGrp = useAppSelector((state) => AssetGroupSlice.Data(state).filter(ag => props.assetGroupID.includes(ag.ID))); + + const [email, setEmail] = React.useState(null); + const [scheduledEmailStatus, setScheduledEmailStatus] = React.useState('uninitiated'); + const [assetGrps, setAssetGrps] = React.useState([]); const userID = useAppSelector(UserInfoSlice.UserAccountID); React.useEffect(() => { + const filters: Search.IFilter[] = [{ SearchText: props.assetGroupID.join(','), IsPivotColumn: false, FieldName: 'ID', Operator: 'IN', Type: 'string' }]; + + const h = new GenericController(`${homePath}api/OpenXDA/AssetGroup`, "Name", true).DBSearch(filters); + + h.done((d) => { + setAssetGrps(d); + }) + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + + }, [props.assetGroupID]) + + React.useEffect(() => { + const activeReportSubscriptionController = new GenericController(`${homePath}api/ActiveScheduleSubscription`, 'Email'); props.assetGroupID.forEach((id) => { - dispatch(ActiveReportSubscriptionSlice.DBAction({ - verb: 'POST', record: { + activeReportSubscriptionController.DBAction( + 'POST',{ ID: 0, UserAccountID: userID, ScheduledEmailTypeID: props.emailTypeID, @@ -53,18 +74,39 @@ const Success = (props: IProps) => { EmailName: '', UserName: '' } - })); + ); }); }, [props.assetGroupID, props.emailTypeID]) + React.useEffect(() => { + setScheduledEmailStatus('loading') + const h = $.ajax({ + type: "GET", + url: `${homePath}api/OpenXDA/ScheduledEmailType/One/${props.emailTypeID}`, + contentType: "application/json; charset=utf-8", + dataType: 'json', + cache: false, + async: true + }) + h.done((d) => { + setEmail(d) + setScheduledEmailStatus('idle') + }); + h.fail(() => setScheduledEmailStatus('error')); + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + }, [props.emailTypeID]) return (
You have successfully subscribed to {email == null ? '' : email.Name + ' '} - for {assetGrp.length > 1 ? (assetGrp.length + " Asset groups") : (assetGrp[0]?.Name ?? null)}. + for {assetGrps.length > 1 ? (assetGrps.length + " Asset groups") : (assetGrps[0]?.Name ?? null)}. If approval is required an Administrator will need to approve the subscription before you receive the next scheduled report.
diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/UserSelect.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/UserSelect.tsx index f85a58376..1d2838cea 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/UserSelect.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/Subscriptions/UserSelect.tsx @@ -21,12 +21,10 @@ // //****************************************************************************************************** -import { useAppDispatch, useAppSelector } from '../hooks'; import * as React from 'react'; -import { UserAccountSlice } from '../Store'; -import { Table, Column, Paging } from '@gpa-gemstone/react-table'; -import { SearchBar, Search } from '@gpa-gemstone/react-interactive' import { Application } from '@gpa-gemstone/application-typings' +import { SearchBar, Search, GenericController } from '@gpa-gemstone/react-interactive' +import { Table, Column, Paging } from '@gpa-gemstone/react-table'; declare var homePath; declare var version; @@ -43,27 +41,42 @@ const defaultSearchcols: Search.IField[] = [ ]; const UserSelect = (props: IProps) => { - const dispatch = useAppDispatch(); - const data = useAppSelector(UserAccountSlice.SearchResults); - const searchStatus = useAppSelector(UserAccountSlice.SearchStatus); + const [data, setData] = React.useState([]); + const [searchStatus, setSearchStatus] = React.useState('uninitiated'); const [sortField, setSortField] = React.useState('Name'); const [ascending, setAscending] = React.useState(true); + const [filters, setFilters] = React.useState[]>([]); const [page, setPage] = React.useState(0); - const totalPages = useAppSelector(UserAccountSlice.TotalPages); - const totalRecords = useAppSelector(UserAccountSlice.TotalRecords); - const recordsPerPage = useAppSelector(UserAccountSlice.RecordsPerPage); + const [totalPages, setTotalPages] = React.useState(0); + const [totalRecords, setTotalRecords] = React.useState(0); + const [recordsPerPage, setRecordsPerPage] = React.useState(0); React.useEffect(() => { - const filters = undefined // setting filter to undefined uses the filter set on the slice by default search. - dispatch(UserAccountSlice.PagedSearch({filter: filters, sortField: sortField, ascending: ascending, page: page})) - }, [sortField, ascending, page]) + setSearchStatus('loading'); + + const h = new GenericController(`${homePath}api/OpenXDA/UserAccount`, "Name", true).PagedSearch(filters, sortField, ascending, page); + + h.done((d) => { + setSearchStatus('idle'); + setData(JSON.parse(d.Data)); + setTotalPages(d.NumberOfPages); + setTotalRecords(d.TotalRecords); + setRecordsPerPage(d.RecordsPerPage); + }) + h.fail(() => setSearchStatus('error')) + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + }, [sortField, ascending, page, filters]) return (
CollumnList={defaultSearchcols} - SetFilter={(flds) => dispatch(UserAccountSlice.PagedSearch({ filter: flds }))} + SetFilter={setFilters} Direction={'left'} defaultCollumn={{ key: 'Name', label: 'Name', type: 'string', isPivotField: false }} Width={'50%'} Label={'Search'} ShowLoading={searchStatus === 'loading'} ResultNote={searchStatus === 'error' ? 'Could not complete Search' : `Displaying User(s) ${totalRecords > 0 ? recordsPerPage * page + 1 : 0}-${recordsPerPage * page + data.length} out of ${totalRecords}`} GetEnum={() => { From d09e164e10e584ac5d223810ce89171077668c63 Mon Sep 17 00:00:00 2001 From: natalie beatty Date: Mon, 10 Aug 2026 14:05:20 -0400 Subject: [PATCH 5/8] use controller in Report components --- .../Scripts/TSX/Report/ByReport.tsx | 84 +++++++++++++------ .../Report/DatasourceUI/DataSourceModal.tsx | 60 ++++++++----- .../Report/DatasourceUI/DataSourceWindow.tsx | 45 ++++++---- .../Scripts/TSX/Report/GeneralInfo.tsx | 33 ++++++-- .../Scripts/TSX/Report/ReportForm.tsx | 53 ++++++++---- .../Scripts/TSX/Report/ReportPage.tsx | 67 +++++++++------ .../Scripts/TSX/Report/Subscriptions.tsx | 42 ++++++---- .../Scripts/TSX/Report/Template.tsx | 6 +- .../Scripts/TSX/Report/TriggerWindow.tsx | 6 +- 9 files changed, 261 insertions(+), 135 deletions(-) diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/ByReport.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/ByReport.tsx index 26a6f4f19..f91829296 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/ByReport.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/ByReport.tsx @@ -21,13 +21,11 @@ // //****************************************************************************************************** -import { useAppDispatch, useAppSelector } from '../hooks'; import * as React from 'react'; -import { LoadingScreen, Modal, Search, SearchBar } from '@gpa-gemstone/react-interactive' +import { LoadingScreen, Modal, Search, SearchBar, GenericController } from '@gpa-gemstone/react-interactive' import { ReactIcons } from '@gpa-gemstone/gpa-symbols'; import { Application } from '@gpa-gemstone/application-typings'; -import { ScheduledEmailType } from '../global'; -import { EmailCategorySlice, ScheduledEmailTypeSlice } from '../Store'; +import { ScheduledEmailType, EmailCategory } from '../global'; import { Table, Column, Paging } from '@gpa-gemstone/react-table'; import { IsCron } from '@gpa-gemstone/helper-functions'; import ReportForm from './ReportForm'; @@ -47,20 +45,19 @@ const emptyEmail: ScheduledEmailType = { FilePath: '' }; -interface IProps {} +interface IProps { } + +const scheduledEmailTypeController = new GenericController(`${homePath}api/OpenXDA/ScheduledEmailType`, "Name", true); const ByReport = (props: IProps) => { - const dispatch = useAppDispatch(); const navigate = useNavigate(); - - const search: Search.IFilter[] = useAppSelector(ScheduledEmailTypeSlice.SearchFilters); - const status: Application.Types.Status = useAppSelector(ScheduledEmailTypeSlice.Status); - const searchStatus: Application.Types.Status = useAppSelector(ScheduledEmailTypeSlice.SearchStatus); - const data: ScheduledEmailType[] = useAppSelector(ScheduledEmailTypeSlice.SearchResults); - const allData: ScheduledEmailType[] = useAppSelector(ScheduledEmailTypeSlice.Data); - const categoryStatus = useAppSelector(EmailCategorySlice.Status); - const categories = useAppSelector(EmailCategorySlice.Data); - const parentID = useAppSelector(ScheduledEmailTypeSlice.ParentID); + const [filters, setFilters] = React.useState[]>([]) + const [status, setStatus] = React.useState('uninitiated'); + const [searchStatus, setSearchStatus] = React.useState('uninitiated'); + const [data, setData] = React.useState([]); + const [allData, setAllData] = React.useState([]); + const [categoryStatus, setCategoryStatus] = React.useState('uninitiated'); + const [categories, setCategories] = React.useState([]); const [showModal, setShowModal] = React.useState(false); const [errors, setErrors] = React.useState([]); @@ -71,24 +68,57 @@ const ByReport = (props: IProps) => { const [ascending, setAscending] = React.useState(false); const [page, setPage] = React.useState(0); - const totalPages = useAppSelector(ScheduledEmailTypeSlice.TotalPages); - const totalRecords = useAppSelector(ScheduledEmailTypeSlice.TotalRecords); - const recordsPerPage = useAppSelector(ScheduledEmailTypeSlice.RecordsPerPage); - + const [totalPages, setTotalPages] = React.useState(0); + const [totalRecords, setTotalRecords] = React.useState(0); + const [recordsPerPage, setRecordsPerPage] = React.useState(0); const [refreshTrigger, setRefreshTrigger] = React.useState(false); React.useEffect(() => { - if (categoryStatus == 'uninitiated' || categoryStatus == 'changed') - dispatch(EmailCategorySlice.Fetch()); + setCategoryStatus('loading') + const h = new GenericController(`${homePath}api/OpenXDA/EmailCategory`, "Name", true).Fetch(); + h.done((d) => { + setCategories(d) + setCategoryStatus('idle') + }); + h.fail(() => setCategoryStatus('error')); + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + }, []); React.useEffect(() => { - dispatch(ScheduledEmailTypeSlice.Fetch()); - }, [parentID, refreshTrigger]); + setStatus('uninitiated') + const h = scheduledEmailTypeController.Fetch(); + h.done((d) => { + setAllData(d) + setStatus('idle') + }); + h.fail(() => setStatus('error')) + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + }, [refreshTrigger]); React.useEffect(() => { - dispatch(ScheduledEmailTypeSlice.PagedSearch({ filter: undefined, sortField, ascending: ascending, page: page })); - }, [sortField, ascending, page, refreshTrigger]) + setSearchStatus('loading') + const h = scheduledEmailTypeController.PagedSearch(filters, sortField, ascending, page); + h.done((d) => { + setData(JSON.parse(d.Data as unknown as string)) + setTotalPages(d.NumberOfPages); + setRecordsPerPage(d.RecordsPerPage); + setTotalRecords(d.TotalRecords); + setSearchStatus('idle'); + }); + h.fail(() => setSearchStatus('error')) + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + }, [sortField, filters, ascending, page, refreshTrigger]) React.useEffect(() => { let e = []; @@ -122,7 +152,7 @@ const ByReport = (props: IProps) => {
CollumnList={searchFields} - SetFilter={(flds) => dispatch(ScheduledEmailTypeSlice.PagedSearch({ filter: flds }))} + SetFilter={setFilters} Direction={'left'} defaultCollumn={{ key: 'Name', label: 'Name', type: 'string', isPivotField: false }} Width={'50%'} Label={'Search'} ShowLoading={searchStatus === 'loading'} ResultNote={searchStatus === 'error' ? 'Could not complete Search' : `Displaying Report(s) ${totalRecords > 0 ? recordsPerPage * page + 1 : 0}-${recordsPerPage * page + data.length} out of ${totalRecords}`} GetEnum={() => { @@ -228,7 +258,7 @@ const ByReport = (props: IProps) => { Show={showModal} ShowX={true} Size={'lg'} ShowCancel={false} ConfirmText={'Add'} CallBack={(conf, isBtn) => { if (conf) - dispatch(ScheduledEmailTypeSlice.DBAction({ verb: "POST", record: newEmail })).then(() => setRefreshTrigger((val) => !val)) + scheduledEmailTypeController.DBAction("POST", newEmail).then(() => setRefreshTrigger((val) => !val)) setShowModal(false); }} DisableConfirm={errors.length > 0} diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/DatasourceUI/DataSourceModal.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/DatasourceUI/DataSourceModal.tsx index 15d0b3c03..cf7166625 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/DatasourceUI/DataSourceModal.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/DatasourceUI/DataSourceModal.tsx @@ -21,13 +21,12 @@ // //****************************************************************************************************** -import { useAppDispatch, useAppSelector } from '../../hooks'; import * as React from 'react'; -import { Modal } from '@gpa-gemstone/react-interactive'; +import { Modal, GenericController } from '@gpa-gemstone/react-interactive'; import { ReactIcons } from '@gpa-gemstone/gpa-symbols'; import { IDataSourceScheduledEmailType, IScheduledDataSource, IScheduledEmailDataSourceSetting } from '../../global'; -import { ScheduledDataSourceSettingSlice, ScheduledDataSourceSlice, ScheduledEmailDataSourceSlice } from '../../Store'; import { Select } from '@gpa-gemstone/react-forms'; +import { Application } from '@gpa-gemstone/application-typings'; import SQLDataSource from './SQLDataSource'; import AzureDataSource from './AzureDataSource'; import { cloneDeep } from 'lodash'; @@ -41,32 +40,55 @@ interface IProps { OnClose: () => void } +const scheduledDataSourceSettingController = new GenericController(`${homePath}api/OpenXDA/ScheduledEmailDataSourceSetting`, "Name", false); +const dataSourceScheduledEmailTypeController = new GenericController(`${homePath}api/OpenXDA/ScheduledEmailDataSourceEmailType`, "ScheduledEmailDataSourceName", false); const DataSourceModal = (props: IProps) => { - const dispatch = useAppDispatch(); - const typeStatus = useAppSelector(ScheduledDataSourceSlice.Status); - const types: IScheduledDataSource[] = useAppSelector(ScheduledDataSourceSlice.Data); + const [typeStatus, setTypeStatus] = React.useState('uninitiated'); + const [types, setTypes] = React.useState([]); const [record, setRecord] = React.useState(); - const originalsettings: IScheduledEmailDataSourceSetting[] = useAppSelector(ScheduledDataSourceSettingSlice.Data); - const settingStatus = useAppSelector(ScheduledDataSourceSettingSlice.Status); - const settingsParentID = useAppSelector(ScheduledDataSourceSettingSlice.ParentID); + const [originalsettings, setOriginalSettings] = React.useState([]); + const [settingStatus, setSettingStatus] = React.useState('uninitiated'); const [currentSettings, setCurrentSettings] = React.useState([]); const [errors, setErrors] = React.useState([]); const [changes, setChanges] = React.useState([]); - React.useEffect(() => { - if (typeStatus == 'uninitiated' || typeStatus == 'changed') - dispatch(ScheduledDataSourceSlice.Fetch()); - }, [typeStatus]); + React.useEffect(() => { + const h = new GenericController(`${homePath}api/OpenXDA/ScheduledEmailDataSource`, "Name", false).Fetch(); + setTypeStatus('loading'); + h.done((d) => { + setTypeStatus('idle'); + setTypes(d); + }) + h.fail(() => setTypeStatus('error')) + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + }, []); React.useEffect(() => { if (props.Record == null) return - if (settingStatus == 'uninitiated' || settingStatus == 'changed' || settingsParentID != props.Record.ID) - dispatch(ScheduledDataSourceSettingSlice.Fetch(props.Record.ID)); - }, [settingStatus, props.Record]); + + setSettingStatus('loading') + const h = scheduledDataSourceSettingController.Fetch(props.Record.ID); + + h.done((d) => { + setOriginalSettings(d) + setSettingStatus('idle') + }); + h.fail(() => setSettingStatus('error')); + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + + }, [props.Record]); React.useEffect(() => { if (props.Record == null) @@ -172,11 +194,11 @@ const DataSourceModal = (props: IProps) => { { if (c && record.ID < 0) { - dispatch(ScheduledEmailDataSourceSlice.DBAction({ verb: 'POST', record: { ...record, Settings: currentSettings.filter(s => s.Value != null) } })) + dataSourceScheduledEmailTypeController.DBAction('POST',{ ...record, Settings: currentSettings.filter(s => s.Value != null) } ) } if (c && record.ID >= 0) { - dispatch(ScheduledEmailDataSourceSlice.DBAction({ verb: 'PATCH', record })) - currentSettings.forEach((s) => { if (s.Value != null) dispatch(ScheduledDataSourceSettingSlice.DBAction({ verb: s.ID == 0 ? 'POST' : 'PATCH', record: s }));}); + dataSourceScheduledEmailTypeController.DBAction('PATCH', record ) + currentSettings.forEach((s) => { if (s.Value != null) scheduledDataSourceSettingController.DBAction( s.ID == 0 ? 'POST' : 'PATCH', s );}); } if (!c) setCurrentSettings(originalsettings); diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/DatasourceUI/DataSourceWindow.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/DatasourceUI/DataSourceWindow.tsx index b1245f411..84e23ca03 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/DatasourceUI/DataSourceWindow.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/DatasourceUI/DataSourceWindow.tsx @@ -21,15 +21,14 @@ // //****************************************************************************************************** -import { useAppDispatch, useAppSelector } from '../../hooks'; import * as React from 'react'; +import { Application } from '@gpa-gemstone/application-typings'; +import { ReactIcons } from '@gpa-gemstone/gpa-symbols'; +import { GenericController, Search, Warning } from '@gpa-gemstone/react-interactive'; +import { Column, Paging, Table } from '@gpa-gemstone/react-table'; import { ScheduledEmailType, IDataSourceScheduledEmailType } from '../../global'; -import { ScheduledEmailDataSourceSlice } from '../../Store'; import DataSourceModal from './DataSourceModal'; -import { Table, Column, Paging } from '@gpa-gemstone/react-table'; import DataSourceTesting from './DataSourceTesting'; -import { ReactIcons } from '@gpa-gemstone/gpa-symbols'; -import { Warning, Search } from '@gpa-gemstone/react-interactive'; declare var homePath; @@ -37,24 +36,38 @@ declare var version; interface IProps { Record: ScheduledEmailType } +const dataSourceScheduledEmailTypeController = new GenericController(`${homePath}api/OpenXDA/ScheduledEmailDataSourceEmailType`, "ScheduledEmailDataSourceName", false); const DataSourceWindow = (props: IProps) => { - const dispatch = useAppDispatch(); - const status = useAppSelector(ScheduledEmailDataSourceSlice.Status); - const data = useAppSelector(ScheduledEmailDataSourceSlice.SearchResults); - const emailID = useAppSelector(ScheduledEmailDataSourceSlice.ParentID); + const [status, setStatus] = React.useState('uninitiated'); + const [data, setData] = React.useState([]); const [dataSource, setDataSource] = React.useState(null); const [showTest, setShowTest] = React.useState(false); const [showRemoveWarning, setShowRemoveWarning] = React.useState(false); const [showDataSourceModal, setShowDataSourceModal] = React.useState(false); const [page, setPage] = React.useState(0); - const totalPages = useAppSelector(ScheduledEmailDataSourceSlice.TotalPages); + const [totalPages, setTotalPages] = React.useState(0); const [refreshTrigger, setRefreshTrigger] = React.useState(false); React.useEffect(() => { - const filters: Search.IFilter[] = [{ FieldName: "ScheduledEmailTypeID", SearchText: props.Record.ID.toString(), Operator: "=", IsPivotColumn: false, Type: 'string' }] - dispatch(ScheduledEmailDataSourceSlice.PagedSearch({ filter: filters, sortField: 'ScheduledEmailDataSourceName', ascending: true, page: page })); - }, [status, props.Record.ID, emailID, dataSource, page, refreshTrigger]); + const filters: Search.IFilter[] = [{ FieldName: "ScheduledEmailTypeID", SearchText: props.Record.ID.toString(), Operator: "=", IsPivotColumn: false, Type: 'string' }]; + setStatus('loading'); + + const h = dataSourceScheduledEmailTypeController.PagedSearch(filters, 'ID', true, page) + + h.done((d) => { + setStatus('idle'); + setData(JSON.parse(d.Data)); + setTotalPages(d.NumberOfPages); + }) + + h.fail(() => setStatus('error')) + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + }, [props.Record.ID, dataSource, page, refreshTrigger]); return (
@@ -154,9 +167,9 @@ const DataSourceWindow = (props: IProps) => { { if (c) - dispatch(ScheduledEmailDataSourceSlice.DBAction({ - verb: 'DELETE', record: dataSource - })).then(() => setRefreshTrigger((val) => !val)); + dataSourceScheduledEmailTypeController.DBAction( + 'DELETE', dataSource + ).then(() => setRefreshTrigger((val) => !val)); setShowRemoveWarning(false); }} diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/GeneralInfo.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/GeneralInfo.tsx index 529d6f7b3..74e66c5db 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/GeneralInfo.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/GeneralInfo.tsx @@ -21,13 +21,13 @@ // //****************************************************************************************************** -import { useAppDispatch, useAppSelector } from '../hooks'; import * as React from 'react'; -import { ToolTip } from '@gpa-gemstone/react-forms'; +import { Application } from '@gpa-gemstone/application-typings'; import { ReactIcons } from '@gpa-gemstone/gpa-symbols'; -import { ScheduledEmailType } from '../global'; -import { ScheduledEmailTypeSlice } from '../Store'; import { IsCron } from '@gpa-gemstone/helper-functions'; +import { ToolTip } from '@gpa-gemstone/react-forms'; +import { GenericController } from '@gpa-gemstone/react-interactive'; +import { ScheduledEmailType } from '../global'; import ReportForm from './ReportForm'; declare var homePath; @@ -35,23 +35,40 @@ declare var version; interface IProps { Record: ScheduledEmailType } +const scheduledEmailTypeController = new GenericController(`${homePath}api/OpenXDA/ScheduledEmailType`, "Name", true); const GeneralInfo = (props: IProps) => { - const dispatch = useAppDispatch(); const [email, setEmail] = React.useState(props.Record); const [hasChanged, setHasChanged] = React.useState(false); const [hover, setHover] = React.useState<('submit' | 'clear' | 'none')>('none'); - const allEmails = useAppSelector(ScheduledEmailTypeSlice.Data); + const [scheduledEmails, setScheduledEmails] = React.useState([]); + const [scheduledEmailStatus, setScheduledEmailStatus] = React.useState('uninitiated'); const [errors, setErrors] = React.useState([]); + React.useEffect(() => { + setScheduledEmailStatus('loading') + const h = scheduledEmailTypeController.Fetch(); + h.done((d) => { + setScheduledEmails(d) + setScheduledEmailStatus('idle') + }); + h.fail(() => setScheduledEmailStatus('error')); + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + + }, []); + React.useEffect(() => { let e = []; if (email.Name == undefined || email.Name.length < 1) e.push('A Name is required'); - if (allEmails.findIndex(s => s.Name === email.Name && s.ID !== email.ID) >= 0) + if (scheduledEmails.findIndex(s => s.Name === email.Name && s.ID !== email.ID) >= 0) e.push('An Email with this Name already exists'); if (email.EmailCategoryID < 0) e.push('A Category must be selected.'); @@ -98,7 +115,7 @@ const GeneralInfo = (props: IProps) => { diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/ReportForm.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/ReportForm.tsx index acdcec636..5d4a19833 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/ReportForm.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/ReportForm.tsx @@ -21,38 +21,57 @@ // //****************************************************************************************************** -import { useAppDispatch, useAppSelector } from '../hooks'; import * as React from 'react'; -import { ScheduledEmailType } from '../global'; -import { EmailCategorySlice, ScheduledEmailTypeSlice } from '../Store'; +import { ScheduledEmailType, EmailCategory } from '../global'; import { CheckBox, Input, Select } from '@gpa-gemstone/react-forms'; -import { IsCron } from '@gpa-gemstone/helper-functions' +import { IsCron } from '@gpa-gemstone/helper-functions'; +import { Application } from '@gpa-gemstone/application-typings'; +import { GenericController } from '@gpa-gemstone/react-interactive'; interface IProps { record: ScheduledEmailType, setRecord: (d: ScheduledEmailType) => void } const ReportForm = (props: IProps) => { - const dispatch = useAppDispatch(); - const emails = useAppSelector(ScheduledEmailTypeSlice.Data); - const status = useAppSelector(ScheduledEmailTypeSlice.Status); - - const categoryStatus = useAppSelector(EmailCategorySlice.Status); - const categories = useAppSelector(EmailCategorySlice.Data); + const [scheduledEmails, setScheduledEmails] = React.useState([]); + const [scheduledEmailStatus, setScheduledEmailStatus] = React.useState('uninitiated'); + const [categoryStatus, setCategoryStatus] = React.useState('uninitiated'); + const [categories, setCategories] = React.useState([]); React.useEffect(() => { - if (categoryStatus == 'uninitiated' || categoryStatus == 'changed') - dispatch(EmailCategorySlice.Fetch()); - }, [categoryStatus]); + setCategoryStatus('loading') + const h = new GenericController(`${homePath}api/OpenXDA/EmailCategory`, "Name", true).Fetch(); + h.done((d) => { + setCategories(d) + setCategoryStatus('idle') + }); + h.fail(() => setCategoryStatus('error')); + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + }, []); React.useEffect(() => { - if (status == 'uninitiated' || status == 'changed') - dispatch(ScheduledEmailTypeSlice.Fetch()); - }, [status]); + setScheduledEmailStatus('loading') + const h = new GenericController(`${homePath}api/OpenXDA/ScheduledEmailType`, "Name", true).Fetch(); + h.done((d) => { + setScheduledEmails(d) + setScheduledEmailStatus('idle') + }); + h.fail(() => setScheduledEmailStatus('error')); + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + + }, []); function Valid(field: keyof ScheduledEmailType) { if (field == 'Name') - return props.record.Name != null && props.record.Name.length != 0 && emails.findIndex(e => e.Name == props.record.Name && props.record.ID != e.ID) == -1; + return props.record.Name != null && props.record.Name.length != 0 && scheduledEmails.findIndex(e => e.Name == props.record.Name && props.record.ID != e.ID) == -1; if (field == 'Schedule') return props.record.Schedule != null && IsCron(props.record.Schedule); diff --git a/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/ReportPage.tsx b/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/ReportPage.tsx index 1c0b563b9..92316d480 100644 --- a/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/ReportPage.tsx +++ b/Source/Applications/SystemCenterNotification/Scripts/TSX/Report/ReportPage.tsx @@ -21,17 +21,17 @@ // //****************************************************************************************************** -import { useAppDispatch, useAppSelector } from '../hooks'; +import * as $ from 'jquery' import * as React from 'react'; -import { TabSelector, Warning, LoadingScreen, ServerErrorIcon, Modal } from '@gpa-gemstone/react-interactive' import { Application } from '@gpa-gemstone/application-typings'; -import { ScheduledEmailTypeSlice } from '../Store'; +import { GenericController, LoadingScreen, ServerErrorIcon, TabSelector, Warning } from '@gpa-gemstone/react-interactive'; +import { ScheduledEmailType } from '../global'; +import DataSourceWindow from './DatasourceUI/DataSourceWindow'; import GeneralInfo from './GeneralInfo'; import Subscriptions from './Subscriptions'; -import TriggerWindow from './TriggerWindow'; -import DataSourceWindow from './DatasourceUI/DataSourceWindow'; import Template from './Template'; import TestEmail from './TestEmail'; +import TriggerWindow from './TriggerWindow'; declare var homePath; declare var version; @@ -49,13 +49,12 @@ const Tabs = [ const EmailPage = (props: IProps) => { - const dispatch = useAppDispatch(); const [showDelete, setShowDelete] = React.useState(false); const [showTest, setShowTest] = React.useState(false); - const email = useAppSelector((state) => ScheduledEmailTypeSlice.Datum(state, parseInt(props.useParams.id))); - const status: Application.Types.Status = useAppSelector(ScheduledEmailTypeSlice.Status); + const [scheduledEmail, setScheduledEmail] = React.useState(null); + const [scheduledEmailStatus, setScheduledEmailStatus] = React.useState('uninitiated'); const getTab = React.useCallback(() => { if (sessionStorage.hasOwnProperty('ReportPage.Tab')) @@ -66,9 +65,27 @@ const EmailPage = (props: IProps) => { const [tab, setTab] = React.useState(getTab()); React.useEffect(() => { - if (status == 'uninitiated' || status == 'changed') - dispatch(ScheduledEmailTypeSlice.Fetch()); - }, [status]); + setScheduledEmailStatus('loading') + const h = $.ajax({ + type: "GET", + url: `${homePath}api/OpenXDA/ScheduledEmailType/One/${props.useParams.id}`, + contentType: "application/json; charset=utf-8", + dataType: 'json', + cache: false, + async: true + }) + h.done((d) => { + setScheduledEmail(d) + setScheduledEmailStatus('idle') + }); + h.fail(() => setScheduledEmailStatus('error')); + + return function cleanup() { + if (h != null && h.abort != null) + h.abort(); + } + + }, [props.useParams.id]) React.useEffect(() => { const saved = getTab(); @@ -79,12 +96,12 @@ const EmailPage = (props: IProps) => { return (
- - - {!email ? <> : <> + + + {!scheduledEmail ? <> : <>
-

{email != null ? email.Name : ''}

+

{scheduledEmail != null ? scheduledEmail.Name : ''}

@@ -99,38 +116,40 @@ const EmailPage = (props: IProps) => {
{tab == "settings" ?
- +
: <>} {tab == "template" ?
-