Skip to content

Commit

Permalink
fix(exams): provide a feedback when an exam cannot be booked, Ref #495 (
Browse files Browse the repository at this point in the history
#499)

* fix(exams): provide a feedback when an exam cannot be booked, Ref #495

* fix(exams): fix icon not bookable and places not available, Refs #495
  • Loading branch information
FabrizioCostaMedich authored Jun 17, 2024
1 parent 03b9104 commit e1f6822
Show file tree
Hide file tree
Showing 12 changed files with 117 additions and 10 deletions.
1 change: 1 addition & 0 deletions assets/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@
"location": "Location",
"noClassroom": "No place specified for this exam call",
"noLocation": "Room to be defined",
"notAvailable": "Booking not available",
"notes": "Notes",
"title": "Exam call"
},
Expand Down
1 change: 1 addition & 0 deletions assets/translations/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@
"location": "Luogo",
"noClassroom": "Nessun luogo specificato per questo appello",
"noLocation": "Aula da definire",
"notAvailable": "Prenotazione non disponibile",
"notes": "Note",
"title": "Appello"
},
Expand Down
1 change: 1 addition & 0 deletions lib/ui/components/CtaButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ export const CtaButton = ({
{
color: variant === 'filled' ? colors.white : color,
},
disabled ? { color: colors.disableTitle } : undefined,
]}
baseStyle={{ fontWeight: fontWeights.medium }}
>
Expand Down
41 changes: 41 additions & 0 deletions lib/ui/components/ErrorCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { PropsWithChildren } from 'react';
import { Platform, ViewProps } from 'react-native';

import { Card } from '@lib/ui/components/Card';
import { Text } from '@lib/ui/components/Text';
import { useTheme } from '@lib/ui/hooks/useTheme';

type Props = PropsWithChildren<
ViewProps & {
text: string;
}
>;

export const ErrorCard = ({ text, ...rest }: Props) => {
const { spacing, fontSizes, colors } = useTheme();
return (
<Card
accessible={Platform.select({ android: true, ios: false })}
rounded
// rounded={rounded ?? Platform.select({ android: false })}
spaced
translucent={false}
style={{
borderStyle: 'solid',
borderWidth: 1,
borderColor: colors.errorCardBorder,
}}
{...rest}
>
<Text
style={{
padding: spacing[5],
color: colors.errorCardText,
fontSize: fontSizes.sm,
}}
>
{text.charAt(0).toUpperCase() + text.slice(1).toLowerCase()}
</Text>
</Card>
);
};
3 changes: 3 additions & 0 deletions lib/ui/types/Theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export interface Colors {
heading: string;
subHeading: string;
prose: string;
disableTitle: string;
longProse: string;
secondaryText: string;
caption: string;
Expand All @@ -96,6 +97,8 @@ export interface Colors {
deadlineCardBorder: string;
examCardBorder: string;
lectureCardSecondary: string;
errorCardText: string;
errorCardBorder: string;
translucentSurface: string;
white: string;
}
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"@miblanchard/react-native-slider": "^2.2.0",
"@openspacelabs/react-native-zoomable-view": "^2.1.5",
"@orama/orama": "^2.0.0-beta.8",
"@polito/api-client": "^1.0.0-ALPHA.63",
"@polito/api-client": "^1.0.0-ALPHA.64",
"@react-native-async-storage/async-storage": "^1.18.2",
"@react-native-clipboard/clipboard": "^1.12.1",
"@react-native-community/blur": "^4.3.0",
Expand Down
3 changes: 3 additions & 0 deletions src/core/themes/dark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const darkTheme: Theme = {
subHeading: lightTheme.palettes.info[400],
title: 'white',
prose: lightTheme.palettes.text[50],
disableTitle: lightTheme.palettes.gray[700],
longProse: lightTheme.palettes.text[50],
secondaryText: lightTheme.palettes.text[400],
caption: lightTheme.palettes.text[500],
Expand All @@ -31,5 +32,7 @@ export const darkTheme: Theme = {
touchableHighlight: 'rgba(255, 255, 255, .08)',
lectureCardSecondary: lightTheme.palettes.gray[300],
tabBarInactive: lightTheme.palettes.gray[400],
errorCardText: lightTheme.palettes.rose[200],
errorCardBorder: lightTheme.palettes.rose[500],
},
};
3 changes: 3 additions & 0 deletions src/core/themes/light.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ export const lightTheme: Theme = {
subHeading: lightBlue[700],
title: navy[700],
prose: gray[800],
disableTitle: '#FFFFFF',
longProse: gray[800],
secondaryText: gray[500],
caption: gray[500],
Expand All @@ -162,6 +163,8 @@ export const lightTheme: Theme = {
deadlineCardBorder: red[700],
examCardBorder: orange[600],
lectureCardSecondary: gray[600],
errorCardText: rose[700],
errorCardBorder: rose[500],
},
palettes: {
navy,
Expand Down
9 changes: 6 additions & 3 deletions src/features/teaching/components/ExamCTA.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ export const ExamCTA = ({ exam }: Props) => {

const examRequestable = exam?.status === ExamStatusEnum.Requestable;
const examAvailable = exam?.status === ExamStatusEnum.Available;
const examUnavailable = exam?.status === ExamStatusEnum.Unavailable;

const confirm = useConfirmationDialog();

const disabledStatuses = [
ExamStatusEnum.RequestAccepted,
ExamStatusEnum.RequestRejected,
ExamStatusEnum.Unavailable,
] as ExamStatusEnum[];
const action = async () => {
if (examRequestable) {
Expand Down Expand Up @@ -80,15 +80,18 @@ export const ExamCTA = ({ exam }: Props) => {
<CtaButton
destructive={!examAvailable && !examRequestable}
title={
examRequestable
examUnavailable
? t('examScreen.notAvailable')
: examRequestable
? t('examScreen.ctaRequest')
: examAvailable
? t('examScreen.ctaBook')
: t('examScreen.ctaCancel')
}
action={action}
loading={mutationsLoading}
disabled={!onlineManager.isOnline()}
disabled={!onlineManager.isOnline() || examUnavailable}
variant="filled"
/>
);
};
43 changes: 41 additions & 2 deletions src/features/teaching/screens/ExamScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@ import { useTranslation } from 'react-i18next';
import { SafeAreaView, ScrollView, View } from 'react-native';

import { faNoteSticky } from '@fortawesome/free-regular-svg-icons';
import { faHourglassEnd, faUsers } from '@fortawesome/free-solid-svg-icons';
import {
faHourglassEnd,
faTriangleExclamation,
faUsers,
} from '@fortawesome/free-solid-svg-icons';
import { Col } from '@lib/ui/components/Col';
import { CtaButtonSpacer } from '@lib/ui/components/CtaButton';
import { ErrorCard } from '@lib/ui/components/ErrorCard';
import { Icon } from '@lib/ui/components/Icon';
import { ListItem } from '@lib/ui/components/ListItem';
import { OverviewList } from '@lib/ui/components/OverviewList';
Expand All @@ -16,6 +21,7 @@ import { ScreenDateTime } from '@lib/ui/components/ScreenDateTime';
import { ScreenTitle } from '@lib/ui/components/ScreenTitle';
import { Text } from '@lib/ui/components/Text';
import { useTheme } from '@lib/ui/hooks/useTheme';
import { ExamStatusEnum } from '@polito/api-client';
import { NativeStackScreenProps } from '@react-navigation/native-stack';

import { BottomBarSpacer } from '../../../core/components/BottomBarSpacer';
Expand All @@ -36,6 +42,7 @@ import { ExamCpdModalContent } from '../../surveys/components/ExamCpdModalConten
import { ExamCTA } from '../components/ExamCTA';
import { ExamStatusBadge } from '../components/ExamStatusBadge';
import { TeachingStackParamList } from '../components/TeachingNavigator';
import { getExam, isExamPassed } from '../utils/exam';

type Props = NativeStackScreenProps<TeachingStackParamList, 'Exam'>;

Expand Down Expand Up @@ -187,14 +194,46 @@ export const ExamScreen = ({ route, navigation }: Props) => {
: t('common.dateToBeDefined')
}
subtitle={t('examScreen.bookingEndsAt')}
trailingItem={
exam?.status === ExamStatusEnum.Unavailable &&
exam?.bookingEndsAt &&
isExamPassed(exam.bookingEndsAt) ? (
<Icon
icon={faTriangleExclamation}
color="red"
size={fontSizes.md}
/>
) : undefined
}
/>

<ListItem
leadingItem={<Icon icon={faUsers} size={fontSizes['2xl']} />}
inverted
title={`${exam?.bookedCount}`}
/* check using undefined since the fields can be 0 */
title={
exam?.availableCount !== undefined &&
exam?.bookedCount !== undefined
? getExam(exam.bookedCount, exam.availableCount)
: ''
}
subtitle={t('examScreen.bookedCount')}
trailingItem={
exam?.status === ExamStatusEnum.Unavailable &&
exam?.availableCount &&
exam.availableCount === 0 ? (
<Icon
icon={faTriangleExclamation}
color="red"
size={fontSizes.md}
/>
) : undefined
}
/>
</OverviewList>
{exam?.feedback && exam?.status === ExamStatusEnum.Unavailable && (
<ErrorCard text={exam.feedback} />
)}
<CtaButtonSpacer />
<BottomBarSpacer />
</SafeAreaView>
Expand Down
12 changes: 12 additions & 0 deletions src/features/teaching/utils/exam.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { DateTime } from 'luxon';

export const isExamPassed = (bookingEndsAt: Date) => {
return DateTime.now().setZone('Europe/Rome').toJSDate() > bookingEndsAt;
};

export const getExam = (bookedCount: number, availableCount: number) => {
if (availableCount === 999) {
return `${bookedCount}`;
}
return `${bookedCount}/${availableCount + bookedCount}`;
};

0 comments on commit e1f6822

Please sign in to comment.