diff --git a/.env.example b/.env.example index 49ffc81fc..442fc5c5a 100644 --- a/.env.example +++ b/.env.example @@ -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" \ No newline at end of file +SENTRY_DSN="sentry-dsn" +# CHROME_EXECUTABLE_PATH="/usr/bin/google-chrome-stable" \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 034d2de98..a29b257aa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 @@ -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" ] diff --git a/Dockerfile.image b/Dockerfile.image index 38809ea00..f25dfa1ce 100644 --- a/Dockerfile.image +++ b/Dockerfile.image @@ -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 diff --git a/app/components/booking/actions-dropdown.tsx b/app/components/booking/actions-dropdown.tsx index 541eaa3e5..41c8ea303 100644 --- a/app/components/booking/actions-dropdown.tsx +++ b/app/components/booking/actions-dropdown.tsx @@ -1,4 +1,5 @@ import { useLoaderData, useSubmit } from "@remix-run/react"; +import { Divider } from "@tremor/react"; import { ChevronRight } from "~/components/icons/library"; import { DropdownMenu, @@ -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 { @@ -45,7 +47,7 @@ export const ActionsDropdown = ({ fullWidth }: Props) => { {isOngoing || isReserved || isOverdue ? ( @@ -104,6 +106,11 @@ export const ActionsDropdown = ({ fullWidth }: Props) => { {(isSelfService && isDraft) || !isSelfService ? ( ) : null} + + diff --git a/app/components/booking/generate-booking-pdf.tsx b/app/components/booking/generate-booking-pdf.tsx new file mode 100644 index 000000000..0cb847c4f --- /dev/null +++ b/app/components/booking/generate-booking-pdf.tsx @@ -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[]; + }; + 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 ( + <> + + + +

Generate booking checklist for "{booking?.name}"

+

You can either preview or download the PDF.

+ + } + > +
+
+ {/** Show spinner if no iframe */} + {!iframeLoaded && ( +
+ +

Generating PDF...

+
+ )} + {totalAssets && ( +
+