grafana/public/app/features/storage/RootView.tsx
Ryan McKinley 4064fa51c6
Export: move export page to a full page (not view on storage) (#60263)
Co-authored-by: nmarrs <nathanielmarrs@gmail.com>
2022-12-13 19:40:20 -08:00

150 lines
3.8 KiB
TypeScript

import { css } from '@emotion/css';
import React, { useMemo, useState } from 'react';
import { useAsync } from 'react-use';
import { DataFrame, GrafanaTheme2 } from '@grafana/data';
import {
Alert,
Button,
Card,
FilterInput,
HorizontalGroup,
Icon,
IconName,
TagList,
useStyles2,
VerticalGroup,
} from '@grafana/ui';
import { getGrafanaStorage } from './storage';
import { StorageInfo, StorageView } from './types';
interface Props {
root: DataFrame;
onPathChange: (p: string, v?: StorageView) => void;
}
export function RootView({ root, onPathChange }: Props) {
const styles = useStyles2(getStyles);
const storage = useAsync(getGrafanaStorage().getConfig);
const [searchQuery, setSearchQuery] = useState<string>('');
let base = location.pathname;
if (!base.endsWith('/')) {
base += '/';
}
const roots = useMemo(() => {
let show = storage.value ?? [];
if (searchQuery?.length) {
const lower = searchQuery.toLowerCase();
show = show.filter((r) => {
const v = r.config;
const isMatch = v.name.toLowerCase().indexOf(lower) >= 0 || v.description.toLowerCase().indexOf(lower) >= 0;
if (isMatch) {
return true;
}
return false;
});
}
const base: StorageInfo[] = [];
const content: StorageInfo[] = [];
for (const r of show ?? []) {
if (r.config.underContentRoot) {
content.push(r);
} else if (r.config.prefix !== 'content') {
base.push(r);
}
}
return { base, content };
}, [searchQuery, storage]);
const renderRoots = (pfix: string, roots: StorageInfo[]) => {
return (
<VerticalGroup>
{roots.map((s) => {
const ok = s.ready;
return (
<Card key={s.config.prefix} href={ok ? `admin/storage/${pfix}${s.config.prefix}/` : undefined}>
<Card.Heading>{s.config.name}</Card.Heading>
<Card.Meta className={styles.clickable}>
{s.config.description}
{s.config.git?.remote && <a href={s.config.git?.remote}>{s.config.git?.remote}</a>}
</Card.Meta>
{s.notice?.map((notice) => (
<Alert key={notice.text} severity={notice.severity} title={notice.text} />
))}
<Card.Tags className={styles.clickable}>
<HorizontalGroup>
<TagList tags={getTags(s)} />
</HorizontalGroup>
</Card.Tags>
<Card.Figure className={styles.clickable}>
<Icon name={getIconName(s.config.type)} size="xxxl" className={styles.secondaryTextColor} />
</Card.Figure>
</Card>
);
})}
</VerticalGroup>
);
};
return (
<div>
<div className="page-action-bar">
<div className="gf-form gf-form--grow">
<FilterInput placeholder="Search Storage" value={searchQuery} onChange={setSearchQuery} />
</div>
<Button className="pull-right" onClick={() => onPathChange('', StorageView.AddRoot)}>
Add Root
</Button>
</div>
<div>{renderRoots('', roots.base)}</div>
<div>
<h3>Content</h3>
{renderRoots('content/', roots.content)}
</div>
</div>
);
}
function getStyles(theme: GrafanaTheme2) {
return {
secondaryTextColor: css`
color: ${theme.colors.text.secondary};
`,
clickable: css`
pointer-events: none;
`,
};
}
function getTags(v: StorageInfo) {
const tags: string[] = [];
if (v.builtin) {
tags.push('Builtin');
}
// Error
if (!v.ready) {
tags.push('Not ready');
}
return tags;
}
export function getIconName(type: string): IconName {
switch (type) {
case 'git':
return 'code-branch';
case 'disk':
return 'folder-open';
case 'sql':
return 'database';
default:
return 'folder-open';
}
}