tianji/src/client/pages/Dashboard.tsx

135 lines
4.0 KiB
TypeScript
Raw Normal View History

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';
import { Button, Divider } 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';
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-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-10-16 16:23:49 +00:00
if (!workspace) {
2023-10-06 07:04:55 +00:00
return <NoWorkspaceTip />;
}
if (isLoading) {
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>
<div>
2023-10-16 16:23:49 +00:00
<Button
icon={<EditOutlined />}
size="large"
onClick={() => setIsEditLayout((state) => !state)}
>
{isEditLayout ? 'Done' : 'Edit'}
2023-09-01 16:52:43 +00:00
</Button>
</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>
{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,
};
}