From cd73fd3d7cf43a4f2e9420d7dafbbe9218598417 Mon Sep 17 00:00:00 2001 From: Harman-singh-waraich Date: Tue, 27 Aug 2024 15:25:45 +0530 Subject: [PATCH] feat(web): export-list-option --- web/package.json | 1 + web/src/assets/svgs/icons/export.svg | 1 + .../hooks/queries/useRegistryAllItemsQuery.ts | 60 +++++++++++++++ .../RegistryDetails/ItemsDownloadLabel.tsx | 73 +++++++++++++++++++ .../pages/AllLists/RegistryDetails/index.tsx | 12 ++- 5 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 web/src/assets/svgs/icons/export.svg create mode 100644 web/src/hooks/queries/useRegistryAllItemsQuery.ts create mode 100644 web/src/pages/AllLists/RegistryDetails/ItemsDownloadLabel.tsx diff --git a/web/package.json b/web/package.json index 19421ab..86a8bdc 100644 --- a/web/package.json +++ b/web/package.json @@ -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", diff --git a/web/src/assets/svgs/icons/export.svg b/web/src/assets/svgs/icons/export.svg new file mode 100644 index 0000000..0882db5 --- /dev/null +++ b/web/src/assets/svgs/icons/export.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/src/hooks/queries/useRegistryAllItemsQuery.ts b/web/src/hooks/queries/useRegistryAllItemsQuery.ts new file mode 100644 index 0000000..866c7d0 --- /dev/null +++ b/web/src/hooks/queries/useRegistryAllItemsQuery.ts @@ -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({ + 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; + }, + }); +}; diff --git a/web/src/pages/AllLists/RegistryDetails/ItemsDownloadLabel.tsx b/web/src/pages/AllLists/RegistryDetails/ItemsDownloadLabel.tsx new file mode 100644 index 0000000..839b174 --- /dev/null +++ b/web/src/pages/AllLists/RegistryDetails/ItemsDownloadLabel.tsx @@ -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(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 ( + refetch()} aria-disabled={isRefetching} ref={ref}> + {isRefetching || isPreparing ? ( + <>Exporting list... + ) : ( + <> + Export as csv + + )} + + ); +}; + +export default ItemsDownloadLabel; diff --git a/web/src/pages/AllLists/RegistryDetails/index.tsx b/web/src/pages/AllLists/RegistryDetails/index.tsx index 780a68d..7d9d6d6 100644 --- a/web/src/pages/AllLists/RegistryDetails/index.tsx +++ b/web/src/pages/AllLists/RegistryDetails/index.tsx @@ -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(); @@ -41,7 +48,7 @@ const RegistryDetails: React.FC = () => { }, [itemDetails, registryDetails, setRegistryDetails]); return ( -
+ { } /> } /> -
+ + ); };