diff --git a/e2e/screenshots/strategy/overlapping/Overlapping/create/form.png b/e2e/screenshots/strategy/overlapping/Overlapping/create/form.png index 197b894a2..41de82356 100644 Binary files a/e2e/screenshots/strategy/overlapping/Overlapping/create/form.png and b/e2e/screenshots/strategy/overlapping/Overlapping/create/form.png differ diff --git a/e2e/screenshots/strategy/recurring/Recurring_range_limit/create/form.png b/e2e/screenshots/strategy/recurring/Recurring_range_limit/create/form.png index 78cdd4d14..b832884cd 100644 Binary files a/e2e/screenshots/strategy/recurring/Recurring_range_limit/create/form.png and b/e2e/screenshots/strategy/recurring/Recurring_range_limit/create/form.png differ diff --git a/src/components/activity/ActivityExport.module.css b/src/components/activity/ActivityExport.module.css new file mode 100644 index 000000000..0cbdc4283 --- /dev/null +++ b/src/components/activity/ActivityExport.module.css @@ -0,0 +1,63 @@ +.export-button { + @apply border-background-800 text-12 gap-8 rounded-full border-2 px-12 py-8; + display: grid; + grid-template-columns: repeat(3, auto); + grid-template-areas: "icon text loading"; + align-items: center; + overflow-y: clip; +} +.export-button:hover { + @apply border-background-700 bg-background-800; +} +.export-button:disabled { + pointer-events: none; + opacity: 0.6; +} +.export-button > * { + grid-row: 1/1; + transition: transform 0.3s var(--ease-back-in-out); +} +.export { + grid-column: text / loading; +} +.exporting { + grid-column: text; +} +.loading { + grid-column: loading; +} + +.export-button:not(:disabled) .export { + transition-delay: 200ms; +} +.export-button:not(:disabled) .exporting { + transform: translateY(40px); + transition-delay: 100ms; +} +.export-button:not(:disabled) .loading { + transform: translateY(40px); + transition-delay: 0ms; +} +.export-button:disabled .export { + transform: translateY(-40px); + transition-delay: 0ms; +} +.export-button:disabled .exporting { + transition-delay: 100ms; +} +.export-button:disabled .loading { + transition-delay: 200ms; +} +.export-button:disabled .loading path { + transform-origin: center; + animation: rotate 0.6s linear infinite; +} + +@keyframes rotate { + from { + transform: 0; + } + to { + transform: rotate(360deg); + } +} \ No newline at end of file diff --git a/src/components/activity/ActivityExport.tsx b/src/components/activity/ActivityExport.tsx index 089ed545d..59ca34dcb 100644 --- a/src/components/activity/ActivityExport.tsx +++ b/src/components/activity/ActivityExport.tsx @@ -6,7 +6,9 @@ import { useActivity } from './ActivityProvider'; import { carbonApi } from 'utils/carbonApi'; import { useTokens } from 'hooks/useTokens'; import { toActivities } from './useActivityQuery'; -import { useState } from 'react'; +import { MouseEvent, useRef, useState } from 'react'; +import { Button } from 'components/common/button'; +import styles from './ActivityExport.module.css'; export const getActivityCSV = (activities: Activity[]) => { const header = [ @@ -66,14 +68,25 @@ export const getActivityCSV = (activities: Activity[]) => { return encodeURI(csvContent); }; +const limit = 10_000; export const ActivityExport = () => { + const ref = useRef(null); const [loading, setLoading] = useState(false); - const { queryParams } = useActivity(); + const { queryParams, size } = useActivity(); const { tokensMap } = useTokens(); + const close = (e: MouseEvent) => { + if (e.target !== e.currentTarget) return; + e.currentTarget.close(); + }; + const download = async () => { setLoading(true); - const data = await carbonApi.getActivity(queryParams); + const data = await carbonApi.getActivity({ + ...queryParams, + offset: 0, + limit, + }); const activities = toActivities(data, tokensMap); const anchor = document.createElement('a'); anchor.href = getActivityCSV(activities); @@ -81,15 +94,52 @@ export const ActivityExport = () => { anchor.click(); setLoading(false); }; + + const shouldDownload = () => { + if (size && size > limit) ref.current?.showModal(); + else download(); + }; + return ( - + <> + + {!!size && size > limit && ( + +
+

+ The export limit is 10,000 rows.  + Only the most recent 10,000 records will be exported. +

+

To include older data, adjust the date range and try again.

+ +
+
+ )} + ); }; diff --git a/src/index.css b/src/index.css index 08a4a9446..b82817b81 100644 --- a/src/index.css +++ b/src/index.css @@ -16,6 +16,12 @@ --main-menu-height: 80px; --mobile-menu-height: 80px; + + --ease-in: cubic-bezier(.55, 0, 1, .45); + --ease-out: cubic-bezier(0, .55, .45, 1); + --ease-in-out: cubic-bezier(.85, 0, .15, 1); + --ease-back-in-out: cubic-bezier(.68, -.6, .32, 1.6); + --ease-back-out: cubic-bezier(.34, 1.56, .64, 1); } @@ -268,3 +274,37 @@ input[type='search']::-webkit-search-cancel-button { display: none; } + +dialog.modal, dialog.modal::backdrop { + transition-behavior: allow-discrete; + transition-property: display, overlay, opacity, transform; + transition-duration: 0.5s; + transition-timing-function: var(--ease-back-in-out); +} +@starting-style { + dialog.modal[open] { + opacity: 0; + transform: translateY(100px); + } + dialog.modal[open]::backdrop { + opacity: 0; + } +} +dialog.modal:not([open]) { + opacity: 0; + transform: scale(0.8); +} +dialog.modal:not([open])::backdrop { + opacity: 0; +} +dialog.modal form { + @apply bg-background-900; + border-radius: 8px; + padding: 16px; + max-width: 390px; + transition: transform 0.3s var(--ease-out); +} +dialog.modal::backdrop { + @apply bg-black/40; + backdrop-filter: blur(8px); +} \ No newline at end of file