AdvisorPanel.utils.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import dayjs from 'dayjs'
  2. import { Gauge, Inbox, Shield } from 'lucide-react'
  3. import type { ElementType } from 'react'
  4. import type { AdvisorItem, AdvisorLintItem, AdvisorNotificationItem } from './AdvisorPanel.types'
  5. import { lintInfoMap } from '@/components/interfaces/Linter/Linter.utils'
  6. import type { Lint } from '@/data/lint/lint-query'
  7. import type { Notification, NotificationData } from '@/data/notifications/notifications-v2-query'
  8. import type { AdvisorSeverity, AdvisorTab } from '@/state/advisor-state'
  9. export const MAX_HOMEPAGE_ADVISOR_ITEMS = 4
  10. export const severityOrder: Record<AdvisorSeverity, number> = {
  11. critical: 0,
  12. warning: 1,
  13. info: 2,
  14. }
  15. export const lintLevelToSeverity = (level: Lint['level']): AdvisorSeverity => {
  16. switch (level) {
  17. case 'ERROR':
  18. return 'critical'
  19. case 'WARN':
  20. return 'warning'
  21. default:
  22. return 'info'
  23. }
  24. }
  25. export const notificationPriorityToSeverity = (
  26. priority: string | null | undefined
  27. ): AdvisorSeverity => {
  28. switch (priority) {
  29. case 'Critical':
  30. return 'critical'
  31. case 'Warning':
  32. return 'warning'
  33. default:
  34. return 'info'
  35. }
  36. }
  37. export const createAdvisorLintItems = (lintData?: Lint[]): AdvisorLintItem[] => {
  38. if (!lintData) return []
  39. return lintData
  40. .map((lint): AdvisorLintItem | null => {
  41. const categories = lint.categories || []
  42. const tab = categories.includes('SECURITY')
  43. ? ('security' as const)
  44. : categories.includes('PERFORMANCE')
  45. ? ('performance' as const)
  46. : undefined
  47. if (!tab) return null
  48. return {
  49. id: lint.cache_key,
  50. title: lint.detail,
  51. severity: lintLevelToSeverity(lint.level),
  52. createdAt: undefined,
  53. tab,
  54. source: 'lint',
  55. original: lint,
  56. }
  57. })
  58. .filter((item): item is AdvisorLintItem => item !== null)
  59. }
  60. export const createAdvisorNotificationItems = (
  61. notifications?: Notification[]
  62. ): AdvisorNotificationItem[] => {
  63. if (!notifications) return []
  64. return notifications.map((notification) => {
  65. const data = notification.data as NotificationData
  66. return {
  67. id: notification.id,
  68. title: data.title,
  69. severity: notificationPriorityToSeverity(notification.priority),
  70. createdAt: dayjs(notification.inserted_at).valueOf(),
  71. tab: 'messages' as const,
  72. source: 'notification' as const,
  73. original: notification,
  74. }
  75. })
  76. }
  77. export const sortAdvisorItems = <T extends AdvisorItem>(items: T[]) => {
  78. return [...items].sort((a, b) => {
  79. const severityDiff = severityOrder[a.severity] - severityOrder[b.severity]
  80. if (severityDiff !== 0) return severityDiff
  81. const createdDiff = (b.createdAt ?? 0) - (a.createdAt ?? 0)
  82. if (createdDiff !== 0) return createdDiff
  83. return getAdvisorItemDisplayTitle(a).localeCompare(getAdvisorItemDisplayTitle(b))
  84. })
  85. }
  86. export const formatItemDate = (timestamp: number): string => {
  87. const daysFromNow = dayjs().diff(dayjs(timestamp), 'day')
  88. const formattedTimeFromNow = dayjs(timestamp).fromNow()
  89. const formattedInsertedAt = dayjs(timestamp).format('MMM DD, YYYY')
  90. return daysFromNow > 1 ? formattedInsertedAt : formattedTimeFromNow
  91. }
  92. export const getAdvisorItemDisplayTitle = (item: AdvisorItem): string => {
  93. if (item.source === 'lint') {
  94. return (
  95. lintInfoMap.find((info) => info.name === item.original.name)?.title ||
  96. item.title.replace(/[`\\]/g, '')
  97. )
  98. }
  99. if (item.source === 'signal') {
  100. return `${item.title}`
  101. }
  102. return item.title.replace(/[`\\]/g, '')
  103. }
  104. export const getAdvisorPanelItemDisplayTitle = (item: AdvisorItem): string => {
  105. if (item.source === 'signal') {
  106. return item.title
  107. }
  108. return getAdvisorItemDisplayTitle(item)
  109. }
  110. export const getAdvisorItemSecondaryText = (item: AdvisorItem): string | undefined => {
  111. if (item.source === 'lint') {
  112. return getLintEntityString(item.original)
  113. }
  114. if (item.source === 'signal') {
  115. return `Database · ${item.sourceData.ip}`
  116. }
  117. return undefined
  118. }
  119. export const tabIconMap: Record<Exclude<AdvisorTab, 'all'>, ElementType> = {
  120. security: Shield,
  121. performance: Gauge,
  122. messages: Inbox,
  123. }
  124. export const severityColorClasses: Record<AdvisorSeverity, string> = {
  125. critical: 'text-destructive',
  126. warning: 'text-warning',
  127. info: 'text-foreground-light',
  128. }
  129. export const severityBadgeVariants: Record<AdvisorSeverity, 'destructive' | 'warning' | 'default'> =
  130. {
  131. critical: 'destructive',
  132. warning: 'warning',
  133. info: 'default',
  134. }
  135. export const severityLabels: Record<AdvisorSeverity, string> = {
  136. critical: 'Critical',
  137. warning: 'Warning',
  138. info: 'Info',
  139. }
  140. export const getLintEntityString = (lint: Lint | null): string | undefined => {
  141. if (!lint?.metadata) {
  142. return undefined
  143. }
  144. if (lint.metadata.entity) {
  145. return lint.metadata.entity
  146. }
  147. if (lint.metadata.schema && lint.metadata.name) {
  148. const extendedMetadata = lint.metadata as typeof lint.metadata & { arguments?: string }
  149. const args =
  150. typeof extendedMetadata.arguments === 'string' ? extendedMetadata.arguments : undefined
  151. return `${lint.metadata.schema}.${lint.metadata.name}${args !== undefined ? `(${args})` : ''}`
  152. }
  153. return undefined
  154. }