@campxdev/react-blueprint 1.2.16 → 1.2.17

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@campxdev/react-blueprint",
3
- "version": "1.2.16",
3
+ "version": "1.2.17",
4
4
  "main": "./export.ts",
5
5
  "private": false,
6
6
  "dependencies": {
@@ -9,7 +9,7 @@ import {
9
9
  } from '@mui/material';
10
10
  import { Variant } from '@mui/material/styles/createTypography';
11
11
  import { ReactNode } from 'react';
12
- import { DropDownIcon, DropdownMenu } from '../../export';
12
+ import { DropDownIcon, DropdownMenu, Switch, SwitchProps } from '../../export';
13
13
  import {
14
14
  StyledButton,
15
15
  StyledCardActions,
@@ -46,6 +46,8 @@ export interface CardProps {
46
46
  imageSrc?: string;
47
47
  titleImgIcon?: ReactNode;
48
48
  checkBox?: boolean;
49
+ hasSwitch?: boolean;
50
+ switchProps?: SwitchProps;
49
51
  moreOptions?: boolean;
50
52
  menu?: Array<ReactNode>;
51
53
  icon?: ReactNode;
@@ -76,6 +78,8 @@ export const Card = ({
76
78
  icon,
77
79
  children,
78
80
  menu = [],
81
+ switchProps,
82
+ hasSwitch,
79
83
  }: CardProps) => {
80
84
  const handleClick = (e: any) => {
81
85
  footer?.onClick();
@@ -106,6 +110,7 @@ export const Card = ({
106
110
  </Stack>
107
111
  </Stack>
108
112
  <Stack flexDirection={'row'} gap={'12px'} alignItems={'center'}>
113
+ {hasSwitch && <Switch {...switchProps} />}
109
114
  {status && (
110
115
  <StyledStatusText variant="label2" sx={statusSx}>
111
116
  {status}{' '}
@@ -27,6 +27,7 @@ export const DataTable = (props: DataTableProps) => {
27
27
  getRowId={(row) => row.id ?? v4()}
28
28
  {...props}
29
29
  slots={{
30
+ ...props.slots,
30
31
  pagination: () => {
31
32
  if (
32
33
  limit === undefined ||
@@ -0,0 +1,209 @@
1
+ import { Button } from '@mui/material';
2
+ import {
3
+ GridActionsCellItem,
4
+ GridColDef,
5
+ GridEventListener,
6
+ GridRowEditStopReasons,
7
+ GridRowModel,
8
+ GridRowModes,
9
+ GridRowModesModel,
10
+ GridRowParams,
11
+ GridToolbarContainer,
12
+ GridValidRowModel,
13
+ } from '@mui/x-data-grid';
14
+ import { useEffect, useRef, useState } from 'react';
15
+ import { v4 } from 'uuid';
16
+ import { DataTable, DataTableProps, Icons } from '../../export';
17
+
18
+ export type EditableDataTableProps = {
19
+ limit?: number;
20
+ offset?: number;
21
+ totalCount?: number;
22
+ hideDelete?: boolean;
23
+ onPageChange?: (page: number) => void;
24
+ onLimitChange?: (limit: number) => void;
25
+ onChange: (rows: GridValidRowModel) => void;
26
+ onSave?: (params: any) => void;
27
+ onDelete?: (params: any) => void;
28
+ } & Omit<
29
+ DataTableProps,
30
+ | 'rowModesModel'
31
+ | 'onRowModesModelChange'
32
+ | 'onRowEditStop'
33
+ | 'processRowUpdate'
34
+ >;
35
+
36
+ export const EditableDataTable = (props: EditableDataTableProps) => {
37
+ const { rows } = props;
38
+
39
+ useEffect(() => {}, [props.columns]);
40
+
41
+ return (
42
+ <EditableTableCore
43
+ {...props}
44
+ rows={rows?.map((row) => (row.id ? row : { id: v4(), ...row }))}
45
+ />
46
+ );
47
+ };
48
+
49
+ export const EditableTableCore = (props: EditableDataTableProps) => {
50
+ const { rows, columns } = props;
51
+ const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({});
52
+ const processRowUpdate = (newRow: GridRowModel) => {
53
+ const updatedRow = { ...newRow, isNew: false };
54
+ props.onSave && props.onSave(newRow);
55
+ props.onChange(
56
+ rows?.map((row) => (row.id === newRow.id ? updatedRow : row)) ?? [],
57
+ );
58
+ return updatedRow;
59
+ };
60
+
61
+ const handleRowEditStop: GridEventListener<'rowEditStop'> = (
62
+ params,
63
+ event,
64
+ ) => {
65
+ if (params.reason === GridRowEditStopReasons.rowFocusOut) {
66
+ event.defaultMuiPrevented = true;
67
+ }
68
+ };
69
+
70
+ const handleRowModesModelChange = (newRowModesModel: GridRowModesModel) => {
71
+ setRowModesModel(newRowModesModel);
72
+ };
73
+
74
+ const handleEditClick = (params: GridRowParams) => {
75
+ setRowModesModel({
76
+ ...rowModesModel,
77
+ [params.id]: { mode: GridRowModes.Edit },
78
+ });
79
+ };
80
+
81
+ const handleSaveClick = (params: GridRowParams) => {
82
+ setRowModesModel({
83
+ ...rowModesModel,
84
+ [params.id]: { mode: GridRowModes.View },
85
+ });
86
+ };
87
+
88
+ const handleDeleteClick = (params: GridRowParams) => {
89
+ props.onDelete && props.onDelete(params);
90
+ props.onChange(rows?.filter((row) => row.id !== params.id) ?? []);
91
+ };
92
+
93
+ return (
94
+ <>
95
+ <DataTable
96
+ editMode="row"
97
+ onRowModesModelChange={handleRowModesModelChange}
98
+ onRowEditStop={handleRowEditStop}
99
+ processRowUpdate={processRowUpdate}
100
+ rowModesModel={rowModesModel}
101
+ {...props}
102
+ rows={rows}
103
+ columns={[
104
+ ...columns,
105
+ {
106
+ field: 'actions',
107
+ type: 'actions',
108
+ headerName: 'Actions',
109
+ width: 150,
110
+ cellClassName: 'actions',
111
+ getActions: (params) => {
112
+ if (rowModesModel[params.id]?.mode === GridRowModes.Edit) {
113
+ return props.hideDelete
114
+ ? [
115
+ <GridActionsCellItem
116
+ icon={<Icons.SaveIcon />}
117
+ label="Save"
118
+ onClick={() => handleSaveClick(params)}
119
+ />,
120
+ ]
121
+ : [
122
+ <GridActionsCellItem
123
+ icon={<Icons.SaveIcon />}
124
+ label="Save"
125
+ onClick={() => handleSaveClick(params)}
126
+ />,
127
+ <GridActionsCellItem
128
+ icon={<Icons.DeleteIcon />}
129
+ label="Cancel"
130
+ onClick={() => handleDeleteClick(params)}
131
+ color="inherit"
132
+ />,
133
+ ];
134
+ }
135
+
136
+ return props.hideDelete
137
+ ? [
138
+ <GridActionsCellItem
139
+ icon={<Icons.EditIcon />}
140
+ label="Edit"
141
+ onClick={() => handleEditClick(params)}
142
+ color="inherit"
143
+ />,
144
+ ]
145
+ : [
146
+ <GridActionsCellItem
147
+ icon={<Icons.EditIcon />}
148
+ label="Edit"
149
+ onClick={() => handleEditClick(params)}
150
+ color="inherit"
151
+ />,
152
+ <GridActionsCellItem
153
+ icon={<Icons.DeleteIcon />}
154
+ label="Delete"
155
+ onClick={() => handleDeleteClick(params)}
156
+ color="inherit"
157
+ />,
158
+ ];
159
+ },
160
+ } as GridColDef,
161
+ ]}
162
+ slots={{
163
+ footer: (footerProps) => (
164
+ <EditFooter
165
+ fields={props.columns.map((column) => column.field)}
166
+ setRows={props.onChange}
167
+ setRowModesModel={setRowModesModel}
168
+ />
169
+ ),
170
+ }}
171
+ />
172
+ </>
173
+ );
174
+ };
175
+
176
+ interface EditFooterProps {
177
+ setRows: (
178
+ newRows: (oldRows: GridValidRowModel[]) => GridValidRowModel[],
179
+ ) => void;
180
+ setRowModesModel: (
181
+ newModel: (oldModel: GridRowModesModel) => GridRowModesModel,
182
+ ) => void;
183
+ fields: string[];
184
+ }
185
+
186
+ const EditFooter = (props: EditFooterProps) => {
187
+ const { setRows, setRowModesModel, fields } = props;
188
+ const id = useRef(v4()).current;
189
+ const handleClick = () => {
190
+ setRows((oldRows) => [
191
+ ...oldRows,
192
+ {
193
+ id,
194
+ isNew: true,
195
+ ...Object.fromEntries(fields.map((field) => [field, ''])),
196
+ },
197
+ ]);
198
+ setRowModesModel((oldModel) => ({
199
+ ...oldModel,
200
+ [id]: { mode: GridRowModes.Edit, fieldToFocus: 'name' },
201
+ }));
202
+ };
203
+
204
+ return (
205
+ <GridToolbarContainer sx={{ margin: '10px 0px' }}>
206
+ <Button onClick={handleClick}>+ Add Row</Button>
207
+ </GridToolbarContainer>
208
+ );
209
+ };
@@ -3,5 +3,6 @@ export * from './Avatar/Avatar';
3
3
  export * from './Card/Card';
4
4
  export * from './Chips/Chips';
5
5
  export * from './DataTable/DataTable';
6
+ export * from './EditableDataTable/EditableDataTable';
6
7
  export * from './SidePanel/SidePanel';
7
8
  export * from './Typography/Typography';
@@ -1,6 +1,7 @@
1
1
  import { axios as campxAxios } from '@campxdev/campx-web-utils';
2
2
  import {
3
- BaseSelectProps,
3
+ AutocompleteCloseReason,
4
+ AutocompleteProps,
4
5
  Box,
5
6
  Autocomplete as MuiAutocomplete,
6
7
  Paper,
@@ -8,7 +9,7 @@ import {
8
9
  } from '@mui/material';
9
10
  import axios from 'axios';
10
11
  import _ from 'lodash';
11
- import { useEffect, useReducer } from 'react';
12
+ import { SyntheticEvent, useEffect, useReducer } from 'react';
12
13
  import { Typography } from '../../DataDisplay/Typography/Typography';
13
14
  import { Spinner } from '../../Feedback/Spinner/Spinner';
14
15
  import { TextField } from '../TextField/TextField';
@@ -26,16 +27,31 @@ function sleep(duration: number): Promise<void> {
26
27
  export type SingleSelectProps = {
27
28
  options?: { label: string; subLabel?: string; value: any }[] | any[];
28
29
  optionsApiEndPoint?: string;
30
+ optionsApiEndpointParams?: any;
29
31
  useCampxAxios?: boolean;
30
32
  required?: boolean;
31
33
  label?: string;
32
34
  name?: string;
33
- value?: any;
34
35
  getValue?: (option: any) => any;
35
36
  onChange: (value: any) => void;
36
37
  error?: any;
37
38
  helperText?: string;
38
- } & BaseSelectProps;
39
+ } & Omit<
40
+ AutocompleteProps<
41
+ { label: string; subLabel?: string; value: any } | any,
42
+ false,
43
+ false,
44
+ false
45
+ >,
46
+ | 'options'
47
+ | 'open'
48
+ | 'onChange'
49
+ | 'autoFocus'
50
+ | 'PaperComponent'
51
+ | 'renderOption'
52
+ | 'options'
53
+ | 'renderInput'
54
+ >;
39
55
 
40
56
  const CustomPaper = (props: PaperProps) => (
41
57
  <Paper
@@ -126,6 +142,7 @@ const singleSelectReducer = (
126
142
  export const SingleSelect = ({
127
143
  options,
128
144
  optionsApiEndPoint,
145
+ optionsApiEndpointParams,
129
146
  useCampxAxios = true,
130
147
  required = false,
131
148
  label,
@@ -135,6 +152,8 @@ export const SingleSelect = ({
135
152
  onChange,
136
153
  error,
137
154
  helperText,
155
+ onOpen,
156
+ onClose,
138
157
  ...restProps
139
158
  }: SingleSelectProps) => {
140
159
  const generateOptionsMap = (options: any[]) => {
@@ -163,7 +182,7 @@ export const SingleSelect = ({
163
182
 
164
183
  const internalAxios = useCampxAxios ? campxAxios : axios;
165
184
 
166
- const handleOpen = async () => {
185
+ const handleOpen = async (event: SyntheticEvent) => {
167
186
  dispatch({
168
187
  actionType: SingleSelectActionsTypes.OPEN,
169
188
  });
@@ -177,6 +196,7 @@ export const SingleSelect = ({
177
196
  params: {
178
197
  limit,
179
198
  offset,
199
+ ...optionsApiEndpointParams,
180
200
  },
181
201
  })
182
202
  .then((res) => res.data);
@@ -200,6 +220,21 @@ export const SingleSelect = ({
200
220
  });
201
221
  }
202
222
  }
223
+ if (onOpen) {
224
+ onOpen(event);
225
+ }
226
+ };
227
+
228
+ const handleClose = (
229
+ event: SyntheticEvent,
230
+ reason: AutocompleteCloseReason,
231
+ ) => {
232
+ dispatch({
233
+ actionType: SingleSelectActionsTypes.CLOSE,
234
+ });
235
+ if (onClose) {
236
+ onClose(event, reason);
237
+ }
203
238
  };
204
239
 
205
240
  const handleScroll = async (event: any) => {
@@ -288,22 +323,13 @@ export const SingleSelect = ({
288
323
  }
289
324
  return (
290
325
  <MuiAutocomplete
326
+ {...restProps}
291
327
  onChange={(e, value) => {
292
328
  onChange(getValue ? getValue(value) : value?.value);
293
329
  }}
294
330
  open={open}
295
331
  autoFocus={true}
296
332
  value={state.internalOptionsMap[value]}
297
- renderInput={(params) => (
298
- <TextField
299
- {...params}
300
- label={label}
301
- required={required}
302
- name={name}
303
- error={error}
304
- helperText={helperText}
305
- />
306
- )}
307
333
  PaperComponent={CustomPaper}
308
334
  renderOption={(props, option: any) => {
309
335
  return (
@@ -316,20 +342,29 @@ export const SingleSelect = ({
316
342
  );
317
343
  }}
318
344
  ListboxProps={{
345
+ ...restProps.ListboxProps,
319
346
  onScroll: handleScroll,
320
347
  }}
321
348
  slotProps={{
349
+ ...restProps.slotProps,
322
350
  paper: {
351
+ ...restProps.slotProps?.paper,
323
352
  square: loadingInternalOptions,
324
353
  },
325
354
  }}
326
355
  onOpen={handleOpen}
327
- onClose={() => {
328
- dispatch({
329
- actionType: SingleSelectActionsTypes.CLOSE,
330
- });
331
- }}
356
+ onClose={handleClose}
332
357
  options={internalOptions}
358
+ renderInput={(params) => (
359
+ <TextField
360
+ {...params}
361
+ label={label}
362
+ required={required}
363
+ name={name}
364
+ error={error}
365
+ helperText={helperText}
366
+ />
367
+ )}
333
368
  />
334
369
  );
335
370
  };
@@ -4,6 +4,8 @@ import { Typography } from '../../DataDisplay/Typography/Typography';
4
4
  import { Button } from '../../export';
5
5
  import UserBox from './AppHeaderActions/UserBox';
6
6
  import { StyledHeader } from './styles/styles';
7
+ import { StyledIconButton } from '../../Navigation/DropDownMenu/styles';
8
+ import { HelpIcon } from '../../Assets/Icons/IconComponents/HelpIcon';
7
9
 
8
10
  export interface AppHeaderProps {
9
11
  actions?: ReactNode[];
@@ -35,10 +37,10 @@ export const AppHeader = ({
35
37
  <Stack alignItems={'center'} gap={'12px'} flexDirection={'row'}>
36
38
  {/* <StyledIconButton>
37
39
  <a
38
- href={"https://campx.atlassian.net/servicedesk/customer/portal/2"}
40
+ href={'https://helpdesk.campx.in/helpdesk/my-tickets'}
39
41
  target="_blank"
40
42
  rel="noreferrer"
41
- style={{ textDecoration: "none" }}
43
+ style={{ textDecoration: 'none' }}
42
44
  >
43
45
  <HelpIcon size={20} />
44
46
  </a>
@@ -47,6 +49,12 @@ export const AppHeader = ({
47
49
  variant="outlined"
48
50
  id="jiraIssueCollector"
49
51
  className="reportAnIssue"
52
+ onClick={() => {
53
+ window.open(
54
+ 'https://helpdesk.campx.in/helpdesk/my-tickets',
55
+ '_blank',
56
+ );
57
+ }}
50
58
  >
51
59
  <Typography
52
60
  variant="button"