Files
supabase/apps/studio/components/interfaces/Advisors/CreateRuleSheet.tsx
Gildas Garcia 5d97339d41 chore: remove <Select> _Shadcn_ suffix (#45988)
## Problem

The `_Shadcn_` suffix isn't needed anymore on `Select` components

## Solution

Remove it. No other changes

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

## Summary by CodeRabbit

* **Refactor**
* Updated internal component architecture to standardize and simplify
the codebase. These changes improve code maintainability and consistency
across the application without affecting existing functionality or user
experience.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45988)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-15 16:39:57 +02:00

248 lines
8.4 KiB
TypeScript

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<z.infer<typeof FormSchema>>({
mode: 'onBlur',
reValidateMode: 'onChange',
resolver: zodResolver(FormSchema),
defaultValues,
})
const { lint_name, assigned_to, is_disabled } = form.watch()
const onSubmit: SubmitHandler<z.infer<typeof FormSchema>> = 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 (
<Sheet open={open} onOpenChange={onOpenChange}>
<SheetContent className="flex flex-col gap-0">
<SheetHeader className="shrink-0 flex items-center gap-4">
<SheetTitle>Create a rule for "{lint?.title}"</SheetTitle>
</SheetHeader>
<SheetSection className="overflow-auto grow px-0">
<Form {...form}>
<form
id={formId}
className="flex flex-col gap-y-4"
onSubmit={form.handleSubmit(onSubmit)}
>
<FormField
name="is_disabled"
control={form.control}
render={({ field }) => (
<FormItemLayout
layout="flex-row-reverse"
className="px-5"
label={`Disable this lint for ${assigned_to === 'all' ? 'project' : 'the assigned member'}`}
description="Toggles the visiblity of this lint in the Advisor reports"
>
<Tooltip>
<TooltipTrigger type="button">
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
disabled={field.disabled || assigned_to === 'all'}
/>
</FormControl>
</TooltipTrigger>
{assigned_to === 'all' && (
<TooltipContent side="bottom" className="w-72">
Assign this rule to a specific project member before toggling this option
off. This will then configure the rule to{' '}
<span className="text-brand">only be visible</span> to that member in the
advisor reports.
</TooltipContent>
)}
</Tooltip>
</FormItemLayout>
)}
/>
<Separator />
<FormField
name="assigned_to"
control={form.control}
render={({ field }) => (
<FormItemLayout label="Assign rule to" layout="vertical" className="px-5">
<Select
onValueChange={(val) => {
field.onChange(val)
if (val === 'all') form.setValue('is_disabled', true)
}}
defaultValue={field.value}
>
<SelectTrigger className="col-span-8">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All project members</SelectItem>
{members.map((m) => (
<SelectItem key={m.gotrue_id} value={m.gotrue_id}>
{m.username || m.primary_email}
</SelectItem>
))}
</SelectContent>
</Select>
</FormItemLayout>
)}
/>
{!!lint_name && (
<div className="px-5">
<Admonition showIcon={false} type="default">
{generateRuleDescription({
name: lint_name,
disabled: is_disabled,
member: members.find((x) => x.gotrue_id === assigned_to),
})}
</Admonition>
</div>
)}
<Separator />
<FormField
name="note"
control={form.control}
render={({ field }) => (
<FormItemLayout
layout="vertical"
className="px-5"
label="Description"
labelOptional="Optional"
>
<FormControl>
<TextArea
{...field}
rows={4}
className="text-sm"
placeholder="e.g Describe why this rule is being set"
/>
</FormControl>
</FormItemLayout>
)}
/>
</form>
</Form>
</SheetSection>
<SheetFooter>
<Button disabled={isCreating} type="default" onClick={() => onOpenChange(false)}>
Cancel
</Button>
<Button form={formId} htmlType="submit" loading={isCreating}>
Create rule
</Button>
</SheetFooter>
</SheetContent>
</Sheet>
)
}