Skip to content

Commit 865e5d5

Browse files
feat(myopencre): surface CSV import errors in UI
1 parent 53b85b0 commit 865e5d5

File tree

1 file changed

+54
-9
lines changed

1 file changed

+54
-9
lines changed

application/frontend/src/pages/MyOpenCRE/MyOpenCRE.tsx

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,28 @@ import { Button, Container, Form, Header, Message } from 'semantic-ui-react';
55

66
import { useEnvironment } from '../../hooks';
77

8+
type RowValidationError = {
9+
row: number;
10+
code: string;
11+
message: string;
12+
column?: string;
13+
};
14+
15+
type ImportErrorResponse = {
16+
success: false;
17+
type: string;
18+
message?: string;
19+
errors?: RowValidationError[];
20+
};
21+
822
export const MyOpenCRE = () => {
923
const { apiUrl } = useEnvironment();
1024

11-
// Upload enabled only for local/dev
1225
const isUploadEnabled = apiUrl !== '/rest/v1';
1326

1427
const [selectedFile, setSelectedFile] = useState<File | null>(null);
1528
const [loading, setLoading] = useState(false);
16-
const [error, setError] = useState<string | null>(null);
29+
const [error, setError] = useState<ImportErrorResponse | null>(null);
1730
const [success, setSuccess] = useState<any | null>(null);
1831

1932
/* ------------------ CSV DOWNLOAD ------------------ */
@@ -57,7 +70,11 @@ export const MyOpenCRE = () => {
5770
const file = e.target.files[0];
5871

5972
if (!file.name.toLowerCase().endsWith('.csv')) {
60-
setError('Please upload a valid CSV file.');
73+
setError({
74+
success: false,
75+
type: 'FILE_ERROR',
76+
message: 'Please upload a valid CSV file.',
77+
});
6178
e.target.value = '';
6279
setSelectedFile(null);
6380
return;
@@ -90,21 +107,49 @@ export const MyOpenCRE = () => {
90107
);
91108
}
92109

110+
const payload = await response.json();
111+
93112
if (!response.ok) {
94-
const text = await response.text();
95-
throw new Error(text || 'CSV import failed');
113+
setError(payload);
114+
return;
96115
}
97116

98-
const result = await response.json();
99-
setSuccess(result);
117+
setSuccess(payload);
100118
setSelectedFile(null);
101119
} catch (err: any) {
102-
setError(err.message || 'Unexpected error during import');
120+
setError({
121+
success: false,
122+
type: 'CLIENT_ERROR',
123+
message: err.message || 'Unexpected error during import',
124+
});
103125
} finally {
104126
setLoading(false);
105127
}
106128
};
107129

130+
/* ------------------ ERROR RENDERING ------------------ */
131+
132+
const renderErrorMessage = () => {
133+
if (!error) return null;
134+
135+
if (error.errors && error.errors.length > 0) {
136+
return (
137+
<Message negative>
138+
<strong>Import failed due to validation errors</strong>
139+
<ul>
140+
{error.errors.map((e, idx) => (
141+
<li key={idx}>
142+
<strong>Row {e.row}:</strong> {e.message}
143+
</li>
144+
))}
145+
</ul>
146+
</Message>
147+
);
148+
}
149+
150+
return <Message negative>{error.message || 'Import failed'}</Message>;
151+
};
152+
108153
/* ------------------ UI ------------------ */
109154

110155
return (
@@ -140,7 +185,7 @@ export const MyOpenCRE = () => {
140185
</Message>
141186
)}
142187

143-
{error && <Message negative>{error}</Message>}
188+
{renderErrorMessage()}
144189

145190
{success && (
146191
<Message positive>

0 commit comments

Comments
 (0)