MUI datagrid view mode not persisting - reactjs

I'm implementing a MUI datagrid with inline editing. Whenever a cell loses focus, the code sets the mode to 'view' (as it should) but then immediately the processRowUpdate callback is called (to send data to the endpoint) it sets the cell mode back to 'edit' meaning that the cell remains in edit mode.
Does anyone know why this is happening?
Maybe something to do with this processRowUpdate error logged to console?:
TypeError: Cannot read properties of undefined (reading 'id')
at getRowId (productTable.js:213:1)
at getRowIdFromRowModel (gridRowsUtils.js:17:1)
at useGridRows.js:106:1
at Array.forEach (<anonymous>)
at Object.updateRows (useGridRows.js:105:1)
at apiRef.current.<computed> [as updateRows] (useGridApiMethod.js:14:1)
at useGridCellEditing.new.js:334:1
Code:
export default function FullFeaturedCrudGrid(props) {
const [rows, setRows] = React.useState([]);
const [cellModesModel, setCellModesModel] = React.useState({})
const [selectedCellParams, setSelectedCellParams] = React.useState(null);
const { tableName } = props
const [snackbar, setSnackbar] = React.useState(null);
const handleCloseSnackbar = () => setSnackbar(null);
React.useEffect(() => {
console.log('useEffect called')
axios.get(`http://localhost:8000/mvhr/all`)
.then((response) => {
setRows(response.data);
})
}, [])
React.useEffect(() => {
console.log('cellModesModel',cellModesModel)
});
const handleCellFocus = React.useCallback((event) => {
const row = event.currentTarget.parentElement;
const id = row.dataset.id;
const field = event.currentTarget.dataset.field;
setSelectedCellParams({ id, field });
}, []);
const handleDeleteClick = (id) => () => {
axios.delete(`http://localhost:8000/delete_mvhr/${id}`
).then(() => {
setRows(rows.filter((row) => row.id !== id));
setSnackbar({ children: tableName + ' successfully deleted', severity: 'success' });
})
};
const handleCancel = () => {
if (!selectedCellParams) {
return;
}
const { id, field } = selectedCellParams;
setCellModesModel({
...cellModesModel,
[id]: {
...cellModesModel[id],
[field]: { mode: GridCellModes.View },
},
});
};
const processRowUpdate = React.useCallback(
(newRow) => {
axios.put(`http://localhost:8000/mvhr/`, newRow)
.then((response) => {
const updatedRow = { ...newRow, isNew: false };
setRows(rows.map((row) => (row.id === newRow.id ? updatedRow : row)));
setSnackbar({ children: tableName + ' successfully saved', severity: 'success' });
return updatedRow
})
});
const handleProcessRowUpdateError = React.useCallback((error) => {
setSnackbar({ children: error.message, severity: 'error' });
}, []);
const columns = [
{ field: 'description', headerName: 'description', width: 180, editable: true },
{ field: 'elec_efficiency', headerName: 'elec_efficiency', type: 'number', editable: true },
{ field: 'heat_recovery_eff', headerName: 'heat_recovery_eff', type: 'number', editable: true },
{ field: 'range_low', headerName: 'range_low', type: 'number', editable: true },
{ field: 'range_high', headerName: 'range_high', type: 'number', editable: true },
{ field: 'superseded', headerName: 'superseded', type: 'boolean', editable: true },
{
field: 'actions',
type: 'actions',
headerName: 'Actions',
width: 100,
cellClassName: 'actions',
getActions: ({ id }) => {
return [
<GridActionsCellItem
icon={<DeleteIcon />}
label="Delete"
onClick={handleDeleteClick(id)}
color="inherit"
/>,
];
},
},
];
return (
<Box
sx={{
height: '100vh',
width: '100%',
'& .actions': {
color: 'text.secondary',
},
'& .textPrimary': {
color: 'text.primary',
},
}}
>
<StripedDataGrid
rows={rows}
columns={columns}
processRowUpdate={processRowUpdate}
onProcessRowUpdateError={handleProcessRowUpdateError}
onCellEditStop={handleCancel}
cellModesModel={cellModesModel}
onCellModesModelChange={(model) => setCellModesModel(model)}
components={{
Toolbar: AddToolbar,
}}
componentsProps={{
toolbar: { setRows, setSnackbar, tableName },
cell: {
onFocus: handleCellFocus,
},
}}
experimentalFeatures={{ newEditingApi: true }}
getRowClassName={(params) =>
params.indexRelativeToCurrentPage % 2 === 0 ? 'even' : 'odd'
}
/>
{!!snackbar && (
<Snackbar
open
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
onClose={handleCloseSnackbar}
autoHideDuration={6000}
>
<Alert {...snackbar} onClose={handleCloseSnackbar} />
</Snackbar>
)}
</Box>
);
}

Solved it. I refactored the code to return the updatedRow synchronously rather than as a callback to the axios request:
const processRowUpdate = React.useCallback(
(newRow) => {
const updatedRow = { ...newRow, isNew: false };
setRows(rows.map((row) => (row.id === newRow.id ? updatedRow : row)));
setSnackbar({ children: tableName + ' successfully saved', severity: 'success' });
axios.put(`http://localhost:8000/mvhr/`, newRow)
return updatedRow
});

Related

My fetch command doesn't work, what did i do wrong?

im trying to fetch customers and trainings by using a fetch request but for some reason it doesn´t print anything in the page. However it is printing those informations in the network console.
import React, { useState, useEffect } from "react";
import Snackbar from '#material-ui/core/Snackbar';
import Addcustomer from "./Addcustomer";
import Addtraining from "./Addtraining";
import Editcustomer from "./Editcustomer";
import { AgGridReact } from'ag-grid-react'
import'ag-grid-community/dist/styles/ag-grid.css'
import'ag-grid-community/dist/styles/ag-theme-material.css';
export default function Customerlist() {
const [customers, setCustomers] = useState([]);
useEffect(() => fetchData(), []);
const fetchData = () => {
fetch('https://customerrest.herokuapp.com/api/customers')
.then(response => response.json())
.then(data => setCustomers(data.content));
};
const deleteCustomer = link => {
if (window.confirm("Are you sure to delete customer?")) {
console.log(link);
fetch(link, { method: "DELETE" })
.then(res => {
fetchData();
if (res.status >= 200 && res.status < 300) {
Snackbar({ message: "Customer deleted successfully" });
} else {
Snackbar({ message: "Error. Try again." });
}
})
.catch(err => console.error(err));
}
};
const saveCustomer = customer => {
fetch('https://customerrest.herokuapp.com/api/customers', {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(customer)
})
.then(res => {
fetchData();
if (res.status >= 200 && res.status < 300) {
Snackbar({ message: "Customer added successfully" });
} else {
Snackbar({ message: "Error. Try again." });
}
})
.catch(err => console.error(err));
};
const saveTraining = training => {
fetch('https://customerrest.herokuapp.com/api/trainings', {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(training)
})
.then(res => {
fetchData();
if (res.status >= 200 && res.status < 300) {
Snackbar({ message: "Training added successfully" });
} else {
Snackbar({ message: "Error. Try again." });
}
})
.catch(err => console.error(err));
};
const updateCustomer = (customer, link) => {
fetch(link, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(customer)
})
.then(res => fetchData())
.then(Snackbar({ message: "Customer updated successfully" }))
.catch(err => console.error(err));
};
const columns = [
{
title: "Edit",
field: "links[0].href",
render: customerData => (
<Editcustomer updateCustomer={updateCustomer} customer={customerData} />
),
sorting: false
},
{
Header: "First name",
accessor: "firstname"
},
{
Header: "Last name",
accessor: "lastname"
},
{
Header: "Email",
accessor: "email"
},
{
Header: "Phone",
accessor: "phone"
},
{
Header: "Address",
accessor: "streetaddress"
},
{
Header: "Postcode",
accessor: "postcode"
},
{
Header: "City",
accessor: "city"
},
{
title: "Delete",
field: "links[0].href",
render: customerData => (
<button
style={{ cursor: "pointer" }}
onClick={() => deleteCustomer(customerData.links[0].href)}
>Delete</button>
),
sorting: false
},
{
title: "Add training",
render: trainingRow => (
<Addtraining
saveTraining={saveTraining}
customerId={trainingRow.links[0].href}
/>
),
sorting: false
}
];
return (
<div>
<Addcustomer saveCustomer={saveCustomer} />
<AgGridReact
title="Customers"
rowData={customers}
columns={columns}
options={{ sorting: true }}
></AgGridReact>
</div>
);
}
i have multiple fetch requests for training and customers but its not working
it shows the information in the console but it doesn't show them in the page. I would like to see all the information in my page, so what i have to do or what did i do wrong here?
The props passed to AgGridReact component are not the right type.
columnDefs prop is used to declare the column headers and should look like:
const columnDefs = [
{ headerName: 'Customers',
children: [
{
headerName: 'Edit',
valueGetter: (params) => params.data.links[0].href,
cellRenderer: (params) => <Editcustomer updateCustomer={updateCustomer} customer={params.data} />,
sortable: false,
},
{
headerName: 'First name',
field: 'firstname',
},
{
headerName: 'Last name',
field: 'lastname',
},
{
headerName: 'Email',
field: 'email',
},
{
headerName: 'Phone',
field: 'phone',
},
{
headerName: 'Address',
field: 'streetaddress',
},
{
headerName: 'Postcode',
field: 'postcode',
},
{
headerName: 'City',
field: 'city',
},
{
headerName: 'Delete',
valueGetter: (params) => params.data.links[0].href,
cellRenderer: (params) => (
<button style={{ cursor: 'pointer' }} onClick={() => deleteCustomer(params.data.links[0].href)}>
Delete
</button>
),
sortable: false,
},
{
headerName: 'Add training',
valueGetter: (params) => params.data.links[0].href,
cellRenderer: (params) => (
<Addtraining
saveTraining={saveTraining}
customerId={params.data.links[0].href}
/>
),
sortable: false,
},
]
}];
defaultColDef prop is used to declare defaults for the column headers.
const defaultColDef={ sortable: true }
Your implementation of the AgGridReact element should be:
<AgGridReact
rowData={customers}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
></AgGridReact>
Finally, you need to either set the domLayout prop so that the grid size is autocomputed (ideally for very small datasets).
<AgGridReact
rowData={customers}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
domLayout='autoHeight'
></AgGridReact>
Otherwise, you can set the size of the container element so that the grid size is computed from it.
<div
style={{
height: '500px',
width: '600px',
}}
>
<AgGridReact
rowData={customers}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
></AgGridReact>
</div>
you need to conditionaly render the information when the data is ready and you need to map on every object
const [customers, setCustomers] = useState([]);
useEffect(() => fetchData(), []);
return (
<>
{customers && customers.map((obj, i) => (
<div key={i}>
<p>{obj.firstname}</p>
<p>{obj.lastname}</p>
</div>
)}
</>
)
your displaying data is more complicated because you use other components "Addcustomer", "AgGridReact" but im just showing how to basically get the data into html

Material-UI-Search Bar value not clearing

I am using the #material-ui-searchbar to filter data. I'm using the same container for multiple different pages to display the info. When ever I switch between pages (and thus the data) the whole container reloads to display the new data and new {subType}... however, despite me changing the value of {searched} using setSearched("")(verified that it does in fact change from the debugger), the old search value from the previous page/state remains in the search bar even though the search is inactive. Why won't the search bar value update with the change of state? Doesn't make intuitive sense. Here is the code for my container:
import SearchBar from 'material-ui-search-bar';
const facilityGridConfig = {
hospitals: {
columns: [
{ name: 'name', title: 'Name' },
{ name: 'address1', title: 'Address' },
{ name: 'city', title: 'City' },
{ name: 'state', title: 'State' },
{ name: 'zipcode', title: 'Zip' },
{ name: 'phoneNumber', title: 'Phone' },
{ name: 'userTotal', title: 'Therapists', link: '/users/therapists' },
{ name: 'endUserTotal', title: 'Patients', link: '/users/patients' },
]
},
clinics: {
columns: [
{ name: 'name', title: 'Name' },
{ name: 'address1', title: 'Address' },
{ name: 'city', title: 'City' },
{ name: 'state', title: 'State' },
{ name: 'zipcode', title: 'Zip' },
{ name: 'phoneNumber', title: 'Phone' },
{ name: 'userTotal', title: 'Therapists', link: '/users/therapists' },
{ name: 'endUserTotal', title: 'Patients', link: '/users/patients' },
]
},
staff: {
columns: [
{ name: 'firstName', title: 'First Name' },
{ name: 'lastName', title: 'Last Name' },
{ name: 'facility', title: 'Facility' },
{ name: 'logins', title: 'Logins', width: 90 },
{ name: 'enrollmentStatus', title: 'Status', width: 90 },
{ name: 'email', title: 'Email' },
{ name: 'phoneNumber', title: 'Phone' }
]
},
therapists: {
columns: [
{ name: 'firstName', title: 'First Name' },
{ name: 'lastName', title: 'Last Name' },
{ name: 'facility', title: 'Facility' },
{ name: 'logins', title: 'Logins', width: 90 },
{ name: 'enrollmentStatus', title: 'Status', width: 90 },
{ name: 'email', title: 'Email' },
{ name: 'phoneNumber', title: 'Phone' },
{ name: 'endUserTotal', title: 'Patients' },
]
},
patients: {
columns: [
{ name: 'firstName', title: 'First Name' },
{ name: 'lastName', title: 'Last Name' },
{ name: 'facility', title: 'Facility' },
{ name: 'email', title: 'Email' },
{ name: "sessionsInPastWeek", title: "Sessions Last 7 Days"},
]
}
};
const facilityGridConfigSmall = {
hospitals: {
columns: [
{ name: 'name', title: 'Name' },
{ name: 'userTotal', title: 'Therapists', link: '/users/therapists' },
{ name: 'endUserTotal', title: 'Patients', link: '/users/patients' },
]
},
clinics: {
columns: [
{ name: 'name', title: 'Name' },
{ name: 'userTotal', title: 'Therapists', link: '/users/therapists' },
{ name: 'endUserTotal', title: 'Patients', link: '/users/patients' },
]
},
staff: {
columns: [
{ name: 'firstName', title: 'First Name' },
{ name: 'lastName', title: 'Last Name' },
{ name: 'logins', title: 'Logins' }
]
},
therapists: {
columns: [
{ name: 'firstName', title: 'First Name' },
{ name: 'lastName', title: 'Last Name' },
{ name: 'logins', title: 'Logins' },
{ name: 'endUserTotal', title: 'Patients' },
]
},
patients: {
columns: [
{ name: 'firstName', title: 'First Name' },
{ name: 'lastName', title: 'Last Name' },
{ name: "sessionsInPastWeek", title: "Sessions Last 7 Days"},
]
}
};
const useStyles = makeStyles((theme: Theme) => ({
root: {
position: 'relative',
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
height: '100%',
},
container: {
position: 'relative',
height: '100%',
},
breadcrumb: {
fontFamily: "Open Sans",
height: '28px',
},
content: {
position: 'relative',
height: 'calc(100% - 28px)',
},
gridHeaderEdit: {
textAlign: 'center!important' as any,
},
menuIcon: {
marginRight: theme.spacing(1)
},
}));
const FacilitiesGridContainer = (props: any) => {
const {
type,
subType,
uid,
} = props;
const mountedRef = useRef(true);
const classes = useStyles();
const dispatch = useDispatch();
const targetType = uid ? (
subType === Navigations.facilities.hospitals ? Navigations.facilities.clinics :
subType === Navigations.facilities.clinics ? Navigations.users.therapists : Navigations.users.patients
) : subType
const userProfile = useSelector(state => _get(state, ['user', 'profile']) || {}, shallowEqual);
const activeHospital = useSelector(state => _get(state, ['facilities', 'activeHospital']) || null, shallowEqual);
const activeClinic = useSelector(state => _get(state, ['facilities', 'activeClinic']) || null, shallowEqual);
const data = useSelector(state => _get(state, ['facilities', targetType]) || [], shallowEqual);
const loading = useSelector(state => _get(state, ['facilities', 'loading']) || false, shallowEqual);
const [fdata, setfData] = useState(data.data);
const [ openDeleteModal, setopenDeleteModal ] = useState(false);
const [ openInviteModal, setOpenInviteModal ] = useState(false);
const [ facilitiesInEdit, setFacilitiesInEdit ] = useState([] as any);
const [ sort, setSort ] = useState([] as any); //[{ field: 'name', dir: 'asc' }]
const [ visibleAddButton, setVisibleAddButton ] = useState(true);
const [ singleSubType, setSingleSubType ] = useState('');
const [ windowTitle, setWindowTitle ] = useState('');
const [ modalUserName, setModalUserName ] = useState('');
const [ visibleQRModal, setVisibleQRModal ] = useState(true);
const [ openQRModal, setOpenQRModal ] = useState(false);
const [ qrString, setQRString ] = useState('');
const [ qrFullName, setQRfullName ] = useState('');
const gridConfig = _get(facilityGridConfig, [targetType]) || {};
const gridConfigSmall = _get(facilityGridConfigSmall, [targetType]) || {};
const [searched, setSearched] = useState("");
const requestSearch = (searchedVal: string) => {
const filteredData = data.data.filter((row:any) => {
if (subType === "therapists" || subType === "patients" || subType === "staff"){
return (row['firstName'].toLowerCase().includes(searchedVal.toLowerCase()) || row['lastName'].toLowerCase().includes(searchedVal.toLowerCase()));
}
else {
return (row['name'].toLowerCase().includes(searchedVal.toLowerCase()));
}
});
setfData(filteredData);
};
const cancelSearch = () => {
setSearched("");
requestSearch(searched);
};
useEffect(() => {
setSearched("");
return () => {
mountedRef.current = false;
}
}, []);
useEffect(() => {setfData(data.data)}, [data.data] )
useEffect(() => {
setSearched("");
if (subType === Navigations.facilities.hospitals) {
if (uid) {
setSingleSubType('clinic');
} else {
setSingleSubType('hospital');
}
}
else if (subType === Navigations.facilities.clinics) {
if (uid) {
setSingleSubType('therapist');
} else {
setSingleSubType('clinic');
}
}
else if (subType === Navigations.users.therapists) {
if (uid) {
setSingleSubType('patient');
} else {
setSingleSubType('therapist');
}
}
else if (subType === Navigations.users.staff) {
setSingleSubType('staff');
}
else
setSingleSubType('patient');
return () => {
}
}, [subType, uid]);
useEffect(() => {
console.log('call to api here...')
dispatch({
type: FacilityActionType.GetFacilities,
payload: {
type: type,
subType: subType,
targetType: targetType,
facilityUid: targetType === 'therapists' ? (uid || null) : (_get(activeClinic, 'uid') || null),
parentFacilityUid: uid ? null : (_get(activeHospital, 'uid') || null),
parentUid: uid || null,
skip: 0,
take: 0
}
});
if (!userProfile)
setVisibleAddButton(false);
if (subType === 'hospitals') {
if ([UserRoles.SYSTEMADMIN].indexOf(userProfile.role) >= 0 && !!uid) {
setVisibleAddButton(true);
} else if ([UserRoles.SUPERUSER].indexOf(userProfile.role) < 0) {
setVisibleAddButton(false);
} else {
setVisibleAddButton(true);
}
}
else if (subType === 'clinics') {
if ([UserRoles.FACILITYADMIN].indexOf(userProfile.role) >= 0 && !!uid) {
setVisibleAddButton(true);
} else if ([UserRoles.SUPERUSER, UserRoles.SYSTEMADMIN, UserRoles.FACILITYADMIN].indexOf(userProfile.role) < 0) {
setVisibleAddButton(false);
} else {
setVisibleAddButton(true);
}
} else if (subType === 'therapists') {
if ([UserRoles.SUPERUSER, UserRoles.SYSTEMADMIN, UserRoles.FACILITYADMIN].indexOf(userProfile.role) < 0) {
setVisibleAddButton(false);
} else {
setVisibleAddButton(true);
}
} else if (subType === 'patients') {
if ([UserRoles.SUPERUSER, UserRoles.SYSTEMADMIN, UserRoles.FACILITYADMIN, UserRoles.USER].indexOf(userProfile.role) < 0) {
setVisibleAddButton(false);
} else {
setVisibleAddButton(true);
}
}
setWindowTitle("Augment Therapy: "+subType.charAt(0).toUpperCase() + subType.slice(1));
setSearched("");
return () => {
};
}, [userProfile, subType, uid, dispatch, type, targetType, activeClinic, activeHospital])
useEffect(() => {
setSearched("");
if (data) {
if (subType === Navigations.users.therapists) {
for (let rec of data.data) {
if (!rec['enrollmentStatus']) {
rec.enrollmentStatus = '--'
}
}
}
}
}, [subType,data]);
const setActiveFacility = (dataItem: any) => {
const payload: any = getActiveFacilityPayload(subType, uid);
if (dataItem.facilityType === FacilityType.Hospital) {
payload.activeHospital = dataItem;
} else if (dataItem.facilityType === FacilityType.Clinic) {
payload.activeClinic = dataItem;
} else if (subType === 'clinics' && !!uid) {
payload.activeTherapist = dataItem;
} else if (subType === 'staff' && !uid) {
payload.activeStaff = dataItem;
} else if (subType === 'therapists' && !uid) {
payload.activeTherapist = dataItem;
} else {
payload.activePatient = dataItem;
}
dispatch({type: FacilityActionType.SetActiveFacility, payload: payload});
}
const handleRowClick = (e: any) => {
setActiveFacility(e.dataItem);
if (e.dataItem) {
if (e.dataItem.facilityType === FacilityType.Hospital) {
navigate(`${Navigations.facilities.root}/${Navigations.facilities.hospitals}/${e.dataItem.uid}`, false);
} else if (e.dataItem.facilityType === FacilityType.Clinic) {
navigate(`${Navigations.facilities.root}/${Navigations.facilities.clinics}/${e.dataItem.uid}`, false);
} else if (subType === 'clinics') {
if (!uid) {
navigate(`${Navigations.facilities.root}/${Navigations.facilities.clinics}/${_get(e.dataItem, 'uid')}`, false);
} else { // it's therapist
navigate(`${Navigations.users.root}/${Navigations.users.therapists}/${_get(e.dataItem, 'uid')}`, false);
}
} else if (subType === 'therapists') {
if (!uid) {
navigate(`${Navigations.users.root}/${Navigations.users.therapists}/${_get(e.dataItem, 'uid')}`, false);
} else { // it's therapist
navigate(`${Navigations.users.root}/${Navigations.users.patients}/${_get(e.dataItem, 'uid')}`, false);
}
} else if (subType === 'patients') {
navigate(`${Navigations.users.root}/${Navigations.users.patients}/${_get(e.dataItem, 'uid')}`, false);
}
}
}
const handleLink = (dataItem: any, fieldName: string) => {
setActiveFacility(dataItem);
if (fieldName === 'userTotal') {
navigate(`${Navigations.users.root}/${Navigations.users.therapists}`, false);
} else if (fieldName === 'endUserTotal') {
navigate(`${Navigations.users.root}/${Navigations.users.patients}`, false);
}
}
const handleEdit = (dataItem: any) => {
let newType = Navigations.facilities.root;
if (targetType === Navigations.users.staff || targetType === Navigations.users.therapists || targetType === Navigations.users.patients) {
newType = Navigations.users.root;
}
if (dataItem) {
setActiveFacility(dataItem);
navigate(`${newType}/${targetType}/edit`, false, {uid: dataItem.uid});
} else {
navigate(`${newType}/${targetType}/new`);
}
}
const handleDelete = (dataItem: any) => {
setFacilitiesInEdit([...facilitiesInEdit, dataItem]);
setModalName(dataItem);
setopenDeleteModal(true);
}
const handleDeleteOk = () => {
dispatch({
type: FacilityActionType.DeleteFacilities,
payload: {
type: type,
subType: subType,
targetType: targetType,
body: facilitiesInEdit
}});
setopenDeleteModal(false);
}
const handleDeleteCancel = () => {
setFacilitiesInEdit([]);
setopenDeleteModal(false);
};
const handleInvite = (dataItem: any) => {
//setFacilitiesInEdit([...facilitiesInEdit, dataItem]);
setFacilitiesInEdit([dataItem]);
setModalName(dataItem);
setOpenInviteModal(true);
};
const handleInviteOk = () => {
dispatch({
type: FacilityActionType.PostUserSendInvite,
payload: {
body: facilitiesInEdit,
},
});
setOpenInviteModal(false);
};
const handleInviteCancel = () => {
setFacilitiesInEdit([]);
setOpenInviteModal(false);
};
const setModalName = (dataItem: any) => {
if (dataItem.firstName) {
setModalUserName(dataItem.firstName+' '+dataItem.lastName);
} else {
setModalUserName(dataItem.name);
}
return () => {
}
}
const closeQR = () => {
setOpenQRModal( false );
};
const handleQR = (dataItem: any) => {
setFacilitiesInEdit([...facilitiesInEdit, dataItem]);
setQRString(dataItem.qr);
if (dataItem.lastName) {
setQRfullName( dataItem.firstName + ' ' + dataItem.lastName.charAt(0) + '.' );
} else {
setQRfullName( dataItem.firstName );
}
setOpenQRModal(true);
};
return (
<Page
className={classes.root}
title={windowTitle}
>
<Container
maxWidth={false}
className={classes.container}
>
<Grid
container
direction='row'
style={{height: '100%'}}
>
<Grid
item
xs={12}
className={classes.breadcrumb}
>
<FacilitiesBreadCrumb
type={type}
subType={subType}
/>
</Grid>
<Grid
item
xs={12}
className={classes.content}
>
{
loading && (
<CircularIndeterminate/>
)
}
<Grid item xs={12} >
<StatsCards
type={subType}
uid={uid}
/>
</Grid>
<Hidden mdDown>
<DataGrid
style={{ height: '100%', overflow: 'auto', cursor:'pointer' }}
data={orderBy(fdata, sort)}
sortable={{
mode: 'multiple',
}}
sort={sort}
onSortChange={(e) => {
setSort(e.sort);
}}
onRowClick={(e: any) => handleRowClick(e)}
>
{
(visibleAddButton) && (
<GridToolbar>
<Grid container
style={{justifyContent: 'center', display: 'flex', alignItems: 'center', padding: 10}}>
<Grid item>
<SearchBar
value={searched}
onChange={(searchVal) => requestSearch(searchVal)}
onCancelSearch={() => cancelSearch()}
style={{
display: 'flex',
margin: '0 auto',
maxWidth: 800,
minWidth: 800,
paddingRight: 10
}}>
</SearchBar>
</Grid>
<Grid item>
<button
title="Add new"
className="k-button k-primary"
onClick={() => handleEdit(null)}
style={{
height: 50,
paddingLeft: 10
}}
>
Add New {singleSubType}
</button>
</Grid>
</Grid>
</GridToolbar>
)
}
{
(!(targetType==='hospitals' || targetType==='clinics')) && (
<GridColumn
title="Badge"
width="100"
headerClassName={classes.gridHeaderEdit}
cell={CellWithQR({userProfile: userProfile, type:"badge", handleAction: handleQR})}
/>
)
}
{
((targetType==='hospitals' || targetType==='clinics')) && (
<GridColumn
title=""
width="1"
headerClassName={classes.gridHeaderEdit}
cell={CellWithEdit({userProfile: userProfile, type:"empty", handleAction: handleInvite})}
/>
)
}
{
gridConfig.columns.map((c: any, index: number) => {
return (
<GridColumn key={`grid-${index}`}
field={c.name}
title={c.title}
cell={c.link ? CellWithLink({handleLink}) : undefined}
width= {c.width ? c.width : undefined}
/>
)
})
}
<GridColumn
title="Actions"
width="100"
headerClassName={classes.gridHeaderEdit}
cell={CellWithComboMenu({userProfile: userProfile, type:"edit", handleActionInvite: handleInvite, handleActionEdit: handleEdit, handleActionDelete: handleDelete})}
/>
</DataGrid>
</Hidden>
<Hidden lgUp>
<DataGrid
style={{ height: '100%', overflow: 'auto', cursor:'pointer' }}
data={orderBy(fdata, sort)}
sortable={{
mode: 'multiple',
}}
sort={sort}
onSortChange={(e) => {
setSort(e.sort);
}}
onRowClick={(e: any) => handleRowClick(e)}
>
{
(visibleAddButton) && (
<GridToolbar>
<Grid container
style={{justifyContent: 'center', display: 'flex', alignItems: 'center', padding: 10}}>
<Grid item>
<SearchBar
value={searched}
onChange={(searchVal) => requestSearch(searchVal)}
onCancelSearch={() => cancelSearch()}
style={{
display: 'flex',
margin: '0 auto',
maxWidth: 800,
paddingRight: 10
}}>
</SearchBar>
</Grid>
<Grid item>
<button
title="Add new"
className="k-button k-primary"
onClick={() => handleEdit(null)}
style={{
height: 50,
paddingLeft: 10
}}
>
Add New {singleSubType}
</button>
</Grid>
</Grid>
</GridToolbar>
)
}
{
(!(targetType==='hospitals' || targetType==='clinics')) && (
<GridColumn
title="Badge"
width="20"
headerClassName={classes.gridHeaderEdit}
cell={CellWithQR({userProfile: userProfile, type:"badge", handleAction: handleQR})}
/>
)
}
{
((targetType==='hospitals' || targetType==='clinics')) && (
<GridColumn
title=""
width="10"
headerClassName={classes.gridHeaderEdit}
cell={CellWithEdit({userProfile: userProfile, type:"empty", handleAction: handleInvite})}
/>
)
}
{
gridConfigSmall.columns.map((c: any, index: number) => {
return (
<GridColumn key={`grid-${index}`}
field={c.name}
title={c.title}
cell={c.link ? CellWithLink({handleLink}) : undefined}
minResizableWidth = {c.name.toLowerCase().includes("name")? 20 : 10}
/>
)
})
}
<GridColumn
title="Actions"
width="100"
headerClassName={classes.gridHeaderEdit}
cell={CellWithComboMenu({userProfile: userProfile, type:"edit", handleActionInvite: handleInvite, handleActionEdit: handleEdit, handleActionDelete: handleDelete})}
/>
</DataGrid>
</Hidden>
<ConfirmModal
title="Confirm Deletion"
content={`Are you sure you want to delete the ${singleSubType} ( ${modalUserName} ) ?`}
open={openDeleteModal}
handleClose={handleDeleteCancel}
handleOk={handleDeleteOk}
handleCancel={handleDeleteCancel}
/>
<ConfirmModal
title="Confirm Invite"
content={`Send ${modalUserName} an invitation to connect?`}
open={openInviteModal}
handleClose={handleInviteCancel}
handleOk={handleInviteOk}
handleCancel={handleInviteCancel}
/>
{
(openQRModal) && (
<UserQRCode
id="qrCode"
qrCodeString={qrString}
size={256}
userfullname={qrFullName}
open={true}
handleClose={closeQR}
/>
)
}
</Grid>
</Grid>
</Container>
</Page>
);
}
export default FacilitiesGridContainer;```
Well, I'm new to reactjs, but I suggest to call setSearched in a useEffect hook if your filteredData get changed. So it'll be something like this :
useEffect(() => {
setSearched("");
}, [filteredData])
..despite me changing the value of {searched} using setSearched("")..
this because you call setSearched("") on compoenent mount using (useEffect) but you need this to be called every time your filteredData get updated

Ant Design table delete and edit functionalities not reflecting on the PG database

I am trying to implement crud functionalities in Ant design table. I observed that the delete and edit functionalities only works on the instance when I perform the operations on initial render, but after reloading the component, the table returns back to its initial state and the operations don't affect the database in any way.
I see this error on my console
Type '{ title: string; dataIndex: string; key: string; align: string; editable: boolean;
render?: undefined; }' is not assignable to type 'ColumnType<any>'.
Types of property 'align' are incompatible.
Type 'string' is not assignable to type 'AlignType'.
These are the codes below, I hope to break them down in code blocks so they can be understandable
imports
import React, { useState, useEffect } from 'react';
import { Table, Popconfirm, Button, Space, Input, Form } from 'antd';
import { isEmpty } from 'lodash';
api
const apiUrl = 'api/terminals';
useState and useEffect codes
const [gridData, setGridData] = useState([]);
const [loader, setLoader] = useState(false);
const [editingKey, setEditingKey] = useState('');
const [editRow, setEditRow] = useState(false);
const [form] = Form.useForm();
useEffect(() => {
loadData();
}, []);
const loadData = async () => {
setLoader(true);
const response = await axios.get(apiUrl);
setGridData(response.data);
setLoader(false);
};
modifiedData codes
const modifiedData = gridData.map(({ ...item }) => ({
...item,
key: item.id,
}));
const save = async key => {
try {
const row = await form.validateFields();
const newData = [...modifiedData];
const index = newData.findIndex(item => key === item.key);
if (index > -1) {
const item = newData[index];
newData.splice(index, 1, { ...item, ...row });
setGridData(newData);
setEditingKey('');
}
} catch (error) {
console.warn('Error', error);
}
};
edit and cancel function code block
const edit = record => {
form.setFieldsValue({
name: '',
stock: '',
stockLevelDate: '',
tankThreatLevel: '',
tankThreatLevelColor: '',
tankTopPosition: '',
tankTopPositionColor: '',
lowPumpable: '',
lowPumpableColor: '',
tankCapacity: '',
...record,
});
setEditingKey(record.key);
};
const cancel = () => {
setEditingKey('');
};
editableCell function block
const EditableCell = ({ editing, dataIndex, title, record, children, ...restProps }) =>
{
const input = <Input />;
return (
<td {...restProps}>
{editing ? (
<Form.Item
name={dataIndex}
style={{ margin: 0 }}
rules={[
{
required: true,
message: `Please input ${title}`,
},
]}
>
{input}
</Form.Item>
) : (
children
)}
</td>
);
};
editing code block
const isEditing = record => {
return record.key === editingKey;
};
columns block
const columns = [
{
title: 'ID',
dataIndex: 'id',
key: 'id',
},
{
title: 'Name' as string,
dataIndex: 'name',
key: 'name',
align: 'center',
editable: true,
},
{
title: 'Stock',
dataIndex: 'stock',
key: 'stock',
align: 'center',
editable: true,
},
{
title: 'Stock Level Date',
dataIndex: 'stockLevelDate',
key: 'stockLevelDate',
align: 'center',
editable: true,
},
{
title: 'Tank Threat Level',
dataIndex: 'tankThreatLevel',
key: 'tankThreatLevel',
align: 'center',
editable: true,
},
{
title: 'Tank Threat Level Color',
dataIndex: 'tankThreatLevelColor',
key: 'tankThreatLevelColor',
align: 'center',
editable: true,
},
{
title: 'Tank Top Position',
dataIndex: 'tankTopPosition',
key: 'tankTopPosition',
align: 'center',
editable: true,
},
{
title: 'Tank Top Position Color',
dataIndex: 'tankTopPositionColor',
key: 'tankTopPositionColor',
align: 'center',
editable: true,
},
{
title: 'Low Pumpable',
dataIndex: 'lowPumpable',
key: 'lowPumpable',
align: 'center',
editable: true,
},
{
title: 'Low Pumpable Color',
dataIndex: 'lowPumpableColor',
key: 'lowPumpableColor',
align: 'center',
editable: true,
},
{
title: 'Tank Capacity',
dataIndex: 'tankCapacity',
key: 'tankCapacity',
align: 'center',
editable: true,
},
{
title: 'Actions',
dataIndex: 'actions',
key: 'actions',
align: 'center',
render: (_, record) => {
const editable = isEditing(record);
return modifiedData.length >= 1 ? (
<Space>
<Popconfirm title="Sure to delete?" onConfirm={() => handleDelete(record)}>
<Button type="primary" disabled={editable} danger>
Delete
</Button>
</Popconfirm>
{editable ? (
<span>
<Space size="middle">
<Button onClick={e => save(record.key)} type="primary" style={{ marginRight: 8
}}>
{' '}
Save
</Button>
<Popconfirm title="Sure to cancel?" onConfirm={cancel}>
<Button>Cancel</Button>
</Popconfirm>
</Space>
</span>
) : (
<Button onClick={() => edit(record)} type="primary">
Edit
</Button>
)}
</Space>
) : null;
},
},
];
mergedCoulumns block
const mergedColumns = columns.map(col => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: record => ({
record,
dataIndex: col.dataIndex,
title: col.title,
editing: isEditing(record),
}),
};
});
handleDelete code block
const handleDelete = value => {
const dataSource = [...modifiedData];
const filteredData = dataSource.filter(item => item.id !== value.id);
setGridData(filteredData);
};
return jsx
<>
<div>
<h2>Terminals</h2>
<Link to={`${match.url}/new`} className="btn btn-primary jh-create-entity" id="jh
-create-
entity" data-cy="entityCreateButton">
<FontAwesomeIcon icon="plus" />
Add Terminal
</Link>
<hr color="red" />
<Form form={form} component={false}>
<Table
components={{
body: {
cell: EditableCell,
},
}}
columns={mergedColumns}
dataSource={modifiedData}
bordered
loading={loader}
/>
</Form>
</div>
</>
Thanks in anticipation

If Statement Inside Map

I currently have a DataGrid that is rendering data grabbed from my backend mongoDB. The data rendering is mapped to keys specified by the objects in the mongoDB document. In each document is a set of boolean values, and I am trying to check if any of those are true, if they are true it will render a Y in the repairsNeeded column for each row, if not, it will render an N. The main problem I am running into is where/how to check this. I have played with a few different ideas to no avail. Right now I have the repairsNeeded column for each row assigned to the document.isPowerCordDamaged (one of my booleans), which renders true or false depending on if its checked.
Code:
function Rounding() {
const [cartsRounded, setCartsRounded] = useState([]);
let navigate = useNavigate();
useEffect(() => {
userCartsRounded()
.then((response) => {
setCartsRounded(response.data);
})
.catch((err) => {
console.log(err);
});
}, []);
const columns = [
{
field: "serialNum",
headerName: "Cart Serial Number",
width: 250,
},
{
field: "pcNum",
headerName: "Workstation Number",
width: 250,
},
{
field: "dateLastRounded",
headerName: "Last Rounded On",
width: 250,
},
{
field: "repairsNeeded",
headerName: "Repairs?",
width: 100,
},
{
field: "quarter",
headerName: "Quarter",
width: 75,
},
];
const [sortModel, setSortModel] = React.useState([
{
field: "dateLastRounded",
sort: "desc",
},
]);
const rows = useMemo(
() =>
cartsRounded.map((row, index) => ({
...row,
id: index,
serialNum: row.cartSerialNumber,
pcNum: row.pcNumber,
dateLastRounded: moment(row.updatedAt).format("MM-D-YYYY"),
repairsNeeded: row.isPowerCordDamaged,
quarter: moment(row.updatedAt).format("Qo"),
})),
[cartsRounded]
);
return (
<div>
<IconButton
color="primary"
aria-label="new rounding"
component="span"
onClick={() => {
navigate("add_new_cart");
}}
>
<AddCircleOutline />
</IconButton>
<div style={{ height: 400, width: "100%" }}>
<DataGrid
component={Paper}
rows={rows}
columns={columns}
sortModel={sortModel}
pageSize={100}
rowsPerPageOptions={[100]}
/>
</div>
</div>
);
}
export default Rounding;
Document Example:
{
_id: new ObjectId("61b95e447aec51d938e856cc"),
cartSerialNumber: 'testytitit',
pcNumber: '14124f0sdf0sfs',
isPowerCordDamaged: false,
isFuseBlown: false,
isInverterBad: false,
isInterfaceDamaged: false,
isPhysicalDamage: false,
otherNotes: '',
roundedBy: '6186c13beb18d33d5088f7b2',
createdAt: 2021-12-15T03:17:24.495Z,
updatedAt: 2021-12-15T03:17:24.495Z,
__v: 0
}
I may not be understanding the question here. But I think what you are trying to do is have the repairsNeeded field have a Y or N rather than its boolean value.
Try this.
const rows = useMemo(
() =>
cartsRounded.map((row, index) => ({
...row,
id: index,
serialNum: row.cartSerialNumber,
pcNum: row.pcNumber,
dateLastRounded: moment(row.updatedAt).format("MM-D-YYYY"),
repairsNeeded: row.isPowerCordDamaged ? "Y" : "N" // short hand for if (row.isPowerCordDamaged === true) return "Y" else return "N",
quarter: moment(row.updatedAt).format("Qo"),
})),
[cartsRounded]
);
After looking it may be you want to return Y or N if any are true? In that case best pull it into a function.
const isRepairNeeded = (row) => {
if (row.isPowerCordDamaged) return "Y";
if (row.isFuseBlown) return "Y";
... rest of the ifs
return "N"; // N is only returned if none of the if statements === true;
}
const rows = useMemo(
() =>
cartsRounded.map((row, index) => ({
...row,
id: index,
serialNum: row.cartSerialNumber,
pcNum: row.pcNumber,
dateLastRounded: moment(row.updatedAt).format("MM-D-YYYY"),
repairsNeeded: isRepairNeeded(row) // call the function will return "Y" or "N",
quarter: moment(row.updatedAt).format("Qo"),
})),
[cartsRounded]
);

Add new row with a key press like 'enter'

I would like to add a key listener with 'enter' key to "onRowAdd" action from material-table on react,
Could be able to call the function outside the table props and add a normal js listener to key enter for launch the function?
So i searched on the doc of material-table and nothing similar can help me, anyone have some idea how i can fix it?
thanks
import React, { useEffect, useState } from 'react'
import MaterialTable from 'material-table'
import axios from 'axios'
var emitter = require('../config/global_emitter')
export default function MaterialTableDemo(props) {
const [state, setState] = useState({
columns: [
{ title: 'Item', field: 'item' },
{ title: 'Quantity', field: 'quantity', type: 'numeric' },
{ title: 'Description', field: 'description' },
{ title: 'Price', field: 'price', type: 'numeric' },
{
title: 'Location',
field: 'location',
lookup: { 34: 'Batman', 63: 'Back Store' },
},
],
data: []
});
useEffect(() => {
setState({...state, data:[]})
const user = {
name: props.userSelected
}
axios.post('api', user)
.then(e => {
if (e.data !== ''){
setState({...state, data: e.data})
}
})
},[props.userSelected])
const handleUpdate = (data) => {
const upload = {
items: data,
name: props.userSelected
}
axios.post('api', upload)
.then((e) => {
if (e.status === 200){
emitter.emit('confirmMessage', 'Updated list correctly')
}
})
}
return (
<MaterialTable
title={props.userSelected + '´s items ordered'}
columns={state.columns}
data={state.data}
options={{
headerStyle: {
backgroundColor: '#01579b',
color: '#FFF'
},
rowStyle: {
opacity: 1,
animationName: 'fadeInOpacity',
animationIterationCount: 1,
animationTimingFunction: 'ease-in',
animationDuration: '2s'
}
}}
onKeyDown={(ev) => {
console.log(`Pressed keyCode ${ev.key}`);
if (ev.key === 'Enter') {
// Do code here
ev.preventDefault();
}
}}
editable={{
onRowAdd: newData =>
new Promise(resolve => {
setTimeout(() => {
resolve();
addRow(newData)
}, 600);
}),
onRowUpdate: (newData, oldData) =>
new Promise(resolve => {
setTimeout(() => {
resolve();
const data = [...state.data];
data[data.indexOf(oldData)] = newData;
handleUpdate(data)
setState({ ...state, data });
}, 600);
}),
onRowDelete: oldData =>
new Promise(resolve => {
setTimeout(() => {
resolve();
const data = [...state.data];
data.splice(data.indexOf(oldData), 1);
handleUpdate(data)
setState({ ...state, data });
}, 600);
}),
}}
/>
);
}

Resources