(props: DatabaseSelectProps)
| 41 | export type DatabaseSelectProps = BaseProps & (SingleProps | MultiProps); |
| 42 | |
| 43 | export function DatabaseSelect(props: DatabaseSelectProps) { |
| 44 | const { |
| 45 | placeholder, |
| 46 | disabled, |
| 47 | className, |
| 48 | portal, |
| 49 | projectName, |
| 50 | environmentName, |
| 51 | allowedEngineTypeList, |
| 52 | } = props; |
| 53 | const { t } = useTranslation(); |
| 54 | const workspaceResourceName = useAppStore((s) => s.workspaceResourceName()); |
| 55 | // The app store's global database cache — populated by every fetch below |
| 56 | // (upserted fresh) plus databases fetched elsewhere (e.g. Sync Schema's |
| 57 | // deep-link preload) and not evicted by this picker's own project-parent |
| 58 | // fetches. It resolves a selected value that is not on the current page, so |
| 59 | // chips stay labeled across searches and `onChange` payloads carry the real, |
| 60 | // fresh `Database`. Read non-reactively on purpose: `onChange` reads it live |
| 61 | // at click time (payload always current), and every flow that sets a selected |
| 62 | // value either populates the cache first (deep-link) or triggers a local |
| 63 | // re-render (search -> setDatabases), so a selected option never lingers as a |
| 64 | // synthesized label in practice. |
| 65 | const getDatabaseByName = useAppStore((s) => s.getDatabaseByName); |
| 66 | |
| 67 | // Current page / latest search results (drives the dropdown rows). |
| 68 | const [databases, setDatabases] = useState<Database[]>([]); |
| 69 | // Monotonic fetch id: only the latest in-flight fetch may apply its result, |
| 70 | // so an out-of-order (slow mount / stale search) response can't clobber newer |
| 71 | // results. Mirrors DatabaseResourceSelector / the plan-detail DatabaseSelector. |
| 72 | const fetchIdRef = useRef(0); |
| 73 | |
| 74 | // Stabilize engines array to avoid re-fetching on every render. |
| 75 | const enginesRef = useRef(allowedEngineTypeList); |
| 76 | const stableEngines = useMemo(() => { |
| 77 | const prev = enginesRef.current; |
| 78 | if ( |
| 79 | prev && |
| 80 | allowedEngineTypeList && |
| 81 | prev.length === allowedEngineTypeList.length && |
| 82 | prev.every((e, i) => e === allowedEngineTypeList[i]) |
| 83 | ) { |
| 84 | return prev; |
| 85 | } |
| 86 | enginesRef.current = allowedEngineTypeList; |
| 87 | return allowedEngineTypeList; |
| 88 | }, [allowedEngineTypeList]); |
| 89 | |
| 90 | const fetchDatabases = useCallback( |
| 91 | (query: string) => { |
| 92 | const fetchId = ++fetchIdRef.current; |
| 93 | useAppStore |
| 94 | .getState() |
| 95 | .fetchDatabases({ |
| 96 | parent: projectName ?? workspaceResourceName, |
| 97 | filter: { |
| 98 | environment: environmentName, |
| 99 | engines: stableEngines, |
| 100 | query, |
nothing calls this directly
no test coverage detected