All files / src/utils/components/feature-access TeacherMadeAssignmentQuotaBanner.jsx

100% Statements 16/16
97.91% Branches 47/48
100% Functions 1/1
100% Lines 15/15

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                              3x 50x 50x   50x 1x             49x   48x 48x 50x 50x       50x   50x   50x               50x               50x                                                                                                                          
import Alert from "@mui/material/Alert";
import AlertTitle from "@mui/material/AlertTitle";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import LinearProgress from "@mui/material/LinearProgress";
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
import { alpha, useTheme } from "@mui/material/styles";
import useTeacherMadeAssignmentQuota from "@/utils/hooks/useTeacherMadeAssignmentQuota";
import SoftChip from "@/utils/components/feature-access/SoftChip";
 
/**
 * Quota summary for the class Assignments tab (limit is per class, per teacher).
 * @param {number|null} classAssignmentCount Assignments in the current class; omit for plan-only display.
 */
const TeacherMadeAssignmentQuotaBanner = ({ classAssignmentCount = null, sx }) => {
    const theme = useTheme();
    const { quota, ui, loading, openUpgradeOnBlock } = useTeacherMadeAssignmentQuota(classAssignmentCount);
 
    if (loading && !quota) {
        return (
            <Box sx={{ width: "100%", ...sx }}>
                <LinearProgress />
            </Box>
        );
    }
 
    if (!quota) return null;
 
    const isUnlimited = quota.max_assignments === null;
    const hasLimit = quota.max_assignments != null && quota.max_assignments > 0;
    const usagePercent = hasLimit ? Math.min(100, (quota.current_count / quota.max_assignments) * 100) : 0;
    const atLimit = hasLimit && !quota.can_create;
    // A full class also reports can_create: false, so `atLimit` has to win here —
    // otherwise a teacher who used all 10 slots was told assignments were not part
    // of their plan, and the at-limit copy below was unreachable.
    const isBlocked = !atLimit && (quota.max_assignments === 0 || !quota.can_create);
 
    const severity = isBlocked || atLimit ? "warning" : isUnlimited ? "success" : "info";
 
    const title = isBlocked
        ? "Assignment creation not included in your plan"
        : atLimit
          ? "Assignment limit reached in this class"
          : isUnlimited
            ? "Unlimited assignment creation"
            : "Teacher-made assignments (this class)";
 
    const description = isBlocked
        ? "Upgrade your district plan to create assignments in your classes."
        : atLimit
          ? `You have used all ${quota.max_assignments} assignment slots in this class. Delete one to create another, or upgrade for unlimited assignments per class.`
          : isUnlimited
            ? "Your plan allows unlimited teacher-made assignments in each class."
            : `You can create ${quota.remaining ?? 0} more in this class (${quota.current_count} of ${quota.max_assignments} used). Each class has its own limit.`;
 
    return (
        <Alert
            severity={severity}
            variant="outlined"
            sx={{
                width: "100%",
                alignItems: "flex-start",
                bgcolor: alpha(theme.palette[severity].main, theme.palette.mode === "light" ? 0.06 : 0.12),
                ...sx,
            }}
            action={
                ui.upgradeRequired ? (
                    <Button
                        color="warning"
                        size="small"
                        variant="soft"
                        onClick={openUpgradeOnBlock}
                        sx={{ fontWeight: 600 }}
                    >
                        Upgrade
                    </Button>
                ) : null
            }
        >
            <AlertTitle sx={{ mb: 0.5 }}>{title}</AlertTitle>
            <Stack spacing={1.25}>
                <Typography variant="body2">{description}</Typography>
                {hasLimit && (
                    <Box sx={{ width: "100%", maxWidth: 420 }}>
                        <Box sx={{ display: "flex", justifyContent: "space-between", mb: 0.5 }}>
                            <Typography variant="caption" color="text.secondary">
                                This class
                            </Typography>
                            <SoftChip
                                size="sm"
                                color={atLimit ? "warning" : "info"}
                                label={`${quota.current_count} / ${quota.max_assignments}`}
                            />
                        </Box>
                        <LinearProgress
                            variant="determinate"
                            value={usagePercent}
                            color={atLimit ? "warning" : "primary"}
                            sx={{ height: 8, borderRadius: 1 }}
                        />
                    </Box>
                )}
                {isUnlimited && (
                    <SoftChip
                        size="sm"
                        color="success"
                        label="No per-class cap on your plan"
                        sx={{ alignSelf: "flex-start" }}
                    />
                )}
            </Stack>
        </Alert>
    );
};
 
export default TeacherMadeAssignmentQuotaBanner;