+ date === undefined
+ ? undefined
+ : dateFormat(
+ date,
+ props.type === "date" ? "yyyy-MM-dd" : "yyyy-MM-dd'T'HH:mm:ss",
+ );
+
return (
props.field().handleChange(e.target.value)}
+ onInput={(e) => {
+ props.field().handleChange(e.target.value);
+ }}
onKeyDown={(e) => {
if (e.key === "Enter") {
shakeItIfYouWantIt();
@@ -84,8 +97,11 @@ export function InputField(props: {
onFocus={() => props.onFocus?.()}
dir={props.dir}
maxLength={props.maxLength}
+ {...getNumberOptions(props.schema)}
+ {...getDateOptions(props.schema, formatDate)}
min={props.min}
max={props.max}
+ step={props.step?.toString()}
/>
@@ -93,3 +109,43 @@ export function InputField(props: {
);
}
+
+function getNumberOptions(schema: ZodTypeAny | undefined): {
+ min?: number;
+ max?: number;
+ step?: string;
+} {
+ if (schema === undefined) return {};
+ if (getZodType(schema) !== ZodFirstPartyTypeKind.ZodNumber) return {};
+ const numberSchema = schema as ZodNumber;
+
+ return {
+ min: numberSchema.minValue ?? undefined,
+ max: numberSchema.maxValue ?? undefined,
+ step: numberSchema.isInt ? "1" : "any",
+ };
+}
+
+function getDateOptions(
+ schema: ZodTypeAny | undefined,
+ format: (val: Date | undefined) => string | undefined,
+): {
+ min?: string;
+ max?: string;
+} {
+ if (schema === undefined) return {};
+ if (getZodType(schema) !== ZodFirstPartyTypeKind.ZodDate) return {};
+
+ const applyFormat = (it: Date | null) =>
+ it === null ? undefined : format(it);
+
+ return {
+ min: applyFormat((schema as ZodDate).minDate),
+ max: applyFormat((schema as ZodDate).maxDate),
+ };
+}
+
+function getZodType(schema: ZodTypeAny): ZodFirstPartyTypeKind {
+ // oxlint-disable-next-line typescript/no-unsafe-assignment typescript/no-unsafe-member-access
+ return schema._def["typeName"] as ZodFirstPartyTypeKind;
+}
diff --git a/frontend/src/ts/components/ui/form/TextareaField.tsx b/frontend/src/ts/components/ui/form/TextareaField.tsx
index 394069d40645..555027c597ce 100644
--- a/frontend/src/ts/components/ui/form/TextareaField.tsx
+++ b/frontend/src/ts/components/ui/form/TextareaField.tsx
@@ -8,6 +8,7 @@ export function TextareaField(props: {
field: Accessor;
ref?: HTMLTextAreaElement | ((el: HTMLTextAreaElement) => void);
placeholder?: string;
+ autocomplete?: string;
disabled?: boolean;
class?: string;
maxLength?: number;
@@ -28,6 +29,7 @@ export function TextareaField(props: {
id={props.field().name as string}
name={props.field().name as string}
placeholder={props.placeholder ?? ""}
+ autocomplete={props.autocomplete}
value={props.field().state.value as string}
onBlur={() => props.field().handleBlur()}
onInput={(e) => props.field().handleChange(e.currentTarget.value)}