diff --git a/src/entities/activity/lib/services/timeToMinutes.ts b/src/entities/activity/lib/services/timeToMinutes.ts new file mode 100644 index 000000000..96df31d99 --- /dev/null +++ b/src/entities/activity/lib/services/timeToMinutes.ts @@ -0,0 +1,6 @@ +export const timeToMinutes = (time: { + hours: number; + minutes: number; +}): number => { + return time.hours * 60 + time.minutes; +}; diff --git a/src/entities/activity/lib/types/conditionalLogic.ts b/src/entities/activity/lib/types/conditionalLogic.ts index 3504bcb50..8366da184 100644 --- a/src/entities/activity/lib/types/conditionalLogic.ts +++ b/src/entities/activity/lib/types/conditionalLogic.ts @@ -5,7 +5,7 @@ export type ConditionalLogic = { conditions: Array; }; -type Condition = +export type Condition = | IncludesOptionCondition | NotIncludesOptionCondition | EqualToOptionCondition @@ -15,10 +15,130 @@ type Condition = | EqualCondition | NotEqualCondition | BetweenCondition - | OutsideOfCondition; + | OutsideOfCondition + | GreaterThanDateCondition + | LessThanDateCondition + | EqualToDateCondition + | NotEqualToDateCondition + | BetweenDatesCondition + | OutsideOfDatesCondition + | GreaterThanTimeCondition + | LessThanTimeCondition + | EqualToTimeCondition + | NotEqualToTimeCondition + | BetweenTimesCondition + | OutsideOfTimesCondition + | GreaterThanTimeRangeCondition + | LessThanTimeRangeCondition + | EqualToTimeRangeCondition + | NotEqualToTimeRangeCondition + | BetweenTimesRangeCondition + | OutsideOfTimesRangeCondition + | EqualToRowOptionCondition + | NotEqualToRowOptionCondition + | IncludesRowOptionCondition + | NotIncludesRowOptionCondition + | EqualToSliderRowCondition + | NotEqualToSliderRowCondition + | GreaterThanSliderRowCondition + | LessThanSliderRowCondition + | BetweenSliderRowCondition + | OutsideOfSliderRowCondition; export type ConditionType = Condition['type']; +type EqualToRowOptionCondition = { + activityItemName: string; + type: 'EQUAL_TO_ROW_OPTION'; + payload: { + optionValue: string; + rowIndex: number; + }; +}; + +type NotEqualToRowOptionCondition = { + activityItemName: string; + type: 'NOT_EQUAL_TO_ROW_OPTION'; + payload: { + optionValue: string; + rowIndex: number; + }; +}; + +type IncludesRowOptionCondition = { + activityItemName: string; + type: 'INCLUDES_ROW_OPTION'; + payload: { + optionValue: string; + rowIndex: number; + }; +}; + +type NotIncludesRowOptionCondition = { + activityItemName: string; + type: 'NOT_INCLUDES_ROW_OPTION'; + payload: { + optionValue: string; + rowIndex: number; + }; +}; + +type EqualToSliderRowCondition = { + activityItemName: string; + type: 'EQUAL_TO_SLIDER_ROWS'; + payload: { + value: number; + rowIndex: number; + }; +}; + +type NotEqualToSliderRowCondition = { + activityItemName: string; + type: 'NOT_EQUAL_TO_SLIDER_ROWS'; + payload: { + value: number; + rowIndex: number; + }; +}; + +type GreaterThanSliderRowCondition = { + activityItemName: string; + type: 'GREATER_THAN_SLIDER_ROWS'; + payload: { + value: number; + rowIndex: number; + }; +}; + +type LessThanSliderRowCondition = { + activityItemName: string; + type: 'LESS_THAN_SLIDER_ROWS'; + payload: { + value: number; + rowIndex: number; + }; +}; + +type BetweenSliderRowCondition = { + activityItemName: string; + type: 'BETWEEN_SLIDER_ROWS'; + payload: { + minValue: number; + maxValue: number; + rowIndex: number; + }; +}; + +type OutsideOfSliderRowCondition = { + activityItemName: string; + type: 'OUTSIDE_OF_SLIDER_ROWS'; + payload: { + minValue: number; + maxValue: number; + rowIndex: number; + }; +}; + type IncludesOptionCondition = { activityItemName: string; type: 'INCLUDES_OPTION'; @@ -100,3 +220,207 @@ type OutsideOfCondition = { maxValue: number; }; }; + +type GreaterThanDateCondition = { + activityItemName: string; + type: 'GREATER_THAN_DATE'; + payload: { + date: string; + }; +}; + +type LessThanDateCondition = { + activityItemName: string; + type: 'LESS_THAN_DATE'; + payload: { + date: string; + }; +}; + +type EqualToDateCondition = { + activityItemName: string; + type: 'EQUAL_TO_DATE'; + payload: { + date: string; + }; +}; + +type NotEqualToDateCondition = { + activityItemName: string; + type: 'NOT_EQUAL_TO_DATE'; + payload: { + date: string; + }; +}; + +type BetweenDatesCondition = { + activityItemName: string; + type: 'BETWEEN_DATES'; + payload: { + minDate: string; + maxDate: string; + }; +}; + +type OutsideOfDatesCondition = { + activityItemName: string; + type: 'OUTSIDE_OF_DATES'; + payload: { + minDate: string; + maxDate: string; + }; +}; + +type GreaterThanTimeCondition = { + activityItemName: string; + type: 'GREATER_THAN_TIME'; + payload: { + time: { + hours: number; + minutes: number; + }; + }; +}; + +type LessThanTimeCondition = { + activityItemName: string; + type: 'LESS_THAN_TIME'; + payload: { + time: { + hours: number; + minutes: number; + }; + }; +}; + +type EqualToTimeCondition = { + activityItemName: string; + type: 'EQUAL_TO_TIME'; + payload: { + time: { + hours: number; + minutes: number; + }; + }; +}; + +type NotEqualToTimeCondition = { + activityItemName: string; + type: 'NOT_EQUAL_TO_TIME'; + payload: { + time: { + hours: number; + minutes: number; + }; + }; +}; + +type BetweenTimesCondition = { + activityItemName: string; + type: 'BETWEEN_TIMES'; + payload: { + minTime: { + hours: number; + minutes: number; + }; + maxTime: { + hours: number; + minutes: number; + }; + }; +}; + +type OutsideOfTimesCondition = { + activityItemName: string; + type: 'OUTSIDE_OF_TIMES'; + payload: { + minTime: { + hours: number; + minutes: number; + }; + maxTime: { + hours: number; + minutes: number; + }; + }; +}; + +type GreaterThanTimeRangeCondition = { + activityItemName: string; + type: 'GREATER_THAN_TIME_RANGE'; + payload: { + time: { + hours: number; + minutes: number; + }; + fieldName: string; + }; +}; + +type LessThanTimeRangeCondition = { + activityItemName: string; + type: 'LESS_THAN_TIME_RANGE'; + payload: { + time: { + hours: number; + minutes: number; + }; + fieldName: string; + }; +}; + +type EqualToTimeRangeCondition = { + activityItemName: string; + type: 'EQUAL_TO_TIME_RANGE'; + payload: { + time: { + hours: number; + minutes: number; + }; + fieldName: string; + }; +}; + +type NotEqualToTimeRangeCondition = { + activityItemName: string; + type: 'NOT_EQUAL_TO_TIME_RANGE'; + payload: { + time: { + hours: number; + minutes: number; + }; + fieldName: string; + }; +}; + +type BetweenTimesRangeCondition = { + activityItemName: string; + type: 'BETWEEN_TIMES_RANGE'; + payload: { + minTime: { + hours: number; + minutes: number; + }; + maxTime: { + hours: number; + minutes: number; + }; + fieldName: string; + }; +}; + +type OutsideOfTimesRangeCondition = { + activityItemName: string; + type: 'OUTSIDE_OF_TIMES_RANGE'; + payload: { + minTime: { + hours: number; + minutes: number; + }; + maxTime: { + hours: number; + minutes: number; + }; + fieldName: string; + }; +}; diff --git a/src/entities/activity/model/mappers.ts b/src/entities/activity/model/mappers.ts index 1b72378d0..63a3ab5ef 100644 --- a/src/entities/activity/model/mappers.ts +++ b/src/entities/activity/model/mappers.ts @@ -44,7 +44,6 @@ function mapTimerValue(dtoTimer: number | null) { if (dtoTimer) { return getMsFromSeconds(dtoTimer); } - return null; } @@ -64,13 +63,95 @@ function mapConditionalLogic(dto: ConditionalLogicDto | null) { conditionalLogic: { match: dto.match, conditions: dto.conditions.map(condition => { + const { itemName, ...rest } = condition; const updatedCondition = { - ...condition, - activityItemName: condition.itemName, + ...rest, + activityItemName: itemName, }; - - // @ts-expect-error - delete updatedCondition.itemName; + switch (condition.type) { + case 'INCLUDES_OPTION': + case 'NOT_INCLUDES_OPTION': + case 'EQUAL_TO_OPTION': + case 'NOT_EQUAL_TO_OPTION': + updatedCondition.payload = { + optionValue: condition.payload.optionValue, + }; + break; + + case 'NOT_EQUAL_TO_ROW_OPTION': + case 'EQUAL_TO_ROW_OPTION': + case 'INCLUDES_ROW_OPTION': + case 'NOT_INCLUDES_ROW_OPTION': + updatedCondition.payload = { + optionValue: condition.payload.optionValue, + rowIndex: condition.payload.rowIndex, + }; + break; + + case 'GREATER_THAN_SLIDER_ROWS': + case 'LESS_THAN_SLIDER_ROWS': + case 'NOT_EQUAL_TO_SLIDER_ROWS': + case 'EQUAL_TO_SLIDER_ROWS': + updatedCondition.payload = { + value: condition.payload.value, + rowIndex: condition.payload.rowIndex, + }; + break; + + case 'BETWEEN_SLIDER_ROWS': + case 'OUTSIDE_OF_SLIDER_ROWS': + updatedCondition.payload = { + maxValue: condition.payload.maxValue, + minValue: condition.payload.minValue, + rowIndex: condition.payload.rowIndex, + }; + break; + + case 'GREATER_THAN_DATE': + case 'LESS_THAN_DATE': + case 'EQUAL_TO_DATE': + case 'NOT_EQUAL_TO_DATE': + updatedCondition.payload = { date: condition.payload.date }; + break; + + case 'BETWEEN_DATES': + case 'OUTSIDE_OF_DATES': + updatedCondition.payload = { + minDate: condition.payload.minDate, + maxDate: condition.payload.maxDate, + }; + break; + + case 'GREATER_THAN_TIME': + case 'LESS_THAN_TIME': + case 'EQUAL_TO_TIME': + case 'NOT_EQUAL_TO_TIME': + updatedCondition.payload = { time: condition.payload.time }; + break; + + case 'OUTSIDE_OF_TIMES_RANGE': + case 'BETWEEN_TIMES_RANGE': + case 'BETWEEN_TIMES': + case 'OUTSIDE_OF_TIMES': + updatedCondition.payload = { + minTime: condition.payload.minTime, + maxTime: condition.payload.maxTime, + }; + break; + + case 'GREATER_THAN_TIME_RANGE': + case 'LESS_THAN_TIME_RANGE': + case 'EQUAL_TO_TIME_RANGE': + case 'NOT_EQUAL_TO_TIME_RANGE': + updatedCondition.payload = { + time: condition.payload.time, + fieldName: condition.payload.fieldName, + }; + break; + + default: + updatedCondition.payload = condition.payload; + } return updatedCondition; }), @@ -240,6 +321,7 @@ function mapToAudio(dto: AudioItemDto): ActivityItem { hasTopNavigation: false, isHidden: dto.isHidden, ...mapAdditionalText(dto.config), + ...mapConditionalLogic(dto.conditionalLogic), }; } @@ -264,6 +346,7 @@ function mapToAudioPlayer(dto: AudioPlayerItemDto): ActivityItem { hasTopNavigation: false, isHidden: dto.isHidden, ...mapAdditionalText(dto.config), + ...mapConditionalLogic(dto.conditionalLogic), }; } @@ -285,6 +368,7 @@ function mapToDate(dto: DateItemDto): ActivityItem { canBeReset: true, hasTopNavigation: false, ...mapAdditionalText(dto.config), + ...mapConditionalLogic(dto.conditionalLogic), }; } @@ -328,6 +412,7 @@ function mapToGeolocation(dto: GeolocationItemDto): ActivityItem { canBeReset: true, hasTopNavigation: false, ...mapAdditionalText(dto.config), + ...mapConditionalLogic(dto.conditionalLogic), }; } @@ -406,6 +491,7 @@ function mapToStackedCheckboxes(dto: MultiSelectionRowsItemDto): ActivityItem { canBeReset: true, hasTopNavigation: false, isHidden: dto.isHidden, + ...mapConditionalLogic(dto.conditionalLogic), }; } @@ -430,6 +516,7 @@ function mapToNumberSelect(dto: NumberSelectionItemDto): ActivityItem { hasTopNavigation: false, isHidden: dto.isHidden, ...mapAdditionalText(dto.config), + ...mapConditionalLogic(dto.conditionalLogic), }; } @@ -451,6 +538,7 @@ function mapToPhoto(dto: PhotoItemDto): ActivityItem { hasTopNavigation: false, isHidden: dto.isHidden, ...mapAdditionalText(dto.config), + ...mapConditionalLogic(dto.conditionalLogic), }; } @@ -509,6 +597,7 @@ function mapToStackedRadio(dto: SingleSelectionRowsItemDto): ActivityItem { canBeReset: true, hasTopNavigation: false, isHidden: dto.isHidden, + ...mapConditionalLogic(dto.conditionalLogic), }; } @@ -567,6 +656,7 @@ function mapToStackedSlider(dto: SliderRowsItemDto): ActivityItem { canBeReset: true, hasTopNavigation: false, isHidden: dto.isHidden, + ...mapConditionalLogic(dto.conditionalLogic), }; } @@ -618,7 +708,6 @@ function mapToParagraphText(dto: ParagraphTextItemDto): ActivityItem { canBeReset: true, hasTopNavigation: false, isHidden: dto.isHidden, - ...mapConditionalLogic(dto.conditionalLogic), }; } @@ -663,6 +752,7 @@ function mapToVideo(dto: VideoItemDto): ActivityItem { hasTopNavigation: false, isHidden: dto.isHidden, ...mapAdditionalText(dto.config), + ...mapConditionalLogic(dto.conditionalLogic), }; } diff --git a/src/entities/conditional-logic/model/conditions.ts b/src/entities/conditional-logic/model/conditions.ts index de016babd..d6170f758 100644 --- a/src/entities/conditional-logic/model/conditions.ts +++ b/src/entities/conditional-logic/model/conditions.ts @@ -1,3 +1,8 @@ +import { isAfter, isBefore, isEqual, parseISO } from 'date-fns'; + +import { timeToMinutes } from '@app/entities/activity/lib/services/timeToMinutes'; +import { HourMinute } from '@app/shared/lib/types/dateTime'; + export const isBetweenValues = ( input: Maybe, min: number, @@ -10,6 +15,13 @@ export const isBetweenValues = ( return input > min && input < max; }; +function isValidTimeFormat( + time: { hours: number; minutes: number } | null | undefined, +): boolean { + return ( + !!time && typeof time.hours === 'number' && typeof time.minutes === 'number' + ); +} export const isOutsideOfValues = ( input: Maybe, min: number, @@ -22,7 +34,6 @@ export const isOutsideOfValues = ( return input < min || input > max; }; -// and isEqualToOption export const isEqualToValue = ( input: unknown, valueToCompareWith: NonNullable, @@ -74,3 +85,285 @@ export const doesNotIncludeValue = ( return !input.includes(value); }; + +export const isGreaterThanDate = (input: Maybe, date: string) => { + if (!input) return false; + return isAfter(parseISO(input), parseISO(date)); +}; +export const isLessThanDate = (input: Maybe, date: string) => { + if (!input) return false; + return isBefore(parseISO(input), parseISO(date)); +}; + +export const isEqualToDate = (input: Maybe, date: string) => { + if (!input) return false; + return isEqual(parseISO(input), parseISO(date)); +}; + +export const isBetweenDates = ( + input: Maybe, + minDate: string, + maxDate: string, +) => { + if (!input) return false; + return input + ? !isBefore(parseISO(input), parseISO(minDate)) && + !isAfter(parseISO(input), parseISO(maxDate)) + : false; +}; + +export const isNotEqualToDate = (input: Maybe, date: string) => { + if (!input) return false; + return !isEqual(parseISO(input), parseISO(date)); +}; + +export const isOutsideOfDates = ( + input: Maybe, + minDate: string, + maxDate: string, +) => { + if (!input) return false; + return input + ? isBefore(parseISO(input), parseISO(minDate)) || + isAfter(parseISO(input), parseISO(maxDate)) + : false; +}; + +const getTimeBasedOnFieldName = ( + fieldName: string, + timeRange: { startTime: HourMinute | null; endTime: HourMinute | null }, +): HourMinute | null => { + return fieldName === 'from' ? timeRange.startTime : timeRange.endTime; +}; +const parseTimeString = (time: string): HourMinute => { + const [hours, minutes] = time.split(':').map(Number); + return { hours, minutes }; +}; +export const isGreaterThanTimeRange = ( + timeRange: Maybe<{ + startTime: HourMinute | null; + endTime: HourMinute | null; + }>, + { time, fieldName }: { time: HourMinute; fieldName: string }, +): boolean => { + if (!isValidTimeFormat(time) || !timeRange) return false; + const selectedTime = getTimeBasedOnFieldName(fieldName, timeRange); + const normalizedTime = + typeof time === 'string' ? parseTimeString(time) : time; + + return selectedTime + ? timeToMinutes(selectedTime) > timeToMinutes(normalizedTime) + : false; +}; + +export const isLessThanTimeRange = ( + timeRange: Maybe<{ + startTime: HourMinute | null; + endTime: HourMinute | null; + }>, + { time, fieldName }: { time: HourMinute; fieldName: string }, +): boolean => { + if (!isValidTimeFormat(time) || !timeRange) return false; + + const selectedTime = getTimeBasedOnFieldName(fieldName, timeRange); + const normalizedTime = + typeof time === 'string' ? parseTimeString(time) : time; + + return selectedTime + ? timeToMinutes(selectedTime) < timeToMinutes(normalizedTime) + : false; +}; + +export const isEqualToTimeRange = ( + timeRange: Maybe<{ + startTime: HourMinute | null; + endTime: HourMinute | null; + }>, + { time, fieldName }: { time: HourMinute; fieldName: string }, +): boolean => { + if (!isValidTimeFormat(time) || !timeRange) return false; + + const selectedTime = getTimeBasedOnFieldName(fieldName, timeRange); + const normalizedTime = + typeof time === 'string' ? parseTimeString(time) : time; + + return selectedTime + ? timeToMinutes(selectedTime) === timeToMinutes(normalizedTime) + : false; +}; + +export const isNotEqualToTimeRange = ( + timeRange: Maybe<{ + startTime: HourMinute | null; + endTime: HourMinute | null; + }>, + { time, fieldName }: { time: HourMinute; fieldName: string }, +): boolean => { + if (!isValidTimeFormat(time) || !timeRange) return false; + const selectedTime = getTimeBasedOnFieldName(fieldName, timeRange); + const normalizedTime = + typeof time === 'string' ? parseTimeString(time) : time; + + return selectedTime + ? timeToMinutes(selectedTime) !== timeToMinutes(normalizedTime) + : false; +}; + +export const isBetweenTimesRange = ( + timeRange: Maybe<{ + startTime: HourMinute | null; + endTime: HourMinute | null; + }>, + { + minTime, + maxTime, + fieldName, + }: { minTime: HourMinute; maxTime: HourMinute; fieldName: string }, +): boolean => { + if (!timeRange) return false; + const selectedTime = getTimeBasedOnFieldName(fieldName, timeRange); + const selectedTimeInMinutes = selectedTime + ? timeToMinutes(selectedTime) + : null; + + return ( + selectedTimeInMinutes !== null && + selectedTimeInMinutes >= timeToMinutes(minTime) && + selectedTimeInMinutes <= timeToMinutes(maxTime) + ); +}; + +export const isOutsideOfTimesRange = ( + timeRange: Maybe<{ + startTime: HourMinute | null; + endTime: HourMinute | null; + }>, + { + minTime, + maxTime, + fieldName, + }: { minTime: HourMinute; maxTime: HourMinute; fieldName: string }, +): boolean => { + if (!timeRange) return false; + const selectedTime = getTimeBasedOnFieldName(fieldName, timeRange); + const selectedTimeInMinutes = selectedTime + ? timeToMinutes(selectedTime) + : null; + + return ( + selectedTimeInMinutes !== null && + (selectedTimeInMinutes < timeToMinutes(minTime) || + selectedTimeInMinutes > timeToMinutes(maxTime)) + ); +}; + +export const isGreaterThanTime = ( + input: Maybe<{ hours: number; minutes: number }>, + time: { hours: number; minutes: number }, +): boolean => { + if (!isValidTimeFormat(time) || !input) return false; + const inputMinutes = timeToMinutes(input); + const conditionMinutes = timeToMinutes(time); + + const result = inputMinutes > conditionMinutes; + + return result; +}; + +export const isLessThanTime = ( + input: Maybe<{ hours: number; minutes: number }>, + time: { hours: number; minutes: number }, +) => { + if (!isValidTimeFormat(time) || !input) return false; + + const inputMinutes = timeToMinutes(input); + const conditionMinutes = timeToMinutes(time); + + const result = inputMinutes < conditionMinutes; + + return result; +}; + +export const isEqualToTime = ( + input: Maybe<{ hours: number; minutes: number }>, + time: { hours: number; minutes: number }, +) => { + if (!isValidTimeFormat(time) || !input) return false; + + const inputMinutes = timeToMinutes(input); + const conditionMinutes = timeToMinutes(time); + + const result = inputMinutes === conditionMinutes; + + return result; +}; + +export const isNotEqualToTime = ( + input: Maybe<{ hours: number; minutes: number }>, + time: { hours: number; minutes: number }, +) => { + if (!isValidTimeFormat(time) || !input) return false; + + const inputMinutes = timeToMinutes(input); + const conditionMinutes = timeToMinutes(time); + + const result = inputMinutes !== conditionMinutes; + + return result; +}; + +export const isBetweenTimes = ( + input: Maybe<{ hours: number; minutes: number }>, + minTime: { hours: number; minutes: number }, + maxTime: { hours: number; minutes: number }, +) => { + if (!input) return false; + + return isGreaterThanTime(input, minTime) && isLessThanTime(input, maxTime); +}; + +export const isOutsideOfTimes = ( + input: Maybe<{ hours: number; minutes: number }>, + minTime: { hours: number; minutes: number }, + maxTime: { hours: number; minutes: number }, +) => { + if (!input) return false; + return isLessThanTime(input, minTime) || isGreaterThanTime(input, maxTime); +}; + +export const isOutsideOfSliderRowValues = ( + rowValue: number | null, + minValue: number, + maxValue: number, +) => { + return rowValue !== null && (rowValue < minValue || rowValue > maxValue); +}; + +export const isBetweenSliderRowValues = ( + rowValue: number | null, + minValue: number, + maxValue: number, +) => { + return rowValue !== null && rowValue >= minValue && rowValue <= maxValue; +}; + +export const isLessThanSliderRow = (rowValue: number | null, value: number) => { + return rowValue !== null && rowValue < value; +}; + +export const isGreaterThanSliderRow = ( + rowValue: number | null, + value: number, +) => { + return rowValue !== null && rowValue > value; +}; +export const isNotEqualToSliderRow = ( + rowValue: number | null, + value: number, +) => { + return rowValue !== null && rowValue !== value; +}; + +export const isEqualToSliderRow = (rowValue: number | null, value: number) => { + return rowValue !== null && rowValue === value; +}; diff --git a/src/features/pass-survey/model/AnswerValidator.ts b/src/features/pass-survey/model/AnswerValidator.ts index 44bb32e35..24135ec47 100644 --- a/src/features/pass-survey/model/AnswerValidator.ts +++ b/src/features/pass-survey/model/AnswerValidator.ts @@ -6,7 +6,32 @@ import { isGreaterThan, isLessThan, isOutsideOfValues, + isGreaterThanTime, + isLessThanTime, + isEqualToTime, + isNotEqualToTime, + isNotEqualToDate, + isBetweenDates, + isBetweenTimes, + isGreaterThanTimeRange, + isLessThanTimeRange, + isEqualToTimeRange, + isNotEqualToTimeRange, + isBetweenTimesRange, + isOutsideOfTimesRange, + isOutsideOfTimes, + isOutsideOfSliderRowValues, + isBetweenSliderRowValues, + isLessThanSliderRow, + isGreaterThanSliderRow, + isNotEqualToSliderRow, + isEqualToSliderRow, + isGreaterThanDate, + isLessThanDate, + isEqualToDate, + isOutsideOfDates, } from '@app/entities/conditional-logic/model/conditions'; +import { HourMinute } from '@app/shared/lib/types/dateTime'; import { Item } from '@app/shared/ui/survey/CheckBox/types'; import { AnswerValidatorArgs, @@ -26,6 +51,67 @@ export function AnswerValidator( const currentPipelineItem = items?.[step]; const currentAnswer = answers?.[step]; + const getAnswerDate = (): string | null => { + const answer = currentAnswer?.answer as Maybe; + return answer ?? null; + }; + + const getAnswerTime = (): { hours: number; minutes: number } | null => { + let answer = currentAnswer?.answer as Maybe<{ + hours: number; + minutes: number; + }>; + + if (typeof answer === 'string') { + answer = convertTo24HourFormat(answer); + } + + return answer ?? null; + }; + + const getAnswerTimeRange = (): { + startTime: { hours: number; minutes: number } | null; + endTime: { hours: number; minutes: number } | null; + } | null => { + const answer = currentAnswer?.answer as Maybe; + return answer ?? null; + }; + + const convertTo24HourFormat = ( + timeStr: string, + ): { hours: number; minutes: number } => { + const [time, modifier] = timeStr.split(' '); + const [hoursStr, minutesStr] = time.split(':'); + const hours = parseInt(hoursStr, 10); + const minutes = parseInt(minutesStr, 10); + + let adjustedHours = hours; + if (modifier.toLowerCase() === 'pm' && hours < 12) { + adjustedHours += 12; + } + if (modifier.toLowerCase() === 'am' && hours === 12) { + adjustedHours = 0; + } + + return { hours: adjustedHours, minutes }; + }; + + const getSliderRowValue = (rowIndex: number): number | null => { + const answer = currentAnswer?.answer as Maybe; + return answer && answer[rowIndex] !== undefined ? answer[rowIndex] : null; + }; + + const getRowOptionValues = (rowIndex: number): string[] | null => { + const answer = currentAnswer?.answer as Maybe< + { id: string; text: string }[][] + >; + if (answer && answer[rowIndex]) { + const optionIds = answer[rowIndex].map(option => option.id); + return optionIds; + } + return null; + }; + return { isCorrect() { if (!currentPipelineItem?.validationOptions) { @@ -33,44 +119,103 @@ export function AnswerValidator( } const { correctAnswer } = currentPipelineItem.validationOptions; - return correctAnswer === currentAnswer?.answer; }, + isEqualToSliderRow(rowIndex: number, value: number) { + const rowValue = getSliderRowValue(rowIndex); + return isEqualToSliderRow(rowValue, value); + }, + + isNotEqualToSliderRow(rowIndex: number, value: number) { + const rowValue = getSliderRowValue(rowIndex); + return isNotEqualToSliderRow(rowValue, value); + }, + + isGreaterThanSliderRow(rowIndex: number, value: number) { + const rowValue = getSliderRowValue(rowIndex); + + return isGreaterThanSliderRow(rowValue, value); + }, + + isLessThanSliderRow(rowIndex: number, value: number) { + const rowValue = getSliderRowValue(rowIndex); + return isLessThanSliderRow(rowValue, value); + }, + + isBetweenSliderRowValues( + rowIndex: number, + minValue: number, + maxValue: number, + ) { + const rowValue = getSliderRowValue(rowIndex); + + return isBetweenSliderRowValues(rowValue, minValue, maxValue); + }, + + isOutsideOfSliderRowValues( + rowIndex: number, + minValue: number, + maxValue: number, + ) { + const rowValue = getSliderRowValue(rowIndex); + + return isOutsideOfSliderRowValues(rowValue, minValue, maxValue); + }, + + isEqualToRowOption(rowIndex: number, optionValue: string) { + const selectedOption = getRowOptionValues(rowIndex); + return selectedOption !== null && selectedOption.includes(optionValue); + }, + + isNotEqualToRowOption(rowIndex: number, optionValue: string) { + const selectedOption = getRowOptionValues(rowIndex); + return selectedOption !== null && !selectedOption.includes(optionValue); + }, + + includesRowOption(rowIndex: number, optionValue: string) { + const selectedOptions = getRowOptionValues(Number(rowIndex)); + return ( + selectedOptions !== null && includesValue(selectedOptions, optionValue) + ); + }, + + notIncludesRowOption(rowIndex: number, optionValue: string) { + const selectedOptions = getRowOptionValues(Number(rowIndex)); + return ( + selectedOptions !== null && + doesNotIncludeValue(selectedOptions, optionValue) + ); + }, + isBetweenValues(min: number, max: number) { const answer = currentAnswer?.answer as Maybe; - return isBetweenValues(answer, min, max); }, isOutsideOfValues(min: number, max: number) { const answer = currentAnswer?.answer as Maybe; - return isOutsideOfValues(answer, min, max); }, isEqualToValue(value: any) { const answer = currentAnswer?.answer as Maybe; - return isEqualToValue(answer, value); }, isEqualToOption(optionValue: string) { const answer = currentAnswer?.answer as Maybe; const selectedOption = answer?.value?.toString(); - return isEqualToValue(selectedOption, optionValue); }, - isGreaterThen(value: number) { + isGreaterThan(value: number) { const answer = currentAnswer?.answer as Maybe; - return isGreaterThan(answer, value); }, - isLessThen(value: number) { + isLessThan(value: number) { const answer = currentAnswer?.answer as Maybe; - return isLessThan(answer, value); }, @@ -78,7 +223,6 @@ export function AnswerValidator( const answer = (currentAnswer?.answer as Maybe)?.map(item => item.value.toString(), ); - return includesValue(answer, optionValue); }, @@ -86,9 +230,126 @@ export function AnswerValidator( const answer = (currentAnswer?.answer as Maybe)?.map(item => item.value.toString(), ); - return doesNotIncludeValue(answer, optionValue); }, + + isGreaterThanDate(date: string) { + const answerDate = getAnswerDate(); + return isGreaterThanDate(answerDate, date); + }, + + isLessThanDate(date: string) { + const answerDate = getAnswerDate(); + return isLessThanDate(answerDate, date); + }, + + isEqualToDate(date: string) { + const answerDate = getAnswerDate(); + return isEqualToDate(answerDate, date); + }, + + isNotEqualToDate(date: string) { + const answerDate = getAnswerDate(); + return isNotEqualToDate(answerDate, date); + }, + + isBetweenDates(minDate: string, maxDate: string) { + const answerDate = getAnswerDate(); + return isBetweenDates(answerDate, minDate, maxDate); + }, + + isOutsideOfDates(minDate: string, maxDate: string) { + const answerDate = getAnswerDate(); + return isOutsideOfDates(answerDate, minDate, maxDate); + }, + + isGreaterThanTime(time: { hours: number; minutes: number }) { + const answerTime = getAnswerTime(); + return isGreaterThanTime(answerTime, time); + }, + + isLessThanTime(time: { hours: number; minutes: number }) { + const answerTime = getAnswerTime(); + + return isLessThanTime(answerTime, time); + }, + + isEqualToTime(time: { hours: number; minutes: number }) { + const answerTime = getAnswerTime(); + + return isEqualToTime(answerTime, time); + }, + + isNotEqualToTime(time: { hours: number; minutes: number }) { + const answerTime = getAnswerTime(); + + return isNotEqualToTime(answerTime, time); + }, + + isBetweenTimes( + minTime: { hours: number; minutes: number }, + maxTime: { hours: number; minutes: number }, + ) { + const answerTime = getAnswerTime(); + return isBetweenTimes(answerTime, minTime, maxTime); + }, + + isOutsideOfTimes( + minTime: { hours: number; minutes: number }, + maxTime: { hours: number; minutes: number }, + ) { + const answerTime = getAnswerTime(); + return isOutsideOfTimes(answerTime, minTime, maxTime); + }, + + isGreaterThanTimeRange(time: HourMinute, fieldName: string) { + const answerTimeRange = getAnswerTimeRange(); + + return isGreaterThanTimeRange(answerTimeRange, { time, fieldName }); + }, + + isEqualToTimeRange(time: HourMinute, fieldName: string) { + const answerTimeRange = getAnswerTimeRange(); + + return isEqualToTimeRange(answerTimeRange, { time, fieldName }); + }, + + isLessThanTimeRange(time: HourMinute, fieldName: string) { + const answerTimeRange = getAnswerTimeRange(); + return isLessThanTimeRange(answerTimeRange, { time, fieldName }); + }, + + isNotEqualToTimeRange(time: HourMinute, fieldName: string) { + const answerTimeRange = getAnswerTimeRange(); + return isNotEqualToTimeRange(answerTimeRange, { time, fieldName }); + }, + isBetweenTimesRange( + minTime: HourMinute, + maxTime: HourMinute, + fieldName: string, + ) { + const answerTimeRange = getAnswerTimeRange(); + return isBetweenTimesRange(answerTimeRange, { + minTime, + maxTime, + fieldName, + }); + }, + + isOutsideOfTimesRange( + minTime: HourMinute, + maxTime: HourMinute, + fieldName: string, + ) { + const answerTimeRange = getAnswerTimeRange(); + + return isOutsideOfTimesRange(answerTimeRange, { + minTime, + maxTime, + fieldName, + }); + }, + isValidAnswer() { switch (currentPipelineItem?.type) { case 'TimeRange': { diff --git a/src/features/pass-survey/model/IAnswerValidator.ts b/src/features/pass-survey/model/IAnswerValidator.ts index 719bc160d..82d64deb6 100644 --- a/src/features/pass-survey/model/IAnswerValidator.ts +++ b/src/features/pass-survey/model/IAnswerValidator.ts @@ -18,13 +18,102 @@ export interface IAnswerValidator { isEqualToOption(optionValue: string): boolean; - isGreaterThen(value: number): boolean; + isGreaterThan(value: number): boolean; - isLessThen(value: number): boolean; + isLessThan(value: number): boolean; includesOption(optionValue: string): boolean; notIncludesOption(optionValue: string): boolean; + isGreaterThanDate(date: string): boolean; + + isLessThanDate(date: string): boolean; + + isEqualToDate(date: string): boolean; + + isNotEqualToDate(date: string): boolean; + + isBetweenDates(minDate: string, maxDate: string): boolean; + + isOutsideOfDates(minDate: string, maxDate: string): boolean; + + isGreaterThanTime(time: { hours: number; minutes: number }): boolean; + + isLessThanTime(time: { hours: number; minutes: number }): boolean; + + isEqualToTime(time: { hours: number; minutes: number }): boolean; + + isNotEqualToTime(time: { hours: number; minutes: number }): boolean; + + isBetweenTimes( + minTime: { hours: number; minutes: number }, + maxTime: { hours: number; minutes: number }, + ): boolean; + + isOutsideOfTimes( + minTime: { hours: number; minutes: number }, + maxTime: { hours: number; minutes: number }, + ): boolean; + + isGreaterThanTimeRange( + time: { hours: number; minutes: number }, + fieldName: string, + ): boolean; + + isLessThanTimeRange( + time: { hours: number; minutes: number }, + fieldName: string, + ): boolean; + + isEqualToTimeRange( + time: { hours: number; minutes: number }, + fieldName: string, + ): boolean; + + isNotEqualToTimeRange( + time: { hours: number; minutes: number }, + fieldName: string, + ): boolean; + + isBetweenTimesRange( + minTime: { hours: number; minutes: number }, + maxTime: { hours: number; minutes: number }, + fieldName: string, + ): boolean; + + isOutsideOfTimesRange( + minTime: { hours: number; minutes: number }, + maxTime: { hours: number; minutes: number }, + fieldName: string, + ): boolean; + isEqualToSliderRow(rowIndex: number, value: number): boolean; + + isNotEqualToSliderRow(rowIndex: number, value: number): boolean; + + isGreaterThanSliderRow(rowIndex: number, value: number): boolean; + + isLessThanSliderRow(rowIndex: number, value: number): boolean; + + isBetweenSliderRowValues( + rowIndex: number, + minValue: number, + maxValue: number, + ): boolean; + + isOutsideOfSliderRowValues( + rowIndex: number, + minValue: number, + maxValue: number, + ): boolean; + + isEqualToRowOption(rowIndex: number, optionValue: string): boolean; + + isNotEqualToRowOption(rowIndex: number, optionValue: string): boolean; + + includesRowOption(rowIndex: number, optionValue: string): boolean; + + notIncludesRowOption(rowIndex: number, optionValue: string): boolean; + isValidAnswer(): boolean; } diff --git a/src/features/pass-survey/model/PipelineVisibilityChecker.ts b/src/features/pass-survey/model/PipelineVisibilityChecker.ts index 3f706e49c..bc841a36b 100644 --- a/src/features/pass-survey/model/PipelineVisibilityChecker.ts +++ b/src/features/pass-survey/model/PipelineVisibilityChecker.ts @@ -6,6 +6,20 @@ export function PipelineVisibilityChecker( pipeline: PipelineItem[], answers: Answers, ) { + function parseTimeString(timeStr: string) { + const [hours, minutes] = timeStr.split(':').map(Number); + return { hours, minutes }; + } + + function isValidTimeFormat( + time: { hours: number; minutes: number } | null | undefined, + ): boolean { + return ( + !!time && + typeof time.hours === 'number' && + typeof time.minutes === 'number' + ); + } function isItemVisible(index: number) { if (index >= pipeline.length) { return false; @@ -37,12 +51,11 @@ export function PipelineVisibilityChecker( condition.payload.maxValue, ); - case 'OUTSIDE_OF': { + case 'OUTSIDE_OF': return answerValidator.isOutsideOfValues( condition.payload.minValue, condition.payload.maxValue, ); - } case 'EQUAL': return answerValidator.isEqualToValue(condition.payload.value); @@ -59,10 +72,10 @@ export function PipelineVisibilityChecker( ); case 'GREATER_THAN': - return answerValidator.isGreaterThen(condition.payload.value); + return answerValidator.isGreaterThan(condition.payload.value); case 'LESS_THAN': - return answerValidator.isLessThen(condition.payload.value); + return answerValidator.isLessThan(condition.payload.value); case 'INCLUDES_OPTION': return answerValidator.includesOption(condition.payload.optionValue); @@ -72,6 +85,167 @@ export function PipelineVisibilityChecker( condition.payload.optionValue, ); + case 'GREATER_THAN_DATE': + return answerValidator.isGreaterThanDate(condition.payload.date); + + case 'LESS_THAN_DATE': + return answerValidator.isLessThanDate(condition.payload.date); + + case 'EQUAL_TO_DATE': + return answerValidator.isEqualToDate(condition.payload.date); + + case 'NOT_EQUAL_TO_DATE': + return answerValidator.isNotEqualToDate(condition.payload.date); + + case 'BETWEEN_DATES': + return answerValidator.isBetweenDates( + condition.payload.minDate, + condition.payload.maxDate, + ); + + case 'OUTSIDE_OF_DATES': + return answerValidator.isOutsideOfDates( + condition.payload.minDate, + condition.payload.maxDate, + ); + + case 'GREATER_THAN_TIME': { + let conditionTime = condition.payload?.time; + + if (typeof conditionTime === 'string') { + conditionTime = parseTimeString(conditionTime); + } + + if (!isValidTimeFormat(conditionTime)) { + return false; + } + + const isGreater = answerValidator.isGreaterThanTime(conditionTime); + + return isGreater; + } + + case 'LESS_THAN_TIME': + return answerValidator.isLessThanTime(condition.payload.time); + + case 'EQUAL_TO_TIME': + return answerValidator.isEqualToTime(condition.payload.time); + + case 'NOT_EQUAL_TO_TIME': + return answerValidator.isNotEqualToTime(condition.payload.time); + + case 'BETWEEN_TIMES': + return answerValidator.isBetweenTimes( + condition.payload.minTime, + condition.payload.maxTime, + ); + + case 'OUTSIDE_OF_TIMES': + return answerValidator.isOutsideOfTimes( + condition.payload.minTime, + condition.payload.maxTime, + ); + + case 'GREATER_THAN_TIME_RANGE': + return answerValidator.isGreaterThanTimeRange( + condition.payload.time, + condition.payload.fieldName, + ); + + case 'LESS_THAN_TIME_RANGE': + return answerValidator.isLessThanTimeRange( + condition.payload.time, + condition.payload.fieldName, + ); + + case 'EQUAL_TO_TIME_RANGE': + return answerValidator.isEqualToTimeRange( + condition.payload.time, + condition.payload.fieldName, + ); + + case 'NOT_EQUAL_TO_TIME_RANGE': + return answerValidator.isNotEqualToTimeRange( + condition.payload.time, + condition.payload.fieldName, + ); + + case 'BETWEEN_TIMES_RANGE': + return answerValidator.isBetweenTimesRange( + condition.payload.minTime, + condition.payload.maxTime, + condition.payload.fieldName, + ); + + case 'OUTSIDE_OF_TIMES_RANGE': + return answerValidator.isOutsideOfTimesRange( + condition.payload.minTime, + condition.payload.maxTime, + condition.payload.fieldName, + ); + + case 'EQUAL_TO_SLIDER_ROWS': + return answerValidator.isEqualToSliderRow( + condition.payload.rowIndex, + condition.payload.value, + ); + + case 'NOT_EQUAL_TO_SLIDER_ROWS': + return answerValidator.isNotEqualToSliderRow( + condition.payload.rowIndex, + condition.payload.value, + ); + + case 'GREATER_THAN_SLIDER_ROWS': + return answerValidator.isGreaterThanSliderRow( + condition.payload.rowIndex, + condition.payload.value, + ); + + case 'LESS_THAN_SLIDER_ROWS': + return answerValidator.isLessThanSliderRow( + condition.payload.rowIndex, + condition.payload.value, + ); + + case 'BETWEEN_SLIDER_ROWS': + return answerValidator.isBetweenSliderRowValues( + condition.payload.rowIndex, + condition.payload.minValue, + condition.payload.maxValue, + ); + + case 'OUTSIDE_OF_SLIDER_ROWS': + return answerValidator.isOutsideOfSliderRowValues( + condition.payload.rowIndex, + condition.payload.minValue, + condition.payload.maxValue, + ); + + case 'EQUAL_TO_ROW_OPTION': + return answerValidator.isEqualToRowOption( + condition.payload.rowIndex, + condition.payload.optionValue, + ); + + case 'NOT_EQUAL_TO_ROW_OPTION': + return answerValidator.isNotEqualToRowOption( + condition.payload.rowIndex, + condition.payload.optionValue, + ); + + case 'INCLUDES_ROW_OPTION': + return answerValidator.includesRowOption( + condition.payload.rowIndex, + condition.payload.optionValue, + ); + + case 'NOT_INCLUDES_ROW_OPTION': + return answerValidator.notIncludesRowOption( + condition.payload.rowIndex, + condition.payload.optionValue, + ); + default: return true; } diff --git a/src/features/pass-survey/model/tests/AnswerValidator.test.ts b/src/features/pass-survey/model/tests/AnswerValidator.test.ts index 2efd1eb8c..93ca01ccc 100644 --- a/src/features/pass-survey/model/tests/AnswerValidator.test.ts +++ b/src/features/pass-survey/model/tests/AnswerValidator.test.ts @@ -24,11 +24,11 @@ const imitateValidate = (validator: IAnswerValidator, type: ConditionType) => { break; } case 'GREATER_THAN': { - result = validator.isGreaterThen(0); + result = validator.isGreaterThan(0); break; } case 'LESS_THAN': { - result = validator.isLessThen(10); + result = validator.isLessThan(10); break; } case 'INCLUDES_OPTION': { diff --git a/src/features/pass-survey/model/tests/PipelineVisibilityChecker.test.ts b/src/features/pass-survey/model/tests/PipelineVisibilityChecker.test.ts index 15a8ebb33..1e2b96bef 100644 --- a/src/features/pass-survey/model/tests/PipelineVisibilityChecker.test.ts +++ b/src/features/pass-survey/model/tests/PipelineVisibilityChecker.test.ts @@ -1,6 +1,7 @@ import { ConditionType, Match, + Condition, } from '@app/entities/activity/lib/types/conditionalLogic'; import { @@ -11,6 +12,7 @@ import { getEmptyRadioItem as getRadioItem, getSliderItem, getRadioResponse, + getStackedSliderItem, } from './testHelpers'; import { Answers } from '../../lib/hooks/useActivityStorageRecord'; import { PipelineItem } from '../../lib/types/payload'; @@ -36,9 +38,15 @@ describe('PipelineVisibilityChecker: penetration tests', () => { type: ConditionType, payload: any, ) => { + const condition: Condition = { + activityItemName: itemPointedTo.name!, + type, + payload, + } as Condition; + itemSetTo.conditionalLogic = { match: match, - conditions: [{ activityItemName: itemPointedTo.name!, type, payload }], + conditions: [condition], }; }; @@ -219,7 +227,7 @@ describe('PipelineVisibilityChecker: penetration tests', () => { maxValue: 4, }); - const answers: Answers = { '0': { answer: 1 }, '1': { answer: 2 } }; + const answers: Answers = { '0': { answer: 1 }, '1': { answer: 4 } }; const { isItemVisible } = PipelineVisibilityChecker(items, answers); @@ -479,7 +487,7 @@ describe('PipelineVisibilityChecker: penetration tests', () => { payload: { optionValue: '3', }, - }); + } as Condition); const answers: Answers = { '0': { answer: getCheckboxResponse([1, 2]) }, @@ -506,7 +514,7 @@ describe('PipelineVisibilityChecker: penetration tests', () => { payload: { optionValue: '3', }, - }); + } as Condition); const answers: Answers = { '0': { answer: getCheckboxResponse([1, 2]) }, @@ -519,4 +527,44 @@ describe('PipelineVisibilityChecker: penetration tests', () => { expect(result).toEqual(true); }); + + it('Should return true when slider row value equals specified value and type is EQUAL_TO_SLIDER_ROWS', () => { + const items = [getRadioItem('mock-radio-1'), getStackedSliderItem()]; + items[1].name = 'mock-slider-1'; + + setCondition(items[0], items[1], 'any', 'EQUAL_TO_SLIDER_ROWS', { + rowIndex: 0, + value: 5, + }); + + const answers: Answers = { + '1': { answer: [5] }, + }; + + const { isItemVisible } = PipelineVisibilityChecker(items, answers); + + const result = isItemVisible(0); + + expect(result).toEqual(true); + }); + + it('Should return false when slider row value does not equal specified value and type is EQUAL_TO_SLIDER_ROWS', () => { + const items = [getRadioItem('mock-radio-1'), getStackedSliderItem()]; + items[1].name = 'mock-slider-1'; + + setCondition(items[0], items[1], 'any', 'EQUAL_TO_SLIDER_ROWS', { + rowIndex: 0, + value: 5, + }); + + const answers: Answers = { + '1': { answer: [3] }, + }; + + const { isItemVisible } = PipelineVisibilityChecker(items, answers); + + const result = isItemVisible(0); + + expect(result).toEqual(false); + }); }); diff --git a/src/shared/api/services/ActivityItemDto.ts b/src/shared/api/services/ActivityItemDto.ts index 1e0cb8866..22a5147ed 100644 --- a/src/shared/api/services/ActivityItemDto.ts +++ b/src/shared/api/services/ActivityItemDto.ts @@ -45,7 +45,127 @@ type ConditionDto = | EqualConditionDto | NotEqualConditionDto | BetweenConditionDto - | OutsideOfConditionDto; + | OutsideOfConditionDto + | GreaterThanDateConditionDto + | LessThanDateConditionDto + | EqualToDateConditionDto + | NotEqualToDateConditionDto + | BetweenDatesConditionDto + | OutsideOfDatesConditionDto + | GreaterThanTimeConditionDto + | LessThanTimeConditionDto + | EqualToTimeConditionDto + | NotEqualToTimeConditionDto + | BetweenTimesConditionDto + | OutsideOfTimesConditionDto + | GreaterThanTimeRangeConditionDto + | LessThanTimeRangeConditionDto + | EqualToTimeRangeConditionDto + | NotEqualToTimeRangeConditionDto + | BetweenTimesRangeConditionDto + | OutsideOfTimesRangeConditionDto + | EqualToRowOptionConditionDto + | NotEqualToRowOptionConditionDto + | IncludesRowOptionConditionDto + | NotIncludesRowOptionConditionDto + | EqualToSliderRowConditionDto + | NotEqualToSliderRowConditionDto + | GreaterThanSliderRowConditionDto + | LessThanSliderRowConditionDto + | BetweenSliderRowConditionDto + | OutsideOfSliderRowConditionDto; + +type EqualToRowOptionConditionDto = { + itemName: string; + type: 'EQUAL_TO_ROW_OPTION'; + payload: { + optionValue: string; + rowIndex: number; + }; +}; + +type NotEqualToRowOptionConditionDto = { + itemName: string; + type: 'NOT_EQUAL_TO_ROW_OPTION'; + payload: { + optionValue: string; + rowIndex: number; + }; +}; + +type EqualToSliderRowConditionDto = { + itemName: string; + type: 'EQUAL_TO_SLIDER_ROWS'; + payload: { + value: number; + rowIndex: number; + }; +}; + +type NotEqualToSliderRowConditionDto = { + itemName: string; + type: 'NOT_EQUAL_TO_SLIDER_ROWS'; + payload: { + value: number; + rowIndex: number; + }; +}; + +type GreaterThanSliderRowConditionDto = { + itemName: string; + type: 'GREATER_THAN_SLIDER_ROWS'; + payload: { + value: number; + rowIndex: number; + }; +}; + +type LessThanSliderRowConditionDto = { + itemName: string; + type: 'LESS_THAN_SLIDER_ROWS'; + payload: { + value: number; + rowIndex: number; + }; +}; + +type BetweenSliderRowConditionDto = { + itemName: string; + type: 'BETWEEN_SLIDER_ROWS'; + payload: { + minValue: number; + maxValue: number; + rowIndex: number; + }; +}; + +type OutsideOfSliderRowConditionDto = { + itemName: string; + type: 'OUTSIDE_OF_SLIDER_ROWS'; + payload: { + minValue: number; + maxValue: number; + rowIndex: number; + }; +}; + +type IncludesRowOptionConditionDto = { + itemName: string; + type: 'INCLUDES_ROW_OPTION'; + payload: { + optionValue: string; + rowIndex: number; + }; +}; + +type NotIncludesRowOptionConditionDto = { + itemName: string; + type: 'NOT_INCLUDES_ROW_OPTION'; + payload: { + optionValue: string; + rowIndex: number; + }; +}; type IncludesOptionConditionDto = { itemName: string; @@ -128,6 +248,209 @@ type OutsideOfConditionDto = { maxValue: number; }; }; +type GreaterThanDateConditionDto = { + itemName: string; + type: 'GREATER_THAN_DATE'; + payload: { + date: string; + }; +}; + +type LessThanDateConditionDto = { + itemName: string; + type: 'LESS_THAN_DATE'; + payload: { + date: string; + }; +}; + +type EqualToDateConditionDto = { + itemName: string; + type: 'EQUAL_TO_DATE'; + payload: { + date: string; + }; +}; + +type NotEqualToDateConditionDto = { + itemName: string; + type: 'NOT_EQUAL_TO_DATE'; + payload: { + date: string; + }; +}; + +type BetweenDatesConditionDto = { + itemName: string; + type: 'BETWEEN_DATES'; + payload: { + minDate: string; + maxDate: string; + }; +}; + +type OutsideOfDatesConditionDto = { + itemName: string; + type: 'OUTSIDE_OF_DATES'; + payload: { + minDate: string; + maxDate: string; + }; +}; + +type GreaterThanTimeConditionDto = { + itemName: string; + type: 'GREATER_THAN_TIME'; + payload: { + time: { + hours: number; + minutes: number; + }; + }; +}; + +type LessThanTimeConditionDto = { + itemName: string; + type: 'LESS_THAN_TIME'; + payload: { + time: { + hours: number; + minutes: number; + }; + }; +}; + +type EqualToTimeConditionDto = { + itemName: string; + type: 'EQUAL_TO_TIME'; + payload: { + time: { + hours: number; + minutes: number; + }; + }; +}; + +type NotEqualToTimeConditionDto = { + itemName: string; + type: 'NOT_EQUAL_TO_TIME'; + payload: { + time: { + hours: number; + minutes: number; + }; + }; +}; + +type BetweenTimesConditionDto = { + itemName: string; + type: 'BETWEEN_TIMES'; + payload: { + minTime: { + hours: number; + minutes: number; + }; + maxTime: { + hours: number; + minutes: number; + }; + }; +}; + +type OutsideOfTimesConditionDto = { + itemName: string; + type: 'OUTSIDE_OF_TIMES'; + payload: { + minTime: { + hours: number; + minutes: number; + }; + maxTime: { + hours: number; + minutes: number; + }; + }; +}; + +type GreaterThanTimeRangeConditionDto = { + itemName: string; + type: 'GREATER_THAN_TIME_RANGE'; + payload: { + time: { + hours: number; + minutes: number; + }; + fieldName: string; + }; +}; + +type LessThanTimeRangeConditionDto = { + itemName: string; + type: 'LESS_THAN_TIME_RANGE'; + payload: { + time: { + hours: number; + minutes: number; + }; + fieldName: string; + }; +}; + +type EqualToTimeRangeConditionDto = { + itemName: string; + type: 'EQUAL_TO_TIME_RANGE'; + payload: { + time: { + hours: number; + minutes: number; + }; + fieldName: string; + }; +}; + +type NotEqualToTimeRangeConditionDto = { + itemName: string; + type: 'NOT_EQUAL_TO_TIME_RANGE'; + payload: { + time: { + hours: number; + minutes: number; + }; + fieldName: string; + }; +}; + +type BetweenTimesRangeConditionDto = { + itemName: string; + type: 'BETWEEN_TIMES_RANGE'; + payload: { + minTime: { + hours: number; + minutes: number; + }; + maxTime: { + hours: number; + minutes: number; + }; + fieldName: string; + }; +}; + +type OutsideOfTimesRangeConditionDto = { + itemName: string; + type: 'OUTSIDE_OF_TIMES_RANGE'; + payload: { + minTime: { + hours: number; + minutes: number; + }; + maxTime: { + hours: number; + minutes: number; + }; + fieldName: string; + }; +}; type ButtonsConfiguration = { removeBackButton: boolean; diff --git a/src/shared/ui/DateTimePicker.tsx b/src/shared/ui/DateTimePicker.tsx index 175c2477b..b7cd25651 100644 --- a/src/shared/ui/DateTimePicker.tsx +++ b/src/shared/ui/DateTimePicker.tsx @@ -73,7 +73,10 @@ export const DateTimePicker: FC = ({ isVisible={isDatePickerVisible} date={value ? new Date(value) : new Date()} mode={mode} - onConfirm={confirm} + onConfirm={date => { + const utcDate = new Date(date.toISOString()); + confirm(utcDate); + }} onCancel={hideDatePicker} />