@campxdev/react-blueprint 1.7.7 → 1.7.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/package.json +1 -1
  2. package/src/assets/images/svg/index.ts +1 -1
  3. package/src/components/Assets/ErrorPages/InternalServerError.tsx +2 -2
  4. package/src/components/Assets/ErrorPages/NoInternetConnection.tsx +2 -2
  5. package/src/components/Assets/ErrorPages/NoItemFound.tsx +2 -2
  6. package/src/components/Assets/ErrorPages/PageNotFound.tsx +2 -2
  7. package/src/components/Assets/ErrorPages/UnAuthorized.tsx +2 -2
  8. package/src/components/Assets/Icons/IconComponents/HamburgerMenuIcon.tsx +20 -0
  9. package/src/components/Assets/Icons/IconComponents/ObeIcon.tsx +3 -3
  10. package/src/components/Assets/Icons/IconComponents/ReportIssueIcon.tsx +53 -0
  11. package/src/components/Assets/Icons/Icons.tsx +4 -0
  12. package/src/components/DataDisplay/DataTable/DataTable.tsx +1 -3
  13. package/src/components/DataDisplay/EmptyIllustration/EmptyIllustration.tsx +2 -2
  14. package/src/components/DataDisplay/StatusCard/StatusCard.tsx +67 -0
  15. package/src/components/DataDisplay/export.ts +1 -0
  16. package/src/components/Feedback/Snackbar/Snackbar.tsx +37 -26
  17. package/src/components/Input/SingleSelect/components/SingleInput.tsx +13 -10
  18. package/src/components/Layout/AppHeader/AppHeader.tsx +39 -22
  19. package/src/components/Layout/AppHeader/styles/styles.tsx +15 -1
  20. package/src/components/Navigation/Breadcrumbs/Breadcrumbs.tsx +4 -0
  21. package/src/components/Navigation/PreviewFiles/PreviewFiles.tsx +115 -21
  22. package/src/components/Navigation/Sidebar/MenuBar.tsx +176 -0
  23. package/src/components/Navigation/Sidebar/Sidebar.tsx +94 -190
  24. package/src/components/Navigation/Sidebar/styles.tsx +24 -9
  25. package/src/components/Navigation/TabsContainer/TabsContainer.tsx +4 -0
  26. package/src/components/Navigation/UploadDialog/Styles.tsx +1 -1
  27. package/src/components/Navigation/UploadDialog/UploadDialog.tsx +17 -27
  28. package/src/components/Navigation/UploadDialog/UploadDialogContainer.tsx +9 -7
  29. package/src/stories/DataDisplay/StatusCard.stories.tsx +62 -0
  30. package/src/stories/Input/SingleSelect.stories.tsx +6 -1
  31. package/src/stories/Navigation/PreviewFiles.stories.tsx +33 -64
  32. package/src/stories/Navigation/TabsContainer.stories.tsx +11 -1
  33. package/src/themes/commonTheme.ts +7 -4
@@ -4,20 +4,24 @@ import {
4
4
  ListItemButton,
5
5
  ListItemIcon,
6
6
  Stack,
7
+ StackProps,
7
8
  styled,
8
9
  } from '@mui/material';
9
10
  import { Link } from 'react-router-dom';
10
11
 
12
+ interface StyledSidebarContainerProps extends StackProps {
13
+ collapsed?: boolean;
14
+ }
15
+
11
16
  export const createSidebarStyles = (collapsed: boolean) => {
12
- const StyledSidebarContainer = styled(Stack)(({ theme }) => {
13
- return {
14
- backgroundColor: theme.palette.surface.defaultBackground,
15
- height: '100%',
16
- width: collapsed ? '60px' : '250px',
17
- justifyContent: 'space-between',
18
- overflow: 'hidden',
19
- };
20
- });
17
+ const StyledSidebarContainer = styled(Stack, {
18
+ shouldForwardProp: (prop) => prop !== 'collapsed',
19
+ })<StyledSidebarContainerProps>(({ theme, collapsed }) => ({
20
+ backgroundColor: theme.palette.surface.defaultBackground,
21
+ height: '100%',
22
+ justifyContent: collapsed ? 'space-between' : 'flex-start',
23
+ overflow: 'hidden',
24
+ }));
21
25
 
22
26
  const StyledLogoArea = styled(Box)(
23
27
  ({ theme, collapsed }: { theme?: any; collapsed: boolean }) => ({
@@ -28,6 +32,12 @@ export const createSidebarStyles = (collapsed: boolean) => {
28
32
  backgroundColor: theme.palette.surface.paperBackground,
29
33
  height: '60px',
30
34
  borderRadius: '8px',
35
+
36
+ [theme.breakpoints.down('md')]: {
37
+ height: '50px',
38
+ width: '50px',
39
+ minWidth: '0px',
40
+ },
31
41
  }),
32
42
  );
33
43
 
@@ -37,6 +47,11 @@ export const createSidebarStyles = (collapsed: boolean) => {
37
47
  backgroundColor: theme.palette.surface.paperBackground,
38
48
  overflowY: 'auto',
39
49
  overflowX: 'hidden',
50
+
51
+ [theme.breakpoints.down('md')]: {
52
+ margin: '12px 0px',
53
+ width: '250px',
54
+ },
40
55
  '&::-webkit-scrollbar': {
41
56
  width: '0.3em',
42
57
  height: '0.2em',
@@ -21,6 +21,10 @@ const TabContent = styled(Box)(({ theme }) => ({
21
21
  backgroundColor: theme.palette.surface.paperBackground,
22
22
 
23
23
  '&::-webkit-scrollbar': { display: 'none' },
24
+
25
+ '@media (max-width:959px)': {
26
+ height: 'calc(100vh - 224px)',
27
+ },
24
28
  }));
25
29
 
26
30
  export const TabsContainer = ({
@@ -17,7 +17,7 @@ export const UploadDialogBox = styled(Box)<StyledBoxProps>(
17
17
  width: '100%',
18
18
  height: '250px',
19
19
  borderRadius: '7px',
20
- backgroundImage: `url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='7' ry='7' stroke='${strokeColor}' stroke-width='3' stroke-dasharray='4%2c 8' stroke-dashoffset='8' strokeLinecap='round'/%3e%3c/svg%3e")`,
20
+ backgroundImage: `url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='7' ry='7' stroke='${strokeColor}' strokeWidth='3' stroke-dasharray='4%2c 8' stroke-dashoffset='8' strokeLinecap='round'/%3e%3c/svg%3e")`,
21
21
  backgroundColor: theme.palette.surface.defaultBackground,
22
22
  justifyContent: 'center',
23
23
  alignItems: 'center',
@@ -1,6 +1,6 @@
1
- import { Box, Divider, IconButton, Stack } from '@mui/material';
1
+ import { Box, Divider, Stack } from '@mui/material';
2
2
  import { ReactNode, useEffect, useState } from 'react';
3
- import { Button, Icons } from '../../export';
3
+ import { Button } from '../../export';
4
4
  import { Dialog, DialogProps } from '../export';
5
5
  import { PreviewFiles } from '../PreviewFiles/PreviewFiles';
6
6
  import { LoadingUploadDialogContainer } from './LoadingUploadDialogContainer';
@@ -12,7 +12,7 @@ export type UploadDialogProps = {
12
12
  onDialogClose?: () => void;
13
13
  title?: string;
14
14
  sampleFileUrl?: string;
15
- onUpload: (file: File | null) => void;
15
+ onUpload: (file: File | File[]) => void;
16
16
  acceptFileType?: string;
17
17
  loading?: boolean;
18
18
  } & Omit<DialogProps, 'content' | 'onClose' | 'open'>;
@@ -29,7 +29,7 @@ export const UploadDialog = ({
29
29
  ...props
30
30
  }: UploadDialogProps) => {
31
31
  const [open, setOpen] = useState(false);
32
- const [file, setFile] = useState<File | null>(null);
32
+ const [files, setFiles] = useState<File[]>([]);
33
33
  const [uploading, setUploading] = useState(false);
34
34
 
35
35
  const onClose = () => {
@@ -42,18 +42,11 @@ export const UploadDialog = ({
42
42
  setOpen(true);
43
43
  };
44
44
 
45
- const handleDelete = (fileToDelete: File) => {
46
- setFile((prevFile) => {
47
- if (!prevFile) return null;
48
- return prevFile === fileToDelete ? null : prevFile;
49
- });
50
- };
51
-
52
45
  const handleUpload = async (close: () => void) => {
53
- if (file) {
46
+ if (files.length) {
54
47
  try {
55
48
  setUploading(true);
56
- await onUpload(file);
49
+ await onUpload(files.length == 1 ? files[0] : files);
57
50
  setUploading(false);
58
51
  } catch (error) {
59
52
  console.error('Upload failed:', error);
@@ -62,19 +55,13 @@ export const UploadDialog = ({
62
55
  }
63
56
  };
64
57
 
65
- const Actions = ({ file }: { file: File }) => (
66
- <IconButton onClick={() => handleDelete(file)}>
67
- <Icons.DeleteIcon />
68
- </IconButton>
69
- );
70
-
71
58
  useEffect(() => {
72
59
  setUploading(loading);
73
60
  }, [loading]);
74
61
 
75
62
  useEffect(() => {
76
63
  if (!uploading) {
77
- setFile(null);
64
+ setFiles([]);
78
65
  setOpen(false);
79
66
  }
80
67
  }, [uploading]);
@@ -96,7 +83,8 @@ export const UploadDialog = ({
96
83
  <LoadingUploadDialogContainer />
97
84
  ) : (
98
85
  <UploadDialogContainer
99
- setFile={setFile}
86
+ files={files}
87
+ setFiles={setFiles}
100
88
  acceptFileType={acceptFileType}
101
89
  />
102
90
  )}
@@ -110,13 +98,15 @@ export const UploadDialog = ({
110
98
  </a>
111
99
  )}
112
100
  </Box>
113
- {file && !uploading && (
101
+ {files.length && !uploading && (
114
102
  <PreviewFiles
115
103
  sx={{ marginTop: '16px' }}
116
- key={file.name + file.lastModified}
117
- files={file}
104
+ key={1}
105
+ files={files}
118
106
  label="Selected Files"
119
- actions={(file) => <Actions file={file} />}
107
+ onChange={(newFiles) => {
108
+ setFiles(newFiles);
109
+ }}
120
110
  />
121
111
  )}
122
112
  </Box>
@@ -128,7 +118,7 @@ export const UploadDialog = ({
128
118
  <Stack direction={'row'} gap={2} padding={'12px 16px'}>
129
119
  <Button
130
120
  variant="contained"
131
- disabled={!file || uploading}
121
+ disabled={!files.length || uploading}
132
122
  onClick={() => {
133
123
  handleUpload(close);
134
124
  }}
@@ -137,7 +127,7 @@ export const UploadDialog = ({
137
127
  </Button>
138
128
  <Button
139
129
  onClick={() => {
140
- setFile(null);
130
+ setFiles([]);
141
131
  close();
142
132
  }}
143
133
  disableRipple
@@ -4,22 +4,24 @@ import { AnimatedGIFs } from '../../../assets/images/gif';
4
4
  import { ImageContainer, UploadDialogBox } from './Styles';
5
5
 
6
6
  export const UploadDialogContainer = ({
7
- setFile,
7
+ setFiles,
8
+ files,
8
9
  acceptFileType,
9
10
  }: {
10
- setFile: React.Dispatch<React.SetStateAction<File | null>>;
11
+ files: File[];
12
+ setFiles: React.Dispatch<React.SetStateAction<File[]>>;
11
13
  acceptFileType?: string;
12
14
  }) => {
13
15
  const theme = useTheme();
14
16
  const [dragging, setDragging] = useState(false);
15
17
 
16
- const resetFile = useCallback(() => setFile(null), [setFile]);
18
+ const resetFiles = useCallback(() => setFiles([]), [setFiles]);
17
19
 
18
20
  const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
19
- const selectedFile = event.target.files?.[0];
21
+ const selectedFile = event.target.files;
20
22
  if (selectedFile) {
21
- resetFile();
22
- setFile(selectedFile);
23
+ resetFiles();
24
+ setFiles(Array.from(selectedFile));
23
25
  event.target.value = '';
24
26
  }
25
27
  };
@@ -36,7 +38,7 @@ export const UploadDialogContainer = ({
36
38
  setDragging(false);
37
39
  const droppedFile = event.dataTransfer.files?.[0];
38
40
  if (droppedFile) {
39
- setFile(droppedFile);
41
+ setFiles([...files, droppedFile]);
40
42
  event.dataTransfer.clearData();
41
43
  }
42
44
  };
@@ -0,0 +1,62 @@
1
+ import { Meta, StoryFn } from '@storybook/react';
2
+ import { StatusCard, StatusCardProps } from '../../components/export';
3
+
4
+ export default {
5
+ title: 'DataDisplay/StatusCard',
6
+ component: StatusCard,
7
+ argTypes: {
8
+ color: {
9
+ control: 'select',
10
+ options: ['green', 'red', 'orange', 'yellow', 'blue', 'pink'],
11
+ description: 'The color of the status card',
12
+ },
13
+ text: {
14
+ control: 'text',
15
+ description: 'The text displayed on the status card',
16
+ },
17
+ },
18
+ } as Meta<StatusCardProps>;
19
+
20
+ const Template: StoryFn<StatusCardProps> = (args) => <StatusCard {...args} />;
21
+
22
+ export const GreenStatus = Template.bind({});
23
+ GreenStatus.args = {
24
+ color: 'green',
25
+ text: 'Success',
26
+ };
27
+
28
+ export const RedStatus = Template.bind({});
29
+ RedStatus.args = {
30
+ color: 'red',
31
+ text: 'Error',
32
+ };
33
+
34
+ export const OrangeStatus = Template.bind({});
35
+ OrangeStatus.args = {
36
+ color: 'orange',
37
+ text: 'Warning',
38
+ };
39
+
40
+ export const YellowStatus = Template.bind({});
41
+ YellowStatus.args = {
42
+ color: 'yellow',
43
+ text: 'Caution',
44
+ };
45
+
46
+ export const BlueStatus = Template.bind({});
47
+ BlueStatus.args = {
48
+ color: 'blue',
49
+ text: 'Info',
50
+ };
51
+
52
+ export const PinkStatus = Template.bind({});
53
+ PinkStatus.args = {
54
+ color: 'pink',
55
+ text: 'Special',
56
+ };
57
+
58
+ export const CustomStatus = Template.bind({});
59
+ CustomStatus.args = {
60
+ color: 'red',
61
+ text: 'Custom Status Text',
62
+ };
@@ -8,7 +8,7 @@ import {
8
8
  // Make sure your TextField import is correct
9
9
 
10
10
  const topFilms = [
11
- { label: 'The Shawshank Redemption', value: 1994 },
11
+ { label: 'The Shawshank Redemption', value: 1994, subLabel: 'Gowtham' },
12
12
  { label: 'The Godfather', value: 1972 },
13
13
  { label: 'The Godfather: Part II', value: 1974 },
14
14
  { label: 'The Dark Knight', value: 2008 },
@@ -85,6 +85,7 @@ export const Default = {
85
85
  args: {
86
86
  required: true,
87
87
  label: 'Single Select',
88
+ onChange: () => {},
88
89
  },
89
90
  };
90
91
  export const Primary = {
@@ -93,6 +94,7 @@ export const Primary = {
93
94
  options: topFilms,
94
95
  label: 'Single Select',
95
96
  required: false,
97
+ onChange: () => {},
96
98
  },
97
99
  };
98
100
 
@@ -102,6 +104,7 @@ export const WithAPIEndPoint = {
102
104
  optionsApiEndPoint: 'dropdowns/classrooms',
103
105
  label: 'Select Classroom',
104
106
  required: false,
107
+ onChange: () => {},
105
108
  },
106
109
  };
107
110
 
@@ -112,6 +115,7 @@ export const SingleSelectWithValue = {
112
115
  options: topFilms,
113
116
  required: true,
114
117
  value: 1994,
118
+ onChange: () => {},
115
119
  },
116
120
  };
117
121
 
@@ -122,5 +126,6 @@ export const SingleSelectWithValueAndApi = {
122
126
  label: 'Select Classroom',
123
127
  required: true,
124
128
  value: 1,
129
+ onChange: () => {},
125
130
  },
126
131
  };
@@ -1,11 +1,7 @@
1
- import { Box, IconButton } from '@mui/material';
1
+ import { Box } from '@mui/material';
2
2
  import { Meta, StoryFn } from '@storybook/react';
3
3
  import { useState } from 'react';
4
- import {
5
- Icons,
6
- PreviewFiles,
7
- PreviewFilesProps,
8
- } from '../../components/export';
4
+ import { PreviewFiles, PreviewFilesProps } from '../../components/export';
9
5
 
10
6
  export default {
11
7
  title: 'Navigation/PreviewFiles',
@@ -17,73 +13,36 @@ export default {
17
13
  } as Meta<PreviewFilesProps>;
18
14
 
19
15
  const createTestFile = (name: string, type: string, size: number): File => {
20
- return new File(['dummy content'], name, {
21
- type: type,
22
- lastModified: Date.now(),
23
- });
24
- };
25
-
26
- const imageFile = createTestFile('image.jpg', 'image/jpeg', 1024 * 1024);
27
- const pdfFile = createTestFile(
28
- 'document.pdf',
29
- 'application/pdf',
30
- 2 * 1024 * 1024,
31
- );
32
- const excelFile = createTestFile(
33
- 'spreadsheet.xlsx',
34
- 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
35
- 512 * 1024,
36
- );
37
- const wordFile = createTestFile(
38
- 'report.docx',
39
- 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
40
- 3 * 1024 * 1024,
41
- );
42
- const txtFile = createTestFile('notes.txt', 'text/plain', 10 * 1024);
16
+ return new File(['dummy content'], name, {
17
+ type: type,
18
+ lastModified: Date.now(),
19
+ });
20
+ },
21
+ txtFile = createTestFile('notes.txt', 'text/plain', 10 * 1024),
22
+ imageFile = createTestFile('image.jpg', 'image/jpeg', 1024 * 1024),
23
+ pdfFile = createTestFile('document.pdf', 'application/pdf', 2 * 1024 * 1024),
24
+ excelFile = createTestFile(
25
+ 'spreadsheet.xlsx',
26
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
27
+ 512 * 1024,
28
+ ),
29
+ wordFile = createTestFile(
30
+ 'report.docx',
31
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
32
+ 3 * 1024 * 1024,
33
+ );
43
34
 
44
35
  const Template: StoryFn<PreviewFilesProps> = (args) => {
45
36
  const [files, setFiles] = useState<PreviewFilesProps['files']>(args.files);
46
37
 
47
- const Actions = ({ file }: { file: File }) => {
48
- const handleDownload = (file: File) => {
49
- const url = URL.createObjectURL(file);
50
- const a = document.createElement('a');
51
- a.href = url;
52
- a.download = file.name;
53
- document.body.appendChild(a);
54
- a.click();
55
- document.body.removeChild(a);
56
- URL.revokeObjectURL(url);
57
- };
58
-
59
- const handleDelete = (fileToDelete: File) => {
60
- setFiles((prevFiles) => {
61
- if (!prevFiles) return null;
62
- if (Array.isArray(prevFiles)) {
63
- return prevFiles.filter((file) => file !== fileToDelete);
64
- }
65
- return prevFiles === fileToDelete ? null : prevFiles;
66
- });
67
- };
68
-
69
- return (
70
- <>
71
- <IconButton onClick={() => handleDownload(file)}>
72
- <Icons.DownloadIcon />
73
- </IconButton>
74
- <IconButton onClick={() => handleDelete(file)}>
75
- <Icons.DeleteIcon />
76
- </IconButton>
77
- </>
78
- );
79
- };
80
-
81
38
  return (
82
39
  <Box sx={{ width: '400px', padding: '20px' }}>
83
40
  <PreviewFiles
84
41
  {...args}
85
42
  files={files}
86
- actions={(file) => <Actions file={file} />}
43
+ onChange={(newFiles, file) => {
44
+ setFiles(newFiles);
45
+ }}
87
46
  />
88
47
  </Box>
89
48
  );
@@ -93,6 +52,7 @@ export const MultipleFiles = Template.bind({});
93
52
  MultipleFiles.args = {
94
53
  files: [imageFile, pdfFile, excelFile, wordFile, txtFile],
95
54
  label: 'Multiple Files',
55
+ showDownload: true,
96
56
  };
97
57
 
98
58
  export const SingleFile = Template.bind({});
@@ -112,3 +72,12 @@ CustomLabel.args = {
112
72
  files: [imageFile, pdfFile],
113
73
  label: 'Custom Label for Files',
114
74
  };
75
+
76
+ export const FilesFromUrls = Template.bind({});
77
+ FilesFromUrls.args = {
78
+ files: [
79
+ 'https://d2738wjykzpnhe.cloudfront.net/aupulse/aupulse/student-service-requests/PDFs/a_ravi_teja_bonafide.pdf',
80
+ 'https://d2738wjykzpnhe.cloudfront.net/aupulse/signatures/image/6ec02b72-0884-474a-8c73-32ae77b6e113.jpeg',
81
+ ],
82
+ label: 'Files from Urls',
83
+ };
@@ -39,7 +39,17 @@ export const Primary: Story = {
39
39
  },
40
40
  {
41
41
  key: '3',
42
- label: 'year 3',
42
+ label: 'Leaves',
43
+ component: <>Third Year</>,
44
+ },
45
+ {
46
+ key: '4',
47
+ label: 'Class Reassignments',
48
+ component: <>Third Year</>,
49
+ },
50
+ {
51
+ key: '5',
52
+ label: 'Student Service Requests',
43
53
  component: <>Third Year</>,
44
54
  },
45
55
  ],
@@ -532,12 +532,13 @@ export const getCommonTheme = (mode: Theme) => {
532
532
  },
533
533
  indicator: {
534
534
  display: 'flex',
535
- justifyContent: 'center',
535
+ justifyContent: 'flex-start',
536
536
  backgroundColor: 'transparent',
537
- height: '3px',
537
+ height: '4px',
538
538
  '& .MuiTabs-indicatorSpan': {
539
- borderRadius: '30px',
540
- width: '60%',
539
+ width: '60px',
540
+ marginLeft: '15px',
541
+ borderRadius: '20px',
541
542
  backgroundColor: ColorTokens.tertiary.main,
542
543
  },
543
544
  },
@@ -552,9 +553,11 @@ export const getCommonTheme = (mode: Theme) => {
552
553
  color: ColorTokens.text.secondary,
553
554
  fontSize: '14px',
554
555
  fontWeight: '600',
556
+ fontFamily: 'Poppins',
555
557
  textTransform: 'initial',
556
558
  minHeight: '50px',
557
559
  paddingBottom: '0px',
560
+ justifyContent: 'flex-start',
558
561
  '&.Mui-selected': {
559
562
  color: ColorTokens.text.primary,
560
563
  },