Mirror of https://github.com/roostorg/osprey
github.com/roostorg/osprey
1import * as React from 'react';
2import { DownloadOutlined } from '@ant-design/icons';
3import { Alert, Form, Input, Modal, Radio } from 'antd';
4import { Store } from 'antd/lib/form/interface';
5import { RadioChangeEvent } from 'antd/lib/radio';
6
7import { getTopNQueryResultCSV } from '../../actions/EventActions';
8import useQueryStore from '../../stores/QueryStore';
9import OspreyButton, { ButtonColors } from '../../uikit/OspreyButton';
10import Text, { TextSizes } from '../../uikit/Text';
11
12import styles from './CSVDownloadModal.module.css';
13
14interface CSVDownloadModalProps {
15 disabled: boolean;
16 dimension: string;
17}
18
19enum RadioOptions {
20 MAXIMUM,
21 CUSTOM,
22}
23
24const MAXIMUM_ROWS = 100000;
25
26function validateLimitInput(_: any, limitStr: string): Promise<void> {
27 const limit = Number(limitStr);
28
29 if (isNaN(limit) || limit <= 0) {
30 return Promise.reject('Invalid number'); /* eslint-disable-line */
31 } else if (limit > MAXIMUM_ROWS) {
32 return Promise.reject('Maximum 100,000 rows allowed per CSV'); /* eslint-disable-line */
33 }
34
35 return Promise.resolve();
36}
37
38const CSVDownloadModal = ({ disabled, dimension }: CSVDownloadModalProps) => {
39 const [isCSVDownloading, setIsCSVDownloading] = React.useState(false);
40 const [isModalOpen, setIsModalOpen] = React.useState(false);
41 const [limitOption, setLimitOption] = React.useState(RadioOptions.MAXIMUM);
42
43 const executedQuery = useQueryStore((state) => state.executedQuery);
44 const entityFeatureFilters = useQueryStore((state) => state.entityFeatureFilters);
45 const [form] = Form.useForm();
46
47 const handleDownload = async (values?: Store) => {
48 const maybeLimit = Number(values?.limit);
49 const limit = isNaN(maybeLimit) ? MAXIMUM_ROWS : maybeLimit;
50
51 setIsCSVDownloading(true);
52 setIsModalOpen(false);
53 await getTopNQueryResultCSV({ ...executedQuery, entityFeatureFilters }, dimension, limit);
54 setIsCSVDownloading(false);
55
56 form.resetFields();
57 setLimitOption(RadioOptions.MAXIMUM);
58 };
59
60 const handleRadioClick = (e: RadioChangeEvent) => {
61 setLimitOption(e.target.value);
62 };
63
64 const renderLimitForm = () => {
65 return (
66 <Form form={form} onFinish={handleDownload}>
67 <Form.Item name="limit" rules={[{ required: true }, { validator: validateLimitInput }]} validateFirst>
68 <Input className={styles.limitInput} placeholder="Number of rows (maximum 100,000)" />
69 </Form.Item>
70 </Form>
71 );
72 };
73
74 return (
75 <>
76 <OspreyButton
77 color={ButtonColors.LINK_GRAY}
78 onClick={() => setIsModalOpen(true)}
79 disabled={disabled}
80 loading={isCSVDownloading}
81 key="download"
82 >
83 <DownloadOutlined />
84 Download CSV
85 </OspreyButton>
86 <Modal
87 title={<Text size={TextSizes.H5}>Download CSV</Text>}
88 width={400}
89 visible={isModalOpen}
90 onCancel={() => setIsModalOpen(false)}
91 footer={
92 <div className={styles.footer}>
93 <OspreyButton key="cancel" onClick={() => setIsModalOpen(false)}>
94 Cancel
95 </OspreyButton>
96 <OspreyButton
97 key="ok"
98 color={ButtonColors.DARK_BLUE}
99 onClick={limitOption === RadioOptions.CUSTOM ? form.submit : handleDownload}
100 >
101 OK
102 </OspreyButton>
103 </div>
104 }
105 >
106 <Alert
107 style={{ marginBottom: 12 }}
108 message="Large CSV downloads can take several minutes."
109 type="warning"
110 showIcon
111 />
112 <Radio.Group value={limitOption} onChange={handleRadioClick}>
113 <Radio className={styles.radioOption} value={RadioOptions.MAXIMUM}>
114 Download Maximum Rows (up to 100,000)
115 </Radio>
116 <Radio className={styles.radioOption} value={RadioOptions.CUSTOM}>
117 Set Row Limit
118 </Radio>
119 </Radio.Group>
120 {limitOption === RadioOptions.CUSTOM ? renderLimitForm() : null}
121 </Modal>
122 </>
123 );
124};
125
126export default CSVDownloadModal;