2023-10-16 16:23:49 +00:00
|
|
|
import React, { Fragment, useMemo, useState } from 'react';
|
2023-10-06 07:04:55 +00:00
|
|
|
import { ArrowRightOutlined, EditOutlined } from '@ant-design/icons';
|
2023-10-20 16:19:12 +00:00
|
|
|
import { Button, Divider, Empty } from 'antd';
|
2023-10-06 14:08:15 +00:00
|
|
|
import { WebsiteOverview } from '../components/website/WebsiteOverview';
|
2023-10-16 16:23:49 +00:00
|
|
|
import { useCurrentWorkspace } from '../store/user';
|
2023-09-10 07:55:04 +00:00
|
|
|
import { Loading } from '../components/Loading';
|
2023-10-06 07:04:55 +00:00
|
|
|
import { useWorspaceWebsites } from '../api/model/website';
|
|
|
|
import { NoWorkspaceTip } from '../components/NoWorkspaceTip';
|
|
|
|
import { useNavigate } from 'react-router';
|
2023-10-16 16:23:49 +00:00
|
|
|
import { useEvent } from '../hooks/useEvent';
|
|
|
|
import arrayMove from 'array-move';
|
|
|
|
import SortableList, { SortableItem } from 'react-easy-sort';
|
|
|
|
import { defaultErrorHandler, defaultSuccessHandler, trpc } from '../api/trpc';
|
2023-10-20 16:19:12 +00:00
|
|
|
import { Link } from 'react-router-dom';
|
2023-11-07 16:07:58 +00:00
|
|
|
import { DateFilter } from '../components/DateFilter';
|
2023-08-31 16:11:47 +00:00
|
|
|
|
|
|
|
export const Dashboard: React.FC = React.memo(() => {
|
2023-10-16 16:23:49 +00:00
|
|
|
const workspace = useCurrentWorkspace();
|
2023-10-06 07:04:55 +00:00
|
|
|
const navigate = useNavigate();
|
2023-10-16 16:23:49 +00:00
|
|
|
const [isEditLayout, setIsEditLayout] = useState(false);
|
|
|
|
const { isLoading, websiteList, handleSortEnd } = useDashboardWebsiteList();
|
2023-09-10 07:55:04 +00:00
|
|
|
|
2023-10-16 16:23:49 +00:00
|
|
|
if (!workspace) {
|
2023-10-06 07:04:55 +00:00
|
|
|
return <NoWorkspaceTip />;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isLoading) {
|
2023-09-10 07:55:04 +00:00
|
|
|
return <Loading />;
|
|
|
|
}
|
|
|
|
|
2023-09-01 16:52:43 +00:00
|
|
|
return (
|
2023-10-16 16:23:49 +00:00
|
|
|
<div className="py-4">
|
|
|
|
<div className="h-20 flex items-center">
|
2023-09-01 16:52:43 +00:00
|
|
|
<div className="text-2xl flex-1">Dashboard</div>
|
2023-11-07 16:07:58 +00:00
|
|
|
<div className="flex gap-4">
|
|
|
|
{!isEditLayout && <DateFilter />}
|
|
|
|
|
2023-10-20 16:19:12 +00:00
|
|
|
{websiteList.length !== 0 && (
|
|
|
|
<Button
|
|
|
|
icon={<EditOutlined />}
|
|
|
|
size="large"
|
|
|
|
onClick={() => setIsEditLayout((state) => !state)}
|
|
|
|
>
|
|
|
|
{isEditLayout ? 'Done' : 'Edit'}
|
|
|
|
</Button>
|
|
|
|
)}
|
2023-09-01 16:52:43 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
2023-10-16 16:23:49 +00:00
|
|
|
|
|
|
|
{isEditLayout ? (
|
|
|
|
<SortableList
|
|
|
|
className="space-y-2"
|
|
|
|
lockAxis="y"
|
|
|
|
onSortEnd={handleSortEnd}
|
|
|
|
>
|
|
|
|
{websiteList.map((website) => (
|
|
|
|
<SortableItem key={website.id}>
|
|
|
|
<div className="overflow-hidden h-14 w-full border border-black border-opacity-20 flex justify-center items-center rounded-lg bg-white cursor-move">
|
|
|
|
{website.name}
|
|
|
|
</div>
|
|
|
|
</SortableItem>
|
|
|
|
))}
|
|
|
|
</SortableList>
|
|
|
|
) : (
|
|
|
|
<div>
|
2023-10-20 16:19:12 +00:00
|
|
|
{websiteList.length === 0 && (
|
|
|
|
<Empty
|
|
|
|
description={
|
|
|
|
<div>
|
|
|
|
<div>There is no website has been created</div>
|
|
|
|
<Link to="/settings/websites">
|
|
|
|
<Button>Add webiste</Button>
|
|
|
|
</Link>
|
|
|
|
</div>
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
|
2023-10-16 16:23:49 +00:00
|
|
|
{websiteList.map((website, i) => (
|
|
|
|
<Fragment key={website.id}>
|
|
|
|
{i !== 0 && <Divider />}
|
|
|
|
|
|
|
|
<WebsiteOverview
|
|
|
|
website={website}
|
|
|
|
actions={
|
|
|
|
<>
|
|
|
|
<Button
|
|
|
|
type="primary"
|
|
|
|
size="large"
|
|
|
|
onClick={() => {
|
|
|
|
navigate(`/website/${website.id}`);
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
View Details <ArrowRightOutlined />
|
|
|
|
</Button>
|
|
|
|
</>
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
</Fragment>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
)}
|
2023-09-01 16:52:43 +00:00
|
|
|
</div>
|
|
|
|
);
|
2023-08-31 16:11:47 +00:00
|
|
|
});
|
|
|
|
Dashboard.displayName = 'Dashboard';
|
2023-10-16 16:23:49 +00:00
|
|
|
|
|
|
|
function useDashboardWebsiteList() {
|
|
|
|
const workspace = useCurrentWorkspace();
|
|
|
|
const workspaceId = workspace.id;
|
|
|
|
const { isLoading, websites } = useWorspaceWebsites(workspaceId);
|
|
|
|
const [dashboardOrder, setDashboardOrder] = useState(
|
|
|
|
workspace.dashboardOrder
|
|
|
|
);
|
|
|
|
const updateDashboardOrderMutation =
|
|
|
|
trpc.workspace.updateDashboardOrder.useMutation({
|
|
|
|
onSuccess: defaultSuccessHandler,
|
|
|
|
onError: defaultErrorHandler,
|
|
|
|
});
|
|
|
|
|
|
|
|
const websiteList = useMemo(
|
|
|
|
() =>
|
|
|
|
websites.sort((a, b) => {
|
|
|
|
const aIndex = dashboardOrder.findIndex((item) => item === a.id);
|
|
|
|
const bIndex = dashboardOrder.findIndex((item) => item === b.id);
|
|
|
|
|
|
|
|
// In both cases, if in the sorted list, they are sorted according to the sorted list.
|
|
|
|
// If not in the sorted list, put it first
|
|
|
|
return aIndex - bIndex;
|
|
|
|
}),
|
|
|
|
[websites, dashboardOrder]
|
|
|
|
);
|
|
|
|
|
|
|
|
const handleSortEnd = useEvent((oldIndex: number, newIndex: number) => {
|
|
|
|
const newOrder = arrayMove(
|
|
|
|
websiteList.map((w) => w.id),
|
|
|
|
oldIndex,
|
|
|
|
newIndex
|
|
|
|
);
|
|
|
|
setDashboardOrder(newOrder);
|
|
|
|
|
|
|
|
updateDashboardOrderMutation.mutate({
|
|
|
|
workspaceId,
|
|
|
|
dashboardOrder: newOrder,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return {
|
|
|
|
isLoading,
|
|
|
|
websiteList,
|
|
|
|
handleSortEnd,
|
|
|
|
};
|
|
|
|
}
|