diff --git a/web_ui/.gitignore b/web_ui/.gitignore index 5ef6a520..c77c37c5 100644 --- a/web_ui/.gitignore +++ b/web_ui/.gitignore @@ -39,3 +39,5 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +pnpm-lock.yaml \ No newline at end of file diff --git a/web_ui/.husky/pre-commit b/web_ui/.husky/pre-commit new file mode 100644 index 00000000..8eb362e8 --- /dev/null +++ b/web_ui/.husky/pre-commit @@ -0,0 +1,3 @@ +cd web_ui +pnpm lint-staged +pnpm test \ No newline at end of file diff --git a/web_ui/.lintstagedrc.json b/web_ui/.lintstagedrc.json new file mode 100644 index 00000000..f7a24c1f --- /dev/null +++ b/web_ui/.lintstagedrc.json @@ -0,0 +1,3 @@ +{ + "*.{js,jsx,ts,tsx}": ["prettier --write", "eslint --fix", "eslint"] +} \ No newline at end of file diff --git a/web_ui/.prettierrc.mjs b/web_ui/.prettierrc.mjs new file mode 100644 index 00000000..0ce90351 --- /dev/null +++ b/web_ui/.prettierrc.mjs @@ -0,0 +1,9 @@ +/** + * @see https://prettier.io/docs/configuration + * @type {import("prettier").Config} + */ +const config = { + trailingComma: "none", +}; + +export default config; \ No newline at end of file diff --git a/web_ui/package.json b/web_ui/package.json index e01e5464..09fb91e3 100644 --- a/web_ui/package.json +++ b/web_ui/package.json @@ -6,7 +6,9 @@ "dev": "next dev --turbopack", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "prepare": "cd .. && husky web_ui/.husky", + "lint-staged": "lint-staged" }, "dependencies": { "@ant-design/v5-patch-for-react-19": "^1.0.3", @@ -26,6 +28,9 @@ "@types/react-dom": "^19", "eslint": "^9", "eslint-config-next": "15.2.4", + "husky": "^9.1.7", + "lint-staged": "^15.5.1", + "prettier": "^3.5.3", "typescript": "^5" } } diff --git a/web_ui/src/app/home/bots/components/bot-form/BotForm.tsx b/web_ui/src/app/home/bots/components/bot-form/BotForm.tsx index 4ab9d608..8f5bdf29 100644 --- a/web_ui/src/app/home/bots/components/bot-form/BotForm.tsx +++ b/web_ui/src/app/home/bots/components/bot-form/BotForm.tsx @@ -11,6 +11,8 @@ import {UUID} from 'uuidjs' import DynamicFormComponent from "@/app/home/components/dynamic-form/DynamicFormComponent"; import {httpClient} from "@/app/infra/http/HttpClient"; import { Bot } from "@/app/infra/api/api-types"; +import { notification } from "antd"; + export default function BotForm({ initBotId, @@ -27,6 +29,7 @@ export default function BotForm({ const [dynamicForm] = Form.useForm(); const [adapterNameList, setAdapterNameList] = useState([]) const [dynamicFormConfigList, setDynamicFormConfigList] = useState([]) + const [isLoading, setIsLoading] = useState(false) useEffect(() => { initBotFormComponent() @@ -121,6 +124,8 @@ export default function BotForm({ // 只有通过外层固定表单验证才会走到这里,真正的提交逻辑在这里 function onDynamicFormSubmit(value: object) { + setIsLoading(true) + console.log('setloading', true) if (initBotId) { // 编辑提交 console.log('submit edit', form.getFieldsValue() ,value) @@ -135,9 +140,20 @@ export default function BotForm({ // TODO success toast console.log("update bot success", res) onFormSubmit(form.getFieldsValue()) + notification.success({ + message: "更新成功", + description: "机器人更新成功" + }) }).catch(err => { // TODO error toast - console.log("update bot error", err) + notification.error({ + message: "更新失败", + description: "机器人更新失败" + }) + }).finally(() => { + setIsLoading(false) + form.resetFields() + dynamicForm.resetFields() }) } else { // 创建提交 @@ -150,16 +166,28 @@ export default function BotForm({ } httpClient.createBot(newBot).then(res => { // TODO success toast + notification.success({ + message: "创建成功", + description: "机器人创建成功" + }) console.log(res) onFormSubmit(form.getFieldsValue()) }).catch(err => { // TODO error toast - console.log(err) + notification.error({ + message: "创建失败", + description: "机器人创建失败" + }) + }).finally(() => { + setIsLoading(false) + form.resetFields() + dynamicForm.resetFields() }) } setShowDynamicForm(false) - form.resetFields() - dynamicForm.resetFields() + console.log('setloading', false) + // TODO 刷新bot列表 + // TODO 关闭当前弹窗 Already closed @setShowDynamicForm(false)? } function handleSaveButton() { @@ -174,6 +202,7 @@ export default function BotForm({ wrapperCol={{span: 18}} layout='vertical' onFinish={handleFormFinish} + disabled={isLoading} > label={"机器人名称"} @@ -225,6 +254,7 @@ export default function BotForm({ type="primary" htmlType="button" onClick={handleSubmitButton} + loading={isLoading} > 提交 @@ -235,13 +265,14 @@ export default function BotForm({ type="primary" htmlType="submit" onClick={handleSaveButton} + loading={isLoading} > 保存 } diff --git a/web_ui/src/app/home/bots/page.tsx b/web_ui/src/app/home/bots/page.tsx index 65108e71..fdd32f59 100644 --- a/web_ui/src/app/home/bots/page.tsx +++ b/web_ui/src/app/home/bots/page.tsx @@ -5,7 +5,7 @@ import styles from "./botConfig.module.css"; import EmptyAndCreateComponent from "@/app/home/components/empty-and-create-component/EmptyAndCreateComponent"; import {useRouter} from "next/navigation"; import {BotCardVO} from "@/app/home/bots/components/bot-card/BotCardVO"; -import {Modal} from "antd"; +import {Modal, notification, Spin} from "antd"; import BotForm from "@/app/home/bots/components/bot-form/BotForm"; import BotCard from "@/app/home/bots/components/bot-card/BotCard"; import CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent" @@ -19,14 +19,18 @@ export default function BotConfigPage() { const [botList, setBotList] = useState([]) const [isEditForm, setIsEditForm] = useState(false) const [nowSelectedBotCard, setNowSelectedBotCard] = useState() + const [isLoading, setIsLoading] = useState(false) + useEffect(() => { // TODO:补齐加载转圈逻辑 + setIsLoading(true) checkHasLLM().then((hasLLM) => { if (hasLLM) { getBotList() } else { setPageShowRule(BotConfigPageShowRule.NO_LLM) + setIsLoading(false) } }) }, []) @@ -55,8 +59,15 @@ export default function BotConfigPage() { } setBotList(botList) }).catch((err) => { - // TODO error toast console.error("get bot list error", err) + // TODO HACK: need refactor to hook mode Notification, but it's not working under render + notification.error({ + message: "获取机器人列表失败", + description: err.message, + placement: "bottomRight", + }) + }).finally(() => { + setIsLoading(false) }) } @@ -78,6 +89,7 @@ export default function BotConfigPage() { } return ( +
}
+
) } diff --git a/web_ui/src/app/infra/http/HttpClient.ts b/web_ui/src/app/infra/http/HttpClient.ts index e8d51237..f930078a 100644 --- a/web_ui/src/app/infra/http/HttpClient.ts +++ b/web_ui/src/app/infra/http/HttpClient.ts @@ -6,6 +6,7 @@ import { ApiRespPluginConfig, PluginReorderElement, AsyncTaskCreatedResp, ApiRespSystemInfo, ApiRespAsyncTasks, AsyncTask, ApiRespAsyncTask, ApiRespUserToken } from '../api/api-types' +import { notification } from 'antd' type JSONValue = string | number | boolean | JSONObject | JSONArray | null interface JSONObject { [key: string]: JSONValue } @@ -111,6 +112,12 @@ class HttpClient { break case 500: // TODO 弹Toast窗 + // NOTE: move to component layer for customized message? + notification.error({ + message: "服务器错误", + description: errMessage, + placement: "bottomRight", + }) console.error('Server error:', errMessage) break }