tee3apps-cms-sdk-react 0.0.9 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tee3apps-cms-sdk-react",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
4
4
  "description": "Uses JSON to dynamically generate and render UI pages in a website",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -9,6 +9,7 @@ import InputField from './InputField';
9
9
  import RadioField from './RadioField';
10
10
  import SelectField from './SelectField';
11
11
  import ImageComponent from './ImageComponent';
12
+ import TermsAndCondition from './TermsAndCondition';
12
13
 
13
14
  interface BoxRendererProps {
14
15
  box: Box;
@@ -19,6 +20,9 @@ interface BoxRendererProps {
19
20
  validationErrors?: Record<string, boolean>;
20
21
  onFieldChange?: (code: string, value: any) => void;
21
22
  onFormSubmit?: () => void;
23
+ onFormReset?: () => void;
24
+ onFormCancel?: () => void;
25
+ isSubmitting?: boolean;
22
26
  }
23
27
 
24
28
  const BoxRenderer: React.FC<BoxRendererProps> = ({
@@ -29,7 +33,10 @@ const BoxRenderer: React.FC<BoxRendererProps> = ({
29
33
  formValues = {},
30
34
  validationErrors = {},
31
35
  onFieldChange,
32
- onFormSubmit
36
+ onFormSubmit,
37
+ onFormReset,
38
+ onFormCancel,
39
+ isSubmitting = false
33
40
  }) => {
34
41
  // Get colspan based on device mode
35
42
  const getColspan = () => {
@@ -211,8 +218,35 @@ const BoxRenderer: React.FC<BoxRendererProps> = ({
211
218
  );
212
219
  } else if (component.name === 'TextComponent') {
213
220
  return <TextComponent key={index} props={component.props} />;
221
+ } else if (component.name === 'TermsAndCondition' || component.name === 'TERMSANDCONDITION') {
222
+ const code = getFieldCode(component);
223
+ const hasError = validationErrors[code];
224
+ return (
225
+ <div key={index} style={{ width: '100%' }}>
226
+ <TermsAndCondition
227
+ props={component.props}
228
+ value={formValues[code] ?? component.props.checked ?? false}
229
+ onChange={onFieldChange}
230
+ />
231
+ {hasError && (component.props.isRequired || component.props.required) && (
232
+ <div style={{ color: 'red', fontSize: '12px', marginTop: '4px' }}>
233
+ This field is required
234
+ </div>
235
+ )}
236
+ </div>
237
+ );
214
238
  } else if (component.name === 'ButtonField') {
215
- return <Button key={index} props={component.props} onFormSubmit={onFormSubmit} />;
239
+ return (
240
+ <Button
241
+ key={index}
242
+ props={component.props}
243
+ onFormSubmit={onFormSubmit}
244
+ onFormReset={onFormReset}
245
+ onFormCancel={onFormCancel}
246
+ isSubmitting={isSubmitting}
247
+ deviceMode={deviceMode}
248
+ />
249
+ );
216
250
  } else {
217
251
  return (
218
252
  <div key={index} className="mb-4 p-4 bg-gray-100 rounded-md border-2 border-dashed border-gray-300">
@@ -20,13 +20,43 @@ interface ButtonModeProps {
20
20
  interface ButtonProps {
21
21
  props: ComponentProps;
22
22
  onFormSubmit?: () => void;
23
+ onFormReset?: () => void;
24
+ onFormCancel?: () => void;
25
+ isSubmitting?: boolean;
26
+ deviceMode?: string;
23
27
  }
24
28
 
25
- const Button: React.FC<ButtonProps> = ({ props, onFormSubmit }) => {
29
+ const Button: React.FC<ButtonProps> = ({
30
+ props,
31
+ onFormSubmit,
32
+ onFormReset,
33
+ onFormCancel,
34
+ isSubmitting = false,
35
+ deviceMode = 'web'
36
+ }) => {
26
37
  // Get device-specific mode properties with proper typing
27
38
  const getCurrentMode = (): ButtonModeProps => {
39
+ let modeProps: ButtonModeProps | undefined;
40
+
41
+ // Get mode based on deviceMode
42
+ switch (deviceMode) {
43
+ case 'mobileweb':
44
+ modeProps = props.mode?.mobileweb as ButtonModeProps;
45
+ break;
46
+ case 'mobileapp':
47
+ modeProps = props.mode?.mobileapp as ButtonModeProps;
48
+ break;
49
+ case 'tablet':
50
+ modeProps = props.mode?.tablet as ButtonModeProps;
51
+ break;
52
+ case 'web':
53
+ default:
54
+ modeProps = props.mode?.web as ButtonModeProps;
55
+ break;
56
+ }
57
+
28
58
  // Default to web mode if not specified
29
- return (props.mode?.web as ButtonModeProps) || {
59
+ return modeProps || {
30
60
  radius: '4px',
31
61
  bgColor: '#3498db',
32
62
  textstyle: {
@@ -54,28 +84,57 @@ const Button: React.FC<ButtonProps> = ({ props, onFormSubmit }) => {
54
84
  }
55
85
  };
56
86
 
57
- // Handle button click
87
+ // Handle button click based on button type
58
88
  const handleClick = () => {
59
- // You can add button click logic here
60
- console.log('Button clicked:', props.name?.all || 'Button');
89
+ const buttonType = props.buttonType || 'submit';
61
90
 
62
- // Trigger form submission if handler is provided
63
- if (onFormSubmit) {
64
- onFormSubmit();
91
+ // Handle different button types
92
+ switch (buttonType) {
93
+ case 'submit':
94
+ // Trigger form submission if handler is provided
95
+ if (onFormSubmit) {
96
+ onFormSubmit();
97
+ }
98
+ break;
99
+ case 'reset':
100
+ // Trigger form reset if handler is provided
101
+ if (onFormReset) {
102
+ onFormReset();
103
+ }
104
+ break;
105
+ case 'cancel':
106
+ // Trigger form cancel if handler is provided
107
+ if (onFormCancel) {
108
+ onFormCancel();
109
+ }
110
+ break;
111
+ default:
112
+ // Default to submit behavior
113
+ if (onFormSubmit) {
114
+ onFormSubmit();
115
+ }
116
+ break;
65
117
  }
66
118
 
67
- // If there's a link configured, handle it
68
- if (props.linktype === 'EXTERNAL' && props.link?.url) {
119
+ // If there's a link configured, handle it (only for non-submit buttons or when not submitting)
120
+ if (props.linktype === 'EXTERNAL' && props.link?.url && buttonType !== 'submit') {
69
121
  window.open(props.link.url, props.link.target || '_blank');
70
122
  }
71
123
  };
72
124
 
73
- // Get button text - handle both string and {all: string} formats
125
+ // Get button text - handle both string and {all: string, locale: string} formats
74
126
  const getButtonText = () => {
75
127
  if (typeof props.text === 'string') {
76
128
  return props.text;
77
- } else if (props.text?.all) {
78
- return props.text.all;
129
+ } else if (props.text && typeof props.text === 'object') {
130
+ // Try to get locale-specific text first, then fall back to 'all'
131
+ // You can extend this to detect current locale
132
+ const locale = 'en-IN'; // Default locale, can be made dynamic
133
+ if (props.text[locale]) {
134
+ return props.text[locale];
135
+ } else if (props.text.all) {
136
+ return props.text.all;
137
+ }
79
138
  } else if (props.name?.all) {
80
139
  return props.name.all;
81
140
  }
@@ -86,7 +145,8 @@ const Button: React.FC<ButtonProps> = ({ props, onFormSubmit }) => {
86
145
  <div className="mb-6">
87
146
  <button
88
147
  onClick={handleClick}
89
- disabled={props.disabled}
148
+ type={props.buttonType === 'submit' ? 'submit' : 'button'}
149
+ disabled={props.disabled || (isSubmitting && props.buttonType === 'submit')}
90
150
  style={{
91
151
  backgroundColor: currentMode.bgColor || '#3498db',
92
152
  color: textStyle.fontColor,
@@ -98,25 +158,25 @@ const Button: React.FC<ButtonProps> = ({ props, onFormSubmit }) => {
98
158
  borderRadius: currentMode.radius || '4px',
99
159
  padding: '10px 20px',
100
160
  border: 'none',
101
- cursor: props.disabled ? 'not-allowed' : 'pointer',
102
- opacity: props.disabled ? 0.6 : 1,
161
+ cursor: (props.disabled || (isSubmitting && props.buttonType === 'submit')) ? 'not-allowed' : 'pointer',
162
+ opacity: (props.disabled || (isSubmitting && props.buttonType === 'submit')) ? 0.6 : 1,
103
163
  transition: 'all 0.2s ease-in-out',
104
164
  minWidth: '120px'
105
165
  }}
106
166
  onMouseEnter={(e) => {
107
- if (!props.disabled) {
167
+ if (!props.disabled && !(isSubmitting && props.buttonType === 'submit')) {
108
168
  e.currentTarget.style.opacity = '0.8';
109
169
  e.currentTarget.style.transform = 'translateY(-1px)';
110
170
  }
111
171
  }}
112
172
  onMouseLeave={(e) => {
113
- if (!props.disabled) {
173
+ if (!props.disabled && !(isSubmitting && props.buttonType === 'submit')) {
114
174
  e.currentTarget.style.opacity = '1';
115
175
  e.currentTarget.style.transform = 'translateY(0)';
116
176
  }
117
177
  }}
118
178
  >
119
- {getButtonText()}
179
+ {(isSubmitting && props.buttonType === 'submit') ? 'Submitting...' : getButtonText()}
120
180
  </button>
121
181
 
122
182
 
@@ -10,13 +10,36 @@ interface InputFieldProps {
10
10
 
11
11
  const InputField: React.FC<InputFieldProps> = ({ props, value = '', onChange }) => {
12
12
  const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
13
- const newValue = e.target.value;
13
+ let newValue = e.target.value;
14
+
15
+ // Handle postal code formatting
16
+ if (props.format === 'Postalcode' && !props.textArea) {
17
+ // Remove non-alphanumeric characters for postal code
18
+ newValue = newValue.replace(/[^a-zA-Z0-9\s-]/g, '');
19
+
20
+ // Apply formatting based on postaltype
21
+ if (props.postaltype === 'State') {
22
+ // US postal code format: 12345 or 12345-6789
23
+ newValue = newValue.replace(/(\d{5})(\d{0,4})/, (match, p1, p2) => {
24
+ return p2 ? `${p1}-${p2}` : p1;
25
+ });
26
+ }
27
+ }
28
+
14
29
  if (onChange) {
15
30
  const code = props.code || `inputfield_${props.name?.all || 'field'}`;
16
31
  onChange(code, newValue);
17
32
  }
18
33
  };
19
34
 
35
+ // Determine input type based on format
36
+ const getInputType = () => {
37
+ if (props.format === 'Postalcode') {
38
+ return 'text'; // Use text to allow formatting
39
+ }
40
+ return 'text';
41
+ };
42
+
20
43
  return (
21
44
  <div className="input-field">
22
45
  <label className="input-field__label">
@@ -28,6 +51,7 @@ const InputField: React.FC<InputFieldProps> = ({ props, value = '', onChange })
28
51
  <textarea
29
52
  value={value || ''}
30
53
  onChange={handleChange}
54
+ placeholder={props.helperText || ''}
31
55
  minLength={props.minLength}
32
56
  maxLength={props.maxLength}
33
57
  rows={4}
@@ -36,9 +60,10 @@ const InputField: React.FC<InputFieldProps> = ({ props, value = '', onChange })
36
60
  />
37
61
  ) : (
38
62
  <input
39
- type="text"
63
+ type={getInputType()}
40
64
  value={value || ''}
41
65
  onChange={handleChange}
66
+ placeholder={props.helperText || ''}
42
67
  minLength={props.minLength}
43
68
  maxLength={props.maxLength}
44
69
  required={props.required}
@@ -46,10 +71,6 @@ const InputField: React.FC<InputFieldProps> = ({ props, value = '', onChange })
46
71
  />
47
72
  )}
48
73
 
49
- {props.helperText && (
50
- <p className="input-field__helper-text">{props.helperText}</p>
51
- )}
52
-
53
74
  <div className="input-field__status">
54
75
  {props.format && `Format: ${props.format}`}
55
76
  {props.postaltype && ` • Type: ${props.postaltype}`}
@@ -16,6 +16,11 @@ const NumberField: React.FC<NumberFieldProps> = ({ props, value = '', onChange }
16
16
  }
17
17
  };
18
18
 
19
+ // Parse HTML content safely
20
+ const createMarkup = (html: string) => {
21
+ return { __html: html };
22
+ };
23
+
19
24
  return (
20
25
  <div className="number-field">
21
26
  <label className="number-field__label">
@@ -32,6 +37,7 @@ const NumberField: React.FC<NumberFieldProps> = ({ props, value = '', onChange }
32
37
  max={props.max}
33
38
  step={props.step}
34
39
  required={props.required}
40
+ placeholder={props.helperText || ''}
35
41
  className="number-field__input"
36
42
  />
37
43
 
@@ -42,12 +48,19 @@ const NumberField: React.FC<NumberFieldProps> = ({ props, value = '', onChange }
42
48
  )}
43
49
  </div>
44
50
 
45
- {props.helperText && (
46
- <p className="number-field__helper-text">{props.helperText}</p>
51
+ {props.termsandcondition?.all && (
52
+ <div
53
+ className="number-field__terms"
54
+ dangerouslySetInnerHTML={createMarkup(props.termsandcondition.all)}
55
+ />
47
56
  )}
48
57
 
49
58
  <div className="number-field__status">
50
- Range: {props.min}-{props.max} Step: {props.step}
59
+ {props.onText && props.offText ? (
60
+ <span>{props.onText} / {props.offText}</span>
61
+ ) : (
62
+ <span>Range: {props.min}-{props.max} • Step: {props.step}</span>
63
+ )}
51
64
  </div>
52
65
  </div>
53
66
  );
@@ -6,6 +6,9 @@ import './PageForm.css'; // Import the new CSS file
6
6
  interface PageFormProps {
7
7
  jsonData: Row[];
8
8
  onSubmit?: (data: Record<string, any>) => void;
9
+ isUrl?: boolean;
10
+ method?: 'POST' | 'GET' | 'post' | 'get';
11
+ url?: string;
9
12
  }
10
13
 
11
14
  interface Toast {
@@ -14,12 +17,13 @@ interface Toast {
14
17
  type: 'error' | 'success' | 'info' | 'warning';
15
18
  }
16
19
 
17
- const PageForm: React.FC<PageFormProps> = ({ jsonData, onSubmit }) => {
20
+ const PageForm: React.FC<PageFormProps> = ({ jsonData, onSubmit, isUrl = false, method = 'POST', url }) => {
18
21
  // Form state to store all field values
19
22
  const [formValues, setFormValues] = useState<Record<string, any>>({});
20
23
  const [deviceMode, setDeviceMode] = useState<string>('web');
21
24
  const [validationErrors, setValidationErrors] = useState<Record<string, boolean>>({});
22
25
  const [toasts, setToasts] = useState<Toast[]>([]);
26
+ const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
23
27
 
24
28
  // Function to determine device mode based on screen width
25
29
  const getDeviceMode = (width: number) => {
@@ -47,7 +51,7 @@ const PageForm: React.FC<PageFormProps> = ({ jsonData, onSubmit }) => {
47
51
  row.columns?.forEach((box) => {
48
52
  box.components?.forEach((component) => {
49
53
  // Check if component is a form field
50
- const fieldTypes = ['DateField', 'NumberField', 'SelectField', 'RadioField', 'InputField', 'BooleanField'];
54
+ const fieldTypes = ['DateField', 'NumberField', 'SelectField', 'RadioField', 'InputField', 'BooleanField', 'TermsAndCondition', 'TERMSANDCONDITION'];
51
55
  if (fieldTypes.includes(component.name)) {
52
56
  const code = getFieldCode(component);
53
57
  const name = getFieldName(component);
@@ -68,8 +72,8 @@ const PageForm: React.FC<PageFormProps> = ({ jsonData, onSubmit }) => {
68
72
  row.columns?.forEach((box) => {
69
73
  box.components?.forEach((component) => {
70
74
  // Check if component is a form field and is required
71
- const fieldTypes = ['DateField', 'NumberField', 'SelectField', 'RadioField', 'InputField', 'BooleanField'];
72
- if (fieldTypes.includes(component.name) && component.props.required) {
75
+ const fieldTypes = ['DateField', 'NumberField', 'SelectField', 'RadioField', 'InputField', 'BooleanField', 'TermsAndCondition', 'TERMSANDCONDITION'];
76
+ if (fieldTypes.includes(component.name) && (component.props.required || component.props.isRequired)) {
73
77
  const code = getFieldCode(component);
74
78
  const name = getFieldName(component);
75
79
  requiredFields[code] = { code, name };
@@ -138,11 +142,12 @@ const PageForm: React.FC<PageFormProps> = ({ jsonData, onSubmit }) => {
138
142
 
139
143
  Object.keys(requiredFields).forEach((code) => {
140
144
  const value = formValues[code];
141
- // Check if value is empty, null, undefined, empty array, or empty string
145
+ // Check if value is empty, null, undefined, empty array, empty string, or false (for checkboxes)
142
146
  if (
143
147
  value === undefined ||
144
148
  value === null ||
145
149
  value === '' ||
150
+ value === false ||
146
151
  (Array.isArray(value) && value.length === 0)
147
152
  ) {
148
153
  errors[code] = true;
@@ -171,28 +176,97 @@ const PageForm: React.FC<PageFormProps> = ({ jsonData, onSubmit }) => {
171
176
  setToasts(prev => prev.filter(toast => toast.id !== id));
172
177
  }, []);
173
178
 
174
- // Single method to handle form submission - validates, transforms, and sends data to parent
175
- const handleFormSubmit = useCallback(() => {
179
+ // Handler to reset form values
180
+ const handleFormReset = useCallback(() => {
181
+ setFormValues({});
182
+ setValidationErrors({});
183
+ showToast('Form has been reset', 'info');
184
+ }, [showToast]);
185
+
186
+ // Handler to cancel form (same as reset for now, but can be customized)
187
+ const handleFormCancel = useCallback(() => {
188
+ setFormValues({});
189
+ setValidationErrors({});
190
+ showToast('Form has been cancelled', 'info');
191
+ }, [showToast]);
192
+
193
+ // Single method to handle form submission - validates, transforms, and sends data to parent or API
194
+ const handleFormSubmit = useCallback(async () => {
176
195
  // Validate form and get validation result
177
196
  const { isValid, errors } = validateForm();
178
197
 
179
- if (isValid) {
180
- // Transform form values to use field names instead of codes
181
- const transformedData = transformFormValuesToNames();
182
-
183
- // Send transformed data back to parent component
184
- if (onSubmit) {
185
- onSubmit(transformedData);
186
- }
187
- } else {
198
+ if (!isValid) {
188
199
  // If validation fails, show error message with field names
189
200
  const requiredFields = getRequiredFields();
190
201
  const errorFields = Object.keys(errors)
191
202
  .map(code => requiredFields[code]?.name || code)
192
203
  .join(', ');
193
204
  showToast(`Please fill in all required fields: ${errorFields}`, 'error');
205
+ return;
206
+ }
207
+
208
+ // Transform form values to use field names instead of codes
209
+ const transformedData = transformFormValuesToNames();
210
+
211
+ // Check if we should send to URL or use onSubmit callback
212
+ if (isUrl && url) {
213
+ // Send data to API URL
214
+ setIsSubmitting(true);
215
+ try {
216
+ const httpMethod = method.toUpperCase();
217
+ const requestOptions: RequestInit = {
218
+ method: httpMethod,
219
+ headers: {
220
+ 'Content-Type': 'application/json',
221
+ },
222
+ };
223
+
224
+ // For GET requests, append data as query parameters
225
+ // For POST requests, send data in the body
226
+ let requestUrl = url;
227
+ if (httpMethod === 'GET') {
228
+ const queryParams = new URLSearchParams();
229
+ Object.keys(transformedData).forEach((key) => {
230
+ queryParams.append(key, String(transformedData[key]));
231
+ });
232
+ requestUrl = `${url}?${queryParams.toString()}`;
233
+ } else {
234
+ // POST, PUT, PATCH, etc.
235
+ requestOptions.body = JSON.stringify(transformedData);
236
+ }
237
+
238
+ const response = await fetch(requestUrl, requestOptions);
239
+
240
+ if (!response.ok) {
241
+ throw new Error(`HTTP error! status: ${response.status}`);
242
+ }
243
+
244
+ const responseData = await response.json().catch(() => response.text());
245
+
246
+ setIsSubmitting(false);
247
+ showToast('Form submitted successfully!', 'success');
248
+
249
+ // Also call onSubmit if provided, passing the response data
250
+ if (onSubmit) {
251
+ onSubmit(responseData || transformedData);
252
+ }
253
+ } catch (error) {
254
+ setIsSubmitting(false);
255
+ const errorMessage = error instanceof Error ? error.message : 'Failed to submit form';
256
+ showToast(`Error submitting form: ${errorMessage}`, 'error');
257
+
258
+ // Still call onSubmit with error data if provided
259
+ if (onSubmit) {
260
+ onSubmit({ error: errorMessage, data: transformedData });
261
+ }
262
+ }
263
+ } else {
264
+ // Send transformed data back to parent component via onSubmit callback
265
+ if (onSubmit) {
266
+ onSubmit(transformedData);
267
+ }
194
268
  }
195
- }, [formValues, validateForm, getRequiredFields, transformFormValuesToNames, onSubmit, showToast]);
269
+ }, [formValues, validateForm, getRequiredFields, transformFormValuesToNames, onSubmit, showToast, isUrl, method, url]);
196
270
 
197
271
  return (
198
272
  <div className="page-form">
@@ -207,6 +281,9 @@ const PageForm: React.FC<PageFormProps> = ({ jsonData, onSubmit }) => {
207
281
  validationErrors={validationErrors}
208
282
  onFieldChange={handleFieldChange}
209
283
  onFormSubmit={handleFormSubmit}
284
+ onFormReset={handleFormReset}
285
+ onFormCancel={handleFormCancel}
286
+ isSubmitting={isSubmitting}
210
287
  />
211
288
  ))}
212
289
 
@@ -20,6 +20,9 @@ const RadioField: React.FC<RadioFieldProps> = ({ props, value = '', onChange })
20
20
  <div className="radio-field">
21
21
  <label className="radio-field__label">
22
22
  {props.name?.all || 'Radio Field'}
23
+ {props.helperText && (
24
+ <span className="radio-field__placeholder"> ({props.helperText})</span>
25
+ )}
23
26
  {props.required && <span className="radio-field__required">*</span>}
24
27
  </label>
25
28
 
@@ -46,12 +49,6 @@ const RadioField: React.FC<RadioFieldProps> = ({ props, value = '', onChange })
46
49
  ))
47
50
  )}
48
51
  </div>
49
-
50
- {props.helperText && (
51
- <p className="radio-field__helper-text">{props.helperText}</p>
52
- )}
53
-
54
-
55
52
  </div>
56
53
  );
57
54
  };
@@ -9,6 +9,9 @@ interface RowComponentProps {
9
9
  validationErrors?: Record<string, boolean>;
10
10
  onFieldChange?: (code: string, value: any) => void;
11
11
  onFormSubmit?: () => void;
12
+ onFormReset?: () => void;
13
+ onFormCancel?: () => void;
14
+ isSubmitting?: boolean;
12
15
  }
13
16
 
14
17
  const RowComponent: React.FC<RowComponentProps> = ({
@@ -17,7 +20,10 @@ const RowComponent: React.FC<RowComponentProps> = ({
17
20
  formValues = {},
18
21
  validationErrors = {},
19
22
  onFieldChange,
20
- onFormSubmit
23
+ onFormSubmit,
24
+ onFormReset,
25
+ onFormCancel,
26
+ isSubmitting = false
21
27
  }) => {
22
28
  const getCurrentMode = () => {
23
29
  switch(deviceMode) {
@@ -70,6 +76,9 @@ const RowComponent: React.FC<RowComponentProps> = ({
70
76
  validationErrors={validationErrors}
71
77
  onFieldChange={onFieldChange}
72
78
  onFormSubmit={onFormSubmit}
79
+ onFormReset={onFormReset}
80
+ onFormCancel={onFormCancel}
81
+ isSubmitting={isSubmitting}
73
82
  />
74
83
  ))}
75
84
  {/* Clear float after all children */}
@@ -37,6 +37,12 @@
37
37
  resize: vertical;
38
38
  }
39
39
 
40
+ .input-field__input::placeholder,
41
+ .input-field__textarea::placeholder {
42
+ color: #9ca3af; /* gray-400 */
43
+ opacity: 1;
44
+ }
45
+
40
46
  .input-field__helper-text {
41
47
  margin-top: 0.25rem;
42
48
  font-size: 0.75rem;
@@ -50,6 +50,22 @@
50
50
  color: #6b7280; /* gray-500 */
51
51
  }
52
52
 
53
+ .number-field__terms {
54
+ margin-top: 0.25rem;
55
+ font-size: 0.75rem;
56
+ color: #4b5563; /* gray-600 */
57
+ line-height: 1.5;
58
+ }
59
+
60
+ .number-field__terms p {
61
+ margin: 0;
62
+ }
63
+
64
+ .number-field__input::placeholder {
65
+ color: #9ca3af; /* gray-400 */
66
+ opacity: 1;
67
+ }
68
+
53
69
  .number-field__status {
54
70
  margin-top: 0.25rem;
55
71
  font-size: 0.75rem;
@@ -10,6 +10,12 @@
10
10
  margin-bottom: 0.5rem;
11
11
  }
12
12
 
13
+ .radio-field__placeholder {
14
+ color: #9ca3af; /* gray-400 */
15
+ font-weight: 400;
16
+ font-style: italic;
17
+ }
18
+
13
19
  .radio-field__required {
14
20
  color: #ef4444; /* red-500 */
15
21
  margin-left: 0.25rem;