All files / src/app/student-page StudentMainPage.jsx

82.35% Statements 14/17
85.71% Branches 6/7
50% Functions 3/6
100% Lines 14/14

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                                          2x 2x 2x 2x         2x   2x 2x 2x 2x           2x 2x 1x       2x   2x                                                
import { Suspense, useEffect } from "react";
import { Outlet } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { Box } from "@mui/material";
 
import { ThemeToggleProvider } from "@/theme/ThemeContext";
import SimpleLoading from "@/utils/components/SimpleLoading";
import ThemePresets from "@/utils/components/theme-sets/presets/ThemePresets";
import IdleChecker from "@/utils/components/idle-checker/IdleChecker";
import Copyright from "@/utils/components/copyright/Copyright";
import GlobalMaintenanceBanner from "@/utils/components/feature-access/GlobalMaintenanceBanner";
import { UpgradeDialogProvider } from "@/utils/components/feature-access/UpgradeDialogProvider";
 
import { _studentGetData } from "@/store/slices/student/StudentData";
import { fetchStudentDistrictFeatures, fetchStudentOrganization } from "@/store/slices/student/StudentFeatureFlags";
import { _fetchStudentTheme } from "@/store/slices/student/StudentThemes";
import { useFeatureFlag } from "@/utils/hooks/useFeatureFlag";
 
import AppLayout from "./layout/AppLayout";
 
export default function StudentMainPage() {
    const { isLoading } = useSelector((state) => state.StudentData);
    const { isLoading: featuresLoading } = useSelector((state) => state.StudentFeatureFlags);
    const { isLoading: themeLoading } = useSelector((state) => state.StudentThemes);
    const dispatch = useDispatch();
 
    // Theme management is a Basic+ tier feature; the backend gates
    // GET /student/account/theme/fetch on require_feature("student.theme_management"),
    // so fetching it for a district without the entitlement is a guaranteed 403.
    const { isEnabled: themeFeatureEnabled } = useFeatureFlag("student.theme_management", undefined, "student");
 
    useEffect(() => {
        dispatch(_studentGetData());
        dispatch(fetchStudentDistrictFeatures());
        dispatch(fetchStudentOrganization());
    }, [dispatch]);
 
    // Fetch the theme only once the district features confirm the entitlement —
    // gating the call avoids a guaranteed 403 for sub-Basic plans. The end state is
    // identical (no entitlement → default theme); this just skips the doomed request.
    useEffect(() => {
        if (themeFeatureEnabled) {
            dispatch(_fetchStudentTheme());
        }
    }, [dispatch, themeFeatureEnabled]);
 
    const loading = isLoading || featuresLoading || themeLoading;
 
    return (
        <ThemeToggleProvider>
            {loading ? (
                <>
                    <SimpleLoading showLoading />
                    <ThemePresets showTheme={false} />
                </>
            ) : (
                <UpgradeDialogProvider userType="student">
                    <AppLayout>
                        <Box sx={{ minHeight: "100vh" }}>
                            <GlobalMaintenanceBanner userType="student" />
                            <Suspense fallback={<SimpleLoading showLoading />}>
                                <Outlet />
                            </Suspense>
                        </Box>
                    </AppLayout>
                    <Copyright />
                    <IdleChecker idleTime={60 * 2} />
                </UpgradeDialogProvider>
            )}
        </ThemeToggleProvider>
    );
}