Skip to main content
Glama

mcp-google-sheets

new-field-popup.tsx8.01 kB
import { t } from 'i18next'; import { nanoid } from 'nanoid'; import { useState } from 'react'; import { FieldErrors, useForm } from 'react-hook-form'; import { ArrayInput } from '@/components/custom/array-input'; import { Button } from '@/components/ui/button'; import { Form, FormField, FormItem, FormMessage } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Popover, PopoverContent, PopoverTrigger, } from '@/components/ui/popover'; import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; import { ScrollArea } from '@/components/ui/scroll-area'; import { useTableState } from '@/features/tables/components/ap-table-state-provider'; import { tablesUtils } from '@/features/tables/lib/utils'; import { cn } from '@/lib/utils'; import { FieldType, isNil } from '@activepieces/shared'; type NewFieldDialogProps = { children: React.ReactNode; }; type NewFieldFormData = | { name: string; type: FieldType.STATIC_DROPDOWN; data: { options: string[]; }; } | { name: string; type: FieldType.DATE | FieldType.NUMBER | FieldType.TEXT; data: null; }; const FIELD_TYPE_FRIENDLY_NAME: Record<FieldType, string> = { [FieldType.TEXT]: 'Text', [FieldType.NUMBER]: 'Number', [FieldType.DATE]: 'Date', [FieldType.STATIC_DROPDOWN]: 'Dropdown', }; export function NewFieldPopup({ children }: NewFieldDialogProps) { const [open, setOpen] = useState(false); const fields = useTableState((state) => state.fields); const createField = useTableState((state) => state.createField); const form = useForm<NewFieldFormData>({ resolver: (data) => { const errors: FieldErrors<NewFieldFormData> = {}; if (data.name.length === 0) { errors['name'] = { message: t('Name is required'), type: 'required', }; } else { if (fields?.find((field) => field.name === data.name)) { errors['name'] = { message: t('Name must be unique'), type: 'unique', }; } } if (isNil(data.type)) { errors['type'] = { message: t('Type is required'), type: 'required', }; } if ( data.type === FieldType.STATIC_DROPDOWN && (isNil(data.data) || data.data?.options.length === 0 || !data.data?.options.some((option) => option.length > 0)) ) { errors['data'] = { options: { message: t('Please add at least one option'), type: 'required', }, }; } return { values: Object.keys(errors).length === 0 ? data : {}, errors, }; }, defaultValues: { type: FieldType.TEXT, data: null, name: '', }, }); return ( <Popover open={open} modal={false} onOpenChange={setOpen}> <PopoverTrigger asChild>{children}</PopoverTrigger> <PopoverContent className="w-[400px] py-4 px-2 drop-shadow-xl"> <div className="text-lg font-semibold mb-4 px-3">{t('New Field')}</div> <Form {...form}> <form onSubmit={form.handleSubmit(async (data) => { form.reset(); setOpen(false); if (data.type === FieldType.STATIC_DROPDOWN) { createField({ uuid: nanoid(), name: data.name, type: data.type, data: { options: data.data.options .filter((option) => option.length > 0) .map((option) => ({ value: option, })), }, }); } else { createField({ uuid: nanoid(), name: data.name, type: data.type, }); } })} className="mx-2" > <div className="max-h-[80vh] overflow-y-auto space-y-4 px-1 "> <FormField control={form.control} name="name" render={({ field }) => ( <FormItem className="grid space-y-3"> <Label htmlFor="name">{t('Name')}</Label> <Input thin={true} {...field} id="name" /> <FormMessage /> </FormItem> )} /> <FormField control={form.control} name="type" render={({ field }) => ( <FormItem className="grid space-y-2"> <Label>{t('Type')}</Label> <ScrollArea className="max-h-[200px] rounded-md border"> <RadioGroup value={field.value} onValueChange={(value) => { if (value === FieldType.STATIC_DROPDOWN) { form.setValue('data', { options: [''], }); } else { form.setValue('data', null); } field.onChange(value); }} className="p-1" > {Object.values(FieldType).map((type) => ( <div key={type} className="flex items-center"> <RadioGroupItem value={type} id={type} className="sr-only" /> <Label htmlFor={type} className={cn( 'flex items-center gap-2 w-full px-3 py-2 rounded-sm', 'text-left text-accent-foreground cursor-pointer hover:bg-muted', field.value === type && 'bg-muted text-primary', )} > {tablesUtils.getColumnIcon(type)} {FIELD_TYPE_FRIENDLY_NAME[type] ? t(FIELD_TYPE_FRIENDLY_NAME[type]) : t(type)} </Label> </div> ))} </RadioGroup> </ScrollArea> <FormMessage /> </FormItem> )} /> {form.watch('type') === FieldType.STATIC_DROPDOWN && ( <FormField control={form.control} name="data.options" render={({ field }) => ( //needs to be wrapped in form field to show the error message <FormItem className="grid space-y-3"> <Label>{t('Options')}</Label> <ArrayInput inputName="data.options" disabled={false} required={true} thinInputs={true} /> <FormMessage /> </FormItem> )} /> )} </div> <div className="flex justify-end gap-2 pt-2 mt-3"> <Button type="button" size="sm" variant="ghost" onClick={() => setOpen(false)} > {t('Cancel')} </Button> <Button type="submit" size="sm"> {t('Create')} </Button> </div> </form> </Form> </PopoverContent> </Popover> ); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/activepieces/activepieces'

If you have feedback or need assistance with the MCP directory API, please join our Discord server