@@ -7,7 +7,7 @@ import { DEVICE_PARAMS, calculateQPUSeconds } from '../utils/ResourceEstimatorMo
77
88const fontFamily = '-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif' ;
99
10- const ParameterInput = ( { label, value, onChange } ) => {
10+ const ParameterInput = ( { label, value, onChange, hint , error } ) => {
1111 const inputContainerStyle = {
1212 flex : '1 0 45%' ,
1313 marginBottom : '0.75rem' ,
@@ -25,12 +25,29 @@ const ParameterInput = ({ label, value, onChange }) => {
2525 fontFamily : fontFamily
2626 } ;
2727
28+ const hintStyle = {
29+ display : 'block' ,
30+ fontSize : '0.85rem' ,
31+ color : '#6B7280' ,
32+ marginTop : '0.25rem' ,
33+ fontFamily : fontFamily
34+ } ;
35+
36+ const errorStyle = {
37+ display : 'block' ,
38+ fontSize : '0.85rem' ,
39+ color : '#DC2626' ,
40+ marginTop : '0.25rem' ,
41+ fontFamily : fontFamily ,
42+ fontWeight : '500'
43+ } ;
44+
2845 const inputStyle = {
2946 display : 'block' ,
3047 width : '100%' ,
3148 padding : '0.5rem 0.7rem' ,
3249 backgroundColor : 'white' ,
33- border : '1px solid #D1D5DB' ,
50+ border : error ? '2px solid #DC2626' : '1px solid #D1D5DB' ,
3451 borderRadius : '0.35rem' ,
3552 boxSizing : 'border-box' ,
3653 color : '#000' ,
@@ -65,6 +82,7 @@ const ParameterInput = ({ label, value, onChange }) => {
6582 style = { inputStyle }
6683 placeholder = "Enter value"
6784 />
85+ { error ? < span style = { errorStyle } > { error } </ span > : hint && < span style = { hintStyle } > { hint } </ span > }
6886 </ div >
6987 ) ;
7088} ;
@@ -81,30 +99,69 @@ const ResourceEstimator = () => {
8199 const [ estimatedQPU , setEstimatedQPU ] = useState ( null ) ;
82100 const [ history , setHistory ] = useState ( [ ] ) ;
83101 const [ basket , setBasket ] = useState ( [ ] ) ;
102+ const [ validationErrors , setValidationErrors ] = useState ( { } ) ;
84103
85104 // Load history and basket from localStorage on component mount
86105 useEffect ( ( ) => {
87106 setHistory ( JobHistoryManager . loadHistory ( ) ) ;
88107 setBasket ( JobHistoryManager . loadBasket ( ) ) ;
89108 } , [ ] ) ;
90109
110+ // Re-validate qubits when device changes
111+ useEffect ( ( ) => {
112+ if ( formData . qubits ) {
113+ validateField ( 'qubits' , formData . qubits ) ;
114+ }
115+ } , [ selectedDevice ] ) ;
116+
117+ const validateField = ( field , value ) => {
118+ const numValue = parseInt ( value , 10 ) ;
119+ const newErrors = { ...validationErrors } ;
120+
121+ if ( field === 'qubits' ) {
122+ const deviceConfig = DEVICE_PARAMS [ selectedDevice ] ;
123+ if ( value === '' || value === undefined || value === null ) {
124+ newErrors . qubits = 'Qubits is required' ;
125+ } else if ( numValue < 1 ) {
126+ newErrors . qubits = 'Must be at least 1 qubit' ;
127+ } else if ( deviceConfig . max_qubits && numValue > deviceConfig . max_qubits ) {
128+ newErrors . qubits = `${ deviceConfig . name } supports max ${ deviceConfig . max_qubits } qubits` ;
129+ } else {
130+ delete newErrors . qubits ;
131+ }
132+ } else {
133+ // Validate other fields for positive values
134+ if ( value === '' || value === undefined || value === null ) {
135+ newErrors [ field ] = 'This field is required' ;
136+ } else if ( numValue < 1 ) {
137+ newErrors [ field ] = 'Must be a positive integer' ;
138+ } else {
139+ delete newErrors [ field ] ;
140+ }
141+ }
142+
143+ setValidationErrors ( newErrors ) ;
144+ } ;
145+
91146 const handleInputChange = ( field , value ) => {
92147 console . log ( `Updating ${ field } to ${ value } ` ) ;
93148 setFormData ( prevState => ( {
94149 ...prevState ,
95150 [ field ] : value
96151 } ) ) ;
152+ validateField ( field , value ) ;
97153 } ;
98154
99155 const calculateQPU = ( ) => {
100- // Validate all inputs are present before calculating
156+ // Validate all fields first
101157 const requiredFields = [ 'batches' , 'depth' , 'shots' , 'qubits' ] ;
102- const missingFields = requiredFields . filter ( field =>
103- formData [ field ] === '' || formData [ field ] === undefined || formData [ field ] === null
104- ) ;
158+ requiredFields . forEach ( field => validateField ( field , formData [ field ] ) ) ;
159+
160+ // Check if there are any validation errors
161+ const hasErrors = Object . keys ( validationErrors ) . length > 0 ||
162+ requiredFields . some ( field => formData [ field ] === '' || formData [ field ] === undefined || formData [ field ] === null ) ;
105163
106- if ( missingFields . length > 0 ) {
107- alert ( 'Please fill in all parameter fields before calculating.' ) ;
164+ if ( hasErrors ) {
108165 return ;
109166 }
110167
@@ -114,7 +171,7 @@ const ResourceEstimator = () => {
114171 depth : parseInt ( formData . depth , 10 ) ,
115172 shots : parseInt ( formData . shots , 10 ) ,
116173 qubits : parseInt ( formData . qubits , 10 )
117- } ;
174+ }
118175
119176 // Calculate QPU seconds using our model
120177 const qpuSeconds = calculateQPUSeconds ( selectedDevice , numericFormData ) ;
@@ -273,8 +330,10 @@ const ResourceEstimator = () => {
273330 < ParameterInput
274331 label = "Number of Qubits"
275332 value = { formData . qubits }
276- onChange = { ( value ) => handleInputChange ( 'qubits' , value ) }
277- />
333+ onChange = { ( value ) => handleInputChange ( 'qubits' , value ) }
334+ hint = { `Max: ${ DEVICE_PARAMS [ selectedDevice ] . max_qubits } qubits` }
335+ error = { validationErrors . qubits }
336+ />
278337 </ div >
279338
280339 < div style = { { marginTop : '1rem' , textAlign : 'center' } } >
0 commit comments