Files
supabase/apps/studio/components/interfaces/Database/Hooks/EditHookPanel.constants.ts
Gildas Garcia 1150d32462 fix: number inputs does not allow some editions (#46538)
## Problem

Because we have controller inputs and zod validation on numbers, many of
them cannot be cleared correctly as deleting their value resets it to
`0`.

## Solution

Update the `Input` component to allow those editions by always storing
and displaying the user entered value

## How to test

- Open the webhook page and add/edit one
- Clear its timeout value and observe that it is not reset to `0`
- Same for:
  - Database network restrictions
  - API settings max rows
  - Disk size modal

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Refactor**
* Standardized numeric form input handling across examples, settings,
and modals — inputs now rely on form bindings and schema coercion for
consistent parsing and simplified behavior.

* **Chores**
* Added form resolver utilities and a user-event testing library to
development dependencies.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-02 14:38:01 +02:00

80 lines
2.6 KiB
TypeScript

import { getKeyValueFieldArrayValidationIssues } from 'ui-patterns/form/KeyValueFieldArray/validation'
import { z } from 'zod'
import { httpEndpointUrlSchema } from '@/lib/validation/http-url'
const httpRequestSchema = z.object({
function_type: z.literal('http_request'),
http_url: httpEndpointUrlSchema({
requiredMessage: 'Please provide a URL',
invalidMessage: 'Please provide a valid URL',
prefixMessage: 'Please prefix your URL with http:// or https://',
}),
})
const supabaseFunctionSchema = z.object({
function_type: z.literal('supabase_function'),
http_url: z
.string()
.min(1, 'Please select an edge function')
.refine((val) => !val.includes('undefined'), 'No edge functions available for selection'),
})
const httpHeadersSchema = z.array(
z.object({ id: z.string(), name: z.string().trim(), value: z.string().trim() })
)
const httpParametersSchema = z.array(
z.object({ id: z.string(), name: z.string().trim(), value: z.string().trim() })
)
const addKeyValueIssues = (
rows: z.infer<typeof httpHeadersSchema> | z.infer<typeof httpParametersSchema>,
ctx: z.RefinementCtx,
pathPrefix: 'httpHeaders' | 'httpParameters'
) => {
const isHeaderField = pathPrefix === 'httpHeaders'
getKeyValueFieldArrayValidationIssues({
rows,
keyFieldName: 'name',
valueFieldName: 'value',
keyRequiredMessage: isHeaderField ? 'Header name is required' : 'Parameter name is required',
valueRequiredMessage: isHeaderField
? 'Header value is required'
: 'Parameter value is required',
}).forEach((issue) => {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: issue.message,
path: [pathPrefix, ...issue.path],
})
})
}
export const FormSchema = z
.object({
name: z.string().min(1, 'Please provide a name for your webhook'),
table_id: z.string().min(1, 'Please select a table'),
http_method: z.enum(['GET', 'POST']),
timeout_ms: z
.union([
z.literal(''),
z.coerce
.number()
.gte(1000, 'Timeout should be at least 1000ms')
.lte(10000, 'Timeout should not exceed 10,000ms'),
])
.refine((value) => value !== '', 'Timeout is required'),
events: z.array(z.string()).min(1, 'Please select at least one event'),
httpHeaders: httpHeadersSchema,
httpParameters: httpParametersSchema,
})
.and(z.discriminatedUnion('function_type', [httpRequestSchema, supabaseFunctionSchema]))
.superRefine((data, ctx) => {
addKeyValueIssues(data.httpHeaders, ctx, 'httpHeaders')
addKeyValueIssues(data.httpParameters, ctx, 'httpParameters')
})
export type WebhookFormValues = z.infer<typeof FormSchema>