feat: add survey download feature
This commit is contained in:
parent
6674c19e87
commit
eebf00f882
181
pnpm-lock.yaml
181
pnpm-lock.yaml
@ -155,6 +155,12 @@ importers:
|
||||
'@radix-ui/react-menubar':
|
||||
specifier: ^1.0.4
|
||||
version: 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-popover':
|
||||
specifier: ^1.0.7
|
||||
version: 1.0.7(@types/react-dom@18.2.7)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-progress':
|
||||
specifier: ^1.0.3
|
||||
version: 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-scroll-area':
|
||||
specifier: ^1.0.5
|
||||
version: 1.0.5(@types/react-dom@18.2.7)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0)
|
||||
@ -200,12 +206,15 @@ importers:
|
||||
'@trpc/react-query':
|
||||
specifier: ^10.45.0
|
||||
version: 10.45.0(@tanstack/react-query@4.33.0)(@trpc/client@10.45.0)(@trpc/server@10.45.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@types/jsonexport':
|
||||
specifier: ^3.0.5
|
||||
version: 3.0.5
|
||||
ahooks:
|
||||
specifier: ^3.7.10
|
||||
version: 3.7.10(react@18.2.0)
|
||||
antd:
|
||||
specifier: ^5.13.1
|
||||
version: 5.13.1(react-dom@18.2.0)(react@18.2.0)
|
||||
version: 5.13.1(date-fns@3.6.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
array-move:
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1
|
||||
@ -230,6 +239,9 @@ importers:
|
||||
copy-to-clipboard:
|
||||
specifier: ^3.3.3
|
||||
version: 3.3.3
|
||||
date-fns:
|
||||
specifier: ^3.6.0
|
||||
version: 3.6.0
|
||||
dayjs:
|
||||
specifier: 1.11.10
|
||||
version: 1.11.10
|
||||
@ -242,6 +254,9 @@ importers:
|
||||
fuse.js:
|
||||
specifier: ^7.0.0
|
||||
version: 7.0.0
|
||||
jsonexport:
|
||||
specifier: ^3.2.0
|
||||
version: 3.2.0
|
||||
leaflet:
|
||||
specifier: ^1.9.4
|
||||
version: 1.9.4
|
||||
@ -263,6 +278,9 @@ importers:
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.2.0
|
||||
react-day-picker:
|
||||
specifier: ^8.10.1
|
||||
version: 8.10.1(date-fns@3.6.0)(react@18.2.0)
|
||||
react-dom:
|
||||
specifier: ^18.2.0
|
||||
version: 18.2.0(react@18.2.0)
|
||||
@ -881,7 +899,7 @@ packages:
|
||||
'@ant-design/icons': 5.3.6(react-dom@18.2.0)(react@18.2.0)
|
||||
'@ant-design/maps': 1.0.8(react-dom@18.2.0)(react@18.2.0)
|
||||
'@ant-design/plots': 1.2.6(react-dom@18.2.0)(react@18.2.0)
|
||||
antd: 5.13.1(react-dom@18.2.0)(react@18.2.0)
|
||||
antd: 5.13.1(date-fns@3.6.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
lodash: 4.17.21
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
@ -953,7 +971,7 @@ packages:
|
||||
'@antv/x6-react-components': 1.1.20(antd@5.13.1)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@antv/x6-react-shape': 1.6.5(@antv/x6@1.35.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@antv/xflow': 1.1.52(@ant-design/icons@5.3.6)(antd@5.13.1)(classnames@2.5.1)(lodash@4.17.21)(react-dom@18.2.0)(react@18.2.0)(reflect-metadata@0.1.14)
|
||||
antd: 5.13.1(react-dom@18.2.0)(react@18.2.0)
|
||||
antd: 5.13.1(date-fns@3.6.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
lodash: 4.17.21
|
||||
react: 18.2.0
|
||||
react-color: 2.17.3(react@18.2.0)
|
||||
@ -1737,7 +1755,7 @@ packages:
|
||||
react: '>=16.8.6 || >=17.0.0'
|
||||
react-dom: '>=16.8.6 || >=17.0.0'
|
||||
dependencies:
|
||||
antd: 5.13.1(react-dom@18.2.0)(react@18.2.0)
|
||||
antd: 5.13.1(date-fns@3.6.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
clamp: 1.0.1
|
||||
classnames: 2.5.1
|
||||
rc-dropdown: 3.6.2(react-dom@18.2.0)(react@18.2.0)
|
||||
@ -1789,7 +1807,7 @@ packages:
|
||||
'@antv/x6-react-components': 1.1.20(antd@5.13.1)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@antv/x6-react-shape': 1.6.5(@antv/x6@1.35.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@antv/xflow-hook': 1.0.52
|
||||
antd: 5.13.1(react-dom@18.2.0)(react@18.2.0)
|
||||
antd: 5.13.1(date-fns@3.6.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
classnames: 2.5.1
|
||||
immer: 9.0.21
|
||||
lodash: 4.17.21
|
||||
@ -1820,7 +1838,7 @@ packages:
|
||||
'@antv/x6-react-shape': 1.6.5(@antv/x6@1.35.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@antv/xflow-core': 1.1.52(@ant-design/icons@5.3.6)(@antv/x6-react-components@1.1.20)(@antv/x6-react-shape@1.6.5)(@antv/x6@1.35.0)(antd@5.13.1)(lodash@4.17.21)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@antv/xflow-hook': 1.0.52
|
||||
antd: 5.13.1(react-dom@18.2.0)(react@18.2.0)
|
||||
antd: 5.13.1(date-fns@3.6.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
classnames: 2.5.1
|
||||
mana-syringe: 0.2.2
|
||||
moment: 2.30.1
|
||||
@ -1862,7 +1880,7 @@ packages:
|
||||
'@antv/xflow-core': 1.1.52(@ant-design/icons@5.3.6)(@antv/x6-react-components@1.1.20)(@antv/x6-react-shape@1.6.5)(@antv/x6@1.35.0)(antd@5.13.1)(lodash@4.17.21)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@antv/xflow-extension': 1.1.52(@ant-design/icons@5.3.6)(@antv/x6-react-components@1.1.20)(@antv/x6-react-shape@1.6.5)(@antv/x6@1.35.0)(antd@5.13.1)(classnames@2.5.1)(lodash@4.17.21)(react-dom@18.2.0)(react@18.2.0)(reflect-metadata@0.1.14)
|
||||
'@antv/xflow-hook': 1.1.52
|
||||
antd: 5.13.1(react-dom@18.2.0)(react@18.2.0)
|
||||
antd: 5.13.1(date-fns@3.6.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
lodash: 4.17.21
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
@ -7776,6 +7794,41 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-popover@1.0.7(@types/react-dom@18.2.7)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
'@radix-ui/primitive': 1.0.1
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.78)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.78)(react@18.2.0)
|
||||
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.7)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.78)(react@18.2.0)
|
||||
'@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-id': 1.0.1(@types/react@18.2.78)(react@18.2.0)
|
||||
'@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.7)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.7)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.78)(react@18.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.78)(react@18.2.0)
|
||||
'@types/react': 18.2.78
|
||||
'@types/react-dom': 18.2.7
|
||||
aria-hidden: 1.2.3
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
react-remove-scroll: 2.5.5(@types/react@18.2.78)(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-popper@1.1.3(@types/react-dom@18.2.7)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==}
|
||||
peerDependencies:
|
||||
@ -7870,6 +7923,28 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-progress@1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-5G6Om/tYSxjSeEdrb1VfKkfZfn/1IlPWd731h2RfPuSbIfNUgfqAwbKfJCg/PP6nuUCTrYzalwHSpSinoWoCag==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.78)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@types/react': 18.2.78
|
||||
'@types/react-dom': 18.2.7
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==}
|
||||
peerDependencies:
|
||||
@ -10242,6 +10317,12 @@ packages:
|
||||
/@types/json-schema@7.0.15:
|
||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||
|
||||
/@types/jsonexport@3.0.5:
|
||||
resolution: {integrity: sha512-+zDVaDqNguePOU11YNEsuf/ZRS1ciHeKVYGjfvDFmgG6aCy/s4W4Wy1MiXJIbiyMJLuakGLENCndvmGZpVTV1w==}
|
||||
dependencies:
|
||||
'@types/node': 18.17.12
|
||||
dev: false
|
||||
|
||||
/@types/jsonfile@6.1.3:
|
||||
resolution: {integrity: sha512-/yqTk2SZ1wIezK0hiRZD7RuSf4B3whFxFamB1kGStv+8zlWScTMcHanzfc0XKWs5vA1TkHeckBlOyM8jxU8nHA==}
|
||||
dependencies:
|
||||
@ -11169,7 +11250,7 @@ packages:
|
||||
- moment
|
||||
dev: false
|
||||
|
||||
/antd@5.13.1(react-dom@18.2.0)(react@18.2.0):
|
||||
/antd@5.13.1(date-fns@3.6.0)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-/qAPsr6UyJPSFZQD9G7kW98GelH2Bajli+1q7CRW4IinYQ0R0UVJckFX11emByhiU4Jd4WNH/hOO+fZtp0eVDA==}
|
||||
peerDependencies:
|
||||
react: '>=16.9.0'
|
||||
@ -11203,7 +11284,7 @@ packages:
|
||||
rc-motion: 2.9.0(react-dom@18.2.0)(react@18.2.0)
|
||||
rc-notification: 5.3.0(react-dom@18.2.0)(react@18.2.0)
|
||||
rc-pagination: 4.0.4(react-dom@18.2.0)(react@18.2.0)
|
||||
rc-picker: 3.14.6(dayjs@1.11.10)(react-dom@18.2.0)(react@18.2.0)
|
||||
rc-picker: 3.14.6(date-fns@3.6.0)(dayjs@1.11.10)(react-dom@18.2.0)(react@18.2.0)
|
||||
rc-progress: 3.5.1(react-dom@18.2.0)(react@18.2.0)
|
||||
rc-rate: 2.12.0(react-dom@18.2.0)(react@18.2.0)
|
||||
rc-resize-observer: 1.4.0(react-dom@18.2.0)(react@18.2.0)
|
||||
@ -13675,6 +13756,10 @@ packages:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
|
||||
/date-fns@3.6.0:
|
||||
resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==}
|
||||
dev: false
|
||||
|
||||
/dayjs@1.11.10:
|
||||
resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
|
||||
dev: false
|
||||
@ -17455,6 +17540,11 @@ packages:
|
||||
resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
|
||||
dev: true
|
||||
|
||||
/jsonexport@3.2.0:
|
||||
resolution: {integrity: sha512-GbO9ugb0YTZatPd/hqCGR0FSwbr82H6OzG04yzdrG7XOe4QZ0jhQ+kOsB29zqkzoYJLmLxbbrFiuwbQu891XnQ==}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/jsonfile@4.0.0:
|
||||
resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
|
||||
optionalDependencies:
|
||||
@ -20809,7 +20899,7 @@ packages:
|
||||
dependencies:
|
||||
lilconfig: 2.1.0
|
||||
postcss: 8.4.33
|
||||
ts-node: 10.9.1(@types/node@18.17.12)(typescript@4.7.4)
|
||||
ts-node: 10.9.1(@types/node@18.17.12)(typescript@5.4.5)
|
||||
yaml: 2.3.2
|
||||
dev: true
|
||||
|
||||
@ -22496,6 +22586,36 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/rc-picker@3.14.6(date-fns@3.6.0)(dayjs@1.11.10)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-AdKKW0AqMwZsKvIpwUWDUnpuGKZVrbxVTZTNjcO+pViGkjC1EBcjMgxVe8tomOEaIHJL5Gd13vS8Rr3zzxWmag==}
|
||||
engines: {node: '>=8.x'}
|
||||
peerDependencies:
|
||||
date-fns: '>= 2.x'
|
||||
dayjs: 1.11.10
|
||||
luxon: '>= 3.x'
|
||||
moment: '>= 2.x'
|
||||
react: '>=16.9.0'
|
||||
react-dom: '>=16.9.0'
|
||||
peerDependenciesMeta:
|
||||
date-fns:
|
||||
optional: true
|
||||
dayjs:
|
||||
optional: true
|
||||
luxon:
|
||||
optional: true
|
||||
moment:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
'@rc-component/trigger': 1.18.2(react-dom@18.2.0)(react@18.2.0)
|
||||
classnames: 2.5.1
|
||||
date-fns: 3.6.0
|
||||
dayjs: 1.11.10
|
||||
rc-util: 5.38.1(react-dom@18.2.0)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/rc-picker@3.14.6(dayjs@1.11.10)(react-dom@17.0.2)(react@17.0.2):
|
||||
resolution: {integrity: sha512-AdKKW0AqMwZsKvIpwUWDUnpuGKZVrbxVTZTNjcO+pViGkjC1EBcjMgxVe8tomOEaIHJL5Gd13vS8Rr3zzxWmag==}
|
||||
engines: {node: '>=8.x'}
|
||||
@ -22525,35 +22645,6 @@ packages:
|
||||
react-dom: 17.0.2(react@17.0.2)
|
||||
dev: false
|
||||
|
||||
/rc-picker@3.14.6(dayjs@1.11.10)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-AdKKW0AqMwZsKvIpwUWDUnpuGKZVrbxVTZTNjcO+pViGkjC1EBcjMgxVe8tomOEaIHJL5Gd13vS8Rr3zzxWmag==}
|
||||
engines: {node: '>=8.x'}
|
||||
peerDependencies:
|
||||
date-fns: '>= 2.x'
|
||||
dayjs: 1.11.10
|
||||
luxon: '>= 3.x'
|
||||
moment: '>= 2.x'
|
||||
react: '>=16.9.0'
|
||||
react-dom: '>=16.9.0'
|
||||
peerDependenciesMeta:
|
||||
date-fns:
|
||||
optional: true
|
||||
dayjs:
|
||||
optional: true
|
||||
luxon:
|
||||
optional: true
|
||||
moment:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
'@rc-component/trigger': 1.18.2(react-dom@18.2.0)(react@18.2.0)
|
||||
classnames: 2.5.1
|
||||
dayjs: 1.11.10
|
||||
rc-util: 5.38.1(react-dom@18.2.0)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/rc-progress@3.5.1(react-dom@17.0.2)(react@17.0.2):
|
||||
resolution: {integrity: sha512-V6Amx6SbLRwPin/oD+k1vbPrO8+9Qf8zW1T8A7o83HdNafEVvAxPV5YsgtKFP+Ud5HghLj33zKOcEHrcrUGkfw==}
|
||||
peerDependencies:
|
||||
@ -23138,6 +23229,16 @@ packages:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/react-day-picker@8.10.1(date-fns@3.6.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==}
|
||||
peerDependencies:
|
||||
date-fns: ^2.28.0 || ^3.0.0
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
date-fns: 3.6.0
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/react-dev-utils@12.0.1(typescript@4.7.4)(webpack@5.89.0):
|
||||
resolution: {integrity: sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==}
|
||||
engines: {node: '>=14'}
|
||||
@ -23488,6 +23589,7 @@ packages:
|
||||
/react-remove-scroll-bar@2.3.5(@types/react@18.2.78)(react@18.2.0):
|
||||
resolution: {integrity: sha512-3cqjOqg6s0XbOjWvmasmqHch+RLxIEk2r/70rzGXuz3iIGQsQheEQyqYCBb5EECoD01Vo2SIbDqW4paLeLTASw==}
|
||||
engines: {node: '>=10'}
|
||||
deprecated: please update to the following version as this contains a bug (https://github.com/theKashey/react-remove-scroll-bar/issues/57)
|
||||
peerDependencies:
|
||||
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
@ -26317,7 +26419,6 @@ packages:
|
||||
typescript: 5.4.5
|
||||
v8-compile-cache-lib: 3.0.1
|
||||
yn: 3.1.1
|
||||
dev: false
|
||||
|
||||
/ts-pattern@4.3.0:
|
||||
resolution: {integrity: sha512-pefrkcd4lmIVR0LA49Imjf9DYLK8vtWhqBPA3Ya1ir8xCW0O2yjL9dsCVvI7pCodLC5q7smNpEtDR2yVulQxOg==}
|
||||
|
199
src/client/components/survey/SurveyDownloadBtn.tsx
Normal file
199
src/client/components/survey/SurveyDownloadBtn.tsx
Normal file
@ -0,0 +1,199 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import {
|
||||
DialogHeader,
|
||||
DialogFooter,
|
||||
Dialog,
|
||||
DialogTrigger,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useTranslation } from '@i18next-toolkit/react';
|
||||
import { LuCalendar } from 'react-icons/lu';
|
||||
import { cn } from '@/utils/style';
|
||||
import dayjs from 'dayjs';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
|
||||
import { Calendar } from '../ui/calendar';
|
||||
import { DateRange } from 'react-day-picker';
|
||||
import { AppRouterOutput, trpc } from '@/api/trpc';
|
||||
import { useCurrentWorkspaceId } from '@/store/user';
|
||||
import { useEvent } from '@/hooks/useEvent';
|
||||
import { clamp, pick } from 'lodash-es';
|
||||
import { Progress } from '../ui/progress';
|
||||
import jsonExport from 'jsonexport/dist';
|
||||
import { downloadCSV } from '@/utils/dom';
|
||||
import { message } from 'antd';
|
||||
|
||||
interface SurveyDownloadBtnProps {
|
||||
surveyId: string;
|
||||
}
|
||||
export const SurveyDownloadBtn: React.FC<SurveyDownloadBtnProps> = React.memo(
|
||||
(props) => {
|
||||
const { surveyId } = props;
|
||||
const workspaceId = useCurrentWorkspaceId();
|
||||
const [date, setDate] = useState<DateRange | undefined>({
|
||||
from: dayjs().subtract(1, 'months').toDate(),
|
||||
to: dayjs().toDate(),
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { data: info } = trpc.survey.get.useQuery({
|
||||
workspaceId,
|
||||
surveyId,
|
||||
});
|
||||
const { data: count = 1 } = trpc.survey.count.useQuery({
|
||||
workspaceId,
|
||||
surveyId,
|
||||
});
|
||||
const [downloadProgress, setDownloadProgress] = useState<number | null>(
|
||||
null
|
||||
);
|
||||
|
||||
const trpcUtils = trpc.useUtils();
|
||||
|
||||
const handleStart = useEvent(async () => {
|
||||
try {
|
||||
const limit = 1000;
|
||||
let cursor: string | undefined = undefined;
|
||||
const downloadResultList: AppRouterOutput['survey']['resultList']['items'][number][] =
|
||||
[];
|
||||
setDownloadProgress(0);
|
||||
const startAt = date?.from
|
||||
? dayjs(date.from).startOf('day').valueOf()
|
||||
: undefined;
|
||||
const endAt = date?.to
|
||||
? dayjs(date.to).endOf('day').valueOf()
|
||||
: undefined;
|
||||
|
||||
while (true) {
|
||||
const { items, nextCursor } = await trpcUtils.survey.resultList.fetch(
|
||||
{
|
||||
workspaceId,
|
||||
surveyId,
|
||||
limit: 1000,
|
||||
cursor,
|
||||
startAt,
|
||||
endAt,
|
||||
}
|
||||
);
|
||||
|
||||
downloadResultList.push(...items);
|
||||
setDownloadProgress(clamp(downloadResultList.length / count, 0, 1));
|
||||
|
||||
cursor = nextCursor;
|
||||
if (items.length < limit) {
|
||||
// no more
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// download with csv file
|
||||
const fields = info?.payload.items ?? [];
|
||||
const csv = await jsonExport(
|
||||
downloadResultList.map((item) => {
|
||||
const map = fields.reduce(
|
||||
(prev, curr) => ({
|
||||
...prev,
|
||||
[curr.label]: item.payload[curr.name],
|
||||
}),
|
||||
{} as Record<string, string>
|
||||
);
|
||||
|
||||
return {
|
||||
id: item.id,
|
||||
sessionId: item.sessionId,
|
||||
...map,
|
||||
...pick(item, [
|
||||
'language',
|
||||
'browser',
|
||||
'os',
|
||||
'country',
|
||||
'subdivision1',
|
||||
'subdivision2',
|
||||
'city',
|
||||
]),
|
||||
};
|
||||
})
|
||||
);
|
||||
let filename = info?.name ?? surveyId;
|
||||
if (date && startAt && endAt) {
|
||||
filename += `-${dayjs(startAt).format('YYYY-MM-DD')}-${dayjs(endAt).format('YYYY-MM-DD')}`;
|
||||
}
|
||||
downloadCSV(csv, filename);
|
||||
} catch (err) {
|
||||
message.error(String(err));
|
||||
} finally {
|
||||
setDownloadProgress(null);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button>{t('Download')}</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t('Download')}</DialogTitle>
|
||||
<DialogDescription>
|
||||
{t('Download survey data with csv for further use.')}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="grid gap-4 py-4">
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
id="date"
|
||||
variant={'outline'}
|
||||
className={cn(
|
||||
'w-[300px] justify-start text-left font-normal',
|
||||
!date && 'text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
<LuCalendar className="mr-2 h-4 w-4" />
|
||||
{date?.from ? (
|
||||
date.to ? (
|
||||
<>
|
||||
{dayjs(date.from).format('MMM DD, YYYY')} -{' '}
|
||||
{dayjs(date.to).format('MMM DD, YYYY')}
|
||||
</>
|
||||
) : (
|
||||
dayjs(date.from).format('MMM DD, YYYY')
|
||||
)
|
||||
) : (
|
||||
<span>{t('Pick a date')}</span>
|
||||
)}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0" align="start">
|
||||
<Calendar
|
||||
initialFocus
|
||||
mode="range"
|
||||
defaultMonth={date?.from}
|
||||
selected={date}
|
||||
onSelect={setDate}
|
||||
numberOfMonths={2}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
<DialogFooter className="sm:items-center sm:justify-between">
|
||||
<div className="w-full sm:w-[120px]">
|
||||
{typeof downloadProgress === 'number' && (
|
||||
<Progress value={downloadProgress} />
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
loading={typeof downloadProgress === 'number'}
|
||||
onClick={handleStart}
|
||||
>
|
||||
{t('Start')}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
);
|
||||
SurveyDownloadBtn.displayName = 'SurveyDownloadBtn';
|
70
src/client/components/ui/calendar.tsx
Normal file
70
src/client/components/ui/calendar.tsx
Normal file
@ -0,0 +1,70 @@
|
||||
import * as React from "react"
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from "@radix-ui/react-icons"
|
||||
import { DayPicker } from "react-day-picker"
|
||||
|
||||
import { cn } from "@/utils/style"
|
||||
import { buttonVariants } from "@/components/ui/button"
|
||||
|
||||
export type CalendarProps = React.ComponentProps<typeof DayPicker>
|
||||
|
||||
function Calendar({
|
||||
className,
|
||||
classNames,
|
||||
showOutsideDays = true,
|
||||
...props
|
||||
}: CalendarProps) {
|
||||
return (
|
||||
<DayPicker
|
||||
showOutsideDays={showOutsideDays}
|
||||
className={cn("p-3", className)}
|
||||
classNames={{
|
||||
months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
|
||||
month: "space-y-4",
|
||||
caption: "flex justify-center pt-1 relative items-center",
|
||||
caption_label: "text-sm font-medium",
|
||||
nav: "space-x-1 flex items-center",
|
||||
nav_button: cn(
|
||||
buttonVariants({ variant: "outline" }),
|
||||
"h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
|
||||
),
|
||||
nav_button_previous: "absolute left-1",
|
||||
nav_button_next: "absolute right-1",
|
||||
table: "w-full border-collapse space-y-1",
|
||||
head_row: "flex",
|
||||
head_cell:
|
||||
"text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
|
||||
row: "flex w-full mt-2",
|
||||
cell: cn(
|
||||
"relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected].day-range-end)]:rounded-r-md",
|
||||
props.mode === "range"
|
||||
? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
|
||||
: "[&:has([aria-selected])]:rounded-md"
|
||||
),
|
||||
day: cn(
|
||||
buttonVariants({ variant: "ghost" }),
|
||||
"h-8 w-8 p-0 font-normal aria-selected:opacity-100"
|
||||
),
|
||||
day_range_start: "day-range-start",
|
||||
day_range_end: "day-range-end",
|
||||
day_selected:
|
||||
"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
|
||||
day_today: "bg-accent text-accent-foreground",
|
||||
day_outside:
|
||||
"day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30",
|
||||
day_disabled: "text-muted-foreground opacity-50",
|
||||
day_range_middle:
|
||||
"aria-selected:bg-accent aria-selected:text-accent-foreground",
|
||||
day_hidden: "invisible",
|
||||
...classNames,
|
||||
}}
|
||||
components={{
|
||||
IconLeft: ({ ...props }) => <ChevronLeftIcon className="h-4 w-4" />,
|
||||
IconRight: ({ ...props }) => <ChevronRightIcon className="h-4 w-4" />,
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
Calendar.displayName = "Calendar"
|
||||
|
||||
export { Calendar }
|
31
src/client/components/ui/popover.tsx
Normal file
31
src/client/components/ui/popover.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
import * as React from "react"
|
||||
import * as PopoverPrimitive from "@radix-ui/react-popover"
|
||||
|
||||
import { cn } from "@/utils/style"
|
||||
|
||||
const Popover = PopoverPrimitive.Root
|
||||
|
||||
const PopoverTrigger = PopoverPrimitive.Trigger
|
||||
|
||||
const PopoverAnchor = PopoverPrimitive.Anchor
|
||||
|
||||
const PopoverContent = React.forwardRef<
|
||||
React.ElementRef<typeof PopoverPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
|
||||
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
|
||||
<PopoverPrimitive.Portal>
|
||||
<PopoverPrimitive.Content
|
||||
ref={ref}
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 w-72 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",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</PopoverPrimitive.Portal>
|
||||
))
|
||||
PopoverContent.displayName = PopoverPrimitive.Content.displayName
|
||||
|
||||
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
|
26
src/client/components/ui/progress.tsx
Normal file
26
src/client/components/ui/progress.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import * as React from "react"
|
||||
import * as ProgressPrimitive from "@radix-ui/react-progress"
|
||||
|
||||
import { cn } from "@/utils/style"
|
||||
|
||||
const Progress = React.forwardRef<
|
||||
React.ElementRef<typeof ProgressPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
|
||||
>(({ className, value, ...props }, ref) => (
|
||||
<ProgressPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative h-2 w-full overflow-hidden rounded-full bg-primary/20",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ProgressPrimitive.Indicator
|
||||
className="h-full w-full flex-1 bg-primary transition-all"
|
||||
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
||||
/>
|
||||
</ProgressPrimitive.Root>
|
||||
))
|
||||
Progress.displayName = ProgressPrimitive.Root.displayName
|
||||
|
||||
export { Progress }
|
@ -35,6 +35,8 @@
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-label": "^2.0.2",
|
||||
"@radix-ui/react-menubar": "^1.0.4",
|
||||
"@radix-ui/react-popover": "^1.0.7",
|
||||
"@radix-ui/react-progress": "^1.0.3",
|
||||
"@radix-ui/react-scroll-area": "^1.0.5",
|
||||
"@radix-ui/react-select": "^2.0.0",
|
||||
"@radix-ui/react-separator": "^1.0.3",
|
||||
@ -50,6 +52,7 @@
|
||||
"@tianji/shared": "workspace:^",
|
||||
"@trpc/client": "^10.45.0",
|
||||
"@trpc/react-query": "^10.45.0",
|
||||
"@types/jsonexport": "^3.0.5",
|
||||
"ahooks": "^3.7.10",
|
||||
"antd": "^5.13.1",
|
||||
"array-move": "^3.0.1",
|
||||
@ -60,10 +63,12 @@
|
||||
"cmdk": "^1.0.0",
|
||||
"colord": "^2.9.3",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"date-fns": "^3.6.0",
|
||||
"dayjs": "^1.11.9",
|
||||
"eventemitter-strict": "^1.0.1",
|
||||
"filesize": "^10.0.12",
|
||||
"fuse.js": "^7.0.0",
|
||||
"jsonexport": "^3.2.0",
|
||||
"leaflet": "^1.9.4",
|
||||
"lodash-es": "^4.17.21",
|
||||
"lucide-react": "^0.358.0",
|
||||
@ -71,6 +76,7 @@
|
||||
"next-themes": "^0.2.1",
|
||||
"pretty-ms": "^9.0.0",
|
||||
"react": "^18.2.0",
|
||||
"react-day-picker": "^8.10.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-easy-sort": "^1.5.3",
|
||||
"react-grid-layout": "1.4.2",
|
||||
|
@ -18,6 +18,7 @@ import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { DataTable, createColumnHelper } from '@/components/DataTable';
|
||||
import { useMemo } from 'react';
|
||||
import { SurveyDownloadBtn } from '@/components/survey/SurveyDownloadBtn';
|
||||
|
||||
type SurveyResultItem =
|
||||
AppRouterOutput['survey']['resultList']['items'][number];
|
||||
@ -126,7 +127,12 @@ function PageComponent() {
|
||||
<CardHeader>
|
||||
<CardTitle>{t('Count')}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>{count}</CardContent>
|
||||
<CardContent className="flex justify-between">
|
||||
<div>{count}</div>
|
||||
<div>
|
||||
<SurveyDownloadBtn surveyId={surveyId} />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
|
@ -13,3 +13,20 @@ export function preventDefault(e: Event) {
|
||||
}
|
||||
|
||||
export const rootEl = document.getElementById('root');
|
||||
|
||||
export function downloadCSV(csv: string, filename: string): void {
|
||||
const fakeLink = document.createElement('a');
|
||||
fakeLink.style.display = 'none';
|
||||
document.body.appendChild(fakeLink);
|
||||
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
|
||||
// @ts-ignore
|
||||
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
|
||||
// Manage IE11+ & Edge
|
||||
// @ts-ignore
|
||||
window.navigator.msSaveOrOpenBlob(blob, `${filename}.csv`);
|
||||
} else {
|
||||
fakeLink.setAttribute('href', URL.createObjectURL(blob));
|
||||
fakeLink.setAttribute('download', `${filename}.csv`);
|
||||
fakeLink.click();
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import { getRequestInfo } from '../../utils/detect';
|
||||
import { SurveyPayloadSchema } from '../../prisma/zod/schemas';
|
||||
import { buildCursorResponseSchema } from '../../utils/schema';
|
||||
import { fetchDataByCursor } from '../../utils/prisma';
|
||||
import { Prisma } from '@prisma/client';
|
||||
|
||||
export const surveyRouter = router({
|
||||
all: workspaceProcedure
|
||||
@ -259,8 +260,10 @@ export const surveyRouter = router({
|
||||
.input(
|
||||
z.object({
|
||||
surveyId: z.string(),
|
||||
limit: z.number().min(1).max(100).default(50),
|
||||
limit: z.number().min(1).max(1000).default(50),
|
||||
cursor: z.string().optional(),
|
||||
startAt: z.number().optional(),
|
||||
endAt: z.number().optional(),
|
||||
})
|
||||
)
|
||||
.output(buildCursorResponseSchema(SurveyResultModelSchema))
|
||||
@ -268,12 +271,21 @@ export const surveyRouter = router({
|
||||
const limit = input.limit;
|
||||
const { cursor, surveyId } = input;
|
||||
|
||||
const where: Prisma.SurveyResultWhereInput = {
|
||||
surveyId,
|
||||
};
|
||||
|
||||
if (input.startAt && input.endAt) {
|
||||
where.createdAt = {
|
||||
gte: new Date(input.startAt),
|
||||
lte: new Date(input.endAt),
|
||||
};
|
||||
}
|
||||
|
||||
const { items, nextCursor } = await fetchDataByCursor(
|
||||
prisma.surveyResult,
|
||||
{
|
||||
where: {
|
||||
surveyId,
|
||||
},
|
||||
where,
|
||||
limit,
|
||||
cursor,
|
||||
}
|
||||
|
@ -220,6 +220,12 @@ type ExtractFindManyReturnType<T> = T extends (
|
||||
? R
|
||||
: never;
|
||||
|
||||
type ExtractFindManyWhereType<
|
||||
T extends {
|
||||
findMany: (args?: any) => Prisma.PrismaPromise<any>;
|
||||
},
|
||||
> = NonNullable<Parameters<T['findMany']>[0]>['where'];
|
||||
|
||||
/**
|
||||
* @example
|
||||
* const { items, nextCursor } = await fetchDataByCursor(
|
||||
@ -241,7 +247,8 @@ export async function fetchDataByCursor<
|
||||
>(
|
||||
fetchModel: Model,
|
||||
options: {
|
||||
where: Record<string, string>;
|
||||
// where: Record<string, any>;
|
||||
where: ExtractFindManyWhereType<Model>;
|
||||
limit: number;
|
||||
cursor: CursorType;
|
||||
cursorName?: string;
|
||||
|
Loading…
Reference in New Issue
Block a user