All files / src/components/partner TopicSuggestionForm.tsx

96.55% Statements 28/29
100% Branches 13/13
83.33% Functions 5/6
96.55% Lines 28/29

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 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120                                                        6x               113x 113x 113x 113x 113x   113x 44x 6x 6x 6x       113x 2x 2x 2x 2x     113x 3x 1x 1x   2x 2x 2x 2x 2x   2x       113x                       44x                                                                      
/**
 * TopicSuggestionForm
 * Story 8.2: AC3, AC7 — Task 8
 *
 * MUI Dialog for submitting a new topic suggestion.
 * Fields: Title (required, min 5 chars), Description (optional, max 500 chars).
 */
 
import React, { useState } from 'react';
import {
  Button,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  TextField,
} from '@mui/material';
import { useTranslation } from 'react-i18next';
 
interface Props {
  open: boolean;
  onClose: () => void;
  onSubmit: (title: string, description: string) => Promise<void>;
  initialTitle?: string;
  initialDescription?: string;
  editMode?: boolean;
}
 
export const TopicSuggestionForm: React.FC<Props> = ({
  open,
  onClose,
  onSubmit,
  initialTitle = '',
  initialDescription = '',
  editMode = false,
}) => {
  const { t } = useTranslation('partners');
  const [title, setTitle] = useState(initialTitle);
  const [description, setDescription] = useState(initialDescription);
  const [submitting, setSubmitting] = useState(false);
  const [titleError, setTitleError] = useState('');
 
  React.useEffect(() => {
    if (open) {
      setTitle(initialTitle);
      setDescription(initialDescription);
      setTitleError('');
    }
  }, [open, initialTitle, initialDescription]);
 
  const handleClose = () => {
    setTitle('');
    setDescription('');
    setTitleError('');
    onClose();
  };
 
  const handleSubmit = async () => {
    if (title.trim().length < 5) {
      setTitleError(t('portal.topics.form.titleRequired'));
      return;
    }
    setTitleError('');
    setSubmitting(true);
    try {
      await onSubmit(title.trim(), description.trim());
      handleClose();
    } finally {
      setSubmitting(false);
    }
  };
 
  return (
    <Dialog open={open} onClose={handleClose} fullWidth maxWidth="sm">
      <DialogTitle>
        {editMode ? t('portal.topics.form.editDialogTitle') : t('portal.topics.form.dialogTitle')}
      </DialogTitle>
      <DialogContent>
        <TextField
          autoFocus
          required
          fullWidth
          label={t('common:labels.title')}
          value={title}
          onChange={(e) => setTitle(e.target.value)}
          error={!!titleError}
          helperText={titleError || ''}
          inputProps={{ maxLength: 255 }}
          sx={{ mt: 1, mb: 2 }}
          data-testid="topic-form-title"
        />
        <TextField
          fullWidth
          multiline
          rows={3}
          label={t('portal.topics.form.description')}
          value={description}
          onChange={(e) => setDescription(e.target.value)}
          helperText={t('portal.topics.form.descriptionHint')}
          inputProps={{ maxLength: 500 }}
          data-testid="topic-form-description"
        />
      </DialogContent>
      <DialogActions>
        <Button onClick={handleClose} disabled={submitting}>
          {t('common:actions.cancel')}
        </Button>
        <Button
          variant="contained"
          onClick={handleSubmit}
          disabled={submitting}
          data-testid="topic-form-submit"
        >
          {editMode ? t('portal.topics.form.update') : t('portal.topics.form.submit')}
        </Button>
      </DialogActions>
    </Dialog>
  );
};