mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-10-30 14:49:14 +00:00
214 lines
6.2 KiB
TypeScript
214 lines
6.2 KiB
TypeScript
import {Fragment} from 'react';
|
|
import {browserHistory} from 'react-router';
|
|
import styled from '@emotion/styled';
|
|
import type {Location, Query} from 'history';
|
|
|
|
import {
|
|
createDashboard,
|
|
deleteDashboard,
|
|
fetchDashboard,
|
|
} from 'sentry/actionCreators/dashboards';
|
|
import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
|
|
import type {Client} from 'sentry/api';
|
|
import {Button} from 'sentry/components/button';
|
|
import {openConfirmModal} from 'sentry/components/confirm';
|
|
import type {MenuItemProps} from 'sentry/components/dropdownMenu';
|
|
import {DropdownMenu} from 'sentry/components/dropdownMenu';
|
|
import EmptyStateWarning from 'sentry/components/emptyStateWarning';
|
|
import Pagination from 'sentry/components/pagination';
|
|
import TimeSince from 'sentry/components/timeSince';
|
|
import {IconEllipsis} from 'sentry/icons';
|
|
import {t, tn} from 'sentry/locale';
|
|
import {space} from 'sentry/styles/space';
|
|
import type {Organization} from 'sentry/types';
|
|
import {trackAnalytics} from 'sentry/utils/analytics';
|
|
import withApi from 'sentry/utils/withApi';
|
|
import type {DashboardListItem} from 'sentry/views/dashboards/types';
|
|
|
|
import {cloneDashboard} from '../utils';
|
|
|
|
import DashboardCard from './dashboardCard';
|
|
import GridPreview from './gridPreview';
|
|
|
|
type Props = {
|
|
api: Client;
|
|
dashboards: DashboardListItem[] | null;
|
|
location: Location;
|
|
onDashboardsChange: () => void;
|
|
organization: Organization;
|
|
pageLinks: string;
|
|
};
|
|
|
|
function DashboardList({
|
|
api,
|
|
organization,
|
|
location,
|
|
dashboards,
|
|
pageLinks,
|
|
onDashboardsChange,
|
|
}: Props) {
|
|
function handleDelete(dashboard: DashboardListItem) {
|
|
deleteDashboard(api, organization.slug, dashboard.id)
|
|
.then(() => {
|
|
trackAnalytics('dashboards_manage.delete', {
|
|
organization,
|
|
dashboard_id: parseInt(dashboard.id, 10),
|
|
});
|
|
onDashboardsChange();
|
|
addSuccessMessage(t('Dashboard deleted'));
|
|
})
|
|
.catch(() => {
|
|
addErrorMessage(t('Error deleting Dashboard'));
|
|
});
|
|
}
|
|
|
|
async function handleDuplicate(dashboard: DashboardListItem) {
|
|
try {
|
|
const dashboardDetail = await fetchDashboard(api, organization.slug, dashboard.id);
|
|
const newDashboard = cloneDashboard(dashboardDetail);
|
|
newDashboard.widgets.map(widget => (widget.id = undefined));
|
|
await createDashboard(api, organization.slug, newDashboard, true);
|
|
trackAnalytics('dashboards_manage.duplicate', {
|
|
organization,
|
|
dashboard_id: parseInt(dashboard.id, 10),
|
|
});
|
|
onDashboardsChange();
|
|
addSuccessMessage(t('Dashboard duplicated'));
|
|
} catch (e) {
|
|
addErrorMessage(t('Error duplicating Dashboard'));
|
|
}
|
|
}
|
|
|
|
function renderDropdownMenu(dashboard: DashboardListItem) {
|
|
const menuItems: MenuItemProps[] = [
|
|
{
|
|
key: 'dashboard-duplicate',
|
|
label: t('Duplicate'),
|
|
onAction: () => handleDuplicate(dashboard),
|
|
},
|
|
{
|
|
key: 'dashboard-delete',
|
|
label: t('Delete'),
|
|
priority: 'danger',
|
|
onAction: () => {
|
|
openConfirmModal({
|
|
message: t('Are you sure you want to delete this dashboard?'),
|
|
priority: 'danger',
|
|
onConfirm: () => handleDelete(dashboard),
|
|
});
|
|
},
|
|
},
|
|
];
|
|
|
|
return (
|
|
<DropdownMenu
|
|
items={menuItems}
|
|
trigger={triggerProps => (
|
|
<DropdownTrigger
|
|
{...triggerProps}
|
|
aria-label={t('Dashboard actions')}
|
|
size="xs"
|
|
borderless
|
|
onClick={e => {
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
|
|
triggerProps.onClick?.(e);
|
|
}}
|
|
icon={<IconEllipsis direction="down" size="sm" />}
|
|
/>
|
|
)}
|
|
position="bottom-end"
|
|
disabledKeys={dashboards && dashboards.length <= 1 ? ['dashboard-delete'] : []}
|
|
offset={4}
|
|
/>
|
|
);
|
|
}
|
|
function renderGridPreview(dashboard) {
|
|
return <GridPreview widgetPreview={dashboard.widgetPreview} />;
|
|
}
|
|
|
|
function renderMiniDashboards() {
|
|
return dashboards?.map((dashboard, index) => {
|
|
return (
|
|
<DashboardCard
|
|
key={`${index}-${dashboard.id}`}
|
|
title={dashboard.title}
|
|
to={{
|
|
pathname: `/organizations/${organization.slug}/dashboard/${dashboard.id}/`,
|
|
query: {...location.query},
|
|
}}
|
|
detail={tn('%s widget', '%s widgets', dashboard.widgetPreview.length)}
|
|
dateStatus={
|
|
dashboard.dateCreated ? <TimeSince date={dashboard.dateCreated} /> : undefined
|
|
}
|
|
createdBy={dashboard.createdBy}
|
|
renderWidgets={() => renderGridPreview(dashboard)}
|
|
renderContextMenu={() => renderDropdownMenu(dashboard)}
|
|
/>
|
|
);
|
|
});
|
|
}
|
|
|
|
function renderDashboardGrid() {
|
|
if (!dashboards?.length) {
|
|
return (
|
|
<EmptyStateWarning>
|
|
<p>{t('Sorry, no Dashboards match your filters.')}</p>
|
|
</EmptyStateWarning>
|
|
);
|
|
}
|
|
return <DashboardGrid>{renderMiniDashboards()}</DashboardGrid>;
|
|
}
|
|
|
|
return (
|
|
<Fragment>
|
|
{renderDashboardGrid()}
|
|
<PaginationRow
|
|
pageLinks={pageLinks}
|
|
onCursor={(cursor, path, query, direction) => {
|
|
const offset = Number(cursor?.split?.(':')?.[1] ?? 0);
|
|
|
|
const newQuery: Query & {cursor?: string} = {...query, cursor};
|
|
const isPrevious = direction === -1;
|
|
|
|
if (offset <= 0 && isPrevious) {
|
|
delete newQuery.cursor;
|
|
}
|
|
trackAnalytics('dashboards_manage.paginate', {organization});
|
|
|
|
browserHistory.push({
|
|
pathname: path,
|
|
query: newQuery,
|
|
});
|
|
}}
|
|
/>
|
|
</Fragment>
|
|
);
|
|
}
|
|
|
|
const DashboardGrid = styled('div')`
|
|
display: grid;
|
|
grid-template-columns: minmax(100px, 1fr);
|
|
grid-template-rows: repeat(3, max-content);
|
|
gap: ${space(2)};
|
|
|
|
@media (min-width: ${p => p.theme.breakpoints.small}) {
|
|
grid-template-columns: repeat(2, minmax(100px, 1fr));
|
|
}
|
|
|
|
@media (min-width: ${p => p.theme.breakpoints.large}) {
|
|
grid-template-columns: repeat(3, minmax(100px, 1fr));
|
|
}
|
|
`;
|
|
|
|
const PaginationRow = styled(Pagination)`
|
|
margin-bottom: ${space(3)};
|
|
`;
|
|
|
|
const DropdownTrigger = styled(Button)`
|
|
transform: translateX(${space(1)});
|
|
`;
|
|
|
|
export default withApi(DashboardList);
|