Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Com 3854 #696

Merged
merged 8 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
19 changes: 7 additions & 12 deletions src/components/learnerPendingActions/AttendanceSheetCell/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,14 @@ const AttendanceSheetCell = ({ attendanceSheet }: AttendanceSheetCellProps) => {
const setGroupedSlotsToBeSigned = useSetGroupedSlotsToBeSigned();

const goToSignature = () => {
if (course?.subProgram.steps) {
const groupedSlots = groupBy(attendanceSheet.slots, 'step');
const groupedSlotsToBeSigned = course?.subProgram.steps
.map(s => s._id).reduce<Record<string, SlotType[]>>((acc, step) => {
if (groupedSlots[step]) {
acc[step] = groupedSlots[step];
}
return acc;
}, {});
const groupedSlots = groupBy(attendanceSheet.slots, 'step');
const groupedSlotsToBeSigned = course?.subProgram.steps.reduce<Record<string, SlotType[]>>((acc, step) => {
if (groupedSlots[step._id]) acc[step.name] = groupedSlots[step._id];
return acc;
}, {});

setGroupedSlotsToBeSigned(groupedSlotsToBeSigned);
navigation.navigate('UpdateAttendanceSheet', { attendanceSheetId: attendanceSheet._id });
}
setGroupedSlotsToBeSigned(groupedSlotsToBeSigned!);
navigation.navigate('UpdateAttendanceSheet', { attendanceSheetId: attendanceSheet._id });
};
return (
<View style={styles.container}>
Expand Down
6 changes: 2 additions & 4 deletions src/screens/courses/profile/AdminCourseProfile/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,8 @@ const AdminCourseProfile = ({ route, navigation }: AdminCourseProfileProps) => {

const groupedSlots = groupBy(course.slots.filter(slot => !signedSlots.includes(slot._id)), 'step');

return course?.subProgram.steps.map(s => s._id).reduce<Record<string, SlotType[]>>((acc, step) => {
if (groupedSlots[step]) {
acc[step] = groupedSlots[step];
}
return course?.subProgram.steps.reduce<Record<string, SlotType[]>>((acc, step) => {
if (groupedSlots[step._id]) acc[step.name] = groupedSlots[step._id];
return acc;
}, {});
}, [course, isSingle, savedAttendanceSheets]);
Expand Down
27 changes: 4 additions & 23 deletions src/screens/courses/profile/CreateAttendanceSheet/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { useCallback, useEffect, useMemo, useReducer, useState } from 'react';
import { createStackNavigator, StackScreenProps } from '@react-navigation/stack';
import { CompositeScreenProps } from '@react-navigation/native';
import keyBy from 'lodash/keyBy';
import AttendanceSheets from '../../../../api/attendanceSheets';
import { RootStackParamList, RootCreateAttendanceSheetParamList } from '../../../../types/NavigationType';
import { INTER_B2B, DD_MM_YYYY, HH_MM, IS_WEB } from '../../../../core/data/constants';
import { INTER_B2B, DD_MM_YYYY, HH_MM } from '../../../../core/data/constants';
import { errorReducer, initialErrorState, RESET_ERROR, SET_ERROR } from '../../../../reducers/error';
import AttendanceSheetSelectionForm from '../../../../components/AttendanceSheetSelectionForm';
import UploadMethods from '../../../../components/UploadMethods';
Expand All @@ -22,6 +21,7 @@ import MultipleCheckboxList from '../../../../components/form/MultipleCheckboxLi
import AttendanceSignatureContainer from '../../../../components/AttendanceSignatureContainer';
import AttendanceSheetSummary from '../../../../components/AttendanceSheetSummary';
import AttendanceEndScreen from '../../../../components/AttendanceEndScreen';
import { generateFile } from '../helper';

interface CreateAttendanceSheetProps extends CompositeScreenProps<
StackScreenProps<RootStackParamList, 'CreateAttendanceSheet'>,
Expand Down Expand Up @@ -54,10 +54,7 @@ const CreateAttendanceSheet = ({ route, navigation }: CreateAttendanceSheetProps
const [errorSlots, dispatchErrorSlots] = useReducer(errorReducer, initialErrorState);
const [errorSignature, dispatchErrorSignature] = useReducer(errorReducer, initialErrorState);
const [errorConfirmation, dispatchErrorConfirmation] = useReducer(errorReducer, initialErrorState);
const stepsById = useMemo(() => keyBy(course?.subProgram.steps, '_id'), [course]);
const stepsName = useMemo(() =>
Object.keys(groupedSlotsToBeSigned).map(stepId => (stepsById[stepId].name)),
[groupedSlotsToBeSigned, stepsById]);
const stepsName = useMemo(() => Object.keys(groupedSlotsToBeSigned), [groupedSlotsToBeSigned]);
const slotsOptions = useMemo(() =>
Object.values(groupedSlotsToBeSigned)
.map(slotGroup => [...slotGroup]
Expand Down Expand Up @@ -137,26 +134,10 @@ const CreateAttendanceSheet = ({ route, navigation }: CreateAttendanceSheetProps
}
};

const base64ToBlob = (base64Data: string, contentType: string) => {
const byteCharacters = atob(base64Data.split(',')[1]);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i += 1) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);

return new Blob([byteArray], { type: contentType });
};

const saveAttendances = async () => {
try {
setIsLoading(true);
let file;
const contentType = 'image/png';
if (IS_WEB) {
const blob = base64ToBlob(signature, contentType);
file = new File([blob], `trainer_signature_${course?._id}.png`, { type: contentType });
} else file = { uri: signature, type: contentType, name: `trainer_signature_${course?._id}` };
const file = generateFile(signature, course?._id, 'trainer');
const data = formatPayload({
signature: file,
course: course?._id,
Expand Down
16 changes: 12 additions & 4 deletions src/screens/courses/profile/LearnerCourseProfile/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import commonStyles from '../../../../styles/common';
import { CourseType, BlendedCourseType, ELearningProgramType } from '../../../../types/CourseTypes';
import styles from '../styles';
import { useGetLoggedUserId, useSetStatusBarVisible } from '../../../../store/main/hooks';
import { useSetCourse } from '../../../../store/attendanceSheets/hooks';
import { useSetCourse, useResetAttendanceSheetReducer } from '../../../../store/attendanceSheets/hooks';
import ProgressBar from '../../../../components/cards/ProgressBar';
import NiSecondaryButton from '../../../../components/form/SecondaryButton';
import PendingActionsContainer from '../../../../components/learnerPendingActions/PendingActionsContainer';
Expand All @@ -56,6 +56,7 @@ const LearnerCourseProfile = ({ route, navigation }: LearnerCourseProfileProps)
const setStatusBarVisible = useSetStatusBarVisible();
const userId: string | null = useGetLoggedUserId();
const setCourseToStore = useSetCourse();
const resetAttendanceSheetReducer = useResetAttendanceSheetReducer();

const [course, setCourse] = useState<CourseType | null>(null);
const [questionnaires, setQuestionnaires] = useState<QuestionnaireType[]>([]);
Expand Down Expand Up @@ -83,10 +84,10 @@ const LearnerCourseProfile = ({ route, navigation }: LearnerCourseProfileProps)
if (fetchedCourse.format === BLENDED) {
const formattedCourse = {
_id: fetchedCourse._id,
trainer: { identity: (fetchedCourse as BlendedCourseType).trainer.identity },
trainer: { identity: get(fetchedCourse, 'trainer.identity') || {} },
manonpalin marked this conversation as resolved.
Show resolved Hide resolved
subProgram: { steps: fetchedCourse.subProgram.steps.map(s => ({ _id: s._id, name: s.name })) },
} as BlendedCourseType;
setCourseToStore(formattedCourse);
};
setCourseToStore(formattedCourse as BlendedCourseType);
}
const programImage = get(fetchedCourse, 'subProgram.program.image.link') || '';
setCourse(fetchedCourse);
Expand All @@ -104,6 +105,13 @@ const LearnerCourseProfile = ({ route, navigation }: LearnerCourseProfileProps)
}
}, [isFocused, setStatusBarVisible, route.params.courseId, setCourseToStore]);

useEffect(() => () => {
const currentRoute = navigation.getState().routes[navigation.getState().index];
if (currentRoute?.name !== 'UpdateAttendanceSheet') {
resetAttendanceSheetReducer();
}
}, [navigation, resetAttendanceSheetReducer]);

const goBack = useCallback(() => {
navigation.navigate('LearnerCourses');
}, [navigation]);
Expand Down
26 changes: 4 additions & 22 deletions src/screens/courses/profile/UpdateAttendanceSheet/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { useEffect, useMemo, useReducer, useState } from 'react';
import { createStackNavigator, StackScreenProps } from '@react-navigation/stack';
import { CompositeScreenProps } from '@react-navigation/native';
import keyBy from 'lodash/keyBy';
import AttendanceSheets from '../../../../api/attendanceSheets';
import { RootStackParamList, RootUpdateAttendanceSheetParamList } from '../../../../types/NavigationType';
import { DD_MM_YYYY, HH_MM, IS_WEB, LEARNER, LONG_FIRSTNAME_LONG_LASTNAME } from '../../../../core/data/constants';
import { DD_MM_YYYY, HH_MM, LEARNER, LONG_FIRSTNAME_LONG_LASTNAME } from '../../../../core/data/constants';
import { errorReducer, initialErrorState, RESET_ERROR, SET_ERROR } from '../../../../reducers/error';
import AttendanceSheetSelectionForm from '../../../../components/AttendanceSheetSelectionForm';
import { useGetCourse, useGetGroupedSlotsToBeSigned } from '../../../../store/attendanceSheets/hooks';
Expand All @@ -17,6 +16,7 @@ import AttendanceSheetSummary from '../../../../components/AttendanceSheetSummar
import AttendanceEndScreen from '../../../../components/AttendanceEndScreen';
import { useGetLoggedUser } from '../../../../store/main/hooks';
import { formatIdentity } from '../../../../core/helpers/utils';
import { generateFile } from '../helper';

interface UpdateAttendanceSheetProps extends CompositeScreenProps<
StackScreenProps<RootStackParamList, 'UpdateAttendanceSheet'>,
Expand All @@ -42,10 +42,8 @@ const UpdateAttendanceSheet = ({ route, navigation }: UpdateAttendanceSheetProps
const [failUpload, setFailUpload] = useState<boolean>(false);
const [errorSignature, dispatchErrorSignature] = useReducer(errorReducer, initialErrorState);
const [errorConfirmation, dispatchErrorConfirmation] = useReducer(errorReducer, initialErrorState);
const stepsById = useMemo(() => keyBy(course?.subProgram.steps, '_id'), [course]);
const stepsName = useMemo(() =>
manonpalin marked this conversation as resolved.
Show resolved Hide resolved
Object.keys(groupedSlotsToBeSigned).map(stepId => (stepsById[stepId].name)),
[groupedSlotsToBeSigned, stepsById]);
Object.keys(groupedSlotsToBeSigned), [groupedSlotsToBeSigned]);
const slotsOptions = useMemo(() =>
Object.values(groupedSlotsToBeSigned)
.map(slotGroup => [...slotGroup]
Expand Down Expand Up @@ -82,26 +80,10 @@ const UpdateAttendanceSheet = ({ route, navigation }: UpdateAttendanceSheetProps
}
};

const base64ToBlob = (base64Data: string, contentType: string) => {
const byteCharacters = atob(base64Data.split(',')[1]);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i += 1) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);

return new Blob([byteArray], { type: contentType });
};

const saveAttendances = async () => {
manonpalin marked this conversation as resolved.
Show resolved Hide resolved
try {
setIsLoading(true);
let file;
const contentType = 'image/png';
if (IS_WEB) {
const blob = base64ToBlob(signature, contentType);
file = new File([blob], `trainee_signature_${course?._id}.png`, { type: contentType });
} else file = { uri: signature, type: contentType, name: `trainee_signature_${course?._id}` };
const file = generateFile(signature, course?._id, 'trainee');
const data = formatPayload({ signature: file });
await AttendanceSheets.sign(attendanceSheetId, data);
setIsLoading(false);
Expand Down
24 changes: 23 additions & 1 deletion src/screens/courses/profile/helper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { View } from 'react-native';
import get from 'lodash/get';
import { E_LEARNING, ON_SITE, REMOTE, TESTER } from '../../../core/data/constants';
import { E_LEARNING, IS_WEB, ON_SITE, REMOTE, TESTER } from '../../../core/data/constants';
import LiveCell from '../../../components/steps/LiveCell';
import ELearningCell from '../../../components/ELearningCell';
import styles from './styles';
Expand Down Expand Up @@ -40,3 +40,25 @@ export const getTitle = (course) => {
const { misc } = (course as BlendedCourseType);
return `${programName}${misc ? ` - ${misc}` : ''}`;
};

const base64ToBlob = (base64Data: string, contentType: string) => {
const byteCharacters = atob(base64Data.split(',')[1]);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i += 1) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);

return new Blob([byteArray], { type: contentType });
};

export const generateFile = (signature, courseId, mode) => {
manonpalin marked this conversation as resolved.
Show resolved Hide resolved
let file;
const contentType = 'image/png';
if (IS_WEB) {
const blob = base64ToBlob(signature, contentType);
file = new File([blob], `${mode}_signature_${courseId}.png`, { type: contentType });
} else file = { uri: signature, type: contentType, name: `${mode}_signature_${courseId}` };

return file;
};
4 changes: 2 additions & 2 deletions src/store/main/slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { UserType } from '../../types/UserType';
import { resetAllReducers } from '../actions';

type MainStateType = {
loggedUser: any | null,
loggedUser: UserType | null,
statusBarVisible: boolean,
}

Expand All @@ -22,7 +22,7 @@ const setUser = (state: MainStateType, action: PayloadAction<UserType>) => ({
'contact.phone',
'role',
'companyLinkRequest',
]),
]) as UserType,
});

const setStatusBar = (state: MainStateType, action: PayloadAction<boolean>) => (
Expand Down
4 changes: 2 additions & 2 deletions src/types/NavigationType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export type RootStackParamList = {
BlendedAbout: { course: BlendedCourseType, mode: CourseModeType }
ElearningAbout: { program: ELearningProgramType }
AdminCourseProfile: { courseId: string }
CreateAttendanceSheet: {isSingle: boolean};
UpdateAttendanceSheet: {attendanceSheetId: string},
CreateAttendanceSheet: { isSingle: boolean };
UpdateAttendanceSheet: { attendanceSheetId: string },
ProfileEdition: undefined;
PasswordEdition: { userId: string };
ImagePickerManager: undefined;
Expand Down
Loading