You've already forked DataMate
Integrated Redux for state management with auth and settings slices. (#117)
* feat: Implement DatasetFileTransfer component for file selection and management * feat: Add pagination support to file list in Overview component * feat: add DatasetFileTransfer and TagManagement components - Added DatasetFileTransfer component for managing dataset files. - Introduced TagManagement component for handling tags. - Integrated Redux for state management with auth and settings slices. - Updated package.json to include @reduxjs/toolkit and react-redux dependencies. - Refactored existing components to utilize new DatasetFileTransfer and TagManagement components. - Implemented hooks for typed dispatch and selector in Redux. - Enhanced CreateKnowledgeBase and SynthesisTask components to support new features.
This commit is contained in:
120
frontend/package-lock.json
generated
120
frontend/package-lock.json
generated
@@ -8,12 +8,14 @@
|
|||||||
"name": "edatamate",
|
"name": "edatamate",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@reduxjs/toolkit": "^2.11.0",
|
||||||
"@xyflow/react": "^12.8.3",
|
"@xyflow/react": "^12.8.3",
|
||||||
"antd": "^5.27.0",
|
"antd": "^5.27.0",
|
||||||
"jssha": "^3.3.1",
|
"jssha": "^3.3.1",
|
||||||
"lucide-react": "^0.539.0",
|
"lucide-react": "^0.539.0",
|
||||||
"react": "^18.1.1",
|
"react": "^18.1.1",
|
||||||
"react-dom": "^18.1.1",
|
"react-dom": "^18.1.1",
|
||||||
|
"react-redux": "^9.2.0",
|
||||||
"react-router": "^7.8.0",
|
"react-router": "^7.8.0",
|
||||||
"recharts": "2.15.0"
|
"recharts": "2.15.0"
|
||||||
},
|
},
|
||||||
@@ -186,6 +188,7 @@
|
|||||||
"integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==",
|
"integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ampproject/remapping": "^2.2.0",
|
"@ampproject/remapping": "^2.2.0",
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.27.1",
|
||||||
@@ -1370,6 +1373,32 @@
|
|||||||
"react-dom": ">=16.9.0"
|
"react-dom": ">=16.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@reduxjs/toolkit": {
|
||||||
|
"version": "2.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.0.tgz",
|
||||||
|
"integrity": "sha512-hBjYg0aaRL1O2Z0IqWhnTLytnjDIxekmRxm1snsHjHaKVmIF1HiImWqsq+PuEbn6zdMlkIj9WofK1vR8jjx+Xw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@standard-schema/spec": "^1.0.0",
|
||||||
|
"@standard-schema/utils": "^0.3.0",
|
||||||
|
"immer": "^11.0.0",
|
||||||
|
"redux": "^5.0.1",
|
||||||
|
"redux-thunk": "^3.1.0",
|
||||||
|
"reselect": "^5.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.9.0 || ^17.0.0 || ^18 || ^19",
|
||||||
|
"react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-redux": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rolldown/pluginutils": {
|
"node_modules/@rolldown/pluginutils": {
|
||||||
"version": "1.0.0-beta.30",
|
"version": "1.0.0-beta.30",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.30.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.30.tgz",
|
||||||
@@ -1657,6 +1686,18 @@
|
|||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@standard-schema/spec": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@standard-schema/utils": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
|
||||||
|
"integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@tailwindcss/node": {
|
"node_modules/@tailwindcss/node": {
|
||||||
"version": "4.1.12",
|
"version": "4.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.12.tgz",
|
||||||
@@ -2096,6 +2137,7 @@
|
|||||||
"integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==",
|
"integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~7.10.0"
|
"undici-types": "~7.10.0"
|
||||||
}
|
}
|
||||||
@@ -2113,6 +2155,7 @@
|
|||||||
"integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==",
|
"integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
@@ -2128,6 +2171,12 @@
|
|||||||
"@types/react": "^18.0.0"
|
"@types/react": "^18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/use-sync-external-store": {
|
||||||
|
"version": "0.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
|
||||||
|
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "8.39.1",
|
"version": "8.39.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.1.tgz",
|
||||||
@@ -2174,6 +2223,7 @@
|
|||||||
"integrity": "sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg==",
|
"integrity": "sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.39.1",
|
"@typescript-eslint/scope-manager": "8.39.1",
|
||||||
"@typescript-eslint/types": "8.39.1",
|
"@typescript-eslint/types": "8.39.1",
|
||||||
@@ -2472,6 +2522,7 @@
|
|||||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@@ -2720,6 +2771,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"caniuse-lite": "^1.0.30001733",
|
"caniuse-lite": "^1.0.30001733",
|
||||||
"electron-to-chromium": "^1.5.199",
|
"electron-to-chromium": "^1.5.199",
|
||||||
@@ -3116,6 +3168,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
@@ -3204,7 +3257,8 @@
|
|||||||
"version": "1.11.13",
|
"version": "1.11.13",
|
||||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
|
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
|
||||||
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
|
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.4.1",
|
"version": "4.4.1",
|
||||||
@@ -3431,6 +3485,7 @@
|
|||||||
"integrity": "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==",
|
"integrity": "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.2.0",
|
"@eslint-community/eslint-utils": "^4.2.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
@@ -4133,6 +4188,17 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/immer": {
|
||||||
|
"version": "11.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/immer/-/immer-11.0.1.tgz",
|
||||||
|
"integrity": "sha512-naDCyggtcBWANtIrjQEajhhBEuL9b0Zg4zmlWK2CzS6xCWSE39/vvf4LqnMjUAWHBhot4m9MHCM/Z+mfWhUkiA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/immer"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/import-fresh": {
|
"node_modules/import-fresh": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
|
||||||
@@ -5959,6 +6025,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0"
|
"loose-envify": "^1.1.0"
|
||||||
},
|
},
|
||||||
@@ -5971,6 +6038,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||||
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0",
|
"loose-envify": "^1.1.0",
|
||||||
"scheduler": "^0.23.2"
|
"scheduler": "^0.23.2"
|
||||||
@@ -5985,6 +6053,30 @@
|
|||||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/react-redux": {
|
||||||
|
"version": "9.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
||||||
|
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/use-sync-external-store": "^0.0.6",
|
||||||
|
"use-sync-external-store": "^1.4.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^18.2.25 || ^19",
|
||||||
|
"react": "^18.0 || ^19",
|
||||||
|
"redux": "^5.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"redux": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-refresh": {
|
"node_modules/react-refresh": {
|
||||||
"version": "0.17.0",
|
"version": "0.17.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
|
||||||
@@ -6093,6 +6185,28 @@
|
|||||||
"decimal.js-light": "^2.4.1"
|
"decimal.js-light": "^2.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/redux": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/redux-thunk": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"redux": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/reselect": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/resize-observer-polyfill": {
|
"node_modules/resize-observer-polyfill": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
|
||||||
@@ -6660,6 +6774,7 @@
|
|||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -6763,6 +6878,7 @@
|
|||||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -6930,6 +7046,7 @@
|
|||||||
"integrity": "sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==",
|
"integrity": "sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"fdir": "^6.4.6",
|
"fdir": "^6.4.6",
|
||||||
@@ -7020,6 +7137,7 @@
|
|||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,12 +11,14 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@reduxjs/toolkit": "^2.11.0",
|
||||||
"@xyflow/react": "^12.8.3",
|
"@xyflow/react": "^12.8.3",
|
||||||
"antd": "^5.27.0",
|
"antd": "^5.27.0",
|
||||||
"jssha": "^3.3.1",
|
"jssha": "^3.3.1",
|
||||||
"lucide-react": "^0.539.0",
|
"lucide-react": "^0.539.0",
|
||||||
"react": "^18.1.1",
|
"react": "^18.1.1",
|
||||||
"react-dom": "^18.1.1",
|
"react-dom": "^18.1.1",
|
||||||
|
"react-redux": "^9.2.0",
|
||||||
"react-router": "^7.8.0",
|
"react-router": "^7.8.0",
|
||||||
"recharts": "2.15.0"
|
"recharts": "2.15.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import {
|
|||||||
} from "@/pages/DataManagement/dataset.api";
|
} from "@/pages/DataManagement/dataset.api";
|
||||||
import { formatBytes } from "@/utils/unit";
|
import { formatBytes } from "@/utils/unit";
|
||||||
import { useDebouncedEffect } from "@/hooks/useDebouncedEffect";
|
import { useDebouncedEffect } from "@/hooks/useDebouncedEffect";
|
||||||
import { DatasetFileCols as fileCols } from "../pages/KnowledgeBase/knowledge-base.const";
|
|
||||||
|
|
||||||
interface DatasetFileTransferProps
|
interface DatasetFileTransferProps
|
||||||
extends React.HTMLAttributes<HTMLDivElement> {
|
extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
@@ -22,6 +21,28 @@ interface DatasetFileTransferProps
|
|||||||
onSelectedFilesChange: (filesMap: { [key: string]: DatasetFile }) => void;
|
onSelectedFilesChange: (filesMap: { [key: string]: DatasetFile }) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fileCols = [
|
||||||
|
{
|
||||||
|
title: "所属数据集",
|
||||||
|
dataIndex: "datasetName",
|
||||||
|
key: "datasetName",
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "文件名",
|
||||||
|
dataIndex: "fileName",
|
||||||
|
key: "fileName",
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "大小",
|
||||||
|
dataIndex: "fileSize",
|
||||||
|
key: "fileSize",
|
||||||
|
ellipsis: true,
|
||||||
|
render: formatBytes,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
// Customize Table Transfer
|
// Customize Table Transfer
|
||||||
const DatasetFileTransfer: React.FC<DatasetFileTransferProps> = ({
|
const DatasetFileTransfer: React.FC<DatasetFileTransferProps> = ({
|
||||||
open,
|
open,
|
||||||
@@ -5,14 +5,18 @@ import router from "./routes/routes";
|
|||||||
import { App as AntdApp, Spin } from "antd";
|
import { App as AntdApp, Spin } from "antd";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
import TopLoadingBar from "./components/TopLoadingBar";
|
import TopLoadingBar from "./components/TopLoadingBar";
|
||||||
|
import { store } from "./store";
|
||||||
|
import { Provider } from "react-redux";
|
||||||
|
|
||||||
createRoot(document.getElementById("root")!).render(
|
createRoot(document.getElementById("root")!).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
|
<Provider store={store}>
|
||||||
<AntdApp>
|
<AntdApp>
|
||||||
<Suspense fallback={<Spin />}>
|
<Suspense fallback={<Spin />}>
|
||||||
<TopLoadingBar />
|
<TopLoadingBar />
|
||||||
<RouterProvider router={router} />
|
<RouterProvider router={router} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</AntdApp>
|
</AntdApp>
|
||||||
|
</Provider>
|
||||||
</StrictMode>
|
</StrictMode>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
UploadOutlined,
|
UploadOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import TagManager from "@/components/TagManagement";
|
import TagManager from "@/components/business/TagManagement";
|
||||||
import { Link, useNavigate } from "react-router";
|
import { Link, useNavigate } from "react-router";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { SearchControls } from "@/components/SearchControls";
|
import { SearchControls } from "@/components/SearchControls";
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
} from "antd";
|
} from "antd";
|
||||||
import { PlusOutlined } from "@ant-design/icons";
|
import { PlusOutlined } from "@ant-design/icons";
|
||||||
import { addKnowledgeBaseFilesUsingPost } from "../knowledge-base.api";
|
import { addKnowledgeBaseFilesUsingPost } from "../knowledge-base.api";
|
||||||
import DatasetFileTransfer from "../../../components/DatasetFileTransfer";
|
import DatasetFileTransfer from "@/components/business/DatasetFileTransfer";
|
||||||
import { DescriptionsItemType } from "antd/es/descriptions";
|
import { DescriptionsItemType } from "antd/es/descriptions";
|
||||||
import { DatasetFileCols } from "../knowledge-base.const";
|
import { DatasetFileCols } from "../knowledge-base.const";
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ export default function AddDataDialog({ knowledgeBase, onDataAdded }) {
|
|||||||
try {
|
try {
|
||||||
// 构造符合API要求的请求数据
|
// 构造符合API要求的请求数据
|
||||||
const requestData = {
|
const requestData = {
|
||||||
files: Object.values(selectedFilesMap),
|
files: Object.entries(selectedFilesMap),
|
||||||
processType: newKB.processType,
|
processType: newKB.processType,
|
||||||
chunkSize: Number(newKB.chunkSize), // 确保是数字类型
|
chunkSize: Number(newKB.chunkSize), // 确保是数字类型
|
||||||
overlapSize: Number(newKB.overlapSize), // 确保是数字类型
|
overlapSize: Number(newKB.overlapSize), // 确保是数字类型
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Button, Form, Input, message, Modal, Select } from "antd";
|
import { Button, Form, Input, message, Modal, Select } from "antd";
|
||||||
import { PlusOutlined } from "@ant-design/icons";
|
import { PlusOutlined } from "@ant-design/icons";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
import { queryModelListUsingGet } from "@/pages/SettingsPage/settings.apis";
|
import { queryModelListUsingGet } from "@/pages/SettingsPage/settings.apis";
|
||||||
import { ModelI } from "@/pages/SettingsPage/ModelAccess";
|
import { ModelI } from "@/pages/SettingsPage/ModelAccess";
|
||||||
import {
|
import {
|
||||||
@@ -8,6 +9,7 @@ import {
|
|||||||
updateKnowledgeBaseByIdUsingPut,
|
updateKnowledgeBaseByIdUsingPut,
|
||||||
} from "../knowledge-base.api";
|
} from "../knowledge-base.api";
|
||||||
import { KnowledgeBaseItem } from "../knowledge-base.model";
|
import { KnowledgeBaseItem } from "../knowledge-base.model";
|
||||||
|
import { showSettings } from "@/store/slices/settingsSlice";
|
||||||
|
|
||||||
export default function CreateKnowledgeBase({
|
export default function CreateKnowledgeBase({
|
||||||
isEdit,
|
isEdit,
|
||||||
@@ -25,6 +27,7 @@ export default function CreateKnowledgeBase({
|
|||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [models, setModels] = useState<ModelI[]>([]);
|
const [models, setModels] = useState<ModelI[]>([]);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const embeddingModelOptions = models
|
const embeddingModelOptions = models
|
||||||
.filter((model) => model.type === "EMBEDDING")
|
.filter((model) => model.type === "EMBEDDING")
|
||||||
@@ -130,6 +133,19 @@ export default function CreateKnowledgeBase({
|
|||||||
placeholder="请选择索引模型"
|
placeholder="请选择索引模型"
|
||||||
options={embeddingModelOptions}
|
options={embeddingModelOptions}
|
||||||
disabled={isEdit} // 编辑模式下禁用索引模型修改
|
disabled={isEdit} // 编辑模式下禁用索引模型修改
|
||||||
|
popupRender={(menu) => (
|
||||||
|
<>
|
||||||
|
{menu}
|
||||||
|
<Button
|
||||||
|
block
|
||||||
|
type="link"
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
onClick={() => dispatch(showSettings())}
|
||||||
|
>
|
||||||
|
添加模型
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import { menuItems } from "@/pages/Layout/menu";
|
|||||||
import { NavLink, useLocation, useNavigate } from "react-router";
|
import { NavLink, useLocation, useNavigate } from "react-router";
|
||||||
import TaskUpload from "./TaskUpload";
|
import TaskUpload from "./TaskUpload";
|
||||||
import SettingsPage from "../SettingsPage/SettingsPage";
|
import SettingsPage from "../SettingsPage/SettingsPage";
|
||||||
|
import { useAppSelector, useAppDispatch } from "@/store/hooks";
|
||||||
|
import { showSettings, hideSettings } from "@/store/slices/settingsSlice";
|
||||||
|
|
||||||
const AsiderAndHeaderLayout = () => {
|
const AsiderAndHeaderLayout = () => {
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
@@ -17,7 +19,8 @@ const AsiderAndHeaderLayout = () => {
|
|||||||
const [activeItem, setActiveItem] = useState<string>("");
|
const [activeItem, setActiveItem] = useState<string>("");
|
||||||
const [sidebarOpen, setSidebarOpen] = useState(true);
|
const [sidebarOpen, setSidebarOpen] = useState(true);
|
||||||
const [taskCenterVisible, setTaskCenterVisible] = useState(false);
|
const [taskCenterVisible, setTaskCenterVisible] = useState(false);
|
||||||
const [settingVisible, setSettingVisible] = useState(false);
|
const settingVisible = useAppSelector((state) => state.settings.visible);
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
// Initialize active item based on current pathname
|
// Initialize active item based on current pathname
|
||||||
const initActiveItem = () => {
|
const initActiveItem = () => {
|
||||||
@@ -140,7 +143,7 @@ const AsiderAndHeaderLayout = () => {
|
|||||||
<Button
|
<Button
|
||||||
block
|
block
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSettingVisible(true);
|
dispatch(showSettings());
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
设置
|
设置
|
||||||
@@ -167,7 +170,7 @@ const AsiderAndHeaderLayout = () => {
|
|||||||
<Button
|
<Button
|
||||||
block
|
block
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSettingVisible(true);
|
dispatch(showSettings());
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SettingOutlined />
|
<SettingOutlined />
|
||||||
@@ -181,7 +184,7 @@ const AsiderAndHeaderLayout = () => {
|
|||||||
width="100%"
|
width="100%"
|
||||||
height="100%"
|
height="100%"
|
||||||
open={settingVisible}
|
open={settingVisible}
|
||||||
onClose={() => setSettingVisible(false)}
|
onClose={() => dispatch(hideSettings())}
|
||||||
bodyStyle={{ padding: 0 }}
|
bodyStyle={{ padding: 0 }}
|
||||||
destroyOnHidden={true}
|
destroyOnHidden={true}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import type {
|
|||||||
OperatorI,
|
OperatorI,
|
||||||
} from "@/pages/OperatorMarket/operator.model";
|
} from "@/pages/OperatorMarket/operator.model";
|
||||||
import Filters from "./components/Filters";
|
import Filters from "./components/Filters";
|
||||||
import TagManagement from "@/components/TagManagement";
|
import TagManagement from "@/components/business/TagManagement";
|
||||||
import { ListView } from "./components/List";
|
import { ListView } from "./components/List";
|
||||||
import useFetchData from "@/hooks/useFetchData";
|
import useFetchData from "@/hooks/useFetchData";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import type { Dataset } from "@/pages/DataManagement/dataset.model";
|
import type { DatasetFile } from "@/pages/DataManagement/dataset.model";
|
||||||
import {
|
import {
|
||||||
Steps,
|
Steps,
|
||||||
Card,
|
Card,
|
||||||
@@ -37,7 +37,7 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { Link, useNavigate } from "react-router";
|
import { Link, useNavigate } from "react-router";
|
||||||
import { queryDatasetsUsingGet } from "../DataManagement/dataset.api";
|
import { queryDatasetsUsingGet } from "../DataManagement/dataset.api";
|
||||||
import DatasetFileTransfer from "../../components/DatasetFileTransfer";
|
import DatasetFileTransfer from "@/components/business/DatasetFileTransfer";
|
||||||
|
|
||||||
const { TextArea } = Input;
|
const { TextArea } = Input;
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ export default function SynthesisTaskCreate() {
|
|||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
const [createStep, setCreateStep] = useState(1);
|
const [createStep, setCreateStep] = useState(1);
|
||||||
const [selectedFiles, setSelectedFiles] = useState<string[]>([]);
|
const [selectedFiles, setSelectedFiles] = useState<string[]>([]);
|
||||||
const [selectedMap, setSelectedMap] = useState<Record<string, DatasetFile[]>>(
|
const [selectedMap, setSelectedMap] = useState<Record<string, DatasetFile>>(
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
const [files] = useState<File[]>([]);
|
const [files] = useState<File[]>([]);
|
||||||
@@ -318,8 +318,8 @@ export default function SynthesisTaskCreate() {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
<DatasetFileTransfer
|
<DatasetFileTransfer
|
||||||
open
|
open
|
||||||
selectedMap={selectedMap}
|
selectedFilesMap={selectedMap}
|
||||||
onSelectedChange={setSelectedMap}
|
onSelectedFilesChange={setSelectedMap}
|
||||||
/>
|
/>
|
||||||
<h2 className="font-medium text-gray-900 text-lg mt-6 mb-2">
|
<h2 className="font-medium text-gray-900 text-lg mt-6 mb-2">
|
||||||
任务配置
|
任务配置
|
||||||
|
|||||||
6
frontend/src/store/hooks.ts
Normal file
6
frontend/src/store/hooks.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { useDispatch, useSelector, TypedUseSelectorHook } from 'react-redux';
|
||||||
|
import type { RootState, AppDispatch } from './index';
|
||||||
|
|
||||||
|
// 类型化的 hooks
|
||||||
|
export const useAppDispatch = () => useDispatch<AppDispatch>();
|
||||||
|
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
|
||||||
15
frontend/src/store/index.ts
Normal file
15
frontend/src/store/index.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { configureStore } from "@reduxjs/toolkit";
|
||||||
|
import authSlice from "./slices/authSlice";
|
||||||
|
import settingsSlice from "./slices/settingsSlice";
|
||||||
|
|
||||||
|
// 创建 Store
|
||||||
|
export const store = configureStore({
|
||||||
|
reducer: {
|
||||||
|
auth: authSlice,
|
||||||
|
settings: settingsSlice,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 导出类型
|
||||||
|
export type RootState = ReturnType<typeof store.getState>;
|
||||||
|
export type AppDispatch = typeof store.dispatch;
|
||||||
75
frontend/src/store/slices/authSlice.ts
Normal file
75
frontend/src/store/slices/authSlice.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
// store/slices/authSlice.js
|
||||||
|
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
// 异步 thunk
|
||||||
|
export const loginUser = createAsyncThunk(
|
||||||
|
'auth/login',
|
||||||
|
async (credentials, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/auth/login', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(credentials),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Login failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
return rejectWithValue(error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const authSlice = createSlice({
|
||||||
|
name: 'auth',
|
||||||
|
initialState: {
|
||||||
|
user: null,
|
||||||
|
token: localStorage.getItem('token'),
|
||||||
|
isAuthenticated: false,
|
||||||
|
loading: false,
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
reducers: {
|
||||||
|
logout: (state) => {
|
||||||
|
state.user = null;
|
||||||
|
state.token = null;
|
||||||
|
state.isAuthenticated = false;
|
||||||
|
localStorage.removeItem('token');
|
||||||
|
},
|
||||||
|
clearError: (state) => {
|
||||||
|
state.error = null;
|
||||||
|
},
|
||||||
|
setToken: (state, action) => {
|
||||||
|
state.token = action.payload;
|
||||||
|
localStorage.setItem('token', action.payload);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder
|
||||||
|
.addCase(loginUser.pending, (state) => {
|
||||||
|
state.loading = true;
|
||||||
|
state.error = null;
|
||||||
|
})
|
||||||
|
.addCase(loginUser.fulfilled, (state, action) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.user = action.payload.user;
|
||||||
|
state.token = action.payload.token;
|
||||||
|
state.isAuthenticated = true;
|
||||||
|
localStorage.setItem('token', action.payload.token);
|
||||||
|
})
|
||||||
|
.addCase(loginUser.rejected, (state, action) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.error = action.payload;
|
||||||
|
state.isAuthenticated = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { logout, clearError, setToken } = authSlice.actions;
|
||||||
|
export default authSlice.reducer;
|
||||||
23
frontend/src/store/slices/settingsSlice.ts
Normal file
23
frontend/src/store/slices/settingsSlice.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// Settings Slice
|
||||||
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
const settingsSlice = createSlice({
|
||||||
|
name: "settings",
|
||||||
|
initialState: {
|
||||||
|
visible: false,
|
||||||
|
},
|
||||||
|
reducers: {
|
||||||
|
showSettings: (state) => {
|
||||||
|
state.visible = true;
|
||||||
|
},
|
||||||
|
hideSettings: (state) => {
|
||||||
|
state.visible = false;
|
||||||
|
},
|
||||||
|
toggleSettings: (state) => {
|
||||||
|
state.visible = !state.visible;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { showSettings, hideSettings, toggleSettings } = settingsSlice.actions;
|
||||||
|
export default settingsSlice.reducer;
|
||||||
Reference in New Issue
Block a user