chore(ledger): 4895 refactor ledger csv extract (#5053)
This commit is contained in:
parent
a0844d41bf
commit
9dc9588a14
@ -6,7 +6,7 @@ import { TelemetryApproval } from './telemetry-approval';
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { useOnboardingStore } from '../welcome-dialog/use-get-onboarding-step';
|
import { useOnboardingStore } from '../welcome-dialog/use-get-onboarding-step';
|
||||||
|
|
||||||
const TELEMETRY_APPROVAL_TOAST_ID = 'telemetry_tost_id';
|
const TELEMETRY_APPROVAL_TOAST_ID = 'telemetry_toast_id';
|
||||||
|
|
||||||
export const Telemetry = () => {
|
export const Telemetry = () => {
|
||||||
const onboardingDissmissed = useOnboardingStore((store) => store.dismissed);
|
const onboardingDissmissed = useOnboardingStore((store) => store.dismissed);
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
useEthTransactionUpdater,
|
useEthTransactionUpdater,
|
||||||
useEthWithdrawApprovalsManager,
|
useEthWithdrawApprovalsManager,
|
||||||
} from '@vegaprotocol/web3';
|
} from '@vegaprotocol/web3';
|
||||||
|
import { useLedgerDownloadManager } from '@vegaprotocol/ledger';
|
||||||
|
|
||||||
export const TransactionHandlers = () => {
|
export const TransactionHandlers = () => {
|
||||||
useVegaTransactionManager();
|
useVegaTransactionManager();
|
||||||
@ -14,5 +15,6 @@ export const TransactionHandlers = () => {
|
|||||||
useEthTransactionManager();
|
useEthTransactionManager();
|
||||||
useEthTransactionUpdater();
|
useEthTransactionUpdater();
|
||||||
useEthWithdrawApprovalsManager();
|
useEthWithdrawApprovalsManager();
|
||||||
|
useLedgerDownloadManager();
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
@ -1 +1,2 @@
|
|||||||
export * from './lib/ledger-export-form';
|
export * from './lib/ledger-export-form';
|
||||||
|
export * from './lib/ledger-download-store';
|
||||||
|
142
libs/ledger/src/lib/ledger-download-store.tsx
Normal file
142
libs/ledger/src/lib/ledger-download-store.tsx
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import { create } from 'zustand';
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
import { useCallback, useEffect } from 'react';
|
||||||
|
import type { Toast } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { useToasts, Intent } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import { subscribeWithSelector } from 'zustand/middleware';
|
||||||
|
|
||||||
|
type DownloadSettings = {
|
||||||
|
title: string;
|
||||||
|
link: string;
|
||||||
|
filename?: string;
|
||||||
|
isDownloaded?: boolean;
|
||||||
|
isChanged?: boolean;
|
||||||
|
isError?: boolean;
|
||||||
|
errorMessage?: string;
|
||||||
|
isDelayed?: boolean;
|
||||||
|
intent?: Intent;
|
||||||
|
blob?: Blob;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LedgerDownloadFileStore = {
|
||||||
|
queue: DownloadSettings[];
|
||||||
|
hasItem: (link: string) => boolean;
|
||||||
|
removeItem: (link: string) => void;
|
||||||
|
updateQueue: (item: DownloadSettings) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useLedgerDownloadFile = create<LedgerDownloadFileStore>()(
|
||||||
|
subscribeWithSelector((set, get) => ({
|
||||||
|
queue: [],
|
||||||
|
hasItem: (link: string) =>
|
||||||
|
get().queue.findIndex((item) => item.link === link) > -1,
|
||||||
|
removeItem: (link: string) => {
|
||||||
|
const queue = get().queue;
|
||||||
|
const index = queue.findIndex((item) => item.link === link);
|
||||||
|
if (index > -1) {
|
||||||
|
queue.splice(index, 1);
|
||||||
|
set({ queue: [...queue] });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateQueue: (newitem: DownloadSettings) => {
|
||||||
|
const queue = get().queue;
|
||||||
|
const index = queue.findIndex((item) => item.link === newitem.link);
|
||||||
|
if (index > -1) {
|
||||||
|
queue[index] = { ...queue[index], ...newitem };
|
||||||
|
set({ queue: [...queue] });
|
||||||
|
} else {
|
||||||
|
set({ queue: [newitem, ...queue] });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
const ErrorContent = ({ message }: { message?: string }) => (
|
||||||
|
<>
|
||||||
|
<h4 className="mb-1 text-sm">{t('Something went wrong')}</h4>
|
||||||
|
<p>{message || t('Try again later')}</p>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const InfoContent = ({ progress = false }) => (
|
||||||
|
<>
|
||||||
|
<p>{t('Please note this can take several minutes.')}</p>
|
||||||
|
<p>{t('You will be notified here when your file is ready.')}</p>
|
||||||
|
<h4 className="my-2">
|
||||||
|
{progress ? t('Still in progress') : t('Download has been started')}
|
||||||
|
</h4>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const useLedgerDownloadManager = () => {
|
||||||
|
const queue = useLedgerDownloadFile((store) => store.queue);
|
||||||
|
const updateQueue = useLedgerDownloadFile((store) => store.updateQueue);
|
||||||
|
const removeItem = useLedgerDownloadFile((store) => store.removeItem);
|
||||||
|
const [setToast, updateToast, hasToast, removeToast] = useToasts((store) => [
|
||||||
|
store.setToast,
|
||||||
|
store.update,
|
||||||
|
store.hasToast,
|
||||||
|
store.remove,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const onDownloadClose = useCallback(
|
||||||
|
(id: string) => {
|
||||||
|
removeToast(id);
|
||||||
|
removeItem(id);
|
||||||
|
},
|
||||||
|
[removeToast, removeItem]
|
||||||
|
);
|
||||||
|
|
||||||
|
const createToast = (item: DownloadSettings) => {
|
||||||
|
let content: ReactNode;
|
||||||
|
switch (true) {
|
||||||
|
case item.isError:
|
||||||
|
content = <ErrorContent message={item.errorMessage} />;
|
||||||
|
break;
|
||||||
|
case Boolean(item.blob):
|
||||||
|
content = (
|
||||||
|
<>
|
||||||
|
<h4 className="mb-1 text-sm">{t('Your file is ready')}</h4>
|
||||||
|
<a
|
||||||
|
onClick={() => onDownloadClose(item.link)}
|
||||||
|
href={URL.createObjectURL(item.blob as Blob)}
|
||||||
|
download={item.filename}
|
||||||
|
className="underline"
|
||||||
|
>
|
||||||
|
{t('Get file here')}
|
||||||
|
</a>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
content = <InfoContent progress={item.isDelayed} />;
|
||||||
|
}
|
||||||
|
const toast: Toast = {
|
||||||
|
id: item.link,
|
||||||
|
intent: item.intent || Intent.Primary,
|
||||||
|
content: (
|
||||||
|
<>
|
||||||
|
<h3 className="mb-1 text-md uppercase">{item.title}</h3>
|
||||||
|
{content}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
onClose: () => onDownloadClose(item.link),
|
||||||
|
loader: !item.isDownloaded && !item.isError,
|
||||||
|
};
|
||||||
|
if (hasToast(toast.id)) {
|
||||||
|
updateToast(toast.id, toast);
|
||||||
|
} else {
|
||||||
|
setToast(toast);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
queue.forEach((item) => {
|
||||||
|
if (item.isChanged) {
|
||||||
|
createToast(item);
|
||||||
|
updateQueue({ ...item, isChanged: false });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [queue, createToast, updateQueue]);
|
||||||
|
};
|
@ -1,10 +1,21 @@
|
|||||||
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
|
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
|
||||||
import { createDownloadUrl, LedgerExportForm } from './ledger-export-form';
|
import { createDownloadUrl, LedgerExportForm } from './ledger-export-form';
|
||||||
import { formatForInput, toNanoSeconds } from '@vegaprotocol/utils';
|
import { formatForInput, toNanoSeconds } from '@vegaprotocol/utils';
|
||||||
|
import {
|
||||||
|
useLedgerDownloadManager,
|
||||||
|
useLedgerDownloadFile,
|
||||||
|
} from './ledger-download-store';
|
||||||
|
import { Intent } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
|
const mockSetToast = jest.fn();
|
||||||
|
jest.mock('@vegaprotocol/ui-toolkit', () => ({
|
||||||
|
...jest.requireActual('@vegaprotocol/ui-toolkit'),
|
||||||
|
useToasts: jest.fn(() => [mockSetToast, jest.fn(), jest.fn(() => false)]),
|
||||||
|
}));
|
||||||
const vegaUrl = 'https://vega-url.co.uk/querystuff';
|
const vegaUrl = 'https://vega-url.co.uk/querystuff';
|
||||||
|
|
||||||
const mockResponse = {
|
const mockResponse = {
|
||||||
|
ok: true,
|
||||||
headers: { get: jest.fn() },
|
headers: { get: jest.fn() },
|
||||||
blob: () => '',
|
blob: () => '',
|
||||||
};
|
};
|
||||||
@ -28,6 +39,7 @@ describe('LedgerExportForm', () => {
|
|||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
jest.useRealTimers();
|
jest.useRealTimers();
|
||||||
|
jest.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be properly rendered', async () => {
|
it('should be properly rendered', async () => {
|
||||||
@ -43,8 +55,6 @@ describe('LedgerExportForm', () => {
|
|||||||
// userEvent does not work with faked timers
|
// userEvent does not work with faked timers
|
||||||
fireEvent.click(screen.getByTestId('ledger-download-button'));
|
fireEvent.click(screen.getByTestId('ledger-download-button'));
|
||||||
|
|
||||||
expect(screen.getByTestId('download-spinner')).toBeInTheDocument();
|
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(global.fetch).toHaveBeenCalledWith(
|
expect(global.fetch).toHaveBeenCalledWith(
|
||||||
`https://vega-url.co.uk/api/v2/ledgerentry/export?partyId=${partyId}&assetId=${
|
`https://vega-url.co.uk/api/v2/ledgerentry/export?partyId=${partyId}&assetId=${
|
||||||
@ -52,9 +62,6 @@ describe('LedgerExportForm', () => {
|
|||||||
}&dateRange.startTimestamp=1691057410000000000`
|
}&dateRange.startTimestamp=1691057410000000000`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
await waitFor(() => {
|
|
||||||
expect(screen.queryByTestId('download-spinner')).not.toBeInTheDocument();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('assetID should be properly change request url', async () => {
|
it('assetID should be properly change request url', async () => {
|
||||||
@ -75,8 +82,6 @@ describe('LedgerExportForm', () => {
|
|||||||
|
|
||||||
fireEvent.click(screen.getByTestId('ledger-download-button'));
|
fireEvent.click(screen.getByTestId('ledger-download-button'));
|
||||||
|
|
||||||
expect(screen.getByTestId('download-spinner')).toBeInTheDocument();
|
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(global.fetch).toHaveBeenCalledWith(
|
expect(global.fetch).toHaveBeenCalledWith(
|
||||||
`https://vega-url.co.uk/api/v2/ledgerentry/export?partyId=${partyId}&assetId=${
|
`https://vega-url.co.uk/api/v2/ledgerentry/export?partyId=${partyId}&assetId=${
|
||||||
@ -84,9 +89,6 @@ describe('LedgerExportForm', () => {
|
|||||||
}&dateRange.startTimestamp=1691057410000000000`
|
}&dateRange.startTimestamp=1691057410000000000`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
await waitFor(() => {
|
|
||||||
expect(screen.queryByTestId('download-spinner')).not.toBeInTheDocument();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('date-from should properly change request url', async () => {
|
it('date-from should properly change request url', async () => {
|
||||||
@ -110,8 +112,6 @@ describe('LedgerExportForm', () => {
|
|||||||
|
|
||||||
fireEvent.click(screen.getByTestId('ledger-download-button'));
|
fireEvent.click(screen.getByTestId('ledger-download-button'));
|
||||||
|
|
||||||
expect(screen.getByTestId('download-spinner')).toBeInTheDocument();
|
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(global.fetch).toHaveBeenCalledWith(
|
expect(global.fetch).toHaveBeenCalledWith(
|
||||||
`https://vega-url.co.uk/api/v2/ledgerentry/export?partyId=${partyId}&assetId=${
|
`https://vega-url.co.uk/api/v2/ledgerentry/export?partyId=${partyId}&assetId=${
|
||||||
@ -119,10 +119,6 @@ describe('LedgerExportForm', () => {
|
|||||||
}&dateRange.startTimestamp=${toNanoSeconds(newDate)}`
|
}&dateRange.startTimestamp=${toNanoSeconds(newDate)}`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(screen.queryByTestId('download-spinner')).not.toBeInTheDocument();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('date-to should properly change request url', async () => {
|
it('date-to should properly change request url', async () => {
|
||||||
@ -156,10 +152,6 @@ describe('LedgerExportForm', () => {
|
|||||||
)}`
|
)}`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(screen.queryByTestId('download-spinner')).not.toBeInTheDocument();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Time zone sentence should be properly displayed', () => {
|
it('Time zone sentence should be properly displayed', () => {
|
||||||
@ -205,6 +197,50 @@ describe('LedgerExportForm', () => {
|
|||||||
screen.queryByText(/^The downloaded file uses the UTC/)
|
screen.queryByText(/^The downloaded file uses the UTC/)
|
||||||
).not.toBeInTheDocument();
|
).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('A toast notification should be displayed', async () => {
|
||||||
|
useLedgerDownloadFile.setState({ queue: [] });
|
||||||
|
const TestWrapper = () => {
|
||||||
|
useLedgerDownloadManager();
|
||||||
|
return (
|
||||||
|
<LedgerExportForm
|
||||||
|
partyId={partyId}
|
||||||
|
vegaUrl={vegaUrl}
|
||||||
|
assets={assetsMock}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
render(<TestWrapper />);
|
||||||
|
expect(screen.getByText('symbol asset-id')).toBeInTheDocument();
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByTestId('ledger-download-button'));
|
||||||
|
|
||||||
|
const link = `https://vega-url.co.uk/api/v2/ledgerentry/export?partyId=${partyId}&assetId=${
|
||||||
|
Object.keys(assetsMock)[0]
|
||||||
|
}&dateRange.startTimestamp=1691057410000000000`;
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mockSetToast).toHaveBeenCalledWith({
|
||||||
|
id: link,
|
||||||
|
content: expect.any(Object),
|
||||||
|
onClose: expect.any(Function),
|
||||||
|
intent: Intent.Primary,
|
||||||
|
loader: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(global.fetch).toHaveBeenCalledWith(link);
|
||||||
|
});
|
||||||
|
|
||||||
|
mockSetToast.mockClear();
|
||||||
|
(global.fetch as jest.Mock).mockClear();
|
||||||
|
fireEvent.click(screen.getByTestId('ledger-download-button')); // click again
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mockSetToast).toHaveBeenCalled();
|
||||||
|
expect(global.fetch).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('createDownloadUrl', () => {
|
describe('createDownloadUrl', () => {
|
||||||
|
@ -1,17 +1,22 @@
|
|||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import { z } from 'zod';
|
import { format, subDays } from 'date-fns';
|
||||||
import {
|
import {
|
||||||
TradingButton,
|
Intent,
|
||||||
Loader,
|
Loader,
|
||||||
|
TradingButton,
|
||||||
TradingFormGroup,
|
TradingFormGroup,
|
||||||
TradingInput,
|
TradingInput,
|
||||||
TradingSelect,
|
TradingSelect,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { toNanoSeconds, VEGA_ID_REGEX } from '@vegaprotocol/utils';
|
import { z } from 'zod';
|
||||||
|
import {
|
||||||
|
formatForInput,
|
||||||
|
toNanoSeconds,
|
||||||
|
VEGA_ID_REGEX,
|
||||||
|
} from '@vegaprotocol/utils';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { localLoggerFactory } from '@vegaprotocol/logger';
|
import { localLoggerFactory } from '@vegaprotocol/logger';
|
||||||
import { formatForInput } from '@vegaprotocol/utils';
|
import { useLedgerDownloadFile } from './ledger-download-store';
|
||||||
import { subDays } from 'date-fns';
|
|
||||||
|
|
||||||
const DEFAULT_EXPORT_FILE_NAME = 'ledger_entries.csv';
|
const DEFAULT_EXPORT_FILE_NAME = 'ledger_entries.csv';
|
||||||
|
|
||||||
@ -73,10 +78,14 @@ export const LedgerExportForm = ({ partyId, vegaUrl, assets }: Props) => {
|
|||||||
const maxFromDate = formatForInput(new Date(dateTo || now.current));
|
const maxFromDate = formatForInput(new Date(dateTo || now.current));
|
||||||
const maxToDate = formatForInput(now.current);
|
const maxToDate = formatForInput(now.current);
|
||||||
|
|
||||||
const [isDownloading, setIsDownloading] = useState(false);
|
|
||||||
const [assetId, setAssetId] = useState(Object.keys(assets)[0]);
|
const [assetId, setAssetId] = useState(Object.keys(assets)[0]);
|
||||||
const protohost = getProtoHost(vegaUrl);
|
const protohost = getProtoHost(vegaUrl);
|
||||||
const disabled = Boolean(!assetId || isDownloading);
|
const disabled = Boolean(!assetId);
|
||||||
|
|
||||||
|
const hasItem = useLedgerDownloadFile((store) => store.hasItem);
|
||||||
|
const updateDownloadQueue = useLedgerDownloadFile(
|
||||||
|
(store) => store.updateQueue
|
||||||
|
);
|
||||||
|
|
||||||
const assetDropDown = (
|
const assetDropDown = (
|
||||||
<TradingSelect
|
<TradingSelect
|
||||||
@ -87,7 +96,6 @@ export const LedgerExportForm = ({ partyId, vegaUrl, assets }: Props) => {
|
|||||||
}}
|
}}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
data-testid="select-ledger-asset"
|
data-testid="select-ledger-asset"
|
||||||
disabled={isDownloading}
|
|
||||||
>
|
>
|
||||||
{Object.keys(assets).map((assetKey) => (
|
{Object.keys(assets).map((assetKey) => (
|
||||||
<option key={assetKey} value={assetKey}>
|
<option key={assetKey} value={assetKey}>
|
||||||
@ -97,32 +105,78 @@ export const LedgerExportForm = ({ partyId, vegaUrl, assets }: Props) => {
|
|||||||
</TradingSelect>
|
</TradingSelect>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const link = createDownloadUrl({
|
||||||
|
protohost,
|
||||||
|
partyId,
|
||||||
|
assetId,
|
||||||
|
dateFrom,
|
||||||
|
dateTo,
|
||||||
|
});
|
||||||
|
|
||||||
const startDownload = async (event: React.FormEvent<HTMLFormElement>) => {
|
const startDownload = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
try {
|
|
||||||
const link = createDownloadUrl({
|
const title = t('Downloading for %s from %s till %s', [
|
||||||
protohost,
|
assets[assetId],
|
||||||
partyId,
|
format(new Date(dateFrom), 'dd MMMM yyyy HH:mm'),
|
||||||
assetId,
|
format(new Date(dateTo || Date.now()), 'dd MMMM yyyy HH:mm'),
|
||||||
dateFrom,
|
]);
|
||||||
dateTo,
|
|
||||||
|
const downloadStoreItem = {
|
||||||
|
title,
|
||||||
|
link,
|
||||||
|
isChanged: true,
|
||||||
|
};
|
||||||
|
if (hasItem(link)) {
|
||||||
|
updateDownloadQueue(downloadStoreItem);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ts = setTimeout(() => {
|
||||||
|
updateDownloadQueue({
|
||||||
|
...downloadStoreItem,
|
||||||
|
intent: Intent.Warning,
|
||||||
|
isDelayed: true,
|
||||||
|
isChanged: true,
|
||||||
});
|
});
|
||||||
setIsDownloading(true);
|
}, 1000 * 30);
|
||||||
|
|
||||||
|
try {
|
||||||
|
updateDownloadQueue(downloadStoreItem);
|
||||||
const resp = await fetch(link);
|
const resp = await fetch(link);
|
||||||
|
if (!resp?.ok) {
|
||||||
|
if (resp?.status === 429) {
|
||||||
|
throw new Error('Too many requests. Try again later.');
|
||||||
|
}
|
||||||
|
throw new Error('Download of ledger entries failed');
|
||||||
|
}
|
||||||
const { headers } = resp;
|
const { headers } = resp;
|
||||||
const nameHeader = headers.get('content-disposition');
|
const nameHeader = headers.get('content-disposition');
|
||||||
const filename = nameHeader?.split('=').pop() ?? DEFAULT_EXPORT_FILE_NAME;
|
const filename = nameHeader?.split('=').pop() ?? DEFAULT_EXPORT_FILE_NAME;
|
||||||
|
updateDownloadQueue({
|
||||||
|
...downloadStoreItem,
|
||||||
|
filename,
|
||||||
|
});
|
||||||
const blob = await resp.blob();
|
const blob = await resp.blob();
|
||||||
if (blob) {
|
if (blob) {
|
||||||
const link = document.createElement('a');
|
updateDownloadQueue({
|
||||||
link.href = URL.createObjectURL(blob);
|
...downloadStoreItem,
|
||||||
link.download = filename;
|
blob,
|
||||||
link.click();
|
isDownloaded: true,
|
||||||
|
isChanged: true,
|
||||||
|
intent: Intent.Success,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
localLoggerFactory({ application: 'ledger' }).error('Download file', err);
|
localLoggerFactory({ application: 'ledger' }).error('Download file', err);
|
||||||
|
updateDownloadQueue({
|
||||||
|
...downloadStoreItem,
|
||||||
|
intent: Intent.Danger,
|
||||||
|
isError: true,
|
||||||
|
isChanged: true,
|
||||||
|
errorMessage: (err as Error).message || undefined,
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setIsDownloading(false);
|
clearTimeout(ts);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -145,7 +199,6 @@ export const LedgerExportForm = ({ partyId, vegaUrl, assets }: Props) => {
|
|||||||
id="date-from"
|
id="date-from"
|
||||||
value={dateFrom}
|
value={dateFrom}
|
||||||
onChange={(e) => setDateFrom(e.target.value)}
|
onChange={(e) => setDateFrom(e.target.value)}
|
||||||
disabled={disabled}
|
|
||||||
max={maxFromDate}
|
max={maxFromDate}
|
||||||
/>
|
/>
|
||||||
</TradingFormGroup>
|
</TradingFormGroup>
|
||||||
@ -156,19 +209,10 @@ export const LedgerExportForm = ({ partyId, vegaUrl, assets }: Props) => {
|
|||||||
id="date-to"
|
id="date-to"
|
||||||
value={dateTo}
|
value={dateTo}
|
||||||
onChange={(e) => setDateTo(e.target.value)}
|
onChange={(e) => setDateTo(e.target.value)}
|
||||||
disabled={disabled}
|
|
||||||
max={maxToDate}
|
max={maxToDate}
|
||||||
/>
|
/>
|
||||||
</TradingFormGroup>
|
</TradingFormGroup>
|
||||||
<div className="relative text-sm" title={t('Download all to .csv file')}>
|
<div className="relative text-sm" title={t('Download all to .csv file')}>
|
||||||
{isDownloading && (
|
|
||||||
<div
|
|
||||||
className="absolute flex items-center justify-center w-full h-full"
|
|
||||||
data-testid="download-spinner"
|
|
||||||
>
|
|
||||||
<Loader size="small" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<TradingButton
|
<TradingButton
|
||||||
fill
|
fill
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
@ -8,5 +8,13 @@ When I enter on ledger entries tab in portfolio page
|
|||||||
- in the form **Must** see a dropdown for select an asset, in which reports will be downloaded (<a name="7007-LEEN-002" href="#7007-LEEN-002">7007-LEEN-002</a>)
|
- in the form **Must** see a dropdown for select an asset, in which reports will be downloaded (<a name="7007-LEEN-002" href="#7007-LEEN-002">7007-LEEN-002</a>)
|
||||||
- in the form **Must** see inputs for select time period, in which reports will be downloaded (<a name="7007-LEEN-003" href="#7007-LEEN-003">7007-LEEN-003</a>)
|
- in the form **Must** see inputs for select time period, in which reports will be downloaded (<a name="7007-LEEN-003" href="#7007-LEEN-003">7007-LEEN-003</a>)
|
||||||
- default preselected period **Must** be the last 7 days (<a name="7007-LEEN-004" href="#7007-LEEN-004">7007-LEEN-004</a>)
|
- default preselected period **Must** be the last 7 days (<a name="7007-LEEN-004" href="#7007-LEEN-004">7007-LEEN-004</a>)
|
||||||
- during download a loader component **Must** be visible and all interactive elements in the form **Must** be disabled (<a name="7007-LEEN-005" href="#7007-LEEN-005">7007-LEEN-005</a>)
|
|
||||||
- **Must** see a note about time in file are in UTC and timezone of the user relative to UTC (<a name="7007-LEEN-006" href="#7007-LEEN-006">7007-LEEN-006</a>)
|
- **Must** see a note about time in file are in UTC and timezone of the user relative to UTC (<a name="7007-LEEN-006" href="#7007-LEEN-006">7007-LEEN-006</a>)
|
||||||
|
- As a user, I **must** see a message saying that this can take several minutes (<a name="7007-LEEN-007" href="#7007-LEEN-007">7007-LEEN-007</a>)
|
||||||
|
- After half a minute, the message is updated to say something like 'Still in progress' (<a name="7007-LEEN-008" href="#7007-LEEN-008">7007-LEEN-008</a>)
|
||||||
|
- A toast is shown when the download is complete (<a name="7007-LEEN-009" href="#7007-LEEN-009">7007-LEEN-009</a>)
|
||||||
|
- The download button should never be disabled
|
||||||
|
- If user tries to download file which is already in download: (<a name="7007-LEEN-010" href="#7007-LEEN-010">7007-LEEN-010</a>)
|
||||||
|
- if notification stayed open, nothing happens
|
||||||
|
- If notification was closed, will be open, no any new request will be fired
|
||||||
|
- If something has changed in the form (asset, dates, `Date.now`) new download will start.
|
||||||
|
- The state of the download form should be in sync with the download itself if you navigate away from the page or reload (<a name="7007-LEEN-011" href="#7007-LEEN-011">7007-LEEN-011</a>)
|
||||||
|
Loading…
Reference in New Issue
Block a user