All files / src/utils/components/question-bank/question-content/import ImportResults.jsx

85.71% Statements 6/7
69.44% Branches 25/36
50% Functions 1/2
85.71% Lines 6/7

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                                                        6x 6x 6x   6x     6x   6x                                                                                                                                            
import * as React from "react";
import Box from "@mui/material/Box";
import Paper from "@mui/material/Paper";
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
import Alert from "@mui/material/Alert";
import AlertTitle from "@mui/material/AlertTitle";
import Button from "@mui/material/Button";
import CheckCircleOutlineIcon from "@mui/icons-material/CheckCircleOutline";
 
/**
 * What actually happened, after a commit.
 *
 * A partial result is reported as a partial result. The API returns `partial: true` when
 * some questions were written and others were not, and saying "done" in that case would
 * leave a teacher unable to tell which questions reached their bank.
 *
 * The same principle governs the two reasons a question can be found and not created.
 * "Removed during review" was accurate when the teacher deleting a row was the only way
 * to lose one. Duplicate skipping added a second reason, and it landed in the same
 * derived bucket -- so an import of a question already in the bank told the teacher they
 * had removed something they never touched. The counts are separated here because the
 * two need different actions from them: one is their own edit, the other is a question
 * they already own.
 *
 * Developer: Allan Ninal
 */
export default function ImportResults({ committed, rowErrors = [], questionsFound = 0, onDone }) {
    const created = committed?.created ?? 0;
    const failed = committed?.failed ?? 0;
    const partial = Boolean(committed?.partial);
    // Reported by the API, not derived: it knows WHY it skipped these.
    const skippedDuplicates = committed?.skipped_duplicates ?? 0;
    // What is left over once the explained outcomes are accounted for is what the
    // teacher actually removed on the review screen.
    const removedInReview = Math.max(questionsFound - created - failed - skippedDuplicates, 0);
 
    return (
        <Box data-testid="import-results">
            <Paper variant="outlined" sx={{ p: 3 }}>
                <Stack spacing={2}>
                    {partial ? (
                        <Alert severity="warning" data-testid="partial-result">
                            <AlertTitle>Some questions were not added</AlertTitle>
                            {created} of {created + failed} were added to your question bank. The rest were not —
                            nothing was lost, but you will need to try those again.
                        </Alert>
                    ) : (
                        <Alert
                            // Nothing added is not a success, and a green tick over
                            // "Added 0 questions" reads as one.
                            severity={created > 0 ? "success" : "info"}
                            icon={created > 0 ? <CheckCircleOutlineIcon fontSize="inherit" /> : undefined}
                            data-testid="success-result"
                        >
                            <AlertTitle>
                                Added {created} question{created === 1 ? "" : "s"}
                            </AlertTitle>
                            {created > 0
                                ? "They are in your question bank now."
                                : "Nothing new was added to your question bank."}
                        </Alert>
                    )}
 
                    {failed > 0 && (
                        <Box>
                            <Typography variant="subtitle2" gutterBottom>
                                Not added
                            </Typography>
                            {(committed?.failures ?? []).map((failure, i) => (
                                <Typography key={i} variant="body2" color="text.secondary">
                                    Question {failure.question}: {failure.message}
                                </Typography>
                            ))}
                        </Box>
                    )}
 
                    {rowErrors.length > 0 && (
                        <Typography variant="body2" color="text.secondary">
                            {rowErrors.length} row{rowErrors.length === 1 ? "" : "s"} in your file could not be read and
                            were not imported.
                        </Typography>
                    )}
 
                    {skippedDuplicates > 0 && (
                        <Typography variant="body2" color="text.secondary" data-testid="skipped-duplicates">
                            {skippedDuplicates} question{skippedDuplicates === 1 ? " was" : "s were"} skipped
                            {skippedDuplicates === 1 ? " because it is" : " because they are"} already in your bank.
                        </Typography>
                    )}
 
                    {removedInReview > 0 && (
                        <Typography variant="body2" color="text.secondary" data-testid="removed-in-review">
                            {removedInReview} question{removedInReview === 1 ? " was" : "s were"} removed during review.
                        </Typography>
                    )}
 
                    <Stack direction="row" justifyContent="flex-end">
                        <Button variant="contained" onClick={onDone} data-testid="results-done">
                            Back to my questions
                        </Button>
                    </Stack>
                </Stack>
            </Paper>
        </Box>
    );
}