Skip to content

Commit

Permalink
feat(web): export-list-option
Browse files Browse the repository at this point in the history
  • Loading branch information
Harman-singh-waraich committed Aug 27, 2024
1 parent 91c47ac commit cd73fd3
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 2 deletions.
1 change: 1 addition & 0 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
"graphql": "^16.8.1",
"graphql-request": "~6.1.0",
"jose": "^5.3.0",
"json-2-csv": "^5.5.5",
"moment": "^2.30.1",
"overlayscrollbars": "^2.4.6",
"overlayscrollbars-react": "^0.5.3",
Expand Down
1 change: 1 addition & 0 deletions web/src/assets/svgs/icons/export.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 60 additions & 0 deletions web/src/hooks/queries/useRegistryAllItemsQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { graphql } from "src/graphql";
import { ItemDetailsQuery, RegistryItemsQuery } from "src/graphql/graphql";
import { useQuery } from "@tanstack/react-query";
import { useGraphqlBatcher } from "context/GraphqlBatcher";
export type { ItemDetailsQuery };

export const registryItemsQuery = graphql(`
query RegistryItems($registryId: Bytes!, $first: Int, $skip: Int) {
items(where: { registryAddress: $registryId }, orderBy: latestRequestSubmissionTime, first: $first, skip: $skip) {
id
status
disputed
props {
label
description
value
isIdentifier
type
}
}
}
`);

export const useRegistryItemsQuery = (id?: string) => {
const { graphqlBatcher } = useGraphqlBatcher();

return useQuery<RegistryItemsQuery["items"]>({
queryKey: ["registryItemsQuery", id],
// only fetch when download button clicked
enabled: false,
queryFn: async () => {
let allData: RegistryItemsQuery["items"] = [];
const first = 1000;
let skip = 0;
let keepFetching = true;

try {
while (keepFetching) {
const data = await graphqlBatcher.fetch({
id: crypto.randomUUID(),
document: registryItemsQuery,
variables: { registryId: id, first, skip },
});

allData = allData.concat(data.items);

// If less than 1000 items are returned, we have reached the end
if (data.items.length < first) {
keepFetching = false;
}

skip += first;
}
} catch (error) {
console.error("Error fetching data:", error);
}
return allData;
},
});
};
73 changes: 73 additions & 0 deletions web/src/pages/AllLists/RegistryDetails/ItemsDownloadLabel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React, { useEffect, useRef, useState } from "react";
import styled from "styled-components";
import { useRegistryItemsQuery } from "queries/useRegistryAllItemsQuery";
import { json2csv } from "json-2-csv";
import ExportIcon from "svgs/icons/export.svg";

const StyledLink = styled.a`
display: flex;
flex-direction: row;
align-items: end;
gap: 8px;
text-decoration: none;
color: ${({ theme }) => theme.primaryBlue};
margin-top: 48px;
align-self: flex-end;
cursor: pointer;
`;

const StyledExportIcon = styled(ExportIcon)`
path {
stroke: ${({ theme }) => theme.primaryBlue};
}
`;

const ItemsDownloadLabel: React.FC<{ registryAddress?: string }> = ({ registryAddress }) => {
const [isPreparing, setIsPreparing] = useState(false);
const ref = useRef<HTMLAnchorElement>(null);

const { data: items, refetch, isRefetching } = useRegistryItemsQuery(registryAddress);

useEffect(() => {
if (!items || !ref.current) return;
setIsPreparing(true);
const flattenedItems = items.flatMap((item) =>
item.props.map((prop) => ({
id: item.id,
status: item.status,
disputed: item.disputed,
propLabel: prop.label,
propDescription: prop.description,
propValue: prop.value,
propIsIdentifier: prop.isIdentifier,
propType: prop.type,
}))
);
const csvData = json2csv(flattenedItems);
const blob = new Blob([csvData], { type: "text/csv;charset=utf-8;" });
const link = ref.current;

if (link.download !== undefined) {
const url = URL.createObjectURL(blob);
link.setAttribute("href", url);
link.setAttribute("download", `${registryAddress}.csv`);
link.click();
}

setIsPreparing(false);
}, [items]);

return (
<StyledLink onClick={() => refetch()} aria-disabled={isRefetching} ref={ref}>
{isRefetching || isPreparing ? (
<>Exporting list...</>
) : (
<>
Export as csv <StyledExportIcon />
</>
)}
</StyledLink>
);
};

export default ItemsDownloadLabel;
12 changes: 10 additions & 2 deletions web/src/pages/AllLists/RegistryDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ import { RegistryDetails as RegistryDetailsType, useRegistryDetailsContext } fro
import { useRegistryDetailsQuery } from "queries/useRegistryDetailsQuery";
import { useItemDetailsQuery } from "queries/useItemDetailsQuery";
import { List_filters } from "consts/filters";
import ItemsDownloadLabel from "./ItemsDownloadLabel";
import styled from "styled-components";

const Container = styled.div`
display: flex;
flex-direction: column;
`;

const RegistryDetails: React.FC = () => {
const { id } = useParams();
Expand Down Expand Up @@ -41,7 +48,7 @@ const RegistryDetails: React.FC = () => {
}, [itemDetails, registryDetails, setRegistryDetails]);

return (
<div>
<Container>
<RegistryInformationCard
id={listAddress}
{...{
Expand All @@ -63,7 +70,8 @@ const RegistryDetails: React.FC = () => {
<Route path="history" element={<History itemId={itemId} />} />
<Route path="*" element={<Navigate to={`list/1/desc/${JSON.stringify(List_filters.Active)}`} replace />} />
</Routes>
</div>
<ItemsDownloadLabel registryAddress={listAddress} />
</Container>
);
};

Expand Down

0 comments on commit cd73fd3

Please sign in to comment.