diff --git a/packages/utils/index.ts b/packages/utils/index.ts index 1cf6a84c76..0f3c5310d4 100644 --- a/packages/utils/index.ts +++ b/packages/utils/index.ts @@ -19,17 +19,14 @@ export { diff, isEqual, omit } from './src/objectUtils'; export { buildPath, formatQueryString, parseQueryString } from './src/urlUtils'; export { ariaCheck, - arrayMinLength, dateAfterOrEqual, dateAfterOrEqualToToday, dateAfterToday, dateBeforeOrEqual, dateBeforeOrEqualToToday, dateBeforeToday, - dateIsAfter, dateIsBefore, dateRangesNotOverlapping, - dateRangesNotOverlappingCrossTypes, hasValidDate, hasValidDecimal, hasValidDecimalMaxNumberOfDecimals, @@ -38,45 +35,27 @@ export { hasValidInteger, hasValidName, hasValidOrgNumber, - hasValidOrgNumberOrFodselsnr, hasValidPeriod, - hasValidPeriodIncludingOtherErrors, hasValidSaksnummerOrFodselsnummerFormat, hasValidText, - hasValidValue, - isArbeidsProsentVidUtsettelse100, - isDatesEqual, - isTrekkdagerMerEnnNullUtsettelse, - isUkerOgDagerVidNullUtbetalningsgrad, - isUtbetalingMerEnnNullUtsettelse, - isUtbetalingsgradMerSamitidigUttaksprosent, isWithinOpptjeningsperiode, - isutbetalingPlusArbeidsprosentMerEn100, maxLength, - maxLengthOrFodselsnr, maxValue, - maxValueFormatted, minLength, minValue, - minValueFormatted, - notDash, required, requiredIfCustomFunctionIsTrue, requiredIfNotPristine, - validPeriodeFomTom, - validateProsentandel, } from './src/validation/validators'; export { dateNotAfterOrEqualMessage, dateNotBeforeOrEqualMessage, - dateRangesOverlappingBetweenPeriodTypesMessage, dateRangesOverlappingMessage, invalidDateMessage, invalidDecimalMessage, invalidPeriodMessage, isRequiredMessage, - sammeFodselsnummerSomSokerMessage, } from './src/validation/messages'; export { findAksjonspunkt } from './src/micro-frontends/findAksjonspunkt'; diff --git a/packages/utils/src/arbeidsforholdUtils.ts b/packages/utils/src/arbeidsforholdUtils.ts index 6ae6cb70a3..59841a3515 100644 --- a/packages/utils/src/arbeidsforholdUtils.ts +++ b/packages/utils/src/arbeidsforholdUtils.ts @@ -1,6 +1,6 @@ import { ArbeidsforholdV2, ArbeidsgiverOpplysningerPerId } from '@k9-sak-web/types'; -const getEndCharFromId = id => (id ? `...${id.substring(id.length - 4, id.length)}` : ''); +const getEndCharFromId = (id: string) => (id ? `...${id.substring(id.length - 4, id.length)}` : ''); export const utledArbeidsforholdNavn = ( arbeidsforhold: ArbeidsforholdV2, diff --git a/packages/utils/src/arrayUtils.ts b/packages/utils/src/arrayUtils.ts index 7394ea1d85..99bfea58be 100644 --- a/packages/utils/src/arrayUtils.ts +++ b/packages/utils/src/arrayUtils.ts @@ -1,6 +1,6 @@ -export const range = length => [...Array(length).keys()]; +export const range = (length: number) => [...Array(length).keys()]; -export const haystack = (object, keys, defaultValue = null) => { +export const haystack = (object: unknown, keys: string | string[], defaultValue = null) => { const keysArray = Array.isArray(keys) ? keys : keys.replace(/(\[(\d+)\])/g, '.$2').split('.'); const currentObject = object[keysArray[0]]; if (currentObject && keysArray.length > 1) { @@ -9,7 +9,7 @@ export const haystack = (object, keys, defaultValue = null) => { return currentObject === undefined ? defaultValue : currentObject; }; -export const makeArrayWithoutDuplicates = (array: any[]) => { +export const makeArrayWithoutDuplicates = (array: unknown[]) => { const arrayWithoutDuplicates = []; array.forEach(value => { if (!arrayWithoutDuplicates.includes(value)) { @@ -19,5 +19,5 @@ export const makeArrayWithoutDuplicates = (array: any[]) => { return arrayWithoutDuplicates; }; -export const getArrayDifference = (baseArray: any[], otherArray: any[]) => +export const getArrayDifference = (baseArray: string[], otherArray: string[]) => baseArray.filter(value => otherArray.includes(value) === false); diff --git a/packages/utils/src/beregning/VilkarMapper.tsx b/packages/utils/src/beregning/VilkarMapper.tsx index b8f6bc79cf..83ef1cac98 100644 --- a/packages/utils/src/beregning/VilkarMapper.tsx +++ b/packages/utils/src/beregning/VilkarMapper.tsx @@ -1,5 +1,6 @@ -import { Vilkar as FTVilkarType } from '@navikt/ft-types'; import { BeregningReferanse } from '@k9-sak-web/types'; +import { Vilkar as FTVilkarType } from '@navikt/ft-types'; +import { VilkårMedPerioderDto } from '@navikt/k9-sak-typescript-client'; type Periode = { fom: string; tom: string }; @@ -11,8 +12,7 @@ const erForlengelse = (beregningreferanserTilVurdering: BeregningReferanse[], pe return undefined; }; -// TODO: Legg til type for vilkar når kodeverkjobben er gjort. Formattering her skal vere på nytt format (må gjøres utenfor) -const mapVilkar = (vilkar, beregningreferanserTilVurdering: BeregningReferanse[]): FTVilkarType => +const mapVilkar = (vilkar: VilkårMedPerioderDto, beregningreferanserTilVurdering: BeregningReferanse[]): FTVilkarType => ({ vilkarType: vilkar.vilkarType, overstyrbar: vilkar.overstyrbar, @@ -26,6 +26,6 @@ const mapVilkar = (vilkar, beregningreferanserTilVurdering: BeregningReferanse[] vilkarStatus: p.vilkarStatus, erForlengelse: erForlengelse(beregningreferanserTilVurdering, p.periode), })), - } as FTVilkarType); + }) as FTVilkarType; export default mapVilkar; diff --git a/packages/utils/src/currencyUtils.ts b/packages/utils/src/currencyUtils.ts index aac4f8b04a..b93150341b 100644 --- a/packages/utils/src/currencyUtils.ts +++ b/packages/utils/src/currencyUtils.ts @@ -1,9 +1,9 @@ -export const formatCurrencyWithKr = value => { +export const formatCurrencyWithKr = (value: string | number) => { const formattedValue = Number(value).toLocaleString('nb-NO').replace(/,|\s/g, ' '); return `${formattedValue} kr`; }; -export const formatCurrencyNoKr = value => { +export const formatCurrencyNoKr = (value: string | number) => { if (value === null || value === undefined) { return undefined; } @@ -12,7 +12,7 @@ export const formatCurrencyNoKr = value => { if (Number.isNaN(newVal)) { return undefined; } - return Number(Math.round(newVal)).toLocaleString('nb-NO').replace(/,|\s/g, ' '); + return Number(Math.round(+newVal)).toLocaleString('nb-NO').replace(/,|\s/g, ' '); }; export const removeSpacesFromNumber = (input: number | string): number => { @@ -20,10 +20,10 @@ export const removeSpacesFromNumber = (input: number | string): number => { return input as number; } const parsedValue = parseInt((input as string).replace(/\s/g, ''), 10); - return Number.isNaN(parsedValue) ? input as number : parsedValue; + return Number.isNaN(parsedValue) ? (input as number) : parsedValue; }; -export const parseCurrencyInput = input => { +export const parseCurrencyInput = (input: number | string) => { const inputNoSpace = input.toString().replace(/\s/g, ''); const parsedValue = parseInt(inputNoSpace, 10); return Number.isNaN(parsedValue) ? '' : formatCurrencyNoKr(parsedValue); diff --git a/packages/utils/src/date-utils/dateComparison.ts b/packages/utils/src/date-utils/dateComparison.ts index df33a47a68..710c583b78 100644 --- a/packages/utils/src/date-utils/dateComparison.ts +++ b/packages/utils/src/date-utils/dateComparison.ts @@ -6,7 +6,7 @@ export const isDayAfter = (d1: dayjs.Dayjs, d2: dayjs.Dayjs) => { return dayAfterD1.isSame(d2Day); }; -export function isSameOrBefore(date, otherDate) { +export function isSameOrBefore(date: dayjs.Dayjs | string, otherDate: dayjs.Dayjs | string) { const dateFormats = ['YYYY-MM-DD', 'DD.MM.YYYY']; const dateInQuestion = dayjs(date, dateFormats).utc(true); const formattedOtherDate = dayjs(otherDate, dateFormats).utc(true); diff --git a/packages/utils/src/date-utils/isValid.ts b/packages/utils/src/date-utils/isValid.ts index 9608b01dfc..c802dea516 100644 --- a/packages/utils/src/date-utils/isValid.ts +++ b/packages/utils/src/date-utils/isValid.ts @@ -1,4 +1,4 @@ // eslint-disable-next-line no-restricted-globals -const isValid = (date: any) => !isNaN(new Date(date) as any); +const isValid = (date: string) => !isNaN(new Date(date) as any); export default isValid; diff --git a/packages/utils/src/decodeHtmlEntity.ts b/packages/utils/src/decodeHtmlEntity.ts index 67bd1300ea..8ba5be8db4 100644 --- a/packages/utils/src/decodeHtmlEntity.ts +++ b/packages/utils/src/decodeHtmlEntity.ts @@ -2,7 +2,7 @@ * Decoder for decoding html entity navn og entity code * på sikkerhetshensyn '<(< <)' og '> (> >)' er ikke decodert */ -const decodeHtmlEntity = str => { +const decodeHtmlEntity = (str: string) => { if (str === null || str === undefined) { return str; } diff --git a/packages/utils/src/findStatusForVedtak.ts b/packages/utils/src/findStatusForVedtak.ts index 179a00d8e9..74b437d5ec 100644 --- a/packages/utils/src/findStatusForVedtak.ts +++ b/packages/utils/src/findStatusForVedtak.ts @@ -1,18 +1,24 @@ +import aksjonspunktCodes from '@fpsak-frontend/kodeverk/src/aksjonspunktCodes'; import { isAksjonspunktOpen } from '@fpsak-frontend/kodeverk/src/aksjonspunktStatus'; import { isAvslag } from '@fpsak-frontend/kodeverk/src/behandlingResultatType'; -import aksjonspunktCodes from '@fpsak-frontend/kodeverk/src/aksjonspunktCodes'; import vilkarUtfallType from '@fpsak-frontend/kodeverk/src/vilkarUtfallType'; +import { Aksjonspunkt, Behandlingsresultat, Vilkar } from '@k9-sak-web/types'; -const hasOnlyClosedAps = (aksjonspunkter, vedtakAksjonspunkter) => +const hasOnlyClosedAps = (aksjonspunkter: Aksjonspunkt[], vedtakAksjonspunkter: Aksjonspunkt[]) => aksjonspunkter .filter(ap => !vedtakAksjonspunkter.some(vap => vap.definisjon.kode === ap.definisjon.kode)) .every(ap => !isAksjonspunktOpen(ap.status.kode)); -const hasAksjonspunkt = ap => ap.definisjon.kode === aksjonspunktCodes.OVERSTYR_BEREGNING; +const hasAksjonspunkt = (ap: Aksjonspunkt) => ap.definisjon.kode === aksjonspunktCodes.OVERSTYR_BEREGNING; -const isAksjonspunktOpenAndOfType = ap => hasAksjonspunkt(ap) && isAksjonspunktOpen(ap.status.kode); +const isAksjonspunktOpenAndOfType = (ap: Aksjonspunkt) => hasAksjonspunkt(ap) && isAksjonspunktOpen(ap.status.kode); -const findStatusForVedtak = (vilkar, aksjonspunkter, vedtakAksjonspunkter, behandlingsresultat) => { +const findStatusForVedtak = ( + vilkar: Vilkar[], + aksjonspunkter: Aksjonspunkt[], + vedtakAksjonspunkter: Aksjonspunkt[], + behandlingsresultat: Behandlingsresultat, +) => { if (vilkar.length === 0) { return vilkarUtfallType.IKKE_VURDERT; } diff --git a/packages/utils/src/fodselsnummerUtils.ts b/packages/utils/src/fodselsnummerUtils.ts index d4079e338d..b42afdf072 100644 --- a/packages/utils/src/fodselsnummerUtils.ts +++ b/packages/utils/src/fodselsnummerUtils.ts @@ -1,6 +1,6 @@ export const fodselsnummerPattern = /^\d{11}$/; -const sum = (fodselsnummer, faktors) => { +const sum = (fodselsnummer: string, faktors: number[]) => { let s = 0; for (let i = 0; i < faktors.length; i += 1) { s += parseInt(fodselsnummer[i], 10) * faktors[i]; @@ -8,7 +8,7 @@ const sum = (fodselsnummer, faktors) => { return s; }; -export const isValidFodselsnummer = input => { +export const isValidFodselsnummer = (input: string | number) => { const fodselsnummer = `${input}`; if (!fodselsnummerPattern.test(fodselsnummer)) { return false; diff --git a/packages/utils/src/kodeverkUtils.ts b/packages/utils/src/kodeverkUtils.ts index 713753669c..357bd00f67 100644 --- a/packages/utils/src/kodeverkUtils.ts +++ b/packages/utils/src/kodeverkUtils.ts @@ -1,7 +1,8 @@ -import { KodeverkMedNavn, Kodeverk } from '@k9-sak-web/types'; +import { KodeverkObject } from '@k9-sak-web/lib/kodeverk/types.js'; +import { Kodeverk } from '@k9-sak-web/types'; export const getKodeverknavnFraKode = ( - alleKodeverk: { [key: string]: KodeverkMedNavn[] }, + alleKodeverk: { [key: string]: KodeverkObject[] }, kodeverkType: string, kode: string, undertype?: string, @@ -18,7 +19,7 @@ export const getKodeverknavnFraKode = ( return kodeverk ? kodeverk.navn : ''; }; export const getKodeverknavnFn = - (alleKodeverk: { [key: string]: KodeverkMedNavn[] }, kodeverkTyper: { [key: string]: string }) => + (alleKodeverk: { [key: string]: KodeverkObject[] }, kodeverkTyper: { [key: string]: string }) => (kodeverkOjekt: Kodeverk, undertype?: string) => getKodeverknavnFraKode(alleKodeverk, kodeverkTyper[kodeverkOjekt.kodeverk], kodeverkOjekt.kode, undertype); diff --git a/packages/utils/src/languageUtils.ts b/packages/utils/src/languageUtils.ts index b86b764f8a..2d81ccc1d9 100644 --- a/packages/utils/src/languageUtils.ts +++ b/packages/utils/src/languageUtils.ts @@ -4,14 +4,14 @@ import getPackageIntl from '../i18n/getPackageIntl'; const intl = getPackageIntl(); -export const replaceNorwegianCharacters = str => { +export const replaceNorwegianCharacters = (str: string) => { let result = str.split('æ').join('ae'); result = result.split('ø').join('oe'); return result.split('å').join('aa'); }; // TODO Fjern bruk av denne -export const getLanguageCodeFromSprakkode = sprakkode => { +export const getLanguageCodeFromSprakkode = (sprakkode: Kodeverk) => { if (!sprakkode) { return 'Malform.Bokmal'; } diff --git a/packages/utils/src/objectUtils.ts b/packages/utils/src/objectUtils.ts index 09b60e8dd5..baa8c27c54 100644 --- a/packages/utils/src/objectUtils.ts +++ b/packages/utils/src/objectUtils.ts @@ -1,18 +1,18 @@ -export const isEqual = (obj1, obj2) => JSON.stringify(obj1) === JSON.stringify(obj2); +export const isEqual = (obj1: object, obj2: object) => JSON.stringify(obj1) === JSON.stringify(obj2); -export const omit = (object, ...keysToOmit) => +export const omit = (object: object, ...keysToOmit: string[]) => Object.keys(object) .filter(key => !keysToOmit.includes(key)) .map(key => ({ [key]: object[key] })) .reduce((a, b) => Object.assign(a, b), {}); -const isNullOrUndefined = obj => obj === null || typeof obj === 'undefined'; -const isNotNullAndObject = obj => obj !== null && typeof obj === 'object' && obj.constructor; +const isNullOrUndefined = (value: unknown): boolean => value === null || value === undefined; +const isNotNullAndObject = (value: unknown): value is object => value !== null && typeof value === 'object'; -const redefineIfUndefined = (obj, otherObjOfType) => { +const redefineIfUndefined = (obj: T, otherObjOfType: T): T | null => { if (isNullOrUndefined(obj) && isNotNullAndObject(otherObjOfType)) { try { - return new otherObjOfType.constructor(); + return new (otherObjOfType.constructor as { new (): T })(); } catch { return null; } @@ -20,7 +20,7 @@ const redefineIfUndefined = (obj, otherObjOfType) => { return obj; }; -export const diff = (a, b) => { +export const diff = (a: unknown, b: unknown) => { const thing1 = redefineIfUndefined(a, b); const thing2 = redefineIfUndefined(b, a); if (typeof thing1 !== typeof thing2) { diff --git a/packages/utils/src/period-utils/findHolesInPeriods.ts b/packages/utils/src/period-utils/findHolesInPeriods.ts index 0e30aef1f7..86a8dd7165 100644 --- a/packages/utils/src/period-utils/findHolesInPeriods.ts +++ b/packages/utils/src/period-utils/findHolesInPeriods.ts @@ -2,7 +2,7 @@ import { initializeDate } from '@k9-sak-web/lib/dateUtils/initializeDate.js'; import Period from './Period'; import sortPeriodsByFomDate from './sortPeriodsByFomDate'; -const checkIfPeriodsAreEdgeToEdge = (period, otherPeriod) => { +const checkIfPeriodsAreEdgeToEdge = (period: Period, otherPeriod: Period) => { const dayAfterPeriod = initializeDate(period.tom).add(1, 'day'); const startOfNextPeriod = initializeDate(otherPeriod.fom); return dayAfterPeriod.isSame(startOfNextPeriod); diff --git a/packages/utils/src/stringUtils.ts b/packages/utils/src/stringUtils.ts index 1c488066b9..4306e3b6c8 100644 --- a/packages/utils/src/stringUtils.ts +++ b/packages/utils/src/stringUtils.ts @@ -1,6 +1,6 @@ -export const joinNonNullStrings = (strings: any[]): string => strings.filter(s => !!s).join(''); +export const joinNonNullStrings = (strings: string[]): string => strings.filter(s => !!s).join(''); -export function safeJSONParse(str) { +export function safeJSONParse(str: any) { try { return JSON.parse(str); } catch { diff --git a/packages/utils/src/urlUtils.spec.ts b/packages/utils/src/urlUtils.spec.ts index 6fce2894d0..39f7ad2775 100644 --- a/packages/utils/src/urlUtils.spec.ts +++ b/packages/utils/src/urlUtils.spec.ts @@ -1,36 +1,87 @@ -import { buildPath, parseQueryString } from './urlUtils'; +import { buildPath, formatQueryString, parseQueryString } from './urlUtils'; describe('Url-utils', () => { - it('skal parse url parameter', () => { - const queryString = '?errormessage=Det+finnes+ingen+sak+med+denne+referansen%3A+266'; - expect(parseQueryString(queryString)).toEqual({ errormessage: 'Det finnes ingen sak med denne referansen: 266' }); + describe('formatQueryString', () => { + it('should format query string correctly with valid parameters', () => { + const queryParams = { param1: 'value1', param2: 'value2' }; + const result = formatQueryString(queryParams); + expect(result).toBe('?param1=value1¶m2=value2'); + }); + + it('should filter out undefined, null, and empty values', () => { + const queryParams = { param1: 'value1', param2: undefined, param3: null, param4: '' }; + const result = formatQueryString(queryParams); + expect(result).toBe('?param1=value1'); + }); + + it('should URL-encode values', () => { + const queryParams = { param1: 'value 1', param2: 'value&2' }; + const result = formatQueryString(queryParams); + expect(result).toBe('?param1=value+1¶m2=value%262'); + }); + + it('should replace URL-encoded spaces with plus', () => { + const queryParams = { param1: 'value 1' }; + const result = formatQueryString(queryParams); + expect(result).toBe('?param1=value+1'); + }); }); - it('skal parse to url parametere', () => { - const queryString = '?errormessage=Det+finnes+ingen+sak+med+denne+referansen%3A+266&message=Dette+er+en+test'; - expect(parseQueryString(queryString)).toEqual({ - errormessage: 'Det finnes ingen sak med denne referansen: 266', - message: 'Dette er en test', + describe('parseQueryString', () => { + it('skal parse url parameter', () => { + const queryString = '?errormessage=Det+finnes+ingen+sak+med+denne+referansen%3A+266'; + expect(parseQueryString(queryString)).toEqual({ errormessage: 'Det finnes ingen sak med denne referansen: 266' }); + }); + + it('skal parse to url parametere', () => { + const queryString = '?errormessage=Det+finnes+ingen+sak+med+denne+referansen%3A+266&message=Dette+er+en+test'; + expect(parseQueryString(queryString)).toEqual({ + errormessage: 'Det finnes ingen sak med denne referansen: 266', + message: 'Dette er en test', + }); }); }); - it('skal bygge path fra route', () => { - const route = '/test/:param/bar/:bar(\\d+)/:optionalParam?/:requiredParam/:baz([a-z]{2})/:qux([a-z]{2})?'; - const params = { - param: 'foo', // valid - bar: '1', // valid - baz: '1', // invalid, does not match regex - required, parameter definition should be included instead - qux: '1', // invalid, does not match regex - optional, should be omitted - }; + describe('buildPath', () => { + it('skal bygge path fra route', () => { + const route = '/test/:param/bar/:bar(\\d+)/:optionalParam?/:requiredParam/:baz([a-z]{2})/:qux([a-z]{2})?'; + const params = { + param: 'foo', // valid + bar: '1', // valid + baz: '1', // invalid, does not match regex - required, parameter definition should be included instead + qux: '1', // invalid, does not match regex - optional, should be omitted + }; + + const path = buildPath(route, params); + + expect(path).toEqual('/test/foo/bar/1/:requiredParam/:baz([a-z]{2})'); + + const relativeRoute = 'hei/paa/:hvem'; - const path = buildPath(route, params); + const relativePath = buildPath(relativeRoute, { hvem: 'deg' }); - expect(path).toEqual('/test/foo/bar/1/:requiredParam/:baz([a-z]{2})'); + expect(relativePath).toEqual('hei/paa/deg'); + }); - const relativeRoute = 'hei/paa/:hvem'; + it('should handle optional parameters correctly', () => { + const path = '/path/:param1/:param2?'; + const params = { param1: 'value1' }; + const result = buildPath(path, params); + expect(result).toBe('/path/value1'); + }); - const relativePath = buildPath(relativeRoute, { hvem: 'deg' }); + it('should handle parameters with patterns correctly', () => { + const path = '/path/:param1(\\d+)/:param2'; + const params = { param1: '123', param2: 'value2' }; + const result = buildPath(path, params); + expect(result).toBe('/path/123/value2'); + }); - expect(relativePath).toEqual('hei/paa/deg'); + it('should return the original segment if parameter is not provided', () => { + const path = '/path/:param1/:param2'; + const params = { param1: 'value1' }; + const result = buildPath(path, params); + expect(result).toBe('/path/value1/:param2'); + }); }); }); diff --git a/packages/utils/src/urlUtils.ts b/packages/utils/src/urlUtils.ts index 4a2e14bebe..f912181c77 100644 --- a/packages/utils/src/urlUtils.ts +++ b/packages/utils/src/urlUtils.ts @@ -1,4 +1,4 @@ -export const parseQueryString = (queryString = '') => +export const parseQueryString = (queryString: string = '') => queryString .replace(/^\?/, '') // Remove leading question mark .replace(/\+/g, '%20') // Replace plus signs with URL-encoded spaces @@ -7,7 +7,7 @@ export const parseQueryString = (queryString = '') => .map(([key, value]) => ({ [key]: decodeURIComponent(value) })) // URL-decode value .reduce((a, b) => ({ ...a, ...b }), {}); -export const formatQueryString = (queryParams = {}) => +export const formatQueryString = (queryParams: { [key: string]: string | boolean } = {}) => `?${ // Add leading question mark Object.entries(queryParams) @@ -16,22 +16,22 @@ export const formatQueryString = (queryParams = {}) => .map(([key, value]) => [key, encodeURIComponent(value as string)]) // URL-encode value .map(([key, encodedValue]) => `${key}=${encodedValue}`) .join('&') // Join with delimiter '&' - .replaceAll('%20', '+') // Replace URL-encoded spaces with plus + .replace('%20', '+') // Replace URL-encoded spaces with plus }`; const paramSegmentPattern = /^:(\w+)(\(.+\))?(\?)?$/; -const resolveParam = params => segment => { +const resolveParam = (params: { [key: string]: string | number }) => (segment: string) => { if (!paramSegmentPattern.test(segment)) { return segment; } const [paramName, paramPattern, optional] = paramSegmentPattern.exec(segment).slice(1, 4); - const paramMatch = new RegExp(paramPattern || '(.+)').exec(params[paramName]); + const paramMatch = new RegExp(paramPattern || '(.+)').exec(`${params[paramName]}`); const paramValue = paramMatch ? paramMatch[1].replace(/^undefined$/, '') : ''; return paramValue || (optional ? '' : segment); }; -export const buildPath = (path, params = {}) => +export const buildPath = (path: string, params: { [key: string]: string | number } = {}) => path .replace(/^\//, ' /') // Add whitespace before leading slash to keep it from being consumed by split .replace(/\/$/, '/ ') // Add whitespace after trailing slash to keep it from being consumed by split diff --git a/packages/utils/src/validation/messages.ts b/packages/utils/src/validation/messages.ts index 053bc8ea27..cc6d691a37 100644 --- a/packages/utils/src/validation/messages.ts +++ b/packages/utils/src/validation/messages.ts @@ -1,7 +1,6 @@ export const isRequiredMessage = () => [{ id: 'ValidationMessage.NotEmpty' }]; export const minLengthMessage = length => [{ id: 'ValidationMessage.MinLength' }, { length }]; export const maxLengthMessage = length => [{ id: 'ValidationMessage.MaxLength' }, { length }]; -export const maxLengthOrFodselsnrMessage = length => [{ id: 'ValidationMessage.maxLengthOrFodselsnr' }, { length }]; export const minValueMessage = (length: number) => [{ id: 'ValidationMessage.MinValue' }, { length }]; export const maxValueMessage = (length: number) => [{ id: 'ValidationMessage.MaxValue' }, { length }]; export const invalidDateMessage = () => [{ id: 'ValidationMessage.InvalidDate' }]; @@ -13,38 +12,14 @@ export const invalidDecimalMessage = (text, maxNumberOfDecimals) => [ export const dateNotBeforeOrEqualMessage = limit => [{ id: 'ValidationMessage.DateNotBeforeOrEqual' }, { limit }]; export const dateNotAfterOrEqualMessage = limit => [{ id: 'ValidationMessage.DateNotAfterOrEqual' }, { limit }]; export const dateRangesOverlappingMessage = () => [{ id: 'ValidationMessage.DateRangesOverlapping' }]; -export const datesNotEqual = value => [{ id: 'ValidationMessage.DatesNotEqual' }, { value }]; export const invalidFodselsnummerFormatMessage = () => [{ id: 'ValidationMessage.InvalidFodselsnummerFormat' }]; export const invalidFodselsnummerMessage = () => [{ id: 'ValidationMessage.InvalidFodselsnummer' }]; -export const sammeFodselsnummerSomSokerMessage = () => [{ id: 'ValidationMessage.SammeFodselsnummerSomSoker' }]; export const invalidSaksnummerOrFodselsnummerFormatMessage = () => [ { id: 'ValidationMessage.InvalidSaksnummerOrFodselsnummerFormat' }, ]; export const invalidTextMessage = text => [{ id: 'ValidationMessage.InvalidText' }, { text }]; -export const invalidValueMessage = value => [{ id: 'ValidationMessage.InvalidValue' }, { value }]; -export const arrayMinLengthMessage = length => [{ id: 'ValidationMessage.ArrayMinLength' }, { length }]; export const invalidDatesInPeriodMessage = () => [{ id: 'ValidationMessage.InvalidDatesInPeriod' }]; export const invalidPeriodMessage = () => [{ id: 'ValidationMessage.InvalidPeriod' }]; export const invalidPeriodRangeMessage = () => [{ id: 'ValidationMessage.InvalidPeriodRange' }]; -export const utbetalingsgradErMerSamtidigUttaksprosentMessage = () => [ - { id: 'ValidationMessage.utbetalingsgradErMerSamtidigUttaksprosent' }, -]; -export const trekkdagerErMerEnnNullUtsettelseMessage = () => [ - { id: 'ValidationMessage.trekkdagerErMerEnnNullUtsettelse' }, -]; -export const utbetalingMerEnnNullUtsettelseMessage = () => [{ id: 'ValidationMessage.utbetalingMerEnnNullUtsettelse' }]; export const invalidNumberMessage = text => [{ id: 'ValidationMessage.InvalidNumber' }, { text }]; -export const ukerOgDagerVidNullUtbetalningsgradMessage = () => [ - { id: 'ValidationMessage.ukerOgDagerVidNullUtbetalningsgradMessage' }, -]; -export const arbeidsprosentMåVare100VidUtsettelseAvArbeidMessage = () => [ - { id: 'ValidationMessage.UtsettelseUtenFullArbeid' }, -]; -export const merEn100ProsentMessage = () => [{ id: 'ValidationMessage.MerEn100Prosent' }]; -export const dateRangesOverlappingBetweenPeriodTypesMessage = () => [ - { id: 'ValidationMessage.DateRangesOverlappingPeriodTypes' }, -]; export const invalidOrgNumberMessage = () => [{ id: 'ValidationMessage.InvalidOrganisasjonsnummer' }]; -export const invalidOrgNumberOrFodselsnrMessage = () => [ - { id: 'ValidationMessage.InvalidOrganisasjonsnummerOrFodselsnr' }, -]; diff --git a/packages/utils/src/validation/validators.ts b/packages/utils/src/validation/validators.ts index 2ca0fc4baf..3a8ea46892 100644 --- a/packages/utils/src/validation/validators.ts +++ b/packages/utils/src/validation/validators.ts @@ -1,16 +1,11 @@ import { validateTextCharacters } from '@k9-sak-web/gui/utils/validation/validateTextCharacters.js'; -import { DDMMYYYY_DATE_FORMAT, ISO_DATE_FORMAT } from '@k9-sak-web/lib/dateUtils/formats.js'; -import moment from 'moment'; -import { removeSpacesFromNumber } from '../currencyUtils'; +import { DDMMYYYY_DATE_FORMAT } from '@k9-sak-web/lib/dateUtils/formats.js'; +import moment, { Moment } from 'moment'; import { fodselsnummerPattern, isValidFodselsnummer } from '../fodselsnummerUtils'; import { - arbeidsprosentMåVare100VidUtsettelseAvArbeidMessage, - arrayMinLengthMessage, dateNotAfterOrEqualMessage, dateNotBeforeOrEqualMessage, - dateRangesOverlappingBetweenPeriodTypesMessage, dateRangesOverlappingMessage, - datesNotEqual, invalidDateMessage, invalidDatesInPeriodMessage, invalidDecimalMessage, @@ -19,23 +14,15 @@ import { invalidIntegerMessage, invalidNumberMessage, invalidOrgNumberMessage, - invalidOrgNumberOrFodselsnrMessage, invalidPeriodMessage, invalidPeriodRangeMessage, invalidSaksnummerOrFodselsnummerFormatMessage, invalidTextMessage, - invalidValueMessage, isRequiredMessage, maxLengthMessage, - maxLengthOrFodselsnrMessage, maxValueMessage, - merEn100ProsentMessage, minLengthMessage, minValueMessage, - trekkdagerErMerEnnNullUtsettelseMessage, - ukerOgDagerVidNullUtbetalningsgradMessage, - utbetalingMerEnnNullUtsettelseMessage, - utbetalingsgradErMerSamtidigUttaksprosentMessage, } from './messages'; import { dateRangesAreSequential, @@ -51,89 +38,85 @@ import { yesterday, } from './validatorsHelper'; -export const maxLengthOrFodselsnr = length => text => - isEmpty(text) || text.toString().trim().length <= length ? null : maxLengthOrFodselsnrMessage(length); -export const required = value => (isEmpty(value) ? isRequiredMessage() : undefined); -export const atLeastOneRequired = (value, otherValue) => +export const required = (value: string) => (isEmpty(value) ? isRequiredMessage() : undefined); +export const atLeastOneRequired = (value: string, otherValue: string) => isEmpty(value) && isEmpty(otherValue) ? isRequiredMessage() : undefined; -export const notDash = value => (value === '-' ? isRequiredMessage() : undefined); -export const requiredIfNotPristine = (value, allValues, props) => +export const requiredIfNotPristine = (value: string, allValues, props: { pristine: boolean }) => props.pristine || !isEmpty(value) ? undefined : isRequiredMessage(); -export const requiredIfCustomFunctionIsTrue = isRequiredFunction => (value, allValues, props) => +export const requiredIfCustomFunctionIsTrue = isRequiredFunction => (value: string, allValues, props) => isEmpty(value) && isRequiredFunction(allValues, props) ? isRequiredMessage() : undefined; -export const minLength = length => text => +export const minLength = (length: number) => (text: string) => isEmpty(text) || text.toString().trim().length >= length ? null : minLengthMessage(length); -export const maxLength = length => text => +export const maxLength = (length: number) => (text: string) => isEmpty(text) || text.toString().trim().length <= length ? null : maxLengthMessage(length); -export const minValue = length => number => (number >= length ? null : minValueMessage(length)); -export const maxValue = length => number => (number <= length ? null : maxValueMessage(length)); +export const minValue = (length: number) => (number: number | string) => + +number >= length ? null : minValueMessage(length); +export const maxValue = (length: number) => (number: number | string) => + +number <= length ? null : maxValueMessage(length); -export const minValueFormatted = min => number => (removeSpacesFromNumber(number) >= min ? null : minValueMessage(min)); -export const maxValueFormatted = max => number => (removeSpacesFromNumber(number) <= max ? null : maxValueMessage(max)); +export const hasValidOrgNumber = (number: number | string) => + number.toString().trim().length === 9 ? null : invalidOrgNumberMessage(); -export const hasValidOrgNumber = number => (number.toString().trim().length === 9 ? null : invalidOrgNumberMessage()); -export const hasValidOrgNumberOrFodselsnr = number => - number.toString().trim().length === 9 || number.toString().trim().length === 11 - ? null - : invalidOrgNumberOrFodselsnrMessage(); - -const hasValidNumber = text => (isEmpty(text) || numberRegex.test(text) ? null : invalidNumberMessage(text)); -const hasValidInt = text => (isEmpty(text) || integerRegex.test(text) ? null : invalidIntegerMessage(text)); -const hasValidDec = (text, maxNumberOfDecimals = 2) => - isEmpty(text) || decimalRegexWithMax(maxNumberOfDecimals).test(text) +const hasValidNumber = (text: string | number) => + isEmpty(text) || numberRegex.test(`${text}`) ? null : invalidNumberMessage(text); +const hasValidInt = (text: string | number) => + isEmpty(text) || integerRegex.test(`${text}`) ? null : invalidIntegerMessage(text); +const hasValidDec = (text: string | number, maxNumberOfDecimals: number = 2) => + isEmpty(text) || decimalRegexWithMax(maxNumberOfDecimals).test(`${text}`) ? null : invalidDecimalMessage(text, maxNumberOfDecimals); -export const hasValidInteger = text => hasValidNumber(text) || hasValidInt(text); -export const hasValidDecimalMaxNumberOfDecimals = maxNumberOfDecimals => text => +export const hasValidInteger = (text: string | number) => hasValidNumber(text) || hasValidInt(text); +export const hasValidDecimalMaxNumberOfDecimals = (maxNumberOfDecimals: number) => (text: string) => hasValidNumber(text) || hasValidDec(text, maxNumberOfDecimals); -export const hasValidDecimal = text => hasValidNumber(text) || hasValidDec(text); +export const hasValidDecimal = (text: string | number) => hasValidNumber(text) || hasValidDec(text); -export const hasValidSaksnummerOrFodselsnummerFormat = text => +export const hasValidSaksnummerOrFodselsnummerFormat = (text: string) => isEmpty(text) || saksnummerOrFodselsnummerPattern.test(text) ? null : invalidSaksnummerOrFodselsnummerFormatMessage(); -export const hasValidDate = text => (isEmpty(text) || isoDateRegex.test(text) ? null : invalidDateMessage()); -const getBeforeErrorMessage = (latest, customErrorMessage) => { +export const hasValidDate = (text: string) => (isEmpty(text) || isoDateRegex.test(text) ? null : invalidDateMessage()); +const getBeforeErrorMessage = (latest: string | Moment | Date, customErrorMessage: (date: string) => string) => { const date = moment(latest).format(DDMMYYYY_DATE_FORMAT); return customErrorMessage ? customErrorMessage(date) : dateNotBeforeOrEqualMessage(date); }; export const dateBeforeOrEqual = - (latest, customErrorMessageFunction = undefined) => - text => + (latest: string | Moment | Date, customErrorMessageFunction: (date: string) => string = undefined) => + (text: string) => isEmpty(text) || moment(text).isSameOrBefore(moment(latest).startOf('day')) ? null : getBeforeErrorMessage(latest, customErrorMessageFunction); -const getAfterErrorMessage = (earliest, customErrorMessage) => { +const getAfterErrorMessage = (earliest: string | Moment | Date, customErrorMessage: (date: string) => any) => { const date = moment(earliest).format(DDMMYYYY_DATE_FORMAT); return customErrorMessage ? customErrorMessage(date) : dateNotAfterOrEqualMessage(date); }; export const dateAfterOrEqual = - (earliest, customErrorMessageFunction = undefined) => - text => + (earliest: string | Moment | Date, customErrorMessageFunction?: (date: string) => string) => + (text: string | Moment) => isEmpty(text) || moment(text).isSameOrAfter(moment(earliest).startOf('day')) ? null : getAfterErrorMessage(earliest, customErrorMessageFunction); -export const dateIsBefore = (dateToCheckAgainst, errorMessageFunction) => inputDate => - isEmpty(inputDate) || moment(inputDate).isBefore(moment(dateToCheckAgainst).startOf('day')) - ? null - : errorMessageFunction(moment(dateToCheckAgainst).format(DDMMYYYY_DATE_FORMAT)); +export const dateIsBefore = + (dateToCheckAgainst: string, errorMessageFunction: (date: string) => { id?: string; dato?: string }[]) => + (inputDate: string) => + isEmpty(inputDate) || moment(inputDate).isBefore(moment(dateToCheckAgainst).startOf('day')) + ? null + : errorMessageFunction(moment(dateToCheckAgainst).format(DDMMYYYY_DATE_FORMAT)); -export const dateRangesNotOverlapping = ranges => +export const dateRangesNotOverlapping = (ranges: Array) => dateRangesAreSequential(ranges) ? null : dateRangesOverlappingMessage(); -export const dateRangesNotOverlappingCrossTypes = ranges => - dateRangesAreSequential(ranges) ? null : dateRangesOverlappingBetweenPeriodTypesMessage(); -export const dateBeforeToday = text => dateBeforeOrEqual(yesterday())(text); -export const dateBeforeOrEqualToToday = text => dateBeforeOrEqual(moment().startOf('day'))(text); -export const dateAfterToday = text => dateAfterOrEqual(tomorrow())(text); -export const dateAfterOrEqualToToday = text => dateAfterOrEqual(moment().startOf('day'))(text); +export const dateBeforeToday = (text: string) => dateBeforeOrEqual(yesterday())(text); +export const dateBeforeOrEqualToToday = (text: string) => dateBeforeOrEqual(moment().startOf('day'))(text); +export const dateAfterToday = (text: string) => dateAfterOrEqual(tomorrow())(text); +export const dateAfterOrEqualToToday = (text: string) => dateAfterOrEqual(moment().startOf('day'))(text); -export const hasValidFodselsnummerFormat = text => +export const hasValidFodselsnummerFormat = (text: string) => !fodselsnummerPattern.test(text) ? invalidFodselsnummerFormatMessage() : null; -export const hasValidFodselsnummer = text => (!isValidFodselsnummer(text) ? invalidFodselsnummerMessage() : null); +export const hasValidFodselsnummer = (text: string) => + !isValidFodselsnummer(text) ? invalidFodselsnummerMessage() : null; -export const hasValidText = text => { +export const hasValidText = (text: string) => { if (text === undefined || text === null) { return null; } @@ -147,7 +130,7 @@ export const hasValidText = text => { return null; }; -export const hasValidName = text => { +export const hasValidName = (text: string) => { if (!nameRegex.test(text)) { const illegalChars = text.replace(nameGyldigRegex, ''); return invalidTextMessage(illegalChars.replace(/[\t]/g, 'Tabulatortegn')); @@ -155,76 +138,7 @@ export const hasValidName = text => { return null; }; -export const hasValidValue = value => invalidValue => (value !== invalidValue ? invalidValueMessage(value) : null); -export const arrayMinLength = length => value => - value && value.length >= length ? null : arrayMinLengthMessage(length); - -export const dateIsAfter = (date, checkAgainsDate) => moment(date).isAfter(checkAgainsDate); -export const isDatesEqual = (date1, date2) => - date1 !== date2 ? datesNotEqual(moment(date2).format(DDMMYYYY_DATE_FORMAT)) : null; - -const validateDate = (dateAsText, date, earliestDate, latestDate) => { - const error = required(dateAsText) || hasValidDate(dateAsText); - if (!error && earliestDate) { - return dateAfterOrEqual(earliestDate)(date); - } - if (!error && latestDate) { - return dateBeforeOrEqual(latestDate)(date); - } - return error; -}; - -interface Options { - todayOrBefore?: boolean; - todayOrAfter?: boolean; - tidligstDato?: string; -} - -export const hasValidPeriodIncludingOtherErrors = (values, otherErrors = [{}], options: Options = {}) => { - const today = moment().format(ISO_DATE_FORMAT); - let earliestDate = options.todayOrAfter ? today : null; - if (options.tidligstDato) { - earliestDate = options.tidligstDato; - } - const latestDate = options.todayOrBefore ? today : null; - if (!values || !values.length) { - return { _error: isRequiredMessage() }; - } - const arrayErrors = values.map(({ periodeFom, periodeTom }, index) => { - const periodeFomDate = moment(periodeFom, ISO_DATE_FORMAT); - const periodeTomDate = moment(periodeTom, ISO_DATE_FORMAT); - const periodeFomError = validateDate(periodeFom, periodeFomDate, earliestDate, latestDate); - let periodeTomError = validateDate(periodeTom, periodeTomDate, earliestDate, latestDate); - if (!periodeFomError) { - periodeTomError = periodeTomError || dateAfterOrEqual(periodeFomDate)(periodeTomDate); - } - if (periodeFomError || periodeTomError || otherErrors[index] !== null) { - return { - periodeFom: periodeFomError, - periodeTom: periodeTomError, - ...otherErrors[index], - }; - } - return null; - }); - if (arrayErrors.some(errors => errors !== null)) { - return arrayErrors; - } - const overlapError = dateRangesNotOverlapping(values.map(({ periodeFom, periodeTom }) => [periodeFom, periodeTom])); - if (overlapError) { - return { _error: overlapError }; - } - return null; -}; - -export const validPeriodeFomTom = (fomDate, tomDate) => { - if (isEmpty(fomDate) && isEmpty(tomDate)) { - return null; - } - return moment(fomDate).isSameOrBefore(moment(tomDate).startOf('day')) ? null : invalidPeriodMessage(); -}; - -export const hasValidPeriod = (fomDate, tomDate) => { +export const hasValidPeriod = (fomDate: string, tomDate: string) => { if (isEmpty(fomDate) && isEmpty(tomDate)) { return null; } @@ -234,52 +148,12 @@ export const hasValidPeriod = (fomDate, tomDate) => { return moment(fomDate).isSameOrBefore(moment(tomDate).startOf('day')) ? null : invalidPeriodMessage(); }; -export const isWithinOpptjeningsperiode = (fomDateLimit, tomDateLimit) => (fom, tom) => { - const isBefore = moment(fom).isBefore(moment(fomDateLimit)); - const isAfter = moment(tom).isAfter(moment(tomDateLimit)); - return isBefore || isAfter ? invalidPeriodRangeMessage() : null; -}; - -export const isUtbetalingsgradMerSamitidigUttaksprosent = (samtidigUttaksProsent, utbetalingsgrad) => { - if (samtidigUttaksProsent < utbetalingsgrad) { - return utbetalingsgradErMerSamtidigUttaksprosentMessage(); - } - return null; -}; - -export const isUkerOgDagerVidNullUtbetalningsgrad = (weeks, days, utbetalingsgrad) => { - if (weeks === 0 && days === 0 && utbetalingsgrad > 0) { - return ukerOgDagerVidNullUtbetalningsgradMessage(); - } - return null; -}; - -export const isutbetalingPlusArbeidsprosentMerEn100 = (utbetalingsgrad, prosentArbeid) => { - if (utbetalingsgrad + prosentArbeid > 100) { - return merEn100ProsentMessage(); - } - return null; -}; - -const getSum = (total, num) => total + num; - -export const isArbeidsProsentVidUtsettelse100 = (values, aktivitetArray) => { - const andelIArbeid = [0]; - if (values.utsettelseType && values.erOppfylt && aktivitetArray) { - aktivitetArray.forEach(aktivitet => { - andelIArbeid.push(aktivitet.prosentArbeid); - }); - const prosentIArbeid = andelIArbeid.reduce(getSum); - if (prosentIArbeid < 100) { - return arbeidsprosentMåVare100VidUtsettelseAvArbeidMessage(); - } - return null; - } - return null; -}; - -export const validateProsentandel = prosentandel => - required(prosentandel) || hasValidDecimal(prosentandel) || hasValidNumber(prosentandel.replace('.', '')); +export const isWithinOpptjeningsperiode = + (fomDateLimit: string, tomDateLimit: string) => (fom: string, tom: string) => { + const isBefore = moment(fom).isBefore(moment(fomDateLimit)); + const isAfter = moment(tom).isAfter(moment(tomDateLimit)); + return isBefore || isAfter ? invalidPeriodRangeMessage() : null; + }; export const ariaCheck = () => { let errors; @@ -300,7 +174,3 @@ export const ariaCheck = () => { } }, 300); }; - -export const isTrekkdagerMerEnnNullUtsettelse = value => (value > 0 ? trekkdagerErMerEnnNullUtsettelseMessage() : null); - -export const isUtbetalingMerEnnNullUtsettelse = value => (value > 0 ? utbetalingMerEnnNullUtsettelseMessage() : null); diff --git a/packages/utils/src/validation/validatorsHelper.ts b/packages/utils/src/validation/validatorsHelper.ts index 5b7b53e7df..3620301ef7 100644 --- a/packages/utils/src/validation/validatorsHelper.ts +++ b/packages/utils/src/validation/validatorsHelper.ts @@ -1,4 +1,4 @@ -import moment from 'moment'; +import moment, { Moment } from 'moment'; export const isoDateRegex = /(19|20)\d{2}-(0?[1-9]|1[0-2])-(0?[1-9]|1\d|2\d|3[01])$/; export const numberRegex = /^\d+([,.]\d+)?$/; @@ -11,12 +11,13 @@ export const nameRegex = /^[0-9a-zA-ZæøåÆØÅAaÁáBbCcČčDdĐđEeFfGgHhIiJ export const nameGyldigRegex = /[0-9a-zA-ZæøåÆØÅAaÁáBbCcČčDdĐđEeFfGgHhIiJjKkLlMmNnŊŋOoPpRrSsŠšTtŦŧUuVvZzŽžéôèÉöüäÖÜÄ .'-]*/g; -export const isEmpty = text => text === null || text === undefined || text.toString().trim().length === 0; +export const isEmpty = (text: string | number | Moment | null | undefined) => + text === null || text === undefined || text.toString().trim().length === 0; export const yesterday = () => moment().subtract(1, 'days').startOf('day'); export const tomorrow = () => moment().add(1, 'days').startOf('day'); -export const dateRangesAreSequential = ranges => { +export const dateRangesAreSequential = (ranges: Array) => { if (Array.isArray(ranges)) { const isBeforeTheNextDate = (element, index, array) => { const current = moment(element).startOf('day');