DataApiProjectUrlCard.tsx106 lines · main
1import { useParams } from 'common'
2import { AlertCircle } from 'lucide-react'
3import { parseAsString, useQueryState } from 'nuqs'
4import { useEffect } from 'react'
5import { Alert, AlertTitle } from 'ui'
6import {
7 PageSection,
8 PageSectionAside,
9 PageSectionContent,
10 PageSectionDescription,
11 PageSectionMeta,
12 PageSectionSummary,
13 PageSectionTitle,
14} from 'ui-patterns'
15import { Input } from 'ui-patterns/DataInputs/Input'
16import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
17
18import { getApiEndpoint } from '@/components/interfaces/Integrations/DataApi/DataApi.utils'
19import { DatabaseSelector } from '@/components/ui/DatabaseSelector'
20import { useProjectApiUrl } from '@/data/config/project-endpoint-query'
21import { useLoadBalancersQuery } from '@/data/read-replicas/load-balancers-query'
22import { useReadReplicasQuery } from '@/data/read-replicas/replicas-query'
23import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
24import { useStaticEffectEvent } from '@/hooks/useStaticEffectEvent'
25import { useDatabaseSelectorStateSnapshot } from '@/state/database-selector'
26
27export const DataApiProjectUrlCard = () => {
28 const { isPending: isLoading } = useSelectedProjectQuery()
29 const { ref: projectRef } = useParams()
30 const state = useDatabaseSelectorStateSnapshot()
31
32 const [querySource, setQuerySource] = useQueryState('source', parseAsString)
33
34 const { data: resolvedEndpoint } = useProjectApiUrl({ projectRef })
35 const {
36 data: databases,
37 isError,
38 isPending: isLoadingDatabases,
39 } = useReadReplicasQuery({ projectRef })
40 const { data: loadBalancers } = useLoadBalancersQuery({ projectRef })
41
42 const syncSelectedDb = useStaticEffectEvent(() => {
43 if (querySource && querySource !== state.selectedDatabaseId) {
44 state.setSelectedDatabaseId(querySource)
45 }
46 })
47 useEffect(() => {
48 syncSelectedDb()
49 }, [syncSelectedDb, querySource, projectRef])
50
51 const selectedDatabase = databases?.find((db) => db.identifier === state.selectedDatabaseId)
52 const loadBalancerSelected = state.selectedDatabaseId === 'load-balancer'
53 const replicaSelected = selectedDatabase?.identifier !== projectRef
54
55 const endpoint = getApiEndpoint({
56 selectedDatabaseId: state.selectedDatabaseId,
57 projectRef,
58 resolvedEndpoint,
59 loadBalancers,
60 selectedDatabase,
61 })
62
63 return (
64 <PageSection className="first:pt-0">
65 <PageSectionMeta>
66 <PageSectionSummary>
67 <PageSectionTitle>API URL</PageSectionTitle>
68 <PageSectionDescription>
69 {loadBalancerSelected
70 ? 'RESTful endpoint for querying and managing your databases through your load balancer'
71 : replicaSelected
72 ? 'RESTful endpoint for querying your read replica'
73 : 'RESTful endpoint for querying and managing your database'}
74 </PageSectionDescription>
75 </PageSectionSummary>
76 <PageSectionAside>
77 <DatabaseSelector
78 additionalOptions={
79 (loadBalancers ?? []).length > 0
80 ? [{ id: 'load-balancer', name: 'API Load Balancer' }]
81 : []
82 }
83 onSelectId={() => {
84 setQuerySource(null)
85 }}
86 />
87 </PageSectionAside>
88 </PageSectionMeta>
89 <PageSectionContent>
90 {isLoading || isLoadingDatabases ? (
91 <div className="space-y-2">
92 <ShimmeringLoader />
93 <ShimmeringLoader className="w-3/4" delayIndex={1} />
94 </div>
95 ) : isError ? (
96 <Alert variant="destructive">
97 <AlertCircle size={16} />
98 <AlertTitle>Failed to retrieve project URL</AlertTitle>
99 </Alert>
100 ) : (
101 <Input copy readOnly className="font-mono" value={endpoint} />
102 )}
103 </PageSectionContent>
104 </PageSection>
105 )
106}