diff --git a/server/controllers/classroom.ts b/server/controllers/classroom.ts
index e78a00193..403efb1de 100644
--- a/server/controllers/classroom.ts
+++ b/server/controllers/classroom.ts
@@ -19,6 +19,27 @@ export const classroomController = new Controller('/classrooms');
* @return {object} Route API JSON response
*/
+classroomController.get({ path: '', userType: UserType.ADMIN }, async (_req: Request, res: Response) => {
+ const villageId = _req.query.villageId as string;
+ try {
+ const classroomRepository = AppDataSource.getRepository(Classroom);
+ let classrooms;
+ if (villageId) {
+ const query = classroomRepository.createQueryBuilder('classroom');
+ query.where('classroom.villageId = :villageId', { villageId });
+
+ classrooms = await query.getMany();
+ //classrooms = await classroomRepository.find({ where: { villageId: +villageId } });
+ } else {
+ classrooms = await classroomRepository.find();
+ }
+ res.sendJSON(classrooms);
+ } catch (e) {
+ console.error(e);
+ res.status(500).sendJSON({ message: 'An error occurred while fetching classrooms' });
+ }
+});
+
classroomController.get({ path: '/:id', userType: UserType.TEACHER }, async (req: Request, res: Response, next: NextFunction) => {
const id = parseInt(req.params.id, 10) || 0;
const classroom = await AppDataSource.getRepository(Classroom).findOne({
diff --git a/src/components/admin/dashboard-statistics/ClassroomStats.tsx b/src/components/admin/dashboard-statistics/ClassroomStats.tsx
index 0932e7138..9fede9382 100644
--- a/src/components/admin/dashboard-statistics/ClassroomStats.tsx
+++ b/src/components/admin/dashboard-statistics/ClassroomStats.tsx
@@ -1,14 +1,24 @@
import React, { useState } from 'react';
+import { Box, Tab, Tabs } from '@mui/material';
+
+import { OneVillageTable } from '../OneVillageTable';
+import TabPanel from './TabPanel';
+import StatsCard from './cards/StatsCard/StatsCard';
import ClassroomDropdown from './filters/ClassroomDropdown';
import CountriesDropdown from './filters/CountriesDropdown';
import PhaseDropdown from './filters/PhaseDropdown';
import VillageDropdown from './filters/VillageDropdown';
-import { mockClassroomsStats } from './mocks/mocks';
import { PelicoCard } from './pelico-card';
import styles from './styles/charts.module.css';
+import { createFamiliesWithoutAccountRows } from './utils/tableCreator';
+import { FamiliesWithoutAccountHeaders } from './utils/tableHeaders';
+import { useGetVillagesStats } from 'src/api/statistics/statistics.get';
+import { useClassrooms } from 'src/services/useClassrooms';
import { useCountries } from 'src/services/useCountries';
import { useVillages } from 'src/services/useVillages';
+import type { ClassroomFilter } from 'types/classroom.type';
+import type { OneVillageTableRow } from 'types/statistics.type';
import type { VillageFilter } from 'types/village.type';
const ClassroomStats = () => {
@@ -16,12 +26,16 @@ const ClassroomStats = () => {
const [selectedVillage, setSelectedVillage] = useState('');
const [selectedClassroom, setSelectedClassroom] = useState();
const [selectedPhase, setSelectedPhase] = useState('4');
- const [options, setOptions] = useState({ countryIsoCode: '' });
+ const [villageFilter, setVillageFilter] = useState({ countryIsoCode: '' });
+ const [classroomFilter, setClassroomFilter] = useState({ villageId: '' });
+ const [value, setValue] = React.useState(0);
const pelicoMessage = 'Merci de sélectionner une classe pour analyser ses statistiques ';
const { countries } = useCountries();
- const { villages } = useVillages(options);
+ const { villages } = useVillages(villageFilter);
+ const villagesStats = useGetVillagesStats(+selectedVillage, +selectedPhase);
+ const { classrooms } = useClassrooms(classroomFilter);
const handleCountryChange = (country: string) => {
setSelectedCountry(country);
@@ -30,21 +44,26 @@ const ClassroomStats = () => {
};
React.useEffect(() => {
- setOptions({
+ setVillageFilter({
countryIsoCode: selectedCountry,
});
- }, [selectedCountry, selectedPhase]);
+ setClassroomFilter({
+ villageId: selectedVillage,
+ });
+ }, [selectedCountry, selectedPhase, selectedVillage]);
+
+ const [familiesWithoutAccountRows, setFamiliesWithoutAccountRows] = React.useState>([]);
+ React.useEffect(() => {
+ if (villagesStats.data?.familiesWithoutAccount) {
+ setFamiliesWithoutAccountRows(createFamiliesWithoutAccountRows(villagesStats.data?.familiesWithoutAccount));
+ }
+ }, [villagesStats.data?.familiesWithoutAccount]);
const handleVillageChange = (village: string) => {
setSelectedVillage(village);
setSelectedClassroom('');
};
- const classroomsMap = mockClassroomsStats
- .filter((classroom) => classroom.villageName === selectedVillage)
- .map((classroom) => classroom.classroomId);
- const classrooms = [...new Set(classroomsMap)];
-
const handleClassroomChange = (classroom: string) => {
setSelectedClassroom(classroom);
};
@@ -53,9 +72,15 @@ const ClassroomStats = () => {
setSelectedPhase(phase);
};
+ const noDataFoundMessage = 'Pas de données pour le Village-Monde sélectionné';
+
+ const handleTabChange = (_event: React.SyntheticEvent, newValue: number) => {
+ setValue(newValue);
+ };
+
return (
<>
-
+
@@ -68,8 +93,43 @@ const ClassroomStats = () => {
-
- {!selectedClassroom ? : null}
+
+
+
+
+
+
+ Statistiques - En classe
+
+
+ {!selectedClassroom ? (
+
+ ) : (
+ <>
+ {noDataFoundMessage}
}
+ data={familiesWithoutAccountRows}
+ columns={FamiliesWithoutAccountHeaders}
+ titleContent={`À surveiller : comptes non créés (${familiesWithoutAccountRows.length})`}
+ />
+
+ Nombre de codes enfant créés
+ Nombre de familles connectées
+
+ >
+ )}
+
>
);
};
diff --git a/src/components/admin/dashboard-statistics/filters/ClassroomDropdown.tsx b/src/components/admin/dashboard-statistics/filters/ClassroomDropdown.tsx
index a641ccdc4..60196d481 100644
--- a/src/components/admin/dashboard-statistics/filters/ClassroomDropdown.tsx
+++ b/src/components/admin/dashboard-statistics/filters/ClassroomDropdown.tsx
@@ -7,8 +7,10 @@ import MenuItem from '@mui/material/MenuItem';
import type { SelectChangeEvent } from '@mui/material/Select';
import Select from '@mui/material/Select';
+import type { Classroom } from 'types/classroom.type';
+
interface ClassroomDropdownProps {
- classrooms: number[];
+ classrooms: Classroom[];
onClassroomChange: (classrooms: string) => void;
}
@@ -27,8 +29,8 @@ export default function ClassroomDropdown({ classrooms, onClassroomChange }: Cla
diff --git a/src/services/useClassrooms.ts b/src/services/useClassrooms.ts
new file mode 100644
index 000000000..6f517015e
--- /dev/null
+++ b/src/services/useClassrooms.ts
@@ -0,0 +1,50 @@
+import React from 'react';
+import type { QueryFunction } from 'react-query';
+import { useQueryClient, useQuery } from 'react-query';
+
+import { axiosRequest } from 'src/utils/axiosRequest';
+import type { Classroom, ClassroomFilter } from 'types/classroom.type';
+
+export const useClassrooms = (options?: ClassroomFilter): { classrooms: Classroom[]; setClassrooms(newClassrooms: Classroom[]): void } => {
+ const queryClient = useQueryClient();
+ const buildUrl = () => {
+ if (!options) return '/classrooms';
+ const { villageId } = options;
+ const queryParams = [];
+
+ if (villageId) queryParams.push(`villageId=${villageId}`);
+
+ return queryParams.length ? `/classrooms?${queryParams.join('&')}` : '/classrooms';
+ };
+
+ const url = buildUrl();
+ // The query function for fetching villages, memoized with useCallback
+ const getClassrooms: QueryFunction = React.useCallback(async () => {
+ const response = await axiosRequest({
+ method: 'GET',
+ url,
+ });
+ if (response.error) {
+ return [];
+ }
+ return response.data;
+ }, [url]);
+
+ // Notice that we include `countryIsoCode` in the query key so that the query is refetched
+ const { data, isLoading, error } = useQuery(
+ ['classrooms', options], // Include countryIsoCode in the query key to trigger refetching
+ getClassrooms,
+ );
+
+ const setClassrooms = React.useCallback(
+ (newClassrooms: Classroom[]) => {
+ queryClient.setQueryData(['classrooms', options], newClassrooms); // Ensure that the query key includes countryIsoCode
+ },
+ [queryClient, options],
+ );
+
+ return {
+ classrooms: isLoading || error ? [] : data || [],
+ setClassrooms,
+ };
+};
diff --git a/types/classroom.type.ts b/types/classroom.type.ts
index 4c8e2f2cf..020a093be 100644
--- a/types/classroom.type.ts
+++ b/types/classroom.type.ts
@@ -36,3 +36,7 @@ export interface InitialStateOptionsProps {
ownClass: StateOptions;
ownClassTimeDelay: StateOptions;
}
+
+export interface ClassroomFilter {
+ villageId: string;
+}