Skip to content

Commit

Permalink
Merge pull request #53 from kleros/feat/downdable-lists
Browse files Browse the repository at this point in the history
Feat/downdable lists
  • Loading branch information
Harman-singh-waraich authored Aug 28, 2024
2 parents 91c47ac + 7a9d6f3 commit 058af70
Show file tree
Hide file tree
Showing 6 changed files with 172 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;
},
});
};
75 changes: 75 additions & 0 deletions web/src/pages/AllLists/RegistryDetails/ItemsDownloadLabel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
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.map((item) => {
const row = {
id: item.id,
status: item.status,
disputed: item.disputed,
};

item.props.forEach((prop) => {
row[`${prop.label} (${prop.description})`] = prop.value;
});

return row;
});

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", `Kleros-Curate-${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
25 changes: 25 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4851,6 +4851,7 @@ __metadata:
graphql: "npm:^16.8.1"
graphql-request: "npm:~6.1.0"
jose: "npm:^5.3.0"
json-2-csv: "npm:^5.5.5"
lru-cache: "npm:^7.18.3"
moment: "npm:^2.30.1"
overlayscrollbars: "npm:^2.4.6"
Expand Down Expand Up @@ -14210,6 +14211,13 @@ __metadata:
languageName: node
linkType: hard

"deeks@npm:3.1.0":
version: 3.1.0
resolution: "deeks@npm:3.1.0"
checksum: 4297893cb792bc207b1fdac6090094c8c666669227b385bc69d98ea751294614948288a39a38faa0a44d7045d1bf05eacd188eea7516a1a7d0fee9a7faa34e7d
languageName: node
linkType: hard

"deep-eql@npm:^4.0.1, deep-eql@npm:^4.1.3":
version: 4.1.3
resolution: "deep-eql@npm:4.1.3"
Expand Down Expand Up @@ -14523,6 +14531,13 @@ __metadata:
languageName: node
linkType: hard

"doc-path@npm:4.1.1":
version: 4.1.1
resolution: "doc-path@npm:4.1.1"
checksum: 455dd7458d4fa9ec0662fc307c4c3a7112bd0d554b8a6e0e86a6c6554f125727b4f80af430c1038f5d471ed5586c95354260928a31b80e632519480d745af713
languageName: node
linkType: hard

"docker-compose@npm:0.23.19":
version: 0.23.19
resolution: "docker-compose@npm:0.23.19"
Expand Down Expand Up @@ -20308,6 +20323,16 @@ __metadata:
languageName: node
linkType: hard

"json-2-csv@npm:^5.5.5":
version: 5.5.5
resolution: "json-2-csv@npm:5.5.5"
dependencies:
deeks: "npm:3.1.0"
doc-path: "npm:4.1.1"
checksum: 671a72d484349fa2b74e32fb35f02706ea056dee0d840c89b39165ef5f35fddc515b6c1ff9b684689796060af229c7161ce2af5f1d0646bea7694b881187b9d1
languageName: node
linkType: hard

"json-buffer@npm:3.0.1":
version: 3.0.1
resolution: "json-buffer@npm:3.0.1"
Expand Down

0 comments on commit 058af70

Please sign in to comment.