Adding
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import { AlertActionTypes } from './../actions/AlertActions';
|
||||
import { alertReducer} from './AlertReducer';
|
||||
|
||||
describe('Alert Reducer', () => {
|
||||
// No test for default case because it is unreachable with only one AlertActionType.
|
||||
|
||||
it ('Should return new state', () => {
|
||||
// arrange
|
||||
const payload = [
|
||||
{ id: 1,
|
||||
accountNo: "12345",
|
||||
message: "Test message",
|
||||
dateAdded: new Date(2019, 1, 1),
|
||||
dateMarkedRead: new Date(2019, 1, 2)}];
|
||||
|
||||
// act
|
||||
const newState = alertReducer(undefined, { type: AlertActionTypes.GET_ALL, alerts: payload});
|
||||
|
||||
// assert
|
||||
expect(newState.alerts).toEqual(payload);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,74 @@
|
||||
// Import Reducer type
|
||||
import {Reducer} from 'redux';
|
||||
import {
|
||||
AlertActions,
|
||||
AlertActionTypes,
|
||||
} from '../actions/AlertActions';
|
||||
import { Moment } from 'moment';
|
||||
|
||||
const cloneDeep = require('lodash.clonedeep');
|
||||
|
||||
export interface IAlert {
|
||||
id: number;
|
||||
repoId: number;
|
||||
accountNo: string;
|
||||
dateAdded: Moment;
|
||||
dateMarkedRead?: Moment;
|
||||
createdBy: string;
|
||||
markedReadBy: string;
|
||||
message: string;
|
||||
subMessage?: string;
|
||||
dayCount: number;
|
||||
}
|
||||
|
||||
export interface IAlertState {
|
||||
readonly alerts: IAlert[];
|
||||
readonly actions: IAlert[];
|
||||
}
|
||||
|
||||
// Define the initial state
|
||||
const initialAlertState: IAlertState = {
|
||||
alerts: [],
|
||||
actions: []
|
||||
};
|
||||
|
||||
const removeDismissedAlert = (dismissedAlert: IAlert, originalAlerts: IAlert[]): IAlert[] => {
|
||||
if (dismissedAlert.dateMarkedRead) {
|
||||
const foundIndex = originalAlerts.findIndex((alert) => alert.id === dismissedAlert.id);
|
||||
if (foundIndex) {
|
||||
const copiedAlerts = cloneDeep(originalAlerts) as IAlert[];
|
||||
copiedAlerts.splice(foundIndex, 1);
|
||||
return copiedAlerts;
|
||||
}
|
||||
}
|
||||
return originalAlerts;
|
||||
}
|
||||
|
||||
export const alertReducer: Reducer<IAlertState, AlertActions> = ( state = initialAlertState, action ) => {
|
||||
switch (action.type) {
|
||||
case AlertActionTypes.GET_ALL: {
|
||||
return {
|
||||
...state,
|
||||
alerts: action.alerts,
|
||||
actions: state.actions
|
||||
};
|
||||
}
|
||||
case AlertActionTypes.GET_ALL_ACTIONS: {
|
||||
return {
|
||||
...state,
|
||||
alerts: state.alerts,
|
||||
actions: action.actions,
|
||||
};
|
||||
}
|
||||
case AlertActionTypes.MARK_ALERT_READ: {
|
||||
const alertsWithoutDismissed = removeDismissedAlert(action.alert, state.alerts);
|
||||
return {
|
||||
...state,
|
||||
alerts: alertsWithoutDismissed,
|
||||
actions: state.actions,
|
||||
};
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,345 @@
|
||||
import { LOCATION_CHANGE, LocationChangePayload } from "connected-react-router";
|
||||
import { Moment } from "moment";
|
||||
import { Reducer } from "redux";
|
||||
import { AuctionAssignmentActions, AuctionAssignmentActionTypes } from "../actions/AuctionAssignmentActions";
|
||||
import HashRouterService from "../services/HashRouterService";
|
||||
import { ICollateral } from "../services/service-types/RepoAccountTypes";
|
||||
import AsyncState from "../shared/enumerations/AsyncState";
|
||||
import Routes from "../shared/Routes";
|
||||
import IAsyncStateContainer from "../shared/types/IAsyncStateContainer";
|
||||
import { IAuctionHouse } from "./AuctionHouseReducer";
|
||||
|
||||
const cloneDeep = require("lodash.clonedeep");
|
||||
|
||||
export interface IAutoIMS {
|
||||
repoId: number;
|
||||
vin: string;
|
||||
auctionAssignmentId: number;
|
||||
auctionCustomerNo: string;
|
||||
assignmentAccepted?: Moment;
|
||||
dateReceivedFromAutoIMS?: Moment;
|
||||
scheduledSaleDate?: Moment;
|
||||
soldDate?: Moment;
|
||||
announcedConditions: string;
|
||||
arbitrationReason: string;
|
||||
asIs100KMiles: boolean;
|
||||
auctionAge: number;
|
||||
auctionGuideBookPrice: number;
|
||||
buyerId: string;
|
||||
buyerName: string;
|
||||
chargeAmount: number;
|
||||
checkAmount: number;
|
||||
clientAge: number;
|
||||
clientCancelled?: Moment;
|
||||
collisionDamage: string;
|
||||
inspectedDate?: Moment;
|
||||
dateofLoss?: Moment;
|
||||
datePickedUp?: Moment;
|
||||
dateEnteredAutoIMS?: Moment;
|
||||
daysRun: number;
|
||||
daysRunHighestBidRecordedbyAuction: string;
|
||||
daystoDelivery: number;
|
||||
description: string;
|
||||
isDrivable: boolean;
|
||||
floorPriceAccepted: number;
|
||||
fundsSent?: Moment;
|
||||
grossPercentFloor: number;
|
||||
highestBid: number;
|
||||
statedMileage: number;
|
||||
netPercentFloor: number;
|
||||
otherAdvanceFees: number;
|
||||
otherTotal: string;
|
||||
otherAdvancedOther: string;
|
||||
otherAdvancedStorage: string;
|
||||
otherAdvancedTow: string;
|
||||
otherMiscellaneous: string;
|
||||
salePrice?: number;
|
||||
titleNumber: string;
|
||||
titleReceived?: Moment;
|
||||
titleReturned?: Moment;
|
||||
titleState: string;
|
||||
titleStatus: string;
|
||||
titleStatusfromAuction: string;
|
||||
totalCharges: number;
|
||||
approvedVehicleGrade: number;
|
||||
isOperable: boolean;
|
||||
shipmentDate?: Moment;
|
||||
status: string;
|
||||
storageCost: number;
|
||||
storageRatePerDay: number;
|
||||
towCost: number;
|
||||
vehicleDecimalGradeCode: number;
|
||||
vehicleGrade: number;
|
||||
mmrNationalAdjustedPrice: number;
|
||||
mmrRegionalAdjustedPrice: number;
|
||||
mmr: string;
|
||||
conditionReportLink: string;
|
||||
collateralImagePath: string;
|
||||
}
|
||||
|
||||
export interface IAuctionAssignmentToCollateral {
|
||||
auctionAssignmentId: number;
|
||||
collateralId: number;
|
||||
collateral: ICollateral;
|
||||
}
|
||||
|
||||
export interface IAuctionAssignment {
|
||||
id: number;
|
||||
repoId: number;
|
||||
isActive: boolean;
|
||||
auctionId: string;
|
||||
auctionCustomerNo: string;
|
||||
collateralVIN: string;
|
||||
dateSubmitted?: Moment;
|
||||
floorPrice?: number;
|
||||
floorPriceResponseCode: number;
|
||||
floorPriceResponseDescription: string;
|
||||
holdStatus: number;
|
||||
holdUntil?: Moment;
|
||||
redeemedDate?: Moment;
|
||||
auction?: IAuctionHouse;
|
||||
autoIMS?: IAutoIMS;
|
||||
sentTitleDocsDate?: Moment;
|
||||
cleaningLevel: number;
|
||||
legalSaleDate?: Moment;
|
||||
availablePickupDate?: Moment;
|
||||
auctionNotes?: string;
|
||||
retailValue?: number;
|
||||
auctionAssignmentToCollaterals: IAuctionAssignmentToCollateral[];
|
||||
floorPriceValuesUsed?: IFloorPriceValuesUsed;
|
||||
}
|
||||
|
||||
export interface ICollateralValuation {
|
||||
id: number;
|
||||
year: number;
|
||||
make: string;
|
||||
model: string;
|
||||
uid: string;
|
||||
ucgVehicleId: string;
|
||||
lookupDate?: Moment;
|
||||
mileage_Hours: string;
|
||||
makeCode?: number;
|
||||
makeDescription: string;
|
||||
seriesCode?: number;
|
||||
seriesDescription: string;
|
||||
bodyCode?: number;
|
||||
bodyDescription: string;
|
||||
weight?: number;
|
||||
msrp?: number;
|
||||
aveMileage: number;
|
||||
mileageAdjustment: number;
|
||||
auctionInfoUnavailable: boolean;
|
||||
lowAdjustedAuctionValue: number;
|
||||
averageAdjustedAuctionValue: number;
|
||||
highAdjustedAuctionValue: number;
|
||||
dateRange: string;
|
||||
loan?: number;
|
||||
loanPlusVinAccMileage?: number;
|
||||
retail?: number;
|
||||
retailPlusVinAccMileage?: number;
|
||||
avgTradeIn?: number;
|
||||
avgTradeInPlusVinAccMileage?: number;
|
||||
roughTradeIn?: number;
|
||||
roughTradeInPlusVinAccMileage?: number;
|
||||
tradeIn?: number;
|
||||
tradeInPlusVinAccMileage?: number;
|
||||
}
|
||||
|
||||
export interface IFloorPriceCalculationResult {
|
||||
strategyName: string;
|
||||
success: boolean;
|
||||
floorPrice: number;
|
||||
resultWeight: number;
|
||||
failureReason: string;
|
||||
}
|
||||
|
||||
export interface IFloorPriceValuesUsed {
|
||||
auctionAssignmentId: number;
|
||||
repoId: number;
|
||||
condition?: number;
|
||||
lowParam: number;
|
||||
highParam: number;
|
||||
percentageOf: number;
|
||||
auctionGuideBookPrice?: number;
|
||||
wasAuctionGuideBookPriceUsed: boolean;
|
||||
mmrNationalAdjustedPrice?: number;
|
||||
mmrRegionalAdjustedPrice?: number;
|
||||
wasMMRNationalAdjustedPriceUsed: boolean;
|
||||
wasMMRRegionalAdjustedPriceUsed: boolean;
|
||||
nadaLowAdjustedAuctionValue: number;
|
||||
nadaAverageAdjustedAuctionValue: number;
|
||||
nadaHighAdjustedAuctionValue: number;
|
||||
wasNADALowAdjustedAuctionValueUsed: boolean;
|
||||
wasNADAAverageAdjustedAuctionValueUsed: boolean;
|
||||
wasNADAHighAdjustedAuctionValueUsed: boolean;
|
||||
nadaMmrFloorPrice?: number;
|
||||
collateralValuation: ICollateralValuation;
|
||||
}
|
||||
|
||||
export interface IFloorPriceCalculationResultSet {
|
||||
primaryResult: IFloorPriceCalculationResult;
|
||||
allResults: IFloorPriceCalculationResult[];
|
||||
floorPriceValuesUsed: IFloorPriceValuesUsed;
|
||||
}
|
||||
|
||||
export interface IVehicleLocation {
|
||||
type?: number;
|
||||
name: string;
|
||||
address: string;
|
||||
city: string;
|
||||
state: string;
|
||||
zip: string;
|
||||
phone: string;
|
||||
}
|
||||
|
||||
export interface IAuctionAssignmentPostRequest {
|
||||
auctionAssignment: IAuctionAssignment;
|
||||
collaterals: ICollateral[];
|
||||
vehicleLocation: IVehicleLocation;
|
||||
primaryCollateralId: number;
|
||||
}
|
||||
|
||||
export interface IAuctionAssignmentState {
|
||||
loadedAuctionAssignments: IAuctionAssignment[];
|
||||
selectedAuctionAssignmentId: IAsyncStateContainer<number>;
|
||||
currentFloorPriceCalculationSet: IAsyncStateContainer<IFloorPriceCalculationResultSet>;
|
||||
}
|
||||
|
||||
const initialAuctionAssignmentState: IAuctionAssignmentState = {
|
||||
loadedAuctionAssignments: [],
|
||||
selectedAuctionAssignmentId: {
|
||||
state: AsyncState.Inactive
|
||||
},
|
||||
currentFloorPriceCalculationSet: {
|
||||
state: AsyncState.Inactive
|
||||
}
|
||||
};
|
||||
|
||||
export const validateAuctionAssignmentPostRequest = (request: IAuctionAssignmentPostRequest): boolean => {
|
||||
if (
|
||||
!request.auctionAssignment.auctionId ||
|
||||
!request.auctionAssignment.cleaningLevel ||
|
||||
!request.auctionAssignment.legalSaleDate ||
|
||||
!request.auctionAssignment.availablePickupDate
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
!request.vehicleLocation.type ||
|
||||
request.vehicleLocation.address === "" ||
|
||||
request.vehicleLocation.city === "" ||
|
||||
request.vehicleLocation.state === "" ||
|
||||
request.vehicleLocation.zip === ""
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!request.collaterals.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const collateral of request.collaterals) {
|
||||
if (
|
||||
!collateral.bodyStyle || collateral.bodyStyle === "" ||
|
||||
collateral.vehicleType === undefined || collateral.vehicleType === null ||
|
||||
!collateral.collateralTypeId
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!request.primaryCollateralId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const copyAndInsertIntoAuctionAssignments = (auctionAssignment: IAuctionAssignment | IAuctionAssignment[], loadedAuctionAssignments: IAuctionAssignment[]): IAuctionAssignment[] => {
|
||||
const clonedAuctionAssignments: IAuctionAssignment[] = cloneDeep(loadedAuctionAssignments);
|
||||
|
||||
if (Array.isArray(auctionAssignment)) {
|
||||
for (const aa of auctionAssignment) {
|
||||
mutateInsertIntoAuctionAssignments(aa, clonedAuctionAssignments);
|
||||
}
|
||||
} else {
|
||||
mutateInsertIntoAuctionAssignments(auctionAssignment, clonedAuctionAssignments);
|
||||
}
|
||||
|
||||
return clonedAuctionAssignments;
|
||||
};
|
||||
|
||||
/**
|
||||
* WARNING: This mutates the array and is probably not the method you want!
|
||||
*/
|
||||
const mutateInsertIntoAuctionAssignments = (auctionAssignmentToInsert: IAuctionAssignment, auctionAssignments: IAuctionAssignment[]): void => {
|
||||
const indexFound = auctionAssignments.findIndex((x) => x.id === auctionAssignmentToInsert.id);
|
||||
if (indexFound >= 0) {
|
||||
auctionAssignments[indexFound] = auctionAssignmentToInsert;
|
||||
} else {
|
||||
auctionAssignments.push(auctionAssignmentToInsert);
|
||||
}
|
||||
};
|
||||
|
||||
export const auctionAssignmentReducer: Reducer<IAuctionAssignmentState, AuctionAssignmentActions> = (state = initialAuctionAssignmentState, action) => {
|
||||
switch (action.type) {
|
||||
case AuctionAssignmentActionTypes.INSERT_MULTIPLE: {
|
||||
return {
|
||||
...state,
|
||||
loadedAuctionAssignments: copyAndInsertIntoAuctionAssignments(action.auctionAssignments, state.loadedAuctionAssignments)
|
||||
};
|
||||
}
|
||||
case AuctionAssignmentActionTypes.GET_FLOOR_PRICE: {
|
||||
return {
|
||||
...state,
|
||||
currentFloorPriceCalculationSet: action.floorPriceCalculationResultSet,
|
||||
};
|
||||
}
|
||||
case AuctionAssignmentActionTypes.INSERT: {
|
||||
return {
|
||||
...state,
|
||||
loadedAuctionAssignments: copyAndInsertIntoAuctionAssignments(action.auctionAssignment, state.loadedAuctionAssignments)
|
||||
};
|
||||
}
|
||||
case AuctionAssignmentActionTypes.INSERT_FROM_POST: {
|
||||
return {
|
||||
...state,
|
||||
loadedAuctionAssignments: copyAndInsertIntoAuctionAssignments(action.auctionAssignmentResponse.auctionAssignment, state.loadedAuctionAssignments)
|
||||
};
|
||||
}
|
||||
case AuctionAssignmentActionTypes.SET_SELECTED_AUCTION_ASSIGNMENT: {
|
||||
return {
|
||||
...state,
|
||||
selectedAuctionAssignmentId: action.auctionAssignmentId
|
||||
};
|
||||
}
|
||||
case LOCATION_CHANGE: {
|
||||
return {
|
||||
...state,
|
||||
selectedAuctionAssignmentId: getAuctionIdFromPayload(action.payload),
|
||||
currentFloorPriceCalculationSet: {
|
||||
state: AsyncState.Inactive
|
||||
}
|
||||
};
|
||||
}
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getAuctionIdFromPayload = (payload: LocationChangePayload): IAsyncStateContainer<number> => {
|
||||
const selectorMap = HashRouterService.createHashSelector(payload.location.hash, Routes.FloorCalculation_Decl());
|
||||
const auctionAssignmentId = selectorMap[Routes.AuctionAssignmentIdKey()];
|
||||
if (auctionAssignmentId) {
|
||||
return {
|
||||
state: AsyncState.Success,
|
||||
data: parseInt(auctionAssignmentId)
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
state: AsyncState.Inactive
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,87 @@
|
||||
import { Reducer } from "redux";
|
||||
import { AuctionHouseActions, AuctionHouseActionTypes } from "../actions/AuctionHouseActions";
|
||||
import AuctionHouseService from "../services/AuctionHouseService";
|
||||
import AsyncState from "../shared/enumerations/AsyncState";
|
||||
import IAsyncStateContainer from "../shared/types/IAsyncStateContainer";
|
||||
|
||||
export interface IAuctionHouse {
|
||||
auctionID: string;
|
||||
name: string;
|
||||
active: string;
|
||||
address_Line_1: string;
|
||||
address_Line_2: string;
|
||||
city: string;
|
||||
state: string;
|
||||
zip: number;
|
||||
country: string;
|
||||
phone: string;
|
||||
fax: string;
|
||||
latitude?: number;
|
||||
longitude?: number;
|
||||
}
|
||||
|
||||
export interface IAuctionHouseState {
|
||||
auctionHouses: IAsyncStateContainer<IAuctionHouse[]>;
|
||||
postedAuctionHouse: IAsyncStateContainer<IAuctionHouse>;
|
||||
}
|
||||
|
||||
const initialAuctionHouseState: IAuctionHouseState = {
|
||||
auctionHouses: {
|
||||
state: AsyncState.Inactive,
|
||||
},
|
||||
postedAuctionHouse : {
|
||||
state: AsyncState.Inactive
|
||||
}
|
||||
};
|
||||
|
||||
export const auctionHouseReducer: Reducer<IAuctionHouseState, AuctionHouseActions> = (state = initialAuctionHouseState, action) => {
|
||||
switch (action.type) {
|
||||
case AuctionHouseActionTypes.GET_AUCTION_HOUSES: {
|
||||
return {
|
||||
...state,
|
||||
auctionHouses: action.auctionHouses,
|
||||
};
|
||||
}
|
||||
case AuctionHouseActionTypes.POST_AUCTION_HOUSE: {
|
||||
if(action.auctionHouse.data && action.auctionHouse.state === AsyncState.Success){
|
||||
return {
|
||||
auctionHouses:{
|
||||
data:AuctionHouseService.CopyAndInsertIntoAuctionHouses(state.auctionHouses.data,action.auctionHouse.data),
|
||||
state: AsyncState.Inactive
|
||||
} ,
|
||||
postedAuctionHouse:{
|
||||
data: action.auctionHouse.data,
|
||||
state: AsyncState.Success
|
||||
}
|
||||
};
|
||||
}else{
|
||||
return{
|
||||
...state,
|
||||
postedAuctionHouse: action.auctionHouse
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
case AuctionHouseActionTypes.PUT_AUCTION_HOUSE: {
|
||||
if(action.auctionHouse.data && action.auctionHouse.state === AsyncState.Success){
|
||||
return {
|
||||
auctionHouses:{
|
||||
data:AuctionHouseService.CopyAndUpdateAuctionHouse(state.auctionHouses.data,action.auctionHouse.data),
|
||||
state: AsyncState.Inactive
|
||||
} ,
|
||||
postedAuctionHouse:{
|
||||
data: action.auctionHouse.data,
|
||||
state: AsyncState.Success
|
||||
}
|
||||
};
|
||||
}else{
|
||||
return{
|
||||
...state,
|
||||
postedAuctionHouse: action.auctionHouse
|
||||
};
|
||||
}
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,102 @@
|
||||
import { Moment } from "moment";
|
||||
import { Reducer } from "redux";
|
||||
import {CollectionsAccountActions, CollectionsAccountActionTypes} from "../actions/CollectionsAccountActions";
|
||||
import { IRepoAccount } from "../services/service-types/RepoAccountTypes";
|
||||
import AsyncState from "../shared/enumerations/AsyncState";
|
||||
import IAsyncStateContainer from "../shared/types/IAsyncStateContainer";
|
||||
|
||||
export interface ICollectionsAccount {
|
||||
accountId : number;
|
||||
accountIdentifier: string;
|
||||
accountNumber: string;
|
||||
accountStatus: string;
|
||||
accountTypeId: number;
|
||||
activeFlag: boolean;
|
||||
amountLastPaid?: number;
|
||||
balance?: number;
|
||||
bankruptcyFlag: boolean;
|
||||
branch: string;
|
||||
dueDate: Moment;
|
||||
firstPaymentDueDate: Moment;
|
||||
originalLoanAmount?: number;
|
||||
originalLoanDate: Moment;
|
||||
dqAmount: number;
|
||||
daysDq?: number;
|
||||
cases: ICollectionsCase[];
|
||||
}
|
||||
|
||||
export interface ICollectionsCase {
|
||||
caseId: number;
|
||||
caseNumber: string;
|
||||
caseDescription: string;
|
||||
caseSTageId: number;
|
||||
repoStatus: number;
|
||||
caseStatusId: number;
|
||||
caseTitle: string;
|
||||
caseTypeBasisId: number;
|
||||
caseTypeId: number;
|
||||
closedDate?: Moment;
|
||||
createDate: Moment;
|
||||
createdUserId: number;
|
||||
currentStageDate?: Moment;
|
||||
currentStatusDate?: Moment;
|
||||
lastReviewDate?: Moment;
|
||||
nextReviewDate?: Moment;
|
||||
stage1CompletionDate?: Moment;
|
||||
stage2CompletionDate?: Moment;
|
||||
stage3CompletionDate?: Moment;
|
||||
stage4CompletionDate?: Moment;
|
||||
stage5CompletionDate?: Moment;
|
||||
}
|
||||
|
||||
export interface ICollectionsAccountState{
|
||||
collectionsAccount: IAsyncStateContainer<ICollectionsAccount[]>;
|
||||
searchPhrase: string;
|
||||
associatedRepoAccounts: IAsyncStateContainer<IRepoAccount[]>;
|
||||
didAddToQueue: IAsyncStateContainer <boolean>;
|
||||
}
|
||||
|
||||
const initialCollectionsAccountState: ICollectionsAccountState = {
|
||||
collectionsAccount: {
|
||||
state: AsyncState.Inactive
|
||||
},
|
||||
searchPhrase: "",
|
||||
associatedRepoAccounts: {
|
||||
state:AsyncState.Inactive
|
||||
},
|
||||
didAddToQueue: {
|
||||
state: AsyncState.Inactive,
|
||||
data: false
|
||||
}
|
||||
};
|
||||
|
||||
export const collectionsAccountReducer: Reducer<ICollectionsAccountState, CollectionsAccountActions> = (state = initialCollectionsAccountState, action) => {
|
||||
switch (action.type) {
|
||||
case CollectionsAccountActionTypes.GET_COLLECTIONS_ACCOUNT: {
|
||||
return {
|
||||
...state,
|
||||
collectionsAccount: action.collectionsAccount,
|
||||
};
|
||||
}
|
||||
case CollectionsAccountActionTypes.SET_SEARCH_PHRASE: {
|
||||
return {
|
||||
...state,
|
||||
searchPhrase: action.searchPhrase
|
||||
};
|
||||
}
|
||||
case CollectionsAccountActionTypes.FIND_ASSOCIATED_REPOS: {
|
||||
return {
|
||||
...state,
|
||||
associatedRepoAccounts: action.associatedRepoAccounts
|
||||
};
|
||||
}
|
||||
case CollectionsAccountActionTypes.PUT_ACCOUNT_ON_QUEUE: {
|
||||
return {
|
||||
...state,
|
||||
didAddToQueue: action.didAddToQueue
|
||||
};
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Reducer } from "redux";
|
||||
import { FactoidActions, FactoidActionTypes } from "../actions/FactoidActions";
|
||||
import { IFactoid } from "../services/service-types/FactoidTypes";
|
||||
import IAsyncStateContainer from "../shared/types/IAsyncStateContainer";
|
||||
import AsyncState from "../shared/enumerations/AsyncState";
|
||||
|
||||
export interface IFactoidState {
|
||||
readonly factoids: IAsyncStateContainer<IFactoid[]>;
|
||||
readonly selectedFactoidIndex: number;
|
||||
}
|
||||
|
||||
const initialFactoidState: IFactoidState = {
|
||||
factoids: {
|
||||
state: AsyncState.Inactive
|
||||
},
|
||||
selectedFactoidIndex: 0
|
||||
};
|
||||
|
||||
export const factoidReducer: Reducer<IFactoidState, FactoidActions> = (state = initialFactoidState, action) => {
|
||||
switch (action.type) {
|
||||
case FactoidActionTypes.SET_SELECTED_FACTOID_INDEX: {
|
||||
return {
|
||||
...state,
|
||||
selectedFactoidIndex: action.index
|
||||
};
|
||||
}
|
||||
case FactoidActionTypes.GET_ALL_FACTOIDS: {
|
||||
return {
|
||||
...state,
|
||||
factoids: action.factoids
|
||||
};
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,116 @@
|
||||
import moment from "moment";
|
||||
import { AuctionAssignmentActionTypes, IAuctionAssignmentInsertFromPost } from "../actions/AuctionAssignmentActions";
|
||||
import {IRepoAccountInsertCollateral, RepoAccountActionTypes} from "../actions/RepoAccountActions";
|
||||
import { ICollateral, IRepoAccount, IRepoAccountMap } from "../services/service-types/RepoAccountTypes";
|
||||
import AsyncState from "../shared/enumerations/AsyncState";
|
||||
import { IAuctionAssignmentPostRequest } from "./AuctionAssignmentReducer";
|
||||
import {IRepoAccountState, repoAccountReducer} from "./RepoAccountReducer";
|
||||
|
||||
const buildTestRepoAccount = (repoId: number, collaterals: ICollateral[]): IRepoAccount => {
|
||||
return {
|
||||
repoId: repoId,
|
||||
collaterals: collaterals,
|
||||
accountNo: "accountNo",
|
||||
status: "status",
|
||||
memberNo: 1,
|
||||
loanNo: 1,
|
||||
accountTypeCode: "code",
|
||||
branch: 1,
|
||||
balance: 1,
|
||||
delinquentAmount: 1,
|
||||
daysDelinquent: 1,
|
||||
requiredPaymentAmount: 1,
|
||||
origAmount: 1,
|
||||
primaryIndividualId: "id",
|
||||
borrowers: [],
|
||||
repoStatusChanges: []
|
||||
};
|
||||
};
|
||||
|
||||
describe("Repo account reducer", () => {
|
||||
|
||||
const getDummRepoAccountSate = (): IRepoAccountState => {
|
||||
return {
|
||||
repoAccounts: {},
|
||||
filteredAccounts: {
|
||||
state: AsyncState.Inactive
|
||||
},
|
||||
selectedRepoId: {
|
||||
state: AsyncState.Inactive
|
||||
},
|
||||
activeAccountFilters: {
|
||||
state: AsyncState.Inactive,
|
||||
data: {}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
it("Returns nothing when state has no accounts", () => {
|
||||
// arrange
|
||||
const action: any = {type: "SOME_UNRELATED_ACTION_TYPE"};
|
||||
|
||||
// act
|
||||
const newState = repoAccountReducer(getDummRepoAccountSate(), action);
|
||||
|
||||
// assert
|
||||
expect(newState.repoAccounts).toEqual({});
|
||||
});
|
||||
|
||||
it("Returns new state when account is marked retitled", () => {
|
||||
// arrange state
|
||||
const collateral: ICollateral = {collateralId: 1, repoId: 1, vin: "vin", make: "make", model: "model", year: 2019, repoCompanySelections: []};
|
||||
const state: IRepoAccountState = {
|
||||
...getDummRepoAccountSate(),
|
||||
repoAccounts: {
|
||||
"1": buildTestRepoAccount(1, [collateral])
|
||||
}
|
||||
};
|
||||
|
||||
// arrange action
|
||||
const marked = Object.assign({}, collateral, {retitledInLandmarkName: moment()});
|
||||
const action: IRepoAccountInsertCollateral = {
|
||||
type: RepoAccountActionTypes.INSERT_COLLATERAL,
|
||||
collateral: marked
|
||||
};
|
||||
|
||||
// act
|
||||
const newState = repoAccountReducer(state, action);
|
||||
|
||||
// assert
|
||||
const collaterals = newState.repoAccounts[1].collaterals;
|
||||
expect(collaterals.length).toEqual(1);
|
||||
expect(collaterals[0]).toEqual(marked);
|
||||
expect(collaterals[0]).not.toEqual(collateral);
|
||||
});
|
||||
|
||||
it("Update from AuctionAssignmet Post Request", () => {
|
||||
const collateral: ICollateral = {collateralId: 1, repoId: 1, vin: "vin", make: "make", model: "model", year: 2019, repoCompanySelections: []};
|
||||
const state: IRepoAccountState = {
|
||||
...getDummRepoAccountSate(),
|
||||
repoAccounts: {
|
||||
"1": buildTestRepoAccount(1, [collateral])
|
||||
}
|
||||
};
|
||||
|
||||
// arrange action
|
||||
const newMakeCollateral = Object.assign({}, collateral, {make: "new make"});
|
||||
const auctionAssignmentPostRequest: IAuctionAssignmentPostRequest = {
|
||||
primaryCollateralId: 0,
|
||||
auctionAssignment: {} as any,
|
||||
vehicleLocation: {} as any,
|
||||
collaterals: [
|
||||
newMakeCollateral,
|
||||
],
|
||||
};
|
||||
const action: IAuctionAssignmentInsertFromPost = {type: AuctionAssignmentActionTypes.INSERT_FROM_POST, auctionAssignmentResponse: auctionAssignmentPostRequest};
|
||||
|
||||
// act
|
||||
const newState = repoAccountReducer(state, action);
|
||||
|
||||
// assert
|
||||
const collaterals = newState.repoAccounts[1].collaterals;
|
||||
expect(collaterals.length).toEqual(1);
|
||||
expect(collaterals[0]).toEqual(newMakeCollateral);
|
||||
expect(collaterals[0]).not.toEqual(collateral);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,119 @@
|
||||
import {Reducer} from 'redux';
|
||||
import {RepoAccountActions, RepoAccountActionTypes} from "../actions/RepoAccountActions";
|
||||
import IAsyncStateContainer from '../shared/types/IAsyncStateContainer';
|
||||
import AsyncState from '../shared/enumerations/AsyncState';
|
||||
import { AuctionAssignmentActionTypes } from '../actions/AuctionAssignmentActions';
|
||||
import { IRepoAccountMap, IRepoAccountFilters } from '../services/service-types/RepoAccountTypes';
|
||||
import RepoAccountService from '../services/RepoAccountService';
|
||||
import { LOCATION_CHANGE, LocationChangePayload } from 'connected-react-router';
|
||||
import Routes from '../shared/Routes';
|
||||
import HashRouterService from '../services/HashRouterService';
|
||||
import { IPagedResponse } from '../shared/types/IPagedResponse';
|
||||
|
||||
export interface IRepoAccountState {
|
||||
readonly repoAccounts: IRepoAccountMap;
|
||||
readonly filteredAccounts: IAsyncStateContainer<IPagedResponse<number>>;
|
||||
readonly selectedRepoId: IAsyncStateContainer<number>;
|
||||
readonly accountFilters: IRepoAccountFilters;
|
||||
readonly searchPhrase: string;
|
||||
}
|
||||
|
||||
const initialRepoAccountState: IRepoAccountState = {
|
||||
repoAccounts: {},
|
||||
filteredAccounts: {
|
||||
state: AsyncState.Inactive,
|
||||
data: {
|
||||
items: [],
|
||||
totalItems: 0,
|
||||
currentPage: 1,
|
||||
totalPages: 1
|
||||
}
|
||||
},
|
||||
selectedRepoId: {
|
||||
state: AsyncState.Inactive,
|
||||
},
|
||||
accountFilters: {onlyOpenAccounts: true},
|
||||
searchPhrase: ''
|
||||
};
|
||||
|
||||
export const repoAccountReducer: Reducer<IRepoAccountState, RepoAccountActions> = (state = initialRepoAccountState, action) => {
|
||||
switch (action.type) {
|
||||
case RepoAccountActionTypes.INSERT_ACCOUNTS: {
|
||||
return {
|
||||
...state,
|
||||
repoAccounts: RepoAccountService.CreateAmmendedRepoAccountMap(state.repoAccounts, action.repoAccounts),
|
||||
};
|
||||
}
|
||||
case RepoAccountActionTypes.INSERT_ONE_ACCOUNT: {
|
||||
return {
|
||||
...state,
|
||||
repoAccounts: RepoAccountService.CreateAmmendedRepoAccountMap(state.repoAccounts, action.repoAccount),
|
||||
};
|
||||
}
|
||||
case RepoAccountActionTypes.SET_SELECTED_ACCOUNT: {
|
||||
return {
|
||||
...state,
|
||||
selectedRepoId: action.repoId,
|
||||
};
|
||||
}
|
||||
case RepoAccountActionTypes.SET_FILTERED_ACCOUNTS: {
|
||||
return {
|
||||
...state,
|
||||
filteredAccounts: action.filteredAccounts
|
||||
};
|
||||
}
|
||||
case RepoAccountActionTypes.SET_FILTERS: {
|
||||
return {
|
||||
...state,
|
||||
accountFilters: action.filters
|
||||
};
|
||||
}
|
||||
case RepoAccountActionTypes.INSERT_COLLATERAL: {
|
||||
return {
|
||||
...state,
|
||||
repoAccounts: RepoAccountService.CreateUpdatedRepoAccountsFromCollateral(state.repoAccounts, action.collateral),
|
||||
};
|
||||
}
|
||||
case RepoAccountActionTypes.UPDATE_REFUND_STATUS: {
|
||||
return {
|
||||
...state,
|
||||
repoAccounts: RepoAccountService.CreateUpdatedRepoAccountsFromRefundStatus(state.repoAccounts, action.refundStatus)
|
||||
}
|
||||
}
|
||||
case RepoAccountActionTypes.SET_SEARCH_PHRASE: {
|
||||
return {
|
||||
...state,
|
||||
searchPhrase: action.searchPhrase
|
||||
}
|
||||
}
|
||||
case AuctionAssignmentActionTypes.INSERT_FROM_POST: {
|
||||
return {
|
||||
...state,
|
||||
repoAccounts: RepoAccountService.CreateUpdatedRepoAccountsFromCollateral(state.repoAccounts, action.auctionAssignmentResponse.collaterals),
|
||||
}
|
||||
}
|
||||
case LOCATION_CHANGE: {
|
||||
return {
|
||||
...state,
|
||||
selectedRepoId: getRepoIdFromPayload(action.payload)
|
||||
}
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
const getRepoIdFromPayload = (payload: LocationChangePayload): IAsyncStateContainer<number> => {
|
||||
const selectorMap = HashRouterService.createHashSelector(payload.location.hash, Routes.Account_Decl());
|
||||
const repoId = selectorMap[Routes.RepoIdKey()];
|
||||
if (repoId) {
|
||||
return {
|
||||
state: AsyncState.Success,
|
||||
data: parseInt(repoId)
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
state: AsyncState.Inactive
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import { Moment } from "moment";
|
||||
import { Reducer } from "redux";
|
||||
import { RepoCompanyActions, RepoCompanyActionTypes } from "../actions/RepoCompanyActions";
|
||||
import AsyncState from "../shared/enumerations/AsyncState";
|
||||
import IAsyncStateContainer from "../shared/types/IAsyncStateContainer";
|
||||
|
||||
export interface IRepoCompany {
|
||||
id: number;
|
||||
referenceId?: number;
|
||||
name: string;
|
||||
email?: string;
|
||||
address_Line_1?: string;
|
||||
address_Line_2?: string;
|
||||
city?: string;
|
||||
state?: string;
|
||||
zip?: string;
|
||||
country?: string;
|
||||
phone?: string;
|
||||
fax?: string;
|
||||
readonly disabledBy?: string;
|
||||
disabledDate?: Moment;
|
||||
}
|
||||
|
||||
export interface IRepoCompanyState {
|
||||
repoCompanies: IAsyncStateContainer<IRepoCompany[]>;
|
||||
}
|
||||
|
||||
const initialCompanyState: IRepoCompanyState = {
|
||||
repoCompanies: {
|
||||
state: AsyncState.Inactive
|
||||
}
|
||||
};
|
||||
|
||||
const concatRepoCompanies = (repoCompanies: IAsyncStateContainer<IRepoCompany[]>, newRepoCompany: IRepoCompany): IAsyncStateContainer<IRepoCompany[]> => {
|
||||
if (!repoCompanies.data) {
|
||||
return {
|
||||
state: repoCompanies.state,
|
||||
data: [newRepoCompany]
|
||||
};
|
||||
}
|
||||
|
||||
const copiedRepoCompanies = repoCompanies.data.concat([]);
|
||||
const indexOfCompany = copiedRepoCompanies.findIndex((repoCompany) => repoCompany.id === newRepoCompany.id);
|
||||
if (indexOfCompany >= 0) {
|
||||
copiedRepoCompanies[indexOfCompany] = newRepoCompany;
|
||||
} else {
|
||||
copiedRepoCompanies.push(newRepoCompany);
|
||||
}
|
||||
|
||||
return {
|
||||
state: repoCompanies.state,
|
||||
data: copiedRepoCompanies
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* NOTE: This mutates the parameter
|
||||
*/
|
||||
const sortRepoCompanies = (repoCompanies: IAsyncStateContainer<IRepoCompany[]>): void => {
|
||||
if (repoCompanies.data) {
|
||||
repoCompanies.data.sort((a, b) =>
|
||||
a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 0
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const repoCompanyReducer: Reducer<IRepoCompanyState, RepoCompanyActions> = (state = initialCompanyState, action) => {
|
||||
switch (action.type) {
|
||||
case RepoCompanyActionTypes.SET_REPO_COMPANIES: {
|
||||
sortRepoCompanies(action.repoCompanies);
|
||||
return {
|
||||
repoCompanies: action.repoCompanies
|
||||
};
|
||||
}
|
||||
case RepoCompanyActionTypes.INSERT_SINGLE_REPO_COMPANY: {
|
||||
const updatedRepoCompanies = concatRepoCompanies(state.repoCompanies, action.repoCompany);
|
||||
sortRepoCompanies(updatedRepoCompanies);
|
||||
return {
|
||||
repoCompanies: updatedRepoCompanies
|
||||
};
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
import { getCompareValueForCells } from "./ReportingReducer";
|
||||
|
||||
describe("Reporting Reducer", () => {
|
||||
|
||||
it("getCompareValueForCells() Sorts Two Decimal Values", () => {
|
||||
const value = getCompareValueForCells(
|
||||
{ display: "Shouldn't Matter", value: 1 },
|
||||
{ display: "Shouldn't Matter", value: -1 }
|
||||
);
|
||||
|
||||
expect(value).toBe(2);
|
||||
});
|
||||
|
||||
it("getCompareValueForCells() Sorts Valid / null Values (1)", () => {
|
||||
const value = getCompareValueForCells(
|
||||
{ display: "Shouldn't Matter", value: null },
|
||||
{ display: "Shouldn't Matter", value: -2 }
|
||||
);
|
||||
|
||||
expect(value).toBe(-1);
|
||||
});
|
||||
|
||||
it("getCompareValueForCells() Sorts Valid / null Values (2)", () => {
|
||||
const value = getCompareValueForCells(
|
||||
{ display: "Shouldn't Matter", value: -2 },
|
||||
{ display: "Shouldn't Matter", value: null }
|
||||
);
|
||||
|
||||
expect(value).toBe(1);
|
||||
});
|
||||
|
||||
it("getCompareValueForCells() Sorts Valid / undefined Values (1)", () => {
|
||||
const value = getCompareValueForCells(
|
||||
{ display: "Shouldn't Matter" },
|
||||
{ display: "Shouldn't Matter", value: -3 }
|
||||
);
|
||||
|
||||
expect(value).toBe(-1);
|
||||
});
|
||||
|
||||
it("getCompareValueForCells() Sorts Valid / undefined Values (2)", () => {
|
||||
const value = getCompareValueForCells(
|
||||
{ display: "Shouldn't Matter", value: 5 },
|
||||
{ display: "Shouldn't Matter" }
|
||||
);
|
||||
|
||||
expect(value).toBe(1);
|
||||
});
|
||||
|
||||
it("getCompareValueForCells() Sorts Display", () => {
|
||||
const value = getCompareValueForCells(
|
||||
{ display: "B" },
|
||||
{ display: "a" }
|
||||
);
|
||||
|
||||
expect(value).toBe(1);
|
||||
});
|
||||
|
||||
it("getCompareValueForCells() Sorts String Value", () => {
|
||||
const value = getCompareValueForCells(
|
||||
{ display: "B", value: "a" },
|
||||
{ display: "a", value: "B" }
|
||||
);
|
||||
|
||||
expect(value).toBe(-1);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,94 @@
|
||||
import moment, {Moment} from "moment";
|
||||
import {Reducer} from "redux";
|
||||
import {ReportingActions, ReportingActionTypes} from "../actions/ReportingActions";
|
||||
import AsyncState from "../shared/enumerations/AsyncState";
|
||||
import IAsyncStateContainer from "../shared/types/IAsyncStateContainer";
|
||||
|
||||
export interface IReportDataTableCell {
|
||||
display: string;
|
||||
value?: any;
|
||||
}
|
||||
|
||||
export interface IReportReferenceRow {
|
||||
repoId: number;
|
||||
cells: IReportDataTableCell[];
|
||||
}
|
||||
|
||||
export interface IReportTable {
|
||||
headers: string[];
|
||||
cells: IReportDataTableCell[][];
|
||||
}
|
||||
|
||||
export interface IDataTable {
|
||||
headers: string[];
|
||||
rows: IReportReferenceRow[];
|
||||
}
|
||||
|
||||
export interface IReport {
|
||||
reportTable?: IReportTable;
|
||||
dataTable: IDataTable;
|
||||
}
|
||||
|
||||
export interface IReportingState {
|
||||
reportForm: IAdminReportForm;
|
||||
currentReport: IAsyncStateContainer<IReport>;
|
||||
}
|
||||
|
||||
export interface IReportUrlDisplayMap {
|
||||
display: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface IAdminReportForm {
|
||||
reportUrl?: IReportUrlDisplayMap;
|
||||
lowDate?: Moment;
|
||||
highDate?: Moment;
|
||||
}
|
||||
|
||||
export const getCompareValueForCells = (a: IReportDataTableCell, b: IReportDataTableCell): number => {
|
||||
const isValid = (value: any) => {
|
||||
return value !== undefined && value !== null;
|
||||
};
|
||||
|
||||
if (!isValid(a.value) && !isValid(b.value)) {
|
||||
return a.display.toLowerCase() < b.display.toLowerCase() ? -1 : 1;
|
||||
}
|
||||
|
||||
if (isValid(a.value) && isValid(b.value)) {
|
||||
if (typeof a.value === "string" && typeof b.value === "string") {
|
||||
return a.value.toLowerCase() < b.value.toLowerCase() ? -1 : 1;
|
||||
} else {
|
||||
return (a.value - b.value);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValid(a.value)) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
const initialReportingState: IReportingState = {
|
||||
reportForm: {
|
||||
lowDate: moment().subtract(1, "years"),
|
||||
highDate: moment()
|
||||
},
|
||||
currentReport: {
|
||||
state: AsyncState.Inactive
|
||||
}
|
||||
};
|
||||
|
||||
export const reportingReducer: Reducer<IReportingState, ReportingActions> = (state = initialReportingState, action) => {
|
||||
switch (action.type) {
|
||||
case ReportingActionTypes.SET_REPORT: {
|
||||
return {
|
||||
...state,
|
||||
reportForm: action.reportForm,
|
||||
currentReport: action.currentReport,
|
||||
};
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
import { Reducer } from "redux";
|
||||
import { SavedLocationActions, SavedLocationActionTypes } from "../actions/SavedLocationActions";
|
||||
import AsyncState from "../shared/enumerations/AsyncState";
|
||||
import IAsyncStateContainer from "../shared/types/IAsyncStateContainer";
|
||||
|
||||
export interface ISavedLocation {
|
||||
id: number;
|
||||
name: string;
|
||||
locationType: number;
|
||||
address_Line_1: string;
|
||||
address_Line_2?: string;
|
||||
city: string;
|
||||
state: string;
|
||||
zip: string;
|
||||
country?: string;
|
||||
phone?: string;
|
||||
fax?: string;
|
||||
}
|
||||
|
||||
export interface ISavedLocationState {
|
||||
savedLocations: IAsyncStateContainer<ISavedLocation[]>;
|
||||
}
|
||||
|
||||
const initialLocationState: ISavedLocationState = {
|
||||
savedLocations: {
|
||||
state: AsyncState.Inactive
|
||||
}
|
||||
};
|
||||
|
||||
const concatSavedLocations = (savedLocations: IAsyncStateContainer<ISavedLocation[]>, newSavedLocation: ISavedLocation): IAsyncStateContainer<ISavedLocation[]> => {
|
||||
if (!savedLocations.data) {
|
||||
return {
|
||||
state: savedLocations.state,
|
||||
data: [newSavedLocation]
|
||||
};
|
||||
}
|
||||
|
||||
const copiedSavedLocations = savedLocations.data.concat([]);
|
||||
const indexOfLocation = copiedSavedLocations.findIndex((savedLocation) => savedLocation.id === newSavedLocation.id);
|
||||
if (indexOfLocation >= 0) {
|
||||
copiedSavedLocations[indexOfLocation] = newSavedLocation;
|
||||
} else {
|
||||
copiedSavedLocations.push(newSavedLocation);
|
||||
}
|
||||
|
||||
return {
|
||||
state: savedLocations.state,
|
||||
data: copiedSavedLocations
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* NOTE: This mutates the parameter
|
||||
*/
|
||||
const sortSavedLocations = (savedLocations: IAsyncStateContainer<ISavedLocation[]>): void => {
|
||||
if (savedLocations.data) {
|
||||
savedLocations.data.sort((a, b) =>
|
||||
a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 0
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const savedLocationReducer: Reducer<ISavedLocationState, SavedLocationActions> = (state = initialLocationState, action) => {
|
||||
switch (action.type) {
|
||||
case SavedLocationActionTypes.SET_SAVED_LOCATIONS: {
|
||||
sortSavedLocations(action.savedLocations);
|
||||
return {
|
||||
savedLocations: action.savedLocations
|
||||
};
|
||||
}
|
||||
case SavedLocationActionTypes.INSERT_SINGLE_SAVED_LOCATION: {
|
||||
const updatedSavedLocations = concatSavedLocations(state.savedLocations, action.savedLocation);
|
||||
sortSavedLocations(updatedSavedLocations);
|
||||
return {
|
||||
savedLocations: updatedSavedLocations
|
||||
};
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
import {UserInfo} from "react-adal";
|
||||
import {Reducer} from "redux";
|
||||
import {SecurityActions, SecurityActionTypes} from "../actions/SecurityActions";
|
||||
|
||||
export interface ISecurityState {
|
||||
readonly userInfo: UserInfo;
|
||||
readonly userToken: string;
|
||||
}
|
||||
|
||||
const initialSecurityState: ISecurityState = {
|
||||
userInfo: {
|
||||
profile: {},
|
||||
userName: "Unknown"
|
||||
},
|
||||
userToken: "invalid"
|
||||
};
|
||||
|
||||
export const securityReducer: Reducer<ISecurityState, SecurityActions> = (state = initialSecurityState, action) => {
|
||||
switch (action.type) {
|
||||
case SecurityActionTypes.APPLY_USER: {
|
||||
return {
|
||||
...state,
|
||||
userInfo: action.user
|
||||
};
|
||||
}
|
||||
case SecurityActionTypes.APPLY_TOKEN: {
|
||||
return {
|
||||
...state,
|
||||
userToken: action.userToken
|
||||
};
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
import { ICollateralType, ICollateralTypesToVehicleType, getCollateralTypesFromVehicleType, IAllowedStatusChange, getViableStatuses } from "./SettingsReducer";
|
||||
|
||||
describe('Settings Reducer', () => {
|
||||
const collateralTypes: ICollateralType[] = [
|
||||
{
|
||||
id: 1,
|
||||
description: 'Collateral Type 1',
|
||||
lookupHandler: '',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
description: 'Collateral Type 4',
|
||||
lookupHandler: '',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
description: 'Collateral Type 2',
|
||||
lookupHandler: '',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
description: 'Collateral Type 3',
|
||||
lookupHandler: '',
|
||||
},
|
||||
];
|
||||
const collateralTypeToVehicleTypes: ICollateralTypesToVehicleType[] = [
|
||||
{
|
||||
id: 1,
|
||||
vehicleTypeId: 1,
|
||||
collateralTypeId: 1,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
vehicleTypeId: 1,
|
||||
collateralTypeId: 3,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
vehicleTypeId: 2,
|
||||
collateralTypeId: 10,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
vehicleTypeId: 1,
|
||||
collateralTypeId: 3,
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
vehicleTypeId: 1,
|
||||
collateralTypeId: 1,
|
||||
},
|
||||
];
|
||||
|
||||
it('Get Valid Collateral Types from Vehicle Type', () => {
|
||||
const foundCollateral = getCollateralTypesFromVehicleType(
|
||||
collateralTypeToVehicleTypes,
|
||||
collateralTypes,
|
||||
1,
|
||||
);
|
||||
expect(foundCollateral).toEqual([
|
||||
{
|
||||
id: 1,
|
||||
description: 'Collateral Type 1',
|
||||
lookupHandler: '',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
description: 'Collateral Type 3',
|
||||
lookupHandler: '',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('Get Empty Array For Un-Matched Vehciel Type', () => {
|
||||
const foundCollateral = getCollateralTypesFromVehicleType(
|
||||
collateralTypeToVehicleTypes,
|
||||
collateralTypes,
|
||||
5,
|
||||
);
|
||||
expect(foundCollateral).toEqual([]);
|
||||
});
|
||||
|
||||
it('Get Empty Array For Un-Matched Collateral Type', () => {
|
||||
const foundCollateral = getCollateralTypesFromVehicleType(
|
||||
collateralTypeToVehicleTypes,
|
||||
collateralTypes,
|
||||
2,
|
||||
);
|
||||
expect(foundCollateral).toEqual([]);
|
||||
});
|
||||
|
||||
it('Get Empty Array For Undefined Vehciel Type', () => {
|
||||
const foundCollateral = getCollateralTypesFromVehicleType(
|
||||
collateralTypeToVehicleTypes,
|
||||
collateralTypes,
|
||||
);
|
||||
expect(foundCollateral).toEqual([]);
|
||||
});
|
||||
|
||||
it ('Viable status changes produces correct relations', () => {
|
||||
const dummyAllowedStatusChanges: IAllowedStatusChange[] = [
|
||||
{
|
||||
id: 0,
|
||||
currentStatus: 'A',
|
||||
allowedChangeStatus: 'B',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
currentStatus: 'A',
|
||||
allowedChangeStatus: 'B',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
currentStatus: 'A',
|
||||
allowedChangeStatus: 'B',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
currentStatus: 'A',
|
||||
allowedChangeStatus: ' B ',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
currentStatus: 'A',
|
||||
allowedChangeStatus: 'C',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
currentStatus: 'H',
|
||||
allowedChangeStatus: 'A',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
currentStatus: 'A',
|
||||
allowedChangeStatus: 'A',
|
||||
}
|
||||
];
|
||||
|
||||
expect(getViableStatuses('A', dummyAllowedStatusChanges)).toStrictEqual(['A', 'B', 'C']);
|
||||
expect(getViableStatuses('H', dummyAllowedStatusChanges)).toStrictEqual(['H', 'A']);
|
||||
expect(getViableStatuses('F', dummyAllowedStatusChanges)).toStrictEqual(['F']);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,157 @@
|
||||
import { Reducer } from "redux";
|
||||
import { SettingsActions, SettingsActionTypes } from "../actions/SettingsActions";
|
||||
|
||||
export interface IAccountType {
|
||||
id: number;
|
||||
accountTypeCode: string;
|
||||
description: string;
|
||||
isVisible: boolean;
|
||||
categoryId: number;
|
||||
}
|
||||
|
||||
export interface IAllowedStatusChange {
|
||||
id: number;
|
||||
currentStatus: string;
|
||||
allowedChangeStatus: string;
|
||||
}
|
||||
|
||||
export interface IBodyStyle {
|
||||
code: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface ICleaningLevel {
|
||||
code: number;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface ICollateralType {
|
||||
id: number;
|
||||
description: string;
|
||||
lookupHandler: string;
|
||||
}
|
||||
|
||||
export interface ICollateralTypesToVehicleType {
|
||||
id: number;
|
||||
collateralTypeId: number;
|
||||
vehicleTypeId: number;
|
||||
}
|
||||
|
||||
export interface IHoldStatus {
|
||||
code: number;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface ILoanCategory {
|
||||
id: number;
|
||||
categoryName: string;
|
||||
}
|
||||
|
||||
export interface ILocationType {
|
||||
code: number;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface IVehicleType {
|
||||
code: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface IBranchSetEntry {
|
||||
branchId: number;
|
||||
branchName: string;
|
||||
}
|
||||
|
||||
export interface IExternalUser {
|
||||
id: number;
|
||||
name: string;
|
||||
code: string;
|
||||
referenceId: number;
|
||||
}
|
||||
|
||||
export interface ISettingsState {
|
||||
readonly accountTypes: IAccountType[];
|
||||
readonly allowedStatusChanges: IAllowedStatusChange[];
|
||||
readonly bodyStyles: IBodyStyle[];
|
||||
readonly cleaningLevels: ICleaningLevel[];
|
||||
readonly collateralTypes: ICollateralType[];
|
||||
readonly collateralTypesToVehicleTypes: ICollateralTypesToVehicleType[];
|
||||
readonly holdStatuses: IHoldStatus[];
|
||||
readonly loanCategories: ILoanCategory[];
|
||||
readonly locationTypes: ILocationType[];
|
||||
readonly vehicleTypes: IVehicleType[];
|
||||
readonly branchSet: IBranchSetEntry[];
|
||||
readonly externalUsers: IExternalUser[];
|
||||
readonly repoTypeMap: string[];
|
||||
readonly transactionTypeCodes: string[];
|
||||
readonly transactionTypeCodeQueryPrefix: string;
|
||||
readonly transactionHistoryParameterDateFormat: string;
|
||||
readonly transactionHistoryStartDateOffset: number;
|
||||
}
|
||||
|
||||
const initialSettingsState: { holdStatuses: any[]; loanCategories: any[]; vehicleTypes: any[]; bodyStyles: any[]; externalUsers: any[]; allowedStatusChanges: any[]; cleaningLevels: any[]; branchSet: any[]; collateralTypes: any[]; repoTypeMap: any[]; locationTypes: any[]; collateralTypesToVehicleTypes: any[]; accountTypes: any[]; transactionTypeCodes: any[]; transactionTypeCodeQueryPrefix: any; transactionHistoryParameterDateFormat: any; transactionHistoryStartDateOffset: any } = {
|
||||
accountTypes: [],
|
||||
allowedStatusChanges: [],
|
||||
bodyStyles: [],
|
||||
cleaningLevels: [],
|
||||
collateralTypes: [],
|
||||
collateralTypesToVehicleTypes: [],
|
||||
holdStatuses: [],
|
||||
loanCategories: [],
|
||||
locationTypes: [],
|
||||
vehicleTypes: [],
|
||||
branchSet: [],
|
||||
externalUsers: [],
|
||||
repoTypeMap: [],
|
||||
transactionTypeCodes: [],
|
||||
transactionTypeCodeQueryPrefix: "",
|
||||
transactionHistoryParameterDateFormat: "",
|
||||
transactionHistoryStartDateOffset: 0
|
||||
};
|
||||
|
||||
export const getViableStatuses = (currentStatus: string, allowedStatusChanges: IAllowedStatusChange[]) => {
|
||||
currentStatus = currentStatus.trim();
|
||||
const viableStatuses: string[] = [currentStatus];
|
||||
for (const allowedStatusChange of allowedStatusChanges) {
|
||||
if (allowedStatusChange.currentStatus.trim() === currentStatus && viableStatuses.indexOf(allowedStatusChange.allowedChangeStatus.trim()) === -1) {
|
||||
viableStatuses.push(allowedStatusChange.allowedChangeStatus);
|
||||
}
|
||||
}
|
||||
return viableStatuses;
|
||||
};
|
||||
|
||||
export const getCollateralTypesFromVehicleType = (collateralToVehicleTypes: ICollateralTypesToVehicleType[], collateralTypes: ICollateralType[], vehicleTypeCode?: number) => {
|
||||
if (vehicleTypeCode === undefined || vehicleTypeCode === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const uniqueCollateralTypeIDs: number[] = [];
|
||||
for (const c2v of collateralToVehicleTypes) {
|
||||
if (
|
||||
c2v.vehicleTypeId === vehicleTypeCode &&
|
||||
uniqueCollateralTypeIDs.indexOf(c2v.collateralTypeId) === -1
|
||||
) {
|
||||
uniqueCollateralTypeIDs.push(c2v.collateralTypeId);
|
||||
}
|
||||
}
|
||||
|
||||
const matchedCollateralTypes: ICollateralType[] = [];
|
||||
for (const collateralTypeID of uniqueCollateralTypeIDs) {
|
||||
const foundCollateralType = collateralTypes.find((ct => ct.id === collateralTypeID));
|
||||
if (foundCollateralType) {
|
||||
matchedCollateralTypes.push(foundCollateralType);
|
||||
}
|
||||
}
|
||||
|
||||
return matchedCollateralTypes;
|
||||
};
|
||||
|
||||
export const settingsReducer: Reducer<ISettingsState, SettingsActions> = (state = initialSettingsState, action) => {
|
||||
switch (action.type) {
|
||||
case SettingsActionTypes.GET_SETTINGS: {
|
||||
return action.settings;
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
import { Reducer } from 'redux';
|
||||
import { VehicleFloorPricingParametersActions, VehicleFloorPricingParametersActionTypes } from "../actions/VehicleFloorPricingParametersActions";
|
||||
|
||||
export interface IVehicleFloorPricingParameters {
|
||||
id: number;
|
||||
lowCondition?: number;
|
||||
highCondition?: number;
|
||||
nadaFirstValue?: string;
|
||||
nadaSecondValue?: string;
|
||||
mmrAdjustedValue?: string;
|
||||
percentageOf?: number;
|
||||
}
|
||||
|
||||
export interface IVehicleFloorPricingParametersState {
|
||||
readonly vehicleFloorPricingParameters: IVehicleFloorPricingParameters[];
|
||||
}
|
||||
|
||||
export interface VehicleFloorPricingParametersPutRequest {
|
||||
vehicleFloorPricingParameters: IVehicleFloorPricingParameters[]
|
||||
}
|
||||
|
||||
const initialVehicleFloorPricingParametersState: IVehicleFloorPricingParametersState = {
|
||||
vehicleFloorPricingParameters: []
|
||||
}
|
||||
|
||||
export const vehicleFloorPricingParametersReducer: Reducer<IVehicleFloorPricingParametersState, VehicleFloorPricingParametersActions> = (state = initialVehicleFloorPricingParametersState, action) => {
|
||||
switch (action.type) {
|
||||
case VehicleFloorPricingParametersActionTypes.GET_VEHICLE_FLOOR_PRICING_PARAMETERS: {
|
||||
return {
|
||||
...state,
|
||||
vehicleFloorPricingParameters: action.vehicleFloorPricingParameters
|
||||
};
|
||||
}
|
||||
case VehicleFloorPricingParametersActionTypes.PUT_VEHICLE_FLOOR_PRICING_PARAMETERS: {
|
||||
return {
|
||||
...state,
|
||||
vehicleFloorPricingParameters: action.vehicleFloorPricingParameters
|
||||
};
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user