Skip to content

Commit 9898430

Browse files
Merge branch 'main' into colin/system-options-nits
2 parents c4b9704 + 0c12519 commit 9898430

26 files changed

+835
-509
lines changed

hooks/useChangeManagement.ts

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Location, SubjectAPI } from '@utils/types';
22
import { useCallback, useEffect, useState } from 'react';
3-
3+
import { useCustomToast } from '../src/components/CustomToast';
44
/**
55
* This hook manages pending changes to system entities using an entity-based tracking approach.
66
* Instead of tracking individual atomic changes, it stores the final state of each modified entity.
@@ -18,7 +18,6 @@ interface UseChangeManagementProps {
1818
locations: Location[];
1919
absenceCap: number;
2020
onRefresh?: () => void;
21-
toast?: any;
2221
}
2322

2423
interface UseChangeManagementReturn {
@@ -42,8 +41,9 @@ export const useChangeManagement = ({
4241
locations: initialLocations,
4342
absenceCap: initialAbsenceCap,
4443
onRefresh,
45-
toast,
4644
}: UseChangeManagementProps): UseChangeManagementReturn => {
45+
const showToast = useCustomToast();
46+
4747
// Store maps of pending entity changes
4848
const [pendingSubjects, setPendingSubjects] = useState<
4949
Map<number, SubjectAPI | null>
@@ -396,12 +396,10 @@ export const useChangeManagement = ({
396396
}
397397

398398
// Show success toast
399-
if (toast) {
400-
toast({
401-
title: 'Changes saved',
399+
if (showToast) {
400+
showToast({
401+
description: 'Changes saved.',
402402
status: 'success',
403-
duration: 3000,
404-
isClosable: true,
405403
});
406404
}
407405

@@ -416,13 +414,10 @@ export const useChangeManagement = ({
416414
}
417415

418416
// Show error toast if there was a problem
419-
if (!success && toast) {
420-
toast({
421-
title: 'Error',
417+
if (!success && showToast) {
418+
showToast({
422419
description: errorMessage || 'Failed to save changes',
423420
status: 'error',
424-
duration: 5000,
425-
isClosable: true,
426421
});
427422
}
428423

src/components/CustomToast.tsx

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { Alert, AlertIcon, Box, Text, useToast } from '@chakra-ui/react';
2+
import { ReactNode } from 'react';
3+
4+
type ToastStatus = 'success' | 'error';
5+
6+
interface CustomToastProps {
7+
description?: string | ReactNode;
8+
status?: ToastStatus;
9+
}
10+
11+
export const CustomToast: React.FC<CustomToastProps> = ({
12+
description,
13+
status = 'success',
14+
}) => {
15+
return (
16+
<Alert
17+
status={status}
18+
variant="subtle"
19+
bg="white"
20+
border="1px solid"
21+
borderColor={status === 'error' ? '#BF3232' : '#5A8934'}
22+
borderRadius="md"
23+
px={3}
24+
py={3}
25+
width="360px"
26+
alignItems="center"
27+
boxShadow="md"
28+
sx={{
29+
'& [data-status="success"] svg': {
30+
color: '#5A8934',
31+
},
32+
'& [data-status="error"] svg': {
33+
color: '#BF3232',
34+
},
35+
}}
36+
>
37+
<AlertIcon boxSize="30px" />
38+
<Box pl={2}>
39+
{typeof description === 'string' ? (
40+
<Text fontSize="14px" color="black">
41+
{description}
42+
</Text>
43+
) : (
44+
<Box fontSize="14px" color="black">
45+
{description}
46+
</Box>
47+
)}
48+
</Box>
49+
</Alert>
50+
);
51+
};
52+
53+
export const useCustomToast = () => {
54+
const toast = useToast();
55+
56+
return ({
57+
description,
58+
status = 'success',
59+
}: {
60+
description?: string | ReactNode;
61+
status?: string;
62+
}) => {
63+
const validStatuses = ['success', 'error'] as const;
64+
const safeStatus = validStatuses.includes(status as ToastStatus)
65+
? (status as ToastStatus)
66+
: 'success';
67+
68+
toast({
69+
isClosable: true,
70+
position: 'bottom-left',
71+
render: () => (
72+
<CustomToast description={description} status={safeStatus} />
73+
),
74+
});
75+
};
76+
};

src/components/absences/FileUpload.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { Box, Image, Input, Text, useToast } from '@chakra-ui/react';
1+
import { Box, Image, Input, Text } from '@chakra-ui/react';
22
import { LessonPlanFile } from '@utils/types';
33
import { useRef, useState } from 'react';
4+
import { useCustomToast } from '../CustomToast';
45

56
interface FileUploadProps {
67
lessonPlan: File | null;
@@ -17,18 +18,15 @@ export const FileUpload: React.FC<FileUploadProps> = ({
1718
}) => {
1819
const [isDragging, setIsDragging] = useState(false);
1920
const inputRef = useRef<HTMLInputElement>(null);
20-
const toast = useToast();
21+
const showToast = useCustomToast();
2122

2223
const validateAndSetFile = (file: File) => {
2324
if (file.type === 'application/pdf' || file.name.endsWith('.pdf')) {
2425
setLessonPlan(file);
2526
} else {
26-
toast({
27-
title: 'Invalid File Type',
27+
showToast({
2828
description: 'Please upload a valid PDF file.',
2929
status: 'error',
30-
duration: 5000,
31-
isClosable: true,
3230
});
3331
}
3432
};

0 commit comments

Comments
 (0)