RecentItems.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import { useParams } from 'common'
  2. import dayjs from 'dayjs'
  3. import { AnimatePresence, motion } from 'framer-motion'
  4. import Link from 'next/link'
  5. import { useEditorType } from '../editors/EditorsLayout.hooks'
  6. import { buildTableEditorUrl } from '@/components/grid/BrivenGrid.utils'
  7. import { EntityTypeIcon } from '@/components/ui/EntityTypeIcon'
  8. import { ENTITY_TYPE } from '@/data/entity-types/entity-type-constants'
  9. import { editorEntityTypes, useTabsStateSnapshot } from '@/state/tabs'
  10. export function RecentItems() {
  11. const { ref } = useParams()
  12. const tabs = useTabsStateSnapshot()
  13. const editor = useEditorType()
  14. if (!tabs?.recentItems || !ref) return null
  15. // Filter items based on editor type
  16. const filteredItems = !editor
  17. ? [...tabs.recentItems]
  18. : tabs.recentItems.filter((item) => editorEntityTypes[editor].includes(item.type))
  19. const sortedItems = filteredItems.sort((a, b) => b.timestamp - a.timestamp)
  20. return (
  21. <div className="space-y-4">
  22. <h2 className="text-sm text-foreground">Recent items</h2>
  23. <div className="flex flex-col gap-0">
  24. {sortedItems.length === 0 ? (
  25. <motion.div
  26. layout
  27. initial={false}
  28. className="flex flex-col items-center justify-center p-8 text-center border rounded-md border-muted gap-3"
  29. >
  30. <EntityTypeIcon type={'r' as ENTITY_TYPE} />
  31. <div>
  32. <p className="text-xs text-foreground-light">No recent items yet</p>
  33. <p className="text-xs text-foreground-lighter">
  34. Items will appear here as you browse through your project
  35. </p>
  36. </div>
  37. </motion.div>
  38. ) : (
  39. <AnimatePresence>
  40. <div className="grid grid-cols-1 gap-12 gap-y-0">
  41. {sortedItems.map((item, index: number) => (
  42. <motion.div
  43. key={item.id}
  44. initial={{ opacity: 0 }}
  45. animate={{ opacity: 1 }}
  46. exit={{ opacity: 0 }}
  47. transition={{ delay: index * 0.012, duration: 0.15 }}
  48. >
  49. <Link
  50. href={
  51. item.type === 'sql'
  52. ? `/project/${ref}/sql/${item.metadata?.sqlId}`
  53. : item.type === 'r' ||
  54. item.type === 'v' ||
  55. item.type === 'm' ||
  56. item.type === 'f' ||
  57. item.type === 'p'
  58. ? buildTableEditorUrl({
  59. projectRef: ref,
  60. tableId: item.metadata?.tableId!,
  61. schema: item.metadata?.schema,
  62. })
  63. : `/project/${ref}/explorer/${item.type}/${item.metadata?.schema}/${item.metadata?.name}`
  64. }
  65. className="flex items-center gap-4 rounded-lg bg-surface-100 py-2 transition-colors hover:bg-surface-200"
  66. >
  67. <div className="flex h-6 w-6 items-center justify-center rounded-sm bg-surface-100 border">
  68. <EntityTypeIcon type={item.type} />
  69. </div>
  70. <div className="flex flex-1 gap-5 items-center">
  71. <span className="text-sm text-foreground">
  72. <span className="text-foreground-lighter">{item.metadata?.schema}</span>
  73. {item.metadata?.schema && <span className="text-foreground-light">.</span>}
  74. <span className="text-foreground">{item.label || 'Untitled'}</span>
  75. </span>
  76. <div className="bg-border-muted flex grow h-px"></div>
  77. <span className="text-xs text-foreground-lighter">
  78. {dayjs(item.timestamp).fromNow()}
  79. </span>
  80. </div>
  81. </Link>
  82. </motion.div>
  83. ))}
  84. </div>
  85. </AnimatePresence>
  86. )}
  87. </div>
  88. </div>
  89. )
  90. }