From c522196029359811a96da2fd2f5e11e82d3bbbb6 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Wed, 4 Dec 2024 21:55:52 +0800 Subject: [PATCH] feat(ui): new WorkflowList UI using antd --- ui/package-lock.json | 478 ++++++++++++++++++++ ui/package.json | 1 + ui/src/api/notify.ts | 4 +- ui/src/api/statistics.ts | 4 +- ui/src/api/workflow.ts | 4 +- ui/src/i18n/locales/en/nls.common.json | 1 + ui/src/i18n/locales/en/nls.domain.json | 2 +- ui/src/i18n/locales/en/nls.workflow.json | 7 +- ui/src/i18n/locales/zh/nls.common.json | 1 + ui/src/i18n/locales/zh/nls.domain.json | 2 +- ui/src/i18n/locales/zh/nls.workflow.json | 9 +- ui/src/main.tsx | 21 +- ui/src/pages/DashboardLayout.tsx | 15 +- ui/src/pages/LoginLayout.tsx | 4 +- ui/src/pages/dashboard/Dashboard.tsx | 4 +- ui/src/pages/login/Login.tsx | 4 +- ui/src/pages/setting/Account.tsx | 8 +- ui/src/pages/setting/Password.tsx | 8 +- ui/src/pages/workflow/WorkflowList.tsx | 248 ++++++++++ ui/src/pages/workflow/index.tsx | 224 --------- ui/src/repository/access.ts | 10 +- ui/src/repository/certificate.ts | 4 +- ui/src/repository/deployment.ts | 4 +- ui/src/repository/domains.ts | 16 +- ui/src/repository/{api.ts => pocketbase.ts} | 2 +- ui/src/repository/settings.ts | 8 +- ui/src/repository/workflow.ts | 81 ++-- ui/src/router.tsx | 6 +- 28 files changed, 838 insertions(+), 342 deletions(-) create mode 100644 ui/src/pages/workflow/WorkflowList.tsx delete mode 100644 ui/src/pages/workflow/index.tsx rename ui/src/repository/{api.ts => pocketbase.ts} (84%) diff --git a/ui/package-lock.json b/ui/package-lock.json index 7170d6e2..88ff0bf6 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -8,6 +8,7 @@ "name": "ui", "version": "0.0.0", "dependencies": { + "@ant-design/pro-components": "^2.8.2", "@hookform/resolvers": "^3.9.0", "@radix-ui/react-accordion": "^1.2.0", "@radix-ui/react-alert-dialog": "^1.1.1", @@ -174,6 +175,286 @@ "resolved": "https://registry.npmmirror.com/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz", "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==" }, + "node_modules/@ant-design/pro-card": { + "version": "2.9.2", + "resolved": "https://registry.npmmirror.com/@ant-design/pro-card/-/pro-card-2.9.2.tgz", + "integrity": "sha512-mKOmNb7jc3Pz41RrPY7EFKRWBjLdN4tp9yzmRkS2g8K7P3pW435f7Ip6rc+58FWDzbZa8lElTGPxAoFB/dq7LA==", + "dependencies": { + "@ant-design/cssinjs": "^1.21.1", + "@ant-design/icons": "^5.0.0", + "@ant-design/pro-provider": "2.15.2", + "@ant-design/pro-utils": "2.16.2", + "@babel/runtime": "^7.18.0", + "classnames": "^2.3.2", + "omit.js": "^2.0.2", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.4.0" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "react": ">=17.0.0" + } + }, + "node_modules/@ant-design/pro-components": { + "version": "2.8.2", + "resolved": "https://registry.npmmirror.com/@ant-design/pro-components/-/pro-components-2.8.2.tgz", + "integrity": "sha512-gSzt/Pw1ayZeHhxh5yaeP7pGpk0V2ZsB4PZab0s6V88O15Ql3w5ciYTObxbxGXMPc+A72AwVThoYLv2ZIl3cMA==", + "dependencies": { + "@ant-design/pro-card": "2.9.2", + "@ant-design/pro-descriptions": "2.6.2", + "@ant-design/pro-field": "2.17.2", + "@ant-design/pro-form": "2.31.2", + "@ant-design/pro-layout": "7.21.2", + "@ant-design/pro-list": "2.6.2", + "@ant-design/pro-provider": "2.15.2", + "@ant-design/pro-skeleton": "2.2.1", + "@ant-design/pro-table": "3.18.2", + "@ant-design/pro-utils": "2.16.2", + "@babel/runtime": "^7.16.3" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@ant-design/pro-descriptions": { + "version": "2.6.2", + "resolved": "https://registry.npmmirror.com/@ant-design/pro-descriptions/-/pro-descriptions-2.6.2.tgz", + "integrity": "sha512-IrXf4qNMyaypEhO54oZDOFNJ9jrQgg2ovARY7hHRZCChC+I2xVGFCFWXrmtyS82kusxHb6OlLw20ahm+TLZ71w==", + "dependencies": { + "@ant-design/pro-field": "2.17.2", + "@ant-design/pro-form": "2.31.2", + "@ant-design/pro-provider": "2.15.2", + "@ant-design/pro-skeleton": "2.2.1", + "@ant-design/pro-utils": "2.16.2", + "@babel/runtime": "^7.18.0", + "rc-resize-observer": "^0.2.3", + "rc-util": "^5.0.6" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "react": ">=17.0.0" + } + }, + "node_modules/@ant-design/pro-descriptions/node_modules/rc-resize-observer": { + "version": "0.2.6", + "resolved": "https://registry.npmmirror.com/rc-resize-observer/-/rc-resize-observer-0.2.6.tgz", + "integrity": "sha512-YX6nYnd6fk7zbuvT6oSDMKiZjyngjHoy+fz+vL3Tez38d/G5iGdaDJa2yE7345G6sc4Mm1IGRUIwclvltddhmA==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1", + "rc-util": "^5.0.0", + "resize-observer-polyfill": "^1.5.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@ant-design/pro-field": { + "version": "2.17.2", + "resolved": "https://registry.npmmirror.com/@ant-design/pro-field/-/pro-field-2.17.2.tgz", + "integrity": "sha512-cebfWGaE6MYwfchXpU9xA6jPETZOvk3i9+1IvebjSEKKVXecXuA+muZorpwYzORmkgGBmSPyR0KW+6Ttgtmg9Q==", + "dependencies": { + "@ant-design/icons": "^5.0.0", + "@ant-design/pro-provider": "2.15.2", + "@ant-design/pro-utils": "2.16.2", + "@babel/runtime": "^7.18.0", + "@chenshuai2144/sketch-color": "^1.0.8", + "classnames": "^2.3.2", + "dayjs": "^1.11.10", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "omit.js": "^2.0.2", + "rc-util": "^5.4.0", + "swr": "^2.0.0" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "react": ">=17.0.0" + } + }, + "node_modules/@ant-design/pro-form": { + "version": "2.31.2", + "resolved": "https://registry.npmmirror.com/@ant-design/pro-form/-/pro-form-2.31.2.tgz", + "integrity": "sha512-fzchlk+vGi8rCpmC62/SrikuwC2ZpyKnvNVAyihPCNe9oyyv+LD2TZAD0fbshfifP/1aHOOtS4fb7ptYq+LarQ==", + "dependencies": { + "@ant-design/icons": "^5.0.0", + "@ant-design/pro-field": "2.17.2", + "@ant-design/pro-provider": "2.15.2", + "@ant-design/pro-utils": "2.16.2", + "@babel/runtime": "^7.18.0", + "@chenshuai2144/sketch-color": "^1.0.7", + "@umijs/use-params": "^1.0.9", + "classnames": "^2.3.2", + "dayjs": "^1.11.10", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "omit.js": "^2.0.2", + "rc-resize-observer": "^1.1.0", + "rc-util": "^5.0.6" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "rc-field-form": ">=1.22.0", + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@ant-design/pro-layout": { + "version": "7.21.2", + "resolved": "https://registry.npmmirror.com/@ant-design/pro-layout/-/pro-layout-7.21.2.tgz", + "integrity": "sha512-dtqap5YNDrxUWxhi43QJQSv1JLHYPCV4/h4cFM10HNiX/86Cxw37DiCOMdIM/ZwWk619BiwN7CJNgL5Q8obrAQ==", + "dependencies": { + "@ant-design/cssinjs": "^1.21.1", + "@ant-design/icons": "^5.0.0", + "@ant-design/pro-provider": "2.15.2", + "@ant-design/pro-utils": "2.16.2", + "@babel/runtime": "^7.18.0", + "@umijs/route-utils": "^4.0.0", + "@umijs/use-params": "^1.0.9", + "classnames": "^2.3.2", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "omit.js": "^2.0.2", + "path-to-regexp": "8.0.0", + "rc-resize-observer": "^1.1.0", + "rc-util": "^5.0.6", + "swr": "^2.0.0", + "warning": "^4.0.3" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@ant-design/pro-list": { + "version": "2.6.2", + "resolved": "https://registry.npmmirror.com/@ant-design/pro-list/-/pro-list-2.6.2.tgz", + "integrity": "sha512-BEM/WFe8vj4TCdsxa1JDQwl87Xb7oj+3bxA8yLDjRWWwX+D9UuxdYyB2lZsFfSEnphau/mccDE3K/Lbtim6yJg==", + "dependencies": { + "@ant-design/cssinjs": "^1.21.1", + "@ant-design/icons": "^5.0.0", + "@ant-design/pro-card": "2.9.2", + "@ant-design/pro-field": "2.17.2", + "@ant-design/pro-table": "3.18.2", + "@ant-design/pro-utils": "2.16.2", + "@babel/runtime": "^7.18.0", + "classnames": "^2.3.2", + "dayjs": "^1.11.10", + "rc-resize-observer": "^1.0.0", + "rc-util": "^4.19.0" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@ant-design/pro-list/node_modules/rc-util": { + "version": "4.21.1", + "resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-4.21.1.tgz", + "integrity": "sha512-Z+vlkSQVc1l8O2UjR3WQ+XdWlhj5q9BMQNLk2iOBch75CqPfrJyGtcWMcnhRlNuDu0Ndtt4kLVO8JI8BrABobg==", + "dependencies": { + "add-dom-event-listener": "^1.1.0", + "prop-types": "^15.5.10", + "react-is": "^16.12.0", + "react-lifecycles-compat": "^3.0.4", + "shallowequal": "^1.1.0" + } + }, + "node_modules/@ant-design/pro-list/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/@ant-design/pro-provider": { + "version": "2.15.2", + "resolved": "https://registry.npmmirror.com/@ant-design/pro-provider/-/pro-provider-2.15.2.tgz", + "integrity": "sha512-7WSJcjYIuLwco1YiiSgEEJnrqvg7x/YZap8pxOChRnyNh9S3HuV1D5HTc18kfHTpWqZWTAUcS66b0kMP96uKrw==", + "dependencies": { + "@ant-design/cssinjs": "^1.21.1", + "@babel/runtime": "^7.18.0", + "@ctrl/tinycolor": "^3.4.0", + "dayjs": "^1.11.10", + "rc-util": "^5.0.1", + "swr": "^2.0.0" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@ant-design/pro-skeleton": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/@ant-design/pro-skeleton/-/pro-skeleton-2.2.1.tgz", + "integrity": "sha512-3M2jNOZQZWEDR8pheY00OkHREfb0rquvFZLCa6DypGmiksiuuYuR9Y4iA82ZF+mva2FmpHekdwbje/GpbxqBeg==", + "dependencies": { + "@babel/runtime": "^7.18.0" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@ant-design/pro-table": { + "version": "3.18.2", + "resolved": "https://registry.npmmirror.com/@ant-design/pro-table/-/pro-table-3.18.2.tgz", + "integrity": "sha512-IIhWXvpBfdy1hqh0qYQOou6tDawrisFYwFhYdiMwuCnvy7UvaHi/JS4yikMe+KG0XVdh6xxfrF1Ad39SR8CrxQ==", + "dependencies": { + "@ant-design/cssinjs": "^1.21.1", + "@ant-design/icons": "^5.0.0", + "@ant-design/pro-card": "2.9.2", + "@ant-design/pro-field": "2.17.2", + "@ant-design/pro-form": "2.31.2", + "@ant-design/pro-provider": "2.15.2", + "@ant-design/pro-utils": "2.16.2", + "@babel/runtime": "^7.18.0", + "@dnd-kit/core": "^6.0.8", + "@dnd-kit/modifiers": "^6.0.1", + "@dnd-kit/sortable": "^7.0.2", + "@dnd-kit/utilities": "^3.2.1", + "classnames": "^2.3.2", + "dayjs": "^1.11.10", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "omit.js": "^2.0.2", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.0.1" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "rc-field-form": ">=1.22.0", + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@ant-design/pro-utils": { + "version": "2.16.2", + "resolved": "https://registry.npmmirror.com/@ant-design/pro-utils/-/pro-utils-2.16.2.tgz", + "integrity": "sha512-ama73ZSzz9O6Qz6DvHd6cnyUA3vI7N+AAl5BV5plijujtnXpNC8KJMXl9jOI1K7QuUVJgJIKbZ2DVm8LnBcTAQ==", + "dependencies": { + "@ant-design/icons": "^5.0.0", + "@ant-design/pro-provider": "2.15.2", + "@babel/runtime": "^7.18.0", + "classnames": "^2.3.2", + "dayjs": "^1.11.10", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "rc-util": "^5.0.6", + "safe-stable-stringify": "^2.4.3", + "swr": "^2.0.0" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, "node_modules/@ant-design/react-slick": { "version": "1.1.2", "resolved": "https://registry.npmmirror.com/@ant-design/react-slick/-/react-slick-1.1.2.tgz", @@ -550,6 +831,18 @@ "node": ">=6.9.0" } }, + "node_modules/@chenshuai2144/sketch-color": { + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/@chenshuai2144/sketch-color/-/sketch-color-1.0.9.tgz", + "integrity": "sha512-obzSy26cb7Pm7OprWyVpgMpIlrZpZ0B7vbrU0RMbvRg0YAI890S5Xy02Aj1Nhl4+KTbi1lVYHt6HQP8Hm9s+1w==", + "dependencies": { + "reactcss": "^1.2.3", + "tinycolor2": "^1.4.2" + }, + "peerDependencies": { + "react": ">=16.12.0" + } + }, "node_modules/@ctrl/tinycolor": { "version": "3.6.1", "resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", @@ -558,6 +851,68 @@ "node": ">=10" } }, + "node_modules/@dnd-kit/accessibility": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", + "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/core": { + "version": "6.2.0", + "resolved": "https://registry.npmmirror.com/@dnd-kit/core/-/core-6.2.0.tgz", + "integrity": "sha512-KVK/CJmaYGTxTPU6P0+Oy4itgffTUa80B8317sXzfOr1qUzSL29jE7Th11llXiu2haB7B9Glpzo2CDElin+geQ==", + "dependencies": { + "@dnd-kit/accessibility": "^3.1.1", + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/modifiers": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/@dnd-kit/modifiers/-/modifiers-6.0.1.tgz", + "integrity": "sha512-rbxcsg3HhzlcMHVHWDuh9LCjpOVAgqbV78wLGI8tziXY3+qcMQ61qVXIvNKQFuhj75dSfD+o+PYZQ/NUk2A23A==", + "dependencies": { + "@dnd-kit/utilities": "^3.2.1", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@dnd-kit/core": "^6.0.6", + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/sortable": { + "version": "7.0.2", + "resolved": "https://registry.npmmirror.com/@dnd-kit/sortable/-/sortable-7.0.2.tgz", + "integrity": "sha512-wDkBHHf9iCi1veM834Gbk1429bd4lHX4RpAwT0y2cHLf246GAvU2sVw/oxWNpPKQNQRQaeGXhAVgrOl1IT+iyA==", + "dependencies": { + "@dnd-kit/utilities": "^3.2.0", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@dnd-kit/core": "^6.0.7", + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/utilities": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/@dnd-kit/utilities/-/utilities-3.2.2.tgz", + "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/@emotion/hash": { "version": "0.8.0", "resolved": "https://registry.npmmirror.com/@emotion/hash/-/hash-0.8.0.tgz", @@ -3297,6 +3652,19 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@umijs/route-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/@umijs/route-utils/-/route-utils-4.0.1.tgz", + "integrity": "sha512-+1ixf1BTOLuH+ORb4x8vYMPeIt38n9q0fJDwhv9nSxrV46mxbLF0nmELIo9CKQB2gHfuC4+hww6xejJ6VYnBHQ==" + }, + "node_modules/@umijs/use-params": { + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/@umijs/use-params/-/use-params-1.0.9.tgz", + "integrity": "sha512-QlN0RJSBVQBwLRNxbxjQ5qzqYIGn+K7USppMoIOVlf7fxXHsnQZ2bEsa6Pm74bt6DVQxpUE8HqvdStn6Y9FV1w==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -3343,6 +3711,14 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/add-dom-event-listener": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/add-dom-event-listener/-/add-dom-event-listener-1.1.0.tgz", + "integrity": "sha512-WCxx1ixHT0GQU9hb0KI/mhgRQhnU+U3GvwY6ZvVjYq8rsihIGoaIOUbY0yMPBxLH5MDtr0kz3fisWGNcbWW7Jw==", + "dependencies": { + "object-assign": "4.x" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", @@ -3704,6 +4080,11 @@ "resolved": "https://registry.npmmirror.com/classnames/-/classnames-2.5.1.tgz", "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmmirror.com/clsx/-/clsx-2.1.1.tgz", @@ -5321,6 +5702,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -5510,6 +5901,11 @@ "node": ">= 6" } }, + "node_modules/omit.js": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/omit.js/-/omit.js-2.0.2.tgz", + "integrity": "sha512-hJmu9D+bNB40YpL9jYebQl4lsTW6yEHRTroJzNLqQJYHm7c+NQnJGfZmIWh8S3q3KoaxV1aLhV6B3+0N0/kyJg==" + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", @@ -5639,6 +6035,14 @@ "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" }, + "node_modules/path-to-regexp": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-8.0.0.tgz", + "integrity": "sha512-GAWaqWlTjYK/7SVpIUA6CTxmcg65SP30sbjdCvyYReosRkk7Z/LyHWwkK3Vu0FcIi0FNTADUs4eh1AsU5s10cg==", + "engines": { + "node": ">=16" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmmirror.com/path-type/-/path-type-4.0.0.tgz", @@ -5890,6 +6294,21 @@ "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmmirror.com/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", @@ -6553,6 +6972,11 @@ "resolved": "https://registry.npmmirror.com/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.14.2.tgz", @@ -6659,6 +7083,14 @@ } } }, + "node_modules/reactcss": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/reactcss/-/reactcss-1.2.3.tgz", + "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==", + "dependencies": { + "lodash": "^4.0.1" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz", @@ -6814,6 +7246,14 @@ "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "engines": { + "node": ">=10" + } + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.23.2.tgz", @@ -6847,6 +7287,11 @@ "resolved": "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz", "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", @@ -7070,6 +7515,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swr": { + "version": "2.2.5", + "resolved": "https://registry.npmmirror.com/swr/-/swr-2.2.5.tgz", + "integrity": "sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==", + "dependencies": { + "client-only": "^0.0.1", + "use-sync-external-store": "^1.2.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/synckit": { "version": "0.9.2", "resolved": "https://registry.npmmirror.com/synckit/-/synckit-0.9.2.tgz", @@ -7172,6 +7629,11 @@ "node": ">=12.22" } }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==" + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -7356,6 +7818,14 @@ } } }, + "node_modules/use-sync-external-store": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", + "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -7440,6 +7910,14 @@ "node": ">=0.10.0" } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/ui/package.json b/ui/package.json index eb6b0da7..69387634 100644 --- a/ui/package.json +++ b/ui/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "@ant-design/pro-components": "^2.8.2", "@hookform/resolvers": "^3.9.0", "@radix-ui/react-accordion": "^1.2.0", "@radix-ui/react-alert-dialog": "^1.1.1", diff --git a/ui/src/api/notify.ts b/ui/src/api/notify.ts index 52400b32..98a579c8 100644 --- a/ui/src/api/notify.ts +++ b/ui/src/api/notify.ts @@ -1,7 +1,7 @@ -import { getPb } from "@/repository/api"; +import { getPocketBase } from "@/repository/pocketbase"; export const notifyTest = async (channel: string) => { - const pb = getPb(); + const pb = getPocketBase(); const resp = await pb.send("/api/notify/test", { method: "POST", diff --git a/ui/src/api/statistics.ts b/ui/src/api/statistics.ts index 11b84d27..56a0471f 100644 --- a/ui/src/api/statistics.ts +++ b/ui/src/api/statistics.ts @@ -1,7 +1,7 @@ -import { getPb } from "@/repository/api"; +import { getPocketBase } from "@/repository/pocketbase"; export const get = async () => { - const pb = getPb(); + const pb = getPocketBase(); const resp = await pb.send("/api/statistics/get", { method: "GET", diff --git a/ui/src/api/workflow.ts b/ui/src/api/workflow.ts index f639e9b9..9fcf94a4 100644 --- a/ui/src/api/workflow.ts +++ b/ui/src/api/workflow.ts @@ -1,7 +1,7 @@ -import { getPb } from "@/repository/api"; +import { getPocketBase } from "@/repository/pocketbase"; export const run = async (id: string) => { - const pb = getPb(); + const pb = getPocketBase(); const resp = await pb.send("/api/workflow/run", { method: "POST", diff --git a/ui/src/i18n/locales/en/nls.common.json b/ui/src/i18n/locales/en/nls.common.json index 559557b4..fd5aa42b 100644 --- a/ui/src/i18n/locales/en/nls.common.json +++ b/ui/src/i18n/locales/en/nls.common.json @@ -30,6 +30,7 @@ "common.text.updated_at": "Updated At", "common.text.operations": "Operations", "common.text.nodata": "No data available", + "common.text.request_error": "Request error", "common.menu.settings": "Settings", "common.menu.logout": "Logout", diff --git a/ui/src/i18n/locales/en/nls.domain.json b/ui/src/i18n/locales/en/nls.domain.json index 068aec7a..8d6e042f 100644 --- a/ui/src/i18n/locales/en/nls.domain.json +++ b/ui/src/i18n/locales/en/nls.domain.json @@ -40,7 +40,7 @@ "domain.application.form.key_algorithm.label": "Certificate Key Algorithm (Default: RSA2048)", "domain.application.form.key_algorithm.placeholder": "Please select certificate key algorithm", "domain.application.form.timeout.label": "DNS Propagation Timeout (Seconds)", - "domain.application.form.timeoue.placeholder": "Please enter maximum waiting time for DNS propagation", + "domain.application.form.timeout.placeholder": "Please enter maximum waiting time for DNS propagation", "domain.application.form.disable_follow_cname.label": "Disable DNS CNAME following", "domain.application.form.disable_follow_cname.tips": "This option will disable Acme DNS authentication CNAME follow. If you don't understand this option, just keep it by default. ", "domain.application.form.disable_follow_cname.tips_link": "Learn more", diff --git a/ui/src/i18n/locales/en/nls.workflow.json b/ui/src/i18n/locales/en/nls.workflow.json index d85c7ed7..ba27b49e 100644 --- a/ui/src/i18n/locales/en/nls.workflow.json +++ b/ui/src/i18n/locales/en/nls.workflow.json @@ -20,15 +20,15 @@ "workflow.props.description.placeholder": "Please enter description", "workflow.props.executionMethod": "Execution Method", "workflow.props.enabled": "Enabled", - "workflow.props.created": "Created", - "workflow.props.updated": "Updated", + "workflow.props.createdAt": "Created", + "workflow.props.updatedAt": "Updated", "workflow.action": "Action", "workflow.action.edit": "Edit", "workflow.action.create": "Create Workflow", "workflow.action.delete.alert.title": "Delete Workflow", - "workflow.action.delete.alert.description": "Are you sure you want to delete this workflow?", + "workflow.action.delete.alert.content": "Are you sure you want to delete this workflow?", "workflow.history.page.title": "Logs", "workflow.history.props.state": "State", @@ -68,4 +68,3 @@ "workflow.node.notify.form.channel.placeholder": "Please select channel", "workflow.node.notify.form.settingChannel.label": "Setting Channel" } - diff --git a/ui/src/i18n/locales/zh/nls.common.json b/ui/src/i18n/locales/zh/nls.common.json index 691e9a34..760edc0d 100644 --- a/ui/src/i18n/locales/zh/nls.common.json +++ b/ui/src/i18n/locales/zh/nls.common.json @@ -30,6 +30,7 @@ "common.text.updated_at": "更新时间", "common.text.operations": "操作", "common.text.nodata": "暂无数据", + "common.text.request_error": "请求错误", "common.menu.settings": "系统设置", "common.menu.logout": "退出登录", diff --git a/ui/src/i18n/locales/zh/nls.domain.json b/ui/src/i18n/locales/zh/nls.domain.json index 36f6e44d..743c83a3 100644 --- a/ui/src/i18n/locales/zh/nls.domain.json +++ b/ui/src/i18n/locales/zh/nls.domain.json @@ -40,7 +40,7 @@ "domain.application.form.key_algorithm.label": "数字证书算法(默认:RSA2048)", "domain.application.form.key_algorithm.placeholder": "请选择数字证书算法", "domain.application.form.timeout.label": "DNS 传播检查超时时间(单位:秒)", - "domain.application.form.timeoue.placeholder": "请输入 DNS 传播检查超时时间", + "domain.application.form.timeout.placeholder": "请输入 DNS 传播检查超时时间", "domain.application.form.disable_follow_cname.label": "禁用 DNS CNAME 跟随", "domain.application.form.disable_follow_cname.tips": "该选项将禁用 Acme DNS 认证 CNAME 跟随,如果你不了解此选项保持默认即可,", "domain.application.form.disable_follow_cname.tips_link": "了解更多", diff --git a/ui/src/i18n/locales/zh/nls.workflow.json b/ui/src/i18n/locales/zh/nls.workflow.json index a870fae8..915d628e 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.json +++ b/ui/src/i18n/locales/zh/nls.workflow.json @@ -20,15 +20,15 @@ "workflow.props.description.placeholder": "请输入描述", "workflow.props.executionMethod": "执行方式", "workflow.props.enabled": "是否启用", - "workflow.props.created": "创建时间", - "workflow.props.updated": "更新时间", + "workflow.props.createdAt": "创建时间", + "workflow.props.updatedAt": "更新时间", "workflow.action": "操作", "workflow.action.edit": "编辑", - "workflow.action.create": "创建工作流", + "workflow.action.create": "新建工作流", "workflow.action.delete.alert.title": "删除工作流", - "workflow.action.delete.alert.description": "确定要删除此工作流吗?", + "workflow.action.delete.alert.content": "确定要删除此工作流吗?", "workflow.history.page.title": "日志", "workflow.history.props.state": "状态", @@ -68,4 +68,3 @@ "workflow.node.notify.form.channel.placeholder": "请选择推送渠道", "workflow.node.notify.form.settingChannel.label": "设置推送渠道" } - diff --git a/ui/src/main.tsx b/ui/src/main.tsx index 79332280..97fcfd1e 100644 --- a/ui/src/main.tsx +++ b/ui/src/main.tsx @@ -1,7 +1,7 @@ import React from "react"; import ReactDOM from "react-dom/client"; import { RouterProvider } from "react-router-dom"; -import { ConfigProvider } from "antd"; +import { App, ConfigProvider } from "antd"; import AntdLocaleZhCN from "antd/locale/zh_CN"; import "dayjs/locale/zh-cn"; @@ -13,10 +13,19 @@ import "./global.css"; // TODO: antd i18n ReactDOM.createRoot(document.getElementById("root")!).render( - - - - - + + + + + + + ); diff --git a/ui/src/pages/DashboardLayout.tsx b/ui/src/pages/DashboardLayout.tsx index 1d9c5c04..74f4759c 100644 --- a/ui/src/pages/DashboardLayout.tsx +++ b/ui/src/pages/DashboardLayout.tsx @@ -8,7 +8,7 @@ import { Button } from "@/components/ui/button"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"; import { cn } from "@/lib/utils"; -import { getPb } from "@/repository/api"; +import { getPocketBase } from "@/repository/pocketbase"; import { ConfigProvider } from "@/providers/config"; import Version from "@/components/certimate/Version"; @@ -18,7 +18,7 @@ export default function Dashboard() { const location = useLocation(); const { t } = useTranslation(); - if (!getPb().authStore.isValid || !getPb().authStore.isAdmin) { + if (!getPocketBase().authStore.isValid || !getPocketBase().authStore.isAdmin) { return ; } @@ -31,7 +31,7 @@ export default function Dashboard() { }; const handleLogoutClick = () => { - getPb().authStore.clear(); + getPocketBase().authStore.clear(); navigate("/login"); }; @@ -57,7 +57,10 @@ export default function Dashboard() { {t("dashboard.page.title")} - + {t("workflow.page.title")} @@ -103,8 +106,8 @@ export default function Dashboard() { {t("dashboard.page.title")} {t("workflow.page.title")} diff --git a/ui/src/pages/LoginLayout.tsx b/ui/src/pages/LoginLayout.tsx index 8a9aa7a4..d8a17702 100644 --- a/ui/src/pages/LoginLayout.tsx +++ b/ui/src/pages/LoginLayout.tsx @@ -1,10 +1,10 @@ import { Navigate, Outlet } from "react-router-dom"; import Version from "@/components/certimate/Version"; -import { getPb } from "@/repository/api"; +import { getPocketBase } from "@/repository/pocketbase"; const LoginLayout = () => { - if (getPb().authStore.isValid && getPb().authStore.isAdmin) { + if (getPocketBase().authStore.isValid && getPocketBase().authStore.isAdmin) { return ; } diff --git a/ui/src/pages/dashboard/Dashboard.tsx b/ui/src/pages/dashboard/Dashboard.tsx index 19c88f05..8798e371 100644 --- a/ui/src/pages/dashboard/Dashboard.tsx +++ b/ui/src/pages/dashboard/Dashboard.tsx @@ -101,7 +101,7 @@ const Dashboard = () => {
{statistic?.workflowTotal ? ( - + {statistic?.workflowTotal} ) : ( @@ -122,7 +122,7 @@ const Dashboard = () => {
{statistic?.workflowEnabled ? ( - + {statistic?.workflowEnabled} ) : ( diff --git a/ui/src/pages/login/Login.tsx b/ui/src/pages/login/Login.tsx index eff5fd9d..b3bafb04 100644 --- a/ui/src/pages/login/Login.tsx +++ b/ui/src/pages/login/Login.tsx @@ -8,7 +8,7 @@ import { Button } from "@/components/ui/button"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { getErrMessage } from "@/lib/error"; -import { getPb } from "@/repository/api"; +import { getPocketBase } from "@/repository/pocketbase"; const formSchema = z.object({ username: z.string().email({ @@ -33,7 +33,7 @@ const Login = () => { // 2. Define a submit handler. const onSubmit = async (values: z.infer) => { try { - await getPb().admins.authWithPassword(values.username, values.password); + await getPocketBase().admins.authWithPassword(values.username, values.password); navigage("/"); } catch (e) { const message = getErrMessage(e); diff --git a/ui/src/pages/setting/Account.tsx b/ui/src/pages/setting/Account.tsx index 0bc55263..c12b6d94 100644 --- a/ui/src/pages/setting/Account.tsx +++ b/ui/src/pages/setting/Account.tsx @@ -10,7 +10,7 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from " import { Input } from "@/components/ui/input"; import { useToast } from "@/components/ui/use-toast"; import { getErrMessage } from "@/lib/error"; -import { getPb } from "@/repository/api"; +import { getPocketBase } from "@/repository/pocketbase"; const formSchema = z.object({ email: z.string().email("settings.account.email.errmsg.invalid"), @@ -26,17 +26,17 @@ const Account = () => { const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { - email: getPb().authStore.model?.email, + email: getPocketBase().authStore.model?.email, }, }); const onSubmit = async (values: z.infer) => { try { - await getPb().admins.update(getPb().authStore.model?.id, { + await getPocketBase().admins.update(getPocketBase().authStore.model?.id, { email: values.email, }); - getPb().authStore.clear(); + getPocketBase().authStore.clear(); toast({ title: t("settings.account.email.changed.message"), description: t("settings.account.relogin.message"), diff --git a/ui/src/pages/setting/Password.tsx b/ui/src/pages/setting/Password.tsx index 7bd7371d..0868ecbb 100644 --- a/ui/src/pages/setting/Password.tsx +++ b/ui/src/pages/setting/Password.tsx @@ -9,7 +9,7 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from " import { Input } from "@/components/ui/input"; import { useToast } from "@/components/ui/use-toast"; import { getErrMessage } from "@/lib/error"; -import { getPb } from "@/repository/api"; +import { getPocketBase } from "@/repository/pocketbase"; const formSchema = z .object({ @@ -44,19 +44,19 @@ const Password = () => { const onSubmit = async (values: z.infer) => { try { - await getPb().admins.authWithPassword(getPb().authStore.model?.email, values.oldPassword); + await getPocketBase().admins.authWithPassword(getPocketBase().authStore.model?.email, values.oldPassword); } catch (e) { const message = getErrMessage(e); form.setError("oldPassword", { message }); } try { - await getPb().admins.update(getPb().authStore.model?.id, { + await getPocketBase().admins.update(getPocketBase().authStore.model?.id, { password: values.newPassword, passwordConfirm: values.confirmPassword, }); - getPb().authStore.clear(); + getPocketBase().authStore.clear(); toast({ title: t("settings.password.changed.message"), description: t("settings.account.relogin.message"), diff --git a/ui/src/pages/workflow/WorkflowList.tsx b/ui/src/pages/workflow/WorkflowList.tsx new file mode 100644 index 00000000..6451698a --- /dev/null +++ b/ui/src/pages/workflow/WorkflowList.tsx @@ -0,0 +1,248 @@ +import { useEffect, useState } from "react"; +import { useNavigate, useSearchParams } from "react-router-dom"; +import { useTranslation } from "react-i18next"; +import { Button, Modal, notification, Space, Switch, Table, Tooltip, Typography, type TableProps } from "antd"; +import { PageHeader } from "@ant-design/pro-components"; +import { Pencil as PencilIcon, Plus as PlusIcon, Trash2 as Trash2Icon } from "lucide-react"; + +import { Workflow as WorkflowType } from "@/domain/workflow"; +import { list as listWorkflow, remove as removeWorkflow, save as saveWorkflow, type WorkflowListReq } from "@/repository/workflow"; + +const WorkflowList = () => { + const navigate = useNavigate(); + + const { t } = useTranslation(); + + const [modalApi, ModelContextHolder] = Modal.useModal(); + const [notificationApi, NotificationContextHolder] = notification.useNotification(); + + const [loading, setLoading] = useState(false); + + const [searchParams] = useSearchParams(); + + const tableColumns: TableProps["columns"] = [ + { + key: "$index", + align: "center", + title: "", + width: 50, + render: (_, __, index) => (page - 1) * pageSize + index + 1, + }, + { + key: "name", + title: t("common.text.name"), + render: (_, record) => ( + + {record.name} + + {record.description} + + + ), + }, + { + key: "type", + title: t("workflow.props.executionMethod"), + render: (_, record) => { + const method = record.type; + if (!method) { + return "-"; + } else if (method === "manual") { + return {t("workflow.node.start.form.executionMethod.options.manual")}; + } else if (method === "auto") { + return ( + + {t("workflow.node.start.form.executionMethod.options.auto")} + {record.crontab ?? ""} + + ); + } + }, + }, + { + key: "enabled", + title: t("workflow.props.enabled"), + render: (_, record) => { + const enabled = record.enabled; + return ( + <> + { + handleEnabledChange(record.id); + }} + /> + + ); + }, + }, + { + key: "lastExecutedAt", + title: "最近执行状态", + render: () => { + // TODO: 最近执行状态 + return <>TODO; + }, + }, + { + key: "createdAt", + title: t("common.text.created_at"), + ellipsis: true, + render: (_, record) => { + return new Date(record.created!).toLocaleString(); + }, + }, + { + key: "updatedAt", + title: t("common.text.updated_at"), + ellipsis: true, + render: (_, record) => { + return new Date(record.updated!).toLocaleString(); + }, + }, + { + key: "$operations", + align: "end", + width: 100, + render: (_, record) => ( + + + , + ]} + /> + + + columns={tableColumns} + dataSource={tableData} + rowKey={(record) => record.id} + loading={loading} + pagination={{ + current: page, + pageSize: pageSize, + total: tableTotal, + onChange: (page, pageSize) => { + setPage(page); + setPageSize(pageSize); + }, + onShowSizeChange: (page, pageSize) => { + setPage(page); + setPageSize(pageSize); + }, + }} + /> + + {ModelContextHolder} + {NotificationContextHolder} + + ); +}; + +export default WorkflowList; diff --git a/ui/src/pages/workflow/index.tsx b/ui/src/pages/workflow/index.tsx deleted file mode 100644 index bb8460ce..00000000 --- a/ui/src/pages/workflow/index.tsx +++ /dev/null @@ -1,224 +0,0 @@ -import { Button } from "@/components/ui/button"; -import { MoreHorizontal, Plus } from "lucide-react"; -import { useNavigate, useSearchParams } from "react-router-dom"; -import { ColumnDef } from "@tanstack/react-table"; -import { Workflow as WorkflowType } from "@/domain/workflow"; -import { DataTable } from "@/components/workflow/DataTable"; -import { useState } from "react"; -import { list, remove, save, WorkflowListReq } from "@/repository/workflow"; -import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; -import { Switch } from "@/components/ui/switch"; - -import { useTranslation } from "react-i18next"; -import CustomAlertDialog from "@/components/workflow/CustomAlertDialog"; - -const Workflow = () => { - const navigate = useNavigate(); - - const [data, setData] = useState([]); - const [pageCount, setPageCount] = useState(0); - - const { t } = useTranslation(); - - const [alertOpen, setAlertOpen] = useState(false); - - const [alertProps, setAlertProps] = useState<{ - title: string; - description: string; - onConfirm: () => void; - }>(); - - const [searchParams] = useSearchParams(); - - const fetchData = async (page: number, pageSize?: number) => { - const state = searchParams.get("state"); - const req: WorkflowListReq = { page: page, perPage: pageSize }; - if (state && state == "enabled") { - req.enabled = true; - } - const resp = await list(req); - setData(resp.items); - setPageCount(resp.totalPages); - }; - - const columns: ColumnDef[] = [ - { - accessorKey: "name", - header: t("workflow.props.name"), - cell: ({ row }) => { - let name: string = row.getValue("name"); - if (!name) { - name = t("workflow.props.name.default"); - } - return
{name}
; - }, - }, - { - accessorKey: "description", - header: t("workflow.props.description"), - cell: ({ row }) => { - let description: string = row.getValue("description"); - if (!description) { - description = "-"; - } - return
{description}
; - }, - }, - { - accessorKey: "type", - header: t("workflow.props.executionMethod"), - cell: ({ row }) => { - const method = row.getValue("type"); - if (!method) { - return "-"; - } else if (method === "manual") { - return t("workflow.node.start.form.executionMethod.options.manual"); - } else if (method === "auto") { - const crontab: string = row.original.crontab ?? ""; - return ( -
-
{t("workflow.node.start.form.executionMethod.options.auto")}
-
{crontab}
-
- ); - } - }, - }, - { - accessorKey: "enabled", - header: t("workflow.props.enabled"), - cell: ({ row }) => { - const enabled: boolean = row.getValue("enabled"); - - return ( - <> - { - handleCheckedChange(row.original.id ?? ""); - }} - onClick={(e) => { - e.stopPropagation(); - }} - /> - - ); - }, - }, - { - accessorKey: "created", - header: t("workflow.props.created"), - cell: ({ row }) => { - const date: string = row.getValue("created"); - return new Date(date).toLocaleString(); - }, - }, - { - accessorKey: "updated", - header: t("workflow.props.updated"), - cell: ({ row }) => { - const date: string = row.getValue("updated"); - return new Date(date).toLocaleString(); - }, - }, - - { - id: "actions", - cell: ({ row }) => { - const workflow = row.original; - return ( - - - - - - {t("workflow.action")} - { - e.stopPropagation(); - navigate(`/workflow/detail?id=${workflow.id}`); - }} - > - {t("workflow.action.edit")} - - { - e.stopPropagation(); - handleDeleteClick(workflow.id ?? ""); - }} - > - {t("common.delete")} - - - - ); - }, - }, - ]; - - const handleCheckedChange = async (id: string) => { - const resp = await save({ id, enabled: !data.find((item) => item.id === id)?.enabled }); - if (resp) { - setData((prev) => { - return prev.map((item) => { - if (item.id === id) { - return resp; - } - return item; - }); - }); - } - }; - - const handleDeleteClick = (id: string) => { - setAlertProps({ - title: t("workflow.action.delete.alert.title"), - description: t("workflow.action.delete.alert.description"), - onConfirm: async () => { - const resp = await remove(id); - if (resp) { - setData((prev) => { - return prev.filter((item) => item.id !== id); - }); - } - }, - }); - setAlertOpen(true); - }; - const handleCreateClick = () => { - navigate("/workflow/detail"); - }; - - const handleRowClick = (id: string) => { - navigate(`/workflow/detail?id=${id}`); - }; - return ( - <> -
-
{t("workflow.page.title")}
- -
- -
- -
- - - - ); -}; - -export default Workflow; diff --git a/ui/src/repository/access.ts b/ui/src/repository/access.ts index fb942c95..0f82ebcd 100644 --- a/ui/src/repository/access.ts +++ b/ui/src/repository/access.ts @@ -1,10 +1,10 @@ import moment from "moment"; import { Access } from "@/domain/access"; -import { getPb } from "./api"; +import { getPocketBase } from "./pocketbase"; export const list = async () => { - return await getPb().collection("access").getFullList({ + return await getPocketBase().collection("access").getFullList({ sort: "-created", filter: "deleted = null", }); @@ -12,12 +12,12 @@ export const list = async () => { export const save = async (data: Access) => { if (data.id) { - return await getPb().collection("access").update(data.id, data); + return await getPocketBase().collection("access").update(data.id, data); } - return await getPb().collection("access").create(data); + return await getPocketBase().collection("access").create(data); }; export const remove = async (data: Access) => { data.deleted = moment.utc().format("YYYY-MM-DD HH:mm:ss"); - return await getPb().collection("access").update(data.id, data); + return await getPocketBase().collection("access").update(data.id, data); }; diff --git a/ui/src/repository/certificate.ts b/ui/src/repository/certificate.ts index 4f04f4c0..8e48b391 100644 --- a/ui/src/repository/certificate.ts +++ b/ui/src/repository/certificate.ts @@ -1,5 +1,5 @@ import { Certificate } from "@/domain/certificate"; -import { getPb } from "./api"; +import { getPocketBase } from "./pocketbase"; import { RecordListOptions } from "pocketbase"; import { getTimeAfter } from "@/lib/time"; @@ -10,7 +10,7 @@ type CertificateListReq = { }; export const list = async (req: CertificateListReq) => { - const pb = getPb(); + const pb = getPocketBase(); let page = 1; if (req.page) { diff --git a/ui/src/repository/deployment.ts b/ui/src/repository/deployment.ts index 46b14b60..277d0f48 100644 --- a/ui/src/repository/deployment.ts +++ b/ui/src/repository/deployment.ts @@ -1,5 +1,5 @@ import { Deployment, DeploymentListReq } from "@/domain/deployment"; -import { getPb } from "./api"; +import { getPocketBase } from "./pocketbase"; export const list = async (req: DeploymentListReq) => { let page = 1; @@ -17,7 +17,7 @@ export const list = async (req: DeploymentListReq) => { filter = `domain="${req.domain}"`; } - return await getPb().collection("deployments").getList(page, perPage, { + return await getPocketBase().collection("deployments").getList(page, perPage, { filter: filter, sort: "-deployedAt", expand: "domain", diff --git a/ui/src/repository/domains.ts b/ui/src/repository/domains.ts index 15e796d7..7940f35d 100644 --- a/ui/src/repository/domains.ts +++ b/ui/src/repository/domains.ts @@ -1,6 +1,6 @@ import { getTimeAfter } from "@/lib/time"; import { Domain } from "@/domain/domain"; -import { getPb } from "./api"; +import { getPocketBase } from "./pocketbase"; type DomainListReq = { domain?: string; @@ -10,7 +10,7 @@ type DomainListReq = { }; export const list = async (req: DomainListReq) => { - const pb = getPb(); + const pb = getPocketBase(); let page = 1; if (req.page) { @@ -43,24 +43,24 @@ export const list = async (req: DomainListReq) => { }; export const get = async (id: string) => { - const response = await getPb().collection("domains").getOne(id); + const response = await getPocketBase().collection("domains").getOne(id); return response; }; export const save = async (data: Domain) => { if (data.id) { - return await getPb().collection("domains").update(data.id, data); + return await getPocketBase().collection("domains").update(data.id, data); } - return await getPb().collection("domains").create(data); + return await getPocketBase().collection("domains").create(data); }; export const remove = async (id: string) => { - return await getPb().collection("domains").delete(id); + return await getPocketBase().collection("domains").delete(id); }; type Callback = (data: Domain) => void; export const subscribeId = (id: string, callback: Callback) => { - return getPb() + return getPocketBase() .collection("domains") .subscribe( id, @@ -76,5 +76,5 @@ export const subscribeId = (id: string, callback: Callback) => { }; export const unsubscribeId = (id: string) => { - getPb().collection("domains").unsubscribe(id); + getPocketBase().collection("domains").unsubscribe(id); }; diff --git a/ui/src/repository/api.ts b/ui/src/repository/pocketbase.ts similarity index 84% rename from ui/src/repository/api.ts rename to ui/src/repository/pocketbase.ts index 2118764c..f705ae6f 100644 --- a/ui/src/repository/api.ts +++ b/ui/src/repository/pocketbase.ts @@ -4,7 +4,7 @@ const apiDomain = import.meta.env.VITE_API_DOMAIN; console.log(apiDomain); let pb: PocketBase; -export const getPb = () => { +export const getPocketBase = () => { if (pb) return pb; pb = new PocketBase("/"); return pb; diff --git a/ui/src/repository/settings.ts b/ui/src/repository/settings.ts index 5894c50c..1b008f57 100644 --- a/ui/src/repository/settings.ts +++ b/ui/src/repository/settings.ts @@ -1,9 +1,9 @@ import { EmailsSetting, Setting } from "@/domain/settings"; -import { getPb } from "./api"; +import { getPocketBase } from "./pocketbase"; export const getEmails = async () => { try { - const resp = await getPb().collection("settings").getFirstListItem>("name='emails'"); + const resp = await getPocketBase().collection("settings").getFirstListItem>("name='emails'"); return resp; } catch (e) { return { @@ -14,7 +14,7 @@ export const getEmails = async () => { export const getSetting = async (name: string) => { try { - const resp = await getPb().collection("settings").getFirstListItem>(`name='${name}'`); + const resp = await getPocketBase().collection("settings").getFirstListItem>(`name='${name}'`); return resp; } catch (e) { const rs: Setting = { @@ -25,7 +25,7 @@ export const getSetting = async (name: string) => { }; export const update = async (setting: Setting) => { - const pb = getPb(); + const pb = getPocketBase(); let resp: Setting; if (setting.id) { resp = await pb.collection("settings").update(setting.id, setting); diff --git a/ui/src/repository/workflow.ts b/ui/src/repository/workflow.ts index 5163533d..7a7be749 100644 --- a/ui/src/repository/workflow.ts +++ b/ui/src/repository/workflow.ts @@ -1,77 +1,58 @@ -import { Workflow, WorkflowNode, WorkflowRunLog } from "@/domain/workflow"; -import { getPb } from "./api"; -import { RecordListOptions } from "pocketbase"; +import { type RecordListOptions } from "pocketbase"; + +import { type Workflow, type WorkflowNode, type WorkflowRunLog } from "@/domain/workflow"; +import { getPocketBase } from "./pocketbase"; + +export type WorkflowListReq = { + page?: number; + perPage?: number; + enabled?: boolean; +}; + +export const list = async (req: WorkflowListReq) => { + const page = req.page || 1; + const perPage = req.perPage || 10; + + const options: RecordListOptions = { sort: "-created" }; + if (req.enabled != null) { + options.filter = getPocketBase().filter("enabled={:enabled}", { enabled: req.enabled }); + } + + return await getPocketBase().collection("workflow").getList(page, perPage, options); +}; export const get = async (id: string) => { - const response = await getPb().collection("workflow").getOne(id); - return response; + return await getPocketBase().collection("workflow").getOne(id); }; export const save = async (data: Record) => { if (data.id) { - return await getPb() + return await getPocketBase() .collection("workflow") .update(data.id as string, data); } - return await getPb().collection("workflow").create(data); -}; -export type WorkflowListReq = { - page: number; - perPage?: number; - enabled?: boolean; -}; -export const list = async (req: WorkflowListReq) => { - let page = 1; - if (req.page) { - page = req.page; - } - let perPage = 10; - if (req.perPage) { - perPage = req.perPage; - } - - const options: RecordListOptions = { - sort: "-created", - }; - - if (req.enabled !== undefined) { - options.filter = getPb().filter("enabled={:enabled}", { - enabled: req.enabled, - }); - } - - const response = await getPb().collection("workflow").getList(page, perPage, options); - - return response; + return await getPocketBase().collection("workflow").create(data); }; export const remove = async (id: string) => { - return await getPb().collection("workflow").delete(id); + return await getPocketBase().collection("workflow").delete(id); }; type WorkflowLogsReq = { id: string; - page: number; + page?: number; perPage?: number; }; export const logs = async (req: WorkflowLogsReq) => { - let page = 1; - if (req.page) { - page = req.page; - } - let perPage = 10; - if (req.perPage) { - perPage = req.perPage; - } + const page = req.page || 1; + const perPage = req.perPage || 10; - const response = await getPb() + return await getPocketBase() .collection("workflow_run_log") .getList(page, perPage, { sort: "-created", - filter: getPb().filter("workflow={:workflowId}", { workflowId: req.id }), + filter: getPocketBase().filter("workflow={:workflowId}", { workflowId: req.id }), }); - - return response; }; diff --git a/ui/src/router.tsx b/ui/src/router.tsx index bd9e01dd..680e028a 100644 --- a/ui/src/router.tsx +++ b/ui/src/router.tsx @@ -10,7 +10,7 @@ import Dashboard from "./pages/dashboard/Dashboard"; import Account from "./pages/setting/Account"; import Notify from "./pages/setting/Notify"; import SSLProvider from "./pages/setting/SSLProvider"; -import Workflow from "./pages/workflow"; +import WorkflowList from "./pages/workflow/WorkflowList"; import WorkflowDetail from "./pages/workflow/WorkflowDetail"; import Certificate from "./pages/certificate"; @@ -28,8 +28,8 @@ export const router = createHashRouter([ element: , }, { - path: "/workflow", - element: , + path: "/workflows", + element: , }, { path: "/certificate",