feat(ui): improve responsive ui

This commit is contained in:
Fu Diwei 2025-01-16 21:50:16 +08:00
parent e10fb64d6b
commit 831f0ee5d9
2 changed files with 50 additions and 80 deletions

View File

@ -13,7 +13,6 @@
"dashboard.quick_actions": "快捷操作", "dashboard.quick_actions": "快捷操作",
"dashboard.quick_actions.create_workflow": "新建工作流", "dashboard.quick_actions.create_workflow": "新建工作流",
"dashboard.quick_actions.change_login_password": "修改登录密码", "dashboard.quick_actions.change_login_password": "修改登录密码",
"dashboard.quick_actions.notification_settings": "消息推送设置", "dashboard.quick_actions.cofigure_notification": "消息推送设置",
"dashboard.quick_actions.certificate_authority_configuration": "证书颁发机构配置" "dashboard.quick_actions.configure_ca": "证书颁发机构配置"
} }

View File

@ -29,31 +29,54 @@ import { ClientResponseError } from "pocketbase";
import { get as getStatistics } from "@/api/statistics"; import { get as getStatistics } from "@/api/statistics";
import WorkflowRunDetailDrawer from "@/components/workflow/WorkflowRunDetailDrawer"; import WorkflowRunDetailDrawer from "@/components/workflow/WorkflowRunDetailDrawer";
import { type Statistics } from "@/domain/statistics"; import { type Statistics } from "@/domain/statistics";
import { WORKFLOW_TRIGGERS } from "@/domain/workflow";
import { WORKFLOW_RUN_STATUSES, type WorkflowRunModel } from "@/domain/workflowRun"; import { WORKFLOW_RUN_STATUSES, type WorkflowRunModel } from "@/domain/workflowRun";
import { list as listWorkflowRuns } from "@/repository/workflowRun"; import { list as listWorkflowRuns } from "@/repository/workflowRun";
import { getErrMsg } from "@/utils/error"; import { getErrMsg } from "@/utils/error";
const { useBreakpoint } = Grid;
const Dashboard = () => { const Dashboard = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const screens = useBreakpoint();
const { t } = useTranslation(); const { t } = useTranslation();
const { token: themeToken } = theme.useToken(); const { token: themeToken } = theme.useToken();
const breakpoints = Grid.useBreakpoint();
const [notificationApi, NotificationContextHolder] = notification.useNotification(); const [notificationApi, NotificationContextHolder] = notification.useNotification();
const statisticsGridSpans = {
xs: { flex: "50%" },
md: { flex: "50%" },
lg: { flex: "33.3333%" },
xl: { flex: "33.3333%" },
xxl: { flex: "20%" },
};
const [statistics, setStatistics] = useState<Statistics>();
const { loading: statisticsLoading } = useRequest(
() => {
return getStatistics();
},
{
onSuccess: (res) => {
setStatistics(res.data);
},
onError: (err) => {
if (err instanceof ClientResponseError && err.isAbort) {
return;
}
console.error(err);
notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) });
},
}
);
const tableColumns: TableProps<WorkflowRunModel>["columns"] = [ const tableColumns: TableProps<WorkflowRunModel>["columns"] = [
{ {
key: "$index", key: "$index",
align: "center", align: "center",
fixed: "left", fixed: "left",
width: 50, width: 50,
render: (_, __, index) => (page - 1) * pageSize + index + 1, render: (_, __, index) => index + 1,
}, },
{ {
key: "name", key: "name",
@ -98,20 +121,6 @@ const Dashboard = () => {
return <></>; return <></>;
}, },
}, },
{
key: "trigger",
title: t("workflow_run.props.trigger"),
ellipsis: true,
render: (_, record) => {
if (record.trigger === WORKFLOW_TRIGGERS.AUTO) {
return t("workflow_run.props.trigger.auto");
} else if (record.trigger === WORKFLOW_TRIGGERS.MANUAL) {
return t("workflow_run.props.trigger.manual");
}
return <></>;
},
},
{ {
key: "startedAt", key: "startedAt",
title: t("workflow_run.props.started_at"), title: t("workflow_run.props.started_at"),
@ -139,7 +148,6 @@ const Dashboard = () => {
{ {
key: "$action", key: "$action",
align: "end", align: "end",
fixed: "right",
width: 120, width: 120,
render: (_, record) => ( render: (_, record) => (
<Button.Group> <Button.Group>
@ -149,52 +157,17 @@ const Dashboard = () => {
}, },
]; ];
const [tableData, setTableData] = useState<WorkflowRunModel[]>([]); const [tableData, setTableData] = useState<WorkflowRunModel[]>([]);
const [_tableTotal, setTableTotal] = useState<number>(0); const { loading: tableLoading } = useRequest(
const [page, _setPage] = useState<number>(1);
const [pageSize, _setPageSize] = useState<number>(3);
const { loading: loadingWorkflowRun } = useRequest(
() => { () => {
return listWorkflowRuns({ return listWorkflowRuns({
page: page, page: 1,
perPage: pageSize, perPage: 5,
expand: true, expand: true,
}); });
}, },
{ {
refreshDeps: [page, pageSize],
onSuccess: (res) => { onSuccess: (res) => {
setTableData(res.items); setTableData(res.items);
setTableTotal(res.totalItems > 3 ? 3 : res.totalItems);
},
onError: (err) => {
if (err instanceof ClientResponseError && err.isAbort) {
return;
}
console.error(err);
notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) });
},
}
);
const statisticsGridSpans = {
xs: { flex: "50%" },
md: { flex: "50%" },
lg: { flex: "33.3333%" },
xl: { flex: "33.3333%" },
xxl: { flex: "20%" },
};
const [statistics, setStatistics] = useState<Statistics>();
const { loading } = useRequest(
() => {
return getStatistics();
},
{
onSuccess: (res) => {
setStatistics(res.data);
}, },
onError: (err) => { onError: (err) => {
if (err instanceof ClientResponseError && err.isAbort) { if (err instanceof ClientResponseError && err.isAbort) {
@ -218,7 +191,7 @@ const Dashboard = () => {
<StatisticCard <StatisticCard
icon={<SquareSigmaIcon size={48} strokeWidth={1} color={themeToken.colorInfo} />} icon={<SquareSigmaIcon size={48} strokeWidth={1} color={themeToken.colorInfo} />}
label={t("dashboard.statistics.all_certificates")} label={t("dashboard.statistics.all_certificates")}
loading={loading} loading={statisticsLoading}
value={statistics?.certificateTotal ?? "-"} value={statistics?.certificateTotal ?? "-"}
suffix={t("dashboard.statistics.unit")} suffix={t("dashboard.statistics.unit")}
onClick={() => navigate("/certificates")} onClick={() => navigate("/certificates")}
@ -228,7 +201,7 @@ const Dashboard = () => {
<StatisticCard <StatisticCard
icon={<CalendarClockIcon size={48} strokeWidth={1} color={themeToken.colorWarning} />} icon={<CalendarClockIcon size={48} strokeWidth={1} color={themeToken.colorWarning} />}
label={t("dashboard.statistics.expire_soon_certificates")} label={t("dashboard.statistics.expire_soon_certificates")}
loading={loading} loading={statisticsLoading}
value={statistics?.certificateExpireSoon ?? "-"} value={statistics?.certificateExpireSoon ?? "-"}
suffix={t("dashboard.statistics.unit")} suffix={t("dashboard.statistics.unit")}
onClick={() => navigate("/certificates?state=expireSoon")} onClick={() => navigate("/certificates?state=expireSoon")}
@ -238,7 +211,7 @@ const Dashboard = () => {
<StatisticCard <StatisticCard
icon={<CalendarX2Icon size={48} strokeWidth={1} color={themeToken.colorError} />} icon={<CalendarX2Icon size={48} strokeWidth={1} color={themeToken.colorError} />}
label={t("dashboard.statistics.expired_certificates")} label={t("dashboard.statistics.expired_certificates")}
loading={loading} loading={statisticsLoading}
value={statistics?.certificateExpired ?? "-"} value={statistics?.certificateExpired ?? "-"}
suffix={t("dashboard.statistics.unit")} suffix={t("dashboard.statistics.unit")}
onClick={() => navigate("/certificates?state=expired")} onClick={() => navigate("/certificates?state=expired")}
@ -248,7 +221,7 @@ const Dashboard = () => {
<StatisticCard <StatisticCard
icon={<WorkflowIcon size={48} strokeWidth={1} color={themeToken.colorInfo} />} icon={<WorkflowIcon size={48} strokeWidth={1} color={themeToken.colorInfo} />}
label={t("dashboard.statistics.all_workflows")} label={t("dashboard.statistics.all_workflows")}
loading={loading} loading={statisticsLoading}
value={statistics?.workflowTotal ?? "-"} value={statistics?.workflowTotal ?? "-"}
suffix={t("dashboard.statistics.unit")} suffix={t("dashboard.statistics.unit")}
onClick={() => navigate("/workflows")} onClick={() => navigate("/workflows")}
@ -258,7 +231,7 @@ const Dashboard = () => {
<StatisticCard <StatisticCard
icon={<FolderCheckIcon size={48} strokeWidth={1} color={themeToken.colorSuccess} />} icon={<FolderCheckIcon size={48} strokeWidth={1} color={themeToken.colorSuccess} />}
label={t("dashboard.statistics.enabled_workflows")} label={t("dashboard.statistics.enabled_workflows")}
loading={loading} loading={statisticsLoading}
value={statistics?.workflowEnabled ?? "-"} value={statistics?.workflowEnabled ?? "-"}
suffix={t("dashboard.statistics.unit")} suffix={t("dashboard.statistics.unit")}
onClick={() => navigate("/workflows?state=enabled")} onClick={() => navigate("/workflows?state=enabled")}
@ -268,34 +241,32 @@ const Dashboard = () => {
<Divider /> <Divider />
<Flex vertical={!screens.md} gap={16}> <Flex justify="stretch" vertical={!breakpoints.lg} gap={16}>
<Card className="sm:h-full sm:w-[500px] sm:pb-32"> <Card className="max-lg:flex-1 lg:w-[360px]" title={t("dashboard.quick_actions")}>
<div className="text-lg font-semibold">{t("dashboard.quick_actions")}</div> <Space className="w-full" direction="vertical" size="large">
<div className="mt-9"> <Button block type="primary" size="large" icon={<PlusOutlined />} onClick={() => navigate("/workflows/new")}>
<Button className="w-full" type="primary" size="large" icon={<PlusOutlined />} onClick={() => navigate("/workflows/new")}>
{t("dashboard.quick_actions.create_workflow")} {t("dashboard.quick_actions.create_workflow")}
</Button> </Button>
<Button className="mt-5 w-full" size="large" icon={<LockOutlined />} onClick={() => navigate("/settings/password")}> <Button block size="large" icon={<LockOutlined />} onClick={() => navigate("/settings/password")}>
{t("dashboard.quick_actions.change_login_password")} {t("dashboard.quick_actions.change_login_password")}
</Button> </Button>
<Button className="mt-5 w-full" size="large" icon={<SendOutlined />} onClick={() => navigate("/settings/notification")}> <Button block size="large" icon={<SendOutlined />} onClick={() => navigate("/settings/notification")}>
{t("dashboard.quick_actions.notification_settings")} {t("dashboard.quick_actions.notification_settings")}
</Button> </Button>
<Button className="mt-5 w-full" size="large" icon={<ApiOutlined />} onClick={() => navigate("/settings/ssl-provider")}> <Button block size="large" icon={<ApiOutlined />} onClick={() => navigate("/settings/ssl-provider")}>
{t("dashboard.quick_actions.certificate_authority_configuration")} {t("dashboard.quick_actions.certificate_authority_configuration")}
</Button> </Button>
</div> </Space>
</Card> </Card>
<Card className="size-full"> <Card className="flex-1" title={t("dashboard.latest_workflow_run")}>
<div className="text-lg font-semibold">{t("dashboard.latest_workflow_run")} </div>
<Table<WorkflowRunModel> <Table<WorkflowRunModel>
className="mt-5"
columns={tableColumns} columns={tableColumns}
dataSource={tableData} dataSource={tableData}
loading={loadingWorkflowRun} loading={tableLoading}
locale={{ locale={{
emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />, emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />,
}} }}
pagination={false}
rowKey={(record: WorkflowRunModel) => record.id} rowKey={(record: WorkflowRunModel) => record.id}
scroll={{ x: "max(100%, 960px)" }} scroll={{ x: "max(100%, 960px)" }}
/> />