import axios from "axios"; import { LocationChangeAction } from "connected-react-router"; import { toast } from "react-toastify"; import { ActionCreator, Dispatch } from "redux"; import { ThunkAction } from "redux-thunk"; import RepoAccountService from "../services/RepoAccountService"; import { ICollateral, IRefundStatus, IRepoAccount, IRepoAccountFilters, IRepoCompanySelection, IRepoCompanySelectionPostResponse } from "../services/service-types/RepoAccountTypes"; import Configuration from "../shared/configuration/Configuration"; import AsyncState from "../shared/enumerations/AsyncState"; import UrlFormatting from "../shared/formatting/UrlFormatting"; import ServerLogging from "../shared/logging/ServerLogging"; import ToastDefaults from "../shared/notifications/ToastDefaults"; import IAsyncStateContainer from "../shared/types/IAsyncStateContainer"; import { IPagedResponse } from "../shared/types/IPagedResponse"; import { IAppState } from "../store/Store"; import { IAuctionAssignmentInsertFromPost } from "./AuctionAssignmentActions"; import {getUser} from "../adalConfig"; // Create Action Constants export enum RepoAccountActionTypes { SET_SELECTED_ACCOUNT = "REPOACCOUNT_SET_SELECTED_ACCOUNT", INSERT_ONE_ACCOUNT = "REPOACCOUNT_INSERT_ONE", INSERT_ACCOUNTS = "REPOACCOUNT_INSERT_ACCOUNTS", SET_FILTERED_ACCOUNTS = "REPOACCOUNT_SET_FILTERED_ACCOUNTS", INSERT_COLLATERAL = "REPOACCOUNT_INSERT_COLLATERAL", SET_FILTERS = "REPOACCOUNT_SET_FILTERS", UPDATE_REFUND_STATUS = "REPOACCOUNT_UPDATE_REFUND_STATUS", SET_SEARCH_PHRASE = "REPOACCOUNT_SET_SEARCH_PHRASE" } export interface IRepoAccountSelectAccountAction { type: RepoAccountActionTypes.SET_SELECTED_ACCOUNT; repoId: IAsyncStateContainer; } export interface IRepoAccountSetFilteredAccountsAction { type: RepoAccountActionTypes.SET_FILTERED_ACCOUNTS; filteredAccounts: IAsyncStateContainer>; } export interface IRepoAccountInsertOneAccount { type: RepoAccountActionTypes.INSERT_ONE_ACCOUNT; repoAccount: IRepoAccount; } export interface IRepoAccountInsertAccounts { type: RepoAccountActionTypes.INSERT_ACCOUNTS; repoAccounts: IRepoAccount[]; } export interface IRepoAccountInsertCollateral { type: RepoAccountActionTypes.INSERT_COLLATERAL; collateral: ICollateral; } export interface IRepoAccountSetAccountFiltersAction { type: RepoAccountActionTypes.SET_FILTERS; filters: IRepoAccountFilters; } export interface IRepoAccountUpdateRefundStatus { type: RepoAccountActionTypes.UPDATE_REFUND_STATUS; refundStatus: IRefundStatus; } export interface IRepoAccountSetSearchPhraseAction { type: RepoAccountActionTypes.SET_SEARCH_PHRASE; searchPhrase: string; } /* Combine the action types with a union (we assume there are more) example: export type RepoAccountActions = IGetAllAction | IGetOneAction ... */ export type RepoAccountActions = IRepoAccountSelectAccountAction | IRepoAccountSetFilteredAccountsAction | IAuctionAssignmentInsertFromPost | IRepoAccountInsertOneAccount | IRepoAccountInsertAccounts | IRepoAccountInsertCollateral | IRepoAccountSetAccountFiltersAction | IRepoAccountUpdateRefundStatus | IRepoAccountSetSearchPhraseAction | LocationChangeAction; export const getInitialRepoAccounts: ActionCreator, IAppState, {}, IRepoAccountInsertAccounts | IRepoAccountSetFilteredAccountsAction | IRepoAccountSetFilteredAccountsAction>> = () => { return async (dispatch, getState) => { // Clear filters, as this fetch will not match the current filters const newFilter: IRepoAccountFilters = {onlyOpenAccounts: getState().repoAccountState.accountFilters.onlyOpenAccounts}; dispatch({ type: RepoAccountActionTypes.SET_FILTERED_ACCOUNTS, filteredAccounts: { state: getState().repoAccountState.filteredAccounts.state } }); await postFilteredAccountsFromURL(Configuration.PrefixRepoApiUrl(Configuration.Get.urls.repoApi.accounts_filter), newFilter, dispatch); }; }; export const setSearchPhrase = (searchPhrase: string): IRepoAccountSetSearchPhraseAction => { return { type: RepoAccountActionTypes.SET_SEARCH_PHRASE, searchPhrase: searchPhrase }; }; export const goToNextRepoAccountPage: ActionCreator, IAppState, {}, IRepoAccountSetAccountFiltersAction | IRepoAccountSetFilteredAccountsAction | IRepoAccountInsertAccounts>> = () => { return async (dispatch, getState) => { const currentFilters = getState().repoAccountState.accountFilters; const filterPageResponse = getState().repoAccountState.filteredAccounts.data; if (filterPageResponse && filterPageResponse.nextPage) { await postFilteredAccountsFromURL(filterPageResponse.nextPage, currentFilters, dispatch); } }; }; export const goToPreviousRepoAccountPage: ActionCreator, IAppState, {}, IRepoAccountSetAccountFiltersAction | IRepoAccountSetFilteredAccountsAction | IRepoAccountInsertAccounts>> = () => { return async (dispatch, getState) => { const currentFilters = getState().repoAccountState.accountFilters; const filterPageResponse = getState().repoAccountState.filteredAccounts.data; if (filterPageResponse && filterPageResponse.previousPage) { await postFilteredAccountsFromURL(filterPageResponse.previousPage, currentFilters, dispatch); } }; }; export const fetchSelectedRepoAccount: ActionCreator, IAppState, {}, IRepoAccountInsertOneAccount | IRepoAccountSelectAccountAction>> = (repoId: number) => { const singleRepoURL = UrlFormatting.ReplaceURLParams( Configuration.PrefixRepoApiUrl(Configuration.Get.urls.repoApi.accounts_single), {key: "repoId", value: repoId}, ); return async (dispatch) => { // Since we are fetching the selected account, // we will need to dispatch both to insert the retrieved // account, as well as to update the selected account dispatch({ type: RepoAccountActionTypes.SET_SELECTED_ACCOUNT, repoId: { state: AsyncState.Loading } }); try { const response = await axios.get(singleRepoURL); RepoAccountService.ConvertResponseToRepoAccount(response.data); dispatch({ type: RepoAccountActionTypes.INSERT_ONE_ACCOUNT, repoAccount: response.data }); dispatch({ type: RepoAccountActionTypes.SET_SELECTED_ACCOUNT, repoId: { state: AsyncState.Success, data: response.data.repoId } }); } catch (err) { ServerLogging.ResolveErrorWithToast(err, { action: "Fetch Selected Repo Account", user: getUser().userName, url: singleRepoURL, arguments: repoId }); dispatch({ type: RepoAccountActionTypes.SET_SELECTED_ACCOUNT, repoId: { state: AsyncState.Failure } }); } }; }; export const searchRepoAccounts: ActionCreator, IAppState, {}, IRepoAccountSetFilteredAccountsAction | IRepoAccountSetAccountFiltersAction | IRepoAccountSetSearchPhraseAction | IRepoAccountInsertAccounts>> = (searchPhrase: string) => { const parameterizedUrl = UrlFormatting.ReplaceURLParams( Configuration.PrefixRepoApiUrl(Configuration.Get.urls.repoApi.accounts_search), { key: "searchPhrase", value: searchPhrase } ); // Since we are fetching the currently filtered accounts, // we will need to dispatch both to insert the retrieved // account, as well as to update the currently filtered accounts return async (dispatch) => { dispatch({ type: RepoAccountActionTypes.SET_FILTERED_ACCOUNTS, filteredAccounts: { state: AsyncState.Loading } }); dispatch({ type: RepoAccountActionTypes.SET_FILTERS, filters: {} }); dispatch({ type: RepoAccountActionTypes.SET_SEARCH_PHRASE, searchPhrase: searchPhrase }); try { const response = await axios.get(parameterizedUrl); RepoAccountService.ConvertResponseToRepoAccounts(response.data); dispatch({ type: RepoAccountActionTypes.INSERT_ACCOUNTS, repoAccounts: response.data }); dispatch({ type: RepoAccountActionTypes.SET_FILTERED_ACCOUNTS, filteredAccounts: { state: AsyncState.Success, data: { items: RepoAccountService.CreateRepoAccountIdMap(response.data), totalItems: response.data.length, currentPage: 1, totalPages: 1 } } }); } catch (err) { ServerLogging.ResolveErrorWithToast(err, { action: "Search Repo Accoutns", user: getUser().userName, url: parameterizedUrl, arguments: searchPhrase }); dispatch({ type: RepoAccountActionTypes.SET_FILTERED_ACCOUNTS, filteredAccounts: { state: AsyncState.Failure } }); } }; }; export const removeRepoAccountFromAlertFilter: ActionCreator, IAppState, {}, IRepoAccountSetFilteredAccountsAction>> = (alertMessage: string, repoId: number) => { return async (dispatch, getState) => { const filteredAccounts = getState().repoAccountState.filteredAccounts; const accountFilters = getState().repoAccountState.accountFilters; if (filteredAccounts.data && alertMessage === accountFilters.alertMessage) { const indexOfEl = filteredAccounts.data.items.indexOf(repoId); if (indexOfEl >= 0) { const updatedAccountItems = filteredAccounts.data.items.slice(); updatedAccountItems.splice(indexOfEl, 1); dispatch({ type: RepoAccountActionTypes.SET_FILTERED_ACCOUNTS, filteredAccounts: { data: { ...filteredAccounts.data, totalItems: filteredAccounts.data.totalItems - 1, items: updatedAccountItems }, state: filteredAccounts.state } }); } } }; }; export const fetchRepoAccountsViaFilters: ActionCreator, IAppState, {}, IRepoAccountSetAccountFiltersAction | IRepoAccountSetFilteredAccountsAction | IRepoAccountSetSearchPhraseAction | IRepoAccountInsertAccounts>> = (filters: IRepoAccountFilters) => { return async (dispatch) => { dispatch({ type: RepoAccountActionTypes.SET_FILTERS, filters: filters }); dispatch({ type: RepoAccountActionTypes.SET_SEARCH_PHRASE, searchPhrase: "" }); await postFilteredAccountsFromURL(Configuration.PrefixRepoApiUrl(Configuration.Get.urls.repoApi.accounts_filter), filters, dispatch); }; }; export const postToNextFilterPage: ActionCreator, IAppState, {}, IRepoAccountSetFilteredAccountsAction | IRepoAccountInsertAccounts>> = (nextPageUrl: string, filters: IRepoAccountFilters) => { return async (dispatch: Dispatch) => { await postFilteredAccountsFromURL(nextPageUrl, filters, dispatch); }; }; export const putRepoAccount: ActionCreator, IAppState, {}, IRepoAccountInsertOneAccount>> = (account: IRepoAccount) => { const url = Configuration.PrefixRepoApiUrl(Configuration.Get.urls.repoApi.accounts) + "/" + account.repoId; return async (dispatch: Dispatch) => { try { const response = await axios.put(url, account); RepoAccountService.ConvertResponseToRepoAccount(response.data); dispatch({ type: RepoAccountActionTypes.INSERT_ONE_ACCOUNT, repoAccount: response.data }); toast.success("Account Updated.", ToastDefaults.baseParams); } catch (err) { ServerLogging.ResolveErrorWithToast(err, { action: "Put Repo Account", user: getUser().userName, url: url, arguments: account }); } }; }; export const putCollateral: ActionCreator, ICollateral, null, IRepoAccountInsertCollateral>> = (collateral: ICollateral) => { const singleCollateralUrl = UrlFormatting.ReplaceURLParams( Configuration.PrefixRepoApiUrl(Configuration.Get.urls.repoApi.collateral_single), {key: "collateralId", value: collateral.collateralId}, ); return async (dispatch) => { try { const response = await axios.put( singleCollateralUrl, collateral ); RepoAccountService.ConvertResponseToCollateral(response.data); dispatch({ type: RepoAccountActionTypes.INSERT_COLLATERAL, collateral: response.data }); toast.success("Collateral Updated.", ToastDefaults.baseParams); } catch (err) { ServerLogging.ResolveErrorWithToast(err, { action: "Put Collateral", user: getUser().userName, url: singleCollateralUrl, arguments: collateral }); } }; }; export const putRepoCompanySelection: ActionCreator, IAppState, {}, IRepoAccountInsertCollateral>> = (repoCompanySelection: IRepoCompanySelection, collateral: ICollateral) => { const repoCompanySelectionPutURL = UrlFormatting.ReplaceURLParams( Configuration.PrefixRepoApiUrl(Configuration.Get.urls.repoApi.repo_company_selection_single), {key: "repoCompanySelectionId", value: repoCompanySelection.id}, ); return async (dispatch) => { try { const response = await axios.put( repoCompanySelectionPutURL, repoCompanySelection ); RepoAccountService.ConvertResponseToRepoCompanySelection(response.data); dispatch({ type: RepoAccountActionTypes.INSERT_COLLATERAL, collateral: RepoAccountService.CreateCollateralFromRepoCompanySelection(collateral, repoCompanySelection) }); toast.success("Repo Selection Updated.", ToastDefaults.baseParams); } catch (err) { ServerLogging.ResolveErrorWithToast(err, { action: "Put Repo Company Selection", user: getUser().userName, url: repoCompanySelectionPutURL, arguments: [repoCompanySelection, collateral] }); } }; }; export const postRepoCompanySelection: ActionCreator, IAppState, {}, IRepoAccountInsertCollateral>> = (repoCompanySelection: IRepoCompanySelection, collateral: ICollateral) => { const repoCompanySelectionPostURL = Configuration.PrefixRepoApiUrl(Configuration.Get.urls.repoApi.repo_company_selection); return async (dispatch) => { try { const response = await axios.post( repoCompanySelectionPostURL, repoCompanySelection ); let responseData = response.data as IRepoCompanySelectionPostResponse; const repoCompanySelectionArray = [ responseData.newRepoCompanySelection ]; RepoAccountService.ConvertResponseToRepoCompanySelection(responseData.newRepoCompanySelection); if (responseData.previousRepoCompanySelection) { RepoAccountService.ConvertResponseToRepoCompanySelection(responseData.previousRepoCompanySelection); repoCompanySelectionArray.push(responseData.previousRepoCompanySelection); } dispatch({ type: RepoAccountActionTypes.INSERT_COLLATERAL, collateral: RepoAccountService.CreateCollateralFromRepoCompanySelection(collateral, repoCompanySelectionArray) }); toast.success("Repo Selection Updated.", ToastDefaults.baseParams); } catch (err) { ServerLogging.ResolveErrorWithToast(err, { action: "Post Repo Company Selection", user: getUser().userName, url: repoCompanySelectionPostURL, arguments: [repoCompanySelection, collateral] }); } }; }; export const putRefundStatus: ActionCreator, IAppState, {}, IRepoAccountUpdateRefundStatus>> = (refundStatus: IRefundStatus) => { const refundStatusPutUrl = UrlFormatting.ReplaceURLParams( Configuration.PrefixRepoApiUrl(Configuration.Get.urls.repoApi.refund_status_single), {key: "repoId", value: refundStatus.repoId} ); return async (dispatch) => { try { const response = await axios.put(refundStatusPutUrl, refundStatus); RepoAccountService.ConvertResponseToRefundStatus(response.data); dispatch({ type: RepoAccountActionTypes.UPDATE_REFUND_STATUS, refundStatus: response.data }); toast.success("Refund Status Updated.", ToastDefaults.baseParams); } catch (err) { ServerLogging.ResolveErrorWithToast(err, { action: "Put Refund Status", user: getUser().userName, url: refundStatusPutUrl, arguments: refundStatus }); } }; }; export const postRefundStatus: ActionCreator, IAppState, {}, IRepoAccountUpdateRefundStatus>> = (refundStatus: IRefundStatus) => { const refundStatusPutUrl = Configuration.PrefixRepoApiUrl(Configuration.Get.urls.repoApi.refund_status); return async (dispatch) => { try { const response = await axios.post(refundStatusPutUrl, refundStatus); RepoAccountService.ConvertResponseToRefundStatus(response.data); dispatch({ type: RepoAccountActionTypes.UPDATE_REFUND_STATUS, refundStatus: response.data }); toast.success("Refund Status Created.", ToastDefaults.baseParams); } catch (err) { ServerLogging.ResolveErrorWithToast(err, { action: "Post Refund Status", user: getUser().userName, url: refundStatusPutUrl, arguments: refundStatus }); } }; }; export const refreshCollateral: ActionCreator, IAppState, {}, IRepoAccountInsertCollateral>> = ( collateral: ICollateral, memberNo: number, loanNo: number) => { const coresCollateralUrl = UrlFormatting.ReplaceURLParams( Configuration.PrefixCoreUrl(Configuration.Get.urls.core.loan_collateral), {key: "loanNo", value: loanNo}, ); return async (dispatch) => { try { const singleRepoCollateralUrl = UrlFormatting.ReplaceURLParams( Configuration.PrefixRepoApiUrl(Configuration.Get.urls.repoApi.collateral_single), {key: "collateralId", value: collateral.collateralId}, ); const response = await axios.get(coresCollateralUrl); const updatedCollateral = updateCollateralWithCoreDetails(response.data, collateral); await axios.put( singleRepoCollateralUrl, updatedCollateral, ); dispatch({ type: RepoAccountActionTypes.INSERT_COLLATERAL, collateral: updatedCollateral, }); toast.success("Collateral Refreshed.", ToastDefaults.baseParams); } catch (err) { ServerLogging.ResolveErrorWithToast(err, { action: "Refresh Collateral", user: getUser().userName, url: coresCollateralUrl, arguments: [collateral, memberNo, loanNo] }); } }; }; const updateCollateralWithCoreDetails = (data: any[], collateral: ICollateral): ICollateral => { // eslint-disable-next-line const found = data.find(c => c.collateralId == collateral.refCollateralId); if (found.other.miscellaneousInformation != null) { collateral.vin = found.other.miscellaneousInformation; } if (found.vehicle != null) { collateral.make = found.vehicle.make; collateral.model = found.vehicle.model; collateral.vin = found.vehicle.vin; collateral.year = found.vehicle.year; } return collateral; }; const fetchFilteredAccountsFromAsyncCallback = async ( repoAccountFetch: () => Promise>, dispatch: Dispatch ) => { dispatch({ type: RepoAccountActionTypes.SET_FILTERED_ACCOUNTS, filteredAccounts: { state: AsyncState.Loading } }); try { const pagedResponse = await repoAccountFetch(); RepoAccountService.ConvertResponseToRepoAccounts(pagedResponse.items); dispatch({ type: RepoAccountActionTypes.INSERT_ACCOUNTS, repoAccounts: pagedResponse.items }); dispatch({ type: RepoAccountActionTypes.SET_FILTERED_ACCOUNTS, filteredAccounts: { state: AsyncState.Success, data: { items: RepoAccountService.CreateRepoAccountIdMap(pagedResponse.items), totalItems: pagedResponse.totalItems, nextPage: pagedResponse.nextPage, previousPage: pagedResponse.previousPage, currentPage: pagedResponse.currentPage, totalPages: pagedResponse.totalPages } } }); } catch (err) { ServerLogging.ResolveErrorWithToast(err, { action: "Fetch Filtered Accounts With Async", user: getUser().userName, }); dispatch({ type: RepoAccountActionTypes.SET_FILTERED_ACCOUNTS, filteredAccounts: { state: AsyncState.Failure } }); } }; const postFilteredAccountsFromURL = async (url: string, filters: IRepoAccountFilters, dispatch: Dispatch) => { fetchFilteredAccountsFromAsyncCallback(async () => { const response = await axios.post(url, filters); return response.data as IPagedResponse; }, dispatch); };