import React from 'react';

const TEMPLATE_KIND = 'dependent-selector';
const TEMPLATE_TITLE = 'Dependent Selector';
const TEMPLATE_DESC = 'Dependent Selector - reusable Yeeflow cascading parent-child selector for structured input.';
const WRITABLE_TARGETS = ["parentSaveToField", "childSaveToField"];

export class CodeInApplication implements CodeInComp {
  description() { return TEMPLATE_DESC; }
  requiredFields(params?: CodeInParams) {
    const required: string[] = [];
    const safeParams: any = params || {};
    const map: any = {};
    ["parentSaveToField", "childSaveToField"].forEach((key) => {
      const candidates = this.collectRequiredFieldCandidates(safeParams[key]).filter((value) => this.isLikelyWritableTargetName(value));
      if (candidates.length > 0) { map[key] = candidates[0]; }
      candidates.forEach((value) => { if (value && required.indexOf(value) === -1) { required.push(value); } });
    });
    (this as any).configuredTargetMap = map;
    return required;
  }
  normalizeExpressionText(value: any) { if(value===undefined||value===null){return '';} if(typeof value==='string'||typeof value==='number'||typeof value==='boolean'){return String(value).trim();} if(Array.isArray(value)){return value.map((item)=>this.normalizeExpressionText(item)).filter(Boolean).join(',');} if(typeof value==='object'){return this.normalizeExpressionText(value.fieldId!==undefined?value.fieldId:value.FieldId!==undefined?value.FieldId:value.variableName!==undefined?value.variableName:value.VariableName!==undefined?value.VariableName:value.tempVariableName!==undefined?value.tempVariableName:value.TempVariableName!==undefined?value.TempVariableName:value.key!==undefined?value.key:value.Key!==undefined?value.Key:value.id!==undefined?value.id:value.Id!==undefined?value.Id:value.name!==undefined?value.name:value.Name!==undefined?value.Name:value.label!==undefined?value.label:value.Label!==undefined?value.Label:value.value!==undefined?value.value:value.Value!==undefined?value.Value:'');} return String(value).trim(); }
  collectRequiredFieldCandidates(value: any) { const out: string[]=[]; const seen:any={}; const add=(v:any)=>{const s=this.normalizeExpressionText(v); if(s&&!seen[s]){seen[s]=true;out.push(s);}}; const visit=(v:any,d:number)=>{if(v===undefined||v===null||d>3){return;} if(typeof v==='string'||typeof v==='number'||typeof v==='boolean'){add(v);return;} if(Array.isArray(v)){v.forEach((x)=>visit(x,d+1));return;} if(typeof v!=='object'){return;} ['fieldId','FieldId','fieldName','FieldName','variableId','VariableId','variableName','VariableName','tempVariableId','TempVariableId','tempVariableName','TempVariableName','id','Id','key','Key','name','Name','code','Code','label','Label','title','Title','path','Path','value','Value'].forEach((k)=>{if(v[k]!==undefined&&v[k]!==null){add(v[k]);}}); ['target','Target','binding','Binding','field','Field','variable','Variable','tempVariable','TempVariable','data','Data','meta','Meta','metadata','Metadata'].forEach((k)=>{if(v[k]!==undefined&&v[k]!==null){visit(v[k],d+1);}});}; visit(value,0); return out; }
  isLikelyWritableTargetName(value: any) { const s=this.normalizeExpressionText(value); if(!s||s==='[]'||s==='{}'){return false;} if(s.charAt(0)==='['||s.charAt(0)==='{'){return false;} if(s.indexOf('":')!==-1||s.indexOf('","')!==-1){return false;} return s.length<=160; }
  inputParameters(): InputParameter[] { return [
      {"id": "dataListId", "name": "Data List ID", "type": "variable", "desc": "Source list.", "example": "203..."},
      {"id": "parentField", "name": "Parent Field", "type": "variable", "desc": "Parent option field.", "example": "Country"},
      {"id": "childField", "name": "Child Field", "type": "variable", "desc": "Child option field.", "example": "State"},
      {"id": "parentSaveToField", "name": "Parent Save To Field", "type": "variable", "desc": "Writable parent output.", "example": "SelectedCountry"},
      {"id": "childSaveToField", "name": "Child Save To Field", "type": "variable", "desc": "Writable child output.", "example": "SelectedState"},
      {"id": "titleText", "name": "Title Text", "type": "string", "desc": "Panel title.", "example": "Location"},
      {"id": "subtitleText", "name": "Subtitle Text", "type": "string", "desc": "Panel subtitle.", "example": "Select country and state"},
      {"id": "maxItems", "name": "Max Items", "type": "string", "desc": "Max loaded records.", "example": "500"},
      {"id": "emptyText", "name": "Empty Text", "type": "string", "desc": "Empty text.", "example": "No options"},
      {"id": "filterExpression", "name": "Filter Expression", "type": "variable", "desc": "Optional filter.", "example": ""}
    ]; }
  render(context: CodeInContext, fieldsValues: any, readonly: boolean) { return <ReusableTemplatePanel context={context} fieldsValues={fieldsValues} readonly={readonly} params={(context && context.params) || {}} configuredOutputTargets={(this as any).configuredTargetMap || {}} />; }
}

class ReusableTemplatePanel extends React.Component<any, any> {
  private mountedFlag: boolean;
  constructor(props: any) {
    super(props);
    this.mountedFlag = false;
    this.state = this.createInitialState(props);
  }
  componentDidMount() { this.mountedFlag = true; if (this.needsQuery()) { this.loadData(); } }
  componentDidUpdate(previousProps: any) { if (this.getConfigKey(previousProps) !== this.getConfigKey(this.props)) { this.setState(this.createInitialState(this.props), () => { if (this.needsQuery()) { this.loadData(); } }); } }
  componentWillUnmount() { this.mountedFlag = false; }
  createInitialState(props: any) { const config=this.getConfigFromProps(props); return { loading:false,errorText:'',rows:[],selected:[],tags:this.readInitialArray(config),checklist:this.readInitialChecklist(config),parentValue:this.toString(this.readTarget(config.parentTarget)),childValue:this.toString(this.readTarget(config.childTarget)),textValue:'',validationMessage:'' }; }
  needsQuery() { return ['approval-timeline','related-record-summary-grid','hierarchical-selector','dependent-selector','activity-timeline','exception-alert-panel'].indexOf(TEMPLATE_KIND)!==-1; }
  getConfig() { return this.getConfigFromProps(this.props); }
  getConfigFromProps(props: any) { const params=(props&&props.params)||{}; return {
    titleText:this.toString(params.titleText)||TEMPLATE_TITLE,
    subtitleText:this.toString(params.subtitleText)||'Choose a parent value, then a dependent child value.',
    emptyText:this.toString(params.emptyText)||'No records to display.',
    dataListId:this.toString(params.dataListId||params.sourceListId||params.targetListId),
    recordIdValue:this.toString(params.recordIdValue),
    recordIdField:this.toString(params.recordIdField),
    relationField:this.toString(params.relationField),
    relationValue:this.toString(params.relationValue),
    displayedFields:this.parseStringArray(params.displayedFieldsJson||params.displayedFields),
    eventDateField:this.toString(params.eventDateField||params.dateField||params.sortField),
    actorField:this.toString(params.actorField),
    titleField:this.toString(params.titleField||params.labelField||params.eventTitleField),
    statusField:this.toString(params.statusField||params.decisionField||params.severityField),
    commentFieldName:this.toString(params.commentField||params.descriptionField),
    idField:this.toString(params.idField)||'ListDataID',
    parentField:this.toString(params.parentField),
    childField:this.toString(params.childField),
    labelField:this.toString(params.labelField||params.titleField)||'Title',
    valueField:this.toString(params.valueField)||'ListDataID',
    filterExpression:this.resolveParameterValue(params.filterExpression),
    maxItems:Math.max(1,this.toNumber(params.maxItems||params.maxRecords||params.maxResults,8)),
    displayType:this.toString(params.displayType)||'card',
    saveMode:this.toString(params.saveMode)||'json',
    delimiter:this.toString(params.delimiter)||',',
    placeholderText:this.toString(params.placeholderText)||'Type and press Enter',
    allowDuplicates:this.toBoolean(params.allowDuplicates,false),
    maxTags:Math.max(1,this.toNumber(params.maxTags||params.maxItems,20)),
    items:this.parseChecklistItems(params.itemsJson),
    requireAllChecked:this.toBoolean(params.requireAllChecked,true),
    showProgress:this.toBoolean(params.showProgress,true),
    multiSelect:this.toBoolean(params.multiSelect,false),
    rules:this.parseRules(params.rulesJson),
    showSeverity:this.toBoolean(params.showSeverity,true),
    saveTarget:this.resolveWriteTarget('saveToField',params.saveToField),
    selectedItemsTarget:this.resolveWriteTarget('selectedItemsField',params.selectedItemsField),
    parentTarget:this.resolveWriteTarget('parentSaveToField',params.parentSaveToField),
    childTarget:this.resolveWriteTarget('childSaveToField',params.childSaveToField),
  }; }
  getConfigKey(props:any) { try { return JSON.stringify((props&&props.params)||{}); } catch(error) { return String(new Date().getTime()); } }
  loadData() { const config=this.getConfig(); if(!config.dataListId){return;} const client=this.props.context&&this.props.context.modules&&this.props.context.modules.yeeSDKClient; if(!client||!client.lists||typeof client.lists.queryItems!=='function'){this.setState({errorText:'Data list service is not available'});return;} const fields=[config.recordIdField,config.relationField,config.eventDateField,config.actorField,config.titleField,config.statusField,config.commentFieldName,config.idField,config.parentField,config.childField,config.labelField,config.valueField].concat(config.displayedFields).filter(Boolean); this.setState({loading:true,errorText:''}); this.queryList(client,config.dataListId,fields,Math.max(50,config.maxItems*10),config.filterExpression).then((rows)=>{if(this.mountedFlag){this.setState({loading:false,rows:this.filterRows(rows,config)});}}).catch(()=>{if(this.mountedFlag){this.setState({loading:false,errorText:'Unable to load records'});}}); }
  filterRows(rows:any[],config:any) { let output=rows||[]; if(config.relationField&&config.relationValue){output=output.filter((r)=>this.toString(this.readField(r,config.relationField))===config.relationValue);} if(config.recordIdField&&config.recordIdValue){output=output.filter((r)=>this.toString(this.readField(r,config.recordIdField))===config.recordIdValue);} if(config.eventDateField){output=output.slice().sort((a,b)=>{const ad=this.parseDate(this.readField(a,config.eventDateField));const bd=this.parseDate(this.readField(b,config.eventDateField));return (bd?bd.getTime():0)-(ad?ad.getTime():0);});} return output.slice(0,config.maxItems); }
  readInitialArray(config:any) { const raw=this.readTarget(config.saveTarget); const parsed=this.safeParseJson(raw); if(Array.isArray(parsed)){return parsed.map((x)=>this.toString(x)).filter(Boolean);} return this.toString(raw).split(config.delimiter||',').map((x)=>x.trim()).filter(Boolean); }
  readInitialChecklist(config:any) { const raw=this.readTarget(config.saveTarget); const parsed=this.safeParseJson(raw); const map:any={}; if(Array.isArray(parsed)){parsed.forEach((x:any)=>{if(x&&typeof x==='object'){map[this.toString(x.id||x.value||x.label)]=x.checked===true;}});} return map; }
  parseStringArray(value:any) { const parsed=this.safeParseJson(value); if(Array.isArray(parsed)){return parsed.map((x)=>this.toString(x)).filter(Boolean);} return this.toString(value).split(',').map((x)=>x.trim()).filter(Boolean); }
  parseChecklistItems(value:any) { const parsed=this.safeParseJson(value); const src=Array.isArray(parsed)?parsed:[]; return src.map((item:any,index:number)=>{ if(typeof item==='string'){return {id:item,label:item,required:true};} return {id:this.toString(item.id||item.value||item.key)||'item-'+index,label:this.toString(item.label||item.title||item.name)||('Item '+(index+1)),required:this.toBoolean(item.required,true),helper:this.toString(item.helper||item.description)}; }); }
  parseRules(value:any) { const parsed=this.safeParseJson(value); return Array.isArray(parsed)?parsed:[]; }
  persistTags(tags:string[],config:any) { const clean=config.allowDuplicates?tags:this.unique(tags); const value=config.saveMode==='delimiter'?clean.join(config.delimiter):JSON.stringify(clean); this.writeTarget(config.saveTarget,value); this.setState({tags:clean,textValue:''}); }
  persistChecklist(map:any,config:any) { const value=config.items.map((item:any)=>({id:item.id,label:item.label,checked:map[item.id]===true,required:item.required===true})); this.writeTarget(config.saveTarget,JSON.stringify(value)); const required=config.items.filter((x:any)=>x.required!==false); const done=required.filter((x:any)=>map[x.id]===true).length; this.setState({checklist:map,validationMessage:config.requireAllChecked&&done<required.length?'Complete all required checklist items before submitting.':''}); }
  unique(values:string[]) { const seen:any={}; const out:string[]=[]; values.forEach((v)=>{const s=this.toString(v); const k=s.toLowerCase(); if(s&&!seen[k]){seen[k]=true;out.push(s);}}); return out; }

  resolveParameterValue(value: any): any {
    if (value === undefined || value === null) { return ''; }
    if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' || Array.isArray(value)) { return value; }
    if (typeof value === 'object') {
      const preferred = this.firstDefined([value.value,value.Value,value.data,value.Data,value.result,value.Result,value.key,value.Key,value.id,value.Id,value.fieldId,value.FieldId,value.fieldName,value.FieldName,value.label,value.Label,value.name,value.Name]);
      if (preferred !== undefined) { return this.resolveParameterValue(preferred); }
    }
    return value;
  }
  toString(value: any): string {
    const resolved = this.resolveParameterValue(value);
    if (resolved === undefined || resolved === null) { return ''; }
    if (typeof resolved === 'string' || typeof resolved === 'number' || typeof resolved === 'boolean') { return String(resolved).trim(); }
    if (Array.isArray(resolved)) { return resolved.map((item) => this.toString(item)).filter(Boolean).join(', '); }
    if (typeof resolved === 'object') {
      const preferred = this.firstDefined([resolved.value,resolved.Value,resolved.text,resolved.Text,resolved.display,resolved.Display,resolved.label,resolved.Label,resolved.name,resolved.Name,resolved.title,resolved.Title,resolved.key,resolved.Key]);
      if (preferred !== undefined) { return this.toString(preferred); }
      try { return JSON.stringify(resolved); } catch (error) { return ''; }
    }
    return '';
  }
  toBoolean(value: any, fallback: boolean): boolean {
    const resolved = this.resolveParameterValue(value);
    if (resolved === undefined || resolved === null || resolved === '') { return fallback; }
    if (typeof resolved === 'boolean') { return resolved; }
    if (typeof resolved === 'number') { return resolved !== 0; }
    const text = this.toString(resolved).toLowerCase();
    if (['true','1','yes','y','on'].indexOf(text) !== -1) { return true; }
    if (['false','0','no','n','off'].indexOf(text) !== -1) { return false; }
    return fallback;
  }
  toNumber(value: any, fallback: number): number { const parsed = Number(this.toString(value).replace(/,/g,'')); return isNaN(parsed) ? fallback : parsed; }
  safeParseJson(value: any): any { if (Array.isArray(value) || (value && typeof value === 'object')) { return value; } const text=this.toString(value); if(!text){return null;} try{return JSON.parse(text);}catch(error){return null;} }
  firstDefined(values: any[]) { for (let i=0;i<values.length;i+=1){ if(values[i]!==undefined && values[i]!==null){ return values[i]; } } return undefined; }
  readObjectValue(source: any, key: string): any { if(!source || typeof source !== 'object' || !key){return undefined;} if(source[key]!==undefined){return source[key];} const wanted=key.toLowerCase(); const keys=Object.keys(source); for(let i=0;i<keys.length;i+=1){ if(keys[i].toLowerCase()===wanted){ return source[keys[i]]; } } return undefined; }
  normalizeCell(value: any): any { if(value===undefined||value===null){return value;} if(typeof value==='string'||typeof value==='number'||typeof value==='boolean'){return value;} if(Array.isArray(value)){return value.map((item)=>this.toString(this.normalizeCell(item))).filter(Boolean).join(', ');} if(typeof value==='object'){ return this.firstDefined([value.value,value.Value,value.fieldValue,value.FieldValue,value.dataValue,value.DataValue,value.valueText,value.ValueText,value.text,value.Text,value.display,value.Display,value.label,value.Label,value.name,value.Name,value.title,value.Title]); } return value; }
  readField(row: any, field: string): any { if(!row||!field){return undefined;} const direct=this.readObjectValue(row,field); if(direct!==undefined){return this.normalizeCell(direct);} const containers=[row.values,row.Values,row.fields,row.Fields,row.data,row.Data,row.item,row.Item]; for(let i=0;i<containers.length;i+=1){ const v=this.readObjectValue(containers[i],field); if(v!==undefined){return this.normalizeCell(v);} } const arrays=[row.fieldValues,row.FieldValues,row.fieldsValues,row.FieldsValues]; for(let g=0;g<arrays.length;g+=1){ const arr=arrays[g]; if(Array.isArray(arr)){ for(let i=0;i<arr.length;i+=1){ const e=arr[i]||{}; const k=this.toString(this.firstDefined([e.fieldId,e.FieldId,e.fieldID,e.FieldID,e.fieldCode,e.FieldCode,e.name,e.Name,e.key,e.Key])); if(k && k.toLowerCase()===field.toLowerCase()){ return this.normalizeCell(this.firstDefined([e.value,e.Value,e.fieldValue,e.FieldValue,e.dataValue,e.DataValue,e.text,e.Text])); } } } } return undefined; }
  extractRows(response: any): any[] { if(!response){return [];} if(Array.isArray(response)){return response;} const candidates=[response.items,response.Items,response.data,response.Data,response.rows,response.Rows,response.records,response.Records,response.list,response.List,response.result,response.Result,response.value,response.Value]; for(let i=0;i<candidates.length;i+=1){ if(Array.isArray(candidates[i])){return candidates[i];} if(candidates[i]&&typeof candidates[i]==='object'){ const nested=this.extractRows(candidates[i]); if(nested.length>0){return nested;} } } return []; }
  queryList(client: any, listId: string, fields: string[], pageSize: number, filter: any): Promise<any[]> { const selected=['ListDataID','ListDataId'].concat(fields||[]); const parsed=this.safeParseJson(filter); const filterValue=parsed!==null&&parsed!==undefined?parsed:filter; const payload:any={listId:listId,dataListId:listId,fields:selected,fieldIds:selected,selectedFields:selected,pageIndex:1,pageNo:1,pageSize:pageSize}; if(filterValue!==undefined&&filterValue!==null&&this.toString(filterValue)!==''){payload.filters=filterValue;payload.filter=filterValue;payload.where=filterValue;payload.filterExpression=filterValue;} const payload2:any={}; Object.keys(payload).forEach((k)=>{payload2[k]=payload[k];}); delete payload2.listId; delete payload2.dataListId; const attempts=[()=>client.lists.queryItems(payload),()=>client.lists.queryItems(listId,payload2),()=>client.lists.queryItems(listId,payload),()=>client.lists.queryItems(listId,payload2,{})]; const run=(index:number):Promise<any[]>=>{ if(index>=attempts.length){return Promise.resolve([]);} let result:any; try{result=attempts[index]();}catch(error){return run(index+1);} return Promise.resolve(result).then((response)=>{const rows=this.extractRows(response); if(rows.length===0&&index<attempts.length-1){return run(index+1);} return rows;}).catch(()=>run(index+1)); }; return run(0); }
  parseDate(value: any): Date | null { const cell=this.normalizeCell(value); if(cell===undefined||cell===null||cell===''){return null;} if(cell instanceof Date && !isNaN(cell.getTime())){return cell;} if(typeof cell==='number'){ if(cell>100000000000){const d=new Date(cell);return isNaN(d.getTime())?null:d;} if(cell>1000000000){const d=new Date(cell*1000);return isNaN(d.getTime())?null:d;} if(cell>20000&&cell<80000){const d=new Date(Math.round((cell-25569)*86400*1000));return isNaN(d.getTime())?null:d;} } const text=this.toString(cell); const iso=/^(\d{4})-(\d{1,2})-(\d{1,2})/.exec(text); if(iso){return new Date(Number(iso[1]),Number(iso[2])-1,Number(iso[3]));} const parsed=new Date(text); return isNaN(parsed.getTime())?null:parsed; }
  formatDate(value: any): string { const d=this.parseDate(value); if(!d){return this.toString(value);} return d.getFullYear()+'-'+this.pad(d.getMonth()+1)+'-'+this.pad(d.getDate()); }
  pad(value:number){return value<10?'0'+value:String(value);}


  resolveWriteTarget(paramName: string, rawValue: any): any { const configured=(this.props&&this.props.configuredOutputTargets)||{}; const candidates=this.mergeCandidates(this.collectTargets(configured[paramName]),this.collectTargets(rawValue)).filter((x)=>this.isLikelyTarget(x)); return {paramName:paramName,candidates:candidates,displayValue:candidates[0]||this.toString(rawValue)}; }
  collectTargets(rawValue:any): string[]{ const out:string[]=[]; const seen:any={}; const add=(v:any)=>{const s=this.toString(v); if(s&&!seen[s]){seen[s]=true;out.push(s);}}; const visit=(v:any,d:number)=>{ if(v===undefined||v===null||d>4){return;} if(typeof v==='string'||typeof v==='number'||typeof v==='boolean'){add(v);return;} if(Array.isArray(v)){v.forEach((x)=>visit(x,d+1));return;} if(typeof v!=='object'){return;} ['fieldId','FieldId','fieldName','FieldName','variableId','VariableId','variableName','VariableName','tempVariableId','TempVariableId','tempVariableName','TempVariableName','id','Id','key','Key','name','Name','code','Code','label','Label','title','Title','path','Path','value','Value'].forEach((k)=>{if(v[k]!==undefined&&v[k]!==null){add(v[k]);}}); ['target','Target','binding','Binding','field','Field','variable','Variable','tempVariable','TempVariable','data','Data','meta','Meta','metadata','Metadata'].forEach((k)=>{if(v[k]!==undefined&&v[k]!==null){visit(v[k],d+1);}}); }; visit(rawValue,0); return out; }
  mergeCandidates(a:string[],b:string[]){const out:string[]=[];const seen:any={};(a||[]).concat(b||[]).forEach((x)=>{const s=this.toString(x);if(s&&!seen[s]){seen[s]=true;out.push(s);}});return out;}
  isLikelyTarget(value:any){const s=this.toString(value); if(!s||s==='[]'||s==='{}'){return false;} if(s.charAt(0)==='['||s.charAt(0)==='{'){return false;} if(s.indexOf('":')!==-1||s.indexOf('\",\"')!==-1){return false;} return s.length<=160;}
  writeTarget(target:any,value:any){const candidates=target&&target.candidates?target.candidates:[]; for(let i=0;i<candidates.length;i+=1){ if(this.writeValue(candidates[i],value)){return true;} } return false;}
  writeValue(name:string,value:any){const context=this.props.context||{}; const fieldsValues=this.props.fieldsValues||{}; const hosts=[context,context.formContext,context.pageContext,context.variableContext,context.variables,context.tempVariables,context.runtimeContext,context.dataContext].filter(Boolean); const methods=['setFieldValue','setFormFieldValue','setFieldData','setValue','setVariableValue','setVariable','setTempVariableValue','setTempVariable','updateFieldValue','updateVariableValue']; for(let h=0;h<hosts.length;h+=1){for(let m=0;m<methods.length;m+=1){const fn=hosts[h][methods[m]]; if(typeof fn==='function'){try{const result=fn.call(hosts[h],name,value); if(result!==false){return true;}}catch(error){}}}} const objectMethods=['setFieldsValue','setFieldValues','setValues','setVariables','setTempVariables']; for(let h=0;h<hosts.length;h+=1){for(let m=0;m<objectMethods.length;m+=1){const fn=hosts[h][objectMethods[m]]; if(typeof fn==='function'){try{const payload:any={};payload[name]=value;const result=fn.call(hosts[h],payload); if(result!==false){return true;}}catch(error){}}}} fieldsValues[name]=value; return true;}
  readTarget(target:any){const candidates=target&&target.candidates?target.candidates:[]; const context=this.props.context||{}; const fieldsValues=this.props.fieldsValues||{}; for(let i=0;i<candidates.length;i+=1){const k=candidates[i]; if(fieldsValues&&fieldsValues[k]!==undefined){return fieldsValues[k];} if(context&&typeof context.getFieldValue==='function'){try{const v=context.getFieldValue(k); if(v!==undefined&&v!==null&&this.toString(v)!==''){return v;}}catch(error){}}} return '';}

  render() { const config=this.getConfig(); return <div className="yft-panel"><style>{this.getStyles()}</style><div className="yft-header"><div><div className="yft-title">{config.titleText}</div><div className="yft-subtitle">{config.subtitleText}</div></div>{this.renderBadge(config)}</div>{this.renderBody(config)}</div>; }
  renderBadge(config:any) { if(TEMPLATE_KIND==='multi-entry-tag-input'){return <span className="yft-badge">{this.state.tags.length} / {config.maxTags}</span>;} if(TEMPLATE_KIND==='checklist-compliance-block'){const total=config.items.length; const done=config.items.filter((x:any)=>this.state.checklist[x.id]===true).length; return <span className="yft-badge">{done} / {total}</span>;} if(this.state.rows&&this.state.rows.length){return <span className="yft-badge">{this.state.rows.length} records</span>;} return null; }
  renderBody(config:any) { if(this.state.loading){return <div className="yft-loading">Loading...</div>;} if(this.state.errorText){return <div className="yft-empty yft-error">{this.state.errorText}</div>;} if(TEMPLATE_KIND==='multi-entry-tag-input'){return this.renderTagInput(config);} if(TEMPLATE_KIND==='checklist-compliance-block'){return this.renderChecklist(config);} if(TEMPLATE_KIND==='hierarchical-selector'){return this.renderTree(config);} if(TEMPLATE_KIND==='dependent-selector'){return this.renderDependent(config);} if(TEMPLATE_KIND==='exception-alert-panel'){return this.renderAlerts(config);} if(TEMPLATE_KIND==='related-record-summary-grid'){return this.renderRelated(config);} return this.renderTimeline(config); }
  renderTagInput(config:any) { const readonly=this.props.readonly; return <div><div className="yft-chiprow">{this.state.tags.map((tag:string,index:number)=><span className="yft-chip" key={tag+'-'+index}>{tag}{!readonly?<button type="button" onClick={()=>{const next=this.state.tags.slice();next.splice(index,1);this.persistTags(next,config);}}>x</button>:null}</span>)}</div>{!readonly?<input className="yft-input" value={this.state.textValue} placeholder={config.placeholderText} onChange={(e)=>this.setState({textValue:e.target.value})} onKeyDown={(e)=>{if(e.key==='Enter'){e.preventDefault();const value=this.toString(this.state.textValue);if(value&&this.state.tags.length<config.maxTags){this.persistTags(this.state.tags.concat([value]),config);}}}} />:null}</div>; }
  renderChecklist(config:any) { if(!config.items.length){return <div className="yft-empty">Configure checklist items JSON.</div>;} const total=config.items.length; const done=config.items.filter((x:any)=>this.state.checklist[x.id]===true).length; return <div>{config.items.map((item:any)=><label className="yft-row" key={item.id}><input type="checkbox" checked={this.state.checklist[item.id]===true} disabled={this.props.readonly} onChange={(e)=>{const map:any={};Object.keys(this.state.checklist).forEach((k)=>map[k]=this.state.checklist[k]);map[item.id]=e.target.checked;this.persistChecklist(map,config);}} /> <span className="yft-row-title">{item.label}{item.required!==false?' *':''}</span>{item.helper?<div className="yft-row-meta">{item.helper}</div>:null}</label>)}{config.showProgress?<div className="yft-progress"><span style={{width:(total?Math.round(done/total*100):0)+'%'}} /></div>:null}{this.state.validationMessage?<div className="yft-validation">{this.state.validationMessage}</div>:null}</div>; }
  renderTimeline(config:any) { const rows=this.state.rows||[]; if(!rows.length){return <div className="yft-empty">{config.emptyText}</div>;} return <div className="yft-timeline">{rows.map((row:any,index:number)=><div className="yft-event" key={index}><div className="yft-row-title">{this.toString(this.readField(row,config.titleField))||this.toString(this.readField(row,config.statusField))||'Event'}</div><div className="yft-row-meta">{this.formatDate(this.readField(row,config.eventDateField))} {this.toString(this.readField(row,config.actorField))}</div>{this.toString(this.readField(row,config.commentFieldName))?<div className="yft-row-meta">{this.toString(this.readField(row,config.commentFieldName))}</div>:null}</div>)}</div>; }
  renderRelated(config:any) { const rows=this.state.rows||[]; if(!rows.length){return <div className="yft-empty">{config.emptyText}</div>;} const fields=config.displayedFields.length?config.displayedFields:[config.titleField,config.statusField,config.commentFieldName].filter(Boolean); if(config.displayType==='table'){return <table className="yft-table"><thead><tr>{fields.map((f:string)=><th key={f}>{f}</th>)}</tr></thead><tbody>{rows.map((row:any,i:number)=><tr key={i}>{fields.map((f:string)=><td key={f}>{this.toString(this.readField(row,f))}</td>)}</tr>)}</tbody></table>;} return <div className="yft-grid">{rows.map((row:any,i:number)=><div className="yft-card" key={i}>{fields.map((f:string,index:number)=><div key={f} className={index===0?'yft-row-title':'yft-row-meta'}>{index>0?f+': ':''}{this.toString(this.readField(row,f))}</div>)}</div>)}</div>; }
  renderTree(config:any) { const rows=this.state.rows||[]; if(!rows.length){return <div className="yft-empty">{config.emptyText}</div>;} const byParent:any={}; rows.forEach((r:any)=>{const p=this.toString(this.readField(r,config.parentField)); if(!byParent[p]){byParent[p]=[];} byParent[p].push(r);}); const renderNodes=(parent:string):any=> (byParent[parent]||[]).map((row:any,index:number)=>{const value=this.toString(this.readField(row,config.valueField)||this.readField(row,config.idField)); const label=this.toString(this.readField(row,config.labelField))||value; const selected=this.state.selected.indexOf(value)!==-1; return <div className="yft-tree-node" key={value||index}><button type="button" className={selected?'yft-btn yft-btn-active':'yft-btn'} onClick={()=>{let next=config.multiSelect?this.state.selected.slice():[]; const pos=next.indexOf(value); if(pos===-1){next.push(value);}else{next.splice(pos,1);} this.writeTarget(config.saveTarget,JSON.stringify(next)); this.setState({selected:next});}}>{label}</button><div className="yft-tree-children">{renderNodes(value)}</div></div>;}); return <div>{renderNodes('')}</div>; }
  renderDependent(config:any) { const rows=this.state.rows||[]; const parents=this.unique(rows.map((r:any)=>this.toString(this.readField(r,config.parentField))).filter(Boolean)); const children=this.unique(rows.filter((r:any)=>this.toString(this.readField(r,config.parentField))===this.state.parentValue).map((r:any)=>this.toString(this.readField(r,config.childField))).filter(Boolean)); return <div className="yft-grid"><div><div className="yft-muted">Parent</div><select className="yft-select" value={this.state.parentValue} disabled={this.props.readonly} onChange={(e)=>{this.setState({parentValue:e.target.value,childValue:''});this.writeTarget(config.parentTarget,e.target.value);this.writeTarget(config.childTarget,'');}}><option value="">Select</option>{parents.map((p)=><option key={p} value={p}>{p}</option>)}</select></div><div><div className="yft-muted">Child</div><select className="yft-select" value={this.state.childValue} disabled={this.props.readonly||!this.state.parentValue} onChange={(e)=>{this.setState({childValue:e.target.value});this.writeTarget(config.childTarget,e.target.value);}}><option value="">Select</option>{children.map((c)=><option key={c} value={c}>{c}</option>)}</select></div></div>; }
  renderAlerts(config:any) { const rows=this.state.rows||[]; const rules=config.rules; let alerts:any[]=[]; if(rules.length){ rows.forEach((row:any)=>{ rules.forEach((rule:any)=>{const field=this.toString(rule.field); const op=this.toString(rule.operator||'equals'); const expected=this.toString(rule.value); const actual=this.toString(this.readField(row,field)); const hit=op==='notEmpty'?actual!=='':op==='empty'?actual==='':op==='contains'?actual.indexOf(expected)!==-1:actual===expected; if(hit){alerts.push({title:this.toString(rule.title)||this.toString(this.readField(row,config.titleField))||'Alert',message:this.toString(rule.message)||field+' '+op+' '+expected,severity:this.toString(rule.severity)||'warning'});} }); });} else { alerts=rows.map((row:any)=>({title:this.toString(this.readField(row,config.titleField))||'Alert',message:this.toString(this.readField(row,config.commentFieldName)),severity:this.toString(this.readField(row,config.statusField))||'warning'})); } if(!alerts.length){return <div className="yft-empty">{config.emptyText}</div>;} return <div>{alerts.slice(0,config.maxItems).map((a:any,i:number)=><div className={'yft-alert yft-alert-'+(a.severity||'warning')} key={i}><div className="yft-row-title">{a.title}</div>{a.message?<div className="yft-row-meta">{a.message}</div>:null}</div>)}</div>; }
  getStyles() { return [".yft-panel{box-sizing:border-box;width:100%;min-width:0;background:#fff;border:1px solid #e5eaf3;border-radius:8px;padding:16px;color:#111827;font-family:Inter,-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif;box-shadow:0 1px 2px rgba(16,24,40,.04);}", ".yft-panel *{box-sizing:border-box;}", ".yft-header{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:14px;}", ".yft-title{font-size:16px;line-height:22px;font-weight:700;color:#101828;}", ".yft-subtitle{font-size:12px;line-height:18px;color:#667085;margin-top:3px;}", ".yft-badge{border:1px solid #e5eaf3;background:#f8fafc;border-radius:999px;padding:3px 9px;font-size:12px;color:#475467;}", ".yft-empty,.yft-loading{min-height:120px;display:flex;align-items:center;justify-content:center;text-align:center;border:1px dashed #d8deea;border-radius:8px;background:#f8fafc;color:#667085;font-size:13px;line-height:20px;padding:20px;}", ".yft-error{color:#b42318;background:#fff7f6;border-color:#fecdca;}", ".yft-row{border:1px solid #e5eaf3;border-radius:8px;padding:10px 12px;background:#fff;margin-bottom:8px;}", ".yft-row-title{font-size:13px;font-weight:700;line-height:18px;color:#101828;}", ".yft-row-meta{font-size:12px;line-height:18px;color:#667085;margin-top:3px;}", ".yft-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;}", ".yft-card{border:1px solid #e5eaf3;border-radius:8px;background:#fff;padding:12px;}", ".yft-table{width:100%;border-collapse:separate;border-spacing:0;border:1px solid #e5eaf3;border-radius:8px;overflow:hidden;}", ".yft-table th{background:#f8fafc;color:#475467;font-size:12px;text-align:left;padding:9px;border-bottom:1px solid #e5eaf3;}", ".yft-table td{font-size:13px;color:#344054;padding:9px;border-bottom:1px solid #eef2f7;}", ".yft-chiprow{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:10px;}", ".yft-chip{display:inline-flex;align-items:center;gap:6px;border:1px solid #c8dafc;background:#edf4ff;color:#0d4fb3;border-radius:999px;padding:5px 9px;font-size:13px;line-height:18px;}", ".yft-chip button{appearance:none;border:0;background:transparent;color:#0d4fb3;cursor:pointer;font-size:15px;line-height:15px;padding:0;}", ".yft-input,.yft-textarea,.yft-select{width:100%;border:1px solid #d8deea;border-radius:8px;padding:9px 10px;font-size:13px;line-height:20px;color:#101828;background:#fff;outline:none;font-family:inherit;}", ".yft-textarea{min-height:86px;resize:vertical;}", ".yft-input:focus,.yft-textarea:focus,.yft-select:focus{border-color:#146ff6;box-shadow:0 0 0 3px rgba(20,111,246,.12);}", ".yft-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px;}", ".yft-btn{appearance:none;border:1px solid #d8deea;background:#fff;color:#344054;border-radius:8px;padding:8px 11px;font-size:13px;font-weight:650;cursor:pointer;}", ".yft-btn-active{border-color:#146ff6;background:#edf4ff;color:#0d4fb3;}", ".yft-btn-primary{border-color:#146ff6;background:#146ff6;color:#fff;}", ".yft-validation{margin-top:10px;border:1px solid #fecdca;background:#fff7f6;color:#b42318;border-radius:8px;padding:9px 10px;font-size:12px;line-height:18px;}", ".yft-progress{height:8px;background:#eef2f7;border-radius:999px;overflow:hidden;margin-top:10px;}", ".yft-progress span{display:block;height:100%;background:#146ff6;border-radius:999px;}", ".yft-timeline{position:relative;padding-left:18px;}", ".yft-timeline:before{content:\"\";position:absolute;left:5px;top:4px;bottom:4px;width:2px;background:#e5eaf3;}", ".yft-event{position:relative;margin-bottom:12px;border:1px solid #e5eaf3;border-radius:8px;padding:10px 12px;background:#fff;}", ".yft-event:before{content:\"\";position:absolute;left:-17px;top:14px;width:10px;height:10px;border-radius:999px;background:#146ff6;box-shadow:0 0 0 4px #edf4ff;}", ".yft-alert{border:1px solid #fecdca;background:#fff7f6;border-radius:8px;padding:10px 12px;margin-bottom:8px;color:#7a271a;}", ".yft-alert-warning{border-color:#fedf89;background:#fffbeb;color:#7a4b00;}", ".yft-alert-info{border-color:#b2ddff;background:#eff8ff;color:#1849a9;}", ".yft-alert-success{border-color:#abefc6;background:#f0fdf4;color:#067647;}", ".yft-tree-node{border:1px solid #e5eaf3;border-radius:8px;padding:9px 10px;margin-bottom:8px;background:#fff;}", ".yft-tree-children{margin-left:18px;margin-top:8px;}", ".yft-muted{color:#667085;font-size:12px;line-height:18px;}", "@media(max-width:640px){.yft-panel{padding:14px}.yft-header{flex-direction:column}.yft-table{display:block;overflow-x:auto}.yft-actions{flex-direction:column}.yft-btn{width:100%;text-align:center}}"].join(''); }
}
