Skip to content

Commit

Permalink
feat: added csv document rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
chesterkmr committed Jan 14, 2025
1 parent 19fb361 commit 3598462
Show file tree
Hide file tree
Showing 14 changed files with 254 additions and 36 deletions.
4 changes: 4 additions & 0 deletions apps/backoffice-v2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,16 @@
"i18next": "^22.4.9",
"i18next-browser-languagedetector": "^7.0.1",
"i18next-http-backend": "^2.1.1",
"jspdf": "^2.5.2",
"jspdf-autotable": "^3.8.4",
"leaflet": "^1.9.4",
"libphonenumber-js": "^1.10.49",
"lodash-es": "^4.17.21",
"lowlight": "^3.1.0",
"lucide-react": "0.445.0",
"match-sorter": "^6.3.1",
"msw": "^1.0.0",
"papaparse": "^5.5.1",
"posthog-js": "^1.154.2",
"qs": "^6.11.2",
"react": "^18.2.0",
Expand Down Expand Up @@ -172,6 +175,7 @@
"@types/leaflet": "^1.9.3",
"@types/lodash-es": "^4.17.12",
"@types/node": "^18.11.13",
"@types/papaparse": "^5.3.15",
"@types/qs": "^6.9.7",
"@types/react": "^18.0.14",
"@types/react-dom": "^18.0.5",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { FunctionComponentWithChildren } from '@/common/types';
import { ctw } from '@/common/utils/ctw/ctw';
import { isCsv } from '@/common/utils/is-csv/is-csv';
import { isPdf } from '@/common/utils/is-pdf/is-pdf';
import { ComponentProps } from 'react';
import ReactCrop, { Crop } from 'react-image-crop';
Expand Down Expand Up @@ -30,7 +31,7 @@ export const ImageEditor: FunctionComponentWithChildren<IImageEditorProps> = ({
<TransformComponent
wrapperClass={`d-full max-w-[600px] max-h-[600px] h-full`}
contentClass={ctw({
'hover:cursor-move': !isPdf(image),
'hover:cursor-move': !isPdf(image) && !isCsv(image),
})}
wrapperStyle={{
width: '100%',
Expand All @@ -41,15 +42,15 @@ export const ImageEditor: FunctionComponentWithChildren<IImageEditorProps> = ({
contentStyle={{
width: '100%',
height: '100%',
display: !isPdf(image) ? 'block' : 'flex',
display: !isPdf(image) && !isCsv(image) ? 'block' : 'flex',
}}
>
<ReactCrop
crop={crop}
onChange={onCrop}
disabled={!isCropping || isPdf(image) || isRotatedOrTransformed}
disabled={!isCropping || isPdf(image) || isCsv(image) || isRotatedOrTransformed}
className={ctw('h-full w-full overflow-hidden [&>div]:!w-full', {
'flex flex-row [&>div]:min-h-[600px]': isPdf(image),
'flex flex-row [&>div]:min-h-[600px]': isPdf(image) || isCsv(image),
})}
>
<div
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isCsv } from '@/common/utils/is-csv/is-csv';
import { forwardRef, useCallback, useEffect, useState } from 'react';
import { ctw } from '../../../utils/ctw/ctw';
import { isPdf } from '../../../utils/is-pdf/is-pdf';
Expand Down Expand Up @@ -31,10 +32,10 @@ export const SelectedImage = forwardRef<HTMLImageElement | HTMLIFrameElement, TS
setIsError(false);
}, [isError, selectedImage?.imageUrl]);

if (isPdf(selectedImage)) {
if (isPdf(selectedImage) || isCsv(selectedImage)) {
return (
<iframe
src={selectedImage?.imageUrl + '#toolbar=0&navpanes=0'}
src={`${selectedImage?.imageUrl}#toolbar=0&navpanes=0`}
ref={ref}
className={ctw(className, `d-full mx-auto`, {
'h-[600px] w-[600px]': isPlaceholder,
Expand All @@ -60,3 +61,5 @@ export const SelectedImage = forwardRef<HTMLImageElement | HTMLIFrameElement, TS
);
},
);

SelectedImage.displayName = 'SelectedImage';
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { isCsv } from '@/common/utils/is-csv/is-csv';
import { FunctionComponent } from 'react';
import { useImageViewerContext } from './hooks/useImageViewerContext/useImageViewerContext';
import { IZoomModalProps } from './interfaces';
import { Modal } from '../Modal/Modal';
import { BallerineImage } from '../../atoms/BallerineImage';
import { ctw } from '../../../utils/ctw/ctw';
import { isPdf } from '../../../utils/is-pdf/is-pdf';
import { BallerineImage } from '../../atoms/BallerineImage';
import { Modal } from '../Modal/Modal';
import { useImageViewerContext } from './hooks/useImageViewerContext/useImageViewerContext';
import { IZoomModalProps } from './interfaces';

/**
* @description To be used by {@link ImageViewer}. Uses the {@link Modal} component with default styling to display an enlarged version of the selected image.
Expand Down Expand Up @@ -33,14 +34,14 @@ export const ZoomModal: FunctionComponent<IZoomModalProps> = ({
hideTitle
{...rest}
>
{isPdf(selectedImage) && (
{(isPdf(selectedImage) || isCsv(selectedImage)) && (
<iframe
src={selectedImage?.imageUrl}
src={`${selectedImage?.imageUrl}${isCsv(selectedImage) ? '#toolbar=0&navpanes=0' : ''}`}
className={ctw(`d-full`, imageClassName)}
{...restImage}
/>
)}
{!isPdf(selectedImage) && (
{!isPdf(selectedImage) && !isCsv(selectedImage) && (
<BallerineImage
withPlaceholder
src={selectedImage?.imageUrl}
Expand Down
2 changes: 0 additions & 2 deletions apps/backoffice-v2/src/common/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
export const DOWNLOAD_ONLY_MIME_TYPES = [
'application/csv',
'text/csv',
// xls
'application/vnd.ms-excel',
// xlsx
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { jsPDF } from 'jspdf';
import 'jspdf-autotable';
import Papa from 'papaparse';

interface jsPDFWithPlugin extends jsPDF {
autoTable: any;
}

export const convertCsvToPdfBase64String = (csvBase64: string) => {
// Extract base64 data from data URI
const base64Data = csvBase64.split(',')[1] || csvBase64;

// Decode base64 to string
const csvString = atob(base64Data);

// Parse CSV string to array using PapaParse
const { data } = Papa.parse(csvString, {
header: true,
skipEmptyLines: true,
});

// Create new PDF document
const doc = new jsPDF() as jsPDFWithPlugin;

// Add table to PDF using autoTable
doc.autoTable({
head: [Object.keys(data[0] as object)], // Column headers
body: data.map(row => Object.values(row as object)), // Row data
startY: 10,
margin: { top: 10 },
styles: { fontSize: 8 },
headStyles: { fillColor: [66, 66, 66] },
});

return doc.output('datauristring');
};
2 changes: 2 additions & 0 deletions apps/backoffice-v2/src/common/utils/is-csv/is-csv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const isCsv = <T extends { fileType: string }>(document: T) =>
document?.fileType === 'text/csv' || document?.fileType === 'application/csv';
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { FunctionComponent } from 'react';
import { ImageOCR } from '@/common/components/molecules/ImageOCR/ImageOCR';
import { ImageViewer } from '@/common/components/organisms/ImageViewer/ImageViewer';
import { ctw } from '@/common/utils/ctw/ctw';
import { isCsv } from '@/common/utils/is-csv/is-csv';
import { isPdf } from '@/common/utils/is-pdf/is-pdf';
import { useDocumentsToolbarLogic } from '@/pages/Entity/components/Case/hooks/useDocumentsToolbarLogic/useDocumentsToolbarLogic';

Expand Down Expand Up @@ -55,7 +56,7 @@ export const DocumentsToolbar: FunctionComponent<{
<ExternalLinkIcon className={`p-0.5`} />
</button>
)}
{!isPdf(image) && !isLoading && (
{!isPdf(image) && !isCsv(image) && !isLoading && (
<>
<button
type={`button`}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { DownloadFile } from '@/common/components/molecules/DownloadFile/Downloa
import { ImageEditor } from '@/common/components/molecules/ImageEditor/ImageEditor';
import { ImageViewer } from '@/common/components/organisms/ImageViewer/ImageViewer';
import { ctw } from '@/common/utils/ctw/ctw';
import { isCsv } from '@/common/utils/is-csv/is-csv';
import { keyFactory } from '@/common/utils/key-factory/key-factory';
import { DocumentsToolbar } from '@/pages/Entity/components/Case/Case.Documents.Toolbar';
import { useDocumentsLogic } from './hooks/useDocuments/useDocumentsLogic';
Expand Down Expand Up @@ -100,17 +101,21 @@ export const Documents: FunctionComponent<IDocumentsProps> = ({
? skeletons.map(index => (
<ImageViewer.SkeletonItem key={`image-viewer-skeleton-${index}`} />
))
: documents?.map(({ imageUrl, title, fileType, fileName, id }) => (
<ImageViewer.Item
id={id}
key={keyFactory(id, title, fileName, fileType, imageUrl)}
src={imageUrl}
fileType={fileType}
fileName={fileName}
alt={title}
caption={title}
/>
))}
: documents?.map(document => {
const { imageUrl, title, fileType, fileName, id } = document;

return !isCsv(document) ? (
<ImageViewer.Item
id={id}
key={keyFactory(id, title, fileName, fileType, imageUrl)}
src={imageUrl}
fileType={fileType}
fileName={fileName}
alt={title}
caption={title}
/>
) : null;
})}
</ImageViewer.List>
<ImageViewer.ZoomModal />
</ImageViewer>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { isCsv } from '@/common/utils/is-csv/is-csv';
import { convertCsvToPdfBase64String } from '../../../../../../common/utils/convert-csv-to-pdf-base64-string/convert-csv-to-pdf-base64-string';
import { IDocumentsProps } from '../../interfaces';

export const convertCsvDocumentsToPdf = (documents: IDocumentsProps['documents']) => {
return documents.map(document => {
if (isCsv(document)) {
return { ...document, imageUrl: convertCsvToPdfBase64String(document.imageUrl) };
}

return document;
});
};
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { ComponentProps, useCallback, useRef, useState } from 'react';
import { ComponentProps, useCallback, useMemo, useRef, useState } from 'react';

import { IDocumentsProps } from '../../interfaces';
import { TransformWrapper } from 'react-zoom-pan-pinch';
import { useCrop } from '@/common/hooks/useCrop/useCrop';
import { DOWNLOAD_ONLY_MIME_TYPES } from '@/common/constants';
import { useCrop } from '@/common/hooks/useCrop/useCrop';
import { useFilterId } from '@/common/hooks/useFilterId/useFilterId';
import { useTesseract } from '@/common/hooks/useTesseract/useTesseract';
import { createArrayOfNumbers } from '@/common/utils/create-array-of-numbers/create-array-of-numbers';
import { useStorageFileByIdQuery } from '@/domains/storage/hooks/queries/useStorageFileByIdQuery/useStorageFileByIdQuery';
import { useCustomerQuery } from '@/domains/customer/hooks/queries/useCustomerQuery/useCustomerQuery';
import { useStorageFileByIdQuery } from '@/domains/storage/hooks/queries/useStorageFileByIdQuery/useStorageFileByIdQuery';
import { TransformWrapper } from 'react-zoom-pan-pinch';
import { IDocumentsProps } from '../../interfaces';
import { convertCsvDocumentsToPdf } from './helpers';

export const useDocumentsLogic = (_initialDocuments: IDocumentsProps['documents']) => {
const documents = useMemo(() => convertCsvDocumentsToPdf(_initialDocuments), [_initialDocuments]);
const initialImage = useMemo(() => documents?.[0], [documents]);

export const useDocumentsLogic = (documents: IDocumentsProps['documents']) => {
const initialImage = documents?.[0];
const { data: customer } = useCustomerQuery();
const { crop, isCropping, onCrop, onCancelCrop } = useCrop();
const selectedImageRef = useRef<HTMLImageElement>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';

export interface DocumentFieldParams {
documentData: Partial<Document>;
acceptFileFormats?: string;
}

export const DocumentField = (
Expand Down Expand Up @@ -243,6 +244,7 @@ export const DocumentField = (
onBlur={onBlur as () => void}
testId={definition.name}
onChange={handleChange}
acceptFileFormats={definition.options.acceptFileFormats}
/>
{!!warnings.length && <ErrorsList errors={warnings.map(err => err.message)} />}
{isTouched && !!validationErrors.length && (
Expand Down
Loading

0 comments on commit 3598462

Please sign in to comment.