Skip to content

Commit

Permalink
FE CCCV Print PDF
Browse files Browse the repository at this point in the history
  • Loading branch information
wtcarter committed Nov 29, 2024
1 parent a012e6e commit 04f37c2
Show file tree
Hide file tree
Showing 28 changed files with 286 additions and 127 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,7 @@ out/

qodana.yaml
/scripts/afs.main.kts
/docs-site/src/pages/404.js

/**/*.js
!workers/*.js
2 changes: 2 additions & 0 deletions packages/PlanLimitsUI/vite.config.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declare const _default: import("vite").UserConfig & Promise<import("vite").UserConfig> & (import("vite").UserConfigFnObject & import("vite").UserConfigExport);
export default _default;
4 changes: 2 additions & 2 deletions packages/cccv/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ caption {
}

.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;
@apply font-source-sans-3 hover:text-white text-nui font-bold text-[16px] leading-[22px] border-2 rounded-3xl pl-4 border-nui pr-4 pt-2 pb-2;
}

button {
Expand All @@ -49,7 +49,7 @@ button:hover {
}

button:disabled {
@apply font-source-sans-3 text-nui font-bold text-[16px] leading-[22px];
@apply font-source-sans-3 text-kapiti font-bold text-[16px] leading-[22px];
}

ul {
Expand Down
5 changes: 5 additions & 0 deletions packages/cccv/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
"@types/lodash": "^4.17.13",
"@types/mapbox-gl": "^3.4.1",
"@types/proj4": "^2.5.5",
"comlink": "^4.4.2",
"dompurify": "^3.2.1",
"file-saver": "^2.0.5",
"lodash": "^4.17.21",
"mapbox-gl": "^3.8.0",
"npm-force-resolutions": "^0.0.10",
Expand All @@ -40,7 +42,9 @@
"react-map-gl": "^7.1.7",
"react-pdf-tailwind": "^2.3.0",
"react-query": "^3.39.3",
"react-refresh": "^0.14.2",
"react-router-dom": "^6.28.0",
"react-use": "^17.5.1",
"storybook": "^8.4.5",
"styled-components": "^6.1.13",
"uninstall": "^0.0.0"
Expand All @@ -64,6 +68,7 @@
"@testing-library/react": "^16.0.1",
"@testing-library/user-event": "^14.5.2",
"@types/dompurify": "^3.2.0",
"@types/file-saver": "^2.0.7",
"@types/node": "^22.9.1",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
} from "@components/Contaminants/ContaminantObjectiveDescription"
import makeSafe from "@lib/makeSafe.ts"
import {parseHtmlListToArray} from "@lib/parseHtmlListToArray.ts"
import _ from "lodash"

Font.register(fonts.inter)

Expand Down Expand Up @@ -106,7 +107,9 @@ const MapImage: React.FC<{ src: string, width?: string }> = ({ src, width }) =>
)
}

export const FreshwaterManagementUnitPDF = (details: FmuFullDetailsWithMap) => {
export type FreshwaterManagementUnitPDFProps = FmuFullDetailsWithMap

export const FreshwaterManagementUnitPDF = (details: FreshwaterManagementUnitPDFProps) => {

const {
fmuName1,
Expand All @@ -119,7 +122,7 @@ export const FreshwaterManagementUnitPDF = (details: FmuFullDetailsWithMap) => {
const contaminants: ContaminantList = fmuContaminants(details.freshwaterManagementUnit)

return (
<Document>
<Document key={_.get(details, "key")}>
<Page size="A4" style={tw("bg-white font-sans p-4 flex flex-col")}>

{/* Header */}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,15 @@ const FreshwaterManagementUnit = (
}

const gotoTangataWhenua = (i: number) => {
if (tangataWhenuaSites) links?.gotoLink(tangataWhenuaSites.features[i])
if (tangataWhenuaSites) {
links?.gotoLink(tangataWhenuaSites.features[i])
}
}

const outerDiv = React.createRef<HTMLDivElement>()

return (
<div className="FreshwaterManagementUnit bg-white p-6 pt-0 relative overflow-hidden" id={`fmu_${id || ''}`}>
<div ref={outerDiv} className="FreshwaterManagementUnit bg-white p-6 pt-0 relative overflow-hidden" id={`fmu_${id || ''}`}>
{showHeader && <FmuPanelHeader className={"mb-6"} fmuName1={fmuName1!}/>}

<div className="overview mt-0" data-testid="catchment-desc">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type {FreshwaterManagementUnitPDFProps} from './FreshwaterManagementUnit.pdf.tsx'

export const renderPDF = async (props: FreshwaterManagementUnitPDFProps) => {
const { pdf } = await import('@react-pdf/renderer')
const { FreshwaterManagementUnitPDF } = await import('./FreshwaterManagementUnit.pdf.tsx')
return pdf(FreshwaterManagementUnitPDF(props)).toBlob()
}
11 changes: 6 additions & 5 deletions packages/cccv/src/components/InfoPanel/SlidingPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ interface InfoPanelProps {
onClose: () => void;
onResize?: (width: number) => void;
children?: React.ReactNode;
className?: string;
}

export default function SlidingPanel({ showPanel, contentChanged, onResize, onClose, children }: InfoPanelProps) {
export default function SlidingPanel({ showPanel, contentChanged, onResize, onClose, children, className }: InfoPanelProps) {
const panelRef = useRef<HTMLDivElement>(null)
const isResizing = useRef(false)
const [panelWidth, setPanelWidth] = useState(300)
Expand Down Expand Up @@ -77,7 +78,7 @@ export default function SlidingPanel({ showPanel, contentChanged, onResize, onCl
return (
<div
ref={panelRef}
className={`sliding-panel ${stateClass} absolute bg-white font-mono shadow-black ${signalUpdatedInfoPanel} ${revealOrHideInfoPanel} transition ease-in-out duration-500 z-[1000]`}
className={`sliding-panel ${stateClass} absolute bg-white font-mono shadow-black ${signalUpdatedInfoPanel} ${revealOrHideInfoPanel} transition ease-in-out duration-500 z-[1000] ${className}`}
style={{
width: isLargeScreen ? `${panelWidth}px` : '100%',
height: isLargeScreen ? `100vh` : '92vh',
Expand All @@ -87,7 +88,7 @@ export default function SlidingPanel({ showPanel, contentChanged, onResize, onCl
data-testid="sliding-panel"
>
<div
className="absolute top-0 m-0 p-0 text-gray-400 cursor-pointer leading-none z-50"
className={`absolute top-0 m-0 p-0 text-gray-400 bg-transparent cursor-pointer leading-none`}
onClick={handleClose}
role="button"
aria-label="Close Panel"
Expand All @@ -100,12 +101,12 @@ export default function SlidingPanel({ showPanel, contentChanged, onResize, onCl
</div>
{isLargeScreen && (
<div
className="absolute left-[-6px] top-0 bottom-0 w-[6px] cursor-ew-resize z-20 bg-gray-100"
className="absolute left-[-6px] top-0 bottom-0 w-[6px] cursor-ew-resize bg-gray-100"
onMouseDown={handleMouseDown}
onDoubleClick={handleDoubleClick}
/>
)}
<div className="relative z-30 overflow-auto h-full">
<div className="relative z-30 mt-10 overflow-auto h-full">
{children}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React, { createContext, useState } from 'react'

interface LoadingContextType {
loading: boolean;
addressLoading: boolean;
setLoading: (value: boolean) => void;
}

// eslint-disable-next-line react-refresh/only-export-components
export const LoadingContext = createContext<LoadingContextType | undefined>(undefined)

export const LoadingProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
Expand Down
93 changes: 48 additions & 45 deletions packages/cccv/src/elements/DownloadLink/DownloadLink.tsx
Original file line number Diff line number Diff line change
@@ -1,60 +1,63 @@
import { UsePDFInstance } from "@react-pdf/renderer"
import React, { useState } from "react"
import { Spinner as DownloadSpinner } from "@components/LoadingIndicator/LoadingIndicatorOverlay.tsx"
import { makeFileNameSafe } from "@lib/makeSafe.ts"
import {makeFileNameSafe} from "@lib/makeSafe.ts"

export const DownloadLink: React.FC<DownloadLinkProps> = ({ instance, fileName, loading, error }) => {
// const [ready, setReady] = useState(false)
//
// useEffect(() => {
// const preloadBlob = async () => {
// if (instance && instance.url) {
// try {
// const response = await fetch(instance.url)
// const blob = await response.blob()
// } catch (error) {
// console.error("Preload failed:", error)
// }
// }
// }
//
// preloadBlob().then()
// }, [instance])

export const DownloadLink: React.FC<DownloadLinkProps> = ({
href,
fileName,
pdfLoading,
hasError,
}) => {
const [downloading, setDownloading] = useState(false)

const handleDownload = () => {
setDownloading(true)

const link = document.createElement("a")
link.href = href
link.download = makeFileNameSafe(fileName, ["pdf"])

document.body.appendChild(link)
link.click()
document.body.removeChild(link)

setDownloading(false)
}

if (pdfLoading) {
return (
<div className="loading-state">
<DownloadSpinner width={3} height={5} />
<span>Preparing PDF...</span>
</div>
)
}

if (hasError) {
return (
<div className="error-state">
<span className="text-red-500">Error generating PDF.</span>
</div>
)
if (instance.blob && fileName) {
setDownloading(true)
const link = document.createElement('a')
try {
link.href = instance.url!
link.download = makeFileNameSafe(fileName, ["pdf"])

document.body.appendChild(link)
link.click()

document.body.removeChild(link)
URL.revokeObjectURL(link.href)
} catch (error) {
console.error("Download failed:", error)
} finally {
URL.revokeObjectURL(link.href)
document.body.removeChild(link)
setDownloading(false)
}
}
}

return (
<button
className="button-style"
onClick={handleDownload}
disabled={downloading || pdfLoading || hasError}
>
{downloading ? <DownloadSpinner width={3} height={5} /> : "Print"}
return loading ? (
<DownloadSpinner width={3} height={5} />
) : (
<button className="button-style" onClick={handleDownload} disabled={loading || error || downloading || !instance.blob}>
Print
</button>
)
}

interface DownloadLinkProps {
pdfLoading: boolean;
href: string;
instance: UsePDFInstance;
fileName: string;
hasError?: boolean;
loading?: boolean;
error?: boolean;
}
8 changes: 5 additions & 3 deletions packages/cccv/src/lib/formatAsFilename.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
export default function formatFilename(input: string, defaultName: string): string {
import {randomString} from "@lib/randomString.ts"

const sanitized = input.replace(/[^a-z0-9\_\-\.]/gi, '_')
export default function formatFilename(input: string, defaultName?: string, extension?: string): string {

const sanitized = input.replace(/[^a-z0-9_\-.]/gi, '_')
const trimmed = sanitized.trim()

return trimmed.length > 0 ? trimmed : defaultName
return trimmed.length > 0 ? trimmed : defaultName || randomString(8) + (extension ? `.${extension}` : '')
}
10 changes: 10 additions & 0 deletions packages/cccv/src/lib/randomString.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const randomString = (length: number) => {
let result = ''
const characters =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
const charactersLength = characters.length
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength))
}
return result
}
4 changes: 2 additions & 2 deletions packages/cccv/src/lib/types/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Feature} from "geojson";
import {Feature} from "geojson"

interface Dictionary<T> {
[index: string]: T;
Expand Down Expand Up @@ -222,4 +222,4 @@ interface MissingDailyUsageHeatmapDataItem {

type DailyUsageHeatmapDataItem =
| PopulatedDailyUsageHeatmapDataItem
| EmptyDailyUsageHeatmapDataItem;
| EmptyDailyUsageHeatmapDataItem;
File renamed without changes.
33 changes: 32 additions & 1 deletion packages/cccv/src/lib/useMapFocus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ const useMapFocus = (
locationInFocus?: IMViewLocation | null
) => {
const [focusPin, setFocusPin] = useState<Marker | null>(null)
const [mapSnapshot, setMapSnapshot] = useState<string | null>(null)
const [creatingMapSnapshot, setCreatingMapSnapshot] = useState(false)

useEffect(() => {
placeFocusPin(locationInFocus)
zoomIntoFeatures(mapRef, locationInFocus)

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [mapRef, locationInFocus])

Expand All @@ -38,7 +41,35 @@ const useMapFocus = (
setFocusPin(pin)
}

return
const takeMapSnapshot = (mapRef: MutableRefObject<CombinedMapRef | null>): void => {
if (!mapRef?.current) {
console.warn("Map reference is null when attempting to take a snapshot.")
setMapSnapshot(null)
return
}

setCreatingMapSnapshot(true)

const map = mapRef.current.getMap()

map.once('idle', () => {
try {
const snapshot = `${map.getCanvas().toDataURL('image/png')}?timestamp=${Date.now()}`
setMapSnapshot(snapshot || null)
} catch (error) {
console.error("Error taking map snapshot:", error)
setMapSnapshot(null)
} finally {
setCreatingMapSnapshot(false)
}
})
}

return {
takeMapSnapshot,
mapSnapshot,
creatingMapSnapshot
}
}

export default useMapFocus
4 changes: 2 additions & 2 deletions packages/cccv/src/lib/useMapTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ const useMapTooltip = ({ mapRef, source, tooltipClassName }: UseMapTooltipProps)
}
}
}, [mapRef, source, debouncedMoveToolTip])

const Tooltip = () => {
if (!tooltipPosition || !tooltipContent) return null

Expand All @@ -117,7 +117,7 @@ const useMapTooltip = ({ mapRef, source, tooltipClassName }: UseMapTooltipProps)
position: 'absolute',
left: tooltipPosition.left,
top: tooltipPosition.top,
zIndex: 999999,
zIndex: 50,
backgroundColor: tooltipStyle['fill-color'] as string,
border: `1px solid ${tooltipStyle['fill-outline-color']}`,
opacity: tooltipStyle['fill-opacity'] as number,
Expand Down
4 changes: 2 additions & 2 deletions packages/cccv/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ const router = createBrowserRouter(routes as RouteObject[], {
})

const LoadingOverlayContainer: React.FC = () => {
const { loading } = useLoadingIndicator()
return <>{loading && <LoadingIndicatorOverlay />}</>
const { addressLoading } = useLoadingIndicator()
return <>{addressLoading && <LoadingIndicatorOverlay />}</>
}

export default LoadingOverlayContainer
Expand Down
Loading

0 comments on commit 04f37c2

Please sign in to comment.