mirror of
https://github.com/formbricks/formbricks.git
synced 2026-02-04 18:49:39 -06:00
cleanups
This commit is contained in:
@@ -1601,8 +1601,6 @@
|
||||
"is_not": "is not",
|
||||
"is_not_between": "is not between",
|
||||
"is_not_selected": "is not selected",
|
||||
"is_on_or_earlier_than": "is on or earlier than",
|
||||
"is_on_or_later_than": "is on or later than",
|
||||
"is_selected": "is selected",
|
||||
"is_shorter_than": "is shorter than",
|
||||
"max_length": "At most (characters)",
|
||||
@@ -1611,12 +1609,11 @@
|
||||
"min_length": "At least (characters)",
|
||||
"min_selections": "At least",
|
||||
"min_value": "At least (value)",
|
||||
"minimum_rows_answered": "Minimum rows answered",
|
||||
"options_selected": "options selected",
|
||||
"pattern": "Matches regex pattern",
|
||||
"phone": "Is valid phone",
|
||||
"position_is": "Position is",
|
||||
"position_is_higher_than": "Position is higher than",
|
||||
"position_is_lower_than": "Position is lower than",
|
||||
"minimum_options_ranked": "Minimum options ranked",
|
||||
"url": "Is valid URL",
|
||||
"file_size_at_least": "File size is at least",
|
||||
"file_size_at_most": "File size is at most",
|
||||
|
||||
@@ -12,10 +12,7 @@ import { getLanguageLabel } from "@formbricks/i18n-utils/src/utils";
|
||||
import { TI18nString } from "@formbricks/types/i18n";
|
||||
import { TSurveyElementTypeEnum, TSurveyMultipleChoiceElement } from "@formbricks/types/surveys/elements";
|
||||
import { TShuffleOption, TSurvey } from "@formbricks/types/surveys/types";
|
||||
import {
|
||||
TValidationRulesForMultipleChoiceMulti,
|
||||
TValidationRulesForMultipleChoiceSingle,
|
||||
} from "@formbricks/types/surveys/validation-rules";
|
||||
import { TValidationRulesForMultipleChoiceMulti } from "@formbricks/types/surveys/validation-rules";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
import { createI18nString, extractLanguageCodes } from "@/lib/i18n/utils";
|
||||
import { ElementFormInput } from "@/modules/survey/components/element-form-input";
|
||||
@@ -404,7 +401,7 @@ export const MultipleChoiceElementForm = ({
|
||||
locale={locale}
|
||||
/>
|
||||
|
||||
{element.type === TSurveyElementTypeEnum.MultipleChoiceMulti ? (
|
||||
{element.type === TSurveyElementTypeEnum.MultipleChoiceMulti && (
|
||||
<ValidationRulesEditor
|
||||
elementType={TSurveyElementTypeEnum.MultipleChoiceMulti}
|
||||
validationRules={element.validationRules ?? []}
|
||||
@@ -421,23 +418,6 @@ export const MultipleChoiceElementForm = ({
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<ValidationRulesEditor
|
||||
elementType={TSurveyElementTypeEnum.MultipleChoiceSingle}
|
||||
validationRules={element.validationRules ?? []}
|
||||
onUpdateRules={(rules: TValidationRulesForMultipleChoiceSingle) => {
|
||||
updateElement(elementIdx, {
|
||||
validationRules: rules,
|
||||
});
|
||||
}}
|
||||
element={element}
|
||||
validationLogic={element.validationLogic}
|
||||
onUpdateValidationLogic={(logic) => {
|
||||
updateElement(elementIdx, {
|
||||
validationLogic: logic,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</form>
|
||||
);
|
||||
|
||||
@@ -91,19 +91,12 @@ export const ValidationRulesEditor = ({
|
||||
is_shorter_than: t("environments.surveys.edit.validation.is_shorter_than"),
|
||||
is_greater_than: t("environments.surveys.edit.validation.is_greater_than"),
|
||||
is_less_than: t("environments.surveys.edit.validation.is_less_than"),
|
||||
is_on_or_later_than: t("environments.surveys.edit.validation.is_on_or_later_than"),
|
||||
is_later_than: t("environments.surveys.edit.validation.is_later_than"),
|
||||
is_on_or_earlier_than: t("environments.surveys.edit.validation.is_on_or_earlier_than"),
|
||||
is_earlier_than: t("environments.surveys.edit.validation.is_earlier_than"),
|
||||
is_between: t("environments.surveys.edit.validation.is_between"),
|
||||
is_not_between: t("environments.surveys.edit.validation.is_not_between"),
|
||||
is_selected: t("environments.surveys.edit.validation.is_selected"),
|
||||
is_not_selected: t("environments.surveys.edit.validation.is_not_selected"),
|
||||
position_is: t("environments.surveys.edit.validation.position_is"),
|
||||
position_is_higher_than: t("environments.surveys.edit.validation.position_is_higher_than"),
|
||||
position_is_lower_than: t("environments.surveys.edit.validation.position_is_lower_than"),
|
||||
answers_provided_greater_than: t("environments.surveys.edit.validation.answers_provided_greater_than"),
|
||||
answers_provided_smaller_than: t("environments.surveys.edit.validation.answers_provided_smaller_than"),
|
||||
minimum_options_ranked: t("environments.surveys.edit.validation.minimum_options_ranked"),
|
||||
minimum_rows_answered: t("environments.surveys.edit.validation.minimum_rows_answered"),
|
||||
file_size_at_least: t("environments.surveys.edit.validation.file_size_at_least"),
|
||||
file_size_at_most: t("environments.surveys.edit.validation.file_size_at_most"),
|
||||
file_extension_is: t("environments.surveys.edit.validation.file_extension_is"),
|
||||
@@ -312,15 +305,6 @@ export const ValidationRulesEditor = ({
|
||||
const config = RULE_TYPE_CONFIG[ruleType];
|
||||
const currentValue = getRuleValue(rule);
|
||||
|
||||
// For ranking rules, extract optionId and position from params
|
||||
const rankingParams =
|
||||
ruleType === "positionIs" ||
|
||||
ruleType === "positionIsHigherThan" ||
|
||||
ruleType === "positionIsLowerThan"
|
||||
? rule.params
|
||||
: null;
|
||||
const rankingOptionId = rankingParams?.optionId ?? "";
|
||||
const rankingPosition = rankingParams?.position ?? 1;
|
||||
|
||||
// Get available types for this rule (current type + unused types, no duplicates)
|
||||
const otherAvailableTypes = getAvailableRuleTypes(
|
||||
@@ -421,56 +405,6 @@ export const ValidationRulesEditor = ({
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
if (config.valueType === "ranking") {
|
||||
// Ranking rules: option selector + position input
|
||||
return (
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<Select
|
||||
value={rankingOptionId}
|
||||
onValueChange={(optionId) => {
|
||||
handleRuleValueChange(rule.id, `${optionId},${rankingPosition}`);
|
||||
}}>
|
||||
<SelectTrigger className="h-9 min-w-[200px] bg-white">
|
||||
<SelectValue placeholder="Select option" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{element &&
|
||||
"choices" in element &&
|
||||
element.choices
|
||||
.filter(
|
||||
(choice) =>
|
||||
choice.id !== "other" && choice.id !== "none" && "label" in choice
|
||||
)
|
||||
.map((choice) => {
|
||||
const choiceLabel =
|
||||
"label" in choice
|
||||
? choice.label.default ||
|
||||
Object.values(choice.label)[0] ||
|
||||
choice.id
|
||||
: choice.id;
|
||||
return (
|
||||
<SelectItem key={choice.id} value={choice.id}>
|
||||
{choiceLabel}
|
||||
</SelectItem>
|
||||
);
|
||||
})}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<span className="text-sm text-slate-500">position</span>
|
||||
<Input
|
||||
type="number"
|
||||
value={rankingPosition}
|
||||
onChange={(e) => {
|
||||
const newPosition = Number(e.target.value) || 1;
|
||||
handleRuleValueChange(rule.id, `${rankingOptionId},${newPosition}`);
|
||||
}}
|
||||
placeholder="1"
|
||||
className="h-9 w-20 bg-white"
|
||||
min={1}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// File extension MultiSelect
|
||||
if (ruleType === "fileExtensionIs" || ruleType === "fileExtensionIsNot") {
|
||||
|
||||
|
||||
@@ -119,24 +119,12 @@ export const RULE_TYPE_CONFIG: Record<
|
||||
valueType: "number",
|
||||
valuePlaceholder: "100",
|
||||
},
|
||||
isOnOrLaterThan: {
|
||||
labelKey: "is_on_or_later_than",
|
||||
needsValue: true,
|
||||
valueType: "text",
|
||||
valuePlaceholder: "YYYY-MM-DD",
|
||||
},
|
||||
isLaterThan: {
|
||||
labelKey: "is_later_than",
|
||||
needsValue: true,
|
||||
valueType: "text",
|
||||
valuePlaceholder: "YYYY-MM-DD",
|
||||
},
|
||||
isOnOrEarlierThan: {
|
||||
labelKey: "is_on_or_earlier_than",
|
||||
needsValue: true,
|
||||
valueType: "text",
|
||||
valuePlaceholder: "YYYY-MM-DD",
|
||||
},
|
||||
isEarlierThan: {
|
||||
labelKey: "is_earlier_than",
|
||||
needsValue: true,
|
||||
@@ -155,42 +143,17 @@ export const RULE_TYPE_CONFIG: Record<
|
||||
valueType: "text",
|
||||
valuePlaceholder: "YYYY-MM-DD,YYYY-MM-DD",
|
||||
},
|
||||
isSelected: {
|
||||
labelKey: "is_selected",
|
||||
needsValue: true,
|
||||
valueType: "option",
|
||||
},
|
||||
isNotSelected: {
|
||||
labelKey: "is_not_selected",
|
||||
needsValue: true,
|
||||
valueType: "option",
|
||||
},
|
||||
positionIs: {
|
||||
labelKey: "position_is",
|
||||
needsValue: true,
|
||||
valueType: "ranking",
|
||||
},
|
||||
positionIsHigherThan: {
|
||||
labelKey: "position_is_higher_than",
|
||||
needsValue: true,
|
||||
valueType: "ranking",
|
||||
},
|
||||
positionIsLowerThan: {
|
||||
labelKey: "position_is_lower_than",
|
||||
needsValue: true,
|
||||
valueType: "ranking",
|
||||
},
|
||||
answersProvidedGreaterThan: {
|
||||
labelKey: "answers_provided_greater_than",
|
||||
minRanked: {
|
||||
labelKey: "minimum_options_ranked",
|
||||
needsValue: true,
|
||||
valueType: "number",
|
||||
valuePlaceholder: "1",
|
||||
},
|
||||
answersProvidedSmallerThan: {
|
||||
labelKey: "answers_provided_smaller_than",
|
||||
minRowsAnswered: {
|
||||
labelKey: "minimum_rows_answered",
|
||||
needsValue: true,
|
||||
valueType: "number",
|
||||
valuePlaceholder: "5",
|
||||
valuePlaceholder: "1",
|
||||
},
|
||||
fileSizeAtLeast: {
|
||||
labelKey: "file_size_at_least",
|
||||
|
||||
@@ -44,36 +44,16 @@ describe("getAvailableRuleTypes", () => {
|
||||
expect(available).toContain("pattern");
|
||||
});
|
||||
|
||||
test("should return isSelected and isNotSelected for multipleChoiceSingle element", () => {
|
||||
test("should return empty array for multipleChoiceSingle element (no validation rules)", () => {
|
||||
const elementType = TSurveyElementTypeEnum.MultipleChoiceSingle;
|
||||
const existingRules: TValidationRule[] = [];
|
||||
|
||||
const available = getAvailableRuleTypes(elementType, existingRules);
|
||||
|
||||
expect(available).toEqual(["isSelected", "isNotSelected"]);
|
||||
});
|
||||
|
||||
test("should return empty array for multipleChoiceSingle when all rules are already added", () => {
|
||||
const elementType = TSurveyElementTypeEnum.MultipleChoiceSingle;
|
||||
const existingRules: TValidationRule[] = [
|
||||
{
|
||||
id: "rule1",
|
||||
type: "isSelected",
|
||||
params: { optionId: "option1" },
|
||||
},
|
||||
{
|
||||
id: "rule2",
|
||||
type: "isNotSelected",
|
||||
params: { optionId: "option2" },
|
||||
},
|
||||
];
|
||||
|
||||
const available = getAvailableRuleTypes(elementType, existingRules);
|
||||
|
||||
expect(available).toEqual([]);
|
||||
});
|
||||
|
||||
test("should return minSelections, maxSelections, isSelected, isNotSelected for multipleChoiceMulti element", () => {
|
||||
test("should return minSelections, maxSelections for multipleChoiceMulti element", () => {
|
||||
const elementType = TSurveyElementTypeEnum.MultipleChoiceMulti;
|
||||
const existingRules: TValidationRule[] = [];
|
||||
|
||||
@@ -81,9 +61,7 @@ describe("getAvailableRuleTypes", () => {
|
||||
|
||||
expect(available).toContain("minSelections");
|
||||
expect(available).toContain("maxSelections");
|
||||
expect(available).toContain("isSelected");
|
||||
expect(available).toContain("isNotSelected");
|
||||
expect(available.length).toBe(4);
|
||||
expect(available.length).toBe(2);
|
||||
});
|
||||
|
||||
test("should return empty array for rating element (no validation rules)", () => {
|
||||
@@ -110,9 +88,7 @@ describe("getAvailableRuleTypes", () => {
|
||||
|
||||
const available = getAvailableRuleTypes(elementType, existingRules);
|
||||
|
||||
expect(available).toContain("isOnOrLaterThan");
|
||||
expect(available).toContain("isLaterThan");
|
||||
expect(available).toContain("isOnOrEarlierThan");
|
||||
expect(available).toContain("isEarlierThan");
|
||||
expect(available).toContain("isBetween");
|
||||
expect(available).toContain("isNotBetween");
|
||||
@@ -133,9 +109,8 @@ describe("getAvailableRuleTypes", () => {
|
||||
|
||||
const available = getAvailableRuleTypes(elementType, existingRules);
|
||||
|
||||
expect(available).toContain("answersProvidedGreaterThan");
|
||||
expect(available).toContain("answersProvidedSmallerThan");
|
||||
expect(available.length).toBe(2);
|
||||
expect(available).toContain("minRowsAnswered");
|
||||
expect(available.length).toBe(1);
|
||||
});
|
||||
|
||||
test("should return ranking validation rules for ranking element", () => {
|
||||
@@ -144,10 +119,8 @@ describe("getAvailableRuleTypes", () => {
|
||||
|
||||
const available = getAvailableRuleTypes(elementType, existingRules);
|
||||
|
||||
expect(available).toContain("positionIs");
|
||||
expect(available).toContain("positionIsHigherThan");
|
||||
expect(available).toContain("positionIsLowerThan");
|
||||
expect(available.length).toBe(3);
|
||||
expect(available).toContain("minRanked");
|
||||
expect(available.length).toBe(1);
|
||||
});
|
||||
|
||||
test("should return empty array for fileUpload element (no validation rules)", () => {
|
||||
|
||||
@@ -44,25 +44,6 @@ export const getRuleValue = (rule: TValidationRule): number | string | undefined
|
||||
if ("startDate" in params && "endDate" in params) {
|
||||
return `${params.startDate},${params.endDate}`;
|
||||
}
|
||||
// Check for ranking rules first (they have both optionId and position)
|
||||
if (
|
||||
rule.type === "positionIs" ||
|
||||
rule.type === "positionIsHigherThan" ||
|
||||
rule.type === "positionIsLowerThan"
|
||||
) {
|
||||
// After checking rule.type, TypeScript narrows rule.params to ranking rule params
|
||||
if ("position" in rule.params) {
|
||||
const positionValue = rule.params.position;
|
||||
if (typeof positionValue === "number") {
|
||||
return positionValue;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
if ("optionId" in params) {
|
||||
// For single/multi select rules, return optionId
|
||||
return params.optionId;
|
||||
}
|
||||
// File upload rules
|
||||
if ("size" in params && "unit" in params) {
|
||||
// For file size rules, return size as number (unit is stored separately)
|
||||
@@ -116,12 +97,8 @@ export const createRuleParams = (
|
||||
return { min: Number(value) || 0 };
|
||||
case "isLessThan":
|
||||
return { max: Number(value) || 100 };
|
||||
case "isOnOrLaterThan":
|
||||
return { date: value === undefined || value === null ? "" : String(value) };
|
||||
case "isLaterThan":
|
||||
return { date: value === undefined || value === null ? "" : String(value) };
|
||||
case "isOnOrEarlierThan":
|
||||
return { date: value === undefined || value === null ? "" : String(value) };
|
||||
case "isEarlierThan":
|
||||
return { date: value === undefined || value === null ? "" : String(value) };
|
||||
case "isBetween":
|
||||
@@ -139,29 +116,10 @@ export const createRuleParams = (
|
||||
return { min: Number(value) || 1 };
|
||||
case "maxSelections":
|
||||
return { max: Number(value) || 3 };
|
||||
case "isSelected":
|
||||
case "isNotSelected":
|
||||
return { optionId: value === undefined || value === null ? "" : String(value) };
|
||||
case "positionIs":
|
||||
case "positionIsHigherThan":
|
||||
case "positionIsLowerThan":
|
||||
// For ranking rules, value is a comma-separated string: "optionId,position"
|
||||
if (typeof value === "string" && value.includes(",")) {
|
||||
const [optionId, position] = value.split(",");
|
||||
return {
|
||||
optionId: optionId?.trim() || "",
|
||||
position: Number(position?.trim()) || 1,
|
||||
};
|
||||
}
|
||||
// Fallback: assume value is just the position, optionId will be set separately
|
||||
return {
|
||||
optionId: "",
|
||||
position: Number(value) || 1,
|
||||
};
|
||||
case "answersProvidedGreaterThan":
|
||||
case "minRanked":
|
||||
return { min: Number(value) || 1 };
|
||||
case "minRowsAnswered":
|
||||
return { min: Number(value) || 1 };
|
||||
case "answersProvidedSmallerThan":
|
||||
return { max: Number(value) || 5 };
|
||||
case "fileSizeAtLeast":
|
||||
// Value should be number, unit is handled separately in the UI
|
||||
return { size: Number(value) || 1, unit: "MB" as const };
|
||||
|
||||
@@ -54,8 +54,6 @@
|
||||
"is_less_than": "Please enter a value less than {max}",
|
||||
"is_longer_than": "Please enter more than {min} characters",
|
||||
"is_not_between": "Please select a date not between {startDate} and {endDate}",
|
||||
"is_on_or_earlier_than": "Please select a date on or earlier than {date}",
|
||||
"is_on_or_later_than": "Please select a date on or later than {date}",
|
||||
"is_shorter_than": "Please enter less than {max} characters",
|
||||
"max_length": "Please enter no more than {max} characters",
|
||||
"max_selections": "Please select no more than {max} options",
|
||||
@@ -63,15 +61,14 @@
|
||||
"min_length": "Please enter at least {min} characters",
|
||||
"min_selections": "Please select at least {min} options",
|
||||
"min_value": "Please enter a value of at least {min}",
|
||||
"minimum_rows_answered": "Please answer at least {min} rows",
|
||||
"option_must_be_selected": "Please select {option}",
|
||||
"option_must_not_be_selected": "Please do not select {option}",
|
||||
"please_enter_a_valid_email_address": "Please enter a valid email address",
|
||||
"please_enter_a_valid_phone_number": "Please enter a valid phone number",
|
||||
"please_enter_a_valid_url": "Please enter a valid URL",
|
||||
"please_fill_out_this_field": "Please fill out this field",
|
||||
"position_must_be": "{option} must be at position {position}",
|
||||
"position_must_be_higher_than": "{option} must be ranked higher than position {position}",
|
||||
"position_must_be_lower_than": "{option} must be ranked lower than position {position}",
|
||||
"minimum_options_ranked": "Please rank at least {min} options",
|
||||
"recaptcha_error": {
|
||||
"message": "Your response could not be submitted because it was flagged as automated activity. If you breathe, please try again.",
|
||||
"title": "We couldn't verify that you're human."
|
||||
|
||||
@@ -4,8 +4,6 @@ import type { TResponseDataValue } from "@formbricks/types/responses";
|
||||
import type { TSurveyElement } from "@formbricks/types/surveys/elements";
|
||||
import type {
|
||||
TValidationRuleParams,
|
||||
TValidationRuleParamsAnswersProvidedGreaterThan,
|
||||
TValidationRuleParamsAnswersProvidedSmallerThan,
|
||||
TValidationRuleParamsContains,
|
||||
TValidationRuleParamsDoesNotContain,
|
||||
TValidationRuleParamsDoesNotEqual,
|
||||
@@ -22,22 +20,17 @@ import type {
|
||||
TValidationRuleParamsIsLessThan,
|
||||
TValidationRuleParamsIsLongerThan,
|
||||
TValidationRuleParamsIsNotBetween,
|
||||
TValidationRuleParamsIsNotSelected,
|
||||
TValidationRuleParamsIsOnOrEarlierThan,
|
||||
TValidationRuleParamsIsOnOrLaterThan,
|
||||
TValidationRuleParamsIsSelected,
|
||||
TValidationRuleParamsIsShorterThan,
|
||||
TValidationRuleParamsMaxLength,
|
||||
TValidationRuleParamsMaxSelections,
|
||||
TValidationRuleParamsMaxValue,
|
||||
TValidationRuleParamsMinLength,
|
||||
TValidationRuleParamsMinRanked,
|
||||
TValidationRuleParamsMinRowsAnswered,
|
||||
TValidationRuleParamsMinSelections,
|
||||
TValidationRuleParamsMinValue,
|
||||
TValidationRuleParamsPattern,
|
||||
TValidationRuleParamsPhone,
|
||||
TValidationRuleParamsPositionIs,
|
||||
TValidationRuleParamsPositionIsHigherThan,
|
||||
TValidationRuleParamsPositionIsLowerThan,
|
||||
TValidationRuleParamsUrl,
|
||||
TValidationRuleType,
|
||||
} from "@formbricks/types/surveys/validation-rules";
|
||||
@@ -398,21 +391,6 @@ export const validators: Record<TValidationRuleType, TValidator> = {
|
||||
return t("errors.is_less_than", { max: typedParams.max });
|
||||
},
|
||||
},
|
||||
isOnOrLaterThan: {
|
||||
check: (value: TResponseDataValue, params: TValidationRuleParams): TValidatorCheckResult => {
|
||||
const typedParams = params as TValidationRuleParamsIsOnOrLaterThan;
|
||||
// Skip validation if value is empty
|
||||
if (!value || typeof value !== "string" || value === "") {
|
||||
return { valid: true };
|
||||
}
|
||||
// Compare dates as strings (YYYY-MM-DD format)
|
||||
return { valid: value >= typedParams.date };
|
||||
},
|
||||
getDefaultMessage: (params: TValidationRuleParams, _element: TSurveyElement, t: TFunction): string => {
|
||||
const typedParams = params as TValidationRuleParamsIsOnOrLaterThan;
|
||||
return t("errors.is_on_or_later_than", { date: typedParams.date });
|
||||
},
|
||||
},
|
||||
isLaterThan: {
|
||||
check: (value: TResponseDataValue, params: TValidationRuleParams): TValidatorCheckResult => {
|
||||
const typedParams = params as TValidationRuleParamsIsLaterThan;
|
||||
@@ -428,21 +406,6 @@ export const validators: Record<TValidationRuleType, TValidator> = {
|
||||
return t("errors.is_later_than", { date: typedParams.date });
|
||||
},
|
||||
},
|
||||
isOnOrEarlierThan: {
|
||||
check: (value: TResponseDataValue, params: TValidationRuleParams): TValidatorCheckResult => {
|
||||
const typedParams = params as TValidationRuleParamsIsOnOrEarlierThan;
|
||||
// Skip validation if value is empty
|
||||
if (!value || typeof value !== "string" || value === "") {
|
||||
return { valid: true };
|
||||
}
|
||||
// Compare dates as strings (YYYY-MM-DD format)
|
||||
return { valid: value <= typedParams.date };
|
||||
},
|
||||
getDefaultMessage: (params: TValidationRuleParams, _element: TSurveyElement, t: TFunction): string => {
|
||||
const typedParams = params as TValidationRuleParamsIsOnOrEarlierThan;
|
||||
return t("errors.is_on_or_earlier_than", { date: typedParams.date });
|
||||
},
|
||||
},
|
||||
isEarlierThan: {
|
||||
check: (value: TResponseDataValue, params: TValidationRuleParams): TValidatorCheckResult => {
|
||||
const typedParams = params as TValidationRuleParamsIsEarlierThan;
|
||||
@@ -488,296 +451,53 @@ export const validators: Record<TValidationRuleType, TValidator> = {
|
||||
return t("errors.is_not_between", { startDate: typedParams.startDate, endDate: typedParams.endDate });
|
||||
},
|
||||
},
|
||||
isSelected: {
|
||||
minRanked: {
|
||||
check: (
|
||||
value: TResponseDataValue,
|
||||
params: TValidationRuleParams,
|
||||
element: TSurveyElement
|
||||
): TValidatorCheckResult => {
|
||||
const typedParams = params as TValidationRuleParamsIsSelected;
|
||||
if (!value) {
|
||||
return { valid: true };
|
||||
}
|
||||
// Find the choice with the specified optionId
|
||||
if (
|
||||
(element.type !== "multipleChoiceSingle" && element.type !== "multipleChoiceMulti") ||
|
||||
!("choices" in element)
|
||||
) {
|
||||
return { valid: true };
|
||||
}
|
||||
const choice = element.choices.find((c) => c.id === typedParams.optionId);
|
||||
if (!choice) {
|
||||
return { valid: true };
|
||||
}
|
||||
// Get all language variants of the choice label
|
||||
const choiceLabels = Object.values(choice.label);
|
||||
|
||||
// Handle single select (string) and multi select (array) responses
|
||||
if (element.type === "multipleChoiceSingle") {
|
||||
// Single select: response is a string (choice label)
|
||||
if (typeof value !== "string" || value === "") {
|
||||
return { valid: true };
|
||||
}
|
||||
return { valid: choiceLabels.includes(value) };
|
||||
} else {
|
||||
// Multi select: response is an array of choice labels
|
||||
if (!Array.isArray(value) || value.length === 0) {
|
||||
return { valid: true };
|
||||
}
|
||||
// Check if any of the selected labels match the choice labels
|
||||
const isSelected = value.some((selectedLabel) => choiceLabels.includes(selectedLabel));
|
||||
return { valid: isSelected };
|
||||
}
|
||||
},
|
||||
getDefaultMessage: (params: TValidationRuleParams, element: TSurveyElement, t: TFunction): string => {
|
||||
const typedParams = params as TValidationRuleParamsIsSelected;
|
||||
if (
|
||||
(element.type !== "multipleChoiceSingle" && element.type !== "multipleChoiceMulti") ||
|
||||
!("choices" in element)
|
||||
) {
|
||||
return t("errors.invalid_format");
|
||||
}
|
||||
const choice = element.choices.find((c) => c.id === typedParams.optionId);
|
||||
const choiceLabel = choice
|
||||
? choice.label.default || Object.values(choice.label)[0] || typedParams.optionId
|
||||
: typedParams.optionId;
|
||||
return t("errors.option_must_be_selected", { option: choiceLabel });
|
||||
},
|
||||
},
|
||||
isNotSelected: {
|
||||
check: (
|
||||
value: TResponseDataValue,
|
||||
params: TValidationRuleParams,
|
||||
element: TSurveyElement
|
||||
): TValidatorCheckResult => {
|
||||
const typedParams = params as TValidationRuleParamsIsNotSelected;
|
||||
if (!value) {
|
||||
return { valid: true };
|
||||
}
|
||||
// Find the choice with the specified optionId
|
||||
if (
|
||||
(element.type !== "multipleChoiceSingle" && element.type !== "multipleChoiceMulti") ||
|
||||
!("choices" in element)
|
||||
) {
|
||||
return { valid: true };
|
||||
}
|
||||
const choice = element.choices.find((c) => c.id === typedParams.optionId);
|
||||
if (!choice) {
|
||||
return { valid: true };
|
||||
}
|
||||
// Get all language variants of the choice label
|
||||
const choiceLabels = Object.values(choice.label);
|
||||
|
||||
// Handle single select (string) and multi select (array) responses
|
||||
if (element.type === "multipleChoiceSingle") {
|
||||
// Single select: response is a string (choice label)
|
||||
if (typeof value !== "string" || value === "") {
|
||||
return { valid: true };
|
||||
}
|
||||
return { valid: !choiceLabels.includes(value) };
|
||||
} else {
|
||||
// Multi select: response is an array of choice labels
|
||||
if (!Array.isArray(value) || value.length === 0) {
|
||||
return { valid: true };
|
||||
}
|
||||
// Check if any of the selected labels match the choice labels
|
||||
const isSelected = value.some((selectedLabel) => choiceLabels.includes(selectedLabel));
|
||||
return { valid: !isSelected };
|
||||
}
|
||||
},
|
||||
getDefaultMessage: (params: TValidationRuleParams, element: TSurveyElement, t: TFunction): string => {
|
||||
const typedParams = params as TValidationRuleParamsIsNotSelected;
|
||||
if (
|
||||
(element.type !== "multipleChoiceSingle" && element.type !== "multipleChoiceMulti") ||
|
||||
!("choices" in element)
|
||||
) {
|
||||
return t("errors.invalid_format");
|
||||
}
|
||||
const choice = element.choices.find((c) => c.id === typedParams.optionId);
|
||||
const choiceLabel = choice
|
||||
? choice.label.default || Object.values(choice.label)[0] || typedParams.optionId
|
||||
: typedParams.optionId;
|
||||
return t("errors.option_must_not_be_selected", { option: choiceLabel });
|
||||
},
|
||||
},
|
||||
positionIs: {
|
||||
check: (
|
||||
value: TResponseDataValue,
|
||||
params: TValidationRuleParams,
|
||||
element: TSurveyElement
|
||||
): TValidatorCheckResult => {
|
||||
const typedParams = params as TValidationRuleParamsPositionIs;
|
||||
const typedParams = params as TValidationRuleParamsMinRanked;
|
||||
// Skip validation if value is empty
|
||||
if (!value || !Array.isArray(value) || value.length === 0) {
|
||||
return { valid: true };
|
||||
}
|
||||
if (element.type !== "ranking" || !("choices" in element)) {
|
||||
if (element.type !== "ranking") {
|
||||
return { valid: true };
|
||||
}
|
||||
// Find the position of the option in the ranking (1-indexed)
|
||||
const position = value.findIndex((item) => {
|
||||
// Response can be choice IDs or choice labels
|
||||
if (item === typedParams.optionId) {
|
||||
return true;
|
||||
}
|
||||
// Check if it's a label that matches the choice
|
||||
const choice = element.choices.find((c) => c.id === typedParams.optionId);
|
||||
if (choice) {
|
||||
const choiceLabels = Object.values(choice.label);
|
||||
return choiceLabels.includes(item);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
// Position is 1-indexed, so add 1 to array index
|
||||
const actualPosition = position === -1 ? 0 : position + 1;
|
||||
return { valid: actualPosition === typedParams.position };
|
||||
// Count how many options have been ranked (array length)
|
||||
const rankedCount = value.length;
|
||||
return { valid: rankedCount >= typedParams.min };
|
||||
},
|
||||
getDefaultMessage: (params: TValidationRuleParams, element: TSurveyElement, t: TFunction): string => {
|
||||
const typedParams = params as TValidationRuleParamsPositionIs;
|
||||
if (element.type !== "ranking" || !("choices" in element)) {
|
||||
return t("errors.invalid_format");
|
||||
}
|
||||
const choice = element.choices.find((c) => c.id === typedParams.optionId);
|
||||
const choiceLabel = choice
|
||||
? choice.label.default || Object.values(choice.label)[0] || typedParams.optionId
|
||||
: typedParams.optionId;
|
||||
return t("errors.position_must_be", { option: choiceLabel, position: typedParams.position });
|
||||
getDefaultMessage: (params: TValidationRuleParams, _element: TSurveyElement, t: TFunction): string => {
|
||||
const typedParams = params as TValidationRuleParamsMinRanked;
|
||||
return t("errors.minimum_options_ranked", { min: typedParams.min });
|
||||
},
|
||||
},
|
||||
positionIsHigherThan: {
|
||||
minRowsAnswered: {
|
||||
check: (
|
||||
value: TResponseDataValue,
|
||||
params: TValidationRuleParams,
|
||||
element: TSurveyElement
|
||||
): TValidatorCheckResult => {
|
||||
const typedParams = params as TValidationRuleParamsPositionIsHigherThan;
|
||||
if (!value || !Array.isArray(value) || value.length === 0) {
|
||||
const typedParams = params as TValidationRuleParamsMinRowsAnswered;
|
||||
// Skip validation if value is empty
|
||||
if (!value || typeof value !== "object" || Array.isArray(value) || value === null) {
|
||||
return { valid: true };
|
||||
}
|
||||
if (element.type !== "ranking" || !("choices" in element)) {
|
||||
return { valid: true };
|
||||
}
|
||||
// Find the position of the option in the ranking (1-indexed)
|
||||
const position = value.findIndex((item) => {
|
||||
if (item === typedParams.optionId) {
|
||||
return true;
|
||||
}
|
||||
const choice = element.choices.find((c) => c.id === typedParams.optionId);
|
||||
if (choice) {
|
||||
const choiceLabels = Object.values(choice.label);
|
||||
return choiceLabels.includes(item);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
// Position is 1-indexed, so add 1 to array index
|
||||
// Higher position means lower position number (better rank)
|
||||
const actualPosition = position === -1 ? 0 : position + 1;
|
||||
return { valid: actualPosition > 0 && actualPosition < typedParams.position };
|
||||
},
|
||||
getDefaultMessage: (params: TValidationRuleParams, element: TSurveyElement, t: TFunction): string => {
|
||||
const typedParams = params as TValidationRuleParamsPositionIsHigherThan;
|
||||
if (element.type !== "ranking" || !("choices" in element)) {
|
||||
return t("errors.invalid_format");
|
||||
}
|
||||
const choice = element.choices.find((c) => c.id === typedParams.optionId);
|
||||
const choiceLabel = choice
|
||||
? choice.label.default || Object.values(choice.label)[0] || typedParams.optionId
|
||||
: typedParams.optionId;
|
||||
return t("errors.position_must_be_higher_than", {
|
||||
option: choiceLabel,
|
||||
position: typedParams.position,
|
||||
});
|
||||
},
|
||||
},
|
||||
positionIsLowerThan: {
|
||||
check: (
|
||||
value: TResponseDataValue,
|
||||
params: TValidationRuleParams,
|
||||
element: TSurveyElement
|
||||
): TValidatorCheckResult => {
|
||||
const typedParams = params as TValidationRuleParamsPositionIsLowerThan;
|
||||
if (!value || !Array.isArray(value) || value.length === 0) {
|
||||
return { valid: true };
|
||||
}
|
||||
if (element.type !== "ranking" || !("choices" in element)) {
|
||||
return { valid: true };
|
||||
}
|
||||
// Find the position of the option in the ranking (1-indexed)
|
||||
const position = value.findIndex((item) => {
|
||||
if (item === typedParams.optionId) {
|
||||
return true;
|
||||
}
|
||||
const choice = element.choices.find((c) => c.id === typedParams.optionId);
|
||||
if (choice) {
|
||||
const choiceLabels = Object.values(choice.label);
|
||||
return choiceLabels.includes(item);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
// Position is 1-indexed, so add 1 to array index
|
||||
// Lower position means higher position number (worse rank)
|
||||
const actualPosition = position === -1 ? 0 : position + 1;
|
||||
return { valid: actualPosition > typedParams.position };
|
||||
},
|
||||
getDefaultMessage: (params: TValidationRuleParams, element: TSurveyElement, t: TFunction): string => {
|
||||
const typedParams = params as TValidationRuleParamsPositionIsLowerThan;
|
||||
if (element.type !== "ranking" || !("choices" in element)) {
|
||||
return t("errors.invalid_format");
|
||||
}
|
||||
const choice = element.choices.find((c) => c.id === typedParams.optionId);
|
||||
const choiceLabel = choice
|
||||
? choice.label.default || Object.values(choice.label)[0] || typedParams.optionId
|
||||
: typedParams.optionId;
|
||||
return t("errors.position_must_be_lower_than", { option: choiceLabel, position: typedParams.position });
|
||||
},
|
||||
},
|
||||
answersProvidedGreaterThan: {
|
||||
check: (
|
||||
value: TResponseDataValue,
|
||||
params: TValidationRuleParams,
|
||||
element: TSurveyElement
|
||||
): TValidatorCheckResult => {
|
||||
const typedParams = params as TValidationRuleParamsAnswersProvidedGreaterThan;
|
||||
if (element.type !== "matrix") {
|
||||
return { valid: true };
|
||||
}
|
||||
// Matrix responses are Record<string, string> where keys are row labels and values are column labels
|
||||
if (!value || typeof value !== "object" || Array.isArray(value) || value === null) {
|
||||
return { valid: true };
|
||||
}
|
||||
// Count non-empty answers (rows that have been answered)
|
||||
const answeredCount = Object.values(value).filter(
|
||||
(v) => v !== "" && v !== null && v !== undefined
|
||||
).length;
|
||||
return { valid: answeredCount > typedParams.min };
|
||||
return { valid: answeredCount >= typedParams.min };
|
||||
},
|
||||
getDefaultMessage: (params: TValidationRuleParams, _element: TSurveyElement, t: TFunction): string => {
|
||||
const typedParams = params as TValidationRuleParamsAnswersProvidedGreaterThan;
|
||||
return t("errors.answers_provided_must_be_greater_than", { min: typedParams.min });
|
||||
},
|
||||
},
|
||||
answersProvidedSmallerThan: {
|
||||
check: (
|
||||
value: TResponseDataValue,
|
||||
params: TValidationRuleParams,
|
||||
element: TSurveyElement
|
||||
): TValidatorCheckResult => {
|
||||
const typedParams = params as TValidationRuleParamsAnswersProvidedSmallerThan;
|
||||
if (element.type !== "matrix") {
|
||||
return { valid: true };
|
||||
}
|
||||
// Matrix responses are Record<string, string> where keys are row labels and values are column labels
|
||||
if (!value || typeof value !== "object" || Array.isArray(value) || value === null) {
|
||||
return { valid: true };
|
||||
}
|
||||
// Count non-empty answers (rows that have been answered)
|
||||
const answeredCount = Object.values(value).filter(
|
||||
(v) => v !== "" && v !== null && v !== undefined
|
||||
).length;
|
||||
return { valid: answeredCount < typedParams.max };
|
||||
},
|
||||
getDefaultMessage: (params: TValidationRuleParams, _element: TSurveyElement, t: TFunction): string => {
|
||||
const typedParams = params as TValidationRuleParamsAnswersProvidedSmallerThan;
|
||||
return t("errors.answers_provided_must_be_smaller_than", { max: typedParams.max });
|
||||
const typedParams = params as TValidationRuleParamsMinRowsAnswered;
|
||||
return t("errors.minimum_rows_answered", { min: typedParams.min });
|
||||
},
|
||||
},
|
||||
fileSizeAtLeast: {
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
ZValidationRulesForFileUpload,
|
||||
ZValidationRulesForMatrix,
|
||||
ZValidationRulesForMultipleChoiceMulti,
|
||||
ZValidationRulesForMultipleChoiceSingle,
|
||||
ZValidationRulesForNPS,
|
||||
ZValidationRulesForOpenText,
|
||||
ZValidationRulesForPictureSelection,
|
||||
@@ -163,7 +162,6 @@ export const ZSurveyMultipleChoiceSingleElement = ZSurveyElementBase.extend({
|
||||
.min(2, { message: "Multiple Choice Element must have at least two choices" }),
|
||||
shuffleOption: ZShuffleOption.optional(),
|
||||
otherOptionPlaceholder: ZI18nString.optional(),
|
||||
validationRules: ZValidationRulesForMultipleChoiceSingle.optional(),
|
||||
});
|
||||
|
||||
// Multiple Choice Multi Element
|
||||
|
||||
@@ -27,23 +27,14 @@ export const ZValidationRuleType = z.enum([
|
||||
"minSelections",
|
||||
"maxSelections",
|
||||
|
||||
// Single select rules
|
||||
"isSelected",
|
||||
"isNotSelected",
|
||||
|
||||
// Ranking rules
|
||||
"positionIs",
|
||||
"positionIsHigherThan",
|
||||
"positionIsLowerThan",
|
||||
"minRanked",
|
||||
|
||||
// Matrix rules
|
||||
"answersProvidedGreaterThan",
|
||||
"answersProvidedSmallerThan",
|
||||
"minRowsAnswered",
|
||||
|
||||
// Date rules
|
||||
"isOnOrLaterThan",
|
||||
"isLaterThan",
|
||||
"isOnOrEarlierThan",
|
||||
"isEarlierThan",
|
||||
"isBetween",
|
||||
"isNotBetween",
|
||||
@@ -125,18 +116,10 @@ export const ZValidationRuleParamsIsLessThan = z.object({
|
||||
max: z.number(),
|
||||
});
|
||||
|
||||
export const ZValidationRuleParamsIsOnOrLaterThan = z.object({
|
||||
date: z.string(), // YYYY-MM-DD format
|
||||
});
|
||||
|
||||
export const ZValidationRuleParamsIsLaterThan = z.object({
|
||||
date: z.string(), // YYYY-MM-DD format
|
||||
});
|
||||
|
||||
export const ZValidationRuleParamsIsOnOrEarlierThan = z.object({
|
||||
date: z.string(), // YYYY-MM-DD format
|
||||
});
|
||||
|
||||
export const ZValidationRuleParamsIsEarlierThan = z.object({
|
||||
date: z.string(), // YYYY-MM-DD format
|
||||
});
|
||||
@@ -151,35 +134,12 @@ export const ZValidationRuleParamsIsNotBetween = z.object({
|
||||
endDate: z.string(), // YYYY-MM-DD format
|
||||
});
|
||||
|
||||
export const ZValidationRuleParamsIsSelected = z.object({
|
||||
optionId: z.string().min(1),
|
||||
export const ZValidationRuleParamsMinRanked = z.object({
|
||||
min: z.number().min(1),
|
||||
});
|
||||
|
||||
export const ZValidationRuleParamsIsNotSelected = z.object({
|
||||
optionId: z.string().min(1),
|
||||
});
|
||||
|
||||
export const ZValidationRuleParamsPositionIs = z.object({
|
||||
optionId: z.string().min(1),
|
||||
position: z.number().min(1),
|
||||
});
|
||||
|
||||
export const ZValidationRuleParamsPositionIsHigherThan = z.object({
|
||||
optionId: z.string().min(1),
|
||||
position: z.number().min(1),
|
||||
});
|
||||
|
||||
export const ZValidationRuleParamsPositionIsLowerThan = z.object({
|
||||
optionId: z.string().min(1),
|
||||
position: z.number().min(1),
|
||||
});
|
||||
|
||||
export const ZValidationRuleParamsAnswersProvidedGreaterThan = z.object({
|
||||
min: z.number().min(0),
|
||||
});
|
||||
|
||||
export const ZValidationRuleParamsAnswersProvidedSmallerThan = z.object({
|
||||
max: z.number().min(0),
|
||||
export const ZValidationRuleParamsMinRowsAnswered = z.object({
|
||||
min: z.number().min(1),
|
||||
});
|
||||
|
||||
// File upload rule params
|
||||
@@ -221,19 +181,12 @@ export const ZValidationRuleParams = z.union([
|
||||
ZValidationRuleParamsIsLessThan,
|
||||
ZValidationRuleParamsMinSelections,
|
||||
ZValidationRuleParamsMaxSelections,
|
||||
ZValidationRuleParamsIsOnOrLaterThan,
|
||||
ZValidationRuleParamsIsLaterThan,
|
||||
ZValidationRuleParamsIsOnOrEarlierThan,
|
||||
ZValidationRuleParamsIsEarlierThan,
|
||||
ZValidationRuleParamsIsBetween,
|
||||
ZValidationRuleParamsIsNotBetween,
|
||||
ZValidationRuleParamsIsSelected,
|
||||
ZValidationRuleParamsIsNotSelected,
|
||||
ZValidationRuleParamsPositionIs,
|
||||
ZValidationRuleParamsPositionIsHigherThan,
|
||||
ZValidationRuleParamsPositionIsLowerThan,
|
||||
ZValidationRuleParamsAnswersProvidedGreaterThan,
|
||||
ZValidationRuleParamsAnswersProvidedSmallerThan,
|
||||
ZValidationRuleParamsMinRanked,
|
||||
ZValidationRuleParamsMinRowsAnswered,
|
||||
ZValidationRuleParamsFileSizeAtLeast,
|
||||
ZValidationRuleParamsFileSizeAtMost,
|
||||
ZValidationRuleParamsFileExtensionIs,
|
||||
@@ -261,27 +214,12 @@ export type TValidationRuleParamsIsLongerThan = z.infer<typeof ZValidationRulePa
|
||||
export type TValidationRuleParamsIsShorterThan = z.infer<typeof ZValidationRuleParamsIsShorterThan>;
|
||||
export type TValidationRuleParamsIsGreaterThan = z.infer<typeof ZValidationRuleParamsIsGreaterThan>;
|
||||
export type TValidationRuleParamsIsLessThan = z.infer<typeof ZValidationRuleParamsIsLessThan>;
|
||||
export type TValidationRuleParamsIsOnOrLaterThan = z.infer<typeof ZValidationRuleParamsIsOnOrLaterThan>;
|
||||
export type TValidationRuleParamsIsLaterThan = z.infer<typeof ZValidationRuleParamsIsLaterThan>;
|
||||
export type TValidationRuleParamsIsOnOrEarlierThan = z.infer<typeof ZValidationRuleParamsIsOnOrEarlierThan>;
|
||||
export type TValidationRuleParamsIsEarlierThan = z.infer<typeof ZValidationRuleParamsIsEarlierThan>;
|
||||
export type TValidationRuleParamsIsBetween = z.infer<typeof ZValidationRuleParamsIsBetween>;
|
||||
export type TValidationRuleParamsIsNotBetween = z.infer<typeof ZValidationRuleParamsIsNotBetween>;
|
||||
export type TValidationRuleParamsIsSelected = z.infer<typeof ZValidationRuleParamsIsSelected>;
|
||||
export type TValidationRuleParamsIsNotSelected = z.infer<typeof ZValidationRuleParamsIsNotSelected>;
|
||||
export type TValidationRuleParamsPositionIs = z.infer<typeof ZValidationRuleParamsPositionIs>;
|
||||
export type TValidationRuleParamsPositionIsHigherThan = z.infer<
|
||||
typeof ZValidationRuleParamsPositionIsHigherThan
|
||||
>;
|
||||
export type TValidationRuleParamsPositionIsLowerThan = z.infer<
|
||||
typeof ZValidationRuleParamsPositionIsLowerThan
|
||||
>;
|
||||
export type TValidationRuleParamsAnswersProvidedGreaterThan = z.infer<
|
||||
typeof ZValidationRuleParamsAnswersProvidedGreaterThan
|
||||
>;
|
||||
export type TValidationRuleParamsAnswersProvidedSmallerThan = z.infer<
|
||||
typeof ZValidationRuleParamsAnswersProvidedSmallerThan
|
||||
>;
|
||||
export type TValidationRuleParamsMinRanked = z.infer<typeof ZValidationRuleParamsMinRanked>;
|
||||
export type TValidationRuleParamsMinRowsAnswered = z.infer<typeof ZValidationRuleParamsMinRowsAnswered>;
|
||||
export type TValidationRuleParamsFileSizeAtLeast = z.infer<typeof ZValidationRuleParamsFileSizeAtLeast>;
|
||||
export type TValidationRuleParamsFileSizeAtMost = z.infer<typeof ZValidationRuleParamsFileSizeAtMost>;
|
||||
export type TValidationRuleParamsFileExtensionIs = z.infer<typeof ZValidationRuleParamsFileExtensionIs>;
|
||||
@@ -397,24 +335,12 @@ export const ZValidationRule = z.discriminatedUnion("type", [
|
||||
params: ZValidationRuleParamsIsLessThan,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("isOnOrLaterThan"),
|
||||
params: ZValidationRuleParamsIsOnOrLaterThan,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("isLaterThan"),
|
||||
params: ZValidationRuleParamsIsLaterThan,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("isOnOrEarlierThan"),
|
||||
params: ZValidationRuleParamsIsOnOrEarlierThan,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("isEarlierThan"),
|
||||
@@ -435,44 +361,14 @@ export const ZValidationRule = z.discriminatedUnion("type", [
|
||||
}),
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("isSelected"),
|
||||
params: ZValidationRuleParamsIsSelected,
|
||||
type: z.literal("minRanked"),
|
||||
params: ZValidationRuleParamsMinRanked,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("isNotSelected"),
|
||||
params: ZValidationRuleParamsIsNotSelected,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("positionIs"),
|
||||
params: ZValidationRuleParamsPositionIs,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("positionIsHigherThan"),
|
||||
params: ZValidationRuleParamsPositionIsHigherThan,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("positionIsLowerThan"),
|
||||
params: ZValidationRuleParamsPositionIsLowerThan,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("answersProvidedGreaterThan"),
|
||||
params: ZValidationRuleParamsAnswersProvidedGreaterThan,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("answersProvidedSmallerThan"),
|
||||
params: ZValidationRuleParamsAnswersProvidedSmallerThan,
|
||||
type: z.literal("minRowsAnswered"),
|
||||
params: ZValidationRuleParamsMinRowsAnswered,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
z.object({
|
||||
@@ -527,26 +423,22 @@ const OPEN_TEXT_RULES = [
|
||||
"isLessThan",
|
||||
] as const;
|
||||
|
||||
const MULTIPLE_CHOICE_SINGLE_RULES = ["isSelected", "isNotSelected"] as const;
|
||||
const MULTIPLE_CHOICE_SINGLE_RULES = [] as const;
|
||||
const MULTIPLE_CHOICE_MULTI_RULES = [
|
||||
"minSelections",
|
||||
"maxSelections",
|
||||
"isSelected",
|
||||
"isNotSelected",
|
||||
] as const;
|
||||
const RATING_RULES = [] as const;
|
||||
const NPS_RULES = [] as const;
|
||||
const DATE_RULES = [
|
||||
"isOnOrLaterThan",
|
||||
"isLaterThan",
|
||||
"isOnOrEarlierThan",
|
||||
"isEarlierThan",
|
||||
"isBetween",
|
||||
"isNotBetween",
|
||||
] as const;
|
||||
const CONSENT_RULES = [] as const;
|
||||
const MATRIX_RULES = ["answersProvidedGreaterThan", "answersProvidedSmallerThan"] as const;
|
||||
const RANKING_RULES = ["positionIs", "positionIsHigherThan", "positionIsLowerThan"] as const;
|
||||
const MATRIX_RULES = ["minRowsAnswered"] as const;
|
||||
const RANKING_RULES = ["minRanked"] as const;
|
||||
const FILE_UPLOAD_RULES = ["fileSizeAtLeast", "fileSizeAtMost", "fileExtensionIs", "fileExtensionIsNot"] as const;
|
||||
const PICTURE_SELECTION_RULES = ["minSelections", "maxSelections"] as const;
|
||||
const ADDRESS_RULES = [] as const;
|
||||
@@ -725,23 +617,6 @@ export const ZValidationRulesForOpenText: z.ZodType<TValidationRulesForOpenText>
|
||||
])
|
||||
);
|
||||
|
||||
export const ZValidationRulesForMultipleChoiceSingle: z.ZodType<TValidationRulesForMultipleChoiceSingle> =
|
||||
z.array(
|
||||
z.discriminatedUnion("type", [
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("isSelected"),
|
||||
params: ZValidationRuleParamsIsSelected,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("isNotSelected"),
|
||||
params: ZValidationRuleParamsIsNotSelected,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
])
|
||||
);
|
||||
|
||||
export const ZValidationRulesForMultipleChoiceMulti: z.ZodType<TValidationRulesForMultipleChoiceMulti> =
|
||||
z.array(
|
||||
@@ -758,18 +633,6 @@ export const ZValidationRulesForMultipleChoiceMulti: z.ZodType<TValidationRulesF
|
||||
params: ZValidationRuleParamsMaxSelections,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("isSelected"),
|
||||
params: ZValidationRuleParamsIsSelected,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("isNotSelected"),
|
||||
params: ZValidationRuleParamsIsNotSelected,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
])
|
||||
);
|
||||
|
||||
@@ -779,24 +642,12 @@ export const ZValidationRulesForNPS: z.ZodType<TValidationRulesForNPS> = z.array
|
||||
|
||||
export const ZValidationRulesForDate: z.ZodType<TValidationRulesForDate> = z.array(
|
||||
z.discriminatedUnion("type", [
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("isOnOrLaterThan"),
|
||||
params: ZValidationRuleParamsIsOnOrLaterThan,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("isLaterThan"),
|
||||
params: ZValidationRuleParamsIsLaterThan,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("isOnOrEarlierThan"),
|
||||
params: ZValidationRuleParamsIsOnOrEarlierThan,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("isEarlierThan"),
|
||||
@@ -824,14 +675,8 @@ export const ZValidationRulesForMatrix: z.ZodType<TValidationRulesForMatrix> = z
|
||||
z.discriminatedUnion("type", [
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("answersProvidedGreaterThan"),
|
||||
params: ZValidationRuleParamsAnswersProvidedGreaterThan,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("answersProvidedSmallerThan"),
|
||||
params: ZValidationRuleParamsAnswersProvidedSmallerThan,
|
||||
type: z.literal("minRowsAnswered"),
|
||||
params: ZValidationRuleParamsMinRowsAnswered,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
])
|
||||
@@ -841,20 +686,8 @@ export const ZValidationRulesForRanking: z.ZodType<TValidationRulesForRanking> =
|
||||
z.discriminatedUnion("type", [
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("positionIs"),
|
||||
params: ZValidationRuleParamsPositionIs,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("positionIsHigherThan"),
|
||||
params: ZValidationRuleParamsPositionIsHigherThan,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
z.object({
|
||||
id: z.string(),
|
||||
type: z.literal("positionIsLowerThan"),
|
||||
params: ZValidationRuleParamsPositionIsLowerThan,
|
||||
type: z.literal("minRanked"),
|
||||
params: ZValidationRuleParamsMinRanked,
|
||||
customErrorMessage: ZI18nString.optional(),
|
||||
}),
|
||||
])
|
||||
|
||||
Reference in New Issue
Block a user