import { zodResolver } from '@hookform/resolvers/zod' import { useParams } from 'common' import { useRouter } from 'next/router' import { useEffect } from 'react' import { SubmitHandler, useForm } from 'react-hook-form' import { toast } from 'sonner' import { Button, Form, FormControl, FormField, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Separator, Sheet, SheetContent, SheetFooter, SheetHeader, SheetSection, SheetTitle, Switch, TextArea, Tooltip, TooltipContent, TooltipTrigger, } from 'ui' import { Admonition } from 'ui-patterns' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' import * as z from 'zod' import { LintInfo } from '../Linter/Linter.constants' import { lintInfoMap } from '../Linter/Linter.utils' import { generateRuleDescription } from './AdvisorRules.utils' import { useLintRuleCreateMutation } from '@/data/lint/create-lint-rule-mutation' import { useOrganizationMembersQuery } from '@/data/organizations/organization-members-query' import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization' interface CreateRuleSheetProps { lint?: LintInfo open: boolean onOpenChange: (value: boolean) => void } const FormSchema = z.object({ lint_name: z.string().optional(), note: z.string().optional(), assigned_to: z.string().optional(), is_disabled: z.boolean(), }) const defaultValues = { lint_name: undefined, note: undefined, assigned_to: 'all', is_disabled: true, } /** * [Joshen] JFYI while the API supports adding rules on a category, I'm intentionally leaving that functionality out for now * as the only use case for that would be to ignore _all_ lints in that category (which I'm not sure if that's what we want to advise users doing atm) * * (Spoken with Hieu) We'll eventually support granularity of Entity/Items as well, just not atm */ export const CreateRuleSheet = ({ lint, open, onOpenChange }: CreateRuleSheetProps) => { const router = useRouter() const { ref: projectRef } = useParams() const routeCategory = router.pathname.split('/').pop() const { data: organization } = useSelectedOrganizationQuery() const { data: members = [] } = useOrganizationMembersQuery({ slug: organization?.slug }) const { mutate: createRule, isPending: isCreating } = useLintRuleCreateMutation({ onSuccess: (_, vars) => { const ruleLint = vars.exception.lint_name const ruleLintMeta = lintInfoMap.find((x) => x.name === ruleLint) toast.success(`Successfully created new rule for ${ruleLintMeta?.title}`) if (ruleLintMeta) { if (!!routeCategory && routeCategory !== ruleLintMeta.category) { router.push( `/project/${projectRef}/advisors/rules/${ruleLintMeta.category}?lint=${ruleLintMeta.name}` ) } else { // setExpandedLint(ruleLintMeta?.name) } } onOpenChange(false) }, }) const formId = 'create-lint-rule-form' const form = useForm>({ mode: 'onBlur', reValidateMode: 'onChange', resolver: zodResolver(FormSchema as any), defaultValues, }) const { lint_name, assigned_to, is_disabled } = form.watch() const onSubmit: SubmitHandler> = async (values) => { if (!projectRef) return console.error('Project ref is required') createRule({ projectRef, exception: { ...values, lint_category: undefined, lint_name: values.lint_name, assigned_to: values.assigned_to === 'all' ? undefined : values.assigned_to, }, }) } useEffect(() => { if (open) form.reset({ ...defaultValues, lint_name: lint?.name }) }, [open]) return ( Create a rule for "{lint?.title}"
( {assigned_to === 'all' && ( Assign this rule to a specific project member before toggling this option off. This will then configure the rule to{' '} only be visible to that member in the advisor reports. )} )} /> ( )} /> {!!lint_name && (
{generateRuleDescription({ name: lint_name, disabled: is_disabled, member: members.find((x) => x.gotrue_id === assigned_to), })}
)} (