Skip to content

Commit

Permalink
feat(bulk-download-qr): create dialog for bulk download qr codes
Browse files Browse the repository at this point in the history
  • Loading branch information
rockingrohit9639 committed Jan 31, 2025
1 parent c3eec61 commit 5f9b36d
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 25 deletions.
28 changes: 26 additions & 2 deletions app/components/assets/bulk-actions-dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useState } from "react";
import { useNavigation } from "@remix-run/react";
import { useAtomValue } from "jotai";
import { useHydrated } from "remix-utils/use-hydrated";
Expand Down Expand Up @@ -33,6 +34,7 @@ import {
import When from "../when/when";
import BookSelectedAssetsDropdown from "./assets-index/book-selected-assets-dropdown";
import BulkDownloadQrDialog from "./bulk-download-qr-dialog";
import Icon from "../icons/icon";

export default function BulkActionsDropdown() {
const isHydrated = useHydrated();
Expand All @@ -58,6 +60,7 @@ export default function BulkActionsDropdown() {
function ConditionalDropdown() {
const navigation = useNavigation();
const isLoading = isFormProcessing(navigation.state);
const [isBulkDownloadQrOpen, setIsBulkDownloadQrOpen] = useState(false);

const {
ref: dropdownRef,
Expand Down Expand Up @@ -130,6 +133,13 @@ function ConditionalDropdown() {
<BulkMarkAvailabilityDialog type="unavailable" />
</When>

<BulkDownloadQrDialog
isDialogOpen={isBulkDownloadQrOpen}
onClose={() => {
setIsBulkDownloadQrOpen(false);
}}
/>

<When
truthy={userHasPermission({
roles,
Expand Down Expand Up @@ -250,8 +260,22 @@ function ConditionalDropdown() {
</DropdownMenuItem>
</When>

<DropdownMenuItem className="py-1 lg:p-0">
<BulkDownloadQrDialog />
<DropdownMenuItem
onClick={() => {
closeMenu();
setIsBulkDownloadQrOpen(true);
}}
className="py-1 lg:p-0"
>
<Button
variant="link"
className="w-full justify-start px-4 py-3 text-gray-700 hover:text-gray-700"
width="full"
>
<span className="flex items-center gap-2">
<Icon icon="download" /> Download QR Codes
</span>
</Button>
</DropdownMenuItem>

<When
Expand Down
119 changes: 96 additions & 23 deletions app/components/assets/bulk-download-qr-dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,52 @@
import { useState } from "react";
import { cloneElement, useState } from "react";
import domtoimage from "dom-to-image";
import { useAtomValue } from "jotai";
import JSZip from "jszip";
import { DownloadIcon } from "lucide-react";
import { renderToStaticMarkup } from "react-dom/server";
import { selectedBulkItemsAtom } from "~/atoms/list";
import { useSearchParams } from "~/hooks/search-params";
import { isSelectingAllItems } from "~/utils/list";
import { tw } from "~/utils/tw";
import Icon from "../icons/icon";
import { Dialog, DialogPortal } from "../layout/dialog";
import type { QrDef } from "../qr/qr-preview";
import { QrLabel } from "../qr/qr-preview";
import { Button } from "../shared/button";

export default function BulkDownloadQrDialog() {
const [isGeneratingQrCodes, setIsGeneratingQrCodes] = useState(false);
const [error, setError] = useState("");
import When from "../when/when";

type BulkDownloadQrDialogProps = {
className?: string;
isDialogOpen: boolean;
onClose: () => void;
};

type DownloadState =
| { status: "idle" }
| { status: "loading" }
| { status: "success" }
| { status: "error"; error: string };

export default function BulkDownloadQrDialog({
className,
isDialogOpen,
onClose,
}: BulkDownloadQrDialogProps) {
const [downloadState, setDownloadState] = useState<DownloadState>({
status: "idle",
});
const [searchParams] = useSearchParams();

const selectedAssets = useAtomValue(selectedBulkItemsAtom);
const allAssetsSelected = isSelectingAllItems(selectedAssets);

const disabled =
selectedAssets.length === 0 || downloadState.status === "loading";

function handleClose() {
setDownloadState({ status: "idle" });
onClose();
}

async function handleBulkDownloadQr() {
const query = new URLSearchParams();
Expand All @@ -24,7 +55,7 @@ export default function BulkDownloadQrDialog() {
query.append("assetIds", asset.id);
});

setIsGeneratingQrCodes(true);
setDownloadState({ status: "loading" });

try {
/* Getting all validated assets with qr object */
Expand All @@ -47,7 +78,7 @@ export default function BulkDownloadQrDialog() {

/* Converting our React compoentn to html so that we can later convert it into an image */
const qrCodeContent = renderToStaticMarkup(
<div className="flex w-full items-center justify-center p-6">
<div className="flex w-full items-center justify-center p-6 text-center">
<QrLabel data={{ qr: asset.qr }} title={asset.title} />
</div>
);
Expand All @@ -65,6 +96,7 @@ export default function BulkDownloadQrDialog() {
display: "flex",
alignItems: "center",
justifyContent: "center",
textAlign: "center",
transform: "scale(2)",
transformOrigin: "center",
},
Expand All @@ -91,26 +123,67 @@ export default function BulkDownloadQrDialog() {
setTimeout(() => {
URL.revokeObjectURL(downloadLink.href);
}, 4e4);

setDownloadState({ status: "success" });
} catch (error) {
setError(
error instanceof Error ? error.message : "Something went wrong."
);
} finally {
setIsGeneratingQrCodes(false);
setDownloadState({
status: "error",
error: error instanceof Error ? error.message : "Something went wrong.",
});
}
}

return (
<Button
variant="link"
className="w-full justify-start px-4 py-3 text-gray-700 hover:text-gray-700"
width="full"
onClick={handleBulkDownloadQr}
disabled={!selectedAssets.length || isGeneratingQrCodes}
>
<span className="flex items-center gap-2">
<Icon icon="download" /> Download QR Codes
</span>
</Button>
<DialogPortal>
<Dialog
open={isDialogOpen}
onClose={handleClose}
className={className}
title={
<div className="flex items-center justify-center rounded-full border-8 border-primary-50 bg-primary-100 p-2 text-primary-600">
<DownloadIcon />
</div>
}
>
<div className="px-6 py-4">
<h4 className="mb-1">
Download qr codes for{" "}
{allAssetsSelected ? "all" : selectedAssets.length} asset(s).
</h4>
<p className="mb-4">
{allAssetsSelected ? "All" : selectedAssets.length} qr code(s) will
be downloaded in a zip file.
</p>
<When truthy={downloadState.status === "success"}>
<p className="mb-4 text-success-500">
Successfully downloaded qr codes.
</p>
</When>

{downloadState.status === "error" ? (
<p className="mb-4 text-error-500">{downloadState.error}</p>
) : null}

<div className="flex w-full items-center justify-center gap-4">
<Button
className="flex-1"
variant="secondary"
onClick={handleClose}
disabled={disabled}
>
Close
</Button>

<Button
className="flex-1"
onClick={handleBulkDownloadQr}
disabled={disabled}
>
Download
</Button>
</div>
</div>
</Dialog>
</DialogPortal>
);
}

0 comments on commit 5f9b36d

Please sign in to comment.