Get team folder and preselect it
This commit is contained in:
+3
@@ -249,6 +249,7 @@ const injectedRtkApi = api
|
||||
sort: queryArg.sort,
|
||||
limit: queryArg.limit,
|
||||
explain: queryArg.explain,
|
||||
owner: queryArg.owner,
|
||||
},
|
||||
}),
|
||||
providesTags: ['Search'],
|
||||
@@ -606,6 +607,8 @@ export type GetSearchApiArg = {
|
||||
type?: 'folder' | 'dashboard';
|
||||
/** search/list within a folder (not recursive) */
|
||||
folder?: string;
|
||||
/** filter by owner reference name or UID */
|
||||
owner?: string;
|
||||
/** count distinct terms for selected fields */
|
||||
facet?: string[];
|
||||
/** tag query filter */
|
||||
|
||||
@@ -20,6 +20,7 @@ import { FolderRepo } from './FolderRepo';
|
||||
import { getDOMId, NestedFolderList } from './NestedFolderList';
|
||||
import Trigger from './Trigger';
|
||||
import { useFoldersQuery } from './useFoldersQuery';
|
||||
import { useTeamOwnedFolder } from './useTeamOwnedFolder';
|
||||
import { useTreeInteractions } from './useTreeInteractions';
|
||||
import { getRootFolderItem } from './utils';
|
||||
|
||||
@@ -82,7 +83,10 @@ export function NestedFolderPicker({
|
||||
id,
|
||||
}: NestedFolderPickerProps) {
|
||||
const styles = useStyles2(getStyles);
|
||||
const selectedFolder = useGetFolderQueryFacade(value);
|
||||
const { folder: teamFolder } = useTeamOwnedFolder();
|
||||
const effectiveValue = value || teamFolder?.name;
|
||||
const selectedFolder = useGetFolderQueryFacade(effectiveValue);
|
||||
|
||||
// user might not have access to the folder, but they have access to the dashboard
|
||||
// in this case we disable the folder picker - this is an edge case when user has edit access to a dashboard
|
||||
// but doesn't have access to the folder
|
||||
@@ -112,6 +116,12 @@ export function NestedFolderPicker({
|
||||
rootFolderItem,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (value === undefined && teamFolder && onChange) {
|
||||
onChange(teamFolder.name, teamFolder.title);
|
||||
}
|
||||
}, [onChange, teamFolder, value]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!search) {
|
||||
setSearchResults(null);
|
||||
@@ -209,6 +219,23 @@ export function NestedFolderPicker({
|
||||
[search, fetchFolderPage]
|
||||
);
|
||||
|
||||
const teamFolderTreeItem = useMemo(() => {
|
||||
if (!teamFolder) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
isOpen: false,
|
||||
level: 0,
|
||||
item: {
|
||||
kind: 'folder' as const,
|
||||
title: teamFolder.title,
|
||||
uid: teamFolder.name,
|
||||
parentUID: teamFolder.folder,
|
||||
},
|
||||
};
|
||||
}, [teamFolder]);
|
||||
|
||||
const flatTree = useMemo(() => {
|
||||
let flatTree: Array<DashboardsTreeItem<DashboardViewItemWithUIItems>> = [];
|
||||
|
||||
@@ -229,6 +256,10 @@ export function NestedFolderPicker({
|
||||
})) ?? [];
|
||||
}
|
||||
|
||||
if (teamFolderTreeItem) {
|
||||
flatTree = [teamFolderTreeItem, ...flatTree];
|
||||
}
|
||||
|
||||
// It's not super optimal to filter these in an additional iteration, but
|
||||
// these options are used infrequently that its not a big deal
|
||||
if (!showRootFolder || excludeUIDs?.length) {
|
||||
@@ -246,7 +277,7 @@ export function NestedFolderPicker({
|
||||
}
|
||||
|
||||
return flatTree;
|
||||
}, [browseFlatTree, excludeUIDs, isBrowsing, searchResults?.items, showRootFolder]);
|
||||
}, [browseFlatTree, excludeUIDs, isBrowsing, searchResults?.items, showRootFolder, teamFolderTreeItem]);
|
||||
|
||||
const isItemLoaded = useCallback(
|
||||
(itemIndex: number) => {
|
||||
@@ -276,7 +307,7 @@ export function NestedFolderPicker({
|
||||
});
|
||||
|
||||
let label = selectedFolder.data?.title;
|
||||
if (value === '') {
|
||||
if (!label) {
|
||||
label = t('browse-dashboards.folder-picker.root-title', 'Dashboards');
|
||||
}
|
||||
|
||||
@@ -362,7 +393,7 @@ export function NestedFolderPicker({
|
||||
|
||||
<NestedFolderList
|
||||
items={flatTree}
|
||||
selectedFolder={value}
|
||||
selectedFolder={effectiveValue}
|
||||
focusedItemIndex={focusedItemIndex}
|
||||
onFolderExpand={handleFolderExpand}
|
||||
onFolderSelect={handleFolderSelect}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { skipToken } from '@reduxjs/toolkit/query';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { TeamDto } from '@grafana/api-clients/rtkq/legacy';
|
||||
import { useGetSearchQuery } from 'app/api/clients/dashboard/v0alpha1';
|
||||
import { api as profileApi } from 'app/features/profile/api';
|
||||
|
||||
/**
|
||||
* Returns the first folder owned by any team the current user belongs to.
|
||||
* Uses the dashboard v0alpha1 search API with the `owner` filter.
|
||||
*/
|
||||
export function useTeamOwnedFolder() {
|
||||
const [teams, setTeams] = useState<TeamDto[] | null>(null);
|
||||
const [teamError, setTeamError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
profileApi
|
||||
.loadTeams()
|
||||
.then((result) => {
|
||||
if (!cancelled) {
|
||||
setTeams(result);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
if (!cancelled) {
|
||||
setTeamError(err);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const owner = useMemo(() => {
|
||||
if (!teams || teams.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
const firstTeam = teams[0];
|
||||
// Prefer UID if available, otherwise fallback to name
|
||||
return firstTeam.uid ?? firstTeam.name;
|
||||
}, [teams]);
|
||||
|
||||
const searchArgs =
|
||||
owner !== undefined
|
||||
? {
|
||||
owner,
|
||||
type: 'folder' as const,
|
||||
// Now with the dummy backend this wouldn't work as we filter for owner after we get results
|
||||
// from the DB and this would be only applied to the DB call.
|
||||
// limit: 1,
|
||||
}
|
||||
: skipToken;
|
||||
|
||||
const { data, isFetching, error: searchError } = useGetSearchQuery(searchArgs);
|
||||
|
||||
const folder = data?.hits?.[0];
|
||||
|
||||
return {
|
||||
folder,
|
||||
isLoading: (teams === null && !teamError) || isFetching,
|
||||
error: teamError ?? searchError,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user