From f2fd76591faaab938c0d5f87f1491c0d8dd358cd Mon Sep 17 00:00:00 2001 From: Fabrizio Costa Medich <134924838+FabrizioCostaMedich@users.noreply.github.com> Date: Thu, 6 Feb 2025 14:58:28 +0100 Subject: [PATCH] fix(transcript): add weighted average on career screen and on teaching screen (#578) * fix(transcript): add weighted average on career screen and on teaching screen * fix(ui): change english translation --------- Co-authored-by: miky41195 Co-authored-by: Cristina Ferrian <54667563+Bri74@users.noreply.github.com> --- assets/translations/en.json | 7 +++- assets/translations/it.json | 7 +++- lib/ui/components/ModalContent.tsx | 22 ++++++++-- src/core/components/BottomModal.tsx | 30 ++++++++------ .../teaching/screens/TeachingScreen.tsx | 12 ++++-- .../components/CareerScreenModal.tsx | 17 ++++++-- .../transcript/screens/CareerScreen.tsx | 40 ++++++++++++++++++- 7 files changed, 109 insertions(+), 26 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index 6659327a..420ccc19 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -757,7 +757,7 @@ "averageLabel": "Average grade for admission to the Final Examination", "finalAverageLabelDescription":{ "description": "It is the average grade, weighted according to the credits relating to each exam with a grade*, adjusted for the {{-excludedCreditsNumber}} worst credits", - "formula": "sum (grade * credits) / sum of credits,\nexcluding the {{-excludedCreditsNumber}} worst credits" + "formula": "(sum (grade * credits) / sum of credits) * (110/30) ,\nexcluding the {{-excludedCreditsNumber}} worst credits" }, "laude": "*Honours does not count toward the calculation", "masterAdmissionAverage": "Master admission average grade", @@ -774,6 +774,11 @@ "title": "Career", "weightedAverageLabelDescription":{ "description":"This is the average, weighted according to the credits relating to each exam with a grade*", + "formula": "(sum (grade * credits) / sum of credits) * (110/30)" + }, + "weightedAverage": "Weighted average of grades", + "weightedAverageDescription":{ + "description":"It is the average grade, weighted according to the credits relating to each exam with a grade*, expressed in thirtieths.", "formula": "sum (grade * credits) / sum of credits" }, "yourCareer": "Your career" diff --git a/assets/translations/it.json b/assets/translations/it.json index bc289cea..4e62d22e 100644 --- a/assets/translations/it.json +++ b/assets/translations/it.json @@ -784,7 +784,7 @@ "averageLabel": "Media di ammissione all’esame di laurea", "finalAverageLabelDescription":{ "description": "E’ la media, pesata in funzione dei crediti relativi ad ogni esame con voto*, depurata dei {{-excludedCreditsNumber}} peggiori crediti", - "formula": "somma (voto * crediti) / somma crediti,\nescludendo i peggiori {{-excludedCreditsNumber}} crediti" + "formula": "(somma (voto * crediti) / somma crediti) * (110/30),\nescludendo i peggiori {{-excludedCreditsNumber}} crediti" }, "laude": "*Le lodi non concorrono al calcolo", "masterAdmissionAverage": "Media di ammissione al II liv", @@ -801,6 +801,11 @@ "title": "Carriera", "weightedAverageLabelDescription":{ "description":"E’ la media, pesata in funzione dei crediti relativi ad ogni esame con voto*", + "formula": "(somma (voto * crediti) / somma crediti) * (110/30)" + }, + "weightedAverage": "Media pesata dei voti", + "weightedAverageDescription":{ + "description":"E’ la media, pesata in funzione dei crediti relativi ad ogni esame con voto*, espressa in trentesimi", "formula": "somma (voto * crediti) / somma crediti" }, "yourCareer": "La tua carriera" diff --git a/lib/ui/components/ModalContent.tsx b/lib/ui/components/ModalContent.tsx index bfaa0b9e..c9808082 100644 --- a/lib/ui/components/ModalContent.tsx +++ b/lib/ui/components/ModalContent.tsx @@ -1,6 +1,6 @@ -import { PropsWithChildren } from 'react'; +import { PropsWithChildren, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { StyleSheet, View } from 'react-native'; +import { ScrollView, StyleSheet, View } from 'react-native'; import { faTimes } from '@fortawesome/free-solid-svg-icons'; import { HeaderAccessory } from '@lib/ui/components/HeaderAccessory'; @@ -12,16 +12,26 @@ import { Theme } from '@lib/ui/types/Theme'; type Props = { title: string; close: () => void; + scrollViewRef?: any; + setScrollOffset?: (value: number) => void; }; export const ModalContent = ({ children, close, title, + scrollViewRef, + setScrollOffset, }: PropsWithChildren) => { const styles = useStylesheet(createStyles); const { t } = useTranslation(); + const handleOnScroll = useCallback((event: any) => { + if (setScrollOffset) { + setScrollOffset(event.nativeEvent.contentOffset.y); + } + }, []); + return ( - {children} + + {children} + ); }; diff --git a/src/core/components/BottomModal.tsx b/src/core/components/BottomModal.tsx index 047298e5..c16e128c 100644 --- a/src/core/components/BottomModal.tsx +++ b/src/core/components/BottomModal.tsx @@ -1,5 +1,5 @@ -import { PropsWithChildren, useRef } from 'react'; -import { ScrollView, View } from 'react-native'; +import { PropsWithChildren, useCallback } from 'react'; +import { View } from 'react-native'; import Modal from 'react-native-modal'; import { SCREEN_HEIGHT, SCREEN_WIDTH } from '@gorhom/bottom-sheet'; @@ -8,6 +8,8 @@ export type BottomModalProps = PropsWithChildren<{ visible: boolean; onClose?: () => void; dismissable?: boolean; + scrollOffset?: number; + scrollViewRef?: any; }>; export const BottomModal = ({ @@ -15,22 +17,21 @@ export const BottomModal = ({ visible, onClose, dismissable, + scrollOffset, + scrollViewRef, }: BottomModalProps) => { const handleCloseModal = () => { dismissable && onClose?.(); }; - const scrollViewRef = useRef(null); - - const handleScrollTo = (position: { - x?: number; - y?: number; - animated?: boolean; - }) => { - if (scrollViewRef.current) { - scrollViewRef.current.scrollTo(position); - } - }; + const handleScrollTo = useCallback( + (p: any) => { + if (scrollViewRef && scrollViewRef.current) { + scrollViewRef.current.scrollTo(p); + } + }, + [scrollViewRef], + ); return ( {children} diff --git a/src/features/teaching/screens/TeachingScreen.tsx b/src/features/teaching/screens/TeachingScreen.tsx index 888b2963..8b89256d 100644 --- a/src/features/teaching/screens/TeachingScreen.tsx +++ b/src/features/teaching/screens/TeachingScreen.tsx @@ -39,7 +39,7 @@ import { useGetExams } from '../../../core/queries/examHooks'; import { useGetStudent } from '../../../core/queries/studentHooks'; import { useGetSurveyCategories } from '../../../core/queries/surveysHooks'; import { GlobalStyles } from '../../../core/styles/GlobalStyles'; -import { formatFinalGrade } from '../../../utils/grades'; +import { formatFinalGrade, formatThirtiethsGrade } from '../../../utils/grades'; import { CourseListItem } from '../../courses/components/CourseListItem'; import { isCourseDetailed } from '../../courses/utils/courses'; import { ExamListItem } from '../components/ExamListItem'; @@ -209,7 +209,14 @@ export const TeachingScreen = ({ navigation }: Props) => { underlayColor={colors.touchableHighlight} > - + + { : null, )} color={colors.title} - valueStyle={{ paddingVertical: 10 }} /> diff --git a/src/features/transcript/components/CareerScreenModal.tsx b/src/features/transcript/components/CareerScreenModal.tsx index ce8ead44..ab948f4a 100644 --- a/src/features/transcript/components/CareerScreenModal.tsx +++ b/src/features/transcript/components/CareerScreenModal.tsx @@ -1,4 +1,4 @@ -import { ScrollView, StyleSheet, View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import { ModalContent } from '@lib/ui/components/ModalContent'; import { Text } from '@lib/ui/components/Text'; @@ -9,6 +9,8 @@ export const CareerScreenModal = ({ title, itemList, onDismiss, + scrollViewRef, + setScrollOffset, }: { title: string; itemList: { @@ -17,12 +19,19 @@ export const CareerScreenModal = ({ dot: boolean; }[]; onDismiss: () => void; + scrollViewRef?: any; + setScrollOffset?: (value: number) => void; }) => { const styles = useStylesheet(createStyles); return ( - - + + {itemList.map((item, index) => { if (item.content.description) { return ( @@ -50,7 +59,7 @@ export const CareerScreenModal = ({ ); } })} - + ); }; diff --git a/src/features/transcript/screens/CareerScreen.tsx b/src/features/transcript/screens/CareerScreen.tsx index a480755d..c6bec316 100644 --- a/src/features/transcript/screens/CareerScreen.tsx +++ b/src/features/transcript/screens/CareerScreen.tsx @@ -1,3 +1,4 @@ +import { useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { SafeAreaView, ScrollView, StyleSheet, View } from 'react-native'; @@ -58,11 +59,25 @@ export const CareerScreen = () => { close: closeBottomModal, } = useBottomModal(); + const [scrollOffset, setScrollOffset] = useState(0); + const scrollViewRef = useRef(null); const onPressAverageEvent = () => { showBottomModal( { }, ]} onDismiss={closeBottomModal} + scrollViewRef={scrollViewRef} + setScrollOffset={setScrollOffset} />, ); }; @@ -147,7 +164,12 @@ export const CareerScreen = () => { return ( <> - + { /> + <> + + + {t('transcriptMetricsScreen.weightedAverage')} + + + + + + + {t('transcriptMetricsScreen.averageLabel')}