| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 |
- import { format } from 'date-fns'
- import { CalendarIcon, ExternalLink } from 'lucide-react'
- import { useEffect } from 'react'
- import { useFormContext, type Control } from 'react-hook-form'
- import ReactMarkdown from 'react-markdown'
- import {
- Button,
- Calendar,
- FormControl,
- FormInputGroupInput,
- Input,
- InputGroup,
- InputGroupAddon,
- Popover,
- PopoverContent,
- PopoverTrigger,
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
- Separator,
- SheetSection,
- Switch,
- Textarea,
- FormField as UIFormField,
- useWatch,
- } from 'ui'
- import { Input as DataInput } from 'ui-patterns/DataInputs/Input'
- import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
- import type { Enum } from './AuthProvidersForm.types'
- import { Markdown } from '@/components/interfaces/Markdown'
- import { BASE_PATH } from '@/lib/constants'
- interface FormFieldProps {
- projectRef: string | undefined
- organizationSlug: string | undefined
- name: string
- properties: any
- control: Control
- hasAccess: boolean
- disabled?: boolean
- readOnly?: boolean
- }
- const FormField = ({
- projectRef,
- name,
- properties,
- organizationSlug,
- control,
- hasAccess,
- disabled: disabledProp,
- readOnly,
- }: FormFieldProps) => {
- const { setValue } = useFormContext()
- const { description: originalDescription } = properties
- let description = originalDescription
- if (originalDescription && projectRef) {
- description = originalDescription.replace(
- /\(\.\.\/auth\/(.*?)\)/g,
- `(/project/${projectRef}/auth/$1)`
- )
- }
- const fieldValue = useWatch({ control, name })
- if (!hasAccess) {
- const planMessage = organizationSlug
- ? `Only available on [Pro plan](/org/${organizationSlug}/billing?panel=subscriptionPlan) and above.`
- : ''
- description = originalDescription ? `${originalDescription} ${planMessage}` : planMessage
- }
- const disabled =
- disabledProp || (properties.type === 'boolean' ? !hasAccess && !fieldValue : !hasAccess)
- const showValue = useWatch({
- control,
- name: properties.show?.key,
- disabled: properties.show == null,
- })
- useEffect(() => {
- if (properties.show?.key != null && !showValue && fieldValue !== '') {
- setValue(name, '', { shouldDirty: true })
- }
- }, [fieldValue, name, properties.show?.key, setValue, showValue])
- if (properties.show) {
- if (properties.show.matches) {
- if (!properties.show.matches.includes(showValue)) {
- return null
- }
- } else if (!showValue) {
- return null
- }
- }
- switch (properties.type) {
- case 'datetime':
- return (
- <>
- <SheetSection>
- <UIFormField
- control={control}
- name={name}
- disabled={disabled || readOnly}
- render={({ field }) => (
- <FormItemLayout
- layout="horizontal"
- label={properties.title}
- description={
- description ? (
- <ReactMarkdown unwrapDisallowed disallowedElements={['p']}>
- {description}
- </ReactMarkdown>
- ) : null
- }
- >
- <FormControl>
- <Popover>
- <PopoverTrigger asChild>
- <Button
- type="outline"
- className="w-full justify-start text-left font-normal px-3 py-4"
- icon={<CalendarIcon className="h-4 w-4" />}
- size="small"
- >
- {field.value ? format(new Date(field.value), 'PPP') : 'Pick a date'}
- </Button>
- </PopoverTrigger>
- <PopoverContent className="w-auto p-0" align="start">
- <Calendar
- mode="single"
- selected={field.value}
- onSelect={(date) => {
- field.onChange(date?.toISOString())
- }}
- initialFocus
- />
- </PopoverContent>
- </Popover>
- </FormControl>
- </FormItemLayout>
- )}
- />
- </SheetSection>
- <Separator className="w-full" />
- </>
- )
- case 'string':
- return (
- <>
- <SheetSection>
- <UIFormField
- control={control}
- name={name}
- disabled={disabled}
- render={({ field }) => (
- <FormItemLayout
- layout="horizontal"
- label={properties.title}
- description={
- description ? (
- <Markdown content={description} className="text-foreground-lighter" />
- ) : null
- }
- >
- <FormControl className="col-span-6">
- {properties.isSecret ? (
- <DataInput
- {...field}
- id={name}
- size="small"
- copy
- reveal
- readOnly={readOnly}
- />
- ) : (
- <Input {...field} id={name} readOnly={readOnly} />
- )}
- </FormControl>
- </FormItemLayout>
- )}
- />
- </SheetSection>
- <Separator className="w-full" />
- </>
- )
- case 'multiline-string':
- return (
- <>
- <SheetSection>
- <UIFormField
- control={control}
- name={name}
- disabled={disabled}
- render={({ field }) => (
- <FormItemLayout
- layout="horizontal"
- label={properties.title}
- description={
- description ? (
- <Markdown content={description} className="text-foreground-lighter" />
- ) : null
- }
- >
- <FormControl className="col-span-6">
- <Textarea
- {...field}
- id={name}
- rows={4}
- placeholder="Enter multi-line text"
- className="resize-none"
- readOnly={readOnly}
- />
- </FormControl>
- </FormItemLayout>
- )}
- />
- </SheetSection>
- <Separator className="w-full" />
- </>
- )
- case 'number':
- return (
- <>
- <SheetSection>
- <UIFormField
- control={control}
- name={name}
- disabled={disabled}
- render={({ field }) => (
- <FormItemLayout
- layout="horizontal"
- label={properties.title}
- description={
- description ? (
- <Markdown content={description} className="text-foreground-lighter" />
- ) : null
- }
- >
- <FormControl className="col-span-6">
- {properties.units ? (
- <InputGroup>
- <FormInputGroupInput
- {...field}
- id={name}
- type="number"
- onChange={(e) =>
- field.onChange(e.target.value === '' ? '' : Number(e.target.value))
- }
- readOnly={readOnly}
- />
- <InputGroupAddon align="inline-end">
- <ReactMarkdown unwrapDisallowed disallowedElements={['p']}>
- {properties.units}
- </ReactMarkdown>
- </InputGroupAddon>
- </InputGroup>
- ) : (
- <Input
- {...field}
- id={name}
- type="number"
- onChange={(e) =>
- field.onChange(e.target.value === '' ? '' : Number(e.target.value))
- }
- readOnly={readOnly}
- />
- )}
- </FormControl>
- </FormItemLayout>
- )}
- />
- </SheetSection>
- <Separator className="w-full" />
- </>
- )
- case 'boolean':
- return (
- <>
- <SheetSection>
- <UIFormField
- control={control}
- name={name}
- disabled={disabled || readOnly}
- render={({ field }) => (
- <FormItemLayout
- layout="horizontal"
- label={properties.title}
- description={
- <div className="flex flex-col gap-1">
- {description ? <Markdown content={description} /> : null}
- {properties.link && (
- <span>
- <Button asChild type="default" size="tiny" icon={<ExternalLink />}>
- <a href={properties.link} target="_blank" rel="noreferrer noopener">
- Documentation
- </a>
- </Button>
- </span>
- )}
- </div>
- }
- >
- <FormControl className="col-span-6">
- <Switch
- id={name}
- checked={field.value}
- onCheckedChange={field.onChange}
- size="small"
- />
- </FormControl>
- </FormItemLayout>
- )}
- />
- </SheetSection>
- <Separator className="w-full" />
- </>
- )
- case 'select':
- return (
- <>
- <SheetSection>
- <UIFormField
- control={control}
- name={name}
- disabled={disabled || readOnly}
- render={({ field }) => (
- <FormItemLayout
- layout="horizontal"
- label={properties.title}
- description={
- description ? (
- <div className="form-field-markdown">
- <ReactMarkdown unwrapDisallowed disallowedElements={['p']}>
- {description}
- </ReactMarkdown>
- </div>
- ) : null
- }
- >
- <FormControl className="col-span-6">
- <Select
- defaultValue={properties.enum[0]?.value}
- value={field.value}
- onValueChange={field.onChange}
- >
- <SelectTrigger>
- <SelectValue placeholder="Select an option" />
- </SelectTrigger>
- <SelectContent>
- {properties.enum.map((option: Enum) => (
- <SelectItem key={option.value} value={option.value}>
- <span className="flex gap-2 items-center">
- {option.icon ? (
- <img
- alt={`${option.label} icon`}
- className="h-6 w-6"
- src={`${BASE_PATH}/img/icons/${option.icon}`}
- />
- ) : null}
- {option.label}
- </span>
- </SelectItem>
- ))}
- </SelectContent>
- </Select>
- </FormControl>
- </FormItemLayout>
- )}
- />
- </SheetSection>
- <Separator className="w-full" />
- </>
- )
- default:
- return null
- }
- }
- export default FormField
|