Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { PREVIEWABLE_EXTENSIONS } from '@/activities/files/const/previewable-extensions.const';
import { downloadFile } from '@/activities/files/utils/downloadFile';
import { fetchCsvPreview } from '@/activities/files/utils/fetchCsvPreview';
import {
type CsvPreviewData,
fetchCsvPreview,
} from '@/activities/files/utils/fetchCsvPreview';
import { getFileType } from '@/activities/files/utils/getFileType';
import DocViewer, { DocViewerRenderers } from '@cyntler/react-doc-viewer';
import '@cyntler/react-doc-viewer/dist/index.css';
Expand Down Expand Up @@ -79,6 +82,18 @@ const StyledTitle = styled.div`
font-weight: ${({ theme }) => theme.font.weight.semiBold};
`;

const StyledCsvTable = styled.table`
width: 100%;
text-align: left;
border-collapse: collapse;
color: ${({ theme }) => theme.font.color.primary};

th,
td {
padding: 5px 10px;
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CSV table cells render raw text without any sanitization or escaping. While React automatically escapes text content to prevent XSS, consider edge cases where cell content might contain very long strings without spaces, which could break the table layout. Adding word-break or overflow-wrap styles to table cells would prevent layout issues.

Suggested change
padding: 5px 10px;
padding: 5px 10px;
word-break: break-word;
overflow-wrap: anywhere;

Copilot uses AI. Check for mistakes.
}
`;
Comment on lines +85 to +95
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The custom CSV table lacks overflow handling. The StyledDocumentViewerContainer has overflow:auto for other renderers (line 52), but StyledCsvTable doesn't inherit this behavior. For CSVs with many columns or wide content, the table may overflow the container without scrollbars. Consider wrapping the table in a scrollable container or adding overflow styles to handle wide tables.

Copilot uses AI. Check for mistakes.

type DocumentViewerProps = {
documentName: string;
documentUrl: string;
Expand Down Expand Up @@ -165,7 +180,9 @@ export const DocumentViewer = ({
}: DocumentViewerProps) => {
const { t } = useLingui();
const theme = useTheme();
const [csvPreview, setCsvPreview] = useState<string | undefined>(undefined);
const [csvPreview, setCsvPreview] = useState<CsvPreviewData | undefined>(
undefined,
);

const { extension } = getFileNameAndExtension(documentName);
const fileExtension = isDefined(documentExtension)
Expand All @@ -175,15 +192,11 @@ export const DocumentViewer = ({
const isPreviewable = PREVIEWABLE_EXTENSIONS.includes(fileExtension);
const isMsOfficeFile = MS_OFFICE_EXTENSIONS.includes(fileExtension);

const mimeType = PREVIEWABLE_EXTENSIONS.includes(fileExtension)
? MIME_TYPE_MAPPING[fileExtension]
: undefined;
const mimeType = isPreviewable ? MIME_TYPE_MAPPING[fileExtension] : undefined;

useEffect(() => {
if (fileExtension === 'csv') {
fetchCsvPreview(documentUrl).then((content) => {
setCsvPreview(content);
});
fetchCsvPreview(documentUrl).then(setCsvPreview);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The promise returned by fetchCsvPreview lacks a .catch() handler. A failed network request will cause the component to show a loading state indefinitely.
Severity: MEDIUM

Suggested Fix

Add a .catch() block to the promise chain after the call to fetchCsvPreview. Inside the handler, log the error and update the component's state to display an error message to the user instead of the loading indicator.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location:
packages/twenty-front/src/modules/activities/files/components/DocumentViewer.tsx#L199

Potential issue: The `useEffect` hook that calls `fetchCsvPreview` for CSV files does
not handle promise rejections. If the underlying `fetch` call fails due to a network
error, CORS issue, or an invalid URL, the promise will reject. Because there is no
`.catch()` block, the `csvPreview` state will never be updated, and the component will
remain indefinitely in a "Loading csv ..." state, with no error message or recovery path
for the user.

Did we get this right? 👍 / 👎 to inform future reviews.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: CSV preview state isn’t reset on documentUrl change, so switching between CSVs can show stale table data until the new fetch resolves or fails.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/twenty-front/src/modules/activities/files/components/DocumentViewer.tsx, line 199:

<comment>CSV preview state isn’t reset on documentUrl change, so switching between CSVs can show stale table data until the new fetch resolves or fails.</comment>

<file context>
@@ -175,15 +192,11 @@ export const DocumentViewer = ({
-      fetchCsvPreview(documentUrl).then((content) => {
-        setCsvPreview(content);
-      });
+      fetchCsvPreview(documentUrl).then(setCsvPreview);
     }
   }, [documentUrl, fileExtension]);
</file context>
Suggested change
fetchCsvPreview(documentUrl).then(setCsvPreview);
setCsvPreview(undefined);
fetchCsvPreview(documentUrl).then(setCsvPreview);
Fix with Cubic

}
}, [documentUrl, fileExtension]);
Comment on lines 197 to 201
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CSV preview stuck loading

fetchCsvPreview(documentUrl).then(setCsvPreview) has no cancellation or error handling. If the fetch/parse rejects (network/CORS/non-2xx causing response.text() to throw, etc.), the component will stay on “Loading csv ...” and you’ll get an unhandled rejection. It can also race when documentUrl changes quickly, setting CSV state for the previous URL after the new one. Consider aborting/guarding the effect and handling errors by rendering the existing “Preview Not Available / Download” UI for CSV.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/twenty-front/src/modules/activities/files/components/DocumentViewer.tsx
Line: 197:201

Comment:
**CSV preview stuck loading**

`fetchCsvPreview(documentUrl).then(setCsvPreview)` has no cancellation or error handling. If the fetch/parse rejects (network/CORS/non-2xx causing `response.text()` to throw, etc.), the component will stay on “Loading csv ...” and you’ll get an unhandled rejection. It can also race when `documentUrl` changes quickly, setting CSV state for the previous URL after the new one. Consider aborting/guarding the effect and handling errors by rendering the existing “Preview Not Available / Download” UI for CSV.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 197 to 201
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The useEffect hook that calls fetchCsvPreview does not handle errors. If the fetch or parsing fails, the csvPreview state will remain undefined, and users will see the "Loading csv ..." message indefinitely. Consider adding error handling with try-catch and displaying an appropriate error message to users when CSV loading fails.

Copilot uses AI. Check for mistakes.

Expand Down Expand Up @@ -218,12 +231,37 @@ export const DocumentViewer = ({
);
}

if (fileExtension === 'csv' && !isDefined(csvPreview))
if (fileExtension === 'csv') {
if (!isDefined(csvPreview)) {
return (
<StyledDocumentViewerContainer>
<Trans>Loading csv ... </Trans>
</StyledDocumentViewerContainer>
);
}
return (
<StyledDocumentViewerContainer>
<Trans>Loading csv ... </Trans>
<StyledCsvTable>
<thead>
<tr>
{csvPreview.headers.map((header, columnIndex) => (
<th key={columnIndex}>{header}</th>
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The table headers and cells lack proper ARIA attributes or semantic attributes that could enhance accessibility. While the basic table structure is semantically correct, consider whether headers should have scope attributes (scope="col") to explicitly indicate they're column headers, especially for screen reader users navigating complex CSV data.

Suggested change
<th key={columnIndex}>{header}</th>
<th key={columnIndex} scope="col">
{header}
</th>

Copilot uses AI. Check for mistakes.
))}
</tr>
</thead>
<tbody>
{csvPreview.rows.map((row, rowIndex) => (
<tr key={rowIndex}>
{row.map((cell, cellIndex) => (
<td key={cellIndex}>{cell}</td>
))}
</tr>
))}
</tbody>
</StyledCsvTable>
</StyledDocumentViewerContainer>
);
}

if (isMsOfficeFile && isPrivateUrl(documentUrl)) {
return (
Expand Down Expand Up @@ -251,12 +289,7 @@ export const DocumentViewer = ({
<DocViewer
documents={[
{
uri:
fileExtension === 'csv' && isDefined(csvPreview)
? window.URL.createObjectURL(
new Blob([csvPreview], { type: 'text/csv' }),
)
: documentUrl,
uri: documentUrl,
fileName: documentName,
fileType: mimeType,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@ import Papa from 'papaparse';

const DEFAULT_PREVIEW_ROWS = 50;

export const fetchCsvPreview = async (url: string): Promise<string> => {
export type CsvPreviewData = {
headers: string[];
rows: string[][];
};

export const fetchCsvPreview = async (url: string): Promise<CsvPreviewData> => {
const response = await fetch(url);
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fetch call lacks error handling for network failures or non-2xx HTTP status codes. If the fetch fails or returns an error status, the function will throw an unhandled error. Consider adding error handling similar to other fetch calls in the codebase, which check response.ok and throw appropriate errors with status information.

Suggested change
const response = await fetch(url);
let response: Response;
try {
response = await fetch(url);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw new Error(`Failed to fetch CSV preview from "${url}": ${message}`);
}
if (!response.ok) {
throw new Error(
`Failed to fetch CSV preview from "${url}": ${response.status} ${response.statusText}`,
);
}

Copilot uses AI. Check for mistakes.
const text = await response.text();
Comment on lines +10 to 12
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-OK responses parsed

fetchCsvPreview parses await response.text() without checking response.ok, so a 403/404 HTML body (or any non-CSV content) will still be parsed and rendered as a “CSV” table. PapaParse also exposes result.errors which is currently ignored, so malformed CSV can silently produce surprising headers/rows. This should be treated as a fetch/parse failure so the caller can show the normal “preview unavailable” path instead of rendering garbage.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/twenty-front/src/modules/activities/files/utils/fetchCsvPreview.ts
Line: 10:12

Comment:
**Non-OK responses parsed**

`fetchCsvPreview` parses `await response.text()` without checking `response.ok`, so a 403/404 HTML body (or any non-CSV content) will still be parsed and rendered as a “CSV” table. PapaParse also exposes `result.errors` which is currently ignored, so malformed CSV can silently produce surprising `headers/rows`. This should be treated as a fetch/parse failure so the caller can show the normal “preview unavailable” path instead of rendering garbage.

How can I resolve this? If you propose a fix, please make it concise.


const result = Papa.parse(text, {
preview: DEFAULT_PREVIEW_ROWS,
const result = Papa.parse<string[]>(text, {
preview: DEFAULT_PREVIEW_ROWS + 1, // +1 for header row
skipEmptyLines: true,
header: true,
header: false,
});
Comment on lines +14 to 18
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Papa.parse can encounter errors during CSV parsing (e.g., malformed CSV files). The result object has an 'errors' array that should be checked. Consider validating result.errors and handling parse errors gracefully to prevent displaying malformed data or providing unclear error messages to users.

Copilot uses AI. Check for mistakes.

const data = result.data as Record<string, string>[];

const csvContent = Papa.unparse(data, {
header: true,
});
const [headers = [], ...rows] = result.data;
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the CSV file is empty or contains only a header row with no data rows, the destructuring will work but rows will be an empty array. While this is technically correct, consider whether an empty CSV should show an empty table or a specific message to users indicating there's no data to preview.

Copilot uses AI. Check for mistakes.

return csvContent;
return { headers, rows };
};
Comment on lines +10 to 23
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fetchCsvPreview function lacks test coverage. Other utility functions in this directory (downloadFile, getFileType) have corresponding test files in tests/. Consider adding tests for this function to verify correct CSV parsing, header extraction, and edge cases like empty CSVs, CSVs with only headers, or malformed data.

Copilot uses AI. Check for mistakes.
Loading