Files
supabase/apps/studio/components/interfaces/Reports/ReportLintsTableRow.tsx
T
280928a8e6 Feat/lints (#22003)
* Add lints, 2

* Add basic table

* Handle ignoring lints

* Add loading state

* Cleanup

* move utils

* Cleanup

* Shift lint to react query

* Merge master solve import lint issue

* Update lints UI

* Optional params

* Add entity and schema name

* Rename lints to linter, format results

* Wrap in markdown

* Adjust layout of some components

* Add CTAs to each lint

* fix ignored problems section

* Update type

* Add cta buttons for auth rls initialization issues

* Change metadata object

* Remove unneeded file.

* Add a component for CTA for a lint row.

* Make the lint query to be more type-safe.

* Various fixes to the lint table.

* Show the remediation link.

* Fix the button icons.

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
2024-04-01 20:37:48 +00:00

171 lines
6.0 KiB
TypeScript

import { Eye, EyeIcon, EyeOff, HelpCircle, Table2 } from 'lucide-react'
import { useState } from 'react'
import { useParams } from 'common'
import Table from 'components/to-be-cleaned/Table'
import { LINT_TYPES, Lint } from 'data/lint/lint-query'
import { useLocalStorageQuery } from 'hooks'
import { LOCAL_STORAGE_KEYS } from 'lib/constants'
import {
Badge,
Button,
Modal,
TooltipContent_Shadcn_,
TooltipTrigger_Shadcn_,
Tooltip_Shadcn_,
} from 'ui'
import { Markdown } from '../Markdown'
import { LintCTA, getHumanReadableTitle } from './ReportLints.utils'
type ReportLintsTableRowProps = {
lint: Lint
}
const ReportLintsTableRow = ({ lint }: ReportLintsTableRowProps) => {
const { ref } = useParams()
const [selectedLint, setSelectedLint] = useState<Lint | null>(null)
const [lintIgnoreList, setLintIgnoreList] = useLocalStorageQuery<string[]>(
LOCAL_STORAGE_KEYS.PROJECT_LINT_IGNORE_LIST,
[]
)
const isIgnored = lintIgnoreList.includes(lint.cache_key)
// if the lint type can't be handled (there's no CTA text defined), don't render it
if (!LINT_TYPES.includes(lint.name)) {
return null
}
const toggleLintIgnore = () => {
let currentIgnoreList = []
const cacheKey = lint.cache_key
// Check if the cacheKey exists in the array and ignore or unignore it
const index = lintIgnoreList.indexOf(cacheKey)
if (index !== -1) {
currentIgnoreList = lintIgnoreList.filter((l) => l !== cacheKey)
} else {
currentIgnoreList = lintIgnoreList.concat(cacheKey)
}
setLintIgnoreList(currentIgnoreList)
setSelectedLint(null)
}
return (
<>
<Table.tr>
<Table.td className="w-20 align-top">
<Badge
className="!rounded w-16 font-mono text-center justify-center"
variant={
lint.level === 'ERROR' ? 'destructive' : lint.level === 'WARN' ? 'warning' : 'default'
}
>
{lint.level}
</Badge>
</Table.td>
<Table.td className="flex flex-col gap-y-2">
<div>
<div className="flex items-center gap-x-2">
<p className="text-foreground">{getHumanReadableTitle(lint.name)}</p>
<Tooltip_Shadcn_>
<TooltipTrigger_Shadcn_ asChild>
<HelpCircle size={14} />
</TooltipTrigger_Shadcn_>
<TooltipContent_Shadcn_ side="bottom" className="w-72">
{lint.description}
</TooltipContent_Shadcn_>
</Tooltip_Shadcn_>
</div>
{(lint.metadata?.type === 'table' || lint.metadata?.type === 'view') && (
<div className="flex items-center gap-x-4 text-xs py-1">
{lint.metadata?.schema && (
<div className="flex items-center gap-x-1">
<p className="text-foreground-lighter">schema</p>
<p className="text-foreground font-mono">{lint.metadata?.schema}</p>
</div>
)}
<div className="flex items-center gap-x-1">
{lint.metadata?.type === 'table' && (
<Table2 size={15} strokeWidth={1.5} className="text-foreground-lighter" />
)}
{lint.metadata?.type === 'view' && (
<Eye size={15} strokeWidth={1.5} className="text-foreground-lighter" />
)}
<p className="text-foreground font-mono">{lint.metadata?.name}</p>
</div>
</div>
)}
</div>
<div className="flex flex-col gap-y-1">
{lint.detail && (
<Markdown
className="text-foreground-light max-w-full leading-relaxed"
content={lint.detail}
/>
)}
{lint.remediation && (
<p className="text-foreground-light max-w-full leading-relaxed">
You can read more about this lint rule and the best ways to remedy it{' '}
<a
className="underline text-foreground-light transition-all hover:text-foreground hover:decoration-brand"
href={lint.remediation}
target="_blank"
rel="noreferrer"
>
here
</a>
</p>
)}
</div>
</Table.td>
<Table.td>
<div className="flex items-center justify-end gap-x-2">
<LintCTA title={lint.name} projectRef={ref!} metadata={lint.metadata} />
<Tooltip_Shadcn_>
<TooltipTrigger_Shadcn_ asChild>
<Button
type="text"
size="small"
className="px-1"
onClick={(event) => {
event.stopPropagation()
setSelectedLint(lint)
}}
icon={isIgnored ? <EyeIcon strokeWidth={1} /> : <EyeOff strokeWidth={1} />}
/>
</TooltipTrigger_Shadcn_>
<TooltipContent_Shadcn_ side="bottom">
{isIgnored ? 'Unignore problem' : 'Ignore problem'}
</TooltipContent_Shadcn_>
</Tooltip_Shadcn_>
</div>
</Table.td>
</Table.tr>
<Modal
size="small"
alignFooter="right"
visible={selectedLint !== null}
onCancel={() => setSelectedLint(null)}
onConfirm={() => toggleLintIgnore()}
header={<h3>Confirm to {isIgnored ? 'unignore' : 'ignore'} this lint</h3>}
>
<div className="py-4">
<Modal.Content>
<p className="text-sm">
{isIgnored
? 'Unignoring this lint will remove it from the Ignored Issues list. It will move it back to the main list above.'
: 'Ignoring this lint will remove it from the main list. It will still be visible in the Ignored Issues list below.'}
</p>
</Modal.Content>
</div>
</Modal>
</>
)
}
export default ReportLintsTableRow