Skip to content

Commit

Permalink
Fix PDF load and preview
Browse files Browse the repository at this point in the history
  • Loading branch information
wtcarter committed Sep 24, 2024
1 parent b30accd commit d061681
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 56 deletions.
6 changes: 5 additions & 1 deletion packages/cccv/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,14 @@ caption {
@apply font-source-sans-3 text-[16px] leading-[22px] text-textCaption;
}

button {
.button-style {
@apply font-source-sans-3 text-nui font-bold text-[16px] leading-[22px] border-2 rounded-3xl pl-4 border-nui pr-4 pt-2 pb-2;
}

button {
@apply button-style;
}

button:hover {
@apply font-source-sans-3 text-white bg-nui font-bold text-[16px] leading-[22px];
}
Expand Down
2 changes: 1 addition & 1 deletion packages/cccv/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"@heroicons/react": "^2.1.3",
"@lingui/react": "^4.11.1",
"@material-tailwind/react": "^2.1.9",
"@react-pdf/renderer": "^3.4.5",
"@react-pdf/renderer": "^4.0.0",
"@tanstack/react-query": "^5.28.8",
"@turf/turf": "^7.1.0",
"@types/lodash": "^4.17.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,38 @@
import "./FreshwaterManagementUnit.scss";
import purify from "dompurify";
import {Key} from "react";
import { FmuFullDetailsWithMap } from "@models/FreshwaterManagementUnit.ts";
import { PDFDownloadLink } from "@react-pdf/renderer";
import { FreshwaterManagementUnitPDF } from "@components/FreshwaterManagementUnit/FreshwaterManagementUnit.pdf";
import formatFilename from "@lib/formatAsFilename";
import dateTimeString from "@lib/dateTimeString";
import { ContaminantList, contaminants as fmuContaminants } from "@components/FreshwaterManagementUnit/utils.ts";
import EmailLink from "@components/EmailLink/EmailLink.tsx";
import { Contaminants } from "@components/Contaminants/Contaminants.tsx";
import makeSafe from "@lib/makeSafe.ts";
import {parseHtmlListToArray} from "@lib/parseHtmlListToArray.ts";
import "./FreshwaterManagementUnit.scss"
import purify from "dompurify"
import { Key, useEffect, useState, useMemo } from "react"
import { FmuFullDetailsWithMap } from "@models/FreshwaterManagementUnit.ts"
import {usePDF, UsePDFInstance} from "@react-pdf/renderer"
import { FreshwaterManagementUnitPDF } from "@components/FreshwaterManagementUnit/FreshwaterManagementUnit.pdf"
import formatFilename from "@lib/formatAsFilename"
import dateTimeString from "@lib/dateTimeString"
import { ContaminantList, contaminants as fmuContaminants } from "@components/FreshwaterManagementUnit/utils.ts"
import EmailLink from "@components/EmailLink/EmailLink.tsx"
import { Contaminants } from "@components/Contaminants/Contaminants.tsx"
import makeSafe from "@lib/makeSafe.ts"
import { parseHtmlListToArray } from "@lib/parseHtmlListToArray.ts"
import { Spinner } from "@components/LoadingIndicator/LoadingIndicatorOverlay"

const FreshwaterManagementUnit = (details: FmuFullDetailsWithMap) => {
interface DownloadLinkProps {
pdfLoading: boolean;
instance: UsePDFInstance;
fileName: string;
hasError?: boolean;
}

if (!details?.freshwaterManagementUnit) {
return <div>No data found.</div>
const DownloadLink: React.FC<DownloadLinkProps> = ({ pdfLoading, instance, fileName, hasError }) => {
if (hasError) {
return <span>Error loading PDF</span>
}

if (pdfLoading) {
return <Spinner width={3} height={5} /> // Replace this with your preferred spinner component
}

return <a className="button-style" href={instance.url!} download={fileName}>Print</a>
}

const FreshwaterManagementUnit = (details: FmuFullDetailsWithMap) => {
const {
id,
fmuName1,
Expand All @@ -27,24 +42,50 @@ const FreshwaterManagementUnit = (details: FmuFullDetailsWithMap) => {

const tangataWhenuaSites = details.tangataWhenuaSites

const fileName = formatFilename((fmuName1 || '').toString(), `fmu_${id}`) + `_${dateTimeString()}` + '.pdf'
const fileName = formatFilename(fmuName1 || "", `fmu_${id}`) + `_${dateTimeString()}` + ".pdf"

const contaminants: ContaminantList = fmuContaminants(details.freshwaterManagementUnit)

const pdfDocument = useMemo(() => <FreshwaterManagementUnitPDF {...details} />, [details])

const [instance, updateInstance] = usePDF({ document: pdfDocument })
const [pdfLoading, setPdfLoading] = useState(true)
const [hasError, setHasError] = useState(false)

useEffect(() => {
if (instance) {
setPdfLoading(instance.loading)
if (instance.error) {
setHasError(true)
setPdfLoading(false)
}
}
}, [instance])

useEffect(() => {
updateInstance(pdfDocument)
}, [fileName, pdfDocument, updateInstance])

if (!details?.freshwaterManagementUnit) {
return <div>No data found.</div>
}

return (
<div className="FreshwaterManagementUnit bg-white p-6 pt-0 relative overflow-hidden" id={`fmu_${id || ''}`}>
<h1 className="w-[80%]">{fmuName1 || ""}</h1>

<div className="absolute top-0 right-0 m-6 mt-0">
<PDFDownloadLink document={<FreshwaterManagementUnitPDF {...details} />} fileName={fileName}>
<button>Print</button>
</PDFDownloadLink>
<div className="absolute top-0 right-0 m-6 mt-3">
<DownloadLink pdfLoading={pdfLoading} instance={instance} fileName={fileName} hasError={hasError} />
</div>

<div className="overview mt-6" data-testid="catchment-desc">
<h2>Overview</h2>
<div dangerouslySetInnerHTML={{ __html: purify.sanitize(makeSafe(catchmentDescription ?? "<p>No overview available</p>")) }} />
</div>
<div className="overview mt-6" data-testid="catchment-desc">
<h2>Overview</h2>
<div
dangerouslySetInnerHTML={{
__html: purify.sanitize(makeSafe(catchmentDescription ?? "<p>No overview available</p>")),
}}
/>
</div>

<div className="contaminants mt-6">
<h2>Contaminants</h2>
Expand All @@ -61,29 +102,41 @@ const FreshwaterManagementUnit = (details: FmuFullDetailsWithMap) => {
<p className="italic">This area contains sites of significance to Tangata Whenua including:</p>
<div className="tangata-whenua-sites">
<ul className="mt-2 list-disc">
{tangataWhenuaSites?.map((site: { location: string }, index: Key | null | undefined) => <li
className="my-0" key={index}>{site?.location}</li>)}
{tangataWhenuaSites?.map((site: { location: string }, index: Key | null | undefined) => (
<li className="my-0" key={index}>
{site?.location}
</li>
))}
</ul>
</div>
</div>
) : <div></div>}

{implementationIdeas ? (
<div className="implementation-ideas mt-6">
<h2>Implementation Ideas</h2>
<div className="implementation-ideas">
<ul className={"mt-2"}>
{parseHtmlListToArray(implementationIdeas)?.map((idea: string, index) => <li
className="list-disc my-0" key={index}>{makeSafe(idea)}</li>)}
</ul>
</div>
</div>
) : <div></div>}
) : (
<div></div>
)}

{implementationIdeas ? (
<div className="implementation-ideas mt-6">
<h2>Implementation Ideas</h2>
<div className="implementation-ideas">
<ul className={"mt-2"}>
{parseHtmlListToArray(implementationIdeas)?.map((idea: string, index) => (
<li className="list-disc my-0" key={index}>
{makeSafe(idea)}
</li>
))}
</ul>
</div>
</div>
) : (
<div></div>
)}

<div className="about-this-information mt-6">
<h3>About this information</h3>
<p>The content, data, and information used in this app comes from multiple sources, including Greater
Wellington’s <a>Natural Resources Plan</a> (2018) and Whaitua Implementation Plans.</p>
<p>
The content, data, and information used in this app comes from multiple sources, including Greater
Wellington’s <a>Natural Resources Plan</a> (2018) and Whaitua Implementation Plans.
</p>
<div className="mt-6 flex justify-center">
<EmailLink>Contact us for more information</EmailLink>
</div>
Expand Down
2 changes: 1 addition & 1 deletion packages/cccv/src/components/InfoPanel/SlidingPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export default function SlidingPanel({ showPanel, contentChanged, onResize, chil
useEffect(() => {
if (panelRef.current && isLargeScreen) {
const parentWidth = panelRef.current.parentElement?.getBoundingClientRect().width || 0
const initialWidth = parentWidth * 0.3
const initialWidth = parentWidth * 0.35
setPanelWidth(initialWidth)
onResize && onResize(initialWidth)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
import React from 'react'

export const LoadingIndicatorOverlay: React.FC = () => {
interface LoadingIndicatorOverlayProps {
width?: number;
height?: number;
}

export function Spinner(props: { width: number, height: number }) {
return <div
className={`spinner-border animate-spin inline-block w-${props.width} h-${props.height} border-4 rounded-full`}
role="status">
<svg className={`animate-spin -ml-1 mr-3 w-${props.width} h-${props.height} text-white" xmlns="http://www.w3.org/2000/svg`}
fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="navy"
stroke-width="4"></circle>
<path className="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
</div>
}

export const LoadingIndicatorOverlay: React.FC<LoadingIndicatorOverlayProps> = ({ width = 5, height = 5 }) => {
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="spinner-border animate-spin inline-block w-8 h-8 border-4 rounded-full" role="status">
<svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="navy"
stroke-width="4"></circle>
<path className="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
</div>
<Spinner width={width} height={height}/>
</div>
)
}
3 changes: 2 additions & 1 deletion packages/cccv/src/pages/MapPage/MapPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ export default function MapPage() {
<main role="application">
{/* Position the sliding panel relative to this map panel */}
<div className={`map-panel relative`}>
<InteractiveMap startLocation={locationDetails as ViewLocation} locationInFocus={selectedLocation} setLocationInFocus={(selectLocation)} />
<InteractiveMap startLocation={locationDetails as ViewLocation} locationInFocus={selectedLocation}
setLocationInFocus={(selectLocation)}/>
<div className={`address-box`}>
<AddressSearch
onSelect={selectAddress}
Expand Down

0 comments on commit d061681

Please sign in to comment.