@campxdev/react-blueprint 1.6.3 → 1.6.5

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.
@@ -4,9 +4,8 @@ import React from 'react';
4
4
  import { Provider } from 'react-redux';
5
5
  import { BrowserRouter } from 'react-router-dom';
6
6
  import { useDarkMode } from 'storybook-dark-mode';
7
- import { store } from '../src/redux/export';
7
+ import { store } from '../src/redux/store';
8
8
  import { MuiThemeProvider } from '../src/themes/MuiThemeProvider';
9
-
10
9
  import { DarkColorTokens } from '../src/themes/colorTokens/darkColorTokens';
11
10
  import { LightColorTokens } from '../src/themes/colorTokens/lightColorTokens';
12
11
  import { darkTheme } from '../src/themes/darkTheme';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@campxdev/react-blueprint",
3
- "version": "1.6.3",
3
+ "version": "1.6.5",
4
4
  "main": "./export.ts",
5
5
  "private": false,
6
6
  "dependencies": {
package/src/App.tsx CHANGED
@@ -1,6 +1,7 @@
1
- import { Stack } from '@mui/material';
1
+ import { LocalizationProvider } from '@mui/x-date-pickers';
2
+ import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFnsV3';
2
3
  import './App.css';
3
- import { Timeline, Typography } from './components/export';
4
+ import { ActivityLogView } from './components/export';
4
5
 
5
6
  interface RowType {
6
7
  lastName: string;
@@ -17,50 +18,74 @@ function App() {
17
18
  paddingLeft: '16px',
18
19
  }}
19
20
  >
20
- <Timeline
21
- position="left"
22
- timelineItems={[
23
- {
24
- timelineContent: (
25
- <Stack>
26
- <Typography>Requirement Gathering</Typography>
27
- </Stack>
28
- ),
29
- variant: 'info',
30
- },
31
- {
32
- timelineContent: (
33
- <Stack>
34
- <Typography>Requirement Gathering</Typography>
35
- </Stack>
36
- ),
37
- variant: 'warning',
38
- },
39
- {
40
- timelineContent: (
41
- <Stack>
42
- <Typography>Requirement Gathering</Typography>
43
- </Stack>
44
- ),
45
- variant: 'success',
46
- },
47
- {
48
- timelineContent: (
49
- <Stack>
50
- <Typography>Requirement Gathering</Typography>
51
- </Stack>
52
- ),
53
- },
54
- {
55
- timelineContent: (
56
- <Stack>
57
- <Typography>Requirement Gathering</Typography>
58
- </Stack>
59
- ),
60
- variant: 'error',
61
- },
62
- ]}
63
- />
21
+ <LocalizationProvider dateAdapter={AdapterDateFns}>
22
+ <ActivityLogView
23
+ activitiesData={[
24
+ {
25
+ userName: 'ss',
26
+ action: 'create',
27
+ message: 's',
28
+ timestamp: new Date().toISOString(),
29
+ },
30
+ {
31
+ userName: 'ss',
32
+ action: 'update',
33
+ message: 's',
34
+ timestamp: new Date().toISOString(),
35
+ },
36
+ {
37
+ userName: 'ss',
38
+ action: 'update',
39
+ message: 's',
40
+ timestamp: new Date().toISOString(),
41
+ },
42
+ {
43
+ userName: 'ss',
44
+ action: 'update',
45
+ message: 's',
46
+ timestamp: new Date().toISOString(),
47
+ },
48
+ {
49
+ userName: 'ss',
50
+ action: 'update',
51
+ message: 's',
52
+ timestamp: new Date().toISOString(),
53
+ },
54
+ {
55
+ userName: 'ss',
56
+ action: 'delete',
57
+ message: 's',
58
+ timestamp: new Date().toISOString(),
59
+ },
60
+ {
61
+ userName: 'ss',
62
+ action: 'update',
63
+ message: 's',
64
+ timestamp: new Date().toISOString(),
65
+ },
66
+ {
67
+ userName: 'ss',
68
+ action: 'update',
69
+ message: 's',
70
+ timestamp: new Date().toISOString(),
71
+ },
72
+ ]}
73
+ isFetchingNextPage={false}
74
+ fetchNextPage={function (): void {
75
+ // throw new Error('Function not implemented.');
76
+ }}
77
+ hasNextPage={undefined}
78
+ fromDate={null}
79
+ toDate={null}
80
+ setFromDate={function (date: Date | null): void {
81
+ // throw new Error('Function not implemented.');
82
+ }}
83
+ setToDate={function (date: Date | null): void {
84
+ // throw new Error('Function not implemented.');
85
+ }}
86
+ isLoading={false}
87
+ />
88
+ </LocalizationProvider>
64
89
  </div>
65
90
  );
66
91
  }
@@ -0,0 +1,233 @@
1
+ import { TimelineContent, TimelineItem, TimelineSeparator } from '@mui/lab';
2
+ import { Box, Stack, Typography, useTheme } from '@mui/material';
3
+ import { format, isToday } from 'date-fns';
4
+ import _ from 'lodash';
5
+ import { useState } from 'react';
6
+ import { Button, DatePicker, Spinner } from '../../export';
7
+ import {
8
+ ActivityIcon,
9
+ convertUTCtoIST,
10
+ NoDataFound,
11
+ ScrollToTopButton,
12
+ useIntersectionObserver,
13
+ } from './service';
14
+ import {
15
+ StyledActivityLogViewBox,
16
+ StyledIconBox,
17
+ StyledSectionTitle,
18
+ StyledSpinnerBox,
19
+ StyledTimeline,
20
+ StyledTimelineConnector,
21
+ StyledTimelineDot,
22
+ } from './styles';
23
+
24
+ export type ActivityAction = 'create' | 'update' | 'delete';
25
+
26
+ export interface Activity {
27
+ userName: string;
28
+ action: ActivityAction;
29
+ message: string;
30
+ timestamp: string;
31
+ }
32
+
33
+ interface ActivityLogProps {
34
+ activitiesData: Activity[];
35
+ isFetchingNextPage: boolean;
36
+ fetchNextPage: () => void;
37
+ hasNextPage: boolean | undefined;
38
+ fromDate: Date | null;
39
+ toDate: Date | null;
40
+ setFromDate: (date: Date | null) => void;
41
+ setToDate: (date: Date | null) => void;
42
+ isLoading: boolean;
43
+ }
44
+
45
+ const ActivityLogFilter = ({
46
+ fromDate,
47
+ toDate,
48
+ setFromDate,
49
+ setToDate,
50
+ fetchNextPage,
51
+ isLoading,
52
+ }: {
53
+ fromDate: Date | null;
54
+ toDate: Date | null;
55
+ setFromDate: (date: Date | null) => void;
56
+ setToDate: (date: Date | null) => void;
57
+ fetchNextPage: () => void;
58
+ isLoading: boolean;
59
+ }) => {
60
+ const [localFromDate, setLocalFromDate] = useState(fromDate);
61
+ const [localToDate, setLocalToDate] = useState(toDate);
62
+
63
+ const handleFilterSubmit = () => {
64
+ setFromDate(localFromDate);
65
+ setToDate(localToDate);
66
+ fetchNextPage();
67
+ };
68
+
69
+ const clearFilters = () => {
70
+ setLocalFromDate(null);
71
+ setLocalToDate(null);
72
+ setFromDate(null);
73
+ setToDate(null);
74
+ };
75
+
76
+ return (
77
+ <Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 3 }}>
78
+ <Box sx={{ display: 'flex', alignItems: 'center' }}>
79
+ <DatePicker
80
+ key={`from-date-${localFromDate}`}
81
+ label="From Date"
82
+ value={localFromDate}
83
+ onChange={setLocalFromDate}
84
+ disabled={isLoading}
85
+ />
86
+ <DatePicker
87
+ key={`to-date-${localToDate}`}
88
+ label="To Date"
89
+ value={localToDate}
90
+ onChange={setLocalToDate}
91
+ disabled={isLoading}
92
+ />
93
+ <Stack spacing={1} direction="row" ml={1} mt={2}>
94
+ <Button
95
+ onClick={handleFilterSubmit}
96
+ variant="contained"
97
+ disabled={isLoading}
98
+ loading={isLoading}
99
+ >
100
+ Submit
101
+ </Button>
102
+ </Stack>
103
+ </Box>
104
+ <Box sx={{ display: 'flex', mt: 3.5 }}>
105
+ <Button onClick={clearFilters} disabled={isLoading}>
106
+ Clear
107
+ </Button>
108
+ </Box>
109
+ </Box>
110
+ );
111
+ };
112
+
113
+ export const ActivityLogView = ({
114
+ activitiesData,
115
+ isFetchingNextPage,
116
+ fetchNextPage,
117
+ hasNextPage,
118
+ fromDate,
119
+ toDate,
120
+ setFromDate,
121
+ setToDate,
122
+ isLoading,
123
+ }: ActivityLogProps) => {
124
+ const theme = useTheme();
125
+ const lastItemRef = useIntersectionObserver(() => {
126
+ if (!isFetchingNextPage && hasNextPage) fetchNextPage();
127
+ });
128
+
129
+ const loading = isLoading && activitiesData.length === 0;
130
+
131
+ // Group activities by Today and Past Week and Older
132
+ const groupedActivities = _.groupBy(activitiesData, (activity) => {
133
+ const activityDate = convertUTCtoIST(activity.timestamp);
134
+ return isToday(activityDate)
135
+ ? 'Today'
136
+ : activityDate > new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)
137
+ ? 'Past Week'
138
+ : 'Older';
139
+ });
140
+
141
+ const renderMessage = (activity: Activity) =>
142
+ activity.message.split('\n').map((part, index) => (
143
+ <Typography key={index} component="div">
144
+ {index === 0 && (
145
+ <span style={{ color: theme.palette.text.primary }}>
146
+ {activity.userName}
147
+ </span>
148
+ )}{' '}
149
+ {part.split(/('[^']*')/g).map((text, idx) => {
150
+ const isQuotedText = text.startsWith("'") && text.endsWith("'");
151
+ return (
152
+ <span
153
+ key={idx}
154
+ style={{
155
+ color: isQuotedText
156
+ ? theme.palette.text.primary
157
+ : theme.palette.text.secondary,
158
+ }}
159
+ >
160
+ {isQuotedText ? text.slice(1, -1) : text}
161
+ </span>
162
+ );
163
+ })}
164
+ </Typography>
165
+ ));
166
+
167
+ return (
168
+ <StyledActivityLogViewBox className="scrollable-activity-log">
169
+ <ActivityLogFilter
170
+ fromDate={fromDate}
171
+ toDate={toDate}
172
+ setFromDate={setFromDate}
173
+ setToDate={setToDate}
174
+ fetchNextPage={fetchNextPage}
175
+ isLoading={isLoading}
176
+ />
177
+
178
+ {loading ? (
179
+ <StyledSpinnerBox>
180
+ <Spinner />
181
+ </StyledSpinnerBox>
182
+ ) : activitiesData.length === 0 ? (
183
+ <NoDataFound message={'No data found'} />
184
+ ) : (
185
+ Object.entries(groupedActivities).map(([section, activities]) => (
186
+ <Box key={section}>
187
+ <StyledSectionTitle>{section}</StyledSectionTitle>
188
+ <StyledTimeline>
189
+ {activities.map((activity, index) => (
190
+ <TimelineItem
191
+ key={index}
192
+ ref={index === activities.length - 1 ? lastItemRef : null}
193
+ >
194
+ <TimelineSeparator>
195
+ <StyledTimelineDot>
196
+ <StyledIconBox>
197
+ <ActivityIcon type={activity.action} />
198
+ </StyledIconBox>
199
+ </StyledTimelineDot>
200
+ {index < activities.length - 1 && (
201
+ <StyledTimelineConnector />
202
+ )}
203
+ </TimelineSeparator>
204
+ <TimelineContent
205
+ sx={{
206
+ display: 'flex',
207
+ justifyContent: 'space-between',
208
+ marginTop: 1,
209
+ }}
210
+ >
211
+ <Typography>{renderMessage(activity)}</Typography>
212
+ <Typography variant="caption">
213
+ {format(
214
+ convertUTCtoIST(activity.timestamp),
215
+ "d MMMM yyyy 'at' hh:mm a",
216
+ )}
217
+ </Typography>
218
+ </TimelineContent>
219
+ </TimelineItem>
220
+ ))}
221
+ </StyledTimeline>
222
+ </Box>
223
+ ))
224
+ )}
225
+ {isFetchingNextPage && !loading && (
226
+ <StyledSpinnerBox>
227
+ <Spinner />
228
+ </StyledSpinnerBox>
229
+ )}
230
+ <ScrollToTopButton />
231
+ </StyledActivityLogViewBox>
232
+ );
233
+ };
@@ -0,0 +1,65 @@
1
+ export const DeleteIcon = () => {
2
+ return (
3
+ <svg
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ width="16"
6
+ height="16"
7
+ viewBox="0 0 16 16"
8
+ fill="none"
9
+ style={{ display: 'flex', alignItems: 'center' }}
10
+ >
11
+ <g clipPath="url(#clip0_2752_9015)">
12
+ <path
13
+ d="M10.7933 1.3335H5.20659C2.77992 1.3335 1.33325 2.78016 1.33325 5.20683V10.7868C1.33325 13.2202 2.77992 14.6668 5.20659 14.6668H10.7866C13.2133 14.6668 14.6599 13.2202 14.6599 10.7935V5.20683C14.6666 2.78016 13.2199 1.3335 10.7933 1.3335ZM10.6666 8.50016H5.33325C5.20064 8.50016 5.07347 8.44749 4.9797 8.35372C4.88593 8.25995 4.83325 8.13277 4.83325 8.00016C4.83325 7.86756 4.88593 7.74038 4.9797 7.64661C5.07347 7.55284 5.20064 7.50016 5.33325 7.50016H10.6666C10.7992 7.50016 10.9264 7.55284 11.0201 7.64661C11.1139 7.74038 11.1666 7.86756 11.1666 8.00016C11.1666 8.13277 11.1139 8.25995 11.0201 8.35372C10.9264 8.44749 10.7992 8.50016 10.6666 8.50016Z"
14
+ fill="#F2353C"
15
+ />
16
+ </g>
17
+ <defs>
18
+ <clipPath id="clip0_2752_9015">
19
+ <rect width="16" height="16" fill="white" />
20
+ </clipPath>
21
+ </defs>
22
+ </svg>
23
+ );
24
+ };
25
+
26
+ export const CreateIcon = () => {
27
+ return (
28
+ <svg
29
+ xmlns="http://www.w3.org/2000/svg"
30
+ width="16"
31
+ height="16"
32
+ viewBox="0 0 16 16"
33
+ fill="none"
34
+ >
35
+ <path
36
+ d="M10.793 1.3335H5.20632C2.77965 1.3335 1.33325 2.7799 1.33325 5.20656V10.7868C1.33325 13.2199 2.77965 14.6668 5.20632 14.6668H10.7866C13.2133 14.6668 14.6602 13.2204 14.6602 10.7938V5.20656C14.6666 2.7799 13.2197 1.3335 10.793 1.3335ZM10.6666 8.4999H8.49965V10.6668C8.49965 10.7994 8.44697 10.9266 8.35321 11.0204C8.25944 11.1142 8.13226 11.1668 7.99965 11.1668C7.86704 11.1668 7.73987 11.1142 7.6461 11.0204C7.55233 10.9266 7.49965 10.7994 7.49965 10.6668V8.4999H5.33325C5.20064 8.4999 5.07347 8.44722 4.9797 8.35345C4.88593 8.25968 4.83325 8.1325 4.83325 7.9999C4.83325 7.86729 4.88593 7.74011 4.9797 7.64634C5.07347 7.55257 5.20064 7.4999 5.33325 7.4999H7.49965V5.3335C7.49965 5.20089 7.55233 5.07371 7.6461 4.97994C7.73987 4.88617 7.86704 4.8335 7.99965 4.8335C8.13226 4.8335 8.25944 4.88617 8.35321 4.97994C8.44697 5.07371 8.49965 5.20089 8.49965 5.3335V7.4999H10.6666C10.7992 7.4999 10.9264 7.55257 11.0201 7.64634C11.1139 7.74011 11.1666 7.86729 11.1666 7.9999C11.1666 8.1325 11.1139 8.25968 11.0201 8.35345C10.9264 8.44722 10.7992 8.4999 10.6666 8.4999Z"
37
+ fill="#4BAABE"
38
+ />
39
+ </svg>
40
+ );
41
+ };
42
+
43
+ export const UpdateIcon = () => {
44
+ return (
45
+ <svg
46
+ xmlns="http://www.w3.org/2000/svg"
47
+ width="16"
48
+ height="16"
49
+ viewBox="0 0 16 16"
50
+ fill="none"
51
+ >
52
+ <g clipPath="url(#clip0_2752_9033)">
53
+ <path
54
+ d="M10.7933 1.3335H5.20659C2.77992 1.3335 1.33325 2.78016 1.33325 5.20683V10.7868C1.33325 13.2202 2.77992 14.6668 5.20659 14.6668H10.7866C13.2133 14.6668 14.6599 13.2202 14.6599 10.7935V5.20683C14.6666 2.78016 13.2199 1.3335 10.7933 1.3335ZM11.1866 6.46683L7.40659 10.2468C7.36025 10.2934 7.30519 10.3303 7.24456 10.3555C7.18392 10.3806 7.11891 10.3936 7.05325 10.3936C6.98759 10.3936 6.92258 10.3806 6.86195 10.3555C6.80131 10.3303 6.74625 10.2934 6.69992 10.2468L4.81325 8.36016C4.76413 8.31439 4.72473 8.25919 4.6974 8.19785C4.67007 8.13652 4.65538 8.07031 4.65419 8.00318C4.65301 7.93604 4.66536 7.86936 4.6905 7.8071C4.71565 7.74484 4.75308 7.68828 4.80056 7.6408C4.84804 7.59332 4.90459 7.5559 4.96685 7.53075C5.02911 7.5056 5.0958 7.49325 5.16293 7.49444C5.23007 7.49562 5.29628 7.51031 5.35761 7.53764C5.41894 7.56497 5.47414 7.60437 5.51992 7.6535L7.05325 9.18683L10.4799 5.76016C10.5747 5.67184 10.7001 5.62376 10.8296 5.62605C10.9591 5.62833 11.0827 5.68081 11.1743 5.77242C11.2659 5.86402 11.3184 5.98761 11.3207 6.11715C11.323 6.24668 11.2749 6.37205 11.1866 6.46683Z"
55
+ fill="#88B053"
56
+ />
57
+ </g>
58
+ <defs>
59
+ <clipPath id="clip0_2752_9033">
60
+ <rect width="16" height="16" fill="white" />
61
+ </clipPath>
62
+ </defs>
63
+ </svg>
64
+ );
65
+ };
@@ -0,0 +1,101 @@
1
+ import { ArrowUpward, Info } from '@mui/icons-material';
2
+ import { Box, Fab, Typography } from '@mui/material';
3
+ import { addHours, addMinutes, parseISO } from 'date-fns';
4
+ import { useCallback, useRef } from 'react';
5
+ import { emptyListImage } from '../../../assets/images/svg';
6
+ import { StyledBox } from '../../Assets/ErrorPages/styles';
7
+ import { ActivityAction } from '../../export';
8
+ import { CreateIcon, DeleteIcon, UpdateIcon } from './Icons';
9
+
10
+ /**
11
+ * Converts a given UTC date string to Indian Standard Time (IST).
12
+ *
13
+ * @param dateString - The date string in ISO format to be converted.
14
+ * @returns The converted date object in IST.
15
+ */
16
+ export const convertUTCtoIST = (dateString: string) => {
17
+ const date = parseISO(dateString);
18
+ return addMinutes(addHours(date, 5), 30);
19
+ };
20
+
21
+ export const ActivityIcon = ({ type }: { type: ActivityAction }) => {
22
+ switch (type) {
23
+ case 'create':
24
+ return <CreateIcon />;
25
+ case 'delete':
26
+ return <DeleteIcon />;
27
+ case 'update':
28
+ return <UpdateIcon />;
29
+ default:
30
+ return <Info />;
31
+ }
32
+ };
33
+
34
+ export const ScrollToTopButton = () => {
35
+ const handleScrollToTop = () => {
36
+ const container = document.querySelector('.scrollable-activity-log');
37
+ if (container) {
38
+ container.scrollTo({
39
+ top: 0,
40
+ behavior: 'smooth',
41
+ });
42
+ }
43
+ };
44
+
45
+ return (
46
+ <Box
47
+ sx={{
48
+ position: 'absolute',
49
+ bottom: 20,
50
+ right: 20,
51
+ zIndex: 1500,
52
+ }}
53
+ >
54
+ <Fab
55
+ color="primary"
56
+ size="small"
57
+ onClick={handleScrollToTop}
58
+ aria-label="Scroll to top"
59
+ >
60
+ <ArrowUpward />
61
+ </Fab>
62
+ </Box>
63
+ );
64
+ };
65
+
66
+ export const NoDataFound = ({ message }: { message: string }) => {
67
+ return (
68
+ <>
69
+ <StyledBox>
70
+ <img
71
+ src={emptyListImage}
72
+ alt="page not found"
73
+ width={'350px'}
74
+ style={{ margin: '20px' }}
75
+ />
76
+ <Typography variant="subtitle1">{message}</Typography>
77
+ </StyledBox>
78
+ </>
79
+ );
80
+ };
81
+
82
+ export function useIntersectionObserver(callback: () => void) {
83
+ const observer = useRef<IntersectionObserver | null>(null);
84
+
85
+ const handleObserver = useCallback(
86
+ (node: HTMLDivElement) => {
87
+ if (observer.current) observer.current.disconnect();
88
+
89
+ observer.current = new IntersectionObserver((entries) => {
90
+ if (entries[0].isIntersecting) {
91
+ callback();
92
+ }
93
+ });
94
+
95
+ if (node) observer.current.observe(node);
96
+ },
97
+ [callback],
98
+ );
99
+
100
+ return handleObserver;
101
+ }
@@ -0,0 +1,52 @@
1
+ import {
2
+ Timeline,
3
+ TimelineConnector,
4
+ TimelineDot,
5
+ timelineItemClasses,
6
+ } from '@mui/lab';
7
+ import { Box, styled, Typography } from '@mui/material';
8
+
9
+ export const StyledSpinnerBox = styled(Box)({
10
+ display: 'flex',
11
+ justifyContent: 'center',
12
+ alignItems: 'center',
13
+ });
14
+
15
+ export const StyledSectionTitle = styled(Typography)(({ theme }) => ({
16
+ fontSize: '12px',
17
+ color: theme.palette.text.secondary,
18
+ }));
19
+
20
+ export const StyledTimeline = styled(Timeline)({
21
+ [`& .${timelineItemClasses.root}:before`]: { flex: 0, padding: 0 },
22
+ [`& .${timelineItemClasses.root}:not(:first-of-type)`]: {
23
+ marginTop: '0px',
24
+ },
25
+ });
26
+
27
+ export const StyledTimelineDot = styled(TimelineDot)(({ theme }) => ({
28
+ backgroundColor: theme.palette.surface.defaultBackground,
29
+ boxShadow: 'none',
30
+ width: 30,
31
+ padding: 0,
32
+ paddingLeft: 5,
33
+ height: 30,
34
+ }));
35
+
36
+ export const StyledTimelineConnector = styled(TimelineConnector)(
37
+ ({ theme }) => ({
38
+ width: '1px',
39
+ }),
40
+ );
41
+
42
+ export const StyledActivityLogViewBox = styled(Box)({
43
+ '&::-webkit-scrollbar': { display: 'none' },
44
+ overflowY: 'auto',
45
+ maxHeight: '70vh',
46
+ });
47
+
48
+ export const StyledIconBox = styled(Box)({
49
+ display: 'flex',
50
+ justifyContent: 'center',
51
+ alignItems: 'center',
52
+ });
@@ -63,4 +63,6 @@ export const CellContainer = styled(Stack)(({ theme }) => ({
63
63
  alignItems: 'center',
64
64
  height: '100%',
65
65
  flexDirection: 'row',
66
+ justifyContent: 'flex-start',
67
+ gap: '12px',
66
68
  }));
@@ -1,4 +1,5 @@
1
1
  export * from './AccordionGroup/AccordionGroup';
2
+ export * from './ActivityLogView/ActivityLogView';
2
3
  export * from './Avatar/Avatar';
3
4
  export * from './Card/Card';
4
5
  export * from './Chips/Chips';
@@ -2,7 +2,8 @@ import { MenuListProps, MenuProps, Stack, useTheme } from '@mui/material';
2
2
  import { GridDensity } from '@mui/x-data-grid';
3
3
  import { capitalize } from 'lodash';
4
4
  import { useDispatch, useSelector } from 'react-redux';
5
- import { RootState, setDensity } from '../../../../../redux/export';
5
+ import { setDensity } from '../../../../../redux/slices/pageHeaderSlice';
6
+ import { RootState } from '../../../../../redux/store';
6
7
  import { Button, DropdownMenu, Icons, Typography } from '../../../../export';
7
8
  import { DensityAnchor } from '../Anchors';
8
9
 
@@ -18,8 +19,7 @@ export const DensitySelector = ({
18
19
  }: DensitySelectorProps) => {
19
20
  const dispatch = useDispatch();
20
21
  const density = useSelector(
21
- (state: RootState) =>
22
- state.pageHeaderSlice[uniqueId]?.density || 'standard',
22
+ (state: RootState) => state.pageHeader[uniqueId]?.density || 'standard',
23
23
  );
24
24
  const gridDensity: GridDensity[] = ['compact', 'standard', 'comfortable'];
25
25
 
@@ -3,10 +3,8 @@ import { MenuListProps, MenuProps, Typography, useTheme } from '@mui/material';
3
3
  import { GridColDef, GridColumnVisibilityModel } from '@mui/x-data-grid';
4
4
  import { useState } from 'react';
5
5
  import { useDispatch, useSelector } from 'react-redux';
6
- import {
7
- RootState,
8
- setColumnVisibilityModel,
9
- } from '../../../../../redux/export';
6
+ import { setColumnVisibilityModel } from '../../../../../redux/slices/pageHeaderSlice';
7
+ import { RootState } from '../../../../../redux/store';
10
8
  import { Button, Icons, SearchBar, SingleCheckBox } from '../../../../export';
11
9
  import { DropdownMenu } from '../../../../Navigation/export';
12
10
 
@@ -26,7 +24,7 @@ export const TableColumnsSelector = ({
26
24
 
27
25
  const columnVisibilityModel = useSelector(
28
26
  (state: RootState) =>
29
- state.pageHeaderSlice[uniqueId]?.columnVisibilityModel ||
27
+ state.pageHeader[uniqueId]?.columnVisibilityModel ||
30
28
  columns.reduce((acc, column) => {
31
29
  acc[column.field] = true;
32
30
  return acc;
@@ -10,7 +10,7 @@ export const usePageHeader = () => {
10
10
  const dispatch = useDispatch();
11
11
  const filterState = useSelector(
12
12
  (state: RootState) =>
13
- state.pageHeaderSlice[uuidRef.current] ||
13
+ state.pageHeader[uuidRef.current] ||
14
14
  ({} as {
15
15
  density: GridDensity;
16
16
  columnVisibilityModel: GridColumnVisibilityModel;
@@ -1,2 +1 @@
1
- export * from './slices/pageHeaderSlice';
2
- export * from './store';
1
+ export * from './reducers';
@@ -0,0 +1,5 @@
1
+ import { pageHeaderSlice } from './slices/pageHeaderSlice';
2
+
3
+ export const reactBlueprintReducers = {
4
+ pageHeader: pageHeaderSlice.reducer,
5
+ };
@@ -10,7 +10,7 @@ export type PageHeaderState = {
10
10
 
11
11
  const initialState: PageHeaderState = {};
12
12
 
13
- const pageHeaderSlice = createSlice({
13
+ export const pageHeaderSlice = createSlice({
14
14
  name: 'pageHeader',
15
15
  initialState,
16
16
  reducers: {
@@ -31,5 +31,3 @@ const pageHeaderSlice = createSlice({
31
31
 
32
32
  export const { setColumnVisibilityModel, setDensity, resetStateForUniqueId } =
33
33
  pageHeaderSlice.actions;
34
-
35
- export default pageHeaderSlice.reducer;
@@ -1,10 +1,8 @@
1
- import { configureStore } from '@reduxjs/toolkit';
2
- import pageHeaderSlice from './slices/pageHeaderSlice';
1
+ import { combineReducers, configureStore } from '@reduxjs/toolkit';
2
+ import { reactBlueprintReducers } from './reducers';
3
3
 
4
4
  export const store = configureStore({
5
- reducer: {
6
- pageHeaderSlice,
7
- },
5
+ reducer: combineReducers(reactBlueprintReducers),
8
6
  });
9
7
 
10
8
  export type RootState = ReturnType<typeof store.getState>;
@@ -0,0 +1,96 @@
1
+ import { LocalizationProvider } from '@mui/x-date-pickers';
2
+ import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFnsV3';
3
+ import { action } from '@storybook/addon-actions';
4
+ import { Meta, StoryObj } from '@storybook/react/*';
5
+ import { Activity, ActivityLogView } from '../../components/export';
6
+
7
+ // Mock data for the story with diverse timestamps and actions
8
+ const activitiesData: Activity[] = [
9
+ {
10
+ userName: 'John Doe',
11
+ action: 'create',
12
+ message: "created 'Student Registration'",
13
+ timestamp: new Date().toISOString(), // Today
14
+ },
15
+ {
16
+ userName: 'Jane Smith',
17
+ action: 'update',
18
+ message: `updated 'Employee Record'
19
+ • 'Position' has been changed from 'Junior Developer' to 'Senior Developer'
20
+ • 'Salary' has been changed from '$50,000' to '$70,000'
21
+ • 'Department' has been changed from 'IT Support' to 'Software Development'
22
+ • 'Supervisor' has been changed from 'Mr. Adams' to 'Ms. Johnson'
23
+ • 'Work Hours' have been changed from '40' to '45'`,
24
+ timestamp: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(), // 2 days ago (Past Week)
25
+ },
26
+ {
27
+ userName: 'Admin',
28
+ action: 'delete',
29
+ message: "deleted 'Annual Report'",
30
+ timestamp: new Date(Date.now() - 10 * 24 * 60 * 60 * 1000).toISOString(), // 10 days ago (Older)
31
+ },
32
+ {
33
+ userName: 'Alice Johnson',
34
+ action: 'create',
35
+ message: "created 'Course Outline'",
36
+ timestamp: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString(), // 5 days ago (Past Week)
37
+ },
38
+ {
39
+ userName: 'Bob Brown',
40
+ action: 'update',
41
+ message: `updated 'Project Details'
42
+ • 'Project Name' has been changed from 'Website Revamp' to 'Mobile App Launch'
43
+ • 'Deadline' has been changed from 'June 2023' to 'August 2023'
44
+ • 'Team Members' have been changed from '5' to '8'
45
+ • 'Budget' has been changed from '$20,000' to '$35,000'
46
+ • 'Client Contact' has been changed from 'Mr. Lee' to 'Mrs. Gomez'`,
47
+ timestamp: new Date(Date.now() - 1 * 24 * 60 * 60 * 1000).toISOString(), // Yesterday (Past Week)
48
+ },
49
+ {
50
+ userName: 'Charlie Green',
51
+ action: 'delete',
52
+ message: "deleted 'Temporary Files'",
53
+ timestamp: new Date(Date.now() - 15 * 24 * 60 * 60 * 1000).toISOString(), // 15 days ago (Older)
54
+ },
55
+ {
56
+ userName: 'Emily White',
57
+ action: 'update',
58
+ message: `updated 'Client Information'
59
+ • 'Client Name' has been changed from 'Acme Corp' to 'Globex Inc.'
60
+ • 'Contact Number' has been changed from '123-456-7890' to '987-654-3210'
61
+ • 'Address' has been changed from '123 Main St' to '456 Elm St'
62
+ • 'Email' has been changed from 'info@acme.com' to 'contact@globex.com'
63
+ • 'Account Manager' has been changed from 'John Smith' to 'Laura Adams'`,
64
+ timestamp: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000).toISOString(), // 3 days ago (Past Week)
65
+ },
66
+ ];
67
+
68
+ const meta: Meta<typeof ActivityLogView> = {
69
+ title: 'DataDisplay/ActivityLog',
70
+ component: ActivityLogView,
71
+ decorators: [
72
+ (Story) => (
73
+ <LocalizationProvider dateAdapter={AdapterDateFns}>
74
+ <Story />
75
+ </LocalizationProvider>
76
+ ),
77
+ ],
78
+ };
79
+
80
+ export default meta;
81
+
82
+ type Story = StoryObj<typeof ActivityLogView>;
83
+
84
+ export const Default: Story = {
85
+ args: {
86
+ activitiesData,
87
+ isFetchingNextPage: false,
88
+ fetchNextPage: action('fetchNextPage'),
89
+ hasNextPage: true,
90
+ fromDate: null,
91
+ toDate: null,
92
+ setFromDate: action('setFromDate'),
93
+ setToDate: action('setToDate'),
94
+ isLoading: false,
95
+ },
96
+ };