Adding
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
export default class Routes {
|
||||
|
||||
public static Root(): string {
|
||||
return "/";
|
||||
}
|
||||
|
||||
public static Accounts(): string {
|
||||
return this.Root() + this._account + "/";
|
||||
}
|
||||
|
||||
public static Account(repoId: number): string {
|
||||
return this.Accounts() + repoId + "/";
|
||||
}
|
||||
|
||||
public static Admin(): string {
|
||||
return this.Root() + "Admin/";
|
||||
}
|
||||
|
||||
public static Reporting(): string {
|
||||
return this.Root() + "Reporting/";
|
||||
}
|
||||
|
||||
public static FloorCalculation(repoId: number, auctionAssignmentId: number): string {
|
||||
return this.Account(repoId) + this._floorCalculation + "/" + auctionAssignmentId + "/";
|
||||
}
|
||||
|
||||
public static FloorPricing(): string {
|
||||
return this.Root() + "FloorPricing/";
|
||||
}
|
||||
|
||||
public static AdminAuctionHouses(): string {
|
||||
return this.Root() + "AuctionHouses/";
|
||||
}
|
||||
|
||||
public static AdminAccountLookup(): string {
|
||||
return this.Root() + "AccountLookup"
|
||||
}
|
||||
|
||||
public static RepoIdKey(): string {
|
||||
return "repoId";
|
||||
}
|
||||
|
||||
public static Account_Decl(): string {
|
||||
return this.Accounts() + this._RepoIdKey_Decl() + "/";
|
||||
}
|
||||
|
||||
|
||||
public static AuctionAssignmentIdKey(): string {
|
||||
return "auctionAssignmentId";
|
||||
}
|
||||
|
||||
public static FloorCalculation_Decl(): string {
|
||||
return this.Account_Decl() + this._floorCalculation + "/" + this._AuctionAssignmentIdKey_Decl() + "/";
|
||||
}
|
||||
|
||||
public static AdminRepoCompanies(): string {
|
||||
return this.Root() + "RepoCompanies/";
|
||||
}
|
||||
|
||||
private static _RepoIdKey_Decl(): string {
|
||||
return ":" + this.RepoIdKey();
|
||||
}
|
||||
|
||||
private static _AuctionAssignmentIdKey_Decl(): string {
|
||||
return ":" + this.AuctionAssignmentIdKey();
|
||||
}
|
||||
|
||||
|
||||
private static _account = "Account";
|
||||
private static _floorCalculation = "FloorCalculation";
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import ArrayCalculations from "./ArrayCalculations";
|
||||
|
||||
describe('ArrayCalculations', () => {
|
||||
|
||||
it ('GetArrayIntersection() Returns Empty Array When an Empty Array is Passed', () => {
|
||||
expect(ArrayCalculations.GetArrayIntersection([])).toEqual([]);
|
||||
});
|
||||
|
||||
it ('GetArrayIntersection() Returns The Same Array When an Array of Length One (1) is Passed', () => {
|
||||
expect(ArrayCalculations.GetArrayIntersection([[4,1,5,6]])).toEqual([4,1,5,6]);
|
||||
});
|
||||
|
||||
it ('GetArrayIntersection() Returns Valid Intersection for Multiple Numerical Arrays', () => {
|
||||
const intersection = ArrayCalculations.GetArrayIntersection([
|
||||
[1,2,3,4,5,6,7,8,9],
|
||||
[5,1],
|
||||
[1,5,9],
|
||||
[9,7,5,3,1],
|
||||
]);
|
||||
expect(intersection).toEqual([1,5]);
|
||||
});
|
||||
|
||||
it ('GetArrayIntersection() Returns Valid Intersection for Multiple String Arrays', () => {
|
||||
const intersection = ArrayCalculations.GetArrayIntersection([
|
||||
['John', 'Abby', 'Chris'],
|
||||
['Chris', 'Abby'],
|
||||
['Abby']
|
||||
]);
|
||||
expect(intersection).toEqual(['Abby']);
|
||||
});
|
||||
|
||||
it ('GetArrayIntersection() Returns Empty Array for Non-Overlapping Arrays', () => {
|
||||
const intersection = ArrayCalculations.GetArrayIntersection([
|
||||
[1,2,3,4,5,6,7,8,9],
|
||||
[5,1],
|
||||
[13,12,11]
|
||||
]);
|
||||
expect(intersection).toEqual([]);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
class ArrayCalculations {
|
||||
|
||||
/**
|
||||
* Create an array that represents the intersection of all arrays.
|
||||
*
|
||||
* @param arrays The arrays to intersection
|
||||
*/
|
||||
public static GetArrayIntersection<T>(arrays: T[][]): T[] {
|
||||
if (!arrays.length) {
|
||||
return [];
|
||||
}
|
||||
if (arrays.length === 1) {
|
||||
return arrays[0];
|
||||
}
|
||||
|
||||
var currentIntersection = arrays[0];
|
||||
for (const arr of arrays) {
|
||||
currentIntersection = this.GetIntersectionOfTwoArrays(currentIntersection, arr);
|
||||
}
|
||||
|
||||
return currentIntersection;
|
||||
}
|
||||
|
||||
private static GetIntersectionOfTwoArrays<T>(array1: T[], array2: T[]): T[] {
|
||||
return array1.filter(cur => array2.indexOf(cur) !== -1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ArrayCalculations;
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Moment } from "moment";
|
||||
|
||||
export default class DateCalculations {
|
||||
/**
|
||||
* Calculate the absolute difference in days between two dates
|
||||
*
|
||||
* @param date1 The first date
|
||||
* @param date2 The second date
|
||||
*/
|
||||
public static DayDifference(date1: Moment, date2: Moment): number {
|
||||
const difference = date1.diff(date2, 'days');
|
||||
return Math.abs(difference);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import devEnvironment from "../../environments/environment.dev";
|
||||
import prodEnvironment from "../../environments/environment.prod";
|
||||
import uatEnvironment from "../../environments/environment.uat";
|
||||
|
||||
const getConfigurationFromWindowLocation = () => {
|
||||
const hostname = window.location.hostname;
|
||||
if (hostname.match('localhost') || hostname.match('qa')) {
|
||||
return devEnvironment;
|
||||
}
|
||||
if (hostname.match('uat')) {
|
||||
return uatEnvironment;
|
||||
}
|
||||
return prodEnvironment;
|
||||
}
|
||||
|
||||
const config = getConfigurationFromWindowLocation();
|
||||
|
||||
/**
|
||||
* Static class for access application specific configuration data.
|
||||
*/
|
||||
export default class Configuration {
|
||||
|
||||
static get Get() {
|
||||
return config;
|
||||
}
|
||||
|
||||
static PrefixSSRSApiURL(relativeUrl: string){
|
||||
return config.baseUrls.SSRSReportBaseUrl + relativeUrl;
|
||||
}
|
||||
|
||||
static PrefixRepoApiUrl(relativeUrl: string) {
|
||||
return config.baseUrls.repoApi + relativeUrl;
|
||||
}
|
||||
|
||||
static PrefixCoreUrl(relativeUrl: string) {
|
||||
return config.baseUrls.core + relativeUrl;
|
||||
}
|
||||
|
||||
static PrefixCollectionsUrl(relativeUrl: string) {
|
||||
return config.baseUrls.collections + relativeUrl;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Moment } from 'moment';
|
||||
import moment from 'moment';
|
||||
|
||||
export default class DateConversions {
|
||||
|
||||
public static utcToMoment(utcDateTime?: string | Moment): Moment | undefined {
|
||||
if (utcDateTime && utcDateTime !== '') {
|
||||
return moment(utcDateTime);
|
||||
}
|
||||
}
|
||||
|
||||
public static momentToUTC(momentDate?: Moment): string | undefined {
|
||||
if (momentDate) {
|
||||
return momentDate.format();
|
||||
}
|
||||
}
|
||||
|
||||
public static momentToDate(momentDate?: Moment): Date | undefined {
|
||||
if (momentDate) {
|
||||
return momentDate.toDate();
|
||||
}
|
||||
}
|
||||
|
||||
public static dateToMoment(date: Date | null | undefined): Moment | undefined {
|
||||
if (date) {
|
||||
return moment(date);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { NotificationBarType } from "../../components/notification-bar/NotificationBar";
|
||||
|
||||
enum AsyncState {
|
||||
Inactive = 'Inactive',
|
||||
Loading = 'Loading',
|
||||
Success = 'Success',
|
||||
Failure = 'Error',
|
||||
}
|
||||
|
||||
export const mapAsyncStateToNotificationType = (asyncState: AsyncState): NotificationBarType => {
|
||||
switch (asyncState) {
|
||||
case AsyncState.Inactive:
|
||||
return NotificationBarType.Info;
|
||||
case AsyncState.Loading:
|
||||
return NotificationBarType.Loading;
|
||||
case AsyncState.Success:
|
||||
return NotificationBarType.Success;
|
||||
case AsyncState.Failure:
|
||||
return NotificationBarType.Danger;
|
||||
};
|
||||
};
|
||||
|
||||
export default AsyncState;
|
||||
@@ -0,0 +1,19 @@
|
||||
import { AxiosError } from "axios";
|
||||
import { isArray } from "util";
|
||||
|
||||
class AxiosMessageFormatting {
|
||||
|
||||
public static FormatError(error: AxiosError): string {
|
||||
if (error.response && error.response.data) {
|
||||
if (typeof error.response.data === 'string') {
|
||||
return error.response.data;
|
||||
} else if (isArray(error.response.data)) {
|
||||
return error.response.data.reduce((prev, cur, i) => `${prev} (${i + 1}) ${cur}`, '');
|
||||
}
|
||||
}
|
||||
return error.message;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default AxiosMessageFormatting;
|
||||
@@ -0,0 +1,24 @@
|
||||
import moment, { Moment } from 'moment';
|
||||
|
||||
export default class DateFormatting {
|
||||
public static FormatYYYYMMDD(date?: Moment) {
|
||||
if (!date) {
|
||||
return '';
|
||||
}
|
||||
return date.format('YYYY-MM-DD');
|
||||
}
|
||||
|
||||
public static FormatMMDDYYYY(date?: Moment) {
|
||||
if (!date) {
|
||||
return '';
|
||||
}
|
||||
return date.format('MM/DD/YYYY');
|
||||
}
|
||||
|
||||
public static FormatMMDDYYYHHMMA(date?: Moment) {
|
||||
if (!date) {
|
||||
return '';
|
||||
}
|
||||
return moment(date).format('MM/DD/YYYY - h:mm A');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
export default class MonetaryFormatting {
|
||||
private static _enUSDFormatter = new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD',
|
||||
minimumFractionDigits: 2,
|
||||
});
|
||||
|
||||
/**
|
||||
* Format a number into a 2-decimal-place USD Dollar representation
|
||||
*
|
||||
* @param num The number to format. null or undefined can reresent zero
|
||||
*/
|
||||
public static FormatUSD(num?: number): string {
|
||||
if (!num) {
|
||||
num = 0;
|
||||
}
|
||||
|
||||
return MonetaryFormatting._enUSDFormatter.format(num);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
class NumericalFormatting {
|
||||
|
||||
public static FormatPreciseFlooredNumber(num: number, precision: number) {
|
||||
const decimalDigits = Math.pow(10, Math.floor(precision));
|
||||
return Math.floor(num * decimalDigits) / decimalDigits;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default NumericalFormatting;
|
||||
@@ -0,0 +1,17 @@
|
||||
export default class StringFormatting {
|
||||
public static ToTitleCase(value?: string): string {
|
||||
if (!value) {
|
||||
return "";
|
||||
}
|
||||
return value.split(" ").map(
|
||||
(x) => x ? x[0].toUpperCase() + x.substr(1).toLowerCase() : ""
|
||||
).join(" ");
|
||||
}
|
||||
|
||||
public static TrimLeft(value?: string): string {
|
||||
if (!value) {
|
||||
return "";
|
||||
}
|
||||
return value.trimStart();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import UrlFormatting from "./UrlFormatting";
|
||||
|
||||
describe('URL Formatting Class', () => {
|
||||
|
||||
it ('Replace URL Params', () => {
|
||||
const replacedURL = UrlFormatting.ReplaceURLParams(
|
||||
"http://www.test.com/foo/{fooId}/bar/{barId}/bool/{boolId}",
|
||||
{ key: 'fooId', value: 1 },
|
||||
{ key: 'barId', value: 'string' },
|
||||
{ key: 'boolId', value: false },
|
||||
);
|
||||
|
||||
expect(replacedURL).toEqual("http://www.test.com/foo/1/bar/string/bool/false");
|
||||
})
|
||||
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
interface IUrlParam {
|
||||
key: string;
|
||||
value: string | number | boolean;
|
||||
}
|
||||
|
||||
export default class UrlFormatting {
|
||||
/**
|
||||
* Replace the variable parameters of a url with their values
|
||||
*
|
||||
* I.E.
|
||||
*
|
||||
* ReplaceURLParams(
|
||||
* "http://www.test.com/foo/{fooId}/bar/{barId}",
|
||||
* { key: 'fooId', value: 1 },
|
||||
* { key: barId, value: 'second_param' }
|
||||
* );
|
||||
*
|
||||
* Would result in a return value of:
|
||||
* "http://www.test.com/foo/1/bar/second_param"
|
||||
*
|
||||
* NOTE: This method is case sensitive
|
||||
*
|
||||
* @param url The base URL
|
||||
* @param params Each param with it's token and value
|
||||
* @returns A paramterized version of the URL
|
||||
*/
|
||||
public static ReplaceURLParams(url: string, ...params: IUrlParam[]) {
|
||||
for (const param of params) {
|
||||
url = url.replace(`{${param.key}}`, param.value + '');
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate the array value parameter items into a query string format to be added onto the end of a URL
|
||||
*
|
||||
* @param arrayValues The array of values for the query
|
||||
* @param queryPrefix The string to be prepended to each array value
|
||||
* @returns A query formatted URL substring
|
||||
*/
|
||||
public static FormatArrayValuesIntoQuery(arrayValues: string[], queryPrefix: string) {
|
||||
let returnString = "";
|
||||
for (const arrayValue of arrayValues) {
|
||||
returnString += (queryPrefix + arrayValue);
|
||||
}
|
||||
return returnString;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import Axios, {AxiosError} from "axios";
|
||||
import {toast} from "react-toastify";
|
||||
import Configuration from "../configuration/Configuration";
|
||||
import AxiosMessageFormatting from "../formatting/AxiosMessageFormatting";
|
||||
import ToastDefaults from "../notifications/ToastDefaults";
|
||||
|
||||
interface IClientError {
|
||||
name: string;
|
||||
message: string;
|
||||
stackTrace: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* url: Provide the URL you were calling if available/relevant.
|
||||
* action: Name the action you were taking to help a developer find where the error occurred. (Required)
|
||||
* user: Provide a user if possible
|
||||
* arguments Provide the arguments relevant to the action
|
||||
*/
|
||||
export interface LcuErrorContext {
|
||||
url?: string;
|
||||
action: string;
|
||||
user?: string;
|
||||
arguments?: any;
|
||||
}
|
||||
|
||||
class ServerLogging {
|
||||
|
||||
public static ResolveErrorWithToast(err: AxiosError, context: LcuErrorContext) {
|
||||
ServerLogging.ResolveError(err,context);
|
||||
toast.error(AxiosMessageFormatting.FormatError(err), ToastDefaults.errorParams);
|
||||
}
|
||||
|
||||
public static ResolveError(err: AxiosError, context: LcuErrorContext) {
|
||||
if (!Configuration.Get.production) {
|
||||
window.console.error(err);
|
||||
window.console.error(context);
|
||||
}
|
||||
ServerLogging.LogError(err, context);
|
||||
}
|
||||
|
||||
public static LogError(error: IClientError | AxiosError, context?: LcuErrorContext) {
|
||||
let stackTrace: string = "";
|
||||
|
||||
if (this.isAxiosError(error)) {
|
||||
stackTrace = error.stack || "Unknown";
|
||||
} else {
|
||||
stackTrace = error.stackTrace;
|
||||
}
|
||||
|
||||
const clientError: IClientError = {
|
||||
name: error.name,
|
||||
message: error.message,
|
||||
stackTrace: stackTrace
|
||||
};
|
||||
|
||||
// We can do this without worry, the Stacktrace that arrives at the server is never a usable stacktrace
|
||||
// and always looks like: at e.exports (https://qa-bsatool.lcu-internal.com/static/js/2.29bed6e0.chunk.js:2:1378526)
|
||||
if (context) {
|
||||
try {
|
||||
clientError.stackTrace = JSON.stringify(context);
|
||||
} catch (stringifyError) {
|
||||
// This will happen if there is a circular reference or some other nonsense in arguments field.
|
||||
context.arguments = "Arguments failed JSON.Stringify call";
|
||||
clientError.stackTrace = JSON.stringify(context);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Axios.post(Configuration.PrefixRepoApiUrl(Configuration.Get.urls.repoApi.logging), clientError);
|
||||
} catch (err) {
|
||||
window.console.error("Failed to log error to server.");
|
||||
}
|
||||
}
|
||||
|
||||
private static isAxiosError(error: IClientError | AxiosError): error is AxiosError {
|
||||
return (error as AxiosError).stack !== undefined;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ServerLogging;
|
||||
@@ -0,0 +1,17 @@
|
||||
class ToastDefaults {
|
||||
|
||||
public static baseParams = {
|
||||
autoClose: 3000,
|
||||
hideProgressBar: true,
|
||||
draggable: false
|
||||
};
|
||||
|
||||
public static errorParams = {
|
||||
autoClose: 5000,
|
||||
hideProgressBar: true,
|
||||
draggable: false
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
export default ToastDefaults;
|
||||
@@ -0,0 +1,4 @@
|
||||
export default interface IAlertGroup {
|
||||
message: string;
|
||||
repoIds: number[];
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import AsyncState from "../enumerations/AsyncState";
|
||||
|
||||
interface IAsyncStateContainer<T> {
|
||||
data?: T;
|
||||
state: AsyncState;
|
||||
}
|
||||
|
||||
export interface IAsyncStateContainerStrict<T> extends IAsyncStateContainer<T> {
|
||||
data: T;
|
||||
}
|
||||
|
||||
export default IAsyncStateContainer;
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface IColumnSort {
|
||||
index: number;
|
||||
isReversed: boolean;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface IPagedResponse<T> {
|
||||
items: T[];
|
||||
totalItems: number;
|
||||
currentPage: number;
|
||||
totalPages: number;
|
||||
nextPage?: string;
|
||||
previousPage?: string;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { IAlert } from '../../reducers/AlertReducer';
|
||||
|
||||
export default interface IState {
|
||||
alerts: IAlert[];
|
||||
}
|
||||
Reference in New Issue
Block a user