I need some help pls. I created a menu with section and courses; but the section-labels are repeating.
I would like all courses with the same section to be displayed on a single section lable.
Please see code and screenshot.
<Menu
defaultSelectedKeys={[clicked]}
inlineCollapsed={collapsed}
style={{ height: "100vh", overflow: "scroll" }}
mode="inline"
>
{course.lessons.map((lesson, index) => (
<SubMenu
title={lesson.section}>
<ItemGroup
key={index}>
<Item
onClick={() => setClicked(index)}
key={index}
icon={<Avatar>{index + 1}</Avatar>}
>
{lesson.title.substring(0, 30)}
{" "}
{completedLessons.includes(lesson._id) ? (
<CheckCircleFilled
className="float-end text-primary ml-2"
style={{ marginTop: "13px" }}
/>
) : (
<MinusCircleFilled
className="float-end text-danger ml-2"
style={{ marginTop: "13px" }}
/>
)}
</Item>
</ItemGroup>
</SubMenu>
))}
</Menu>
Screenshot of the Menu
You need to group the lessons by section, then you must iterate over each lessons in the section.
Here is a possible solution :
// group lessons by section into an object {sectionName: [lesson1, lesson2]}
const lessonsBySection = course.lessons.reduce(function(obj, lesson) {
(obj[lesson.section] = obj[lesson.section] || []).push(lesson);
return obj;
}, {});
// get all the sections (sorted alphabetically)
const sections = Object.keys(lessonsBySection).sort();
return (
<Menu ...>
{sections.map((section, sectionIndex) => (
<SubMenu key={section} title={section}>
<ItemGroup>
{lessonsBySection[section].map((lesson, lessonIndex) => (
<Item
key={lesson._id}
onClick={() => setClicked(
courses.lessons.findIndex(l => l._id === lesson._id)
)}
icon={<Avatar>{lessonIndex + 1}</Avatar>}
>
...
</Item>
))}
</ItemGroup>
</SubMenu>
))}
</Menu>
)
Related
How to maintain states for multiple tabs having same panel Components. For example, on tab1 it shows user1 details, on tab2 it shows user2 details. But when I click on tab2 details or inner components it also happen on tab1 because of same redux state. How to manage different state? Maybe on basis of tab index? Or whatever you could suggest.
<Tabs
onChange={handleCurrentPatient}
value={currentPatient}
variant="scrollable"
scrollButtons="auto"
textColor="primary"
indicatorColor="primary"
>
<Tab label="Patients" />
{dynTabs.map((patient, ind) => (
<Tab
label={patient.patientName}
onClick={() => {
dispatch(SetCurrentPatient(patient.emrId));
}}
value={patient.emrId}
key={ind}
icon={
patient.closeIcon ? (
<Box onClick={(e) => e.stopPropagation()}>
<CloseIcon
// fontSize="small"
style={{ fontSize: "16px" }}
/>
</Box>
) : (
""
)
}
iconPosition="end"
/>
))}
</Tabs>;
{
dynTabs.map((item2, index) =>
item2.emrId == currentPatient ? (
PatientList.map((item1, key2) =>
currentPatient == item1.emrId ? ( // tab body of current active tab
<TabPanel key={key2} value={index} index={index}>
<PatientInfo data={item1}></PatientInfo>
<Divider sx={{ borderTop: 1, borderTopColor: "#186ad1" }} />
<PatientRecordTabs />
</TabPanel>
) : (
<></>
)
)
) : (
<>{dynTabs.length == 0 ? setCurrentPatient("000") : <></>}</>
)
);
}
I want different states for same tab panel components on basis of tab id or index whatever. Any suggestions?
I am trying to perform a simple exercise, wherein I am showing the data from the local JSON file saved in my folder, and then reading it to show the data with useState, such that when a user clicks in he can edit the author and location filed and save it.
The issue I get is when I save the data in my group or location it does not populate the new value, however, it just removes the new value, but when I filter and select all values from the dropdown I can see the values in there.
Can anyone please let me know what is the missing part here? I want to achieve the task that when a user updates the value on the author group or location group they should be there with the new group value.
Please the link to the code below and a working demo on codeSandbox
watch the full code and demo here
The source code for the author group component I tried so far;
import { useEffect, useState } from "react";
import {
Container,
Accordion,
AccordionSummary,
AccordionDetails,
Typography,
Button
} from "#mui/material";
import ExpandMoreIcon from "#mui/icons-material/ExpandMore";
import EditIcon from "#mui/icons-material/Edit";
const Authorsgroup = ({
posts,
groupDropdownValue,
setShowForm,
setPostId,
showForm
}) => {
const authorGroup = posts.reduce((group, authorgroup) => {
(group[authorgroup.author.replace(/ +/g, "")] =
group[authorgroup.author.replace(/ +/g, "")] || []).push(authorgroup);
return group;
}, {});
const [authorGroupValues, setAuthorGroupValues] = useState(authorGroup);
useEffect(() => {
setAuthorGroupValues(authorGroup);
console.log(authorGroupValues);
}, [groupDropdownValue, showForm]);
return (
<>
{/* all gorupby authors */}
<Container>
{/* show group of Tapesh */}
<Container style={{ marginTop: "3rem" }}>
{authorGroupValues?.Tapesh.map((post, index) => (
<Accordion key={index}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
id="panel1a-header"
>
<Typography variant="h6" style={{ color: "#EB1283" }}>
{post.id} {post.author} {post.location}
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography variant="h4">{post.location}</Typography>
<Typography>{post.text}</Typography>
<Button
variant="outlined"
onClick={() => {
setShowForm(!showForm);
setPostId(post.id);
}}
startIcon={<EditIcon />}
>
Edit
</Button>
</AccordionDetails>
</Accordion>
))}
</Container>
{/* show group of HappyManager */}
<Container style={{ marginTop: "3rem" }}>
{authorGroupValues?.HappyManager.map((post, index) => (
<Accordion key={index}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
id="panel1a-header"
>
<Typography variant="h6" style={{ color: "orange" }}>
{post.id} {post.author} {post.location}
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography variant="h4">{post.location}</Typography>
<Typography>{post.text}</Typography>
<Button
variant="outlined"
onClick={() => {
setShowForm(!showForm);
setPostId(post.id);
}}
startIcon={<EditIcon />}
>
Edit
</Button>
</AccordionDetails>
</Accordion>
))}
</Container>
{/* show group of HappyDeveloper */}
<Container style={{ marginTop: "3rem" }}>
{authorGroupValues?.HappyDeveloper.map((post, index) => (
<Accordion key={index}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
id="panel1a-header"
>
<Typography variant="h6" style={{ color: "green" }}>
{post.id} {post.author} {post.location}
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography variant="h4">{post.location}</Typography>
<Typography>{post.text}</Typography>
<Button
variant="outlined"
onClick={() => {
setShowForm(!showForm);
setPostId(post.id);
}}
startIcon={<EditIcon />}
>
Edit
</Button>
</AccordionDetails>
</Accordion>
))}
</Container>
{/* show group of HappyUser */}
<Container style={{ marginTop: "3rem" }}>
{authorGroupValues?.HappyUser.map((post, index) => (
<Accordion key={index}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
id="panel1a-header"
>
<Typography variant="h6" style={{ color: "red" }}>
{post.id} {post.author} {post.location}
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography variant="h4">{post.location}</Typography>
<Typography>{post.text}</Typography>
<Button
variant="outlined"
onClick={() => {
setShowForm(!showForm);
setPostId(post.id);
}}
startIcon={<EditIcon />}
>
Edit
</Button>
</AccordionDetails>
</Accordion>
))}
</Container>
</Container>
</>
);
};
export default Authorsgroup;
Thanks any suggestions or help are really appreciated.
Hello I am trying to make a drag and drop in my application but I have got a huge error. I have no idea if property is missing because in IDE it is an error free code.
Uncaught Error: Could not find "store" in the context of "Connect(Droppable)". Either wrap the root component in a <Provider>, or pass a custom React context provider to <Provider> and the corresponding React context consumer to Connect(Droppable) in connect options.
Down here are my components
3 Columns ( I want drag and drop across one column each, not between them)
const DetailColumns: React.FC<funcProps> = (props) => {
const onDragEnd = () => {};
return (
<Box
h="80vh"
borderWidth="0.5rem"
borderColor="orange.300"
borderRadius="1rem"
w="80%"
marginTop="2rem"
>
<Grid w="100%" h="100%" templateColumns="60% 20% 20%">
<Box border="0.5rem" borderColor="orange.300">
<RecipeDescriptionBox recipe={props.recipe} />
</Box>
<Box borderLeftWidth="0.5rem" borderColor="orange.300">
<RecipeStepsBox recipe={props.recipe} />
</Box>
<DragDropContext onDragEnd={onDragEnd}>
<Box borderLeftWidth="0.5rem" borderColor="orange.300">
<RecipeIngredientsBox recipe={props.recipe} />
</Box>
</DragDropContext>
</Grid>
</Box>
);
};
List of Items
<Box>
<ColumnHeader title="Steps" />
<Droppable droppableId="unique">
{(provided) => (
<Box {...provided.droppableProps} innerRef={provided.innerRef}>
{steps.map((step, index) => (
<DetailListItem
key={step}
itemName={step}
indexOfItem={index}
id={Math.random().toString()}
/>
))}
{provided.placeholder}
</Box>
)}
</Droppable>
</Box>
Item element
<Draggable draggableId={props.id} index={props.indexOfItem}>
{(provided) => (
<Box
{...provided.draggableProps}
{...provided.dragHandleProps}
innerRef={provided.innerRef}
>
<Flex margin="1rem">
<Box
bgGradient="linear(to-r, orange.200, orange.400)"
height="6vh"
width="6vh"
boxShadow="md"
rounded="md"
>
<Grid w="100%" h="100%" placeItems="center">
<Text color="white" fontWeight="700" fontSize="140%">
{props.amount && props.amount + props.unit}
{!props.amount && props.indexOfItem}
</Text>
</Grid>
</Box>
<Grid placeItems="center">
<Text marginLeft="1rem" fontWeight="500" fontSize="1.8rem">
{props.itemName}
</Text>
</Grid>
</Flex>
</Box>
)}
</Draggable>
If you want I can put this code to some sandbox to make it easier to debug.
I've implemented react beautiful dnd into my app, however I've found that dragging items that are initialised in the first column mostly doesn't work (it sometimes works if you keep refreshing). I tried swapping my columns around and it appears the first one rendered has the problem rather than one column having an issue.
I'm not sure what is causing it, the error I receive in the console from the library is:
Unable to find draggable with id: 6112804b8127dd10b00138d8.
The id however matches the ids in my lists and the HTML viewed in inspect element.
Here's the minimum code, I've not included the functions for filtering search, reordering, move and onDragEnd (isn't being fired as it doesn't find the draggable)
const id2List = {
'available': availableSections,
'selected': selectedSections
};
const getList = id => id2List[id];
return (
<Container>
<Widget title={template.name} paddingStyle="10px" buttonRight={<SaveButton handleSubmit={handleSubmit} loading={loading}/>}>
<DragDropContext onDragEnd={onDragEnd}>
<Row>
<Col >
Available Sections
<Form.Control onChange={(e) => setSearchTextAvailable(e.target.value)} className="mb-3" type="text" placeholder="Search..." />
<Droppable key="available" droppableId="available">
{provided => (
<div ref={provided.innerRef} {...provided.droppableProps}>
{Object.keys(visibleAvailableSections).map((section) => {return <SectionCard key ={visibleAvailableSections[section]._id} section={ visibleAvailableSections[section] } index={parseInt(availableSections.indexOf(visibleAvailableSections[section])) } /> })}
{provided.placeholder}
</div>
)}
</Droppable>
</Col>
<Col>
Selected Sections
<Form.Control onChange={(e) => setSearchTextSelected(e.target.value)} className="mb-3" type="text" placeholder="Search..." />
<Droppable key="selected" droppableId="selected">
{provided => (
<div ref={provided.innerRef} {...provided.droppableProps}>
{Object.keys(visibleSelectedSections).map((section) => {return <SectionCard key ={visibleSelectedSections[section]._id.toString()} section={ visibleSelectedSections[section] } index={parseInt(selectedSections.indexOf(visibleSelectedSections[section])) } /> })}
{provided.placeholder}
</div>
)}
</Droppable>
</Col>
</Row>
</DragDropContext>
</Widget>
</Container>
)
const SectionCard = ({section, index}) => {
return (
<Draggable draggableId={section._id.toString()} key={section._id} index={index}>
{provided => (
<Card key={"card-" + section._id.toString()} style={{ width: '18rem' }}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<Card.Body>
<Card.Title>{ section.name }</Card.Title>
<Card.Subtitle className="text-muted">{ section.description }</Card.Subtitle>
{ section.tags.map((tag) => {
return <Badge pill bg={"secondary"} key={section._id + tag} >{ tag }</Badge>
})}
</Card.Body>
</Card>
)}
</Draggable>
);
}
I am using formik for the forms. In my form, I need to use the concept of FieldArray and it's a nested one. I could not fill the nested forms with respective values. Here is how I have done
const Itinerary = ({ itineraries, push, remove }) => {
return (
<>
<Heading>Itinerary</Heading>
{itineraries && itineraries.length === 0 && (
<span
style={{
color: theme.color.primary,
padding: "0 10px",
cursor: "pointer"
}}
onClick={() => push({})}
>
+
</span>
)}
{itineraries &&
itineraries.length > 0 &&
itineraries.map((day, index) => {
return (
<React.Fragment key={index}>
<Row key={index} style={{ alignItems: "center" }}>
<Text>
How many
<DaysField
component={TextField}
name={`itineraries.${index}.days`}
placeholder="days"
normalize={val => val && parseInt(val)}
/>
of itinerary?
</Text>
{itineraries && itineraries.length - 1 === index && (
<>
<Button>
<Button.Round onClick={() => push({})}>+</Button.Round>
</Button>
<Button>
<Button.Round onClick={() => remove(index)}>
-
</Button.Round>
</Button>
</>
)}
</Row>
<FieldArray
name={`itineraries.${index}.itinerary`}
render={({ push, remove }) => (
<>
<Heading>
Fill up itinerary
{itineraries[index].itinerary &&
itineraries[index].itinerary.length === 0 && (
<span
style={{
color: theme.color.primary,
padding: "0 10px",
cursor: "pointer"
}}
onClick={() => push({})}
>
+
</span>
)}
</Heading>
{itineraries[index].itinerary &&
itineraries[index].itinerary.length > 0 &&
itineraries[index].itinerary.map((i, idx) => {
console.log(
"itinerary index",
itineraries[index].itinerary[idx]
);
return (
<React.Fragment>
<Row
style={{
alignItems: "center",
alignSelf: "center"
}}
>
<Col xs={12} md={3}>
<Field
component={TextField}
placeholder="Day"
name={`${itineraries[index].itinerary[idx]}.day`}
/>
</Col>
<Col xs={12} md={3}>
<Field
component={TextField}
placeholder="Description"
name={`${itineraries[index].itinerary[idx]}.description`}
/>
</Col>
<Col xs={12} md={3}>
<Field
component={TextField}
placeholder="Overnight"
name={`${itineraries[index].itinerary[idx]}.overnight`}
/>
</Col>
<Col xs={12} md={2}>
<Field
component={TextField}
placeholder="Altitude"
name={`${itineraries[index].itinerary}.altitude`}
normalize={value => {
return value && parseFloat(value);
}}
/>
</Col>
<Col xs={12} md={1}>
{itineraries &&
itineraries.length - 1 === index && (
<>
<ActionBtn>
<Button>
<Button.Round
onClick={() => push({})}
>
+
</Button.Round>
</Button>
<Button>
<Button.Round
onClick={() => remove(index)}
color={theme.color.red}
>
-
</Button.Round>
</Button>
</ActionBtn>
</>
)}
</Col>
</Row>
</React.Fragment>
);
})}
</>
)}
/>
</React.Fragment>
);
})}
</>
);
};
The itineraries initialValues looks like this
itineraries: [
{
days: 1,
itinerary: [
{
day: "Day 1",
description: "description",
overnight: "overnight info",
altitude: 150.5
},
{
day: "Day 2",
description: "description 2",
overnight: "overnight info",
altitude: 150.5
}
]
}
]
Only Nested part is not working. Can anyone help me at this, please?
You problem is in how you pass the name for the component:
name={`${itineraries[index].itinerary[idx]}.overnight`}
This way, what you are doing is passing the value of itineraries[index].itinerary[idx] to a string.
What you should do is pass that as a string, not it's value:
name={`itineraries[${index}].itinerary[${idx}].overnight`}
This way, only the indexes get the value printed, which is correct.
Here is the difference
${itineraries[index].itinerary[idx].overnight will return [object Object].overnight because you are access the value of itineraries[index].itinerary[idx] and it's calling .toString().
But itineraries[${index}].itinerary[${idx}].overnight will return itineraries[0].itinerary[0].overnight which is a valid formik string and it can get it's value.