BannerRlsTester.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import { LOCAL_STORAGE_KEYS } from 'common'
  2. import { useParams } from 'common/hooks'
  3. import { AnimatePresence, motion } from 'framer-motion'
  4. import { Check, Loader2, Terminal } from 'lucide-react'
  5. import { useEffect, useState } from 'react'
  6. import { Badge, Button, cn } from 'ui'
  7. import { BannerCard } from '../BannerCard'
  8. import { useBannerStack } from '../BannerStackProvider'
  9. import { useFeaturePreviewModal } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
  10. import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
  11. const text = 'select * from colors'
  12. export const BannerRlsTester = () => {
  13. const { ref } = useParams()
  14. const { selectFeaturePreview } = useFeaturePreviewModal()
  15. const [runQueryAnimate, setRunQueryAnimate] = useState(false)
  16. const [showSummary, setShowSummary] = useState(false)
  17. const { dismissBanner } = useBannerStack()
  18. const [, setIsDismissed] = useLocalStorageQuery(
  19. LOCAL_STORAGE_KEYS.RLS_TESTER_BANNER_DISMISSED(ref ?? ''),
  20. false
  21. )
  22. useEffect(() => {
  23. setTimeout(() => setRunQueryAnimate(true), 2400)
  24. }, [])
  25. useEffect(() => {
  26. if (runQueryAnimate) {
  27. setTimeout(() => setShowSummary(true), 1700)
  28. }
  29. }, [runQueryAnimate])
  30. return (
  31. <BannerCard
  32. onDismiss={() => {
  33. setIsDismissed(true)
  34. dismissBanner('rls-tester-banner')
  35. }}
  36. >
  37. <div className="flex flex-col gap-y-4">
  38. <div className="flex flex-col gap-y-2 items-start">
  39. <Badge variant="success" className="-ml-0.5 uppercase inline-flex items-center mb-2">
  40. Preview
  41. </Badge>
  42. <div className={cn('transition-all bg-surface-100 w-full border rounded-md')}>
  43. <div className="flex items-center gap-x-2 p-2">
  44. {runQueryAnimate && !showSummary ? (
  45. <Loader2 size={12} className="animate-spin" />
  46. ) : (
  47. <Terminal size={12} />
  48. )}
  49. <p
  50. className="uppercase tracking-tight text-xs font-mono overflow-hidden whitespace-nowrap border-r-2 border-overlay"
  51. style={{
  52. width: `${text.length}ch`,
  53. animation: `typewriter 2s steps(${text.length}) forwards, blink-caret 0.75s step-end infinite`,
  54. }}
  55. >
  56. {text}
  57. </p>
  58. </div>
  59. <AnimatePresence>
  60. {showSummary && (
  61. <motion.div
  62. initial={{ height: 0 }}
  63. animate={{ height: '50px' }}
  64. exit={{ height: 0 }}
  65. transition={{
  66. type: 'spring',
  67. stiffness: 420,
  68. damping: 30,
  69. mass: 0.4,
  70. }}
  71. className="border-t text-xs p-2"
  72. >
  73. <div className="flex items-center gap-x-2">
  74. <Check size={12} strokeWidth={3} className="text-brand" />
  75. <p>
  76. Can access <code className="text-code-inline">public.colors</code>
  77. </p>
  78. </div>
  79. <div>
  80. <p className="text-foreground-light mt-0.5 ml-5">2 policies applied</p>
  81. </div>
  82. </motion.div>
  83. )}
  84. </AnimatePresence>
  85. </div>
  86. </div>
  87. <div className="flex flex-col gap-y-1 mb-2">
  88. <p className="text-sm font-medium">Row Level Security (RLS) Tester</p>
  89. <p className="text-xs text-foreground-lighter text-balance">
  90. Verify your RLS policies are correct by running queries as a specific user
  91. </p>
  92. </div>
  93. <Button
  94. type="default"
  95. className="w-min"
  96. onClick={() => selectFeaturePreview(LOCAL_STORAGE_KEYS.UI_PREVIEW_RLS_TESTER)}
  97. >
  98. Enable feature preview
  99. </Button>
  100. </div>
  101. </BannerCard>
  102. )
  103. }