### What problem does this PR solve? Feat: Display document parsing status #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.19.0
| @@ -23,6 +23,7 @@ | |||
| "@radix-ui/react-collapsible": "^1.1.3", | |||
| "@radix-ui/react-dialog": "1.1.4", | |||
| "@radix-ui/react-dropdown-menu": "2.1.4", | |||
| "@radix-ui/react-hover-card": "^1.1.11", | |||
| "@radix-ui/react-icons": "^1.3.1", | |||
| "@radix-ui/react-label": "^2.1.0", | |||
| "@radix-ui/react-navigation-menu": "^1.2.1", | |||
| @@ -5484,6 +5485,340 @@ | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-hover-card": { | |||
| "version": "1.1.11", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-hover-card/-/react-hover-card-1.1.11.tgz", | |||
| "integrity": "sha512-q9h9grUpGZKR3MNhtVCLVnPGmx1YnzBgGR+O40mhSNGsUnkR+LChVH8c7FB0mkS+oudhd8KAkZGTJPJCjdAPIg==", | |||
| "license": "MIT", | |||
| "dependencies": { | |||
| "@radix-ui/primitive": "1.1.2", | |||
| "@radix-ui/react-compose-refs": "1.1.2", | |||
| "@radix-ui/react-context": "1.1.2", | |||
| "@radix-ui/react-dismissable-layer": "1.1.7", | |||
| "@radix-ui/react-popper": "1.2.4", | |||
| "@radix-ui/react-portal": "1.1.6", | |||
| "@radix-ui/react-presence": "1.1.4", | |||
| "@radix-ui/react-primitive": "2.1.0", | |||
| "@radix-ui/react-use-controllable-state": "1.2.2" | |||
| }, | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "@types/react-dom": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", | |||
| "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| }, | |||
| "@types/react-dom": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-hover-card/node_modules/@floating-ui/core": { | |||
| "version": "1.6.9", | |||
| "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.6.9.tgz", | |||
| "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==", | |||
| "license": "MIT", | |||
| "dependencies": { | |||
| "@floating-ui/utils": "^0.2.9" | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-hover-card/node_modules/@floating-ui/dom": { | |||
| "version": "1.6.13", | |||
| "resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.6.13.tgz", | |||
| "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", | |||
| "license": "MIT", | |||
| "dependencies": { | |||
| "@floating-ui/core": "^1.6.0", | |||
| "@floating-ui/utils": "^0.2.9" | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-hover-card/node_modules/@floating-ui/react-dom": { | |||
| "version": "2.1.2", | |||
| "resolved": "https://registry.npmmirror.com/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", | |||
| "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", | |||
| "license": "MIT", | |||
| "dependencies": { | |||
| "@floating-ui/dom": "^1.0.0" | |||
| }, | |||
| "peerDependencies": { | |||
| "react": ">=16.8.0", | |||
| "react-dom": ">=16.8.0" | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/primitive": { | |||
| "version": "1.1.2", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/primitive/-/primitive-1.1.2.tgz", | |||
| "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", | |||
| "license": "MIT" | |||
| }, | |||
| "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-arrow": { | |||
| "version": "1.1.4", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-arrow/-/react-arrow-1.1.4.tgz", | |||
| "integrity": "sha512-qz+fxrqgNxG0dYew5l7qR3c7wdgRu1XVUHGnGYX7rg5HM4p9SWaRmJwfgR3J0SgyUKayLmzQIun+N6rWRgiRKw==", | |||
| "license": "MIT", | |||
| "dependencies": { | |||
| "@radix-ui/react-primitive": "2.1.0" | |||
| }, | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "@types/react-dom": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", | |||
| "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| }, | |||
| "@types/react-dom": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-compose-refs": { | |||
| "version": "1.1.2", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", | |||
| "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", | |||
| "license": "MIT", | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-context": { | |||
| "version": "1.1.2", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-context/-/react-context-1.1.2.tgz", | |||
| "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", | |||
| "license": "MIT", | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-popper": { | |||
| "version": "1.2.4", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-popper/-/react-popper-1.2.4.tgz", | |||
| "integrity": "sha512-3p2Rgm/a1cK0r/UVkx5F/K9v/EplfjAeIFCGOPYPO4lZ0jtg4iSQXt/YGTSLWaf4x7NG6Z4+uKFcylcTZjeqDA==", | |||
| "license": "MIT", | |||
| "dependencies": { | |||
| "@floating-ui/react-dom": "^2.0.0", | |||
| "@radix-ui/react-arrow": "1.1.4", | |||
| "@radix-ui/react-compose-refs": "1.1.2", | |||
| "@radix-ui/react-context": "1.1.2", | |||
| "@radix-ui/react-primitive": "2.1.0", | |||
| "@radix-ui/react-use-callback-ref": "1.1.1", | |||
| "@radix-ui/react-use-layout-effect": "1.1.1", | |||
| "@radix-ui/react-use-rect": "1.1.1", | |||
| "@radix-ui/react-use-size": "1.1.1", | |||
| "@radix-ui/rect": "1.1.1" | |||
| }, | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "@types/react-dom": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", | |||
| "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| }, | |||
| "@types/react-dom": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-portal": { | |||
| "version": "1.1.6", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-portal/-/react-portal-1.1.6.tgz", | |||
| "integrity": "sha512-XmsIl2z1n/TsYFLIdYam2rmFwf9OC/Sh2avkbmVMDuBZIe7hSpM0cYnWPAo7nHOVx8zTuwDZGByfcqLdnzp3Vw==", | |||
| "license": "MIT", | |||
| "dependencies": { | |||
| "@radix-ui/react-primitive": "2.1.0", | |||
| "@radix-ui/react-use-layout-effect": "1.1.1" | |||
| }, | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "@types/react-dom": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", | |||
| "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| }, | |||
| "@types/react-dom": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-presence": { | |||
| "version": "1.1.4", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-presence/-/react-presence-1.1.4.tgz", | |||
| "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", | |||
| "license": "MIT", | |||
| "dependencies": { | |||
| "@radix-ui/react-compose-refs": "1.1.2", | |||
| "@radix-ui/react-use-layout-effect": "1.1.1" | |||
| }, | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "@types/react-dom": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", | |||
| "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| }, | |||
| "@types/react-dom": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-primitive": { | |||
| "version": "2.1.0", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-primitive/-/react-primitive-2.1.0.tgz", | |||
| "integrity": "sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw==", | |||
| "license": "MIT", | |||
| "dependencies": { | |||
| "@radix-ui/react-slot": "1.2.0" | |||
| }, | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "@types/react-dom": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", | |||
| "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| }, | |||
| "@types/react-dom": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-slot": { | |||
| "version": "1.2.0", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", | |||
| "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", | |||
| "license": "MIT", | |||
| "dependencies": { | |||
| "@radix-ui/react-compose-refs": "1.1.2" | |||
| }, | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-use-callback-ref": { | |||
| "version": "1.1.1", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", | |||
| "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", | |||
| "license": "MIT", | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-use-controllable-state": { | |||
| "version": "1.2.2", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", | |||
| "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", | |||
| "license": "MIT", | |||
| "dependencies": { | |||
| "@radix-ui/react-use-effect-event": "0.0.2", | |||
| "@radix-ui/react-use-layout-effect": "1.1.1" | |||
| }, | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-use-layout-effect": { | |||
| "version": "1.1.1", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", | |||
| "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", | |||
| "license": "MIT", | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-use-rect": { | |||
| "version": "1.1.1", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", | |||
| "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", | |||
| "license": "MIT", | |||
| "dependencies": { | |||
| "@radix-ui/rect": "1.1.1" | |||
| }, | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-use-size": { | |||
| "version": "1.1.1", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", | |||
| "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", | |||
| "license": "MIT", | |||
| "dependencies": { | |||
| "@radix-ui/react-use-layout-effect": "1.1.1" | |||
| }, | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/rect": { | |||
| "version": "1.1.1", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/rect/-/rect-1.1.1.tgz", | |||
| "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", | |||
| "license": "MIT" | |||
| }, | |||
| "node_modules/@radix-ui/react-icons": { | |||
| "version": "1.3.1", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-icons/-/react-icons-1.3.1.tgz", | |||
| @@ -7319,6 +7654,39 @@ | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-use-effect-event": { | |||
| "version": "0.0.2", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", | |||
| "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", | |||
| "license": "MIT", | |||
| "dependencies": { | |||
| "@radix-ui/react-use-layout-effect": "1.1.1" | |||
| }, | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-use-effect-event/node_modules/@radix-ui/react-use-layout-effect": { | |||
| "version": "1.1.1", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", | |||
| "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", | |||
| "license": "MIT", | |||
| "peerDependencies": { | |||
| "@types/react": "*", | |||
| "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" | |||
| }, | |||
| "peerDependenciesMeta": { | |||
| "@types/react": { | |||
| "optional": true | |||
| } | |||
| } | |||
| }, | |||
| "node_modules/@radix-ui/react-use-escape-keydown": { | |||
| "version": "1.1.0", | |||
| "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz", | |||
| @@ -34,6 +34,7 @@ | |||
| "@radix-ui/react-collapsible": "^1.1.3", | |||
| "@radix-ui/react-dialog": "1.1.4", | |||
| "@radix-ui/react-dropdown-menu": "2.1.4", | |||
| "@radix-ui/react-hover-card": "^1.1.11", | |||
| "@radix-ui/react-icons": "^1.3.1", | |||
| "@radix-ui/react-label": "^2.1.0", | |||
| "@radix-ui/react-navigation-menu": "^1.2.1", | |||
| @@ -23,6 +23,7 @@ export function ConfirmDeleteDialog({ | |||
| children, | |||
| title, | |||
| onOk, | |||
| onCancel, | |||
| hidden = false, | |||
| }: IProps & PropsWithChildren) { | |||
| const { t } = useTranslation(); | |||
| @@ -48,7 +49,9 @@ export function ConfirmDeleteDialog({ | |||
| </AlertDialogDescription> */} | |||
| </AlertDialogHeader> | |||
| <AlertDialogFooter> | |||
| <AlertDialogCancel>{t('common.cancel')}</AlertDialogCancel> | |||
| <AlertDialogCancel onClick={onCancel}> | |||
| {t('common.cancel')} | |||
| </AlertDialogCancel> | |||
| <AlertDialogAction | |||
| className="bg-colors-background-functional-solid-danger text--colors-text-neutral-strong" | |||
| onClick={onOk} | |||
| @@ -0,0 +1,29 @@ | |||
| 'use client'; | |||
| import * as HoverCardPrimitive from '@radix-ui/react-hover-card'; | |||
| import * as React from 'react'; | |||
| import { cn } from '@/lib/utils'; | |||
| const HoverCard = HoverCardPrimitive.Root; | |||
| const HoverCardTrigger = HoverCardPrimitive.Trigger; | |||
| const HoverCardContent = React.forwardRef< | |||
| React.ElementRef<typeof HoverCardPrimitive.Content>, | |||
| React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content> | |||
| >(({ className, align = 'center', sideOffset = 4, ...props }, ref) => ( | |||
| <HoverCardPrimitive.Content | |||
| ref={ref} | |||
| align={align} | |||
| sideOffset={sideOffset} | |||
| className={cn( | |||
| 'z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-hover-card-content-transform-origin]', | |||
| className, | |||
| )} | |||
| {...props} | |||
| /> | |||
| )); | |||
| HoverCardContent.displayName = HoverCardPrimitive.Content.displayName; | |||
| export { HoverCard, HoverCardContent, HoverCardTrigger }; | |||
| @@ -1,11 +1,23 @@ | |||
| import { IDocumentInfo } from '@/interfaces/database/document'; | |||
| import i18n from '@/locales/config'; | |||
| import kbService from '@/services/knowledge-service'; | |||
| import { useMutation, useQueryClient } from '@tanstack/react-query'; | |||
| import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; | |||
| import { useDebounce } from 'ahooks'; | |||
| import { message } from 'antd'; | |||
| import { get } from 'lodash'; | |||
| import { useCallback } from 'react'; | |||
| import { useParams } from 'umi'; | |||
| import { | |||
| useGetPaginationWithRouter, | |||
| useHandleSearchChange, | |||
| } from './logic-hooks'; | |||
| import { useGetKnowledgeSearchParams } from './route-hook'; | |||
| export const enum DocumentApiAction { | |||
| UploadDocument = 'uploadDocument', | |||
| FetchDocumentList = 'fetchDocumentList', | |||
| UpdateDocumentStatus = 'updateDocumentStatus', | |||
| RunDocumentByIds = 'runDocumentByIds', | |||
| } | |||
| export const useUploadNextDocument = () => { | |||
| @@ -47,3 +59,133 @@ export const useUploadNextDocument = () => { | |||
| return { uploadDocument: mutateAsync, loading, data }; | |||
| }; | |||
| export const useFetchDocumentList = () => { | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| const { searchString, handleInputChange } = useHandleSearchChange(); | |||
| const { pagination, setPagination } = useGetPaginationWithRouter(); | |||
| const { id } = useParams(); | |||
| const debouncedSearchString = useDebounce(searchString, { wait: 500 }); | |||
| const { data, isFetching: loading } = useQuery<{ | |||
| docs: IDocumentInfo[]; | |||
| total: number; | |||
| }>({ | |||
| queryKey: [ | |||
| DocumentApiAction.FetchDocumentList, | |||
| debouncedSearchString, | |||
| pagination, | |||
| ], | |||
| initialData: { docs: [], total: 0 }, | |||
| refetchInterval: 15000, | |||
| enabled: !!knowledgeId || !!id, | |||
| queryFn: async () => { | |||
| const ret = await kbService.get_document_list({ | |||
| kb_id: knowledgeId || id, | |||
| keywords: debouncedSearchString, | |||
| page_size: pagination.pageSize, | |||
| page: pagination.current, | |||
| }); | |||
| if (ret.data.code === 0) { | |||
| return ret.data.data; | |||
| } | |||
| return { | |||
| docs: [], | |||
| total: 0, | |||
| }; | |||
| }, | |||
| }); | |||
| const onInputChange: React.ChangeEventHandler<HTMLInputElement> = useCallback( | |||
| (e) => { | |||
| setPagination({ page: 1 }); | |||
| handleInputChange(e); | |||
| }, | |||
| [handleInputChange, setPagination], | |||
| ); | |||
| return { | |||
| loading, | |||
| searchString, | |||
| documents: data.docs, | |||
| pagination: { ...pagination, total: data?.total }, | |||
| handleInputChange: onInputChange, | |||
| setPagination, | |||
| }; | |||
| }; | |||
| export const useSetDocumentStatus = () => { | |||
| const queryClient = useQueryClient(); | |||
| const { | |||
| data, | |||
| isPending: loading, | |||
| mutateAsync, | |||
| } = useMutation({ | |||
| mutationKey: [DocumentApiAction.UpdateDocumentStatus], | |||
| mutationFn: async ({ | |||
| status, | |||
| documentId, | |||
| }: { | |||
| status: boolean; | |||
| documentId: string; | |||
| }) => { | |||
| const { data } = await kbService.document_change_status({ | |||
| doc_id: documentId, | |||
| status: Number(status), | |||
| }); | |||
| if (data.code === 0) { | |||
| message.success(i18n.t('message.modified')); | |||
| queryClient.invalidateQueries({ | |||
| queryKey: [DocumentApiAction.FetchDocumentList], | |||
| }); | |||
| } | |||
| return data; | |||
| }, | |||
| }); | |||
| return { setDocumentStatus: mutateAsync, data, loading }; | |||
| }; | |||
| export const useRunDocument = () => { | |||
| const queryClient = useQueryClient(); | |||
| const { | |||
| data, | |||
| isPending: loading, | |||
| mutateAsync, | |||
| } = useMutation({ | |||
| mutationKey: [DocumentApiAction.RunDocumentByIds], | |||
| mutationFn: async ({ | |||
| documentIds, | |||
| run, | |||
| shouldDelete, | |||
| }: { | |||
| documentIds: string[]; | |||
| run: number; | |||
| shouldDelete: boolean; | |||
| }) => { | |||
| queryClient.invalidateQueries({ | |||
| queryKey: [DocumentApiAction.FetchDocumentList], | |||
| }); | |||
| const ret = await kbService.document_run({ | |||
| doc_ids: documentIds, | |||
| run, | |||
| delete: shouldDelete, | |||
| }); | |||
| const code = get(ret, 'data.code'); | |||
| if (code === 0) { | |||
| queryClient.invalidateQueries({ | |||
| queryKey: [DocumentApiAction.FetchDocumentList], | |||
| }); | |||
| message.success(i18n.t('message.operated')); | |||
| } | |||
| return code; | |||
| }, | |||
| }); | |||
| return { runDocumentByIds: mutateAsync, loading, data }; | |||
| }; | |||
| @@ -0,0 +1,17 @@ | |||
| import { RunningStatus } from '@/constants/knowledge'; | |||
| export const RunningStatusMap = { | |||
| [RunningStatus.UNSTART]: { | |||
| label: 'UNSTART', | |||
| color: 'cyan', | |||
| }, | |||
| [RunningStatus.RUNNING]: { | |||
| label: 'Parsing', | |||
| color: 'blue', | |||
| }, | |||
| [RunningStatus.CANCEL]: { label: 'CANCEL', color: 'orange' }, | |||
| [RunningStatus.DONE]: { label: 'SUCCESS', color: 'blue' }, | |||
| [RunningStatus.FAIL]: { label: 'FAIL', color: 'red' }, | |||
| }; | |||
| export * from '@/constants/knowledge'; | |||
| @@ -23,8 +23,8 @@ import { | |||
| TableHeader, | |||
| TableRow, | |||
| } from '@/components/ui/table'; | |||
| import { useFetchNextDocumentList } from '@/hooks/document-hooks'; | |||
| import { useSetSelectedRecord } from '@/hooks/logic-hooks'; | |||
| import { useFetchDocumentList } from '@/hooks/use-document-request'; | |||
| import { IDocumentInfo } from '@/interfaces/database/document'; | |||
| import { getExtension } from '@/utils/document-util'; | |||
| import { useMemo } from 'react'; | |||
| @@ -38,7 +38,7 @@ export function DatasetTable() { | |||
| pagination, | |||
| // handleInputChange, | |||
| setPagination, | |||
| } = useFetchNextDocumentList(); | |||
| } = useFetchDocumentList(); | |||
| const [sorting, setSorting] = React.useState<SortingState>([]); | |||
| const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>( | |||
| [], | |||
| @@ -2,7 +2,6 @@ import { useSetModalState } from '@/hooks/common-hooks'; | |||
| import { | |||
| useCreateNextDocument, | |||
| useNextWebCrawl, | |||
| useRunNextDocument, | |||
| useSaveNextDocumentName, | |||
| useSetNextDocumentParser, | |||
| } from '@/hooks/document-hooks'; | |||
| @@ -159,35 +158,3 @@ export const useHandleWebCrawl = () => { | |||
| showWebCrawlUploadModal, | |||
| }; | |||
| }; | |||
| export const useHandleRunDocumentByIds = (id: string) => { | |||
| const { runDocumentByIds, loading } = useRunNextDocument(); | |||
| const [currentId, setCurrentId] = useState<string>(''); | |||
| const isLoading = loading && currentId !== '' && currentId === id; | |||
| const handleRunDocumentByIds = async ( | |||
| documentId: string, | |||
| isRunning: boolean, | |||
| shouldDelete: boolean = false, | |||
| ) => { | |||
| if (isLoading) { | |||
| return; | |||
| } | |||
| setCurrentId(documentId); | |||
| try { | |||
| await runDocumentByIds({ | |||
| documentIds: [documentId], | |||
| run: isRunning ? 2 : 1, | |||
| shouldDelete, | |||
| }); | |||
| setCurrentId(''); | |||
| } catch (error) { | |||
| setCurrentId(''); | |||
| } | |||
| }; | |||
| return { | |||
| handleRunDocumentByIds, | |||
| loading: isLoading, | |||
| }; | |||
| }; | |||
| @@ -19,7 +19,7 @@ export default function Dataset() { | |||
| return ( | |||
| <section className="p-8"> | |||
| <ListFilterBar title="Files"> | |||
| <ListFilterBar title="Dataset"> | |||
| <Button | |||
| variant={'tertiary'} | |||
| size={'sm'} | |||
| @@ -0,0 +1,101 @@ | |||
| import { Button } from '@/components/ui/button'; | |||
| import { | |||
| HoverCard, | |||
| HoverCardContent, | |||
| HoverCardTrigger, | |||
| } from '@/components/ui/hover-card'; | |||
| import { IDocumentInfo } from '@/interfaces/database/document'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import reactStringReplace from 'react-string-replace'; | |||
| import { RunningStatus, RunningStatusMap } from './constant'; | |||
| interface IProps { | |||
| record: IDocumentInfo; | |||
| } | |||
| function Dot({ run }: { run: RunningStatus }) { | |||
| const runningStatus = RunningStatusMap[run]; | |||
| return ( | |||
| <span | |||
| className={'size-2 inline-block rounded'} | |||
| style={{ backgroundColor: runningStatus.color }} | |||
| ></span> | |||
| ); | |||
| } | |||
| export const PopoverContent = ({ record }: IProps) => { | |||
| const { t } = useTranslation(); | |||
| const label = t(`knowledgeDetails.runningStatus${record.run}`); | |||
| const replaceText = (text: string) => { | |||
| // Remove duplicate \n | |||
| const nextText = text.replace(/(\n)\1+/g, '$1'); | |||
| const replacedText = reactStringReplace( | |||
| nextText, | |||
| /(\[ERROR\].+\s)/g, | |||
| (match, i) => { | |||
| return ( | |||
| <span key={i} className={'text-red-600'}> | |||
| {match} | |||
| </span> | |||
| ); | |||
| }, | |||
| ); | |||
| return replacedText; | |||
| }; | |||
| const items = [ | |||
| { | |||
| key: 'process_begin_at', | |||
| label: t('knowledgeDetails.processBeginAt'), | |||
| children: record.process_begin_at, | |||
| }, | |||
| { | |||
| key: 'knowledgeDetails.process_duation', | |||
| label: t('processDuration'), | |||
| children: `${record.process_duation.toFixed(2)} s`, | |||
| }, | |||
| { | |||
| key: 'progress_msg', | |||
| label: t('knowledgeDetails.progressMsg'), | |||
| children: replaceText(record.progress_msg.trim()), | |||
| }, | |||
| ]; | |||
| return ( | |||
| <section> | |||
| <div className="flex gap-2 items-center pb-2"> | |||
| <Dot run={record.run}></Dot> {label} | |||
| </div> | |||
| <div className="flex flex-col max-h-[50vh] overflow-auto"> | |||
| {items.map((x, idx) => { | |||
| return ( | |||
| <div key={x.key} className={idx < 2 ? 'flex gap-2' : ''}> | |||
| <b>{x.label}:</b> | |||
| <div className={'w-full whitespace-pre-line text-wrap '}> | |||
| {x.children} | |||
| </div> | |||
| </div> | |||
| ); | |||
| })} | |||
| </div> | |||
| </section> | |||
| ); | |||
| }; | |||
| export function ParsingCard({ record }: IProps) { | |||
| return ( | |||
| <HoverCard> | |||
| <HoverCardTrigger asChild> | |||
| <Button variant={'ghost'} size={'sm'}> | |||
| <Dot run={record.run}></Dot> | |||
| </Button> | |||
| </HoverCardTrigger> | |||
| <HoverCardContent className="w-[40vw]"> | |||
| <PopoverContent record={record}></PopoverContent> | |||
| </HoverCardContent> | |||
| </HoverCard> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,62 @@ | |||
| import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog'; | |||
| import { Button } from '@/components/ui/button'; | |||
| import { Progress } from '@/components/ui/progress'; | |||
| import { Separator } from '@/components/ui/separator'; | |||
| import { IDocumentInfo } from '@/interfaces/database/document'; | |||
| import { CircleX, Play, RefreshCw } from 'lucide-react'; | |||
| import { RunningStatus } from './constant'; | |||
| import { ParsingCard } from './parsing-card'; | |||
| import { useHandleRunDocumentByIds } from './use-run-document'; | |||
| import { isParserRunning } from './utils'; | |||
| const IconMap = { | |||
| [RunningStatus.UNSTART]: <Play />, | |||
| [RunningStatus.RUNNING]: <CircleX />, | |||
| [RunningStatus.CANCEL]: <RefreshCw />, | |||
| [RunningStatus.DONE]: <RefreshCw />, | |||
| [RunningStatus.FAIL]: <RefreshCw />, | |||
| }; | |||
| export function ParsingStatusCell({ record }: { record: IDocumentInfo }) { | |||
| const { run, parser_id, progress, chunk_num, id } = record; | |||
| const operationIcon = IconMap[run]; | |||
| const p = Number((progress * 100).toFixed(2)); | |||
| const { handleRunDocumentByIds } = useHandleRunDocumentByIds(id); | |||
| const isRunning = isParserRunning(run); | |||
| const isZeroChunk = chunk_num === 0; | |||
| const handleOperationIconClick = | |||
| (shouldDelete: boolean = false) => | |||
| () => { | |||
| handleRunDocumentByIds(record.id, isRunning, shouldDelete); | |||
| }; | |||
| return ( | |||
| <section className="flex gap-2 items-center "> | |||
| <div> | |||
| <Button variant={'ghost'} size={'sm'}> | |||
| {parser_id} | |||
| </Button> | |||
| <Separator orientation="vertical" /> | |||
| </div> | |||
| <ConfirmDeleteDialog | |||
| hidden={isZeroChunk} | |||
| onOk={handleOperationIconClick(true)} | |||
| onCancel={handleOperationIconClick(false)} | |||
| > | |||
| <Button | |||
| variant={'ghost'} | |||
| size={'sm'} | |||
| onClick={isZeroChunk ? handleOperationIconClick(false) : () => {}} | |||
| > | |||
| {operationIcon} | |||
| </Button> | |||
| </ConfirmDeleteDialog> | |||
| {isParserRunning(run) ? ( | |||
| <Progress value={p} className="h-1" /> | |||
| ) : ( | |||
| <ParsingCard record={record}></ParsingCard> | |||
| )} | |||
| </section> | |||
| ); | |||
| } | |||
| @@ -16,6 +16,7 @@ import { | |||
| TooltipTrigger, | |||
| } from '@/components/ui/tooltip'; | |||
| import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; | |||
| import { useSetDocumentStatus } from '@/hooks/use-document-request'; | |||
| import { IDocumentInfo } from '@/interfaces/database/document'; | |||
| import { cn } from '@/lib/utils'; | |||
| import { formatDate } from '@/utils/date'; | |||
| @@ -25,6 +26,7 @@ import { ArrowUpDown, MoreHorizontal, Pencil, Wrench } from 'lucide-react'; | |||
| import { useCallback } from 'react'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { useChangeDocumentParser } from './hooks'; | |||
| import { ParsingStatusCell } from './parsing-status-cell'; | |||
| type UseDatasetTableColumnsType = Pick< | |||
| ReturnType<typeof useChangeDocumentParser>, | |||
| @@ -57,6 +59,7 @@ export function useDatasetTableColumns({ | |||
| // }, [setRecord, showSetMetaModal]); | |||
| const { navigateToChunkParsedResult } = useNavigatePage(); | |||
| const { setDocumentStatus } = useSetDocumentStatus(); | |||
| const columns: ColumnDef<IDocumentInfo>[] = [ | |||
| { | |||
| @@ -94,7 +97,7 @@ export function useDatasetTableColumns({ | |||
| </Button> | |||
| ); | |||
| }, | |||
| meta: { cellClassName: 'max-w-[20vw]' }, | |||
| // meta: { cellClassName: 'max-w-[20vw]' }, | |||
| cell: ({ row }) => { | |||
| const name: string = row.getValue('name'); | |||
| @@ -142,20 +145,34 @@ export function useDatasetTableColumns({ | |||
| ), | |||
| }, | |||
| { | |||
| accessorKey: 'parser_id', | |||
| header: t('chunkMethod'), | |||
| accessorKey: 'status', | |||
| header: t('enabled'), | |||
| cell: ({ row }) => { | |||
| const id = row.original.id; | |||
| return ( | |||
| <Switch | |||
| checked={row.getValue('status') === '1'} | |||
| onCheckedChange={(e) => { | |||
| setDocumentStatus({ status: e, documentId: id }); | |||
| }} | |||
| /> | |||
| ); | |||
| }, | |||
| }, | |||
| { | |||
| accessorKey: 'chunk_num', | |||
| header: t('chunkNumber'), | |||
| cell: ({ row }) => ( | |||
| <div className="capitalize">{row.getValue('parser_id')}</div> | |||
| <div className="capitalize">{row.getValue('chunk_num')}</div> | |||
| ), | |||
| }, | |||
| { | |||
| accessorKey: 'run', | |||
| header: t('parsingStatus'), | |||
| cell: ({ row }) => ( | |||
| <Button variant="destructive" size={'sm'}> | |||
| {row.getValue('run')} | |||
| </Button> | |||
| ), | |||
| // meta: { cellClassName: 'min-w-[20vw]' }, | |||
| cell: ({ row }) => { | |||
| return <ParsingStatusCell record={row.original}></ParsingStatusCell>; | |||
| }, | |||
| }, | |||
| { | |||
| id: 'actions', | |||
| @@ -166,7 +183,6 @@ export function useDatasetTableColumns({ | |||
| return ( | |||
| <section className="flex gap-4 items-center"> | |||
| <Switch id="airplane-mode" /> | |||
| <Button | |||
| variant="icon" | |||
| size={'icon'} | |||
| @@ -0,0 +1,34 @@ | |||
| import { useRunDocument } from '@/hooks/use-document-request'; | |||
| import { useState } from 'react'; | |||
| export const useHandleRunDocumentByIds = (id: string) => { | |||
| const { runDocumentByIds, loading } = useRunDocument(); | |||
| const [currentId, setCurrentId] = useState<string>(''); | |||
| const isLoading = loading && currentId !== '' && currentId === id; | |||
| const handleRunDocumentByIds = async ( | |||
| documentId: string, | |||
| isRunning: boolean, | |||
| shouldDelete: boolean = false, | |||
| ) => { | |||
| if (isLoading) { | |||
| return; | |||
| } | |||
| setCurrentId(documentId); | |||
| try { | |||
| await runDocumentByIds({ | |||
| documentIds: [documentId], | |||
| run: isRunning ? 2 : 1, | |||
| shouldDelete, | |||
| }); | |||
| setCurrentId(''); | |||
| } catch (error) { | |||
| setCurrentId(''); | |||
| } | |||
| }; | |||
| return { | |||
| handleRunDocumentByIds, | |||
| loading: isLoading, | |||
| }; | |||
| }; | |||
| @@ -0,0 +1,6 @@ | |||
| import { RunningStatus } from './constant'; | |||
| export const isParserRunning = (text: RunningStatus) => { | |||
| const isRunning = text === RunningStatus.RUNNING; | |||
| return isRunning; | |||
| }; | |||