Skip to content

Commit

Permalink
Merge pull request Shelf-nu#999 from rajeshj11/feat-942-28-6-2024
Browse files Browse the repository at this point in the history
feat:Generating a PDF with booking overview Shelf-nu#942
  • Loading branch information
DonKoko authored Jun 4, 2024
2 parents 1af7ccc + ee98751 commit 7c1c36c
Show file tree
Hide file tree
Showing 12 changed files with 1,426 additions and 110 deletions.
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,5 @@ GEOCODE_API_KEY="geocode-api-key"
# Used for Sentry error logging
SENTRY_ORG="sentry-org"
SENTRY_PROJECT="sentry-project"
SENTRY_DSN="sentry-dsn"
SENTRY_DSN="sentry-dsn"
# CHROME_EXECUTABLE_PATH="/usr/bin/google-chrome-stable"
45 changes: 43 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,51 @@ ENV NODE_ENV="production"
ARG DEBIAN_FRONTEND="noninteractive"
WORKDIR /src

# Install openssl for Prisma
# Install dependencies for Puppeteer and Google Chrome & Prisma
RUN apt-get update && \
apt-get install -y openssl && \
apt-get install -y \
ca-certificates \
fonts-liberation \
libappindicator3-1 \
libasound2 \
libatk-bridge2.0-0 \
libatk1.0-0 \
libcups2 \
libdbus-1-3 \
libdrm2 \
libgbm1 \
libgtk-3-0 \
libnspr4 \
libnss3 \
libx11-xcb1 \
libxcomposite1 \
libxdamage1 \
libxrandr2 \
wget \
xdg-utils \
libgbm-dev \
gconf-service \
libxss1 \
libxtst6 \
lsb-release \
unzip \
xvfb \
openssl || { cat /var/log/apt/*log; exit 1; } && \
rm -rf /var/lib/apt/lists/*


# Add Google Chrome repository and install Chrome
RUN apt-get update \
&& apt-get install -y wget gnupg \
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | gpg --dearmor -o /usr/share/keyrings/googlechrome-linux-keyring.gpg \
&& sh -c 'echo "deb [arch=amd64 signed-by=/usr/share/keyrings/googlechrome-linux-keyring.gpg] https://dl-ssl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
&& apt-get update \
&& apt-get install -y google-chrome-stable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-khmeros fonts-kacst fonts-freefont-ttf libxss1 dbus dbus-x11 \
--no-install-recommends \
&& rm -rf /var/lib/apt/lists/*

ENV CHROME_EXECUTABLE_PATH="/usr/bin/google-chrome-stable"

# Install all node_modules, including dev dependencies
FROM base AS deps

Expand All @@ -37,6 +77,7 @@ COPY --from=build /src/app/database /src/app/database
COPY --from=build /src/build /src/build
COPY --from=build /src/package.json /src/package.json
COPY --from=build /src/start.sh /src/start.sh

RUN chmod +x /src/start.sh

ENTRYPOINT [ "/src/start.sh" ]
44 changes: 42 additions & 2 deletions Dockerfile.image
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,51 @@ ENV NODE_ENV="production"
ARG DEBIAN_FRONTEND="noninteractive"
WORKDIR /src

# Install openssl for Prisma

# Install dependencies for Puppeteer and Google Chrome & Prisma
RUN apt-get update && \
apt-get install -y openssl && \
apt-get install -y \
ca-certificates \
fonts-liberation \
libappindicator3-1 \
libasound2 \
libatk-bridge2.0-0 \
libatk1.0-0 \
libcups2 \
libdbus-1-3 \
libdrm2 \
libgbm1 \
libgtk-3-0 \
libnspr4 \
libnss3 \
libx11-xcb1 \
libxcomposite1 \
libxdamage1 \
libxrandr2 \
wget \
xdg-utils \
libgbm-dev \
gconf-service \
libxss1 \
libxtst6 \
lsb-release \
unzip \
xvfb \
openssl || { cat /var/log/apt/*log; exit 1; } && \
rm -rf /var/lib/apt/lists/*


# Add Google Chrome repository and install Chrome
RUN apt-get update \
&& apt-get install -y wget gnupg \
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | gpg --dearmor -o /usr/share/keyrings/googlechrome-linux-keyring.gpg \
&& sh -c 'echo "deb [arch=amd64 signed-by=/usr/share/keyrings/googlechrome-linux-keyring.gpg] https://dl-ssl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
&& apt-get update \
&& apt-get install -y google-chrome-stable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-khmeros fonts-kacst fonts-freefont-ttf libxss1 dbus dbus-x11 \
--no-install-recommends \
&& rm -rf /var/lib/apt/lists/*

ENV CHROME_EXECUTABLE_PATH="/usr/bin/google-chrome-stable"
# Install all node_modules, including dev dependencies
FROM base AS deps

Expand Down
9 changes: 8 additions & 1 deletion app/components/booking/actions-dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useLoaderData, useSubmit } from "@remix-run/react";
import { Divider } from "@tremor/react";
import { ChevronRight } from "~/components/icons/library";
import {
DropdownMenu,
Expand All @@ -12,6 +13,7 @@ import { useUserIsSelfService } from "~/hooks/user-user-is-self-service";
import type { loader } from "~/routes/_layout+/bookings.$bookingId";
import { tw } from "~/utils/tw";
import { DeleteBooking } from "./delete-booking";
import { GenerateBookingPdf } from "./generate-booking-pdf";
import { Button } from "../shared/button";

interface Props {
Expand Down Expand Up @@ -45,7 +47,7 @@ export const ActionsDropdown = ({ fullWidth }: Props) => {
<DropdownMenuPortal>
<DropdownMenuContent
align="end"
className="order w-[180px] rounded-md bg-white p-1.5 text-right "
className="order w-[220px] rounded-md bg-white p-1.5 text-right "
>
{isOngoing || isReserved || isOverdue ? (
<DropdownMenuItem asChild>
Expand Down Expand Up @@ -104,6 +106,11 @@ export const ActionsDropdown = ({ fullWidth }: Props) => {
{(isSelfService && isDraft) || !isSelfService ? (
<DeleteBooking booking={booking} />
) : null}
<Divider className="my-2" />
<GenerateBookingPdf
booking={booking}
timeStamp={new Date().getTime()}
/>
</DropdownMenuContent>
</DropdownMenuPortal>
</DropdownMenu>
Expand Down
114 changes: 114 additions & 0 deletions app/components/booking/generate-booking-pdf.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { useState } from "react";
import type { Asset, Booking } from "@prisma/client";
import { Button } from "~/components/shared/button";

import { tw } from "~/utils/tw";
import { Dialog, DialogPortal } from "../layout/dialog";
import { Spinner } from "../shared/spinner";

export const GenerateBookingPdf = ({
booking,
timeStamp,
}: {
booking: {
id: Booking["id"];
name: Booking["name"];
assets: Partial<Asset>[];
};
timeStamp: number;
}) => {
const [iframeLoaded, setIframeLoaded] = useState(false);
const totalAssets = booking.assets.length;
const url = `/bookings/${booking.id.toString()}/generate-pdf/booking-checklist-${new Date()
.toISOString()
.slice(0, 10)}.pdf?timeStamp=${timeStamp}`;
const handleIframeLoad = () => {
setIframeLoaded(true);
};

const handleMobileView = () => {
window.location.href = url;
};

const [isDialogOpen, setIsDialogOpen] = useState(false);

const handleOpenDialog = () => {
setIsDialogOpen(true);
};

const handleCloseDialog = () => {
setIsDialogOpen(false);
};

return (
<>
<Button
variant="link"
className="hidden justify-start rounded-sm px-2 py-1.5 text-left text-sm font-medium text-gray-700 outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 hover:bg-slate-100 hover:text-gray-700 md:block"
width="full"
name="generate pdf"
onClick={handleOpenDialog}
disabled={!totalAssets}
>
Generate overview PDF
</Button>
<DialogPortal>
<Dialog
open={isDialogOpen}
onClose={handleCloseDialog}
className="h-[90vh] w-full py-0 md:h-[calc(100vh-4rem)] md:w-[90%]"
title={
<div>
<h3>Generate booking checklist for "{booking?.name}"</h3>
<p>You can either preview or download the PDF.</p>
</div>
}
>
<div className="flex h-full flex-col px-6">
<div className="grow">
{/** Show spinner if no iframe */}
{!iframeLoaded && (
<div className="m-4 flex h-full flex-1 flex-col items-center justify-center text-center">
<Spinner />
<p className="mt-2">Generating PDF...</p>
</div>
)}
{totalAssets && (
<div
className={tw(iframeLoaded ? "block" : "hidden", "h-full")}
>
<iframe
id="pdfPreview"
width="100%"
height="100%"
onLoad={handleIframeLoad}
src={url}
title="Booking PDF"
allowFullScreen={true}
/>
</div>
)}
</div>
<div className="flex justify-end gap-3 py-4">
<Button variant="secondary" onClick={handleCloseDialog}>
Cancel
</Button>
</div>
</div>
</Dialog>
</DialogPortal>

{/* Only for mobile */}
<Button
variant="link"
className="block justify-start rounded-sm px-2 py-1.5 text-left text-sm font-medium text-gray-700 outline-none hover:bg-slate-100 hover:text-gray-700 disabled:pointer-events-none disabled:opacity-50 md:hidden"
width="full"
name="generate pdf"
disabled={!totalAssets}
onClick={handleMobileView}
>
Generate overview PDF
</Button>
</>
);
};
4 changes: 4 additions & 0 deletions app/components/layout/dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { ReactNode } from "react";
import ReactDOM from "react-dom";
import { tw } from "~/utils/tw";
import { XIcon } from "../icons/library";
import { Button } from "../shared/button";
Expand Down Expand Up @@ -42,3 +43,6 @@ export const Dialog = ({
</dialog>
</div>
) : null;

export const DialogPortal = ({ children }: { children: React.ReactNode }) =>
ReactDOM.createPortal(children, document.body);
Loading

0 comments on commit 7c1c36c

Please sign in to comment.