Spaces:
Running
Running
update
Browse files- package-lock.json +141 -1
- package.json +1 -0
- src/App.css +4 -0
- src/App.tsx +12 -7
- src/api/qePrompts/hooks.ts +49 -0
- src/api/qePrompts/qePromptApi.ts +79 -0
- src/api/qePrompts/types.ts +9 -0
- src/components/common/navbar/Navbar.tsx +9 -1
- src/components/generics/button/Button.scss +1 -0
- src/components/generics/button/Button.tsx +1 -1
- src/components/pages/documentsPage/Documents.tsx +2 -2
- src/components/pages/llmConfigs/LlmConfigModal.scss +1 -1
- src/components/pages/llmConfigs/LlmConfigModal.tsx +10 -10
- src/components/pages/llmPrompts/LlmPromptModal.scss +2 -1
- src/components/pages/llmPrompts/LlmPromptModal.tsx +4 -4
- src/components/pages/qePrompts/QePromptList.scss +209 -0
- src/components/pages/qePrompts/QePromptList.tsx +124 -0
- src/components/pages/qePrompts/QePromptModal.scss +137 -0
- src/components/pages/qePrompts/QePromptModal.tsx +159 -0
- src/components/views/documents/docsList/DocsList.tsx +2 -2
package-lock.json
CHANGED
@@ -9,6 +9,7 @@
|
|
9 |
"version": "0.0.0",
|
10 |
"dependencies": {
|
11 |
"@fontsource/fira-mono": "^5.0.14",
|
|
|
12 |
"@reduxjs/toolkit": "^2.3.0",
|
13 |
"@tanstack/react-query": "^5.54.1",
|
14 |
"@tanstack/react-query-devtools": "^5.54.1",
|
@@ -1175,6 +1176,23 @@
|
|
1175 |
"@jridgewell/sourcemap-codec": "^1.4.14"
|
1176 |
}
|
1177 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1178 |
"node_modules/@nodelib/fs.scandir": {
|
1179 |
"version": "2.1.5",
|
1180 |
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
@@ -1610,6 +1628,11 @@
|
|
1610 |
"@babel/types": "^7.20.7"
|
1611 |
}
|
1612 |
},
|
|
|
|
|
|
|
|
|
|
|
1613 |
"node_modules/@types/estree": {
|
1614 |
"version": "1.0.6",
|
1615 |
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
@@ -1624,6 +1647,11 @@
|
|
1624 |
"dev": true,
|
1625 |
"license": "MIT"
|
1626 |
},
|
|
|
|
|
|
|
|
|
|
|
1627 |
"node_modules/@types/node": {
|
1628 |
"version": "22.13.0",
|
1629 |
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.0.tgz",
|
@@ -2051,6 +2079,11 @@
|
|
2051 |
"dev": true,
|
2052 |
"license": "MIT"
|
2053 |
},
|
|
|
|
|
|
|
|
|
|
|
2054 |
"node_modules/base64-js": {
|
2055 |
"version": "1.5.1",
|
2056 |
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
@@ -2273,6 +2306,15 @@
|
|
2273 |
"node": ">=0.8"
|
2274 |
}
|
2275 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2276 |
"node_modules/color-convert": {
|
2277 |
"version": "2.0.1",
|
2278 |
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
@@ -2290,9 +2332,30 @@
|
|
2290 |
"version": "1.1.4",
|
2291 |
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
2292 |
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
2293 |
-
"dev": true,
|
2294 |
"license": "MIT"
|
2295 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2296 |
"node_modules/colorjs.io": {
|
2297 |
"version": "0.5.2",
|
2298 |
"resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz",
|
@@ -3408,6 +3471,11 @@
|
|
3408 |
"url": "https://github.com/sponsors/sindresorhus"
|
3409 |
}
|
3410 |
},
|
|
|
|
|
|
|
|
|
|
|
3411 |
"node_modules/lodash.merge": {
|
3412 |
"version": "4.6.2",
|
3413 |
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
@@ -3988,6 +4056,20 @@
|
|
3988 |
"node": ">=0.10.0"
|
3989 |
}
|
3990 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3991 |
"node_modules/react-contenteditable": {
|
3992 |
"version": "3.3.7",
|
3993 |
"resolved": "https://registry.npmjs.org/react-contenteditable/-/react-contenteditable-3.3.7.tgz",
|
@@ -4191,6 +4273,22 @@
|
|
4191 |
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
4192 |
}
|
4193 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4194 |
"node_modules/react-transition-group": {
|
4195 |
"version": "4.4.5",
|
4196 |
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
@@ -4887,6 +4985,19 @@
|
|
4887 |
"simple-concat": "^1.0.0"
|
4888 |
}
|
4889 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4890 |
"node_modules/source-map": {
|
4891 |
"version": "0.5.7",
|
4892 |
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
@@ -5180,6 +5291,19 @@
|
|
5180 |
"punycode": "^2.1.0"
|
5181 |
}
|
5182 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5183 |
"node_modules/use-isomorphic-layout-effect": {
|
5184 |
"version": "1.2.0",
|
5185 |
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.0.tgz",
|
@@ -5194,6 +5318,22 @@
|
|
5194 |
}
|
5195 |
}
|
5196 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5197 |
"node_modules/use-sync-external-store": {
|
5198 |
"version": "1.4.0",
|
5199 |
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz",
|
|
|
9 |
"version": "0.0.0",
|
10 |
"dependencies": {
|
11 |
"@fontsource/fira-mono": "^5.0.14",
|
12 |
+
"@microlink/react-json-view": "^1.26.1",
|
13 |
"@reduxjs/toolkit": "^2.3.0",
|
14 |
"@tanstack/react-query": "^5.54.1",
|
15 |
"@tanstack/react-query-devtools": "^5.54.1",
|
|
|
1176 |
"@jridgewell/sourcemap-codec": "^1.4.14"
|
1177 |
}
|
1178 |
},
|
1179 |
+
"node_modules/@microlink/react-json-view": {
|
1180 |
+
"version": "1.26.1",
|
1181 |
+
"resolved": "https://registry.npmjs.org/@microlink/react-json-view/-/react-json-view-1.26.1.tgz",
|
1182 |
+
"integrity": "sha512-2H5QCYdZlJi+oN4YBiUYPPFTNh/KLCN9i9yz8NwmSkRqXSRXYtEVIRffc9L34jdopKGK/tK21SeuzXVJHQLkfQ==",
|
1183 |
+
"dependencies": {
|
1184 |
+
"react-base16-styling": "~0.9.0",
|
1185 |
+
"react-lifecycles-compat": "~3.0.4",
|
1186 |
+
"react-textarea-autosize": "~8.5.7"
|
1187 |
+
},
|
1188 |
+
"engines": {
|
1189 |
+
"node": ">=17"
|
1190 |
+
},
|
1191 |
+
"peerDependencies": {
|
1192 |
+
"react": ">= 15",
|
1193 |
+
"react-dom": ">= 15"
|
1194 |
+
}
|
1195 |
+
},
|
1196 |
"node_modules/@nodelib/fs.scandir": {
|
1197 |
"version": "2.1.5",
|
1198 |
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
|
|
1628 |
"@babel/types": "^7.20.7"
|
1629 |
}
|
1630 |
},
|
1631 |
+
"node_modules/@types/base16": {
|
1632 |
+
"version": "1.0.5",
|
1633 |
+
"resolved": "https://registry.npmjs.org/@types/base16/-/base16-1.0.5.tgz",
|
1634 |
+
"integrity": "sha512-OzOWrTluG9cwqidEzC/Q6FAmIPcnZfm8BFRlIx0+UIUqnuAmi5OS88O0RpT3Yz6qdmqObvUhasrbNsCofE4W9A=="
|
1635 |
+
},
|
1636 |
"node_modules/@types/estree": {
|
1637 |
"version": "1.0.6",
|
1638 |
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
|
|
1647 |
"dev": true,
|
1648 |
"license": "MIT"
|
1649 |
},
|
1650 |
+
"node_modules/@types/lodash": {
|
1651 |
+
"version": "4.17.16",
|
1652 |
+
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.16.tgz",
|
1653 |
+
"integrity": "sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g=="
|
1654 |
+
},
|
1655 |
"node_modules/@types/node": {
|
1656 |
"version": "22.13.0",
|
1657 |
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.0.tgz",
|
|
|
2079 |
"dev": true,
|
2080 |
"license": "MIT"
|
2081 |
},
|
2082 |
+
"node_modules/base16": {
|
2083 |
+
"version": "1.0.0",
|
2084 |
+
"resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz",
|
2085 |
+
"integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ=="
|
2086 |
+
},
|
2087 |
"node_modules/base64-js": {
|
2088 |
"version": "1.5.1",
|
2089 |
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
|
|
2306 |
"node": ">=0.8"
|
2307 |
}
|
2308 |
},
|
2309 |
+
"node_modules/color": {
|
2310 |
+
"version": "3.2.1",
|
2311 |
+
"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
|
2312 |
+
"integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
|
2313 |
+
"dependencies": {
|
2314 |
+
"color-convert": "^1.9.3",
|
2315 |
+
"color-string": "^1.6.0"
|
2316 |
+
}
|
2317 |
+
},
|
2318 |
"node_modules/color-convert": {
|
2319 |
"version": "2.0.1",
|
2320 |
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
|
|
2332 |
"version": "1.1.4",
|
2333 |
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
2334 |
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
|
|
2335 |
"license": "MIT"
|
2336 |
},
|
2337 |
+
"node_modules/color-string": {
|
2338 |
+
"version": "1.9.1",
|
2339 |
+
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
|
2340 |
+
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
|
2341 |
+
"dependencies": {
|
2342 |
+
"color-name": "^1.0.0",
|
2343 |
+
"simple-swizzle": "^0.2.2"
|
2344 |
+
}
|
2345 |
+
},
|
2346 |
+
"node_modules/color/node_modules/color-convert": {
|
2347 |
+
"version": "1.9.3",
|
2348 |
+
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
2349 |
+
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
2350 |
+
"dependencies": {
|
2351 |
+
"color-name": "1.1.3"
|
2352 |
+
}
|
2353 |
+
},
|
2354 |
+
"node_modules/color/node_modules/color-name": {
|
2355 |
+
"version": "1.1.3",
|
2356 |
+
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
2357 |
+
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
|
2358 |
+
},
|
2359 |
"node_modules/colorjs.io": {
|
2360 |
"version": "0.5.2",
|
2361 |
"resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz",
|
|
|
3471 |
"url": "https://github.com/sponsors/sindresorhus"
|
3472 |
}
|
3473 |
},
|
3474 |
+
"node_modules/lodash.curry": {
|
3475 |
+
"version": "4.1.1",
|
3476 |
+
"resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz",
|
3477 |
+
"integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA=="
|
3478 |
+
},
|
3479 |
"node_modules/lodash.merge": {
|
3480 |
"version": "4.6.2",
|
3481 |
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
|
|
4056 |
"node": ">=0.10.0"
|
4057 |
}
|
4058 |
},
|
4059 |
+
"node_modules/react-base16-styling": {
|
4060 |
+
"version": "0.9.1",
|
4061 |
+
"resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.9.1.tgz",
|
4062 |
+
"integrity": "sha512-1s0CY1zRBOQ5M3T61wetEpvQmsYSNtWEcdYzyZNxKa8t7oDvaOn9d21xrGezGAHFWLM7SHcktPuPTrvoqxSfKw==",
|
4063 |
+
"dependencies": {
|
4064 |
+
"@babel/runtime": "^7.16.7",
|
4065 |
+
"@types/base16": "^1.0.2",
|
4066 |
+
"@types/lodash": "^4.14.178",
|
4067 |
+
"base16": "^1.0.0",
|
4068 |
+
"color": "^3.2.1",
|
4069 |
+
"csstype": "^3.0.10",
|
4070 |
+
"lodash.curry": "^4.1.1"
|
4071 |
+
}
|
4072 |
+
},
|
4073 |
"node_modules/react-contenteditable": {
|
4074 |
"version": "3.3.7",
|
4075 |
"resolved": "https://registry.npmjs.org/react-contenteditable/-/react-contenteditable-3.3.7.tgz",
|
|
|
4273 |
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
4274 |
}
|
4275 |
},
|
4276 |
+
"node_modules/react-textarea-autosize": {
|
4277 |
+
"version": "8.5.9",
|
4278 |
+
"resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.9.tgz",
|
4279 |
+
"integrity": "sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==",
|
4280 |
+
"dependencies": {
|
4281 |
+
"@babel/runtime": "^7.20.13",
|
4282 |
+
"use-composed-ref": "^1.3.0",
|
4283 |
+
"use-latest": "^1.2.1"
|
4284 |
+
},
|
4285 |
+
"engines": {
|
4286 |
+
"node": ">=10"
|
4287 |
+
},
|
4288 |
+
"peerDependencies": {
|
4289 |
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
4290 |
+
}
|
4291 |
+
},
|
4292 |
"node_modules/react-transition-group": {
|
4293 |
"version": "4.4.5",
|
4294 |
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
|
|
4985 |
"simple-concat": "^1.0.0"
|
4986 |
}
|
4987 |
},
|
4988 |
+
"node_modules/simple-swizzle": {
|
4989 |
+
"version": "0.2.2",
|
4990 |
+
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
4991 |
+
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
|
4992 |
+
"dependencies": {
|
4993 |
+
"is-arrayish": "^0.3.1"
|
4994 |
+
}
|
4995 |
+
},
|
4996 |
+
"node_modules/simple-swizzle/node_modules/is-arrayish": {
|
4997 |
+
"version": "0.3.2",
|
4998 |
+
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
4999 |
+
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
|
5000 |
+
},
|
5001 |
"node_modules/source-map": {
|
5002 |
"version": "0.5.7",
|
5003 |
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
|
|
5291 |
"punycode": "^2.1.0"
|
5292 |
}
|
5293 |
},
|
5294 |
+
"node_modules/use-composed-ref": {
|
5295 |
+
"version": "1.4.0",
|
5296 |
+
"resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.4.0.tgz",
|
5297 |
+
"integrity": "sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==",
|
5298 |
+
"peerDependencies": {
|
5299 |
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
5300 |
+
},
|
5301 |
+
"peerDependenciesMeta": {
|
5302 |
+
"@types/react": {
|
5303 |
+
"optional": true
|
5304 |
+
}
|
5305 |
+
}
|
5306 |
+
},
|
5307 |
"node_modules/use-isomorphic-layout-effect": {
|
5308 |
"version": "1.2.0",
|
5309 |
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.0.tgz",
|
|
|
5318 |
}
|
5319 |
}
|
5320 |
},
|
5321 |
+
"node_modules/use-latest": {
|
5322 |
+
"version": "1.3.0",
|
5323 |
+
"resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.3.0.tgz",
|
5324 |
+
"integrity": "sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==",
|
5325 |
+
"dependencies": {
|
5326 |
+
"use-isomorphic-layout-effect": "^1.1.1"
|
5327 |
+
},
|
5328 |
+
"peerDependencies": {
|
5329 |
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
5330 |
+
},
|
5331 |
+
"peerDependenciesMeta": {
|
5332 |
+
"@types/react": {
|
5333 |
+
"optional": true
|
5334 |
+
}
|
5335 |
+
}
|
5336 |
+
},
|
5337 |
"node_modules/use-sync-external-store": {
|
5338 |
"version": "1.4.0",
|
5339 |
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz",
|
package.json
CHANGED
@@ -11,6 +11,7 @@
|
|
11 |
},
|
12 |
"dependencies": {
|
13 |
"@fontsource/fira-mono": "^5.0.14",
|
|
|
14 |
"@reduxjs/toolkit": "^2.3.0",
|
15 |
"@tanstack/react-query": "^5.54.1",
|
16 |
"@tanstack/react-query-devtools": "^5.54.1",
|
|
|
11 |
},
|
12 |
"dependencies": {
|
13 |
"@fontsource/fira-mono": "^5.0.14",
|
14 |
+
"@microlink/react-json-view": "^1.26.1",
|
15 |
"@reduxjs/toolkit": "^2.3.0",
|
16 |
"@tanstack/react-query": "^5.54.1",
|
17 |
"@tanstack/react-query-devtools": "^5.54.1",
|
src/App.css
CHANGED
@@ -11,3 +11,7 @@
|
|
11 |
padding-left: 10px;
|
12 |
font-weight: 600;
|
13 |
}
|
|
|
|
|
|
|
|
|
|
11 |
padding-left: 10px;
|
12 |
font-weight: 600;
|
13 |
}
|
14 |
+
|
15 |
+
.app-content {
|
16 |
+
margin-top: 40px;
|
17 |
+
}
|
src/App.tsx
CHANGED
@@ -1,14 +1,15 @@
|
|
1 |
-
import "./App.css";
|
2 |
-
import { Routes, Route, BrowserRouter as Router, Navigate } from "react-router-dom";
|
3 |
import Navbar from '@/components/common/navbar/Navbar';
|
4 |
-
import
|
5 |
-
import Logs from "@/components/pages/logsPage/Logs";
|
6 |
-
import Vectorization from "@/components/pages/vectorizationPage/Vectorization";
|
7 |
-
import { pdfjs } from "react-pdf";
|
8 |
import LLMConfigList from "@/components/pages/llmConfigs/LlmConfigList";
|
9 |
import LlmPromptList from "@/components/pages/llmPrompts/LlmPromptList";
|
10 |
-
import
|
|
|
11 |
import { AuthProvider, useAuth } from "@/context/AuthContext";
|
|
|
|
|
|
|
|
|
|
|
12 |
|
13 |
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
|
14 |
"pdfjs-dist/build/pdf.worker.min.mjs",
|
@@ -45,6 +46,10 @@ const App: React.FC = () => {
|
|
45 |
<PrivateRoute>
|
46 |
<LlmPromptList />
|
47 |
</PrivateRoute>} />
|
|
|
|
|
|
|
|
|
48 |
</Routes>
|
49 |
</div>
|
50 |
</Router>
|
|
|
|
|
|
|
1 |
import Navbar from '@/components/common/navbar/Navbar';
|
2 |
+
import LoginPage from "@/components/pages/auth/Login";
|
|
|
|
|
|
|
3 |
import LLMConfigList from "@/components/pages/llmConfigs/LlmConfigList";
|
4 |
import LlmPromptList from "@/components/pages/llmPrompts/LlmPromptList";
|
5 |
+
import Logs from "@/components/pages/logs/Logs";
|
6 |
+
import Vectorization from "@/components/pages/vectorizationPage/Vectorization";
|
7 |
import { AuthProvider, useAuth } from "@/context/AuthContext";
|
8 |
+
import { pdfjs } from "react-pdf";
|
9 |
+
import { Navigate, Route, BrowserRouter as Router, Routes } from "react-router-dom";
|
10 |
+
import "./App.css";
|
11 |
+
import QePromptList from "./components/pages/qePrompts/QePromptList";
|
12 |
+
|
13 |
|
14 |
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
|
15 |
"pdfjs-dist/build/pdf.worker.min.mjs",
|
|
|
46 |
<PrivateRoute>
|
47 |
<LlmPromptList />
|
48 |
</PrivateRoute>} />
|
49 |
+
<Route path="/qeprompt" element={
|
50 |
+
<PrivateRoute>
|
51 |
+
<QePromptList />
|
52 |
+
</PrivateRoute>} />
|
53 |
</Routes>
|
54 |
</div>
|
55 |
</Router>
|
src/api/qePrompts/hooks.ts
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
2 |
+
import { createQePrompt, deleteQePrompt, fetchQePrompts, setDefaultQePrompt, updateQePrompt } from './qePromptApi';
|
3 |
+
|
4 |
+
export const useQePrompts = () => {
|
5 |
+
const queryClient = useQueryClient();
|
6 |
+
|
7 |
+
const { data: prompts = [], isLoading, error } = useQuery({
|
8 |
+
queryKey: ['qePrompts'],
|
9 |
+
queryFn: fetchQePrompts,
|
10 |
+
});
|
11 |
+
|
12 |
+
const createMutation = useMutation({
|
13 |
+
mutationFn: createQePrompt,
|
14 |
+
onSuccess: () => {
|
15 |
+
queryClient.invalidateQueries({ queryKey: ['qePrompts'] });
|
16 |
+
},
|
17 |
+
});
|
18 |
+
|
19 |
+
const updateMutation = useMutation({
|
20 |
+
mutationFn: updateQePrompt,
|
21 |
+
onSuccess: () => {
|
22 |
+
queryClient.invalidateQueries({ queryKey: ['qePrompts'] });
|
23 |
+
},
|
24 |
+
});
|
25 |
+
|
26 |
+
const setDefaultMutation = useMutation({
|
27 |
+
mutationFn: setDefaultQePrompt,
|
28 |
+
onSuccess: () => {
|
29 |
+
queryClient.invalidateQueries({ queryKey: ['qePrompts'] });
|
30 |
+
},
|
31 |
+
});
|
32 |
+
|
33 |
+
const deleteMutation = useMutation({
|
34 |
+
mutationFn: deleteQePrompt,
|
35 |
+
onSuccess: () => {
|
36 |
+
queryClient.invalidateQueries({ queryKey: ['qePrompts'] });
|
37 |
+
},
|
38 |
+
});
|
39 |
+
|
40 |
+
return {
|
41 |
+
prompts,
|
42 |
+
isLoading,
|
43 |
+
error: error ? (error instanceof Error ? error.message : 'Failed to fetch prompturations') : null,
|
44 |
+
createPrompt: createMutation.mutateAsync,
|
45 |
+
updatePrompt: updateMutation.mutateAsync,
|
46 |
+
setAsDefaultPrompt: setDefaultMutation.mutateAsync,
|
47 |
+
deletePrompt: deleteMutation.mutateAsync,
|
48 |
+
};
|
49 |
+
};
|
src/api/qePrompts/qePromptApi.ts
ADDED
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { query } from '@/shared/api/query';
|
2 |
+
import { QePrompt } from './types';
|
3 |
+
|
4 |
+
export const fetchQePrompts = async (): Promise<QePrompt[]> => {
|
5 |
+
const response = await query<QePrompt[]>({
|
6 |
+
url: '/qe_prompt/',
|
7 |
+
method: 'get',
|
8 |
+
});
|
9 |
+
if ('error' in response) {
|
10 |
+
throw new Error(`Ошибка получения промптов: ${response.error.status}`);
|
11 |
+
}
|
12 |
+
return response.data;
|
13 |
+
};
|
14 |
+
|
15 |
+
export const fetchQePromptById = async (id: number): Promise<QePrompt> => {
|
16 |
+
const response = await query<QePrompt>({
|
17 |
+
url: `/qe_prompt/${id}`,
|
18 |
+
method: 'get',
|
19 |
+
});
|
20 |
+
if ('error' in response) {
|
21 |
+
throw new Error(`Ошибка получения промпта: ${response.error.status}`);
|
22 |
+
}
|
23 |
+
return response.data;
|
24 |
+
};
|
25 |
+
|
26 |
+
export const createQePrompt = async (config: Omit<QePrompt, 'id' | 'created_at'>): Promise<QePrompt> => {
|
27 |
+
const response = await query<QePrompt>({
|
28 |
+
url: '/qe_prompt/',
|
29 |
+
method: 'post',
|
30 |
+
data: config,
|
31 |
+
});
|
32 |
+
if ('error' in response) {
|
33 |
+
throw new Error(`Ошибка создания промпта: ${response.error.status}`);
|
34 |
+
}
|
35 |
+
return response.data;
|
36 |
+
};
|
37 |
+
|
38 |
+
export const updateQePrompt = async (config: QePrompt): Promise<void> => {
|
39 |
+
const response = await query<void>({
|
40 |
+
url: `/qe_prompt/${config.id}`,
|
41 |
+
method: 'put',
|
42 |
+
data: config,
|
43 |
+
});
|
44 |
+
if ('error' in response) {
|
45 |
+
throw new Error(`Ошибка обновления промпта: ${response.error.status}`);
|
46 |
+
}
|
47 |
+
};
|
48 |
+
|
49 |
+
export const setDefaultQePrompt = async (id: number): Promise<void> => {
|
50 |
+
const response = await query<void>({
|
51 |
+
url: `/qe_prompt/default/${id}`,
|
52 |
+
method: 'put',
|
53 |
+
});
|
54 |
+
if ('error' in response) {
|
55 |
+
throw new Error(`Ошибка установки промпта по умолчанию: ${response.error.status}`);
|
56 |
+
}
|
57 |
+
};
|
58 |
+
|
59 |
+
export const deleteQePrompt = async (id: number): Promise<void> => {
|
60 |
+
const response = await query<void>({
|
61 |
+
url: `/qe_prompt/${id}`,
|
62 |
+
method: 'delete',
|
63 |
+
});
|
64 |
+
if ('error' in response) {
|
65 |
+
throw new Error(`Ошибка удаления промпта: ${response.error.status}`);
|
66 |
+
}
|
67 |
+
};
|
68 |
+
|
69 |
+
export const fetchDefaultQePrompt = async (): Promise<QePrompt | null> => {
|
70 |
+
const response = await query<QePrompt>({
|
71 |
+
url: '/qe_prompt/default',
|
72 |
+
method: 'get',
|
73 |
+
});
|
74 |
+
if ('error' in response) {
|
75 |
+
if (response.error.status === 404) return null; // Если дефолтной записи нет
|
76 |
+
throw new Error(`Ошибка получения дефолтной промпта: ${response.error.status}`);
|
77 |
+
}
|
78 |
+
return response.data;
|
79 |
+
};
|
src/api/qePrompts/types.ts
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// src/api/qe_configs/types.ts
|
2 |
+
export interface QePrompt {
|
3 |
+
id: number;
|
4 |
+
is_default: boolean;
|
5 |
+
text: string;
|
6 |
+
name: string;
|
7 |
+
type: string;
|
8 |
+
created_at?: string;
|
9 |
+
}
|
src/components/common/navbar/Navbar.tsx
CHANGED
@@ -1,7 +1,7 @@
|
|
|
|
1 |
import React from 'react';
|
2 |
import { NavLink, useNavigate } from 'react-router-dom';
|
3 |
import './Navbar.scss';
|
4 |
-
import { useAuth } from '@/context/AuthContext';
|
5 |
|
6 |
const Navbar: React.FC = () => {
|
7 |
const { isAuthenticated, logout } = useAuth();
|
@@ -49,6 +49,14 @@ const Navbar: React.FC = () => {
|
|
49 |
Системные промпты
|
50 |
</NavLink>
|
51 |
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
<li>
|
53 |
<button onClick={handleLogout}>Выход</button>
|
54 |
</li>
|
|
|
1 |
+
import { useAuth } from '@/context/AuthContext';
|
2 |
import React from 'react';
|
3 |
import { NavLink, useNavigate } from 'react-router-dom';
|
4 |
import './Navbar.scss';
|
|
|
5 |
|
6 |
const Navbar: React.FC = () => {
|
7 |
const { isAuthenticated, logout } = useAuth();
|
|
|
49 |
Системные промпты
|
50 |
</NavLink>
|
51 |
</li>
|
52 |
+
<li>
|
53 |
+
<NavLink
|
54 |
+
to="/qeprompt"
|
55 |
+
className={({ isActive }) => (isActive ? 'active' : '')}
|
56 |
+
>
|
57 |
+
QE промпты
|
58 |
+
</NavLink>
|
59 |
+
</li>
|
60 |
<li>
|
61 |
<button onClick={handleLogout}>Выход</button>
|
62 |
</li>
|
src/components/generics/button/Button.scss
CHANGED
@@ -9,6 +9,7 @@
|
|
9 |
font-family: inherit;
|
10 |
display: flex;
|
11 |
gap: 10px;
|
|
|
12 |
}
|
13 |
|
14 |
.btn:hover {
|
|
|
9 |
font-family: inherit;
|
10 |
display: flex;
|
11 |
gap: 10px;
|
12 |
+
align-items: center;
|
13 |
}
|
14 |
|
15 |
.btn:hover {
|
src/components/generics/button/Button.tsx
CHANGED
@@ -12,7 +12,7 @@ const Button = ({
|
|
12 |
className,
|
13 |
}: ButtonProps) => {
|
14 |
return (
|
15 |
-
<button onClick={onClick} className={`btn ${buttonType} ${className}`} disabled={disabled}>
|
16 |
{name}
|
17 |
{icon}
|
18 |
{loading && <Spinner />}
|
|
|
12 |
className,
|
13 |
}: ButtonProps) => {
|
14 |
return (
|
15 |
+
<button onClick={onClick} className={`btn ${buttonType} ${className ?? ''}`} disabled={disabled}>
|
16 |
{name}
|
17 |
{icon}
|
18 |
{loading && <Spinner />}
|
src/components/pages/documentsPage/Documents.tsx
CHANGED
@@ -33,9 +33,9 @@ const Documents: FC = () => {
|
|
33 |
if (
|
34 |
(!searchParams.get("datasetId") ||
|
35 |
!datasetsData?.find((e) => e.id.toString() == searchParams.get("datasetId"))) &&
|
36 |
-
datasetsData?.[0]
|
37 |
) {
|
38 |
-
setSearchParams({ datasetId: String(datasetsData?.[0]
|
39 |
}
|
40 |
}, [datasetsData, searchParams, setSearchParams]);
|
41 |
|
|
|
33 |
if (
|
34 |
(!searchParams.get("datasetId") ||
|
35 |
!datasetsData?.find((e) => e.id.toString() == searchParams.get("datasetId"))) &&
|
36 |
+
datasetsData?.[0]?.id
|
37 |
) {
|
38 |
+
setSearchParams({ datasetId: String(datasetsData?.[0]?.id) });
|
39 |
}
|
40 |
}, [datasetsData, searchParams, setSearchParams]);
|
41 |
|
src/components/pages/llmConfigs/LlmConfigModal.scss
CHANGED
@@ -49,7 +49,7 @@
|
|
49 |
label {
|
50 |
display: contents;
|
51 |
|
52 |
-
span {
|
53 |
font-weight: 500;
|
54 |
color: #444;
|
55 |
text-align: right;
|
|
|
49 |
label {
|
50 |
display: contents;
|
51 |
|
52 |
+
span.title {
|
53 |
font-weight: 500;
|
54 |
color: #444;
|
55 |
text-align: right;
|
src/components/pages/llmConfigs/LlmConfigModal.tsx
CHANGED
@@ -128,12 +128,12 @@ interface LLMConfigModalProps {
|
|
128 |
<form onSubmit={handleSubmit}>
|
129 |
{isEditMode && 'id' in formData && (
|
130 |
<label>
|
131 |
-
<span>ID:</span>
|
132 |
<input type="text" value={formData.id} disabled />
|
133 |
</label>
|
134 |
)}
|
135 |
<label>
|
136 |
-
<span>Задать по-умолчанию:</span>
|
137 |
<input
|
138 |
type="checkbox"
|
139 |
name="is_default"
|
@@ -142,7 +142,7 @@ interface LLMConfigModalProps {
|
|
142 |
/>
|
143 |
</label>
|
144 |
<label>
|
145 |
-
<span>Модель:</span>
|
146 |
<input
|
147 |
type="text"
|
148 |
name="model"
|
@@ -151,7 +151,7 @@ interface LLMConfigModalProps {
|
|
151 |
/>
|
152 |
</label>
|
153 |
<label>
|
154 |
-
<span>Temperature:</span>
|
155 |
<input
|
156 |
type="number"
|
157 |
name="temperature"
|
@@ -161,7 +161,7 @@ interface LLMConfigModalProps {
|
|
161 |
/>
|
162 |
</label>
|
163 |
<label>
|
164 |
-
<span>Top P:</span>
|
165 |
<input
|
166 |
type="number"
|
167 |
name="top_p"
|
@@ -171,7 +171,7 @@ interface LLMConfigModalProps {
|
|
171 |
/>
|
172 |
</label>
|
173 |
<label>
|
174 |
-
<span>Min P:</span>
|
175 |
<input
|
176 |
type="number"
|
177 |
name="min_p"
|
@@ -181,7 +181,7 @@ interface LLMConfigModalProps {
|
|
181 |
/>
|
182 |
</label>
|
183 |
<label>
|
184 |
-
<span>Frequency Penalty:</span>
|
185 |
<input
|
186 |
type="number"
|
187 |
name="frequency_penalty"
|
@@ -191,7 +191,7 @@ interface LLMConfigModalProps {
|
|
191 |
/>
|
192 |
</label>
|
193 |
<label>
|
194 |
-
<span>Presence Penalty:</span>
|
195 |
<input
|
196 |
type="number"
|
197 |
name="presence_penalty"
|
@@ -201,7 +201,7 @@ interface LLMConfigModalProps {
|
|
201 |
/>
|
202 |
</label>
|
203 |
<label>
|
204 |
-
<span>N predict:</span>
|
205 |
<input
|
206 |
type="number"
|
207 |
name="n_predict"
|
@@ -211,7 +211,7 @@ interface LLMConfigModalProps {
|
|
211 |
/>
|
212 |
</label>
|
213 |
<label>
|
214 |
-
<span>Seed:</span>
|
215 |
<input
|
216 |
type="number"
|
217 |
name="seed"
|
|
|
128 |
<form onSubmit={handleSubmit}>
|
129 |
{isEditMode && 'id' in formData && (
|
130 |
<label>
|
131 |
+
<span className='title'>ID:</span>
|
132 |
<input type="text" value={formData.id} disabled />
|
133 |
</label>
|
134 |
)}
|
135 |
<label>
|
136 |
+
<span className='title'>Задать по-умолчанию:</span>
|
137 |
<input
|
138 |
type="checkbox"
|
139 |
name="is_default"
|
|
|
142 |
/>
|
143 |
</label>
|
144 |
<label>
|
145 |
+
<span className='title'>Модель:</span>
|
146 |
<input
|
147 |
type="text"
|
148 |
name="model"
|
|
|
151 |
/>
|
152 |
</label>
|
153 |
<label>
|
154 |
+
<span className='title'>Temperature:</span>
|
155 |
<input
|
156 |
type="number"
|
157 |
name="temperature"
|
|
|
161 |
/>
|
162 |
</label>
|
163 |
<label>
|
164 |
+
<span className='title'>Top P:</span>
|
165 |
<input
|
166 |
type="number"
|
167 |
name="top_p"
|
|
|
171 |
/>
|
172 |
</label>
|
173 |
<label>
|
174 |
+
<span className='title'>Min P:</span>
|
175 |
<input
|
176 |
type="number"
|
177 |
name="min_p"
|
|
|
181 |
/>
|
182 |
</label>
|
183 |
<label>
|
184 |
+
<span className='title'>Frequency Penalty:</span>
|
185 |
<input
|
186 |
type="number"
|
187 |
name="frequency_penalty"
|
|
|
191 |
/>
|
192 |
</label>
|
193 |
<label>
|
194 |
+
<span className='title'>Presence Penalty:</span>
|
195 |
<input
|
196 |
type="number"
|
197 |
name="presence_penalty"
|
|
|
201 |
/>
|
202 |
</label>
|
203 |
<label>
|
204 |
+
<span className='title'>N predict:</span>
|
205 |
<input
|
206 |
type="number"
|
207 |
name="n_predict"
|
|
|
211 |
/>
|
212 |
</label>
|
213 |
<label>
|
214 |
+
<span className='title'>Seed:</span>
|
215 |
<input
|
216 |
type="number"
|
217 |
name="seed"
|
src/components/pages/llmPrompts/LlmPromptModal.scss
CHANGED
@@ -13,6 +13,7 @@
|
|
13 |
overflow-y: auto;
|
14 |
overflow-x: hidden;
|
15 |
|
|
|
16 |
.label {
|
17 |
display: flex;
|
18 |
justify-content: space-between;
|
@@ -49,7 +50,7 @@
|
|
49 |
label {
|
50 |
display: contents;
|
51 |
|
52 |
-
span {
|
53 |
font-weight: 500;
|
54 |
color: #444;
|
55 |
text-align: right;
|
|
|
13 |
overflow-y: auto;
|
14 |
overflow-x: hidden;
|
15 |
|
16 |
+
|
17 |
.label {
|
18 |
display: flex;
|
19 |
justify-content: space-between;
|
|
|
50 |
label {
|
51 |
display: contents;
|
52 |
|
53 |
+
span.title {
|
54 |
font-weight: 500;
|
55 |
color: #444;
|
56 |
text-align: right;
|
src/components/pages/llmPrompts/LlmPromptModal.tsx
CHANGED
@@ -117,12 +117,12 @@ const LlmPromptModal: React.FC<LlmPromptModalProps> = ({
|
|
117 |
<form onSubmit={handleSubmit}>
|
118 |
{isEditMode && 'id' in formData && (
|
119 |
<label>
|
120 |
-
<span>ID:</span>
|
121 |
<input type="text" value={formData.id} disabled />
|
122 |
</label>
|
123 |
)}
|
124 |
<label>
|
125 |
-
<span>Задать по-умолчанию:</span>
|
126 |
<input
|
127 |
type="checkbox"
|
128 |
name="is_default"
|
@@ -131,7 +131,7 @@ const LlmPromptModal: React.FC<LlmPromptModalProps> = ({
|
|
131 |
/>
|
132 |
</label>
|
133 |
<label>
|
134 |
-
<span>Название:</span>
|
135 |
<input
|
136 |
type="text"
|
137 |
name="name"
|
@@ -140,7 +140,7 @@ const LlmPromptModal: React.FC<LlmPromptModalProps> = ({
|
|
140 |
/>
|
141 |
</label>
|
142 |
<label>
|
143 |
-
<span>Текст:</span>
|
144 |
<textarea
|
145 |
name="text"
|
146 |
value={formData.text || ''}
|
|
|
117 |
<form onSubmit={handleSubmit}>
|
118 |
{isEditMode && 'id' in formData && (
|
119 |
<label>
|
120 |
+
<span className='title'>ID:</span>
|
121 |
<input type="text" value={formData.id} disabled />
|
122 |
</label>
|
123 |
)}
|
124 |
<label>
|
125 |
+
<span className='title'>Задать по-умолчанию:</span>
|
126 |
<input
|
127 |
type="checkbox"
|
128 |
name="is_default"
|
|
|
131 |
/>
|
132 |
</label>
|
133 |
<label>
|
134 |
+
<span className='title'>Название:</span>
|
135 |
<input
|
136 |
type="text"
|
137 |
name="name"
|
|
|
140 |
/>
|
141 |
</label>
|
142 |
<label>
|
143 |
+
<span className='title'>Текст:</span>
|
144 |
<textarea
|
145 |
name="text"
|
146 |
value={formData.text || ''}
|
src/components/pages/qePrompts/QePromptList.scss
ADDED
@@ -0,0 +1,209 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.qe-prompt-list {
|
2 |
+
padding: 20px;
|
3 |
+
|
4 |
+
.create-button {
|
5 |
+
margin-bottom: 20px;
|
6 |
+
padding: 8px 16px;
|
7 |
+
background-color: #007bff;
|
8 |
+
color: white;
|
9 |
+
border: none;
|
10 |
+
border-radius: 5px;
|
11 |
+
cursor: pointer;
|
12 |
+
|
13 |
+
&:hover {
|
14 |
+
background-color: #0056b3;
|
15 |
+
}
|
16 |
+
}
|
17 |
+
|
18 |
+
.table-container {
|
19 |
+
width: 100%;
|
20 |
+
margin-top: 20px;
|
21 |
+
border: 1px solid #ddd;
|
22 |
+
|
23 |
+
.table-header {
|
24 |
+
display: flex;
|
25 |
+
background-color: #f2f2f2;
|
26 |
+
font-weight: bold;
|
27 |
+
|
28 |
+
.table-cell {
|
29 |
+
flex: 1;
|
30 |
+
padding: 8px;
|
31 |
+
border-right: 1px solid #ddd;
|
32 |
+
cursor: pointer;
|
33 |
+
|
34 |
+
&:nth-child(1) {
|
35 |
+
min-width: 30px;
|
36 |
+
}
|
37 |
+
|
38 |
+
&:nth-child(2) {
|
39 |
+
flex: 30;
|
40 |
+
}
|
41 |
+
|
42 |
+
&:nth-child(3) {
|
43 |
+
min-width: 100px;
|
44 |
+
}
|
45 |
+
|
46 |
+
&:last-child {
|
47 |
+
border-right: none;
|
48 |
+
}
|
49 |
+
}
|
50 |
+
}
|
51 |
+
|
52 |
+
.table-row {
|
53 |
+
display: flex;
|
54 |
+
border-top: 1px solid #ddd;
|
55 |
+
|
56 |
+
&:hover {
|
57 |
+
background-color: #f5f5f5;
|
58 |
+
cursor: pointer;
|
59 |
+
}
|
60 |
+
|
61 |
+
.table-cell {
|
62 |
+
flex: 1;
|
63 |
+
padding: 8px;
|
64 |
+
border-right: 1px solid #ddd;
|
65 |
+
|
66 |
+
&:nth-child(1) {
|
67 |
+
min-width: 30px;
|
68 |
+
}
|
69 |
+
|
70 |
+
&:nth-child(2) {
|
71 |
+
flex: 30;
|
72 |
+
}
|
73 |
+
|
74 |
+
&:nth-child(3) {
|
75 |
+
min-width: 100px;
|
76 |
+
}
|
77 |
+
|
78 |
+
&:last-child {
|
79 |
+
border-right: none;
|
80 |
+
display: flex;
|
81 |
+
justify-content: center;
|
82 |
+
}
|
83 |
+
}
|
84 |
+
}
|
85 |
+
|
86 |
+
.set-default-button,
|
87 |
+
.delete-button {
|
88 |
+
padding: 5px;
|
89 |
+
display: flex;
|
90 |
+
align-items: center;
|
91 |
+
justify-content: center;
|
92 |
+
margin:3px;
|
93 |
+
}
|
94 |
+
|
95 |
+
.delete-button {
|
96 |
+
background-color: #dc3545;
|
97 |
+
color: white;
|
98 |
+
border: none;
|
99 |
+
border-radius: 5px;
|
100 |
+
cursor: pointer;
|
101 |
+
|
102 |
+
&:hover {
|
103 |
+
background-color: #c82333;
|
104 |
+
}
|
105 |
+
}
|
106 |
+
|
107 |
+
.set-default-button {
|
108 |
+
background-color: #28a745;
|
109 |
+
color: white;
|
110 |
+
border: none;
|
111 |
+
border-radius: 5px;
|
112 |
+
cursor: pointer;
|
113 |
+
|
114 |
+
&:hover {
|
115 |
+
background-color: #218838;
|
116 |
+
}
|
117 |
+
}
|
118 |
+
}
|
119 |
+
|
120 |
+
.modal-overlay {
|
121 |
+
position: fixed;
|
122 |
+
top: 0;
|
123 |
+
left: 0;
|
124 |
+
right: 0;
|
125 |
+
bottom: 0;
|
126 |
+
background-color: rgba(0, 0, 0, 0.5);
|
127 |
+
z-index: 1000;
|
128 |
+
}
|
129 |
+
|
130 |
+
.modal-content {
|
131 |
+
padding: 20px;
|
132 |
+
|
133 |
+
.label {
|
134 |
+
display: flex;
|
135 |
+
justify-content: space-between;
|
136 |
+
align-items: center;
|
137 |
+
margin-bottom: 20px;
|
138 |
+
|
139 |
+
.name {
|
140 |
+
margin: 0;
|
141 |
+
}
|
142 |
+
|
143 |
+
.close-button {
|
144 |
+
background: none;
|
145 |
+
border: none;
|
146 |
+
cursor: pointer;
|
147 |
+
padding: 0;
|
148 |
+
}
|
149 |
+
}
|
150 |
+
|
151 |
+
form {
|
152 |
+
display: flex;
|
153 |
+
flex-direction: column;
|
154 |
+
gap: 15px;
|
155 |
+
|
156 |
+
label {
|
157 |
+
display: flex;
|
158 |
+
flex-direction: column;
|
159 |
+
gap: 5px;
|
160 |
+
}
|
161 |
+
|
162 |
+
input {
|
163 |
+
padding: 8px;
|
164 |
+
border: 1px solid #ddd;
|
165 |
+
border-radius: 5px;
|
166 |
+
}
|
167 |
+
|
168 |
+
.button-group {
|
169 |
+
display: flex;
|
170 |
+
gap: 10px;
|
171 |
+
margin-top: 20px;
|
172 |
+
|
173 |
+
button {
|
174 |
+
padding: 8px 16px;
|
175 |
+
border: none;
|
176 |
+
border-radius: 5px;
|
177 |
+
cursor: pointer;
|
178 |
+
|
179 |
+
&:first-child {
|
180 |
+
background-color: #007bff;
|
181 |
+
color: white;
|
182 |
+
|
183 |
+
&:hover {
|
184 |
+
background-color: #0056b3;
|
185 |
+
}
|
186 |
+
}
|
187 |
+
|
188 |
+
&:nth-child(2) {
|
189 |
+
background-color: #28a745;
|
190 |
+
color: white;
|
191 |
+
|
192 |
+
&:hover {
|
193 |
+
background-color: #218838;
|
194 |
+
}
|
195 |
+
}
|
196 |
+
|
197 |
+
&:last-child {
|
198 |
+
background-color: #6c757d;
|
199 |
+
color: white;
|
200 |
+
|
201 |
+
&:hover {
|
202 |
+
background-color: #5a6268;
|
203 |
+
}
|
204 |
+
}
|
205 |
+
}
|
206 |
+
}
|
207 |
+
}
|
208 |
+
}
|
209 |
+
}
|
src/components/pages/qePrompts/QePromptList.tsx
ADDED
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useQePrompts, } from "@/api/qePrompts/hooks";
|
2 |
+
import { QePrompt } from "@/api/qePrompts/types";
|
3 |
+
import React, { useState } from 'react';
|
4 |
+
import { GoStar, GoStarFill, GoTrash } from 'react-icons/go';
|
5 |
+
import Modal from 'react-modal';
|
6 |
+
import './QePromptList.scss';
|
7 |
+
import QePromptModal from './QePromptModal';
|
8 |
+
|
9 |
+
|
10 |
+
|
11 |
+
Modal.setAppElement('#root');
|
12 |
+
|
13 |
+
|
14 |
+
|
15 |
+
const QePromptList: React.FC = () => {
|
16 |
+
const { prompts, isLoading, error, createPrompt, updatePrompt, setAsDefaultPrompt, deletePrompt } = useQePrompts();
|
17 |
+
const [sortField, setSortField] = useState<keyof QePrompt | 'created_at'>('id');
|
18 |
+
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc');
|
19 |
+
const [selectedPrompt, setSelectedPrompt] = useState<QePrompt | null>(null);
|
20 |
+
const [isModalOpen, setIsModalOpen] = useState(false);
|
21 |
+
const [isEditMode, setIsEditMode] = useState(false);
|
22 |
+
|
23 |
+
|
24 |
+
const handleSort = (field: keyof QePrompt | 'created_at') => {
|
25 |
+
const newDirection = sortField === field && sortDirection === 'asc' ? 'desc' : 'asc';
|
26 |
+
setSortField(field);
|
27 |
+
setSortDirection(newDirection);
|
28 |
+
const sortedPrompts = [...prompts].sort((a, b) => {
|
29 |
+
const aValue = field === 'created_at' ? a.created_at || '' : a[field];
|
30 |
+
const bValue = field === 'created_at' ? b.created_at || '' : b[field];
|
31 |
+
return newDirection === 'asc' ? (aValue > bValue ? 1 : -1) : (aValue < bValue ? 1 : -1);
|
32 |
+
});
|
33 |
+
prompts.splice(0, prompts.length, ...sortedPrompts);
|
34 |
+
};
|
35 |
+
|
36 |
+
const openEditModal = (prompt: QePrompt) => {
|
37 |
+
setSelectedPrompt(prompt);
|
38 |
+
setIsEditMode(true);
|
39 |
+
setIsModalOpen(true);
|
40 |
+
};
|
41 |
+
|
42 |
+
const openCreateModal = () => {
|
43 |
+
setSelectedPrompt(null); // Ничего не передаем, запрос будет в модалке
|
44 |
+
setIsEditMode(false);
|
45 |
+
setIsModalOpen(true);
|
46 |
+
};
|
47 |
+
|
48 |
+
const closeModal = () => {
|
49 |
+
setSelectedPrompt(null);
|
50 |
+
setIsModalOpen(false);
|
51 |
+
};
|
52 |
+
|
53 |
+
const handleSave = async (prompt: QePrompt | Omit<QePrompt, 'id' | 'created_at'>) => {
|
54 |
+
if (isEditMode && 'id' in prompt) {
|
55 |
+
await updatePrompt(prompt as QePrompt);
|
56 |
+
} else {
|
57 |
+
await createPrompt(prompt as Omit<QePrompt, 'id' | 'created_at'>);
|
58 |
+
}
|
59 |
+
};
|
60 |
+
|
61 |
+
const handleDelete = async (id: number) => {
|
62 |
+
if (window.confirm('Удаляем версию промпта?')) {
|
63 |
+
await deletePrompt(id);
|
64 |
+
}
|
65 |
+
};
|
66 |
+
|
67 |
+
const handleSetDefault = async (id: number) => {
|
68 |
+
await setAsDefaultPrompt(id);
|
69 |
+
};
|
70 |
+
|
71 |
+
if (isLoading) return <div>Loading...</div>;
|
72 |
+
if (error) return <div>Error: {error}</div>;
|
73 |
+
|
74 |
+
return (
|
75 |
+
<div className="qe-prompt-list">
|
76 |
+
<h1>Промпты Query Expansion</h1>
|
77 |
+
<button className="create-button" onClick={openCreateModal}>
|
78 |
+
Добавить промпт
|
79 |
+
</button>
|
80 |
+
<div className="table-container">
|
81 |
+
<div className="table-header">
|
82 |
+
<div className="table-cell"></div>
|
83 |
+
<div className="table-cell" onClick={() => handleSort('created_at')}>
|
84 |
+
Промпты
|
85 |
+
</div>
|
86 |
+
<div className="table-cell"></div>
|
87 |
+
</div>
|
88 |
+
{prompts.map(prompt => (
|
89 |
+
<div key={prompt.id} className="table-row">
|
90 |
+
<div className="table-cell">
|
91 |
+
<button
|
92 |
+
className="set-default-button"
|
93 |
+
onClick={() => handleSetDefault(prompt.id)}
|
94 |
+
>
|
95 |
+
{prompt.is_default ? <GoStarFill size={20} /> : <GoStar size={20} />}
|
96 |
+
</button>
|
97 |
+
</div>
|
98 |
+
<div className="table-cell" onClick={() => openEditModal(prompt)}>
|
99 |
+
{prompt.name}
|
100 |
+
</div>
|
101 |
+
{/* <div className="table-cell" onClick={() => openEditModal(prompt)}>
|
102 |
+
{prompt.created_at || 'N/A'}
|
103 |
+
</div> */}
|
104 |
+
<div className="table-cell">
|
105 |
+
<button className="delete-button" onClick={() => handleDelete(prompt.id)}>
|
106 |
+
<GoTrash size={20} />
|
107 |
+
</button>
|
108 |
+
</div>
|
109 |
+
</div>
|
110 |
+
))}
|
111 |
+
</div>
|
112 |
+
|
113 |
+
<QePromptModal
|
114 |
+
isOpen={isModalOpen}
|
115 |
+
onRequestClose={closeModal}
|
116 |
+
prompt={selectedPrompt}
|
117 |
+
onSave={handleSave}
|
118 |
+
isEditMode={isEditMode}
|
119 |
+
/>
|
120 |
+
</div>
|
121 |
+
);
|
122 |
+
};
|
123 |
+
|
124 |
+
export default QePromptList;
|
src/components/pages/qePrompts/QePromptModal.scss
ADDED
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.modal-overlay {
|
2 |
+
position: fixed;
|
3 |
+
top: 0;
|
4 |
+
left: 0;
|
5 |
+
right: 0;
|
6 |
+
bottom: 0;
|
7 |
+
background-color: rgba(0, 0, 0, 0.5);
|
8 |
+
z-index: 1000;
|
9 |
+
}
|
10 |
+
|
11 |
+
.modal-content {
|
12 |
+
max-height: 80vh;
|
13 |
+
overflow-y: auto;
|
14 |
+
overflow-x: hidden;
|
15 |
+
|
16 |
+
.label {
|
17 |
+
display: flex;
|
18 |
+
justify-content: space-between;
|
19 |
+
align-items: center;
|
20 |
+
margin-bottom: 20px;
|
21 |
+
|
22 |
+
.name {
|
23 |
+
margin: 0;
|
24 |
+
font-size: 1.5rem;
|
25 |
+
color: #333;
|
26 |
+
}
|
27 |
+
|
28 |
+
.close-button {
|
29 |
+
background: none;
|
30 |
+
border: none;
|
31 |
+
cursor: pointer;
|
32 |
+
padding: 0;
|
33 |
+
color: #666;
|
34 |
+
|
35 |
+
&:hover {
|
36 |
+
color: #333;
|
37 |
+
}
|
38 |
+
}
|
39 |
+
}
|
40 |
+
|
41 |
+
form {
|
42 |
+
display: grid;
|
43 |
+
grid-template-columns: 1fr 2fr;
|
44 |
+
gap: 15px;
|
45 |
+
align-items: center;
|
46 |
+
width: 100%;
|
47 |
+
box-sizing: border-box;
|
48 |
+
|
49 |
+
label {
|
50 |
+
display: contents;
|
51 |
+
|
52 |
+
span.title {
|
53 |
+
font-weight: 500;
|
54 |
+
color: #444;
|
55 |
+
text-align: right;
|
56 |
+
padding-right: 10px;
|
57 |
+
}
|
58 |
+
|
59 |
+
input {
|
60 |
+
max-width: 100%;
|
61 |
+
}
|
62 |
+
|
63 |
+
input[type="text"],
|
64 |
+
input[type="number"] {
|
65 |
+
padding: 8px 12px;
|
66 |
+
border: 1px solid #ccc;
|
67 |
+
border-radius: 5px;
|
68 |
+
font-size: 1rem;
|
69 |
+
color: #333;
|
70 |
+
background-color: #f9f9f9;
|
71 |
+
transition: border-color 0.2s;
|
72 |
+
|
73 |
+
&:focus {
|
74 |
+
border-color: #007bff;
|
75 |
+
outline: none;
|
76 |
+
background-color: #fff;
|
77 |
+
}
|
78 |
+
|
79 |
+
&:disabled {
|
80 |
+
background-color: #e9ecef;
|
81 |
+
color: #666;
|
82 |
+
}
|
83 |
+
}
|
84 |
+
|
85 |
+
input[type="checkbox"] {
|
86 |
+
width: 20px;
|
87 |
+
height: 20px;
|
88 |
+
margin: 0;
|
89 |
+
cursor: pointer;
|
90 |
+
}
|
91 |
+
}
|
92 |
+
|
93 |
+
.button-group {
|
94 |
+
grid-column: span 2;
|
95 |
+
display: flex;
|
96 |
+
justify-content: flex-end;
|
97 |
+
gap: 10px;
|
98 |
+
margin-top: 20px;
|
99 |
+
|
100 |
+
button {
|
101 |
+
padding: 10px 20px;
|
102 |
+
border: none;
|
103 |
+
border-radius: 5px;
|
104 |
+
font-size: 1rem;
|
105 |
+
cursor: pointer;
|
106 |
+
transition: background-color 0.2s;
|
107 |
+
|
108 |
+
&:first-child {
|
109 |
+
background-color: #007bff;
|
110 |
+
color: white;
|
111 |
+
|
112 |
+
&:hover {
|
113 |
+
background-color: #0056b3;
|
114 |
+
}
|
115 |
+
}
|
116 |
+
|
117 |
+
&:nth-child(2) {
|
118 |
+
background-color: #28a745;
|
119 |
+
color: white;
|
120 |
+
|
121 |
+
&:hover {
|
122 |
+
background-color: #218838;
|
123 |
+
}
|
124 |
+
}
|
125 |
+
|
126 |
+
&:last-child {
|
127 |
+
background-color: #6c757d;
|
128 |
+
color: white;
|
129 |
+
|
130 |
+
&:hover {
|
131 |
+
background-color: #5a6268;
|
132 |
+
}
|
133 |
+
}
|
134 |
+
}
|
135 |
+
}
|
136 |
+
}
|
137 |
+
}
|
src/components/pages/qePrompts/QePromptModal.tsx
ADDED
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { fetchDefaultQePrompt, fetchQePromptById } from '@/api/qePrompts/qePromptApi';
|
2 |
+
import { QePrompt } from '@/api/qePrompts/types';
|
3 |
+
import { useQuery } from '@tanstack/react-query';
|
4 |
+
import React from 'react';
|
5 |
+
import { GoX } from 'react-icons/go';
|
6 |
+
import Modal from 'react-modal';
|
7 |
+
import './QePromptModal.scss';
|
8 |
+
|
9 |
+
interface QePromptModalProps {
|
10 |
+
isOpen: boolean;
|
11 |
+
onRequestClose: () => void;
|
12 |
+
prompt: QePrompt | null;
|
13 |
+
onSave: (prompt: QePrompt | Omit<QePrompt, 'id' | 'created_at'>) => Promise<void>;
|
14 |
+
isEditMode: boolean;
|
15 |
+
}
|
16 |
+
|
17 |
+
const customStyles = {
|
18 |
+
content: {
|
19 |
+
top: '40%',
|
20 |
+
left: '50%',
|
21 |
+
right: 'auto',
|
22 |
+
bottom: 'auto',
|
23 |
+
transform: 'translate(-50%, -50%)',
|
24 |
+
borderRadius: '15px',
|
25 |
+
maxWidth: '100%',
|
26 |
+
overflow: 'hidden',
|
27 |
+
},
|
28 |
+
};
|
29 |
+
|
30 |
+
const QePromptModal: React.FC<QePromptModalProps> = ({
|
31 |
+
isOpen,
|
32 |
+
onRequestClose,
|
33 |
+
prompt,
|
34 |
+
onSave,
|
35 |
+
isEditMode,
|
36 |
+
}) => {
|
37 |
+
const [formData, setFormData] = React.useState<QePrompt | Omit<QePrompt, 'id' | 'created_at'>>(() => ({
|
38 |
+
is_default: false,
|
39 |
+
name: 'Промпт ' + Date.now().toLocaleString(),
|
40 |
+
text: "",
|
41 |
+
type: 'query_expansion'
|
42 |
+
}));
|
43 |
+
|
44 |
+
// Запрос промпта для редактирования
|
45 |
+
const { data: serverPrompt, isLoading: isPromptLoading } = useQuery({
|
46 |
+
queryKey: ['qePrompt', prompt?.id],
|
47 |
+
queryFn: () => fetchQePromptById(prompt!.id),
|
48 |
+
enabled: isOpen && isEditMode && !!prompt?.id,
|
49 |
+
});
|
50 |
+
|
51 |
+
// Запрос дефолтного промпта для создания
|
52 |
+
const { data: defaultPrompt, isLoading: isDefaultLoading } = useQuery({
|
53 |
+
queryKey: ['defaultQePrompt'],
|
54 |
+
queryFn: fetchDefaultQePrompt,
|
55 |
+
enabled: isOpen && !isEditMode, // Запрашиваем только при создании
|
56 |
+
});
|
57 |
+
|
58 |
+
React.useEffect(() => {
|
59 |
+
if (isOpen) {
|
60 |
+
if (isEditMode && serverPrompt) {
|
61 |
+
setFormData(serverPrompt); // Данные с сервера для редактирования
|
62 |
+
} else if (!isEditMode && defaultPrompt) {
|
63 |
+
setFormData(defaultPrompt); // Дефолтная запись с сервера для создания
|
64 |
+
} else if (!isEditMode && !defaultPrompt && !isDefaultLoading) {
|
65 |
+
setFormData({ // Если дефолтной записи нет и загрузка завершена
|
66 |
+
is_default: true,
|
67 |
+
name: 'Промпт ' + Date.now().toLocaleString(),
|
68 |
+
text: "",
|
69 |
+
type: 'query_expansion'
|
70 |
+
});
|
71 |
+
}
|
72 |
+
}
|
73 |
+
}, [isOpen, isEditMode, serverPrompt, defaultPrompt, isDefaultLoading]);
|
74 |
+
|
75 |
+
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
76 |
+
const { name, value } = e.target;
|
77 |
+
setFormData(prev => ({
|
78 |
+
...prev,
|
79 |
+
[name]:
|
80 |
+
name === 'is_default' ? (e.target as HTMLInputElement).checked :
|
81 |
+
name === 'text' || name === 'name' ? value :
|
82 |
+
parseFloat(value) || 0,
|
83 |
+
}));
|
84 |
+
};
|
85 |
+
|
86 |
+
const handleSubmit = async (e: React.FormEvent) => {
|
87 |
+
e.preventDefault();
|
88 |
+
try {
|
89 |
+
await onSave(formData);
|
90 |
+
onRequestClose();
|
91 |
+
} catch (err) {
|
92 |
+
console.error('Save failed:', err);
|
93 |
+
}
|
94 |
+
};
|
95 |
+
|
96 |
+
if (isPromptLoading && isEditMode) {
|
97 |
+
return <div>Loading prompt...</div>;
|
98 |
+
}
|
99 |
+
if (isDefaultLoading && !isEditMode) {
|
100 |
+
return <div>Loading default prompt...</div>;
|
101 |
+
}
|
102 |
+
|
103 |
+
return (
|
104 |
+
<Modal
|
105 |
+
isOpen={isOpen}
|
106 |
+
onRequestClose={onRequestClose}
|
107 |
+
style={customStyles}
|
108 |
+
overlayClassName="modal-overlay"
|
109 |
+
>
|
110 |
+
<div className="modal-content">
|
111 |
+
<div className="label">
|
112 |
+
<h3 className="name">{isEditMode ? 'Редактирование промпта' : 'Новый промпт'}</h3>
|
113 |
+
<button className="close-button" onClick={onRequestClose}>
|
114 |
+
<GoX style={{ height: '25px', width: '25px' }} />
|
115 |
+
</button>
|
116 |
+
</div>
|
117 |
+
<form onSubmit={handleSubmit}>
|
118 |
+
{isEditMode && 'id' in formData && (
|
119 |
+
<label>
|
120 |
+
<span className='title'>ID:</span>
|
121 |
+
<input type="text" value={formData.id} disabled />
|
122 |
+
</label>
|
123 |
+
)}
|
124 |
+
<label>
|
125 |
+
<span className='title'>Задать по-умолчанию:</span>
|
126 |
+
<input
|
127 |
+
type="checkbox"
|
128 |
+
name="is_default"
|
129 |
+
checked={formData.is_default}
|
130 |
+
onChange={handleChange}
|
131 |
+
/>
|
132 |
+
</label>
|
133 |
+
<label>
|
134 |
+
<span className='title'>Название:</span>
|
135 |
+
<input
|
136 |
+
type="text"
|
137 |
+
name="name"
|
138 |
+
value={formData.name}
|
139 |
+
onChange={handleChange}
|
140 |
+
/>
|
141 |
+
</label>
|
142 |
+
<label>
|
143 |
+
<span className='title'>Текст:</span>
|
144 |
+
<textarea
|
145 |
+
name="text"
|
146 |
+
value={formData.text || ''}
|
147 |
+
onChange={handleChange}
|
148 |
+
/>
|
149 |
+
</label>
|
150 |
+
<div className="button-group">
|
151 |
+
<button type="submit">{isEditMode ? 'Сохранит��' : 'Создать'}</button>
|
152 |
+
</div>
|
153 |
+
</form>
|
154 |
+
</div>
|
155 |
+
</Modal>
|
156 |
+
);
|
157 |
+
};
|
158 |
+
|
159 |
+
export default QePromptModal;
|
src/components/views/documents/docsList/DocsList.tsx
CHANGED
@@ -21,9 +21,9 @@ export const DocsList: FC<DocsListProps> = ({ datasetId, handleDeleteFile }) =>
|
|
21 |
const [searchInput, setSearchInput] = useState<string | undefined>(undefined);
|
22 |
const [search, setSearch] = useState<string | undefined>(undefined);
|
23 |
const [sort, setSort] = useState<undefined | { field: string; direction: SortDirections }[]>(undefined);
|
24 |
-
|
25 |
const { data: datasetData, isFetching } = useGetDataset(datasetId ?? -1, { page, page_size: pageSize, search, sort });
|
26 |
-
|
27 |
const toggleSort = (field: string) => {
|
28 |
setSort((prevSort) => {
|
29 |
if (prevSort?.length && prevSort?.length > 0) {
|
|
|
21 |
const [searchInput, setSearchInput] = useState<string | undefined>(undefined);
|
22 |
const [search, setSearch] = useState<string | undefined>(undefined);
|
23 |
const [sort, setSort] = useState<undefined | { field: string; direction: SortDirections }[]>(undefined);
|
24 |
+
|
25 |
const { data: datasetData, isFetching } = useGetDataset(datasetId ?? -1, { page, page_size: pageSize, search, sort });
|
26 |
+
|
27 |
const toggleSort = (field: string) => {
|
28 |
setSort((prevSort) => {
|
29 |
if (prevSort?.length && prevSort?.length > 0) {
|