2025-01-07 02:29:29 +08:00
|
|
|
|
'use client';
|
|
|
|
|
|
|
|
|
|
import React, { useMemo, useState } from 'react';
|
2025-05-31 01:16:48 +08:00
|
|
|
|
import type { BusterDashboardListItem } from '@/api/asset_interfaces';
|
|
|
|
|
import { FavoriteStar } from '@/components/features/list';
|
|
|
|
|
import { getShareStatus } from '@/components/features/metrics/StatusBadgeIndicator/helpers';
|
|
|
|
|
import { NewDashboardModal } from '@/components/features/modal/NewDashboardModal';
|
2025-02-27 14:25:53 +08:00
|
|
|
|
import { Avatar } from '@/components/ui/avatar';
|
2025-03-02 12:50:00 +08:00
|
|
|
|
import {
|
|
|
|
|
BusterList,
|
2025-05-31 01:16:48 +08:00
|
|
|
|
type BusterListColumn,
|
|
|
|
|
type BusterListRow,
|
2025-03-02 12:50:00 +08:00
|
|
|
|
ListEmptyStateWithButton
|
|
|
|
|
} from '@/components/ui/list';
|
2025-05-31 01:16:48 +08:00
|
|
|
|
import { Text } from '@/components/ui/typography';
|
2025-03-08 07:02:56 +08:00
|
|
|
|
import { useMemoizedFn } from '@/hooks';
|
2025-05-31 01:16:48 +08:00
|
|
|
|
import { formatDate } from '@/lib';
|
|
|
|
|
import { BusterRoutes, createBusterRoute } from '@/routes';
|
2025-02-08 03:45:47 +08:00
|
|
|
|
import { DashboardSelectedOptionPopup } from './DashboardSelectedPopup';
|
2025-01-07 02:29:29 +08:00
|
|
|
|
|
2025-08-03 13:08:28 +08:00
|
|
|
|
const columns: BusterListColumn<BusterDashboardListItem>[] = [
|
2025-01-07 02:29:29 +08:00
|
|
|
|
{
|
|
|
|
|
dataIndex: 'name',
|
|
|
|
|
title: 'Title',
|
2025-03-23 06:05:12 +08:00
|
|
|
|
render: (data, { id }) => {
|
|
|
|
|
const name = data || 'New Dashboard';
|
|
|
|
|
return (
|
|
|
|
|
<div className="mr-2 flex items-center space-x-1.5">
|
|
|
|
|
<Text truncate>{name}</Text>
|
|
|
|
|
<FavoriteStar
|
|
|
|
|
id={id}
|
2025-07-04 07:10:08 +08:00
|
|
|
|
type={'dashboard'}
|
2025-03-23 06:05:12 +08:00
|
|
|
|
iconStyle="tertiary"
|
|
|
|
|
title={name}
|
|
|
|
|
className="opacity-0 group-hover:opacity-100"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2025-01-07 02:29:29 +08:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
dataIndex: 'last_edited',
|
|
|
|
|
title: 'Last edited',
|
2025-03-21 06:05:59 +08:00
|
|
|
|
width: 140,
|
2025-01-07 02:29:29 +08:00
|
|
|
|
render: (data) => formatDate({ date: data, format: 'lll' })
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
dataIndex: 'created_at',
|
|
|
|
|
title: 'Created at',
|
2025-03-21 06:05:59 +08:00
|
|
|
|
width: 140,
|
2025-01-07 02:29:29 +08:00
|
|
|
|
render: (data) => formatDate({ date: data, format: 'lll' })
|
|
|
|
|
},
|
|
|
|
|
{
|
2025-08-03 13:08:28 +08:00
|
|
|
|
dataIndex: 'status',
|
2025-01-07 02:29:29 +08:00
|
|
|
|
title: 'Sharing',
|
|
|
|
|
width: 65,
|
|
|
|
|
render: (_, data) => getShareStatus(data)
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
dataIndex: 'owner',
|
|
|
|
|
title: 'Owner',
|
|
|
|
|
width: 55,
|
2025-04-10 04:34:36 +08:00
|
|
|
|
render: (_, data: BusterDashboardListItem) => {
|
|
|
|
|
return <Avatar image={data?.owner?.avatar_url} name={data?.owner?.name} size={18} />;
|
2025-01-07 02:29:29 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
];
|
|
|
|
|
|
2025-02-08 03:45:47 +08:00
|
|
|
|
export const DashboardListContent: React.FC<{
|
|
|
|
|
loading: boolean;
|
|
|
|
|
dashboardsList: BusterDashboardListItem[];
|
2025-04-26 04:46:18 +08:00
|
|
|
|
openNewDashboardModal: boolean;
|
|
|
|
|
setOpenNewDashboardModal: (open: boolean) => void;
|
2025-02-08 03:45:47 +08:00
|
|
|
|
className?: string;
|
2025-04-26 04:46:18 +08:00
|
|
|
|
}> = React.memo(
|
|
|
|
|
({
|
|
|
|
|
loading,
|
|
|
|
|
dashboardsList,
|
|
|
|
|
openNewDashboardModal,
|
|
|
|
|
setOpenNewDashboardModal,
|
|
|
|
|
className = ''
|
|
|
|
|
}) => {
|
|
|
|
|
const [selectedDashboardIds, setSelectedDashboardIds] = useState<string[]>([]);
|
2025-01-07 02:29:29 +08:00
|
|
|
|
|
2025-04-26 04:46:18 +08:00
|
|
|
|
const rows: BusterListRow[] = useMemo(() => {
|
|
|
|
|
return dashboardsList.map((dashboard) => {
|
|
|
|
|
return {
|
|
|
|
|
id: dashboard.id,
|
|
|
|
|
data: dashboard,
|
|
|
|
|
link: createBusterRoute({
|
|
|
|
|
route: BusterRoutes.APP_DASHBOARD_ID,
|
|
|
|
|
dashboardId: dashboard.id
|
|
|
|
|
})
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
}, [dashboardsList]);
|
|
|
|
|
|
|
|
|
|
const onClickEmptyState = useMemoizedFn(async () => {
|
|
|
|
|
setOpenNewDashboardModal(true);
|
2025-01-07 02:29:29 +08:00
|
|
|
|
});
|
|
|
|
|
|
2025-04-26 04:46:18 +08:00
|
|
|
|
return (
|
|
|
|
|
<div className={`${className} relative flex h-full flex-col items-center`}>
|
|
|
|
|
<BusterList
|
|
|
|
|
rows={rows}
|
|
|
|
|
columns={columns}
|
|
|
|
|
selectedRowKeys={selectedDashboardIds}
|
|
|
|
|
onSelectChange={setSelectedDashboardIds}
|
|
|
|
|
emptyState={
|
|
|
|
|
!loading ? (
|
|
|
|
|
<ListEmptyStateWithButton
|
2025-05-31 01:16:48 +08:00
|
|
|
|
title={'You don’t have any dashboards yet.'}
|
2025-04-26 04:46:18 +08:00
|
|
|
|
buttonText="New dashboard"
|
2025-05-31 01:16:48 +08:00
|
|
|
|
description={
|
|
|
|
|
'You don’t have any dashboards. As soon as you do, they will start to appear here.'
|
|
|
|
|
}
|
2025-04-26 04:46:18 +08:00
|
|
|
|
onClick={onClickEmptyState}
|
|
|
|
|
/>
|
2025-05-31 01:16:48 +08:00
|
|
|
|
) : null
|
2025-04-26 04:46:18 +08:00
|
|
|
|
}
|
|
|
|
|
/>
|
2025-01-07 02:29:29 +08:00
|
|
|
|
|
2025-04-26 04:46:18 +08:00
|
|
|
|
<DashboardSelectedOptionPopup
|
|
|
|
|
selectedRowKeys={selectedDashboardIds}
|
|
|
|
|
onSelectChange={setSelectedDashboardIds}
|
|
|
|
|
hasSelected={selectedDashboardIds.length > 0}
|
|
|
|
|
/>
|
2025-01-07 02:29:29 +08:00
|
|
|
|
|
2025-04-26 04:46:18 +08:00
|
|
|
|
<NewDashboardModal
|
|
|
|
|
open={openNewDashboardModal}
|
|
|
|
|
onClose={useMemoizedFn(() => setOpenNewDashboardModal(false))}
|
|
|
|
|
useChangePage={true}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
);
|
2025-03-11 06:17:24 +08:00
|
|
|
|
|
|
|
|
|
DashboardListContent.displayName = 'DashboardListContent';
|