mirror of
https://github.com/supabase/supabase.git
synced 2026-05-12 13:19:37 +08:00
This PR migrates the whole monorepo to use Tailwind v4: - Removed `@tailwindcss/container-queries` plugin since it's included by default in v4, - Bump all instances of Tailwind to v4. Made minimal changes to the shared config to remove non-supported features (`alpha` mentions), - Migrate all apps to be compatible with v4 configs, - Fix the `typography.css` import in 3 apps, - Add missing rules which were included by default in v3, - Run `pnpm dlx @tailwindcss/upgrade` on all apps, which renames a lot of classes - Rename all misnamed classes according to https://tailwindcss.com/docs/upgrade-guide#renamed-utilities in all apps. --------- Co-authored-by: Jordi Enric <jordi.err@gmail.com>
237 lines
9.0 KiB
TypeScript
237 lines
9.0 KiB
TypeScript
import { useParams } from 'common'
|
|
import { ChevronRight, Trash } from 'lucide-react'
|
|
import { useState } from 'react'
|
|
import { toast } from 'sonner'
|
|
import {
|
|
Badge,
|
|
Button,
|
|
Card,
|
|
CardContent,
|
|
cn,
|
|
Collapsible_Shadcn_,
|
|
CollapsibleContent_Shadcn_,
|
|
CollapsibleTrigger_Shadcn_,
|
|
} from 'ui'
|
|
import { GenericSkeletonLoader } from 'ui-patterns'
|
|
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
|
|
|
|
import { LintInfo } from '../Linter/Linter.constants'
|
|
import { generateRuleText } from './AdvisorRules.utils'
|
|
import { CreateRuleSheet } from './CreateRuleSheet'
|
|
import { DisableRuleModal } from './DisableRuleModal'
|
|
import { EnableRuleModal } from './EnableRuleModal'
|
|
import AlertError from '@/components/ui/AlertError'
|
|
import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
|
|
import { DocsButton } from '@/components/ui/DocsButton'
|
|
import { useLintRuleDeleteMutation } from '@/data/lint/delete-lint-rule-mutation'
|
|
import { useProjectLintRulesQuery } from '@/data/lint/lint-rules-query'
|
|
import { useOrganizationMembersQuery } from '@/data/organizations/organization-members-query'
|
|
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
|
|
|
|
interface AdvisorRuleItemProps {
|
|
lint: LintInfo
|
|
}
|
|
|
|
// [Joshen] Context: We're going with a simplified interface for LW15 as we launch
|
|
// this as a feature preview. Going to be purely disabling rules for the whole project,
|
|
// not assigning to members, etc (all these can come in the future when we're ready)
|
|
// Hence using this to clearly indicate where is being simplified
|
|
const SIMPLIFIED_INTERFACE = true
|
|
|
|
export const AdvisorRuleItem = ({ lint }: AdvisorRuleItemProps) => {
|
|
const { ref: projectRef } = useParams()
|
|
const { data: organization } = useSelectedOrganizationQuery()
|
|
|
|
const [open, setOpen] = useState(false)
|
|
const [expandedLint, setExpandedLint] = useState<string>()
|
|
const [selectedRuleToDelete, setSelectedRuleToDelete] = useState<string>()
|
|
|
|
const { data: members = [] } = useOrganizationMembersQuery({ slug: organization?.slug })
|
|
const {
|
|
data = { exceptions: [] },
|
|
error,
|
|
isPending: isLoading,
|
|
isSuccess,
|
|
isError,
|
|
} = useProjectLintRulesQuery({ projectRef })
|
|
|
|
const rules = data.exceptions.filter((x) => x.lint_name === lint.name)
|
|
const selectedRuleMeta = data.exceptions.find((x) => x.id === selectedRuleToDelete)
|
|
const selectedMemberForRule = members.find((x) => x.gotrue_id === selectedRuleMeta?.assigned_to)
|
|
|
|
const { mutate: deleteRule, isPending: isDeleting } = useLintRuleDeleteMutation({
|
|
onSuccess: () => {
|
|
toast.success('Successfully deleted rule')
|
|
setSelectedRuleToDelete(undefined)
|
|
},
|
|
})
|
|
|
|
const onDeleteRule = () => {
|
|
if (!projectRef) return console.error('Project ref is required')
|
|
if (!selectedRuleToDelete) return console.error('No rules selected')
|
|
deleteRule({ projectRef, ids: [selectedRuleToDelete] })
|
|
}
|
|
|
|
if (SIMPLIFIED_INTERFACE) {
|
|
return (
|
|
<Card className="border-b-0 rounded-none last:border-b first:rounded-t-md last:rounded-b-md">
|
|
<CardContent className="py-3 flex items-center justify-between text-sm gap-4 cursor-pointer transition hover:bg-surface-200">
|
|
<div className="flex items-center justify-center [&>svg]:text-foreground-lighter">
|
|
{lint.icon}
|
|
</div>
|
|
<div className="flex-1 flex items-center gap-x-2">
|
|
<span>{lint.title}</span>
|
|
{rules.length > 0 && <Badge>Disabled</Badge>}
|
|
</div>
|
|
<div className="flex items-center gap-x-2">
|
|
<DocsButton href={lint.docsLink} />
|
|
{rules.length > 0 ? (
|
|
<EnableRuleModal lint={lint} rule={rules[0]} />
|
|
) : (
|
|
<DisableRuleModal lint={lint} />
|
|
)}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<Collapsible_Shadcn_
|
|
id={lint.name}
|
|
open={expandedLint === lint.name}
|
|
onOpenChange={(open) => {
|
|
if (open) setExpandedLint(lint.name)
|
|
else setExpandedLint(undefined)
|
|
}}
|
|
>
|
|
<CollapsibleTrigger_Shadcn_ asChild className="[&[data-state=open]>div>svg]:rotate-90!">
|
|
<Card className="border-b-0 rounded-none">
|
|
<CardContent className="py-3 flex items-center justify-between text-sm gap-4 cursor-pointer transition hover:bg-surface-200">
|
|
<div className="flex items-center justify-center [&>svg]:text-foreground-lighter">
|
|
{lint.icon}
|
|
</div>
|
|
<div className="flex-1 flex items-center gap-x-2">
|
|
<span>{lint.title}</span>
|
|
{rules.length > 0 && (
|
|
<span className="font-mono text-xs w-5 h-5 rounded-full border border-alternative bg-surface-300 flex items-center justify-center">
|
|
{rules.length}
|
|
</span>
|
|
)}
|
|
</div>
|
|
<div className="flex items-center gap-x-2">
|
|
<DocsButton href={lint.docsLink} />
|
|
<Button
|
|
type="default"
|
|
onClick={(e) => {
|
|
e.stopPropagation()
|
|
setOpen(true)
|
|
}}
|
|
>
|
|
Create rule
|
|
</Button>
|
|
</div>
|
|
<ChevronRight strokeWidth={1.5} size={16} className="transition" />
|
|
</CardContent>
|
|
</Card>
|
|
</CollapsibleTrigger_Shadcn_>
|
|
<CollapsibleContent_Shadcn_
|
|
className={cn(
|
|
'bg-surface border-x border-t rounded-none!',
|
|
rules.length > 0 ? 'divide-y' : ''
|
|
)}
|
|
>
|
|
{isLoading && (
|
|
<div className="px-6 py-3">
|
|
<GenericSkeletonLoader />
|
|
</div>
|
|
)}
|
|
{isError && (
|
|
<AlertError
|
|
className="rounded-none border-0"
|
|
error={error}
|
|
subject="Failed to retrieve advisor rules"
|
|
/>
|
|
)}
|
|
{isSuccess && (
|
|
<>
|
|
{rules.length === 0 ? (
|
|
<div className="px-6 py-3">
|
|
<p className="text-sm text-foreground">
|
|
Lint is visible to all project members in the{' '}
|
|
<span className="capitalize">{lint.category}</span> Advisor.
|
|
</p>
|
|
<p className="text-sm text-foreground-lighter">
|
|
Create a rule to configure the visibility of this lint in your project
|
|
</p>
|
|
</div>
|
|
) : (
|
|
rules.map((rule) => {
|
|
const member = members.find((x) => x.gotrue_id === rule.assigned_to)
|
|
const ruleText = generateRuleText(rule, member)
|
|
return (
|
|
<div key={rule.id} className="px-6 py-3 flex items-center justify-between">
|
|
<div>
|
|
<p className="text-sm">{ruleText}</p>
|
|
{rule.note && (
|
|
<p className="text-sm text-foreground-lighter">{rule.note}</p>
|
|
)}
|
|
</div>
|
|
<div className="flex items-center gap-x-2">
|
|
{/* [Joshen] Will implement in part 2 */}
|
|
{/* <ButtonTooltip
|
|
type="default"
|
|
icon={<Edit />}
|
|
className="w-7"
|
|
tooltip={{ content: { side: 'bottom', text: 'Edit rule' } }}
|
|
/> */}
|
|
<ButtonTooltip
|
|
type="default"
|
|
icon={<Trash />}
|
|
className="w-7"
|
|
onClick={() => setSelectedRuleToDelete(rule.id)}
|
|
tooltip={{ content: { side: 'bottom', text: 'Delete rule' } }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)
|
|
})
|
|
)}
|
|
</>
|
|
)}
|
|
</CollapsibleContent_Shadcn_>
|
|
</Collapsible_Shadcn_>
|
|
|
|
<CreateRuleSheet lint={lint} open={open} onOpenChange={setOpen} />
|
|
|
|
<ConfirmationModal
|
|
size="medium"
|
|
loading={isDeleting}
|
|
visible={!!selectedRuleToDelete}
|
|
onCancel={() => setSelectedRuleToDelete(undefined)}
|
|
title="Confirm to delete selected rule"
|
|
onConfirm={() => onDeleteRule()}
|
|
alert={{
|
|
base: { variant: 'warning' },
|
|
title: 'The following rule will be removed',
|
|
description: !!selectedRuleMeta
|
|
? generateRuleText(selectedRuleMeta, selectedMemberForRule)
|
|
: '',
|
|
}}
|
|
>
|
|
{!!selectedRuleMeta && (
|
|
<>
|
|
<p className="text-sm text-foreground">
|
|
The selected lint will appear under the{' '}
|
|
<span className="capitalize">{lint.category}</span> Advisor again
|
|
{!!selectedRuleMeta.assigned_to ? ' for all project members' : ''} once this rule is
|
|
removed.
|
|
</p>
|
|
</>
|
|
)}
|
|
</ConfirmationModal>
|
|
</>
|
|
)
|
|
}
|