@adcops/autocore-react 3.3.40 → 3.3.43

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.
@@ -1 +1 @@
1
- {"version":3,"file":"TestSetupForm.d.ts","sourceRoot":"","sources":["../../src/components/TestSetupForm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA0C,MAAM,OAAO,CAAC;AAO/D,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC3B,cAAc,EAAE,YAAY,EAAE,CAAC;IAC/B,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,cAAc,EAAE,YAAY,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,kBAAkB;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,kBAAkB,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;CAChE;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA0KtD,CAAC"}
1
+ {"version":3,"file":"TestSetupForm.d.ts","sourceRoot":"","sources":["../../src/components/TestSetupForm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA0C,MAAM,OAAO,CAAC;AAS/D,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC3B,cAAc,EAAE,YAAY,EAAE,CAAC;IAC/B,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,cAAc,EAAE,YAAY,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,kBAAkB;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,kBAAkB,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;CAChE;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAsOtD,CAAC"}
@@ -1 +1 @@
1
- import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import React,{useState,useEffect,useContext}from"react";import{InputText}from"primereact/inputtext";import{AutoComplete}from"primereact/autocomplete";import{EventEmitterContext}from"../core/EventEmitterContext";import{MessageType}from"../hub/CommandMessage";export const TestSetupForm=({schema:e,defaultProjectId:s="",defaultDefinitionId:a="default",onProjectChange:t,onDefinitionChange:n,onValidationChange:r})=>{const[i,o]=useState({}),[c,p]=useState(s),[l,m]=useState(a);useEffect(()=>{t&&t(c)},[c,t]),useEffect(()=>{n&&n(l)},[l,n]);const[d,u]=useState([]),[f,x]=useState([]),[j,g]=useState(!1),{invoke:h}=useContext(EventEmitterContext);useEffect(()=>{(async()=>{try{const e=await h("results.list_projects",MessageType.Request,{});e.success&&e.data&&e.data.projects&&u(e.data.projects)}catch(e){}})()},[h]);useEffect(()=>{if(!e)return;let s=!0;c&&""!==c.trim()||(s=!1),l&&""!==l.trim()||(s=!1);const a=[...e.project_fields,...e.config_fields];for(const e of a)if(e.required&&!e.source&&(void 0===i[e.name]||""===i[e.name])){s=!1;break}g(s),r&&r(s,i)},[i,e,c,l,r]);const _=e=>{if(e.source)return _jsxs("div",{className:"ac-form-span p-inputgroup",children:[_jsx("span",{className:"p-inputgroup-addon",children:e.name}),_jsx(InputText,{value:`Auto-fetched from ${e.source}`,disabled:!0}),e.units&&_jsx("span",{className:"p-inputgroup-addon",children:e.units}),_jsx("span",{className:"p-inputgroup-addon",style:{color:"var(--green-500)"},children:_jsx("i",{className:"pi pi-check"})})]},e.name);const s=(e=>!(e.required&&!e.source)||void 0!==i[e.name]&&""!==i[e.name])(e);return _jsxs("div",{className:"ac-form-span p-inputgroup",children:[_jsx("span",{className:"p-inputgroup-addon",children:e.name}),_jsx(InputText,{value:i[e.name]||"",onChange:s=>o({...i,[e.name]:s.target.value}),placeholder:`Enter ${e.name}`,className:s?"":"p-invalid"}),e.units&&_jsx("span",{className:"p-inputgroup-addon",children:e.units}),_jsx("span",{className:"p-inputgroup-addon",style:{color:s?"var(--green-500)":"var(--red-500)"},children:_jsx("i",{className:s?"pi pi-check":"pi pi-times"})})]},e.name)};return e?_jsxs("div",{className:"ac-form-grid",style:{padding:"1.25rem"},children:[_jsxs("h3",{className:"ac-form-section",style:{display:"flex",alignItems:"center",gap:"10px"},children:["Project & Definition",_jsx("span",{style:{color:j?"var(--green-500)":"var(--red-500)"},children:_jsx("i",{className:j?"pi pi-check-circle":"pi pi-exclamation-circle"})})]}),_jsxs("div",{className:"ac-form-span p-inputgroup",children:[_jsx("span",{className:"p-inputgroup-addon",children:"Project ID"}),_jsx(AutoComplete,{value:c,suggestions:f,completeMethod:e=>{const s=e.query.toLowerCase();x(d.filter(e=>e.toLowerCase().includes(s)))},onChange:e=>(e=>{const s=(e||"").replace(/[^a-zA-Z0-9_]/g,"");p(s)})(e.value),dropdown:!0,placeholder:"Enter or select Project ID",className:c&&""!==c.trim()?"":"p-invalid"}),_jsx("span",{className:"p-inputgroup-addon",style:{color:c&&""!==c.trim()?"var(--green-500)":"var(--red-500)"},children:_jsx("i",{className:c&&""!==c.trim()?"pi pi-check":"pi pi-times"})})]}),_jsxs("div",{className:"ac-form-span p-inputgroup",children:[_jsx("span",{className:"p-inputgroup-addon",children:"Definition ID"}),_jsx(InputText,{value:l,onChange:e=>(e=>{const s=e.replace(/[^a-zA-Z0-9_]/g,"");m(s)})(e.target.value),placeholder:"Enter Definition ID",className:l&&""!==l.trim()?"":"p-invalid"}),_jsx("span",{className:"p-inputgroup-addon",style:{color:l&&""!==l.trim()?"var(--green-500)":"var(--red-500)"},children:_jsx("i",{className:l&&""!==l.trim()?"pi pi-check":"pi pi-times"})})]}),_jsx("h3",{className:"ac-form-section",style:{marginTop:"1rem"},children:"Project Information"}),e.project_fields.map(_),_jsx("h3",{className:"ac-form-section",style:{marginTop:"1rem"},children:"Test Configuration"}),e.config_fields.map(_)]}):_jsx("div",{className:"ac-form-grid",style:{padding:"1.25rem"},children:_jsx("h3",{className:"ac-form-section",children:"No Test Definition Selected"})})};
1
+ import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import React,{useState,useEffect,useContext}from"react";import{AutoComplete}from"primereact/autocomplete";import{EventEmitterContext}from"../core/EventEmitterContext";import{MessageType}from"../hub/CommandMessage";import{ValueInput}from"./ValueInput";import{TextInput}from"./TextInput";import{InputText}from"primereact/inputtext";export const TestSetupForm=({schema:e,defaultProjectId:t="",defaultDefinitionId:s="default",onProjectChange:a,onDefinitionChange:n,onValidationChange:i})=>{const[r,o]=useState({}),[c,l]=useState(t),[m,p]=useState(s);useEffect(()=>{a&&a(c)},[c,a]),useEffect(()=>{n&&n(m)},[m,n]);const[u,d]=useState([]),[f,x]=useState([]),[j,g]=useState(!1),{invoke:h,write:_,subscribe:v,unsubscribe:y}=useContext(EventEmitterContext);useEffect(()=>{if(!e)return;const t=[...e.project_fields,...e.config_fields],s=[];for(const e of t){if(!e.source)continue;const t=e.source,a=e.name,n=v(t,e=>{o(t=>t[a]!==e?{...t,[a]:e}:t)});s.push(n),(async()=>{try{const e=await h(t,MessageType.Read,{}),s=void 0!==e?.data?e.data:e;null!=s&&o(e=>void 0===e[a]||null===e[a]||""===e[a]?{...e,[a]:s}:e)}catch(e){}})()}return()=>{for(const e of s)y(e)}},[e,v,y,h]),useEffect(()=>{(async()=>{try{const e=await h("results.list_projects",MessageType.Request,{});e.success&&e.data&&e.data.projects&&d(e.data.projects)}catch(e){}})()},[h]);useEffect(()=>{if(!e)return;let t=!0;c&&""!==c.trim()||(t=!1),m&&""!==m.trim()||(t=!1);const s=[...e.project_fields,...e.config_fields];for(const e of s)if(e.required&&(void 0===r[e.name]||""===r[e.name]||null===r[e.name])){t=!1;break}g(t),i&&i(t,r)},[r,e,c,m,i]);const I=async(e,t)=>{if(o({...r,[e.name]:t}),e.source)try{await _(e.source,t)}catch(e){}},N=e=>{const t=(e=>!e.required||void 0!==r[e.name]&&""!==r[e.name]&&null!==r[e.name])(e),s="string"!==e.type&&"bool"!==e.type;return _jsxs(React.Fragment,{children:[_jsx("span",{className:"ac-form-label",children:e.name}),s?_jsx(ValueInput,{label:void 0,value:null!=r[e.name]?Number(r[e.name]):null,suffix:e.units?` ${e.units}`:void 0,onValueChanged:t=>I(e,t),className:t?"":"p-invalid"}):_jsx(TextInput,{label:void 0,value:null!=r[e.name]?String(r[e.name]):"",suffix:e.units?` ${e.units}`:void 0,onValueChanged:t=>I(e,t),className:t?"":"p-invalid"}),_jsx("span",{style:{color:t?"var(--green-500)":"var(--red-500)",display:"flex",alignItems:"center"},children:_jsx("i",{className:t?"pi pi-check":"pi pi-times"})})]},e.name)};return e?_jsxs("div",{className:"ac-form-grid",style:{padding:"1.25rem"},children:[_jsxs("h3",{className:"ac-form-section",style:{display:"flex",alignItems:"center",gap:"10px"},children:["Project & Definition",_jsx("span",{style:{color:j?"var(--green-500)":"var(--red-500)"},children:_jsx("i",{className:j?"pi pi-check-circle":"pi pi-exclamation-circle"})})]}),_jsx("span",{className:"ac-form-label",children:"Project ID"}),_jsx(AutoComplete,{value:c,suggestions:f,completeMethod:e=>{const t=e.query.toLowerCase();x(u.filter(e=>e.toLowerCase().includes(t)))},onChange:e=>(e=>{const t=(e||"").replace(/[^a-zA-Z0-9_]/g,"");l(t)})(e.value),dropdown:!0,placeholder:"Enter or select Project ID",className:c&&""!==c.trim()?"":"p-invalid"}),_jsx("span",{style:{color:c&&""!==c.trim()?"var(--green-500)":"var(--red-500)",display:"flex",alignItems:"center"},children:_jsx("i",{className:c&&""!==c.trim()?"pi pi-check":"pi pi-times"})}),_jsx("span",{className:"ac-form-label",children:"Definition ID"}),_jsx(InputText,{value:m,onChange:e=>(e=>{const t=e.replace(/[^a-zA-Z0-9_]/g,"");p(t)})(e.target.value),placeholder:"Enter Definition ID",className:m&&""!==m.trim()?"":"p-invalid"}),_jsx("span",{style:{color:m&&""!==m.trim()?"var(--green-500)":"var(--red-500)",display:"flex",alignItems:"center"},children:_jsx("i",{className:m&&""!==m.trim()?"pi pi-check":"pi pi-times"})}),_jsx("h3",{className:"ac-form-section",style:{marginTop:"1rem"},children:"Project Information"}),e.project_fields.map(N),_jsx("h3",{className:"ac-form-section",style:{marginTop:"1rem"},children:"Test Configuration"}),e.config_fields.map(N)]}):_jsx("div",{className:"ac-form-grid",style:{padding:"1.25rem"},children:_jsx("h3",{className:"ac-form-section",children:"No Test Definition Selected"})})};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adcops/autocore-react",
3
- "version": "3.3.40",
3
+ "version": "3.3.43",
4
4
  "description": "A React component library for industrial user interfaces.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -1,9 +1,11 @@
1
1
  import React, { useState, useEffect, useContext } from 'react';
2
- import { InputText } from 'primereact/inputtext';
3
2
  import { AutoComplete } from 'primereact/autocomplete';
4
3
  import type { AutoCompleteCompleteEvent } from 'primereact/autocomplete';
5
4
  import { EventEmitterContext } from '../core/EventEmitterContext';
6
5
  import { MessageType } from '../hub/CommandMessage';
6
+ import { ValueInput } from './ValueInput';
7
+ import { TextInput } from './TextInput';
8
+ import { InputText } from 'primereact/inputtext';
7
9
 
8
10
  export interface TestFieldDef {
9
11
  name: string;
@@ -54,7 +56,62 @@ export const TestSetupForm: React.FC<TestSetupFormProps> = ({
54
56
  const [filteredProjects, setFilteredProjects] = useState<string[]>([]);
55
57
 
56
58
  const [isValid, setIsValid] = useState(false);
57
- const { invoke } = useContext(EventEmitterContext);
59
+ const { invoke, write, subscribe, unsubscribe } = useContext(EventEmitterContext);
60
+
61
+ // Subscribe to external tags if a field defines a source, and seed the
62
+ // current value once on mount so the form reflects existing GM state
63
+ // instead of staying blank until the next broadcast.
64
+ useEffect(() => {
65
+ if (!schema) return;
66
+
67
+ const allFields = [...schema.project_fields, ...schema.config_fields];
68
+ const subs: any[] = [];
69
+
70
+ for (const field of allFields) {
71
+ if (!field.source) continue;
72
+
73
+ const source = field.source;
74
+ const fieldName = field.name;
75
+
76
+ const subId = subscribe(source, (newVal: any) => {
77
+ setConfig((prev: any) => {
78
+ if (prev[fieldName] !== newVal) {
79
+ return { ...prev, [fieldName]: newVal };
80
+ }
81
+ return prev;
82
+ });
83
+ });
84
+ subs.push(subId);
85
+
86
+ // Pub/sub only delivers future changes, not the current value.
87
+ // Fetch it explicitly so reloading the page doesn't blank the form.
88
+ (async () => {
89
+ try {
90
+ const resp: any = await invoke(
91
+ source as any,
92
+ MessageType.Read as any,
93
+ {} as any,
94
+ );
95
+ const current = resp?.data !== undefined ? resp.data : resp;
96
+ if (current !== undefined && current !== null) {
97
+ setConfig((prev: any) => (
98
+ prev[fieldName] === undefined || prev[fieldName] === null || prev[fieldName] === ''
99
+ ? { ...prev, [fieldName]: current }
100
+ : prev
101
+ ));
102
+ }
103
+ } catch (err) {
104
+ console.warn(`TestSetupForm: failed to seed "${fieldName}" from "${source}":`, err);
105
+ }
106
+ })();
107
+ }
108
+
109
+ return () => {
110
+ for (const subId of subs) {
111
+ unsubscribe(subId);
112
+ }
113
+ };
114
+ }, [schema, subscribe, unsubscribe, invoke]);
58
115
 
59
116
  useEffect(() => {
60
117
  const fetchProjects = async () => {
@@ -89,8 +146,8 @@ export const TestSetupForm: React.FC<TestSetupFormProps> = ({
89
146
  const allFields = [...schema.project_fields, ...schema.config_fields];
90
147
 
91
148
  for (const field of allFields) {
92
- if (field.required && !field.source) {
93
- if (config[field.name] === undefined || config[field.name] === '') {
149
+ if (field.required) {
150
+ if (config[field.name] === undefined || config[field.name] === '' || config[field.name] === null) {
94
151
  valid = false;
95
152
  break;
96
153
  }
@@ -103,8 +160,8 @@ export const TestSetupForm: React.FC<TestSetupFormProps> = ({
103
160
  }, [config, schema, projectId, definitionId, onValidationChange]);
104
161
 
105
162
  const isFieldValid = (field: TestFieldDef) => {
106
- if (!field.required || field.source) return true;
107
- return config[field.name] !== undefined && config[field.name] !== '';
163
+ if (!field.required) return true;
164
+ return config[field.name] !== undefined && config[field.name] !== '' && config[field.name] !== null;
108
165
  };
109
166
 
110
167
  const handleProjectIdChange = (value: string | null | undefined) => {
@@ -113,36 +170,45 @@ export const TestSetupForm: React.FC<TestSetupFormProps> = ({
113
170
  setProjectId(sanitized);
114
171
  };
115
172
 
116
- const renderField = (field: TestFieldDef) => {
173
+ const handleFieldChange = async (field: TestFieldDef, val: any) => {
174
+ setConfig({...config, [field.name]: val});
117
175
  if (field.source) {
118
- return (
119
- <div key={field.name} className="ac-form-span p-inputgroup">
120
- <span className="p-inputgroup-addon">{field.name}</span>
121
- <InputText value={`Auto-fetched from ${field.source}`} disabled />
122
- {field.units && <span className="p-inputgroup-addon">{field.units}</span>}
123
- <span className="p-inputgroup-addon" style={{ color: 'var(--green-500)' }}>
124
- <i className="pi pi-check"></i>
125
- </span>
126
- </div>
127
- );
176
+ try {
177
+ await write(field.source, val);
178
+ } catch(e) {
179
+ console.error("Failed to write to source:", e);
180
+ }
128
181
  }
182
+ };
129
183
 
184
+ const renderField = (field: TestFieldDef) => {
130
185
  const valid = isFieldValid(field);
186
+ const isNum = field.type !== 'string' && field.type !== 'bool';
131
187
 
132
188
  return (
133
- <div key={field.name} className="ac-form-span p-inputgroup">
134
- <span className="p-inputgroup-addon">{field.name}</span>
135
- <InputText
136
- value={config[field.name] || ''}
137
- onChange={(e) => setConfig({...config, [field.name]: e.target.value})}
138
- placeholder={`Enter ${field.name}`}
139
- className={!valid ? 'p-invalid' : ''}
140
- />
141
- {field.units && <span className="p-inputgroup-addon">{field.units}</span>}
142
- <span className="p-inputgroup-addon" style={{ color: valid ? 'var(--green-500)' : 'var(--red-500)' }}>
189
+ <React.Fragment key={field.name}>
190
+ <span className="ac-form-label">{field.name}</span>
191
+ {isNum ? (
192
+ <ValueInput
193
+ label={undefined}
194
+ value={config[field.name] != null ? Number(config[field.name]) : null}
195
+ suffix={field.units ? ` ${field.units}` : undefined}
196
+ onValueChanged={(val) => handleFieldChange(field, val)}
197
+ className={!valid ? 'p-invalid' : ''}
198
+ />
199
+ ) : (
200
+ <TextInput
201
+ label={undefined}
202
+ value={config[field.name] != null ? String(config[field.name]) : ''}
203
+ suffix={field.units ? ` ${field.units}` : undefined}
204
+ onValueChanged={(val) => handleFieldChange(field, val)}
205
+ className={!valid ? 'p-invalid' : ''}
206
+ />
207
+ )}
208
+ <span style={{ color: valid ? 'var(--green-500)' : 'var(--red-500)', display: 'flex', alignItems: 'center' }}>
143
209
  <i className={valid ? "pi pi-check" : "pi pi-times"}></i>
144
210
  </span>
145
- </div>
211
+ </React.Fragment>
146
212
  );
147
213
  };
148
214
 
@@ -163,34 +229,30 @@ export const TestSetupForm: React.FC<TestSetupFormProps> = ({
163
229
  </span>
164
230
  </h3>
165
231
 
166
- <div className="ac-form-span p-inputgroup">
167
- <span className="p-inputgroup-addon">Project ID</span>
168
- <AutoComplete
169
- value={projectId}
170
- suggestions={filteredProjects}
171
- completeMethod={searchProjects}
172
- onChange={(e) => handleProjectIdChange(e.value)}
173
- dropdown
174
- placeholder="Enter or select Project ID"
175
- className={!projectId || projectId.trim() === '' ? 'p-invalid' : ''}
176
- />
177
- <span className="p-inputgroup-addon" style={{ color: projectId && projectId.trim() !== '' ? 'var(--green-500)' : 'var(--red-500)' }}>
178
- <i className={projectId && projectId.trim() !== '' ? "pi pi-check" : "pi pi-times"}></i>
179
- </span>
180
- </div>
232
+ <span className="ac-form-label">Project ID</span>
233
+ <AutoComplete
234
+ value={projectId}
235
+ suggestions={filteredProjects}
236
+ completeMethod={searchProjects}
237
+ onChange={(e) => handleProjectIdChange(e.value)}
238
+ dropdown
239
+ placeholder="Enter or select Project ID"
240
+ className={!projectId || projectId.trim() === '' ? 'p-invalid' : ''}
241
+ />
242
+ <span style={{ color: projectId && projectId.trim() !== '' ? 'var(--green-500)' : 'var(--red-500)', display: 'flex', alignItems: 'center' }}>
243
+ <i className={projectId && projectId.trim() !== '' ? "pi pi-check" : "pi pi-times"}></i>
244
+ </span>
181
245
 
182
- <div className="ac-form-span p-inputgroup">
183
- <span className="p-inputgroup-addon">Definition ID</span>
184
- <InputText
185
- value={definitionId}
186
- onChange={(e) => handleDefinitionIdChange(e.target.value)}
187
- placeholder="Enter Definition ID"
188
- className={!definitionId || definitionId.trim() === '' ? 'p-invalid' : ''}
189
- />
190
- <span className="p-inputgroup-addon" style={{ color: definitionId && definitionId.trim() !== '' ? 'var(--green-500)' : 'var(--red-500)' }}>
191
- <i className={definitionId && definitionId.trim() !== '' ? "pi pi-check" : "pi pi-times"}></i>
192
- </span>
193
- </div>
246
+ <span className="ac-form-label">Definition ID</span>
247
+ <InputText
248
+ value={definitionId}
249
+ onChange={(e) => handleDefinitionIdChange(e.target.value)}
250
+ placeholder="Enter Definition ID"
251
+ className={!definitionId || definitionId.trim() === '' ? 'p-invalid' : ''}
252
+ />
253
+ <span style={{ color: definitionId && definitionId.trim() !== '' ? 'var(--green-500)' : 'var(--red-500)', display: 'flex', alignItems: 'center' }}>
254
+ <i className={definitionId && definitionId.trim() !== '' ? "pi pi-check" : "pi pi-times"}></i>
255
+ </span>
194
256
 
195
257
  <h3 className="ac-form-section" style={{ marginTop: '1rem' }}>Project Information</h3>
196
258
  {schema.project_fields.map(renderField)}