All files / frontend/src/components/SukohForm/fields StringField.tsx

38.46% Statements 10/26
33.33% Branches 9/27
16.66% Functions 1/6
38.46% Lines 10/26

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101                                  1x 1x 1x     1x       1x         1x   1x       1x                               1x                                                         1x                                    
import { useState } from 'react';
import TextField from '@mui/material/TextField';
import FormItemWrapper from '../components/shared/FormItemWrapper';
import Tip from '../../Tip';
import { FieldAIAssistButton } from '../FieldAIAssistButton';
import { useField } from '../useField';
import type { StringField as StringFieldConfig } from '@quiqr/types';
 
interface Props {
  compositeKey: string;
}
 
/**
 * StringField - text input with optional multiLine support.
 * Includes AI assist integration and insert buttons.
 */
function StringField({ compositeKey }: Props) {
  const { field, value, setValue, meta } = useField<string>(compositeKey);
  const config = field as StringFieldConfig;
  const [localValue, setLocalValue] = useState(value ?? config.default ?? '');
 
  // Sync local value when external value changes
  Iif (value !== undefined && value !== localValue) {
    setLocalValue(value);
  }
 
  const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
    setLocalValue(e.target.value);
    setValue(e.target.value, 0);
  };
 
  const iconButtons: React.ReactNode[] = [];
 
  Iif (config.tip) {
    iconButtons.push(<Tip key="tip" markdown={config.tip} />);
  }
 
  Iif (config.txtInsertButtons) {
    const insertButtons = config.txtInsertButtons.map((text, idx) => (
      <button
        key={idx}
        onClick={() => {
          setLocalValue(text);
          setValue(text, 250);
        }}
      >
        {text}
      </button>
    ));
    iconButtons.push(<span key="insert-buttons">{insertButtons}</span>);
  }
 
  // New field-level AI assist (template-based)
  Iif (config.field_prompt_templates && config.field_prompt_templates.length > 0) {
    // Determine if we're in a single or collection context
    const isCollection = meta.collectionKey && meta.collectionItemKey;
    
    iconButtons.push(
      <FieldAIAssistButton
        key="field-ai-assist"
        fieldKey={config.key}
        fieldType="string"
        fieldContent={localValue}
        availableTemplates={config.field_prompt_templates}
        onReplace={(text: string) => {
          setLocalValue(text);
          setValue(text, 0);
        }}
        onAppend={(text: string) => {
          const newValue = localValue ? `${localValue}\n${text}` : text;
          setLocalValue(newValue);
          setValue(newValue, 0);
        }}
        siteKey={meta.siteKey}
        workspaceKey={meta.workspaceKey}
        collectionKey={isCollection ? meta.collectionKey : undefined}
        collectionItemKey={isCollection ? meta.collectionItemKey : undefined}
        singleKey={!isCollection ? meta.collectionItemKey : undefined}
      />
    );
  }
 
  return (
    <FormItemWrapper
      control={
        <TextField
          id={`text-field-${config.key}`}
          onChange={handleChange}
          value={localValue}
          multiline={config.multiLine === true}
          fullWidth
          label={config.title ?? config.key}
        />
      }
      iconButtons={iconButtons}
    />
  );
}
 
export default StringField;