ENCOA-267

This commit is contained in:
Tiago Ribeiro
2024-12-23 10:18:52 +00:00
parent 9cf13e3f26
commit e9c961e633
6 changed files with 157 additions and 142 deletions

View File

@@ -8,6 +8,7 @@ import useGroups from "@/hooks/useGroups";
import useRecordStore from "@/stores/recordStore"; import useRecordStore from "@/stores/recordStore";
import { EntityWithRoles } from "@/interfaces/entity"; import { EntityWithRoles } from "@/interfaces/entity";
import { mapBy } from "@/utils"; import { mapBy } from "@/utils";
import { useAllowedEntities } from "@/hooks/useEntityPermissions";
type TimeFilter = "months" | "weeks" | "days"; type TimeFilter = "months" | "weeks" | "days";
@@ -47,6 +48,8 @@ const RecordFilter: React.FC<Props> = ({
state.setSelectedUser state.setSelectedUser
]); ]);
const allowedViewEntities = useAllowedEntities(user, entities, 'view_student_record')
const entityUsers = useMemo(() => !entity ? users : users.filter(u => mapBy(u.entities, 'id').includes(entity)), [users, entity]) const entityUsers = useMemo(() => !entity ? users : users.filter(u => mapBy(u.entities, 'id').includes(entity)), [users, entity])
useEffect(() => setStatsUserId(user.id), [setStatsUserId, user.id]) useEffect(() => setStatsUserId(user.id), [setStatsUserId, user.id])
@@ -64,7 +67,7 @@ const RecordFilter: React.FC<Props> = ({
<label className="font-normal text-base text-mti-gray-dim">Entity</label> <label className="font-normal text-base text-mti-gray-dim">Entity</label>
<Select <Select
options={entities.map((e) => ({value: e.id, label: e.label}))} options={allowedViewEntities.map((e) => ({ value: e.id, label: e.label }))}
onChange={(value) => setEntity(value?.value || undefined)} onChange={(value) => setEntity(value?.value || undefined)}
isClearable isClearable
styles={{ styles={{
@@ -84,7 +87,7 @@ const RecordFilter: React.FC<Props> = ({
value: x.id, value: x.id,
label: `${x.name} - ${x.email}`, label: `${x.name} - ${x.email}`,
}))} }))}
defaultValue={{value: user.id, label: `${user.name} - ${user.email}`}} defaultValue={{ value: user.id, label: `${user.name} - ${user.email}` }}
onChange={(value) => setStatsUserId(value?.value!)} onChange={(value) => setStatsUserId(value?.value!)}
styles={{ styles={{
menuPortal: (base) => ({ ...base, zIndex: 9999 }), menuPortal: (base) => ({ ...base, zIndex: 9999 }),
@@ -108,7 +111,7 @@ const RecordFilter: React.FC<Props> = ({
value: x.id, value: x.id,
label: `${x.name} - ${x.email}`, label: `${x.name} - ${x.email}`,
}))} }))}
defaultValue={{value: user.id, label: `${user.name} - ${user.email}`}} defaultValue={{ value: user.id, label: `${user.name} - ${user.email}` }}
onChange={(value) => setStatsUserId(value?.value!)} onChange={(value) => setStatsUserId(value?.value!)}
styles={{ styles={{
menuPortal: (base) => ({ ...base, zIndex: 9999 }), menuPortal: (base) => ({ ...base, zIndex: 9999 }),

View File

@@ -82,7 +82,7 @@ interface StatsGridItemProps {
selectedTrainingExams?: string[]; selectedTrainingExams?: string[];
maxTrainingExams?: number; maxTrainingExams?: number;
setSelectedTrainingExams?: React.Dispatch<React.SetStateAction<string[]>>; setSelectedTrainingExams?: React.Dispatch<React.SetStateAction<string[]>>;
renderPdfIcon: (session: string, color: string, textColor: string) => React.ReactNode; renderPdfIcon?: (session: string, color: string, textColor: string) => React.ReactNode;
} }
const StatsGridItem: React.FC<StatsGridItemProps> = ({ const StatsGridItem: React.FC<StatsGridItemProps> = ({
@@ -236,7 +236,7 @@ const StatsGridItem: React.FC<StatsGridItemProps> = ({
{renderLevelScore()} {renderLevelScore()}
</span> </span>
)} )}
{shouldRenderPDFIcon() && renderPdfIcon(session, textColor, textColor)} {shouldRenderPDFIcon() && renderPdfIcon && renderPdfIcon(session, textColor, textColor)}
</div> </div>
{examNumber === undefined ? ( {examNumber === undefined ? (
<> <>

View File

@@ -79,6 +79,8 @@ const CLASSROOM_MANAGEMENT: PermissionLayout[] = [
{ label: "Upload to Classroom", key: "upload_classroom" }, { label: "Upload to Classroom", key: "upload_classroom" },
{ label: "Remove from Classroom", key: "remove_from_classroom" }, { label: "Remove from Classroom", key: "remove_from_classroom" },
{ label: "Delete Classroom", key: "delete_classroom" }, { label: "Delete Classroom", key: "delete_classroom" },
{ label: "View Student Record", key: "view_student_record" },
{ label: "Download Student Report", key: "download_student_record" },
] ]
const ENTITY_MANAGEMENT: PermissionLayout[] = [ const ENTITY_MANAGEMENT: PermissionLayout[] = [

View File

@@ -22,7 +22,7 @@ import { Assignment } from "@/interfaces/results";
import { getEntitiesUsers, getUsers } from "@/utils/users.be"; import { getEntitiesUsers, getUsers } from "@/utils/users.be";
import { getAssignments, getEntitiesAssignments } from "@/utils/assignments.be"; import { getAssignments, getEntitiesAssignments } from "@/utils/assignments.be";
import useGradingSystem from "@/hooks/useGrading"; import useGradingSystem from "@/hooks/useGrading";
import { mapBy, redirect, serialize } from "@/utils"; import { findBy, mapBy, redirect, serialize } from "@/utils";
import { getEntitiesWithRoles } from "@/utils/entities.be"; import { getEntitiesWithRoles } from "@/utils/entities.be";
import { checkAccess } from "@/utils/permissions"; import { checkAccess } from "@/utils/permissions";
import { getGroups, getGroupsByEntities } from "@/utils/groups.be"; import { getGroups, getGroupsByEntities } from "@/utils/groups.be";
@@ -31,6 +31,7 @@ import { Grading } from "@/interfaces";
import { EntityWithRoles } from "@/interfaces/entity"; import { EntityWithRoles } from "@/interfaces/entity";
import CardList from "@/components/High/CardList"; import CardList from "@/components/High/CardList";
import { requestUser } from "@/utils/api"; import { requestUser } from "@/utils/api";
import { useAllowedEntities } from "@/hooks/useEntityPermissions";
export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => { export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
const user = await requestUser(req, res) const user = await requestUser(req, res)
@@ -74,6 +75,7 @@ export default function History({ user, users, assignments, entities, gradingSys
const [filter, setFilter] = useState<Filter>(); const [filter, setFilter] = useState<Filter>();
const { data: stats, isLoading: isStatsLoading } = useFilterRecordsByUser<Stat[]>(statsUserId || user?.id); const { data: stats, isLoading: isStatsLoading } = useFilterRecordsByUser<Stat[]>(statsUserId || user?.id);
const allowedDownloadEntities = useAllowedEntities(user, entities, 'download_student_record')
const renderPdfIcon = usePDFDownload("stats"); const renderPdfIcon = usePDFDownload("stats");
@@ -155,6 +157,9 @@ export default function History({ user, users, assignments, entities, gradingSys
const customContent = (timestamp: string) => { const customContent = (timestamp: string) => {
const dateStats = groupedStats[timestamp]; const dateStats = groupedStats[timestamp];
const statUser = findBy(users, 'id', dateStats[0]?.user)
const canDownload = mapBy(statUser?.entities, 'id').some(e => mapBy(allowedDownloadEntities, 'id').includes(e))
return ( return (
<StatsGridItem <StatsGridItem
@@ -169,7 +174,7 @@ export default function History({ user, users, assignments, entities, gradingSys
selectedTrainingExams={selectedTrainingExams} selectedTrainingExams={selectedTrainingExams}
setSelectedTrainingExams={setSelectedTrainingExams} setSelectedTrainingExams={setSelectedTrainingExams}
maxTrainingExams={MAX_TRAINING_EXAMS} maxTrainingExams={MAX_TRAINING_EXAMS}
renderPdfIcon={renderPdfIcon} renderPdfIcon={canDownload ? renderPdfIcon : undefined}
/> />
); );
}; };

View File

@@ -61,7 +61,9 @@ export type RolePermission =
"edit_grading_system" | "edit_grading_system" |
"view_student_performance" | "view_student_performance" |
"upload_classroom" | "upload_classroom" |
"download_user_list" "download_user_list" |
"view_student_record" |
"download_student_record"
export const DEFAULT_PERMISSIONS: RolePermission[] = [ export const DEFAULT_PERMISSIONS: RolePermission[] = [
"view_students", "view_students",
@@ -136,5 +138,7 @@ export const ADMIN_PERMISSIONS: RolePermission[] = [
"edit_grading_system", "edit_grading_system",
"view_student_performance", "view_student_performance",
"upload_classroom", "upload_classroom",
"download_user_list" "download_user_list",
"view_student_record",
"download_student_record"
] ]

View File

@@ -3,6 +3,7 @@ import { WithEntity } from "@/interfaces/entity";
import { Assignment } from "@/interfaces/results"; import { Assignment } from "@/interfaces/results";
import { CorporateUser, Group, GroupWithUsers, MasterCorporateUser, StudentUser, TeacherUser, Type, User } from "@/interfaces/user"; import { CorporateUser, Group, GroupWithUsers, MasterCorporateUser, StudentUser, TeacherUser, Type, User } from "@/interfaces/user";
import client from "@/lib/mongodb"; import client from "@/lib/mongodb";
import { uniq } from "lodash";
import moment from "moment"; import moment from "moment";
import { getLinkedUsers, getUser } from "./users.be"; import { getLinkedUsers, getUser } from "./users.be";
import { getSpecificUsers } from "./users.be"; import { getSpecificUsers } from "./users.be";
@@ -116,7 +117,7 @@ export const getUsersGroups = async (ids: string[]) => {
export const convertToUsers = (group: Group, users: User[]): GroupWithUsers => export const convertToUsers = (group: Group, users: User[]): GroupWithUsers =>
Object.assign(group, { Object.assign(group, {
admin: users.find((u) => u.id === group.admin), admin: users.find((u) => u.id === group.admin),
participants: group.participants.map((p) => users.find((u) => u.id === p)).filter((x) => !!x) as User[], participants: uniq(group.participants).map((p) => users.find((u) => u.id === p)).filter((x) => !!x) as User[],
}); });
export const getAllAssignersByCorporate = async (corporateID: string, type: Type): Promise<string[]> => { export const getAllAssignersByCorporate = async (corporateID: string, type: Type): Promise<string[]> => {