All files / hooks useClassActivity.ts

97.82% Statements 45/46
87.5% Branches 14/16
100% Functions 10/10
97.67% Lines 42/43

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 1081x   1x 1x   1x                 1x       23x 23x 23x         23x 23x     23x 7x 5x 5x 5x   5x 8x 8x 8x 8x 8x   7x 1x         6x   2x       2x       5x 5x 5x   9x       5x       5x       7x 5x   2x         23x   11x   3x       11x   11x     11x     23x                  
import { useEffect, useState } from "react";
import { UserActivityLogItem } from "../types";
import { getClassActivityByClassId } from "../api/classes";
import { calculateProgress, ProgressData } from "../utils/calculateProgress";
import { UserClass } from "../api/types/user";
import { LogEvent } from "../api/types/event";
 
/**
 * Custom hook to fetch and manage class activity logs.
 * It fetches all activity logs for the provided classes and filters them based on the selected class ID.
 * @param classes - Array of UserClass objects representing the classes to fetch activity for.
 * @param selectedClassId - The ID of the selected class to filter activity logs by.
 * @returns An object containing the following properties:
 */
export const useClassActivity = (
  classes: UserClass[],
  selectedClassId: string | null
) => {
  const [allActivity, setAllActivity] = useState<UserActivityLogItem[]>([]);
  const [classActivity, setClassActivity] = useState<UserActivityLogItem[]>([]);
  const [progressData, setProgressData] = useState<ProgressData>({
    totalAccepted: 0,
    correctSuggestions: 0,
    percentageCorrect: 0,
  });
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
 
  // Fetch all logs only once
  useEffect(() => {
    const fetchAllClassActivity = async () => {
      try {
        setError(null);
        setLoading(true);
 
        const classRequests = classes
          .filter((classInfo) => classInfo.id !== "all")
          .map(async (classInfo) => {
            try {
              const response = await getClassActivityByClassId(classInfo.id!);
              if (response.error) throw new Error(response.error);
 
              if (!Array.isArray(response.data)) {
                throw new Error(
                  "Invalid response: expected an array of activity logs"
                );
              }
 
              return response.data;
            } catch (err) {
              console.error(
                `Failed to fetch for class ${classInfo.classTitle}`,
                err
              );
              return [];
            }
          });
 
        const allLogsArrays = await Promise.all(classRequests);
        const allLogs = allLogsArrays.flat();
        const validLogs = allLogs.filter(
          (log) =>
            log.event === LogEvent.USER_ACCEPT ||
            log.event === LogEvent.USER_REJECT
        );
 
        setAllActivity(validLogs);
      } catch (err) {
        setError(err instanceof Error ? err.message : "Unknown error");
      } finally {
        setLoading(false);
      }
    };
 
    if (classes.length > 0) {
      fetchAllClassActivity();
    } else {
      setLoading(false);
    }
  }, [classes]);
 
  // Filter logs by selected class
  useEffect(() => {
    const filtered =
      selectedClassId && selectedClassId !== "all"
        ? allActivity.filter(
            (log) => log.metadata.userClassId === selectedClassId
          )
        : allActivity;
 
    setClassActivity(filtered);
 
    const progress = calculateProgress(
      selectedClassId === "all" ? allActivity : filtered
    );
    setProgressData(progress);
  }, [selectedClassId, allActivity]);
 
  return {
    allActivity,
    classActivity,
    progressData,
    loading,
    error,
    isEmpty: !loading && classActivity.length === 0,
  };
};