on master help sign in
Download CSV modal
76aa386Will Galebach
src/components/L2/Modal.scss+1
@@ -11,6 +11,7 @@
11 flex-direction: column;
11 flex-direction: column;
12 justify-content: center;
12 justify-content: center;
13 align-items: center;
13 align-items: center;
14+ z-index: 2;
14
15
15 .close {
16 .close {
16 position: absolute;
17 position: absolute;
src/components/L2/Modal.tsx+12−4
@@ -6,10 +6,16 @@ import './Modal.scss';
6export interface ModalProps {
6export interface ModalProps {
7 show: boolean;
7 show: boolean;
8 hide: () => void;
8 hide: () => void;
9+ hideClose?: boolean;
9 children: React.ReactNode;
10 children: React.ReactNode;
10}
11}
11
12
12−export default function Modal({ show, hide, children }: ModalProps) {
13+export default function Modal({
14+ show,
15+ hide,
16+ hideClose = false,
17+ children,
18+}: ModalProps) {
13 const dontHide = (e: MouseEvent) => {
19 const dontHide = (e: MouseEvent) => {
14 e.stopPropagation();
20 e.stopPropagation();
15 };
21 };
@@ -21,9 +27,11 @@ export default function Modal({ show, hide, children }: ModalProps) {
21 return (
27 return (
22 <Box className="modal" onClick={hide}>
28 <Box className="modal" onClick={hide}>
23 <Box className="content">
29 <Box className="content">
30+ {!hideClose && (
24− <Box className="close" onClick={hide}>
31+ <Box className="close" onClick={hide}>
25− &#215;
32+ &#215;
26− </Box>
33+ </Box>
34+ )}
27 <Box onClick={dontHide}>{children}</Box>
35 <Box onClick={dontHide}>{children}</Box>
28 </Box>
36 </Box>
29 </Box>
37 </Box>
src/views/Activate/MasterKeyReveal.tsx+3−3
@@ -14,8 +14,8 @@ import { DEFAULT_FADE_TIMEOUT, MASTER_KEY_DURATION } from 'lib/constants';
14import View from 'components/View';
14import View from 'components/View';
15
15
16const MasterKeyReveal = () => {
16const MasterKeyReveal = () => {
17− const { setIsIn } = useActivateFlow();
17+ const { setIsIn }: any = useActivateFlow();
18− const { push, names } = useLocalRouter();
18+ const { pop, push, names }: any = useLocalRouter();
19 const [triggerAnimation, setTriggerAnimation] = useState<boolean>(false);
19 const [triggerAnimation, setTriggerAnimation] = useState<boolean>(false);
20 const [fadeOutKey, setFadeOutKey] = useState<boolean>(false);
20 const [fadeOutKey, setFadeOutKey] = useState<boolean>(false);
21
21
@@ -74,7 +74,7 @@ const MasterKeyReveal = () => {
74 }, [delayedFadeIn, setIsIn]);
74 }, [delayedFadeIn, setIsIn]);
75
75
76 return (
76 return (
77− <View centered={true}>
77+ <View centered={true} pop={pop} header={null} className="">
78 <ActivateView
78 <ActivateView
79 header={triggerAnimation && header}
79 header={triggerAnimation && header}
80 footer={triggerAnimation && footer}>
80 footer={triggerAnimation && footer}>
src/views/Invite/Cohort.scss+38
@@ -220,4 +220,42 @@
220 font-size: 14px;
220 font-size: 14px;
221 line-height: 1.4em;
221 line-height: 1.4em;
222 }
222 }
223+
224+ .download-csv-modal {
225+ height: 167px;
226+ width: 333px;
227+ display: flex;
228+ flex-direction: column;
229+ justify-content: space-between;
230+ font-weight: 500;
231+
232+ .download-buttons {
233+ justify-content: flex-end;
234+
235+ .close-button, .download-button {
236+ padding: 4px 16px;
237+
238+ &:hover {
239+ opacity: 0.8;
240+ }
241+ }
242+
243+ .close-button {
244+ color: black;
245+ background-color: $washed-gray;
246+ margin-right: 8px;
247+ }
248+ }
249+ }
250+
251+ .csv-loading {
252+ position: absolute;
253+ top: 24px;
254+ right: 24px;
255+
256+ svg {
257+ width: 16px;
258+ height: 16px;
259+ }
260+ }
223}
261}
src/views/Invite/Cohort.tsx+50−8
@@ -49,6 +49,7 @@ import Paginator from 'components/L2/Paginator';
49import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
49import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
50
50
51import './Cohort.scss';
51import './Cohort.scss';
52+import Modal from 'components/L2/Modal';
52
53
53interface L1Invite {
54interface L1Invite {
54 ticket: string;
55 ticket: string;
@@ -89,6 +90,8 @@ export default function InviteCohort() {
89 );
90 );
90 const [showInviteForm, setShowInviteForm] = useState(false);
91 const [showInviteForm, setShowInviteForm] = useState(false);
91 const [loading, setLoading] = useState(false);
92 const [loading, setLoading] = useState(false);
93+ const [showCsvModal, setShowCsvModal] = useState(false);
94+ const [csvElement, setCsvElement] = useState<HTMLAnchorElement | null>();
92 const [error, setError] = useState('');
95 const [error, setError] = useState('');
93 const [l1Invite, setL1Invite] = useState<L1Invite | null>(null);
96 const [l1Invite, setL1Invite] = useState<L1Invite | null>(null);
94 const [page, setPage] = useState(0);
97 const [page, setPage] = useState(0);
@@ -215,9 +218,10 @@ export default function InviteCohort() {
215 point,
218 point,
216 ]);
219 ]);
217
220
218− const downloadCsv = useCallback(() => {
221+ const generateCsv = useCallback(() => {
219− const generateAndDownload = async () => {
222+ const generateCsvElement = async () => {
220 setLoading(true);
223 setLoading(true);
224+ setShowCsvModal(true);
221 const invites = await getInvites();
225 const invites = await getInvites();
222 if (invites) {
226 if (invites) {
223 const csv = invites.reduce(
227 const csv = invites.reduce(
@@ -225,26 +229,35 @@ export default function InviteCohort() {
225 (csvData += generateCsvLine(ind, ticket, planet)),
229 (csvData += generateCsvLine(ind, ticket, planet)),
226 'Number,Planet,Invite URL,Point,Ticket\n'
230 'Number,Planet,Invite URL,Point,Ticket\n'
227 );
231 );
232+ document.getElementById('csv')?.remove();
228 const hiddenElement = document.createElement('a');
233 const hiddenElement = document.createElement('a');
229 hiddenElement.href = `data:text/csv;charset=utf-8,${encodeURIComponent(
234 hiddenElement.href = `data:text/csv;charset=utf-8,${encodeURIComponent(
230 csv
235 csv
231 )}`;
236 )}`;
232 hiddenElement.target = '_blank';
237 hiddenElement.target = '_blank';
238+ hiddenElement.id = 'csv';
233
239
234 //provide the name for the CSV file to be downloaded
240 //provide the name for the CSV file to be downloaded
235 hiddenElement.download = generateCsvName(
241 hiddenElement.download = generateCsvName(
236 DEFAULT_CSV_NAME,
242 DEFAULT_CSV_NAME,
237 point.patp?.slice(1) || 'bridge'
243 point.patp?.slice(1) || 'bridge'
238 );
244 );
239− hiddenElement.click();
245+ setCsvElement(hiddenElement);
240 } else {
246 } else {
241 setError('There was an error creating the CSV');
247 setError('There was an error creating the CSV');
242 }
248 }
243 setLoading(false);
249 setLoading(false);
244 setInviteGeneratingNum(0);
250 setInviteGeneratingNum(0);
245 };
251 };
246− generateAndDownload();
252+ generateCsvElement();
253+ }, [
254+ getInvites,
255+ setError,
256+ setLoading,
247− }, [getInvites, setError, setLoading, setInviteGeneratingNum, point]);
257+ setInviteGeneratingNum,
258+ setShowCsvModal,
259+ point,
260+ ]);
248
261
249 const goNextPage = useCallback(() => {
262 const goNextPage = useCallback(() => {
250 setPage(page + 1);
263 setPage(page + 1);
@@ -425,7 +438,7 @@ export default function InviteCohort() {
425 const showGenerateButton = !hasPending && !hasInvites && point.canSpawn;
438 const showGenerateButton = !hasPending && !hasInvites && point.canSpawn;
426 const showAddMoreButton =
439 const showAddMoreButton =
427 (hasInvites || hasPending) && point.canSpawn && point.l2Quota > 0;
440 (hasInvites || hasPending) && point.canSpawn && point.l2Quota > 0;
428− const generatingCodesText = `Generating invite code ${inviteGeneratingNum} of ${invitePoints.length}...`;
441+ const generatingCodesText = `Generating ${inviteGeneratingNum} of ${invitePoints.length} codes...`;
429
442
430 return (
443 return (
431 <View
444 <View
@@ -441,7 +454,7 @@ export default function InviteCohort() {
441 ) : (
454 ) : (
442 <Row className="has-invites-header">
455 <Row className="has-invites-header">
443 {header}
456 {header}
444− <Row className="download-csv" onClick={downloadCsv}>
457+ <Row className="download-csv" onClick={generateCsv}>
445 <Icon icon="Download" />
458 <Icon icon="Download" />
446 <Box>CSV</Box>
459 <Box>CSV</Box>
447 </Row>
460 </Row>
@@ -486,7 +499,36 @@ export default function InviteCohort() {
486 <strong>{` ${ddmmmYYYY(nextQuotaTime)}`}</strong>.
499 <strong>{` ${ddmmmYYYY(nextQuotaTime)}`}</strong>.
487 </Box>
500 </Box>
488 ) : null}
501 ) : null}
502+ <Modal show={showCsvModal} hide={() => setShowCsvModal(false)} hideClose>
503+ <Box className="download-csv-modal">
504+ <Box>
505+ <Box>Download CSV</Box>
489− <LoadingOverlay loading={loading} text={generatingCodesText} />
506+ {loading && <Box>{generatingCodesText}</Box>}
507+ </Box>
508+ <Row className="download-buttons">
509+ <Button
510+ center
511+ className="ph4 close-button"
512+ solid
513+ onClick={() => setShowCsvModal(false)}>
514+ Close
515+ </Button>
516+ <Button
517+ center
518+ className="ph4 download-button"
519+ disabled={loading}
520+ solid
521+ onClick={() => csvElement?.click()}>
522+ Download CSV
523+ </Button>
524+ </Row>
525+ </Box>
526+ {loading && (
527+ <Box className="csv-loading">
528+ <LoadingSpinner background="#BCDCFF" foreground="#219DFF" />
529+ </Box>
530+ )}
531+ </Modal>
490 </View>
532 </View>
491 );
533 );
492}
534}