Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added csv document rendering #2958

Merged
merged 3 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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' : ''}`}
chesterkmr marked this conversation as resolved.
Show resolved Hide resolved
className={ctw(`d-full`, imageClassName)}
{...restImage}
/>
chesterkmr marked this conversation as resolved.
Show resolved Hide resolved
)}
{!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;
});
};
chesterkmr marked this conversation as resolved.
Show resolved Hide resolved
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]);

chesterkmr marked this conversation as resolved.
Show resolved Hide resolved
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
Loading