Dynamic Filters
This commit is contained in:
249
gui/rpk-gui/package-lock.json
generated
249
gui/rpk-gui/package-lock.json
generated
@@ -14,7 +14,7 @@
|
|||||||
"@mui/lab": "^6.0.0-beta.14",
|
"@mui/lab": "^6.0.0-beta.14",
|
||||||
"@mui/material": "^6.1.7",
|
"@mui/material": "^6.1.7",
|
||||||
"@mui/x-data-grid": "^7.22.2",
|
"@mui/x-data-grid": "^7.22.2",
|
||||||
"@react-querybuilder/material": "^8.6.1",
|
"@mui/x-date-pickers": "^8.3.0",
|
||||||
"@refinedev/cli": "^2.16.21",
|
"@refinedev/cli": "^2.16.21",
|
||||||
"@refinedev/core": "^4.47.1",
|
"@refinedev/core": "^4.47.1",
|
||||||
"@refinedev/devtools": "^1.1.32",
|
"@refinedev/devtools": "^1.1.32",
|
||||||
@@ -47,7 +47,6 @@
|
|||||||
"react-error-boundary": "^6.0.0",
|
"react-error-boundary": "^6.0.0",
|
||||||
"react-hook-form": "^7.30.0",
|
"react-hook-form": "^7.30.0",
|
||||||
"react-i18next": "^15.5.1",
|
"react-i18next": "^15.5.1",
|
||||||
"react-querybuilder": "^8.6.1",
|
|
||||||
"react-router": "^7.0.2"
|
"react-router": "^7.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -1988,6 +1987,122 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@mui/x-date-pickers": {
|
||||||
|
"version": "8.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-8.3.0.tgz",
|
||||||
|
"integrity": "sha512-N4Rw/Q6bAHBj7BpLEGE84MG05B56wWQStZch9NqA0U8vTt9TGvkj27fQaboOIikk6ERQTHM2mMoLZgwQAvlhIw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.27.1",
|
||||||
|
"@mui/utils": "^7.0.2",
|
||||||
|
"@mui/x-internals": "8.3.0",
|
||||||
|
"@types/react-transition-group": "^4.4.12",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"prop-types": "^15.8.1",
|
||||||
|
"react-transition-group": "^4.4.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/react": "^11.9.0",
|
||||||
|
"@emotion/styled": "^11.8.1",
|
||||||
|
"@mui/material": "^5.15.14 || ^6.0.0 || ^7.0.0",
|
||||||
|
"@mui/system": "^5.15.14 || ^6.0.0 || ^7.0.0",
|
||||||
|
"date-fns": "^2.25.0 || ^3.2.0 || ^4.0.0",
|
||||||
|
"date-fns-jalali": "^2.13.0-0 || ^3.2.0-0 || ^4.0.0-0",
|
||||||
|
"dayjs": "^1.10.7",
|
||||||
|
"luxon": "^3.0.2",
|
||||||
|
"moment": "^2.29.4",
|
||||||
|
"moment-hijri": "^2.1.2 || ^3.0.0",
|
||||||
|
"moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0",
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@emotion/styled": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"date-fns": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"date-fns-jalali": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"dayjs": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"luxon": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"moment": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"moment-hijri": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"moment-jalaali": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@mui/x-date-pickers/node_modules/@mui/utils": {
|
||||||
|
"version": "7.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.1.0.tgz",
|
||||||
|
"integrity": "sha512-/OM3S8kSHHmWNOP+NH9xEtpYSG10upXeQ0wLZnfDgmgadTAk5F4MQfFLyZ5FCRJENB3eRzltMmaNl6UtDnPovw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.27.1",
|
||||||
|
"@mui/types": "^7.4.2",
|
||||||
|
"@types/prop-types": "^15.7.14",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"prop-types": "^15.8.1",
|
||||||
|
"react-is": "^19.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@mui/x-date-pickers/node_modules/@mui/x-internals": {
|
||||||
|
"version": "8.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-8.3.0.tgz",
|
||||||
|
"integrity": "sha512-wSVxg5aSO9xvJT7oarhsXqr03NeP355Whm7Qn6z3VvxdGwNc7K7vKpez3E+2KMxtdvywOmragwlSdTaO1K6qkg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.27.1",
|
||||||
|
"@mui/utils": "^7.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@mui/x-internals": {
|
"node_modules/@mui/x-internals": {
|
||||||
"version": "7.29.0",
|
"version": "7.29.0",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.29.0.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.29.0.tgz",
|
||||||
@@ -2226,46 +2341,6 @@
|
|||||||
"integrity": "sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ==",
|
"integrity": "sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@react-querybuilder/material": {
|
|
||||||
"version": "8.6.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@react-querybuilder/material/-/material-8.6.1.tgz",
|
|
||||||
"integrity": "sha512-7dXj9z4JC0O+t7UsTfuDkODoTRdKO0mM6Fp6XGIXLsBlfuG9osBTnL8PY+wK/lrI6JSqbaaBZ/aBZegVEn9clA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"peerDependencies": {
|
|
||||||
"@emotion/react": "^11",
|
|
||||||
"@emotion/styled": "^11",
|
|
||||||
"@mui/icons-material": ">=5",
|
|
||||||
"@mui/material": ">=5",
|
|
||||||
"react": ">=18",
|
|
||||||
"react-querybuilder": "8.6.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@reduxjs/toolkit": {
|
|
||||||
"version": "2.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.8.1.tgz",
|
|
||||||
"integrity": "sha512-GLjHS13LiBdiuxSJvfWs3+Cx5yt97mCbuVlDteTusS6VRksPhoWviO8L1e3Re1G94m6lkw/l4pjEEyyNaGf19g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@standard-schema/spec": "^1.0.0",
|
|
||||||
"@standard-schema/utils": "^0.3.0",
|
|
||||||
"immer": "^10.0.3",
|
|
||||||
"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/@refinedev/cli": {
|
"node_modules/@refinedev/cli": {
|
||||||
"version": "2.16.46",
|
"version": "2.16.46",
|
||||||
"resolved": "https://registry.npmjs.org/@refinedev/cli/-/cli-2.16.46.tgz",
|
"resolved": "https://registry.npmjs.org/@refinedev/cli/-/cli-2.16.46.tgz",
|
||||||
@@ -2954,18 +3029,6 @@
|
|||||||
"url": "https://github.com/sindresorhus/is?sponsor=1"
|
"url": "https://github.com/sindresorhus/is?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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/@tanstack/query-core": {
|
"node_modules/@tanstack/query-core": {
|
||||||
"version": "4.36.1",
|
"version": "4.36.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.36.1.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.36.1.tgz",
|
||||||
@@ -6667,16 +6730,6 @@
|
|||||||
"node": ">= 4"
|
"node": ">= 4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/immer": {
|
|
||||||
"version": "10.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
|
|
||||||
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"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",
|
||||||
@@ -8268,15 +8321,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/numeric-quantity": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/numeric-quantity/-/numeric-quantity-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-+Bt2X6YxM5bg8XIBl76NVeG2eL0Y5VQRoyz6GLYrZXW/TDh7We+tGeX4/WZWhaVGOg5ZjNBEOZt9a86slMhOJA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/object-assign": {
|
"node_modules/object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
@@ -9346,27 +9390,6 @@
|
|||||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
|
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/react-querybuilder": {
|
|
||||||
"version": "8.6.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-querybuilder/-/react-querybuilder-8.6.1.tgz",
|
|
||||||
"integrity": "sha512-1A+eQgTM3WRCNo5+Rvfdyq9fn+QH2pC3dCbrW+hj9Pas5sgNzeXDCJXPKg3Pu5Ujo70dBMBn5K5mrl8/YEp3tg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@reduxjs/toolkit": "^2.7.0",
|
|
||||||
"immer": "^10.1.1",
|
|
||||||
"numeric-quantity": "^2.0.1",
|
|
||||||
"react-redux": "^9.2.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"json-logic-js": "^2",
|
|
||||||
"react": ">=18"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"json-logic-js": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-reconciler": {
|
"node_modules/react-reconciler": {
|
||||||
"version": "0.29.2",
|
"version": "0.29.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.29.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.29.2.tgz",
|
||||||
@@ -9383,29 +9406,6 @@
|
|||||||
"react": "^18.3.1"
|
"react": "^18.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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",
|
|
||||||
"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",
|
||||||
@@ -9526,21 +9526,6 @@
|
|||||||
"esprima": "~4.0.0"
|
"esprima": "~4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"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/remark-gfm": {
|
"node_modules/remark-gfm": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-1.0.0.tgz",
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
"@mui/lab": "^6.0.0-beta.14",
|
"@mui/lab": "^6.0.0-beta.14",
|
||||||
"@mui/material": "^6.1.7",
|
"@mui/material": "^6.1.7",
|
||||||
"@mui/x-data-grid": "^7.22.2",
|
"@mui/x-data-grid": "^7.22.2",
|
||||||
"@react-querybuilder/material": "^8.6.1",
|
"@mui/x-date-pickers": "^8.3.0",
|
||||||
"@refinedev/cli": "^2.16.21",
|
"@refinedev/cli": "^2.16.21",
|
||||||
"@refinedev/core": "^4.47.1",
|
"@refinedev/core": "^4.47.1",
|
||||||
"@refinedev/devtools": "^1.1.32",
|
"@refinedev/devtools": "^1.1.32",
|
||||||
@@ -43,7 +43,6 @@
|
|||||||
"react-error-boundary": "^6.0.0",
|
"react-error-boundary": "^6.0.0",
|
||||||
"react-hook-form": "^7.30.0",
|
"react-hook-form": "^7.30.0",
|
||||||
"react-i18next": "^15.5.1",
|
"react-i18next": "^15.5.1",
|
||||||
"react-querybuilder": "^8.6.1",
|
|
||||||
"react-router": "^7.0.2"
|
"react-router": "^7.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -1,22 +1,32 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { jsonschemaProvider } from "../providers/jsonschema-provider";
|
import { jsonschemaProvider } from "../providers/jsonschema-provider";
|
||||||
import { CircularProgress } from "@mui/material";
|
import { Accordion, AccordionDetails, AccordionSummary, CircularProgress } from "@mui/material";
|
||||||
import { QueryBuilderMaterial } from "@react-querybuilder/material";
|
import FilterForm from "../../filter-form/components/filter-form";
|
||||||
import { QueryBuilder, type RuleGroupType } from 'react-querybuilder';
|
import TextField from "@mui/material/TextField";
|
||||||
|
import { useSearchParams } from "react-router";
|
||||||
|
import { GridExpandMoreIcon } from "@mui/x-data-grid";
|
||||||
|
|
||||||
|
export type OnChangeValue = {
|
||||||
|
search: string | null
|
||||||
|
filters: {[filter: string]: {[op: string]: string}}
|
||||||
|
}
|
||||||
|
|
||||||
type CrudFiltersProps = {
|
type CrudFiltersProps = {
|
||||||
resourceName: string
|
resourceName: string
|
||||||
resourcePath: string
|
resourcePath: string
|
||||||
|
onChange: (value: OnChangeValue) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const CrudFilters = (props: CrudFiltersProps) => {
|
const CrudFilters = (props: CrudFiltersProps) => {
|
||||||
const { resourceName, resourcePath } = props
|
const { resourceName, resourcePath, onChange } = props
|
||||||
|
|
||||||
|
const [hasSearch, setHasSearch] = useState(false)
|
||||||
const [filtersSchema, setFiltersSchema] = useState<any[]>([])
|
const [filtersSchema, setFiltersSchema] = useState<any[]>([])
|
||||||
const [filtersLoading, setFiltersLoading] = useState(true);
|
const [filtersLoading, setFiltersLoading] = useState(true);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchSchema = async () => {
|
const fetchSchema = async () => {
|
||||||
try {
|
try {
|
||||||
|
setHasSearch(await jsonschemaProvider.hasSearch(resourcePath))
|
||||||
const resourceFilters = await jsonschemaProvider.getListFilters(resourceName, resourcePath)
|
const resourceFilters = await jsonschemaProvider.getListFilters(resourceName, resourcePath)
|
||||||
setFiltersSchema(resourceFilters);
|
setFiltersSchema(resourceFilters);
|
||||||
console.log(resourceFilters);
|
console.log(resourceFilters);
|
||||||
@@ -29,20 +39,57 @@ const CrudFilters = (props: CrudFiltersProps) => {
|
|||||||
fetchSchema();
|
fetchSchema();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const [query, setQuery] = useState<RuleGroupType>({ combinator: 'and', rules: [] });
|
|
||||||
|
|
||||||
if (filtersLoading) {
|
if (filtersLoading) {
|
||||||
return <CircularProgress />
|
return <CircularProgress />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let currentValue = {
|
||||||
|
search: "",
|
||||||
|
filters: {}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<QueryBuilderMaterial>
|
<>
|
||||||
<QueryBuilder
|
{hasSearch &&
|
||||||
fields={filtersSchema}
|
<SearchFilter value="" onChange={(value) => {
|
||||||
defaultQuery={query}
|
currentValue.search = value;
|
||||||
onQueryChange={setQuery} />
|
onChange(currentValue);
|
||||||
</QueryBuilderMaterial>
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
<Accordion>
|
||||||
|
<AccordionSummary
|
||||||
|
expandIcon={<>Advanced filters<GridExpandMoreIcon /></>}
|
||||||
|
/>
|
||||||
|
<AccordionDetails>
|
||||||
|
<FilterForm fields={filtersSchema} values={{}} onChange={(value) => {
|
||||||
|
currentValue.filters = value;
|
||||||
|
onChange(currentValue);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</AccordionDetails>
|
||||||
|
</Accordion>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CrudFilters;
|
export default CrudFilters;
|
||||||
|
|
||||||
|
type SearchFilter = {
|
||||||
|
value: string
|
||||||
|
onChange: (value: any) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const SearchFilter = (props: SearchFilter) => {
|
||||||
|
const {value, onChange} = props;
|
||||||
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TextField
|
||||||
|
label="schemas.search"
|
||||||
|
fullWidth={true}
|
||||||
|
onChange={(event) => onChange(event.target.value)}
|
||||||
|
defaultValue={value}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import i18n from '../../../i18n'
|
|||||||
import { JSONSchema7Definition } from "json-schema";
|
import { JSONSchema7Definition } from "json-schema";
|
||||||
import { GridColDef } from "@mui/x-data-grid";
|
import { GridColDef } from "@mui/x-data-grid";
|
||||||
import { GridColType } from "@mui/x-data-grid/models/colDef/gridColType";
|
import { GridColType } from "@mui/x-data-grid/models/colDef/gridColType";
|
||||||
import { Field as FilterField } from "react-querybuilder";
|
|
||||||
|
|
||||||
const API_URL = "/api/v1";
|
const API_URL = "/api/v1";
|
||||||
|
|
||||||
@@ -72,6 +71,10 @@ export const jsonschemaProvider = {
|
|||||||
|
|
||||||
getListFilters: async (resourceName: string, resourcePath: string): Promise<FilterField[]> => {
|
getListFilters: async (resourceName: string, resourcePath: string): Promise<FilterField[]> => {
|
||||||
return getFilters(resourceName, resourcePath);
|
return getFilters(resourceName, resourcePath);
|
||||||
|
},
|
||||||
|
|
||||||
|
hasSearch: async (resourcePath: string): Promise<boolean> => {
|
||||||
|
return hasSearch(resourcePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,36 +91,30 @@ function shortenResourceName(resourceName: string) {
|
|||||||
return convertCamelToSnake(resourceName.replace(/(-Input|-Output|Create|Update|Read)$/g, ""));
|
return convertCamelToSnake(resourceName.replace(/(-Input|-Output|Create|Update|Read)$/g, ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FilterField = {
|
||||||
|
name: string,
|
||||||
|
label: string,
|
||||||
|
type: string,
|
||||||
|
operators: { name: string, label: string }[]
|
||||||
|
}
|
||||||
|
|
||||||
|
async function hasSearch(resourcePath: string): Promise<boolean> {
|
||||||
|
const jst = new JsonSchemaTraverser(await getJsonschema());
|
||||||
|
const pathSchema = jst.getPath(resourcePath);
|
||||||
|
for (const param of pathSchema.parameters) {
|
||||||
|
if (param.name == "search") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
async function getFilters(resourceName: string, resourcePath: string): Promise<FilterField[]> {
|
async function getFilters(resourceName: string, resourcePath: string): Promise<FilterField[]> {
|
||||||
const shortResourceName = shortenResourceName(resourceName);
|
const shortResourceName = shortenResourceName(resourceName);
|
||||||
const rawSchemas = await getJsonschema()
|
const jst = new JsonSchemaTraverser(await getJsonschema());
|
||||||
const resourceParts = `/${resourcePath}/`.split("/")
|
const pathSchema = jst.getPath(resourcePath);
|
||||||
let pathSchema: PathSchema|undefined;
|
|
||||||
for (const path in rawSchemas.paths) {
|
|
||||||
if (rawSchemas.paths[path].hasOwnProperty("get")) {
|
|
||||||
const pathParts = path.split("/")
|
|
||||||
if (pathParts.length == resourceParts.length) {
|
|
||||||
for (let i = 0; i < pathParts.length; i++) {
|
|
||||||
const isVariable = pathParts[i].slice(0,1) == "{" && pathParts[i].slice(-1) == "}";
|
|
||||||
if (! isVariable && pathParts[i] != resourceParts[i] ) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == pathParts.length - 1) {
|
|
||||||
pathSchema = rawSchemas.paths[path].get
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pathSchema !== undefined) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pathSchema === undefined) {
|
|
||||||
throw ("Path not found in schema");
|
|
||||||
}
|
|
||||||
|
|
||||||
const jst = new JsonSchemaTraverser(rawSchemas)
|
|
||||||
const seen: { [k: string]: number } = {};
|
const seen: { [k: string]: number } = {};
|
||||||
let filters: FilterField[] = []
|
let filters: FilterField[] = []
|
||||||
for (const param of pathSchema.parameters) {
|
for (const param of pathSchema.parameters) {
|
||||||
@@ -126,10 +123,10 @@ async function getFilters(resourceName: string, resourcePath: string): Promise<F
|
|||||||
if (! seen.hasOwnProperty(fieldName)) {
|
if (! seen.hasOwnProperty(fieldName)) {
|
||||||
seen[fieldName] = filters.length;
|
seen[fieldName] = filters.length;
|
||||||
const field = jst.getPropertyByPath(jst.getResource(`${resourceName}Read`), fieldName)
|
const field = jst.getPropertyByPath(jst.getResource(`${resourceName}Read`), fieldName)
|
||||||
|
|
||||||
filters.push({
|
filters.push({
|
||||||
name: fieldName,
|
name: fieldName,
|
||||||
label: i18n.t(`schemas.${shortResourceName}.${convertCamelToSnake(fieldName)}`),
|
label: i18n.t(`schemas.${shortResourceName}.${convertCamelToSnake(fieldName)}`),
|
||||||
|
type: getFieldType(fieldName, field),
|
||||||
operators: [{ name: operator, label: operator }]
|
operators: [{ name: operator, label: operator }]
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -157,6 +154,32 @@ function processParamName(param: PathParameter): Operator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getFieldType = (fieldName: string, field: RJSFSchema): string => {
|
||||||
|
if (fieldName == "created_by" || fieldName == "updated_by") {
|
||||||
|
return "author";
|
||||||
|
} else if (Array.isArray(field)) {
|
||||||
|
let enumValues = [];
|
||||||
|
for (const f of field) {
|
||||||
|
enumValues.push(f.const)
|
||||||
|
}
|
||||||
|
return `enum(${enumValues.join("|")})`
|
||||||
|
} else if (field.hasOwnProperty('type')) {
|
||||||
|
if (field.type == "string" && field.format == "date-time") {
|
||||||
|
return "dateTime";
|
||||||
|
}
|
||||||
|
return field.type as string;
|
||||||
|
} else if (field.hasOwnProperty('anyOf') && field.anyOf) {
|
||||||
|
for (const prop of field.anyOf) {
|
||||||
|
if (typeof prop != "boolean" && prop.type != "null") {
|
||||||
|
return prop.type as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return "null";
|
||||||
|
}
|
||||||
|
throw "Unimplemented field type"
|
||||||
|
}
|
||||||
|
|
||||||
const getColumns = async (resourceName: string): Promise<GridColDef[]> => {
|
const getColumns = async (resourceName: string): Promise<GridColDef[]> => {
|
||||||
return buildColumns(await getJsonschema(), resourceName)
|
return buildColumns(await getJsonschema(), resourceName)
|
||||||
}
|
}
|
||||||
@@ -383,6 +406,35 @@ const JsonSchemaTraverser = class {
|
|||||||
return this.rawSchemas.components.schemas[resourceName]
|
return this.rawSchemas.components.schemas[resourceName]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getPath = (resourcePath: string) => {
|
||||||
|
const resourceParts = `/${resourcePath}/`.split("/");
|
||||||
|
let pathSchema: PathSchema|undefined;
|
||||||
|
for (const path in this.rawSchemas.paths) {
|
||||||
|
if (this.rawSchemas.paths[path].hasOwnProperty("get")) {
|
||||||
|
const pathParts = path.split("/")
|
||||||
|
if (pathParts.length == resourceParts.length) {
|
||||||
|
for (let i = 0; i < pathParts.length; i++) {
|
||||||
|
const isVariable = pathParts[i].slice(0,1) == "{" && pathParts[i].slice(-1) == "}";
|
||||||
|
if (! isVariable && pathParts[i] != resourceParts[i] ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == pathParts.length - 1) {
|
||||||
|
pathSchema = this.rawSchemas.paths[path].get
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pathSchema !== undefined) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pathSchema === undefined) {
|
||||||
|
throw ("Path not found in schema");
|
||||||
|
}
|
||||||
|
return pathSchema
|
||||||
|
}
|
||||||
|
|
||||||
public hasDescendant = (resource: RJSFSchema, property_name: string): boolean => {
|
public hasDescendant = (resource: RJSFSchema, property_name: string): boolean => {
|
||||||
if (is_array(resource)) {
|
if (is_array(resource)) {
|
||||||
return property_name == 'items';
|
return property_name == 'items';
|
||||||
@@ -406,7 +458,7 @@ const JsonSchemaTraverser = class {
|
|||||||
throw new Error("Jsonschema format not implemented in property finder");
|
throw new Error("Jsonschema format not implemented in property finder");
|
||||||
}
|
}
|
||||||
|
|
||||||
public getDescendant = (resource: RJSFSchema, property_name: string): RJSFSchema => {
|
public getDescendant = (resource: RJSFSchema, property_name: string): RJSFSchema | RJSFSchema[] => {
|
||||||
if (is_array(resource) && property_name == 'items') {
|
if (is_array(resource) && property_name == 'items') {
|
||||||
return resource.items as RJSFSchema;
|
return resource.items as RJSFSchema;
|
||||||
} else if (is_object(resource) && resource.properties !== undefined && property_name in resource.properties!) {
|
} else if (is_object(resource) && resource.properties !== undefined && property_name in resource.properties!) {
|
||||||
@@ -416,11 +468,15 @@ const JsonSchemaTraverser = class {
|
|||||||
let subresource = this.getResource(subresourceName);
|
let subresource = this.getResource(subresourceName);
|
||||||
return this.getDescendant(subresource, property_name);
|
return this.getDescendant(subresource, property_name);
|
||||||
} else if (is_union(resource)) {
|
} else if (is_union(resource)) {
|
||||||
|
let descendants: RJSFSchema[] = [];
|
||||||
for (const ref of resource.oneOf!) {
|
for (const ref of resource.oneOf!) {
|
||||||
if (this.hasDescendant(ref as RJSFSchema, property_name)) {
|
if (this.hasDescendant(ref as RJSFSchema, property_name)) {
|
||||||
return this.getDescendant(ref as RJSFSchema, property_name);
|
descendants.push(this.getDescendant(ref as RJSFSchema, property_name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (descendants.length > 0) {
|
||||||
|
return descendants;
|
||||||
|
}
|
||||||
} else if (is_enum(resource)) {
|
} else if (is_enum(resource)) {
|
||||||
for (const ref of resource.allOf!) {
|
for (const ref of resource.allOf!) {
|
||||||
if (this.hasDescendant(ref as RJSFSchema, property_name)) {
|
if (this.hasDescendant(ref as RJSFSchema, property_name)) {
|
||||||
|
|||||||
184
gui/rpk-gui/src/lib/filter-form/components/filter-form.tsx
Normal file
184
gui/rpk-gui/src/lib/filter-form/components/filter-form.tsx
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
import TextField from "@mui/material/TextField";
|
||||||
|
import { DateTimePicker } from "@mui/x-date-pickers/DateTimePicker";
|
||||||
|
import Autocomplete from "@mui/material/Autocomplete";
|
||||||
|
import { FirmContext } from "../../../contexts/FirmContext";
|
||||||
|
import { useContext } from "react";
|
||||||
|
import { Box, Grid2, styled } from "@mui/material";
|
||||||
|
import Stack from "@mui/material/Stack";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
export type FilterField = {
|
||||||
|
name: string
|
||||||
|
label: string
|
||||||
|
type: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type FilterFormProps = {
|
||||||
|
values: {[field_name: string]: { [operator: string]: string }}
|
||||||
|
fields: FilterField[]
|
||||||
|
onChange: (value: any) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const FilterForm = (props: FilterFormProps) => {
|
||||||
|
const { fields, values, onChange } = props;
|
||||||
|
|
||||||
|
let currentValue = values
|
||||||
|
|
||||||
|
const formField = fields.filter(f => f.name != "search").map((f, index) =>
|
||||||
|
<>
|
||||||
|
{ f.name == "created_at" && <Box width="100%" key={`created_at_break-${index}`} /> }
|
||||||
|
<Grid2 size={6} key={`${f.name}-${index}`} >
|
||||||
|
<FilterFormField
|
||||||
|
field={f}
|
||||||
|
value={values.hasOwnProperty(f.name) ? values[f.name] : {}}
|
||||||
|
onChange={(value) => {
|
||||||
|
for (const op in value) {
|
||||||
|
if (value[op] == null || value[op] == "") {
|
||||||
|
if (currentValue.hasOwnProperty(f.name)) {
|
||||||
|
if (currentValue[f.name].hasOwnProperty(op)) {
|
||||||
|
delete currentValue[f.name][op];
|
||||||
|
}
|
||||||
|
if (Object.entries(currentValue[f.name]).length == 0) {
|
||||||
|
delete currentValue[f.name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (! currentValue.hasOwnProperty(f.name)) {
|
||||||
|
currentValue[f.name] = {};
|
||||||
|
}
|
||||||
|
currentValue[f.name][op] = value[op];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onChange(currentValue);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid2>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form>
|
||||||
|
<Grid2 container spacing={2}>
|
||||||
|
{formField}
|
||||||
|
</Grid2>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type OperatorValue = { [operator: string]: string }
|
||||||
|
|
||||||
|
type FilterFormFieldProps = {
|
||||||
|
field: FilterField
|
||||||
|
value: OperatorValue
|
||||||
|
onChange: (value: { [operator: string]: string | null }) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const FilterFormField = (props: FilterFormFieldProps) => {
|
||||||
|
const { field, value, onChange } = props;
|
||||||
|
|
||||||
|
if (field.type == "string") {
|
||||||
|
const defaultValue = value.hasOwnProperty('ilike') ? value['ilike'] : undefined
|
||||||
|
return (
|
||||||
|
<TextField
|
||||||
|
name={field.name}
|
||||||
|
label={field.label}
|
||||||
|
defaultValue={defaultValue}
|
||||||
|
fullWidth={true}
|
||||||
|
onChange={(event) => onChange({"ilike": event.target.value})}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
} else if (field.type == "dateTime") {
|
||||||
|
return (
|
||||||
|
<FilterFieldDateRange field={field} value={value} onChange={onChange} />
|
||||||
|
)
|
||||||
|
} else if (field.type == "author") {
|
||||||
|
return (
|
||||||
|
<FilterFieldAuthor field={field} value={value} onChange={onChange} />
|
||||||
|
);
|
||||||
|
} else if (field.type.slice(0, 4) == "enum") {
|
||||||
|
const values = field.type.slice(5,-1).split("|");
|
||||||
|
const defaultValue = value.hasOwnProperty("in") ? [{
|
||||||
|
value: value["in"],
|
||||||
|
label: value["in"]
|
||||||
|
}] : undefined;
|
||||||
|
return (
|
||||||
|
<Autocomplete
|
||||||
|
multiple
|
||||||
|
options={values.map(opt => {
|
||||||
|
return {
|
||||||
|
value: opt,
|
||||||
|
label: opt
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
onChange={(event, value) => onChange({ "in": value.map(v => v.value).join(",") })}
|
||||||
|
getOptionLabel={(option) => option.label}
|
||||||
|
defaultValue={defaultValue}
|
||||||
|
renderInput={(params) => (
|
||||||
|
<TextField
|
||||||
|
{...params}
|
||||||
|
label={field.label}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
throw("Unsupported field filter type");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type FilterFieldAuthorProp = FilterFormFieldProps
|
||||||
|
|
||||||
|
const FilterFieldAuthor = (props: FilterFieldAuthorProp) => {
|
||||||
|
const { field, onChange } = props;
|
||||||
|
const { partnerMap } = useContext(FirmContext)
|
||||||
|
|
||||||
|
if (partnerMap == undefined) {
|
||||||
|
throw "Can't use author filter outside of the context of a firm";
|
||||||
|
}
|
||||||
|
|
||||||
|
let options = []
|
||||||
|
for(let key of Array.from(partnerMap.keys()) ) {
|
||||||
|
options.push({
|
||||||
|
id: key,
|
||||||
|
label: partnerMap.get(key)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Autocomplete
|
||||||
|
multiple
|
||||||
|
renderInput={(params) => <TextField {...params} label={field.label} />}
|
||||||
|
options={options}
|
||||||
|
onChange={(event, value) => onChange({ "in": value.length == 0 ? null : value.map(v => v.id).join(",") })}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const FilterFieldDateRange = (props: FilterFormFieldProps) => {
|
||||||
|
const { field, value, onChange } = props;
|
||||||
|
|
||||||
|
const defaultAfterValue = value.hasOwnProperty('gte') ? dayjs(value['gte']) : undefined;
|
||||||
|
const defaultBeforeValue = value.hasOwnProperty('lte') ? dayjs(value['lte']) : undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack direction="row">
|
||||||
|
<DateTimePicker
|
||||||
|
name={field.name}
|
||||||
|
label={`${field.label} After:`}
|
||||||
|
slotProps={{ textField: { fullWidth: true }, field: { clearable: true }}}
|
||||||
|
defaultValue={defaultAfterValue}
|
||||||
|
onChange={(value) => onChange({'gte': value === null ? null : value.toJSON()})}
|
||||||
|
/>
|
||||||
|
<DateTimePicker
|
||||||
|
name={field.name}
|
||||||
|
label={`${field.label} Before:`}
|
||||||
|
slotProps={{ textField: { fullWidth: true }, field: { clearable: true } }}
|
||||||
|
defaultValue={defaultBeforeValue}
|
||||||
|
onChange={(value) => onChange({'lte': value === null ? null : value.toJSON()})}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FilterForm;
|
||||||
|
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
import React, { useContext } from "react";
|
import React, { useContext } from "react";
|
||||||
import { Link, useNavigate } from "react-router"
|
import { Link, useNavigate } from "react-router"
|
||||||
import { UiSchema } from "@rjsf/utils";
|
import { UiSchema } from "@rjsf/utils";
|
||||||
import { useTranslation } from "@refinedev/core";
|
import { CrudFilter, useTranslation } from "@refinedev/core";
|
||||||
import { List as RefineList, useDataGrid } from "@refinedev/mui";
|
import { List as RefineList, useDataGrid } from "@refinedev/mui";
|
||||||
import { Button } from "@mui/material";
|
import { Button } from "@mui/material";
|
||||||
import { GridColDef, GridValidRowModel } from "@mui/x-data-grid";
|
import { GridColDef, GridValidRowModel } from "@mui/x-data-grid";
|
||||||
import { FirmContext } from "../../../contexts/FirmContext";
|
import { FirmContext } from "../../../contexts/FirmContext";
|
||||||
import CrudList from "../../../lib/crud/components/crud-list";
|
import CrudList from "../../../lib/crud/components/crud-list";
|
||||||
import CrudFilters from "../../../lib/crud/components/crud-filters";
|
import CrudFilters, { OnChangeValue } from "../../../lib/crud/components/crud-filters";
|
||||||
|
import { CrudOperators } from "@refinedev/core/src/contexts/data/types";
|
||||||
|
|
||||||
type ListProps = {
|
type ListProps = {
|
||||||
resource: string,
|
resource: string,
|
||||||
@@ -28,13 +29,34 @@ const List = <T extends GridValidRowModel>(props: ListProps) => {
|
|||||||
const { currentFirm } = useContext(FirmContext);
|
const { currentFirm } = useContext(FirmContext);
|
||||||
const resourceBasePath = `firm/${currentFirm.instance}/${currentFirm.firm}`
|
const resourceBasePath = `firm/${currentFirm.instance}/${currentFirm.firm}`
|
||||||
|
|
||||||
const { dataGridProps, tableQueryResult } = useDataGrid<T>({
|
const { dataGridProps, tableQuery, setFilters } = useDataGrid<T>({
|
||||||
resource: `${resourceBasePath}/${resource}`,
|
resource: `${resourceBasePath}/${resource}`,
|
||||||
});
|
});
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
if (tableQueryResult.error?.status == 404) {
|
if (tableQuery.error?.status == 404) {
|
||||||
throw tableQueryResult.error
|
throw tableQuery.error
|
||||||
|
}
|
||||||
|
|
||||||
|
const onFilterChange = (value: OnChangeValue) => {
|
||||||
|
let newFilters: CrudFilter[] = []
|
||||||
|
if (value.search != null) {
|
||||||
|
newFilters.push({
|
||||||
|
field: "search",
|
||||||
|
operator: "eq",
|
||||||
|
value: value.search
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for (const filterName in value.filters) {
|
||||||
|
for (const operator in value.filters[filterName]) {
|
||||||
|
newFilters.push({
|
||||||
|
field: filterName,
|
||||||
|
operator: operator as Exclude<CrudOperators, "or" | "and">,
|
||||||
|
value: value.filters[filterName][operator]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setFilters(newFilters);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -45,6 +67,7 @@ const List = <T extends GridValidRowModel>(props: ListProps) => {
|
|||||||
<CrudFilters
|
<CrudFilters
|
||||||
resourceName={schemaName}
|
resourceName={schemaName}
|
||||||
resourcePath={`${resourceBasePath}/${resource}`}
|
resourcePath={`${resourceBasePath}/${resource}`}
|
||||||
|
onChange={onFilterChange}
|
||||||
/>
|
/>
|
||||||
<CrudList
|
<CrudList
|
||||||
dataGridProps={dataGridProps}
|
dataGridProps={dataGridProps}
|
||||||
|
|||||||
@@ -61,8 +61,12 @@ const dataProvider: DataProvider = {
|
|||||||
|
|
||||||
if (filters && filters.length > 0) {
|
if (filters && filters.length > 0) {
|
||||||
filters.forEach((filter) => {
|
filters.forEach((filter) => {
|
||||||
if ("field" in filter && filter.value && filter.operator === "contains") {
|
if ("field" in filter) {
|
||||||
params.append(filter.field + "__ilike", filter.value);
|
if (filter.field == "search") {
|
||||||
|
params.append("search", filter.value);
|
||||||
|
} else {
|
||||||
|
params.append(`${filter.field.replace(".", "__")}__${filter.operator}`, filter.value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user