import { cx } from '@emotion/css'; import React, { useCallback, useEffect, useState } from 'react'; import { useEffectOnce } from 'react-use'; import { Alert, Button, LoadingPlaceholder, useStyles2 } from '@grafana/ui'; import ResourcePickerData, { ResourcePickerQueryType } from '../../resourcePicker/resourcePickerData'; import { AzureMetricResource } from '../../types'; import messageFromError from '../../utils/messageFromError'; import { Space } from '../Space'; import Advanced from './Advanced'; import NestedRow from './NestedRow'; import Search from './Search'; import getStyles from './styles'; import { ResourceRow, ResourceRowGroup, ResourceRowType } from './types'; import { findRow, parseResourceDetails, resourceToString } from './utils'; interface ResourcePickerProps { resourcePickerData: ResourcePickerData; resource: T; selectableEntryTypes: ResourceRowType[]; queryType: ResourcePickerQueryType; onApply: (resource?: T) => void; onCancel: () => void; } const ResourcePicker = ({ resourcePickerData, resource, onApply, onCancel, selectableEntryTypes, queryType, }: ResourcePickerProps) => { const styles = useStyles2(getStyles); const [isLoading, setIsLoading] = useState(false); const [rows, setRows] = useState([]); const [selectedRows, setSelectedRows] = useState([]); const [internalSelected, setInternalSelected] = useState(resource); const [errorMessage, setErrorMessage] = useState(undefined); const [shouldShowLimitFlag, setShouldShowLimitFlag] = useState(false); // Sync the resourceURI prop to internal state useEffect(() => { setInternalSelected(resource); }, [resource]); const loadInitialData = useCallback(async () => { if (!isLoading) { try { setIsLoading(true); const resources = await resourcePickerData.fetchInitialRows( queryType, parseResourceDetails(internalSelected ?? {}) ); setRows(resources); } catch (error) { setErrorMessage(messageFromError(error)); } setIsLoading(false); } }, [internalSelected, isLoading, resourcePickerData, queryType]); useEffectOnce(() => { loadInitialData(); }); // set selected row data whenever row or selection changes useEffect(() => { if (!internalSelected) { setSelectedRows([]); } const found = internalSelected && findRow(rows, resourceToString(internalSelected)); if (found) { return setSelectedRows([ { ...found, children: undefined, }, ]); } return setSelectedRows([]); }, [internalSelected, rows]); // Request resources for an expanded resource group const requestNestedRows = useCallback( async (parentRow: ResourceRow) => { // clear error message (also when loading cached resources) setErrorMessage(undefined); // If we already have children, we don't need to re-fetch them. if (parentRow.children?.length) { return; } try { const nestedRows = await resourcePickerData.fetchAndAppendNestedRow(rows, parentRow, queryType); setRows(nestedRows); } catch (error) { setErrorMessage(messageFromError(error)); throw error; } }, [resourcePickerData, rows, queryType] ); const resourceIsString = typeof resource === 'string'; const handleSelectionChanged = useCallback( (row: ResourceRow, isSelected: boolean) => { isSelected ? setInternalSelected(resourceIsString ? row.uri : parseResourceDetails(row.uri)) : setInternalSelected(resourceIsString ? '' : {}); }, [resourceIsString] ); const handleApply = useCallback(() => { if (internalSelected) { onApply(resourceIsString ? internalSelected : parseResourceDetails(internalSelected)); } }, [resourceIsString, internalSelected, onApply]); const handleSearch = useCallback( async (searchWord: string) => { // clear errors and warnings setErrorMessage(undefined); setShouldShowLimitFlag(false); if (!searchWord) { loadInitialData(); return; } try { setIsLoading(true); const searchResults = await resourcePickerData.search(searchWord, queryType); setRows(searchResults); if (searchResults.length >= resourcePickerData.resultLimit) { setShouldShowLimitFlag(true); } } catch (err) { setErrorMessage(messageFromError(err)); } setIsLoading(false); }, [loadInitialData, resourcePickerData, queryType] ); return (
{shouldShowLimitFlag ? (

Showing first {resourcePickerData.resultLimit} results

) : ( )}
Scope Type Location
{isLoading && ( )} {!isLoading && rows.length === 0 && ( )} {!isLoading && rows.map((row) => ( ))}
No resources found
{selectedRows.length > 0 && ( <>
Selection
{selectedRows.map((row) => ( ))}
)} setInternalSelected(r)} />
{errorMessage && ( <> {errorMessage} )}
); }; export default ResourcePicker;