Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions admin-frontend/src/analytics/AnalyticsService.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export interface StudentApiItem {
career_explorer_messages_sent: number | null;
last_login: string | null;
last_active_module: string | null;
treatment_group: string | null;
}

export interface PaginatedResponse<T> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const storyInstructorStudentsFixture: InstructorStudentRow[] = [
careerExplorerMessagesSent: 5,
lastLogin: "Today",
lastActiveModuleId: "career-explorer",
treatmentGroup: "T1",
},
{
id: "student-2",
Expand All @@ -39,6 +40,7 @@ const storyInstructorStudentsFixture: InstructorStudentRow[] = [
careerExplorerMessagesSent: 1,
lastLogin: "2 days ago",
lastActiveModuleId: "skills-discovery",
treatmentGroup: "T2",
},
{
id: "student-3",
Expand All @@ -53,6 +55,7 @@ const storyInstructorStudentsFixture: InstructorStudentRow[] = [
careerExplorerMessagesSent: null,
lastLogin: "1 week ago",
lastActiveModuleId: "workplace-readiness",
treatmentGroup: null,
},
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,12 @@ const InstructorStudentsTable: React.FC<InstructorStudentsTableProps> = ({
setLastLoginFilter,
lastModuleFilter,
setLastModuleFilter,
treatmentGroupFilter,
setTreatmentGroupFilter,
programmes,
years,
modules,
treatmentGroups,
filteredRows,
pagedRows,
pageSize,
Expand Down Expand Up @@ -131,6 +134,34 @@ const InstructorStudentsTable: React.FC<InstructorStudentsTableProps> = ({
},
render: (val, row) => <span style={{ textAlign: "center", display: "block", width: "100%" }}>{row.year}</span>,
},
// Only surface the treatment group column/filter when at least one student is assigned to a group.
...(treatmentGroups.length > 0
? [
{
key: "treatmentGroup" as const,
label: t("instructorDashboard.studentsTable.headers.treatmentGroup").toUpperCase(),
sortable: true,
sortType: "text" as const,
align: "center" as const,
filter: {
options: [
{ value: "all", label: allLabel },
...treatmentGroups.map((group) => ({ value: group, label: group })),
],
value: treatmentGroupFilter,
onChange: setTreatmentGroupFilter,
},
render: (_val: unknown, row: InstructorStudentRow) => {
const label = row.treatmentGroup ?? PLACEHOLDER_SYMBOL;
return (
<Typography variant="body2" noWrap title={label} sx={{ textAlign: "center", width: "100%" }}>
{label}
</Typography>
);
},
},
]
: []),
{
key: "lastLogin",
label: t("instructorDashboard.studentsTable.headers.lastLogin").toUpperCase(),
Expand Down
1 change: 1 addition & 0 deletions admin-frontend/src/hooks/useInstructorStudents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ const toInstructorStudentRow = (
careerExplorerMessagesSent: item.career_explorer_messages_sent ?? null,
lastLogin: formatLastLogin(item.last_login),
lastActiveModuleId,
treatmentGroup: toText(item.treatment_group),
};
};

Expand Down
33 changes: 30 additions & 3 deletions admin-frontend/src/hooks/useInstructorStudentsTableState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ export type StudentsSortKey =
| "careerReadinessStarted"
| "careerReadinessCompleted"
| "skillsDiscoveryStatus"
| "careerExplorerMessagesSent";
| "careerExplorerMessagesSent"
| "treatmentGroup";
type SortDir = "asc" | "desc";

type UseInstructorStudentsTableStateOptions = {
Expand Down Expand Up @@ -88,6 +89,7 @@ export function useInstructorStudentsTableState(
const [yearFilter, setYearFilter] = useState("all");
const [lastLoginFilter, setLastLoginFilter] = useState("all");
const [lastModuleFilter, setLastModuleFilter] = useState("all");
const [treatmentGroupFilter, setTreatmentGroupFilter] = useState("all");
const [pageIndex, setPageIndex] = useState(0);

const debouncedNameSearch = useDebouncedValue(nameSearch, 250);
Expand All @@ -101,6 +103,17 @@ export function useInstructorStudentsTableState(
);
return ["all", ...Array.from(new Set([...knownModuleIds, ...dynamic]))];
}, [rows]);
const treatmentGroups = useMemo(
() =>
Array.from(
new Set(
rows
.map((r) => (typeof r.treatmentGroup === "string" ? r.treatmentGroup.trim() : ""))
.filter((value) => value.length > 0)
)
).sort(),
[rows]
);

const filteredRows = useMemo(() => {
const query = debouncedNameSearch.trim().toLowerCase();
Expand All @@ -109,11 +122,12 @@ export function useInstructorStudentsTableState(
if (programme !== "all" && row.programme !== programme) return false;
if (yearFilter !== "all" && row.year !== yearFilter) return false;
if (lastModuleFilter !== "all" && row.lastActiveModuleId !== lastModuleFilter) return false;
if (treatmentGroupFilter !== "all" && (row.treatmentGroup ?? "") !== treatmentGroupFilter) return false;

if (lastLoginFilter === "all") return true;
return lastLoginDisplayMatchesFilter(row.lastLogin, lastLoginFilter);
});
}, [debouncedNameSearch, lastLoginFilter, lastModuleFilter, programme, rows, yearFilter]);
}, [debouncedNameSearch, lastLoginFilter, lastModuleFilter, programme, rows, treatmentGroupFilter, yearFilter]);

const sortedRows = useMemo(() => {
if (!sortKey) {
Expand All @@ -135,6 +149,7 @@ export function useInstructorStudentsTableState(
const order: Record<string, number> = { not_started: 0, in_progress: 1, completed: 2 };
return order[row.skillsDiscoveryStatus] ?? 0;
}
if (key === "treatmentGroup") return normalizeText(row.treatmentGroup ?? "");
return row.careerExplorerMessagesSent;
};

Expand All @@ -158,7 +173,16 @@ export function useInstructorStudentsTableState(

useEffect(() => {
setPageIndex(0);
}, [debouncedNameSearch, programme, yearFilter, lastLoginFilter, lastModuleFilter, sortKey, sortDir]);
}, [
debouncedNameSearch,
programme,
yearFilter,
lastLoginFilter,
lastModuleFilter,
treatmentGroupFilter,
sortKey,
sortDir,
]);

const totalPages = Math.max(1, Math.ceil(sortedRows.length / pageSize));
const safePageIndex = Math.min(pageIndex, totalPages - 1);
Expand Down Expand Up @@ -206,9 +230,12 @@ export function useInstructorStudentsTableState(
setLastLoginFilter,
lastModuleFilter,
setLastModuleFilter,
treatmentGroupFilter,
setTreatmentGroupFilter,
programmes,
years,
modules,
treatmentGroups,
filteredRows,
pagedRows,
pageSize,
Expand Down
3 changes: 2 additions & 1 deletion admin-frontend/src/i18n/locales/en-GB/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,8 @@
"careerExplorer": "Career Explorer",
"prioritySectorsExplored": "Priority Sectors Explored",
"lastLogin": "Last Login",
"lastActiveModule": "Last Active Module"
"lastActiveModule": "Last Active Module",
"treatmentGroup": "Treatment Group"
},
"filters": {
"all": "All",
Expand Down
3 changes: 2 additions & 1 deletion admin-frontend/src/i18n/locales/en-US/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,8 @@
"careerExplorer": "Career Explorer",
"prioritySectorsExplored": "Priority Sectors Explored",
"lastLogin": "Last Login",
"lastActiveModule": "Last Active Module"
"lastActiveModule": "Last Active Module",
"treatmentGroup": "Treatment Group"
},
"filters": {
"all": "All",
Expand Down
3 changes: 2 additions & 1 deletion admin-frontend/src/i18n/locales/es-AR/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,8 @@
"careerExplorer": "Explorador de carrera",
"prioritySectorsExplored": "Sectores prioritarios explorados",
"lastLogin": "Último inicio de sesión",
"lastActiveModule": "Último módulo activo"
"lastActiveModule": "Último módulo activo",
"treatmentGroup": "Grupo de tratamiento"
},
"filters": {
"all": "Todo",
Expand Down
3 changes: 2 additions & 1 deletion admin-frontend/src/i18n/locales/es-ES/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,8 @@
"careerExplorer": "Explorador de carrera",
"prioritySectorsExplored": "Sectores prioritarios explorados",
"lastLogin": "Último inicio de sesión",
"lastActiveModule": "Último módulo activo"
"lastActiveModule": "Último módulo activo",
"treatmentGroup": "Grupo de tratamiento"
},
"filters": {
"all": "Todo",
Expand Down
3 changes: 2 additions & 1 deletion admin-frontend/src/i18n/locales/ny-ZM/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,8 @@
"careerExplorer": "Kufunafuna Ntchito",
"prioritySectorsExplored": "Magawo Ofunika Kwambiri Afufuzidwa",
"lastLogin": "Kulowa Komaliza",
"lastActiveModule": "Mojiyu Wogwira Pomaliza"
"lastActiveModule": "Mojiyu Wogwira Pomaliza",
"treatmentGroup": "Gulu la Chithandizo"
},
"filters": {
"all": "Zonse",
Expand Down
3 changes: 2 additions & 1 deletion admin-frontend/src/i18n/locales/pt-MZ/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,8 @@
"careerExplorer": "Explorador de Carreiras",
"prioritySectorsExplored": "Setores Prioritários Explorados",
"lastLogin": "Último Acesso",
"lastActiveModule": "Último Módulo Ativo"
"lastActiveModule": "Último Módulo Ativo",
"treatmentGroup": "Grupo de Tratamento"
},
"filters": {
"all": "Todos",
Expand Down
3 changes: 2 additions & 1 deletion admin-frontend/src/i18n/locales/sw-KE/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,8 @@
"careerExplorer": "Mtafiti wa Kazi",
"prioritySectorsExplored": "Sekta za Kipaumbele Zilizogunduliwa",
"lastLogin": "Mwisho wa Kuingia",
"lastActiveModule": "Moduli Amilifu ya Mwisho"
"lastActiveModule": "Moduli Amilifu ya Mwisho",
"treatmentGroup": "Kikundi cha Matibabu"
},
"filters": {
"all": "Wote",
Expand Down
1 change: 1 addition & 0 deletions admin-frontend/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export interface InstructorStudentRow {
careerExplorerMessagesSent: number | null;
lastLogin: string;
lastActiveModuleId: string;
treatmentGroup: string | null;
}

export interface ModuleSummaryRow {
Expand Down
Loading