mirror of
https://github.com/formbricks/formbricks.git
synced 2026-03-13 19:30:36 -05:00
chore: refactor
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { DEFAULT_DATE_STORAGE_FORMAT } from "@formbricks/types/surveys/date-formats";
|
||||
import { TSurvey, TSurveyElementSummaryDate } from "@formbricks/types/surveys/types";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
import { timeSince } from "@/lib/time";
|
||||
@@ -32,7 +33,7 @@ export const DateElementSummary = ({ elementSummary, environmentId, survey, loca
|
||||
};
|
||||
|
||||
const renderResponseValue = (value: string) => {
|
||||
const format = elementSummary.element?.format ?? "y-M-d";
|
||||
const format = elementSummary.element?.format ?? DEFAULT_DATE_STORAGE_FORMAT;
|
||||
return formatStoredDateForDisplay(value, format, `${t("common.invalid_date")}(${value})`);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { CheckCheckIcon, MousePointerClickIcon, PhoneIcon } from "lucide-react";
|
||||
import React from "react";
|
||||
import { logger } from "@formbricks/logger";
|
||||
import { TResponseDataValue } from "@formbricks/types/responses";
|
||||
import { DEFAULT_DATE_STORAGE_FORMAT } from "@formbricks/types/surveys/date-formats";
|
||||
import {
|
||||
TSurveyDateElement,
|
||||
TSurveyElement,
|
||||
@@ -68,7 +69,7 @@ export const RenderResponse: React.FC<RenderResponseProps> = ({
|
||||
break;
|
||||
case TSurveyElementTypeEnum.Date:
|
||||
if (typeof responseData === "string") {
|
||||
const format = (element as TSurveyDateElement).format ?? "y-M-d";
|
||||
const format = element.format ?? DEFAULT_DATE_STORAGE_FORMAT;
|
||||
const formatted = formatStoredDateForDisplay(responseData, format, responseData);
|
||||
if (formatted === responseData) {
|
||||
logger.warn(
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { PlusIcon } from "lucide-react";
|
||||
import { type JSX } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { DATE_STORAGE_FORMAT_IDS, DATE_STORAGE_FORMAT_LABELS } from "@formbricks/types/surveys/date-formats";
|
||||
import type { TSurveyDateElement, TSurveyElement } from "@formbricks/types/surveys/elements";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
@@ -27,20 +28,10 @@ interface IDateElementFormProps {
|
||||
isExternalUrlsAllowed?: boolean;
|
||||
}
|
||||
|
||||
const dateOptions = [
|
||||
{
|
||||
value: "M-d-y",
|
||||
label: "MM-DD-YYYY",
|
||||
},
|
||||
{
|
||||
value: "d-M-y",
|
||||
label: "DD-MM-YYYY",
|
||||
},
|
||||
{
|
||||
value: "y-M-d",
|
||||
label: "YYYY-MM-DD",
|
||||
},
|
||||
];
|
||||
const dateOptions = DATE_STORAGE_FORMAT_IDS.map((value) => ({
|
||||
value,
|
||||
label: DATE_STORAGE_FORMAT_LABELS[value],
|
||||
}));
|
||||
|
||||
export const DateElementForm = ({
|
||||
element,
|
||||
@@ -121,9 +112,7 @@ export const DateElementForm = ({
|
||||
<OptionsSwitch
|
||||
options={dateOptions}
|
||||
currentOption={element.format}
|
||||
handleOptionChange={(value: string) =>
|
||||
updateElement(elementIdx, { format: value as "M-d-y" | "d-M-y" | "y-M-d" })
|
||||
}
|
||||
handleOptionChange={(value: string) => updateElement(elementIdx, { format: value })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
"react-dom": "^19.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@formbricks/types": "workspace:*",
|
||||
"@formkit/auto-animate": "0.9.0",
|
||||
"@radix-ui/react-checkbox": "1.3.3",
|
||||
"@radix-ui/react-dropdown-menu": "2.1.16",
|
||||
|
||||
@@ -1,37 +1,32 @@
|
||||
import * as React from "react";
|
||||
import {
|
||||
DATE_FORMAT_OUTPUT_ORDER,
|
||||
DATE_FORMAT_PARSE_ORDER,
|
||||
DEFAULT_DATE_STORAGE_FORMAT,
|
||||
type TSurveyDateStorageFormat,
|
||||
} from "@formbricks/types/surveys/date-formats";
|
||||
import { Calendar } from "@/components/general/calendar";
|
||||
import { ElementError } from "@/components/general/element-error";
|
||||
import { ElementHeader } from "@/components/general/element-header";
|
||||
import { getDateFnsLocale } from "@/lib/locale";
|
||||
|
||||
/** Storage format for date response values: M=month, d=day, y=year */
|
||||
export type DateStorageFormat = "M-d-y" | "d-M-y" | "y-M-d";
|
||||
export type DateStorageFormat = TSurveyDateStorageFormat;
|
||||
|
||||
const ISO_FIRST_CHARS = /^\d{4}/;
|
||||
|
||||
function parseValueToDate(value: string, format: DateStorageFormat): Date | undefined {
|
||||
const trimmed = value?.trim();
|
||||
if (!trimmed) return undefined;
|
||||
const parts = trimmed.split("-");
|
||||
if (parts.length !== 3) return undefined;
|
||||
const [a, b, c] = parts.map((p) => parseInt(p, 10));
|
||||
if (Number.isNaN(a) || Number.isNaN(b) || Number.isNaN(c)) return undefined;
|
||||
const useIso = /^\d{4}/.test(trimmed);
|
||||
const effective = useIso ? "y-M-d" : format;
|
||||
let year: number;
|
||||
let month: number;
|
||||
let day: number;
|
||||
if (effective === "y-M-d") {
|
||||
year = a;
|
||||
month = b;
|
||||
day = c;
|
||||
} else if (effective === "d-M-y") {
|
||||
day = a;
|
||||
month = b;
|
||||
year = c;
|
||||
} else {
|
||||
month = a;
|
||||
day = b;
|
||||
year = c;
|
||||
}
|
||||
const nums = parts.map((p) => Number.parseInt(p, 10));
|
||||
if (nums.some(Number.isNaN)) return undefined;
|
||||
const useIso = ISO_FIRST_CHARS.test(trimmed);
|
||||
const effective = useIso ? DEFAULT_DATE_STORAGE_FORMAT : format;
|
||||
const order = DATE_FORMAT_PARSE_ORDER[effective];
|
||||
const year = nums[order.yearIdx];
|
||||
const month = nums[order.monthIdx];
|
||||
const day = nums[order.dayIdx];
|
||||
if (month < 1 || month > 12 || day < 1 || day > 31) return undefined;
|
||||
const date = new Date(year, month - 1, day);
|
||||
if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day)
|
||||
@@ -40,16 +35,9 @@ function parseValueToDate(value: string, format: DateStorageFormat): Date | unde
|
||||
}
|
||||
|
||||
function formatDateForStorage(year: string, month: string, day: string, format: DateStorageFormat): string {
|
||||
switch (format) {
|
||||
case "y-M-d":
|
||||
return `${year}-${month}-${day}`;
|
||||
case "M-d-y":
|
||||
return `${month}-${day}-${year}`;
|
||||
case "d-M-y":
|
||||
return `${day}-${month}-${year}`;
|
||||
default:
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
const comps = [year, month, day];
|
||||
const [i, j, k] = DATE_FORMAT_OUTPUT_ORDER[format];
|
||||
return `${comps[i]}-${comps[j]}-${comps[k]}`;
|
||||
}
|
||||
|
||||
interface DateElementProps {
|
||||
@@ -96,7 +84,7 @@ function DateElement({
|
||||
inputId,
|
||||
value,
|
||||
onChange,
|
||||
outputFormat = "y-M-d",
|
||||
outputFormat = DEFAULT_DATE_STORAGE_FORMAT,
|
||||
required = false,
|
||||
requiredLabel,
|
||||
minDate,
|
||||
@@ -121,8 +109,8 @@ function DateElement({
|
||||
const newDate = parseValueToDate(value, outputFormat);
|
||||
setDate((prevDate) => {
|
||||
if (!newDate) return undefined;
|
||||
if (!prevDate || newDate.getTime() !== prevDate.getTime()) return newDate;
|
||||
return prevDate;
|
||||
if (prevDate?.getTime() !== newDate.getTime()) return newDate;
|
||||
return prevDate ?? undefined;
|
||||
});
|
||||
}, [value, outputFormat]);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useState } from "preact/hooks";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { DateElement as SurveyUIDateElement } from "@formbricks/survey-ui";
|
||||
import { type TResponseData, type TResponseTtc } from "@formbricks/types/responses";
|
||||
import { DEFAULT_DATE_STORAGE_FORMAT } from "@formbricks/types/surveys/date-formats";
|
||||
import type { TSurveyDateElement } from "@formbricks/types/surveys/elements";
|
||||
import { TSurveyLanguage } from "@formbricks/types/surveys/types";
|
||||
import { getLocalizedValue } from "@/lib/i18n";
|
||||
@@ -68,7 +69,7 @@ export function DateElement({
|
||||
description={element.subheader ? getLocalizedValue(element.subheader, languageCode) : undefined}
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
outputFormat={element.format ?? "y-M-d"}
|
||||
outputFormat={element.format ?? DEFAULT_DATE_STORAGE_FORMAT}
|
||||
minDate={getMinDate()}
|
||||
maxDate={getMaxDate()}
|
||||
required={isRequired}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
/**
|
||||
* Date storage format for survey date elements.
|
||||
* Values are stored in response data in this order: y = year, M = month, d = day.
|
||||
*/
|
||||
export type TSurveyDateStorageFormat = "M-d-y" | "d-M-y" | "y-M-d";
|
||||
import {
|
||||
DATE_FORMAT_PARSE_ORDER,
|
||||
DATE_STORAGE_FORMATS_LIST,
|
||||
DEFAULT_DATE_STORAGE_FORMAT,
|
||||
type TSurveyDateStorageFormat,
|
||||
} from "@formbricks/types/surveys/date-formats";
|
||||
|
||||
export type { TSurveyDateStorageFormat };
|
||||
|
||||
const ISO_FIRST_CHARS = /^\d{4}/;
|
||||
|
||||
/**
|
||||
* Parse a date string stored in response data using the element's storage format.
|
||||
* Pure function, synchronous, no I/O.
|
||||
* Uses the format registry from @formbricks/types for data-driven parsing.
|
||||
*
|
||||
* Backward compatibility: if the value starts with 4 digits (YYYY), it is treated
|
||||
* as ISO (y-M-d) regardless of format, so legacy YYYY-MM-DD values parse correctly.
|
||||
@@ -17,7 +20,10 @@ const ISO_FIRST_CHARS = /^\d{4}/;
|
||||
* @param format - The format used when the value was stored; defaults to "y-M-d" (ISO)
|
||||
* @returns Parsed Date in local time, or null if invalid
|
||||
*/
|
||||
export function parseDateByFormat(value: string, format: TSurveyDateStorageFormat = "y-M-d"): Date | null {
|
||||
export function parseDateByFormat(
|
||||
value: string,
|
||||
format: TSurveyDateStorageFormat = DEFAULT_DATE_STORAGE_FORMAT
|
||||
): Date | null {
|
||||
const trimmed = value?.trim();
|
||||
if (!trimmed || typeof trimmed !== "string") {
|
||||
return null;
|
||||
@@ -28,44 +34,16 @@ export function parseDateByFormat(value: string, format: TSurveyDateStorageForma
|
||||
return null;
|
||||
}
|
||||
|
||||
// Backward compat: value starts with 4 digits (year) => treat as ISO
|
||||
const useIso = ISO_FIRST_CHARS.test(trimmed);
|
||||
const effectiveFormat = useIso ? "y-M-d" : format;
|
||||
const effectiveFormat = useIso ? DEFAULT_DATE_STORAGE_FORMAT : format;
|
||||
|
||||
let year: number;
|
||||
let month: number;
|
||||
let day: number;
|
||||
const order = DATE_FORMAT_PARSE_ORDER[effectiveFormat];
|
||||
const nums = parts.map((p) => Number.parseInt(p, 10));
|
||||
if (nums.some(Number.isNaN)) return null;
|
||||
const year = nums[order.yearIdx];
|
||||
const month = nums[order.monthIdx];
|
||||
const day = nums[order.dayIdx];
|
||||
|
||||
switch (effectiveFormat) {
|
||||
case "y-M-d": {
|
||||
const [y, m, d] = parts.map((p) => parseInt(p, 10));
|
||||
if (Number.isNaN(y) || Number.isNaN(m) || Number.isNaN(d)) return null;
|
||||
year = y;
|
||||
month = m;
|
||||
day = d;
|
||||
break;
|
||||
}
|
||||
case "d-M-y": {
|
||||
const [d, m, y] = parts.map((p) => parseInt(p, 10));
|
||||
if (Number.isNaN(y) || Number.isNaN(m) || Number.isNaN(d)) return null;
|
||||
year = y;
|
||||
month = m;
|
||||
day = d;
|
||||
break;
|
||||
}
|
||||
case "M-d-y": {
|
||||
const [m, d, y] = parts.map((p) => parseInt(p, 10));
|
||||
if (Number.isNaN(y) || Number.isNaN(m) || Number.isNaN(d)) return null;
|
||||
year = y;
|
||||
month = m;
|
||||
day = d;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
// Month 1-12, day 1-31; Date constructor uses 0-indexed month
|
||||
if (month < 1 || month > 12 || day < 1 || day > 31) {
|
||||
return null;
|
||||
}
|
||||
@@ -78,8 +56,6 @@ export function parseDateByFormat(value: string, format: TSurveyDateStorageForma
|
||||
return date;
|
||||
}
|
||||
|
||||
const STORAGE_FORMATS: TSurveyDateStorageFormat[] = ["y-M-d", "d-M-y", "M-d-y"];
|
||||
|
||||
/**
|
||||
* Try to parse a date string using each known storage format in order.
|
||||
* Use when the storage format is unknown (e.g. recall placeholders).
|
||||
@@ -88,7 +64,7 @@ const STORAGE_FORMATS: TSurveyDateStorageFormat[] = ["y-M-d", "d-M-y", "M-d-y"];
|
||||
* @returns Parsed Date or null if no format matched
|
||||
*/
|
||||
export function parseDateWithFormats(value: string): Date | null {
|
||||
for (const format of STORAGE_FORMATS) {
|
||||
for (const format of DATE_STORAGE_FORMATS_LIST) {
|
||||
const parsed = parseDateByFormat(value, format);
|
||||
if (parsed !== null) return parsed;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { type TJsEnvironmentStateSurvey } from "@formbricks/types/js";
|
||||
import { type TResponseData, type TResponseVariables } from "@formbricks/types/responses";
|
||||
import { type TActionCalculate, type TSurveyBlockLogicAction } from "@formbricks/types/surveys/blocks";
|
||||
import { TSurveyElementTypeEnum } from "@formbricks/types/surveys/constants";
|
||||
import { DEFAULT_DATE_STORAGE_FORMAT } from "@formbricks/types/surveys/date-formats";
|
||||
import type { TSurveyDateElement, TSurveyElement } from "@formbricks/types/surveys/elements";
|
||||
import { type TConditionGroup, type TSingleCondition } from "@formbricks/types/surveys/logic";
|
||||
import { type TSurveyVariable } from "@formbricks/types/surveys/types";
|
||||
@@ -9,11 +10,18 @@ import { parseDateByFormat } from "@/lib/date-format";
|
||||
import { getLocalizedValue } from "@/lib/i18n";
|
||||
import { getElementsFromSurveyBlocks } from "./utils";
|
||||
|
||||
/** Coerce to string for date comparison; avoids Object's default stringification. */
|
||||
function toDateOperandString(value: unknown): string {
|
||||
if (typeof value === "string") return value;
|
||||
if (typeof value === "number") return String(value);
|
||||
return "";
|
||||
}
|
||||
|
||||
function parseDateOperand(value: string, field: TSurveyElement | ""): Date | null {
|
||||
const format =
|
||||
field && typeof field === "object" && field.type === TSurveyElementTypeEnum.Date && "format" in field
|
||||
? ((field as TSurveyDateElement).format ?? "y-M-d")
|
||||
: "y-M-d";
|
||||
? (field.format ?? DEFAULT_DATE_STORAGE_FORMAT)
|
||||
: DEFAULT_DATE_STORAGE_FORMAT;
|
||||
return parseDateByFormat(value, format);
|
||||
}
|
||||
|
||||
@@ -26,7 +34,7 @@ function compareDateOperands(
|
||||
compare: (left: Date, right: Date) => boolean
|
||||
): boolean {
|
||||
const leftDate = parseDateOperand(leftValue, leftField);
|
||||
const rightDate = parseDateOperand(String(rightValue), rightField);
|
||||
const rightDate = parseDateOperand(rightValue, rightField);
|
||||
if (leftDate === null) {
|
||||
console.warn(`[logic] ${operator}: could not parse left date`, {
|
||||
elementId: leftField.id,
|
||||
@@ -468,19 +476,19 @@ const evaluateSingleCondition = (
|
||||
return leftValue !== "clicked";
|
||||
case "isAfter":
|
||||
return compareDateOperands(
|
||||
String(leftValue),
|
||||
String(rightValue),
|
||||
leftField as TSurveyElement,
|
||||
rightField as TSurveyElement,
|
||||
toDateOperandString(leftValue),
|
||||
toDateOperandString(rightValue),
|
||||
leftField,
|
||||
rightField,
|
||||
"isAfter",
|
||||
(l, r) => l.getTime() > r.getTime()
|
||||
);
|
||||
case "isBefore":
|
||||
return compareDateOperands(
|
||||
String(leftValue),
|
||||
String(rightValue),
|
||||
leftField as TSurveyElement,
|
||||
rightField as TSurveyElement,
|
||||
toDateOperandString(leftValue),
|
||||
toDateOperandString(rightValue),
|
||||
leftField,
|
||||
rightField,
|
||||
"isBefore",
|
||||
(l, r) => l.getTime() < r.getTime()
|
||||
);
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import type { TFunction } from "i18next";
|
||||
import type { TResponseDataValue } from "@formbricks/types/responses";
|
||||
import {
|
||||
DEFAULT_DATE_STORAGE_FORMAT,
|
||||
type TSurveyDateStorageFormat,
|
||||
} from "@formbricks/types/surveys/date-formats";
|
||||
import type { TSurveyDateElement, TSurveyElement } from "@formbricks/types/surveys/elements";
|
||||
import type {
|
||||
TValidationRuleParams,
|
||||
@@ -31,22 +35,22 @@ import { parseDateByFormat } from "@/lib/date-format";
|
||||
import { countSelections } from "./validators/selection-utils";
|
||||
import { validateEmail, validatePhone, validateUrl } from "./validators/validation-utils";
|
||||
|
||||
function getDateElementFormat(element: TSurveyElement): "M-d-y" | "d-M-y" | "y-M-d" {
|
||||
function getDateElementFormat(element: TSurveyElement): TSurveyDateStorageFormat {
|
||||
if (element.type === "date" && "format" in element) {
|
||||
return (element as TSurveyDateElement).format ?? "y-M-d";
|
||||
return element.format ?? DEFAULT_DATE_STORAGE_FORMAT;
|
||||
}
|
||||
return "y-M-d";
|
||||
return DEFAULT_DATE_STORAGE_FORMAT;
|
||||
}
|
||||
|
||||
function parseForDateComparison(
|
||||
value: string,
|
||||
format: "M-d-y" | "d-M-y" | "y-M-d",
|
||||
format: TSurveyDateStorageFormat,
|
||||
element: TSurveyElement,
|
||||
ruleName: string,
|
||||
paramDates: [string] | [string, string]
|
||||
): { valueDate: Date; paramDates: Date[] } | null {
|
||||
const valueDate = parseDateByFormat(value, format);
|
||||
const parsedParams = paramDates.map((d) => parseDateByFormat(d, "y-M-d"));
|
||||
const parsedParams = paramDates.map((d) => parseDateByFormat(d, DEFAULT_DATE_STORAGE_FORMAT));
|
||||
if (valueDate === null) {
|
||||
console.warn(`[date validation] ${ruleName}: could not parse response date`, {
|
||||
elementId: element.id,
|
||||
@@ -55,7 +59,7 @@ function parseForDateComparison(
|
||||
});
|
||||
return null;
|
||||
}
|
||||
if (parsedParams.some((d) => d === null)) return null;
|
||||
if (parsedParams.includes(null)) return null;
|
||||
return { valueDate, paramDates: parsedParams as Date[] };
|
||||
}
|
||||
|
||||
|
||||
49
packages/types/surveys/date-formats.ts
Normal file
49
packages/types/surveys/date-formats.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Single source of truth for survey date storage formats.
|
||||
* Values are stored in response data as hyphen-separated strings: year, month, day
|
||||
* in the order defined by each format (e.g. y-M-d = YYYY-MM-DD).
|
||||
*
|
||||
* To add a new format: add the id to the tuple, add parse order and output order
|
||||
* entries, then implement parsing/formatting in packages that consume this (surveys, survey-ui).
|
||||
*/
|
||||
|
||||
/** Supported date storage format ids. Extend this tuple to add new formats. */
|
||||
export const DATE_STORAGE_FORMAT_IDS = ["M-d-y", "d-M-y", "y-M-d"] as const;
|
||||
|
||||
export type TSurveyDateStorageFormat = (typeof DATE_STORAGE_FORMAT_IDS)[number];
|
||||
|
||||
/** Default format (ISO-style). Used when element has no format set. */
|
||||
export const DEFAULT_DATE_STORAGE_FORMAT: TSurveyDateStorageFormat = "y-M-d";
|
||||
|
||||
/**
|
||||
* For each format, indices into the split("-") array for [year, month, day].
|
||||
* parts[yearIdx]=year, parts[monthIdx]=month, parts[dayIdx]=day.
|
||||
*/
|
||||
export const DATE_FORMAT_PARSE_ORDER: Record<
|
||||
TSurveyDateStorageFormat,
|
||||
{ yearIdx: number; monthIdx: number; dayIdx: number }
|
||||
> = {
|
||||
"y-M-d": { yearIdx: 0, monthIdx: 1, dayIdx: 2 },
|
||||
"d-M-y": { yearIdx: 2, monthIdx: 1, dayIdx: 0 },
|
||||
"M-d-y": { yearIdx: 2, monthIdx: 0, dayIdx: 1 },
|
||||
};
|
||||
|
||||
/**
|
||||
* For each format, indices into [year, month, day] for output order.
|
||||
* Output = [year, month, day][out[0]] + "-" + [year, month, day][out[1]] + "-" + [year, month, day][out[2]]
|
||||
*/
|
||||
export const DATE_FORMAT_OUTPUT_ORDER: Record<TSurveyDateStorageFormat, [number, number, number]> = {
|
||||
"y-M-d": [0, 1, 2],
|
||||
"d-M-y": [2, 1, 0],
|
||||
"M-d-y": [1, 2, 0],
|
||||
};
|
||||
|
||||
/** All format ids as an array (for iteration, e.g. try-parse or dropdowns). */
|
||||
export const DATE_STORAGE_FORMATS_LIST: TSurveyDateStorageFormat[] = [...DATE_STORAGE_FORMAT_IDS];
|
||||
|
||||
/** Default display labels for UI (e.g. editor dropdown). Apps can override with i18n. */
|
||||
export const DATE_STORAGE_FORMAT_LABELS: Record<TSurveyDateStorageFormat, string> = {
|
||||
"M-d-y": "MM-DD-YYYY",
|
||||
"d-M-y": "DD-MM-YYYY",
|
||||
"y-M-d": "YYYY-MM-DD",
|
||||
};
|
||||
@@ -3,6 +3,7 @@ import { ZStorageUrl, ZUrl } from "../common";
|
||||
import { ZI18nString } from "../i18n";
|
||||
import { ZAllowedFileExtension } from "../storage";
|
||||
import { TSurveyElementTypeEnum } from "./constants";
|
||||
import { DATE_STORAGE_FORMAT_IDS } from "./date-formats";
|
||||
import { FORBIDDEN_IDS } from "./validation";
|
||||
import { ZValidationRules } from "./validation-rules";
|
||||
|
||||
@@ -257,7 +258,7 @@ export type TSurveyPictureSelectionElement = z.infer<typeof ZSurveyPictureSelect
|
||||
export const ZSurveyDateElement = ZSurveyElementBase.extend({
|
||||
type: z.literal(TSurveyElementTypeEnum.Date),
|
||||
html: ZI18nString.optional(),
|
||||
format: z.enum(["M-d-y", "d-M-y", "y-M-d"]),
|
||||
format: z.enum(DATE_STORAGE_FORMAT_IDS),
|
||||
validation: ZValidation.optional(),
|
||||
});
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import { ZAllowedFileExtension } from "../storage";
|
||||
import { ZBaseStyling } from "../styling";
|
||||
import { type TSurveyBlock, type TSurveyBlockLogicAction, ZSurveyBlocks } from "./blocks";
|
||||
import { findBlocksWithCyclicLogic } from "./blocks-validation";
|
||||
import { DATE_STORAGE_FORMAT_IDS } from "./date-formats";
|
||||
import {
|
||||
type TSurveyElement,
|
||||
TSurveyElementTypeEnum,
|
||||
@@ -560,7 +561,7 @@ export const ZSurveyRatingQuestion = ZSurveyQuestionBase.extend({
|
||||
export const ZSurveyDateQuestion = ZSurveyQuestionBase.extend({
|
||||
type: z.literal(TSurveyQuestionTypeEnum.Date),
|
||||
html: ZI18nString.optional(),
|
||||
format: z.enum(["M-d-y", "d-M-y", "y-M-d"]),
|
||||
format: z.enum(DATE_STORAGE_FORMAT_IDS),
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@@ -806,6 +806,9 @@ importers:
|
||||
|
||||
packages/survey-ui:
|
||||
dependencies:
|
||||
'@formbricks/types':
|
||||
specifier: workspace:*
|
||||
version: link:../types
|
||||
'@formkit/auto-animate':
|
||||
specifier: 0.9.0
|
||||
version: 0.9.0
|
||||
|
||||
Reference in New Issue
Block a user