1- import React from 'react' ;
2- import { Button , Container , Header } from 'semantic-ui-react' ;
1+ import './MyOpenCRE.scss' ;
2+
3+ import React , { useState } from 'react' ;
4+ import { Button , Container , Form , Header , Message } from 'semantic-ui-react' ;
35
46import { useEnvironment } from '../../hooks' ;
57
68export const MyOpenCRE = ( ) => {
79 const { apiUrl } = useEnvironment ( ) ;
8- // console.log('API URL:', apiUrl);
910
10- const downloadCreCsv = async ( ) => {
11- try {
12- const baseUrl = apiUrl || window . location . origin ;
11+ // Upload enabled only for local/dev
12+ const isUploadEnabled = apiUrl !== '/rest/v1' ;
1313
14- const backendUrl = baseUrl . includes ( 'localhost' ) ? 'http://127.0.0.1:5000' : baseUrl ;
14+ const [ selectedFile , setSelectedFile ] = useState < File | null > ( null ) ;
15+ const [ loading , setLoading ] = useState ( false ) ;
16+ const [ error , setError ] = useState < string | null > ( null ) ;
17+ const [ success , setSuccess ] = useState < any | null > ( null ) ;
1518
16- const response = await fetch ( `${ backendUrl } /cre_csv` , {
19+ /* ------------------ CSV DOWNLOAD ------------------ */
20+
21+ const downloadCreCsv = async ( ) => {
22+ try {
23+ const response = await fetch ( `${ apiUrl } /cre_csv` , {
1724 method : 'GET' ,
18- headers : {
19- Accept : 'text/csv' ,
20- } ,
25+ headers : { Accept : 'text/csv' } ,
2126 } ) ;
2227
2328 if ( ! response . ok ) {
@@ -41,6 +46,67 @@ export const MyOpenCRE = () => {
4146 }
4247 } ;
4348
49+ /* ------------------ FILE SELECTION ------------------ */
50+
51+ const onFileChange = ( e : React . ChangeEvent < HTMLInputElement > ) => {
52+ setError ( null ) ;
53+ setSuccess ( null ) ;
54+
55+ if ( ! e . target . files || e . target . files . length === 0 ) return ;
56+
57+ const file = e . target . files [ 0 ] ;
58+
59+ if ( ! file . name . toLowerCase ( ) . endsWith ( '.csv' ) ) {
60+ setError ( 'Please upload a valid CSV file.' ) ;
61+ e . target . value = '' ;
62+ setSelectedFile ( null ) ;
63+ return ;
64+ }
65+
66+ setSelectedFile ( file ) ;
67+ } ;
68+
69+ /* ------------------ CSV UPLOAD ------------------ */
70+
71+ const uploadCsv = async ( ) => {
72+ if ( ! selectedFile ) return ;
73+
74+ setLoading ( true ) ;
75+ setError ( null ) ;
76+ setSuccess ( null ) ;
77+
78+ const formData = new FormData ( ) ;
79+ formData . append ( 'cre_csv' , selectedFile ) ;
80+
81+ try {
82+ const response = await fetch ( `${ apiUrl } /cre_csv_import` , {
83+ method : 'POST' ,
84+ body : formData ,
85+ } ) ;
86+
87+ if ( response . status === 403 ) {
88+ throw new Error (
89+ 'CSV import is disabled on hosted environments. Run OpenCRE locally with CRE_ALLOW_IMPORT=true.'
90+ ) ;
91+ }
92+
93+ if ( ! response . ok ) {
94+ const text = await response . text ( ) ;
95+ throw new Error ( text || 'CSV import failed' ) ;
96+ }
97+
98+ const result = await response . json ( ) ;
99+ setSuccess ( result ) ;
100+ setSelectedFile ( null ) ;
101+ } catch ( err : any ) {
102+ setError ( err . message || 'Unexpected error during import' ) ;
103+ } finally {
104+ setLoading ( false ) ;
105+ }
106+ } ;
107+
108+ /* ------------------ UI ------------------ */
109+
44110 return (
45111 < Container style = { { marginTop : '3rem' } } >
46112 < Header as = "h1" > MyOpenCRE</ Header >
@@ -51,13 +117,56 @@ export const MyOpenCRE = () => {
51117 </ p >
52118
53119 < p >
54- Start by downloading the mapping template below, fill it with your standard’s controls, and map them
55- to CRE IDs .
120+ Start by downloading the CRE catalogue below, then map your standard’s controls or sections to CRE IDs
121+ in the spreadsheet .
56122 </ p >
57123
58- < Button primary onClick = { downloadCreCsv } >
59- Download CRE Catalogue (CSV)
60- </ Button >
124+ < div className = "myopencre-section" >
125+ < Button primary onClick = { downloadCreCsv } >
126+ Download CRE Catalogue (CSV)
127+ </ Button >
128+ </ div >
129+
130+ < div className = "myopencre-section myopencre-upload" >
131+ < Header as = "h3" > Upload Mapping CSV</ Header >
132+
133+ < p > Upload your completed mapping spreadsheet to import your standard into OpenCRE.</ p >
134+
135+ { ! isUploadEnabled && (
136+ < Message info className = "myopencre-disabled" >
137+ CSV upload is disabled on hosted environments due to resource constraints.
138+ < br />
139+ Please run OpenCRE locally to enable standard imports.
140+ </ Message >
141+ ) }
142+
143+ { error && < Message negative > { error } </ Message > }
144+
145+ { success && (
146+ < Message positive >
147+ < strong > Import successful</ strong >
148+ < ul >
149+ < li > New CREs added: { success . new_cres ?. length ?? 0 } </ li >
150+ < li > Standards imported: { success . new_standards } </ li >
151+ </ ul >
152+ </ Message >
153+ ) }
154+
155+ < Form >
156+ < Form . Field >
157+ < input type = "file" accept = ".csv" disabled = { ! isUploadEnabled || loading } onChange = { onFileChange } />
158+ </ Form . Field >
159+
160+ < Button
161+ primary
162+ loading = { loading }
163+ disabled = { ! isUploadEnabled || ! selectedFile || loading }
164+ onClick = { uploadCsv }
165+ >
166+ Upload CSV
167+ </ Button >
168+ </ Form >
169+ </ div >
61170 </ Container >
62171 ) ;
63172} ;
0 commit comments