mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Feature ## Summary - Replace advisor panel tabs with multi-select category filters, including Health - Load health lints in the advisor panel (without blocking other categories on the slower health request) - Rename item `tab` to `category` and add empty-state copy for health Stacked on #49661. ## To test 1. Open any project in Studio. 2. Open Advisor Center from the toolbar (the advisor / lightbulb control). 3. Confirm the old All / Security / Performance / Messages **tabs are gone**. You should see **Category**, **Status**, and **Severity** filters instead. 4. Open Category and confirm **Health** is in the list with Security, Performance, and Messages. 5. Select only **Health**: - If the project is healthy: empty state “No health issues detected” / “Your database, instance and services are all responding normally”. - If it is not: only health issues in the list. 6. Clear Health, then filter **Security** and **Performance** separately. Those lists should still match what you expect from before. 7. With Health selected, also filter Severity to **Info** only. If nothing matches, you should get “No items found” and a way to clear filters — not a false “no health issues” message. 8. From project home, click an advisor card. Advisor Center should still open on that same item. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added category-based filtering for Advisor recommendations, including Security, Performance, Health, and Messages. - Added Health issue recommendations and category-specific icons, labels, and empty-state messaging. - Advisor results now load according to the selected categories. - Added clearer project requirements and hidden-item controls for filtered results. - **Bug Fixes** - Invalid category and severity filter values are safely ignored. - Improved categorization and telemetry for Advisor items, including health and security recommendations. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
83 lines
3.1 KiB
TypeScript
83 lines
3.1 KiB
TypeScript
import { proxy, snapshot, useSnapshot } from 'valtio'
|
|
import { z } from 'zod'
|
|
|
|
export const advisorCategorySchema = z.enum(['security', 'performance', 'health', 'messages'])
|
|
export const advisorSeveritySchema = z.enum(['critical', 'warning', 'info'])
|
|
|
|
export type AdvisorCategory = z.infer<typeof advisorCategorySchema>
|
|
export type AdvisorSeverity = z.infer<typeof advisorSeveritySchema>
|
|
export type AdvisorItemSource = 'lint' | 'notification' | 'signal'
|
|
|
|
const createInitialState = () => ({
|
|
// An empty selection means every category is shown, matching the severity filter
|
|
categoryFilters: [] as AdvisorCategory[],
|
|
severityFilters: ['critical', 'warning'] as AdvisorSeverity[],
|
|
selectedItemId: undefined as string | undefined,
|
|
selectedItemSource: undefined as AdvisorItemSource | undefined,
|
|
// Notification filters
|
|
notificationFilterStatuses: [] as string[],
|
|
notificationFilterPriorities: [] as string[],
|
|
})
|
|
|
|
export const advisorState = proxy({
|
|
...createInitialState(),
|
|
setCategoryFilters(categories: AdvisorCategory[]) {
|
|
advisorState.categoryFilters = categories
|
|
},
|
|
setSeverityFilters(severities: AdvisorSeverity[]) {
|
|
advisorState.severityFilters = severities
|
|
},
|
|
setSelectedItem(id: string | undefined, source?: AdvisorItemSource) {
|
|
advisorState.selectedItemId = id
|
|
advisorState.selectedItemSource = source
|
|
},
|
|
focusItem({ id, source }: { id: string; source?: AdvisorItemSource }) {
|
|
advisorState.selectedItemId = id
|
|
advisorState.selectedItemSource = source
|
|
},
|
|
setNotificationFilters: (value: string, type: 'status' | 'priority') => {
|
|
switch (type) {
|
|
case 'status':
|
|
if (advisorState.notificationFilterStatuses.includes(value)) {
|
|
advisorState.notificationFilterStatuses = advisorState.notificationFilterStatuses.filter(
|
|
(x) => x !== value
|
|
)
|
|
} else {
|
|
advisorState.notificationFilterStatuses = advisorState.notificationFilterStatuses.concat([
|
|
value,
|
|
])
|
|
}
|
|
break
|
|
case 'priority':
|
|
if (advisorState.notificationFilterPriorities.includes(value)) {
|
|
advisorState.notificationFilterPriorities =
|
|
advisorState.notificationFilterPriorities.filter((x) => x !== value)
|
|
} else {
|
|
advisorState.notificationFilterPriorities =
|
|
advisorState.notificationFilterPriorities.concat([value])
|
|
}
|
|
break
|
|
}
|
|
},
|
|
/** Clears the filters that hide items within the selected categories, but not the categories themselves. */
|
|
clearNarrowingFilters() {
|
|
advisorState.severityFilters = []
|
|
advisorState.notificationFilterStatuses = []
|
|
advisorState.notificationFilterPriorities = []
|
|
},
|
|
clearFilters() {
|
|
advisorState.categoryFilters = []
|
|
advisorState.severityFilters = []
|
|
advisorState.notificationFilterStatuses = []
|
|
advisorState.notificationFilterPriorities = []
|
|
},
|
|
reset() {
|
|
Object.assign(advisorState, createInitialState())
|
|
},
|
|
})
|
|
|
|
export const getAdvisorStateSnapshot = () => snapshot(advisorState)
|
|
|
|
export const useAdvisorStateSnapshot = (options?: Parameters<typeof useSnapshot>[1]) =>
|
|
useSnapshot(advisorState, options)
|