=> {
return getGrafanaStorage()
.list(path)
.then((frame) => {
if (frame) {
const name = frame.fields[0];
frame.fields[0] = {
...name,
getLinks: (cfg: ValueLinkConfig) => {
const n = name.values[cfg.valueRowIndex ?? 0];
const p = path + '/' + n;
return [
{
title: `Open ${n}`,
href: `/admin/storage/${p}`,
target: '_self',
origin: name,
onClick: () => {
setPath(p);
},
},
];
},
};
}
return frame;
});
}, [path]);
const isFolder = useMemo(() => {
let isFolder = path?.indexOf('/') < 0;
if (listing.value) {
const length = listing.value.length;
if (length === 1) {
const first: string = listing.value.fields[0].values[0];
isFolder = !path.endsWith(first);
} else {
// TODO: handle files/folders which do not exist
isFolder = true;
}
}
return isFolder;
}, [path, listing]);
const fileNames = useMemo(() => {
return listing.value?.fields?.find((f) => f.name === 'name')?.values.filter((v) => typeof v === 'string') ?? [];
}, [listing]);
const renderView = () => {
const isRoot = !path?.length || path === '/';
switch (view) {
case StorageView.AddRoot:
if (!isRoot) {
setPath('');
return ;
}
return ;
}
const frame = listing.value;
if (!isDataFrame(frame)) {
return <>>;
}
if (isRoot) {
return ;
}
const opts = [{ what: StorageView.Data, text: 'Data' }];
// Root folders have a config page
if (path.indexOf('/') < 0) {
opts.push({ what: StorageView.Config, text: 'Configure' });
}
// Lets only apply permissions to folders (for now)
if (isFolder) {
// opts.push({ what: StorageView.Perms, text: 'Permissions' });
} else {
// TODO: only if the file exists in a storage engine with
opts.push({ what: StorageView.History, text: 'History' });
}
const canAddFolder = isFolder && (path.startsWith('resources') || path.startsWith('content'));
const canDelete = path.startsWith('resources/') || path.startsWith('content/');
const getErrorMessages = () => {
return (
{errorMessages.map((error) => {
return {error}
;
})}
);
};
const clearAlert = () => {
setErrorMessages([]);
};
return (
{canAddFolder && (
<>
>
)}
{canDelete && (
)}
{errorMessages.length > 0 && getErrorMessages()}
{opts.map((opt) => (
setPath(path, opt.what)}
/>
))}
{isFolder ? (
) : (
)}
{isAddingNewFolder && (
{
const folderPath = `${path}/${folderName}`;
const res = await getGrafanaStorage().createFolder(folderPath);
if (typeof res?.error !== 'string') {
setPath(folderPath);
setIsAddingNewFolder(false);
}
}}
onDismiss={() => {
setIsAddingNewFolder(false);
}}
validate={(folderName) => {
const lowerCase = folderName.toLowerCase();
if (filenameAlreadyExists(folderName, fileNames)) {
return 'A file or a folder with the same name already exists';
}
if (!folderNameRegex.test(lowerCase)) {
return 'Name contains illegal characters';
}
if (folderName.length > folderNameMaxLength) {
return `Name is too long, maximum length: ${folderNameMaxLength} characters`;
}
return true;
}}
/>
)}
);
};
return (
{renderView()}
);
}
const getStyles = (theme: GrafanaTheme2) => ({
// TODO: remove `height: 90%`
wrapper: css`
display: flex;
flex-direction: column;
height: 100%;
`,
tableControlRowWrapper: css`
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: ${theme.spacing(2)};
`,
// TODO: remove `height: 100%`
tableWrapper: css`
border: 1px solid ${theme.colors.border.medium};
height: 100%;
`,
border: css`
border: 1px solid ${theme.colors.border.medium};
padding: ${theme.spacing(2)};
`,
errorAlert: css`
padding-top: 20px;
`,
uploadButton: css`
margin-right: ${theme.spacing(2)};
`,
});