I have a working prototype where I cant seem to figure out when I click on a particular button in my nav, then pass that index to another component. It doesn't update the index when I query using "document.querySelector". My goal is to just move the content to the front child position/firstChild depending on what button you clicked. So if you clicked the first button then the last, my last content would be first and so and so on...I've tried a timeout and still no luck.
JS:
.....
const Context = createContext(false);
const dataArr = [
{
id: "113062",
name: "Blue",
region: "US",
sort: "1"
},
{
id: "115102",
name: "Red",
region: "DE",
sort: "2"
},
{
id: "70884",
name: "Green",
region: "US",
sort: "3"
},
{
id: "114683",
name: "Yellow",
region: "US",
sort: "4"
},
{
id: "112878",
name: "Pale",
region: "US",
sort: "5"
},
{
id: "114682",
name: "Orange",
region: "US",
sort: "6"
},
{
id: "120093",
name: "Black",
region: "CH",
sort: "8"
},
{
id: "120594",
name: "White",
region: "CH",
sort: "9"
}
];
const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
"& > *": {
margin: theme.spacing(1)
}
},
grey: {
border: "4px solid white"
},
orange: {
color: theme.palette.getContrastText(deepOrange[500]),
backgroundColor: deepOrange[500],
border: "4px solid black"
},
info: {
margin: "10px"
},
wrapper: {
display: "flex"
},
contentWrapper: {
display: "flex",
flexDirection: "column"
},
elWrapper: {
opacity: 0,
"&.active": {
opacity: 1
}
}
}));
const ToggleItem = ({ id, description }) => {
const { handleChange, selected, classes } = useContext(Context);
const isSelected = selected.includes(description);
const handleClick = (idx) => {
console.log("idx====", idx);
handleChange(description, idx);
};
return (
<>
<Avatar
className={isSelected ? classes.orange : classes.grey}
onClick={() => handleClick(id)}
>
<span style={{ fontSize: ".75rem" }}>{description}</span>
</Avatar>
</>
);
};
const ToggleContainer = ({ selected, list }) => {
const isSelected = list.filter((element) => selected.includes(element));
return (
<>
{dataArr.map((item, idx) => (
<div
key={idx}
className="carouselWrapper"
style={{ display: isSelected.includes(item.name) ? "block" : "none" }}
>
{item.name}
</div>
))}
</>
);
};
const ToggleWrapper = () => {
const data = [];
dataArr.map((el) => data.push(el.name));
const classes = useStyles();
const [selected, setSelected] = useState([]);
const [viewAll, setViewAll] = useState(true);
const handleChange = (val, idx) => {
setViewAll(false);
setSelected((selected) => {
if (selected.length === 1 && selected.includes(val)) {
return handleViewAll();
}
if (selected.includes(val)) {
return selected.filter((v) => v !== val);
} else {
return [val, ...selected];
}
});
setTimeout(() => {
const someParentObject = document.getElementById("contentWrapper");
const someChildObject = document.querySelectorAll(".carouselWrapper")[
idx
];
// eslint-disable-next-line no-unused-expressions
selected.length > 0 &&
someParentObject.insertBefore(
someChildObject,
someParentObject.firstChild
);
console.log(
"someParentObject.children[0]==",
someParentObject.firstChild
);
console.log("someChildObject", someChildObject);
}, 2000);
};
const handleViewAll = () => {
setViewAll(true);
setSelected([]);
};
return (
<Context.Provider
value={{
viewAll,
handleChange,
handleViewAll,
selected,
classes
}}
>
<div className={classes.wrapper}>
<Avatar
className={viewAll ? classes.orange : null}
onClick={handleViewAll}
>
<span style={{ fontSize: "0.75rem", textAlign: "center" }}>
View All
</span>
</Avatar>
{dataArr.map((d, id) => {
return (
<div key={id}>
<ToggleItem id={id} description={d.name} />
</div>
);
})}
</div>
<div id="contentWrapper" className={classes.contentWrapper}>
<ToggleContainer selected={viewAll ? data : selected} list={data} />
</div>
</Context.Provider>
);
};
export default function App() {
return <ToggleWrapper />;
}
Here is a very quick example of one option. It stores the data in a Map which allows for efficient retrieval (this could be moved to context to avoid passing it to multiple child components) and tracks selected by id in a separate state array. It's then just a matter of mapping the selected array to get your ordered list. If selected is empty render the full values() of the Map.
const { useState, useEffect } = React;
function App({ data }) {
const [selected, setSelected] = useState([]);
const [itemMap, setItemMap] = useState(new Map());
useEffect(() => {
setItemMap(new Map(data.map(({ id, ...rest }) => [id, { id, ...rest }])));
}, [data]);
const selectHandler = (id) => {
setSelected(selected => {
if (id === undefined) return [];
return selected.includes(id)
? selected.filter(_id => _id !== id)
: [id, ...selected];
});
}
return (
<div>
<Header itemMap={itemMap} selected={selected} selectHandler={selectHandler} />
<Selected itemMap={itemMap} selected={selected} />
</div>
);
}
function Header({ itemMap, selected, selectHandler }) {
return (
<div className={"header-container"}>
<div className={!selected.length && 'selected'} onClick={() => selectHandler()}>All</div>
{[...itemMap.values()].map(({ id, name }) => (
<div key={id} className={selected.includes(id) && 'selected'} onClick={() => selectHandler(id)}>
{name}
</div>
))}
</div>
);
}
function Selected({ itemMap, selected }) {
return (
<div className={"selected-container"}>
{selected.length
? selected.map((id) => (
<div key={id} >
{itemMap.get(id).name}
</div>
))
: [...itemMap.values()].map(({ id, name }) => (
<div key={id} >
{name}
</div>
))
}
</div>
);
}
const dataArr = [{ id: "113062", name: "Blue", region: "US", sort: "1" }, { id: "115102", name: "Red", region: "DE", sort: "2" }, { id: "70884", name: "Green", region: "US", sort: "3" }, { id: "114683", name: "Yellow", region: "US", sort: "4" }, { id: "112878", name: "Pale", region: "US", sort: "5" }, { id: "114682", name: "Orange", region: "US", sort: "6" }, { id: "120093", name: "Black", region: "CH", sort: "8" }, { id: "120594", name: "White", region: "CH", sort: "9" }];
const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<App data={dataArr} />);
.header-container { width: 100vw; display: flex; justify-content: flex-start;}
.header-container div { width: 40px; height: 40px; display: flex; overflow: hidden; position: relative; margin: 4px; font-size:.75rem; align-items: center; user-select: none; border-radius: 50%; justify-content: center; color: #fafafa; background-color: #bdbdbd;}
.header-container div.selected { background-color: tomato;}
<script crossorigin src="https://unpkg.com/react#18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.production.min.js"></script>
<div id='root'></div>
Related
I'm using react functional components inside that, I have two different array objects. I would like to render the array objects based on the condition which I have. I'm using the function hooks to define a reference and assign to it dynamically. But, this is returning null. can someone help me on this ? In the below code isDemandGraph is returning null while rendering.
function gridHeatMap(props) {
const heatMap = [
{ color: 'redVeryHigh', value: 50 },
{ color: 'redHigh', value: 25 },
{ color: 'redMedium', value: 20 },
{ color: 'redLow', value: 15 },
{ color: 'white', value: 0 },
{ color: 'greenLow', value: 15 },
{ color: 'greenMedium', value: 20 },
{ color: 'greenHigh', value: 25 },
{ color: 'greenVeryHigh', value: 50 },
];
const demandHeatMap = heatMap.reverse();
const isDemandGraph = useRef(null);
const { height, title, viewName } = props;
const classes = useStyles();
return (
<div className={classes.root}>
<div className={classes.title}>{title}</div>
<Grid container spacing={0}>
{viewName === 'demand' ? { isDemandGraph: heatMap } : { isDemandGraph: demandHeatMap }}
{isDemandGraph.map((item, index) => {
return (
<Grid item style={{ flexGrow: '1', height, width: '11%' }} className={item.color}>
</Grid>
</Grid>
</div>
)
It won't work that way, calling the isDemandGraph inside the ternary operator does not assign value to the same.
Also, you should use useState instead of useRef for your usecase.
I am adding the updated code snippet, it should work as expected:
function gridHeatMap(props) {
const heatMap = [
{ color: 'redVeryHigh', value: 50 },
{ color: 'redHigh', value: 25 },
{ color: 'redMedium', value: 20 },
{ color: 'redLow', value: 15 },
{ color: 'white', value: 0 },
{ color: 'greenLow', value: 15 },
{ color: 'greenMedium', value: 20 },
{ color: 'greenHigh', value: 25 },
{ color: 'greenVeryHigh', value: 50 },
];
const demandHeatMap = heatMap.reverse();
const { height, title, viewName } = props;
const [isDemandGraph, setIsDemandGraph ] = useState(viewName === 'demand' ? heatMap : demandHeatMap);
// setIsDemandGraph can be used to update the value of `isDemandGraph`
const classes = useStyles();
return (
<div className={classes.root}>
<div className={classes.title}>{title}</div>
<Grid container spacing={0}>
{isDemandGraph.map((item, index) => {
return (
<Grid item style={{ flexGrow: '1', height, width: '11%' }} className={item.color}>
</Grid>
</div>
I suggest you to use useState instead of useRef and use useEffect to listen prop changes like:
function gridHeatMap(props) {
const { height, title, viewName } = props;
const [isDemandGraph, setisDemandGraph] = useState([]);
const classes = useStyles();
const heatMap = [
{ color: 'redVeryHigh', value: 50 },
{ color: 'redHigh', value: 25 },
{ color: 'redMedium', value: 20 },
{ color: 'redLow', value: 15 },
{ color: 'white', value: 0 },
{ color: 'greenLow', value: 15 },
{ color: 'greenMedium', value: 20 },
{ color: 'greenHigh', value: 25 },
{ color: 'greenVeryHigh', value: 50 },
];
const demandHeatMap = heatMap.reverse();
useEffect(() => {
viewName === 'demand' ? setisDemandGraph(heatMap) : setisDemandGraph(demandHeatMap);
}, [viewName]);
return (
<div className={classes.root}>
<div className={classes.title}>{title}</div>
<Grid container spacing={0}>
{isDemandGraph.map((item, index) => {
return (
<Grid item style={{ flexGrow: '1', height, width: '11%' }} className={item.color}>
</Grid>
</div>
)
I think this is how I would go (please notice I modified a bit the returned JSX, as the one in your code did not look correct to me, so you might want to double check it):
import React, { useState, useMemo, useEffect } from 'react';
// ... other imports
function gridHeatMap({ height, title, viewName }) {
const heatMap = useMemo(() => ([
{ color: 'redVeryHigh', value: 50 },
{ color: 'redHigh', value: 25 },
{ color: 'redMedium', value: 20 },
{ color: 'redLow', value: 15 },
{ color: 'white', value: 0 },
{ color: 'greenLow', value: 15 },
{ color: 'greenMedium', value: 20 },
{ color: 'greenHigh', value: 25 },
{ color: 'greenVeryHigh', value: 50 },
]), []);
const [currentHeatMap, setCurrentHeatMap] = useState(heatMap);
useEffect(() => {
viewName === 'demand'
? setCurrentHeatMap(heatMap)
: setCurrentHeatMap(heatMap.reverse());
}, [viewName]);
const classes = useStyles();
return (
<div className={classes.root}>
<div className={classes.title}>{title}</div>
<GridContainer spacing={0}>
{
currentHeatMap.map((item, index) => (
<Grid
key={`grid-${index}-${item.color}`}
className={item.color}
style={{ flexGrow: '1', height, width: '11%' }}
item
/>
));
}
</GridContainer>
</div>
);
}
export default gridHeatMap;
I used the code from this article as an example.
It is possible to read the data, but not to save it.
Code:
const { Component, Fragment } = wp.element;
const {
RichText,
InspectorControls,
PanelColorSettings,
AlignmentToolbar,
BlockControls,
} = wp.editor;
const { Button, PanelBody, SelectControl, TextControl } = wp.components;
const { __ } = wp.i18n;
const { registerBlockType } = wp.blocks;
const { withSelect, withDispatch } = wp.data;
class Inspector extends Component {
constructor(props) {
super(...arguments);
}
render() {
const backgroundColors = [
{ color: "#525252", name: "Черный" },
{ color: "#872d2d", name: "Акцентный красный" },
{ color: "#e49312", name: "Акцентный желтый" },
{ color: "#bab3a6", name: "Акцентный кремовый" },
];
const fontSizeOptions = [
{ value: "14px", label: __("14px") },
{ value: "16px", label: __("16px") },
{ value: "18px", label: __("18px") },
{ value: "22px", label: __("22px") },
{ value: "28px", label: __("28px") },
];
const paddingTopOptions = [
{ value: "0px", label: __("0px") },
{ value: "10px", label: __("10px") },
{ value: "25px", label: __("25px") },
{ value: "50px", label: __("50px") },
];
const paddingBottomOptions = [
{ value: "0px", label: __("0px") },
{ value: "10px", label: __("10px") },
{ value: "25px", label: __("25px") },
{ value: "50px", label: __("50px") },
];
const {
setAttributes,
attributes: { text_color, font_size, padding_top, padding_bottom },
} = this.props;
let PluginMetaFields = (props) => {
return (
<>
<TextControl
value={props.text_metafield}
label={__("Text Meta", "textdomain")}
onChange={(value) => props.onMetaFieldChange(value)}
/>
</>
);
};
PluginMetaFields = withSelect((select) => {
return {
text_metafield: select("core/editor").getEditedPostAttribute("meta")[
"_myprefix_text_metafield"
],
};
})(PluginMetaFields);
PluginMetaFields = withDispatch((dispatch) => {
return {
onMetaFieldChange: (value) => {
dispatch("core/editor").editPost({
meta: { _myprefix_text_metafield: value },
});
},
};
})(PluginMetaFields);
return (
<InspectorControls key="inspector">
<PanelBody title={__("Настройки абзаца")}>
<PanelColorSettings
title={__("Цвет шрифта")}
initialOpen={true}
colorSettings={[
{
value: text_color,
colors: backgroundColors,
onChange: (value) => setAttributes({ text_color: value }),
label: __("Цвет шрифта"),
},
]}
/>
<SelectControl
label={__("Размер шрифта")}
options={fontSizeOptions}
value={font_size}
onChange={(value) => this.props.setAttributes({ font_size: value })}
/>
<SelectControl
label={__("Отступ сверху")}
options={paddingTopOptions}
value={padding_top}
onChange={(value) =>
this.props.setAttributes({ padding_top: value })
}
/>
<SelectControl
label={__("Отступ снизу")}
options={paddingBottomOptions}
value={padding_bottom}
onChange={(value) =>
this.props.setAttributes({ padding_bottom: value })
}
/>
<PluginMetaFields />
</PanelBody>
</InspectorControls>
);
}
}
class HeadlineBlock extends Component {
render() {
const {
attributes: {
headline,
text_color,
font_size,
padding_top,
padding_bottom,
alignment,
},
setAttributes,
} = this.props;
const onChangeAlignment = (newAlignment) => {
this.props.setAttributes({
alignment: newAlignment === undefined ? "none" : newAlignment,
});
};
return [
<Inspector {...{ setAttributes, ...this.props }} />,
<div>
{
<BlockControls>
<AlignmentToolbar value={alignment} onChange={onChangeAlignment} />
</BlockControls>
}
<RichText
tagName="p"
placeholder={__("Текст...")}
keepPlaceholderOnFocus
value={headline}
formattingControls={["bold", "italic", "strikethrough", "link"]}
className={"font-" + font_size + " post-desc__p-text"}
style={{
color: text_color,
textAlign: alignment,
}}
onChange={(value) => setAttributes({ headline: value })}
/>
</div>,
];
}
}
registerBlockType("amm-custom-block/test-block", {
title: __("Тест блок"),
icon: "shield",
category: "AMM",
attributes: {
headline: {
type: "string",
},
alignment: {
type: "string",
default: "none",
},
text_color: {
type: "string",
default: "#525252",
},
font_size: {
type: "string",
default: "14px",
},
padding_top: {
type: "string",
default: "50px",
},
padding_bottom: {
type: "string",
default: "0px",
},
},
edit: HeadlineBlock,
save: function (props) {
const {
attributes: {
headline,
text_color,
font_size,
padding_top,
padding_bottom,
alignment,
},
} = props;
return (
<Fragment>
{headline && !!headline.length && (
<RichText.Content
tagName="p"
className={"font-" + font_size + " post-desc__p-text"}
style={{
color: text_color,
paddingTop: padding_top,
paddingBottom: padding_bottom,
textAlign: alignment,
}}
value={headline}
/>
)}
</Fragment>
);
},
});
So far just added a text field to the block and am trying to read and save the data.
With reading everything is OK, but saving the data does not work and there are no errors.
Any idea why this is happening?
sorry english is not a native language
The meta field _myprefix_text_metafield is a protected field as it starts with a "_" (underscore). This is why you can read the value in withSelect() but not save over it withDispatch() without passing auth_callback.
To save to a protected field, the auth_callback is required, eg:
<?php
register_post_meta( 'post', '_myprefix_text_metafield', array(
'show_in_rest' => true,
'single' => true,
'type' => 'string',
'auth_callback' => function() {
return current_user_can( 'edit_posts' );
})
);
?>
There is an example in the tutorial you are following:
https://css-tricks.com/managing-wordpress-metadata-in-gutenberg-using-a-sidebar-plugin/#post-291605
Alternatively, if your meta field is not required to be protected/private: re-register your meta field as myprefix_text_metafield (no underscore in the name and no auth_callback) and your current code will work, eg:
<?php
register_post_meta( 'post', 'myprefix_text_metafield', array(
'show_in_rest' => true,
'single' => true,
'type' => 'string'
);
?>
I have one Lock-Unlock button and a delete button, So the problem is when I render data from axios using useEffect hook its working, but if I lock or unlock a user the table is not changing automatically. That means axios is not getting called.
In that case if I put the useState hook in the useEffect, API is getting called multiple times, that is not as expected. In that case can anyone suggest me how can I re render data table as soon as user clicks on lock-unlock button.
FYI, lock unlock functionality is working 100% correctly. What is needed I have to go to other page and again come back to my datatable page in order to see the change.
code snippet:
const useStyles = makeStyles(theme => ({
button: {
margin: theme.spacing(1)
},
input: {
display: "none"
}
}));
/*
Customization of mui
*/
const getMuiTheme = () => createMuiTheme({
overrides: {
MUIDataTableBodyCell: {
root: {
backgroundColor: "#FFFFFE"
}
}
}
});
/* eslint-disable */
const UserDetailsDatatable = () => {
// console.log('POS: ' + localStorageService.getItem("auth_user"));
if(localStorageService.getItem("auth_user") == null){
history.push({
pathname: "/session/signin"
});
}
const [responsive, setResponsive] = useState("vertical");
const [dataRenderHook, setDataRenderHook] = useState([]);
const [tableBodyHeight, setTableBodyHeight] = useState("650px");
const [tableBodyMaxHeight, setTableBodyMaxHeight] = useState("");
const classes = useStyles();
// Redirect to edit
const newMountpage = (rowData) => {
var pickedUpRowData = rowData.rowData;
let path = `/Admin/users/editUser`;
history.push({
pathname: path,
state: {detail: pickedUpRowData}
});
};
/* Deletion */
const deleteRow = (rowDatam) => {
deleteUser(rowDatam);
};
/* User Locker/Unlocker */
const LockerUnlocker = (Iopticore_ID, state) => {
console.log('State: '+ state);
lockerUnlocker(Iopticore_ID, Boolean(state));
}
const ChangeHandler = (event) => {
console.log(event);
};
const columns = [
{
name: "userID",
label: "Iopticore_ID",
options: {
filter: true,
sort: true,
}
},
{
name: "userName",
label: "Corporate ID",
options: {
filter: true,
sort: true,
}
},
{
name: "name",
label: "User Name",
options: {
filter: true,
sort: true,
}
},
{
name: "email",
label: "Email",
options: {
filter: true,
sort: true,
}
},
{
name: "role",
label: "Role",
options: {
filter: true,
sort: true,
}
},
{
name: "external",
label: "IsExternal",
options: {
filter: true,
sort: true,
filterOptions: {
names: ["Yes", "No"],
logic(v, filterVal) {
const show =
(filterVal.indexOf("Yes") >= 0 && ( v === true || v === 'true') ) ||
(filterVal.indexOf("No") >= 0 && ( v === false || v === 'false') );
return !show;
}
},
customBodyRender: (val) => {
return val === true ? "Yes" : "No";
}
}
},
{
name: "locked",
label: "Access",
options: {
filter: true,
sort: true,
empty: true,
filter: true,
sort: true,
filterOptions: {
names: ["Yes", "No"],
logic(v, filterVal) {
const show =
(filterVal.indexOf("Yes") >= 0 && ( v === true || v === 'true') ) ||
(filterVal.indexOf("No") >= 0 && ( v === false || v === 'false') );
return !show;
}
},
customBodyRender: (val, tableMeta) => {
//console.log('v: ' + val + ' ' + JSON.stringify(tableMeta)) ;
return val === true ? (
<Fab
size="small"
variant="extended"
aria-label="Delete"
className={classes.button}
color="default"
onClick={() => LockerUnlocker(tableMeta.rowData[0], tableMeta.rowData[6])}
>
<img src="https://img.icons8.com/plasticine/25/000000/unlock.png"/>
<b>UnLock </b>
</Fab>
) : (
<Fab
size="small"
variant="extended"
aria-label="Delete"
className={classes.button}
color="default"
onClick={() => LockerUnlocker(tableMeta.rowData[0])}
>
<img src="https://img.icons8.com/dusk/25/000000/unlock.png"/>
<b> Lock </b>
</Fab>
);
}
}
},
{
name: "Edit",
options: {
filter: true,
sort: false,
empty: true,
customBodyRender: (value, tableMeta, updateValue) => {
return (
<Fab
size="small"
color="primary"
aria-label="Edit"
className={classes.button}
onClick={() => newMountpage(tableMeta)}
>
<Icon>edit_icon</Icon>
</Fab>
);
}
}
},
{
name: "Delete",
options: {
filter: true,
textAlign: 'center',
sort: false,
empty: true,
customBodyRender: (value, tableMeta, updateValue) => {
return (
<Fab size="small"
color="secondary"
aria-label="Edit"
className={classes.button}
onClick={() => deleteRow(tableMeta.rowData[0])}
>
<Icon>delete_icon</Icon>
</Fab>
);
}
}
}];
const options = {
selectableRows: 'none',
filter: true,
textAlign: 'center',
filterType: "dropdown",
rowsPerPage: 5,
pagination: true,
responsive,
enableNestedDataAccess: '.',
tableBodyHeight,
tableBodyMaxHeight
};
var recentReceivedToken = localStorage.getItem('jwtAuthtokenManager');
var res= [];
useEffect(() => {
(async () => {
res = await axios.get('<URL>', {
headers: {"Authorization" : `Bearer ${recentReceivedToken}`}
},
// console.log('Hola : ' + JSON.stringify(res))
)
.catch((error)=> {
if(error.response.status != 200){
swal({
title: "Opps! Access Denied",
imageUrl: 'https://notionpress.com/new-rewamp/images/404-error.gif',
text: "You Might Not Have Access To This Page.",
icon: "error",
});
}
})
//console.log('Res : ' + JSON.stringify(res));
setDataRenderHook(res.data);
})();
}, []);
//console.log('Data Promise : ' + renderDatatable());
if(dataRenderHook.length === 0){
//console.log('Length: 0');
return(
<React.Fragment>
<div style={{padding:40}}>
<MuiThemeProvider theme={getMuiTheme()}>
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center"
}}
>
<LinearBuffer />
</div>
</MuiThemeProvider>
</div>
</React.Fragment>
)
}else{
//console.log('Length: 1');
return (
<React.Fragment>
<div style={{padding:40}}>
<MuiThemeProvider theme={getMuiTheme()}>
<MUIDataTable
title={"IoptiCore User List"}
data={dataRenderHook}
columns={columns}
options={options}
/>
</MuiThemeProvider>
</div>
</React.Fragment>
);
}
}
export default UserDetailsDatatable;
import React, { useState, useEffect } from "react";
import MUIDataTable from "mui-datatables";
import { makeStyles,createMuiTheme, MuiThemeProvider } from "#material-ui/core/styles";
import { Icon, Fab } from "#material-ui/core";
import history from "history.js";
import axios from "axios";
import swal from 'sweetalert';
import LinearBuffer from './EditUserHelper/progresscircle';
import lockerUnlocker from './Lock_Unlock/lockerUnlocker';
import deleteUser from './DeleteUser/DeleteUser'
import localStorageService from "../../../services/localStorageService";
import apiDataReturner from "./userAxios";
const useStyles = makeStyles(theme => ({
button: {
margin: theme.spacing(1)
},
input: {
display: "none"
}
}));
/*
Customization of mui
*/
const getMuiTheme = () => createMuiTheme({
overrides: {
MUIDataTableBodyCell: {
root: {
backgroundColor: "#FFFFFE"
}
}
}
});
/* eslint-disable */
const UserDetailsDatatable = () => {
// console.log('POS: ' + localStorageService.getItem("auth_user"));
if(localStorageService.getItem("auth_user") == null){
history.push({
pathname: "/session/signin"
});
}
const [responsive, setResponsive] = useState("vertical");
const [dataRenderHook, setDataRenderHook] = useState([]);
const [tableBodyHeight, setTableBodyHeight] = useState("650px");
const [tableBodyMaxHeight, setTableBodyMaxHeight] = useState("");
const [tracker, setTracker] = useState();
const classes = useStyles();
// Redirect to edit
const newMountpage = (rowData) => {
var pickedUpRowData = rowData.rowData;
let path = `/Admin/users/editUser`;
history.push({
pathname: path,
state: {detail: pickedUpRowData}
});
};
/* Deletion */
const deleteRow = (rowDatam) => {
deleteUser(rowDatam);
var x = apiDataReturner();
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
setTracker(x);
}, 3000);
});
promise.then(values => {
setDataRenderHook(values);
});
};
/* User Locker/Unlocker */
const LockerUnlocker = (Iopticore_ID, state) => {
lockerUnlocker(Iopticore_ID, Boolean(state));
var x = apiDataReturner();
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
setTracker(x);
}, 3000);
});
promise.then(values => {
setDataRenderHook(values);
});
}
const ChangeHandler = (event) => {
console.log(event);
};
const columns = [
{
name: "userID",
label: "Iopticore_ID",
options: {
filter: true,
sort: true,
responsive: 'scrollFullHeightFullWidth',
display: false
}
},
{
name: "userName",
label: "Corporate ID",
options: {
filter: true,
responsive: 'scrollFullHeightFullWidth',
sort: true,
}
},
{
name: "name",
label: "User Name",
options: {
filter: true,
responsive: 'scrollFullHeightFullWidth',
sort: true,
}
},
{
name: "email",
label: "Email",
options: {
filter: true,
responsive: 'scrollFullHeightFullWidth',
sort: true,
}
},
{
name: "role",
label: "Role",
options: {
filter: true,
responsive: 'scrollFullHeightFullWidth',
sort: true,
}
},
{
name: "external",
label: "IsExternal",
options: {
filter: true,
sort: true,
responsive: 'scrollFullHeightFullWidth',
filterOptions: {
names: ["Yes", "No"],
logic(v, filterVal) {
const show =
(filterVal.indexOf("Yes") >= 0 && ( v === true || v === 'true') ) ||
(filterVal.indexOf("No") >= 0 && ( v === false || v === 'false') );
return !show;
}
},
customBodyRender: (val) => {
return val === true ? "Yes" : "No";
}
}
},
{
name: "locked",
label: "Access",
options: {
filter: true,
sort: true,
empty: true,
filter: true,
sort: true,
responsive: 'scrollFullHeightFullWidth',
filterOptions: {
names: ["Yes", "No"],
logic(v, filterVal) {
const show =
(filterVal.indexOf("Yes") >= 0 && ( v === true || v === 'true') ) ||
(filterVal.indexOf("No") >= 0 && ( v === false || v === 'false') );
return !show;
}
},
customBodyRender: (val, tableMeta) => {
//console.log('v: ' + val + ' ' + JSON.stringify(tableMeta)) ;
return val === true ? (
<Fab
size="small"
variant="extended"
aria-label="Delete"
className={classes.button}
color="default"
onClick={() => LockerUnlocker(tableMeta.rowData[0], tableMeta.rowData[6])}
>
<img src="https://img.icons8.com/plasticine/25/000000/unlock.png"/>
<b>UnLock </b>
</Fab>
) : (
<Fab
size="small"
variant="extended"
aria-label="Delete"
className={classes.button}
color="default"
onClick={() => LockerUnlocker(tableMeta.rowData[0])}
>
<img src="https://img.icons8.com/dusk/25/000000/unlock.png"/>
<b> Lock </b>
</Fab>
);
}
}
},
{
name: "Edit",
options: {
filter: true,
sort: false,
empty: true,
responsive: 'scrollFullHeightFullWidth',
customBodyRender: (value, tableMeta, updateValue) => {
return (
<Fab
size="small"
color="primary"
aria-label="Edit"
className={classes.button}
onClick={() => newMountpage(tableMeta)}
>
<Icon>edit_icon</Icon>
</Fab>
);
}
}
},
{
name: "Delete",
options: {
filter: true,
textAlign: 'center',
sort: false,
empty: true,
responsive: 'scrollMaxWidth',
customBodyRender: (value, tableMeta, updateValue) => {
return (
<Fab size="small"
color="secondary"
aria-label="Edit"
className={classes.button}
onClick={() => deleteRow(tableMeta.rowData[0])}
>
<Icon>delete_icon</Icon>
</Fab>
);
}
}
}];
const options = {
selectableRows: 'none',
filter: true,
textAlign: 'center',
filterType: "dropdown",
fixedHeaderOptions:true,
rowsPerPage: 5,
pagination: true,
responsive: 'stacked',
enableNestedDataAccess: '.',
tableBodyHeight,
tableBodyMaxHeight
};
//var recentReceivedToken = localStorage.getItem('jwtAuthtokenManager');
var res= [];
useEffect(() => {
var x = apiDataReturner();
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(x);
}, 2000);
});
promise.then(values => {
setDataRenderHook(values);
});
}, [tracker]);
if(dataRenderHook.length === 0){
return(
<React.Fragment>
<div style={{padding:0}}>
<MuiThemeProvider theme={getMuiTheme()}>
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center"
}}
>
<LinearBuffer />
</div>
</MuiThemeProvider>
</div>
</React.Fragment>
)
}else{
return (
<div>
<React.Fragment>
<div style={{padding:0}}>
<MuiThemeProvider theme={getMuiTheme()}>
<MUIDataTable
title={"IoptiCore User List"}
data={dataRenderHook}
columns={columns}
options={options}
/>
</MuiThemeProvider>
</div>
</React.Fragment>
</div>
);
}
}
export default UserDetailsDatatable;
Sharing the code, it might help someone.
I have this component which is a filter for a table..
handleSearch function is responsible to update const filters... its work perfectly when dataIndex props is the same, but when it changes, filters value is backing to it's initial value, an empty array.
I can't manage to resolve it, I've already console log everything.
import React, { useState, } from "react";
import { SearchOutlined } from "#ant-design/icons";
import { Select, Button, Space } from "antd";
const TableFilter = (props) => {
const {
filterType,
filterMode,
filterOptions,
FilterSelectOnFocus,
dataIndex,
setSelectedKeys,
selectedKeys,
confirm,
clearFilters,
} = props;
const [filters, setFilters] = useState([]);
const SelectFilter = (
<Select
style={{ width: 188, marginBottom: 8, display: "block" }}
type={filterType}
mode={filterMode}
name={dataIndex}
value={selectedKeys}
optionFilterProp="children"
placeholder={`Search ${dataIndex}`}
onFocus={FilterSelectOnFocus}
showSearch
onChange={(value) => setSelectedKeys(value ? value : [])}
getPopupContainer={(trigger) => trigger}
notFoundContent
>
{filterOptions?.map((type, key) => (
<Select.Option value={type.value} key={key}>
{type.label}
</Select.Option>
))}
</Select>
);
const defaultFilterTypes = [
{
type: "select",
element: SelectFilter,
},
];
const handleFilterType = () => {
const type = defaultFilterTypes.find((types) => types.type === filterType);
return type.element;
};
const handleSearch = () => {
console.log(filters) //is empty when dataIndex value change, when it's is the same it get the update value of the 75 line
confirm();
const newFilterValues = [...filters]
const index = newFilterValues.findIndex(newValue => newValue.searchedColumn === dataIndex)
if(index === -1){
newFilterValues.push({ searchText: selectedKeys, searchedColumn: dataIndex})
}
else{
newFilterValues[index] = {searchText: selectedKeys, searchedColumn: dataIndex}
}
setFilters(newFilterValues)
}
const handleReset = () => {
console.log('reset');
clearFilters();
setFilters({ searchText: "" });
setSelectedKeys([]);
};
return (
<div style={{ padding: 8 }}>
{handleFilterType()}
<Space>
<Button
type="primary"
onClick={() => handleSearch()}
icon={<SearchOutlined />}
size="small"
style={{ width: 90 }}
>
Search
</Button>
<Button
onClick={() => handleReset()}
size="small"
style={{ width: 90 }}
>
Reset
</Button>
</Space>
</div>
);
};
export default TableFilter;
Table Component
import React, { useEffect, useState } from "react";
import { Table } from "antd";
import { getTransactions } from "../../../../api/Transactions";
import { formatCnpjCpf, formatCurrency } from "../../../../utils/masks";
import TableFilter from "../../../../shared-components/ant-design/containers/TableFilters";
import { getPartnersAsOptions } from "../../../../api/Partners";
const Insider = (props) => {
const [data, setData] = useState([]);
const [paginationValues, setPaginationValues] = useState({
current: 1,
pageSize: 50,
total: 0,
position: ["topRight"],
});
const [partners, setPartners] = useState([{value: null, label: 'carregando...'}])
const context = "insider";
function getColumnSearchProps(
dataIndex,
filterType,
filterMode,
filterOptions,
FilterSelectOnFocus
) {
return {
filterDropdown: ({
setSelectedKeys,
selectedKeys,
confirm,
clearFilters,
}) => {
return (
<TableFilter
dataIndex={dataIndex}
filterType={filterType}
filterMode={filterMode}
filterOptions={filterOptions}
FilterSelectOnFocus={FilterSelectOnFocus}
setSelectedKeys={setSelectedKeys}
selectedKeys={selectedKeys}
confirm={confirm}
clearFilters={clearFilters}
/>
);
},
};
}
async function getPartners(){
if(partners.length > 2){
return
}
const response = await getPartnersAsOptions(paginationValues)
setPartners(response.data)
}
const columns = [
{
dataIndex: ["transactionType", "desc"],
title: "Tipo de Transação",
sorter: true,
key: "orderTransactionType",
...getColumnSearchProps("orderTransactionType"),
},
{
dataIndex: "transactionDate",
title: "Data Transação",
key: "orderTransactionDate",
sorter: true,
...getColumnSearchProps("orderTransactionDate"),
},
{
title: "Nome origem",
dataIndex: ["source", "name"],
sorter: true,
key: "orderSourceCustomerName",
},
{
render: (render) => formatCnpjCpf(render.source.document.value),
title: "Documento origem",
key: "sourceCustomer",
...getColumnSearchProps("sourceCustomer", "select", "tags")
},
{
title: "Nome destino",
dataIndex: ["target", "name"],
sorter: true,
key: "orderTargetCustomerName",
},
{
render: (render) => formatCnpjCpf(render.target.document.value),
title: "Documento destino",
},
{
render: (render) => formatCurrency(render.value),
title: "Valor da transação",
key: "orderValue",
sorter: true,
align: "right",
},
{
render: (render) => formatCurrency(render.chargedTariff),
title: "Tarifa",
key: "orderChargedTariff",
sorter: true,
align: "right",
},
{
render: (render) => formatCurrency(render.cost),
title: "Custo",
key: "orderCost",
sorter: true,
align: "right",
},
{
render: (render) => formatCurrency(render.revenue),
title: "Receita",
key: "orderRevenue",
sorter: true,
align: "right",
},
{
title: "Parceiro",
name: "Parceiro",
dataIndex: ["partner", "name"],
key: "orderPartnerName",
sorter: true,
align: "center",
...getColumnSearchProps(
"orderPartnerName",
"select",
"multiple",
partners,
getPartners)
},
{
title: "id da transação",
name: "id da transação",
dataIndex: "id",
},
];
useEffect(function transactions() {
async function fetchTransactions() {
const response = await getTransactions(context, paginationValues);
if (response) {
const { data, pagination } = response;
setData(data);
setPaginationValues(pagination);
}
}
fetchTransactions();
// eslint-disable-next-line
}, []);
return <Table dataSource={data} columns={columns} />;
};
export default Insider;
You could move this piece of code
const [filters, setFilters] = useState([]);
In a higher level
I am creating a React app and have the following list:
const list = [
{
id: '1',
task: 'task 1',
activities: [{
'Google': [
{url: 'https://www.google.com'},
{visited: false}
],
'Yahoo': [
{url: 'https://www.yahoo.com'},
{visited: false}
],
'Bing': [
{url: 'https://www.bing.com'},
{visited: false}
]
}],
visitedAll: false
},
{
id: '2',
task: 'task 2',
activities: [{
'Facebook': [
{url: 'https://www.facebook.com'},
{visited: false}
],
'Instagram': [
{url: 'https://www.instagram.com'},
{visited: false}
],
'Twitter': [
{url: 'https://www.twitter.com'},
{visited: false}
]
}],
visitedAll: false
}
];
I am iterating through the list like so:
<ul>
{list.map(item => (
<li key={item.id}>
{item.task}
<ul>
{Object.keys(item.activities[0]).map((act, i) => <li key={i}>{act}</li>)}
</ul>
</li>
))}
</ul>
Which will produce the following output:
task 1
Google
Yahoo
Bing
task 2
Facebook
Instagram
Twitter
How can I wrap the submenu list items with their corresponding anchors?
How do I add an onClick event which will set the visited key to true for each item?
You could first modify activities property to be one object using reduce method and then you can create separate component for sub-items where each holds state with visited property.
const {useState} = React;
const list = [{"id":"1","task":"task 1","activities":[{"Google":[{"url":"https://www.google.com"},{"visited":false}],"Yahoo":[{"url":"https://www.yahoo.com"},{"visited":false}],"Bing":[{"url":"https://www.bing.com"},{"visited":false}]}],"visitedAll":false},{"id":"2","task":"task 2","activities":[{"Facebook":[{"url":"https://www.facebook.com"},{"visited":false}],"Instagram":[{"url":"https://www.instagram.com"},{"visited":false}],"Twitter":[{"url":"https://www.twitter.com"},{"visited":false}]}],"visitedAll":false}]
.map(({activities, ...rest}) => ({
...rest, activities: activities.reduce((r, e) => Object.assign(r, e), {})
}))
const SubListItem = props => {
const {data, name} = props;
const [visited, setVisited] = useState(data[1].visited);
const toggle = event => {
event.preventDefault();
setVisited(!visited);
}
return <li>
<a style={{color: visited ? "green" : ''}}
onClick={toggle}
href={data[0].url}>{name}</a>
</li>
}
const List = ({data}) => <ul>
{data.map(item => <li key={item.id}>
{item.task}
<ul>{Object.keys(item.activities).map(act => (
<SubListItem key={act} name={act} data={item.activities[act]} />
))}</ul>
</li>)}
</ul>
ReactDOM.render(<List data={list} />, document.querySelector("#app"))
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="app"></div>
I would implement an Anchor component which will save the state of visited.
Also, the parent component App would have a state of "all visited anchors".
Example:
const list = [
{
id: '1',
task: 'task 1',
activities: {
Google: {
url: 'https://www.google.com'
},
Yahoo: {
url: 'https://www.yahoo.com'
},
Bing: {
url: 'https://www.bing.com'
}
}
},
{
id: '2',
task: 'task 2',
activities: {
Facebook: {
url: 'https://www.facebook.com'
},
Instagram: {
url: 'https://www.instagram.com'
},
Twitter: {
url: 'https://www.twitter.com'
}
}
}
];
const Anchor = ({ name, href, onClick }) => {
const [isVisited, setIsVisited] = useState(false);
const onAnchorClick = () => {
setIsVisited(visited => !visited);
onClick();
};
return (
// v add href={href}
<a onClick={onAnchorClick}>{`${name} is ${
isVisited ? '' : 'not'
} visited`}</a>
);
};
// You can get such object from reducing the list
const visitedInitial = {
Google: false,
Yahoo: false,
Bing: false,
Facebook: false,
Instagram: false,
Twitter: false
};
const App = () => {
const [visited, setVisited] = useState(visitedInitial);
return (
<>
<h1>
{Object.values(visited).every(value => value === true)
? 'All Visited'
: 'Please visit all'}
</h1>
<ul>
{list.map(({ id, task, activities }) => (
<li key={id}>
{task}
<ul>
{Object.entries(activities).map(([name, { url }], i) => (
<li key={i}>
<Anchor
name={name}
href={url}
target="_blank"
onClick={() =>
setVisited(prev => ({ ...prev, [name]: true }))
}
/>
</li>
))}
</ul>
</li>
))}
</ul>
</>
);
};
Here is the answer based on the items you've provided. I've extracted drawing sub-items into a separate function that accepts an item argument. Every sub-item has an onClick handler which sets visited to true.
import React, { useState, useCallback } from "react";
import ReactDOM from "react-dom";
const initialItems = [
{
id: "1",
task: "task 1",
activities: [
{
Google: [{ url: "https://www.google.com" }, { visited: false }],
Yahoo: [{ url: "https://www.yahoo.com" }, { visited: false }],
Bing: [{ url: "https://www.bing.com" }, { visited: false }]
}
]
},
{
id: "2",
task: "task 2",
activities: [
{
Facebook: [{ url: "https://www.facebook.com" }, { visited: false }],
Instagram: [{ url: "https://www.instagram.com" }, { visited: false }],
Twitter: [{ url: "https://www.twitter.com" }, { visited: false }]
}
]
}
];
function App() {
const [items, setItems] = useState(initialItems);
const setVisited = React.useCallback(
(id, activityName) => {
const item = items.find(item => item.id === id);
if (item) {
item.activities[0][activityName][1].visited = true;
setItems([...items]);
}
},
[items, setItems]
);
const getSubItems = useCallback(
item => {
const activities = item.activities[0];
return Object.keys(activities).map(activityName => {
const url = activities[activityName][0].url;
const onClick = () => {
setVisited(item.id, activityName);
};
return (
<li key={activityName}>
<a
href={url}
onClick={onClick}
target="_blank"
rel="noopener noreferrer"
>
{activityName}
</a>
</li>
);
});
},
[setVisited]
);
return (
<div>
<ul>
{items.map(item => (
<li key={item.id}>
{item.task}
<ul>{getSubItems(item)}</ul>
</li>
))}
</ul>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
EXAMPLE