I'm having trouble getting the data and displaying it on the modal interface when editing, usually I will use useState to update the data but is there any way I can pass the data through the variable?.thanks for your help !
view
Here is my code:
this is my index,
I'm trying to get the name and tags back and show the edit form
this is my index
import type { NextPage } from 'next'
import { useEffect, useState } from 'react'
import {
Box,
Button,
Grid,
Icon,
IconButton,
Menu,
MenuItem,
Typography,
} from '#mui/material'
import { MainLayout } from 'components/Layout/MainLayout'
import { HideAppBar } from '../AppBar/HideAppBar'
import { TagProps } from './components/Tag'
import { TraitProps } from './components/Trait'
import { TraitsPickerModal } from './components/TraitsPickerModal'
import { CreateTagCategoryModal } from './components/CreateTagCategoryModal'
import { ALL_TAG, TagCategoryList } from './components/AllTag'
import { Trait } from './components/Trait'
import EditOutlinedIcon from '#mui/icons-material/EditOutlined'
import DeleteOutlinedIcon from '#mui/icons-material/DeleteOutlined'
import { useStaticTagsViewModelSWR } from './hooks/useStaticArtViewModelSWR'
import { TagCategoryEditModal } from './components/Modal/TagCategoryEditModal'
import { TagCategoryDeleteModal } from './components/Modal/TagCategoryDeleteModal'
import { BlackButton } from 'components/Elements/Button/BlackButton'
import { MaterialIconFont } from 'assets/Styles/GlobalStyles'
import {
useStaticSWRTagsListViewModel,
StaticSWRTagstListKey,
} from './hooks/useStaticSWRTagsListViewModel'
import { mutate } from 'swr'
import { TagCategoryListData } from './interface'
const CONTENT_PADDING = '16px'
const dummyTagCategoryList = [
{
name: 'testes',
tagList: [
{
name: 'niceTag',
traitList: [
{
category: 'Clothes',
name: 'Polka',
icon: '/images/test/body4.png',
},
{
category: 'Clothes',
name: 'Dress',
icon: '/images/test/body5.png',
},
],
},
{ name: 'badTag', traitList: [] },
],
},
{
name: 'cat2',
tagList: [
{ name: 'tag1', traitList: [] },
{ name: 'tag2', traitList: [] },
],
},
]
type TaggedListType = TraitProps[][][]
type UntaggedListType = Record<string, TraitProps[]>[]
type TagCategoryListKey = keyof TagCategoryList
const Tags: NextPage = () => {
/**
* state
*/
const [tagCategoryList, setTagCategoryList] = useState<TagProps[]>([])
const [createModalOpen, setCreateModalOpen] = useState(false)
const [traitsPickerOpen, setTraitsPickerOpen] = useState(false)
const [categoryIndex, setCategoryIndex] = useState(0)
const [tagIndex, setTagIndex] = useState(0)
const [taggedList, setTaggedList] = useState<TaggedListType>([])
const [unTaggedList, setUntaggedList] = useState<UntaggedListType>([{}])
const [isVisibleUntaggedList, setIsVisibleUntaggedList] = useState<boolean[]>(
[...new Array(tagCategoryList.length)].map((n) => Boolean(n))
)
const [categoryAnchorElList, setCategoryAnchorElList] = useState(
[...new Array(tagCategoryList.length)].map(() => null)
)
const [categoryAnchorEl, setCategoryAnchorEl] = useState<null | HTMLElement>(
null
)
const [tagCategoryListData, setTagCategoryListData] = useState<
TagCategoryListData[]
>([])
const categoryMenuOpen = Boolean(categoryAnchorEl)
const [idItemTag, setIdItemTag] = useState(0)
/**
* Hook
*/
const { viewModel, setViewModel } = useStaticTagsViewModelSWR()
const { viewModelTagCategory, setViewModelTagCategory } =
useStaticSWRTagsListViewModel()
/**
* useEffect
*/
useEffect(() => {
setTagCategoryList(dummyTagCategoryList)
}, [])
useEffect(() => {
mutate(StaticSWRTagstListKey)
}, [])
useEffect(() => {
setTagCategoryListData(viewModelTagCategory)
}, [viewModelTagCategory])
useEffect(() => {
setTaggedList((list) =>
Object.assign(
tagCategoryList.map((category) => {
return category.tagList.reduce((memo, tag) => {
return tag.traitList
? [...memo, tag.traitList.map((trait) => trait)]
: memo
}, [])
}),
list
)
)
setIsVisibleUntaggedList((list) => [...list, false])
}, [tagCategoryList])
useEffect(() => {
const categoryUsedTraitList = taggedList.reduce((memo, list) => {
return [...memo, list.flat(2)]
}, [])
setUntaggedList(
categoryUsedTraitList.map((list) => {
return Object.keys(ALL_TAG).reduce((memo, key: TagCategoryListKey) => {
memo[key] = ALL_TAG[key].filter((trait) => {
return list.every((item) => {
return item.name !== trait.name
})
})
return memo
}, {} as TagCategoryList)
})
)
}, [taggedList])
/**
* Handler
*/
const onClickTrait = (pickedList: TraitProps[]) => {
setTaggedList((list) => {
list[categoryIndex][tagIndex] = pickedList
return JSON.parse(JSON.stringify(list))
})
}
const onClickToggleUntaggedComponentsBtn = (index: number) => {
setIsVisibleUntaggedList((list) => {
list[index] = !list[index]
return [...list]
})
}
const addTagCategory = (tagCategory: TagCategoryListData) => {
setViewModelTagCategory([...viewModelTagCategory, tagCategory])
}
const menuClick = (
event: React.MouseEvent<HTMLElement>,
idItemTag: number
) => {
setIdItemTag(idItemTag)
setCategoryAnchorEl(event.currentTarget)
}
const menuClose = () => {
setCategoryAnchorEl(null)
}
const openTagCategoryEditModal = (idItemTag: number) => {
const item = viewModelTagCategory.find((tagId) => tagId.id === idItemTag)
console.log(item?.name)
const itemTags = item?.tagList.map((tag) => {
return {
id: tag.id,
name: tag.name,
}
})
console.log(itemTags)
setViewModel({ ...viewModel, tagCategoryEditModal: { isOpen: true } })
setCategoryAnchorEl(null)
}
const openTagCategoryDeleteModal = (idItemTag: number) => {
console.log(idItemTag)
setViewModel({ ...viewModel, tagCategoryDeleteModal: { isOpen: true } })
setCategoryAnchorEl(null)
}
return (
<MainLayout>
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
<HideAppBar tabNum={2} />
<MaterialIconFont />
<TagCategoryEditModal />
<TagCategoryDeleteModal />
<Grid
container
sx={{
position: 'relative',
display: 'block',
textAlign: 'left',
padding: CONTENT_PADDING,
}}
>
<Typography
variant="h5"
style={{
textAlign: 'left',
fontWeight: 'bold',
}}
>
Tags
</Typography>
<Box sx={{ display: 'flex' }}></Box>
<Typography
variant="h6"
style={{ color: '#888888', textAlign: 'left' }}
>
Use tags to organize and group your token traits.
</Typography>
<BlackButton
onClick={() => setCreateModalOpen(true)}
sx={{
position: 'absolute',
right: CONTENT_PADDING,
top: CONTENT_PADDING,
paddingRight: '16px',
fontWeight: 'bold',
fontSize: '0.9rem',
}}
>
<Icon sx={{ marginRight: '8px' }}>add</Icon>New Tag Category
</BlackButton>
{viewModelTagCategory && (
<Box>
{viewModelTagCategory.map((tagCategory, i) => (
<Box key={tagCategory.name + i}>
<Box className="category-box" sx={{ marginTop: '16px' }}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Typography
variant="subtitle1"
sx={{ fontWeight: 'bold' }}
>
{tagCategory.name}_{tagCategory.id}
</Typography>
<IconButton
aria-label="more"
id={`tag-category-menu-btn-${tagCategory.id}`}
aria-controls={
categoryMenuOpen
? `tag-category-menu-${tagCategory.id}`
: undefined
}
aria-expanded={categoryMenuOpen ? 'true' : undefined}
aria-haspopup="true"
data-category-num={tagCategory.id}
onClick={(e) => menuClick(e, tagCategory.id)}
>
<Icon>more_vert</Icon>
</IconButton>
<Menu
id={`tag-category-menu-${tagCategory.id}`}
MenuListProps={{
'aria-labelledby': `tag-category-menu-btn-${tagCategory.id}`,
}}
anchorEl={categoryAnchorEl}
open={categoryMenuOpen}
onClose={menuClose}
>
<MenuItem
onClick={() => openTagCategoryEditModal(idItemTag)}
>
<EditOutlinedIcon sx={{ mr: 1 }} />
<Typography>Edit</Typography>
</MenuItem>
<MenuItem
onClick={() => openTagCategoryDeleteModal(idItemTag)}
sx={{ color: 'red' }}
>
<DeleteOutlinedIcon sx={{ mr: 1 }} />
<Typography>Delete</Typography>
</MenuItem>
</Menu>
</Box>
{tagCategory.tagList.length > 0 &&
tagCategory.tagList.map((tag, j) => (
<Box key={j}>
<Box sx={{ display: 'flex', marginTop: '32px' }}>
<Box
sx={{
display: 'flex',
alignItems: 'center',
background: '#283237',
color: '#ffffff',
padding: '0 0 0 12px',
borderRadius: '20px',
fontWeight: 'bold',
marginLeft: '8px',
':first-of-type': {
marginLeft: 0,
},
}}
>
{tag.name}
<IconButton
size="small"
sx={{ color: '#ffffff' }}
>
<Icon fontSize="small">close</Icon>
</IconButton>
</Box>
<Button
sx={{
display: 'flex',
alignItems: 'center',
background: '#ffffff',
color: '#000000',
padding: '0 12px',
borderRadius: '20px',
fontWeight: 'bold',
border: '#aaaaaa 1px solid',
marginLeft: '8px',
textTransform: 'none',
':hover': {
borderColor: '#1976d2',
boxShadow: '0px 0px 0px 1px #1976d2 inset',
},
':only-child': {
marginLeft: 0,
},
}}
onClick={() => {
setCategoryIndex(i)
setTagIndex(j)
setTraitsPickerOpen(true)
}}
>
Add Traits
</Button>
</Box>
{taggedList[i] && (
<Box
sx={{
display: 'flex',
flexWrap: 'wrap',
marginTop: '8px',
width: '1000px',
}}
>
{taggedList[i][j].map((trait, k) => (
<Trait key={k} {...trait} />
))}
</Box>
)}
</Box>
))}
</Box>
<Button
onClick={() => onClickToggleUntaggedComponentsBtn(i)}
sx={{
fontSize: '0.85rem',
marginTop: '40px',
color: '#005eff',
textTransform: 'none',
}}
>
{isVisibleUntaggedList[i] ? 'Hide' : 'Show'} untagged
components
</Button>
{isVisibleUntaggedList[i] && (
<Box sx={{ width: '1000px', maxWidth: '100vw' }}>
{unTaggedList[i] &&
Object.keys(unTaggedList[i]).map((key, k) => (
<Box key={k}>
{unTaggedList[i][key].length > 0 && (
<>
<Box sx={{ fontWeight: 'bold' }}>{key}</Box>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',
}}
>
{unTaggedList[i][key].map((trait, l) => (
<Trait key={`${k}_${l}`} {...trait} />
))}
</Box>
</>
)}
</Box>
))}
</Box>
)}
</Box>
))}
</Box>
)}
<CreateTagCategoryModal
open={createModalOpen}
setOpen={setCreateModalOpen}
onCreate={addTagCategory}
/>
<TraitsPickerModal
title={
tagCategoryList[categoryIndex]
? `${tagCategoryList[categoryIndex].tagList[tagIndex].name} Traits`
: ''
}
open={traitsPickerOpen}
setOpen={setTraitsPickerOpen}
onPick={onClickTrait}
traitList={
taggedList[categoryIndex]
? taggedList[categoryIndex][tagIndex]
: []
}
/>
</Grid>
</Box>
</MainLayout>
)
}
export default Tags
My modalEdit
import * as React from 'react'
import {
Box,
Button,
Modal,
Typography,
IconButton,
TextField,
FormControlLabel,
Switch,
} from '#mui/material'
import { ModalBox } from 'components/Elements/Box/ModalBox'
import { useStaticTagsViewModelSWR } from '../../hooks/useStaticArtViewModelSWR'
import CloseIcon from '#mui/icons-material/Close'
import AddCircleOutlineIcon from '#mui/icons-material/AddCircleOutline'
import DeleteOutlineOutlinedIcon from '#mui/icons-material/DeleteOutlineOutlined'
export const TagCategoryEditModal = () => {
const { viewModel, setViewModel } = useStaticTagsViewModelSWR()
const [includeMetadataFlag, setIncludeMetadataFlag] =
React.useState<boolean>(true)
const modalClose = () => {
setViewModel({ ...viewModel, tagCategoryEditModal: { isOpen: false } })
}
return (
<Modal
open={viewModel.tagCategoryEditModal.isOpen}
onClose={modalClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<>
<ModalBox
style={{ display: 'flex', flexDirection: 'column', height: 450 }}
>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
mb: 2,
}}
>
<Typography variant="h4" sx={{ fontWeight: 'bold' }} gutterBottom>
Edit Tag Category
</Typography>
<IconButton onClick={modalClose}>
<CloseIcon />
</IconButton>
</Box>
<Box sx={{ mb: 3 }}>
<Typography sx={{ mb: 1 }}>Name</Typography>
<TextField sx={{ width: '100%' }} />
</Box>
<Box sx={{ mb: 3 }}>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
}}
>
<Typography>Tags</Typography>
<IconButton>
<AddCircleOutlineIcon />
</IconButton>
</Box>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
}}
>
<TextField sx={{ width: '100%', mr: 2 }} />
<IconButton>
<DeleteOutlineOutlinedIcon />
</IconButton>
</Box>
</Box>
<Box sx={{ mb: 2 }}>
<FormControlLabel
sx={{ ml: 0 }}
value="start"
control={
<Switch
color="primary"
checked={includeMetadataFlag}
onChange={() => setIncludeMetadataFlag(!includeMetadataFlag)}
/>
}
label="Include in metadata?"
labelPlacement="start"
/>
</Box>
<Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>
<Button
variant="outlined"
sx={{ color: 'black', borderColor: '#d1d8db', marginRight: 1 }}
onClick={modalClose}
>
Cancel
</Button>
<Button variant="contained">Save</Button>
</Box>
</ModalBox>
</>
</Modal>
)
}
Related
in the following code, how do I define itemData as post.images[i]?
import clientPromise from "../lib/mongodb";
import Box from '#mui/material/Box';
import Avatar from '#mui/material/Avatar';
import IconButton from '#mui/material/IconButton';
import ChatBubbleOutlineRoundedIcon from '#mui/icons-material/ChatBubbleOutlineRounded';
import FavoriteBorderRoundedIcon from '#mui/icons-material/FavoriteBorderRounded';
import ShareRoundedIcon from '#mui/icons-material/ShareRounded';
import Button from '#mui/material/Button';
import ImageList from '#mui/material/ImageList';
import ImageListItem from '#mui/material/ImageListItem';
export default function Posts({ posts }) {
return (
<div>
<ul>
<div style={{ width: '100%' }}>
<Box sx={{ display: 'flex', justifyContent: 'center'}}>
<div style={{ width: '75%' }}>
{posts.map((post) => (
<li style={{ border: '1px solid grey', borderRadius: '12px', margin: "10px", padding: "10px" }}>
<Box sx={{ display: 'flex', justifyContent: 'start', alignItems: 'center'}}>
<Avatar alt={post.userName} src={post.userAvatar}/>
<h4 style={{ paddingLeft:'20px', paddingRight: '20px' }}>{post.userName}</h4>
<p style={{ color: 'grey', fontSize: '12px' }}>{post.timestamp}</p>
</Box>
<Box>
<p style={{ display: 'block', width: '100%', margin: '0'}}>{post.text}</p>
</Box>
<Box sx={{ my: 1, display: 'flex', justifyContent: 'end', alignItems: 'center'}}>
<IconButton><ChatBubbleOutlineRoundedIcon/></IconButton>
<IconButton><FavoriteBorderRoundedIcon/></IconButton>
<IconButton><ShareRoundedIcon/></IconButton>
</Box>
<Box><Button href={post.link} variant="outlined">Link</Button></Box>
<Box>
<ImageList sx={{ width: 500, height: 450 }} cols={3} rowHeight={164}>
{itemData.map((item) => (
<ImageListItem key={item.img}>
<img
src={`${item.img}?w=164&h=164&fit=crop&auto=format`}
srcSet={`${item.img}?w=164&h=164&fit=crop&auto=format&dpr=2 2x`}
alt={item.title}
loading="lazy"
/>
</ImageListItem>
))}
</ImageList>
</Box>
</li>
))}
</div>
</Box>
</div>
</ul>
</div>
)
}
const itemData = [
{
img: 'https://images.unsplash.com/photo-1523217582562-09d0def993a6',
title: 'House',
},
{
img: 'https://images.unsplash.com/photo-1615874694520-474822394e73',
title: 'Living Room',
},
{
img: 'https://images.unsplash.com/photo-1616594039964-ae9021a400a0',
title: 'Bedroom',
},
{
img: 'https://images.unsplash.com/photo-1616486886892-ff366aa67ba4',
title: 'Dining Room',
},
{
img: 'https://images.unsplash.com/photo-1521783593447-5702b9bfd267',
title: 'Bathroom',
}
];
export async function getServerSideProps() {
try {
const client = await clientPromise;
const db = client.db("abode-app");
const posts = await db
.collection("posts")
.find({})
.sort({ metacritic: -1 })
.limit(20)
.toArray();
return {
props: { posts: JSON.parse(JSON.stringify(posts)) },
};
} catch (e) {
console.error(e);
}
}
I have tried loading the images one by one and they work, but prefer to loop over them.
I am mapping over an array and rendering a custom card component for each index in the array. However, I am receiving the error "Each child in a list should have a unique "key" prop" 1. Although, I am passing the index as the key. I have tried with a React.fragment and passing the index down to the card component and adding the key there. Both methods are still throwing the same error.
Main Component
import React from "react";
import { useRouter } from "next/router";
import { Button, Container } from "#mui/material";
import { makeStyles } from "#mui/styles";
import { InstructionsCard } from "../layout/directory";
import {
RiNumber1 as OneIcon,
RiNumber2 as TwoIcon,
RiNumber3 as ThreeIcon,
} from "react-icons/ri";
function InstructionSection() {
const router = useRouter();
const classes = useStyles();
const instructions = [
{
id: 1,
icon: OneIcon,
title: "step one",
text: [
"Navigate to the",
<Button
onClick={() => router.push("/requirements")}
size="small"
style={{ margin: "5px" }}
variant="outlined"
color="inherit"
>
requirements
</Button>,
"page for our most frequently asked questions and specific requirements before booking any activity. ",
],
},
{
id: 2,
icon: TwoIcon,
title: "step two",
text: [
"Find the activity you are interested in and read through the information carefully. Be sure to fully understand the,",
<Button
onClick={() => router.push("/#upgrades")}
size="small"
style={{ margin: "5px" }}
variant="outlined"
color="inherit"
>
entry fee
</Button>,
" and",
<Button
onClick={() => router.push("/#upgrades")}
size="small"
style={{ margin: "5px" }}
variant="outlined"
color="inherit"
>
upgrade
</Button>,
" packages",
],
},
{
id: 3,
icon: ThreeIcon,
title: "step three",
text: [
"Please, be sure to verify we are ",
<Button
onClick={() => router.push("/locations")}
size="small"
style={{ margin: "5px" }}
variant="outlined"
color="inherit"
>
located
</Button>,
" in your area. Select an experience, date, time-slot, toggle any upgrades, and continue through checkout.",
],
},
];
return (
<Container className={classes.root}>
{/* instructions iteration */}
{instructions.map((_instruction, index) => {
return (
<React.Fragment key={index}>
<InstructionsCard item={_instruction} />
</React.Fragment>
);
})}
</Container>
);
}
// custom styles
const useStyles = makeStyles((theme) => ({
root: {
[theme.breakpoints.down("md")]: {
flexDirection: "column",
},
width: "100%",
display: "flex",
justifyContent: "space-evenly",
},
}));
export default InstructionSection;
Card Component
import { makeStyles } from "#mui/styles";
import { Card, CardContent, Typography, Divider } from "#mui/material";
const InstructionsCard = ({ item }) => {
const classes = useStyles();
const Icon = item.icon;
return (
<Card className={classes.root}>
<CardContent>
<Icon className={classes.icon} />
<Typography className={classes.title} variant="h5" component="h6">
{item.title.toUpperCase()}
</Typography>
<Divider className={classes.divider} />
<Typography
variant="subtitle1"
component="p"
sx={{ mb: 1.5 }}
color="text.secondary"
>
{item.text}
</Typography>
</CardContent>
</Card>
);
};
const useStyles = makeStyles((theme) => ({
root: {
[theme.breakpoints.down("md")]: {
margin: theme.spacing(4, 0, 4, 0),
},
background: theme.palette.primary.main,
borderRadius: theme.spacing(5),
padding: theme.spacing(2),
margin: theme.spacing(5),
width: "100%",
textAlign: "center",
boxShadow: `0px 0px 10px 10px ${theme.palette.offset.main}`,
},
icon: {
background: theme.palette.secondary.dark,
width: "50px",
height: "50px",
padding: "15px",
borderRadius: theme.spacing(5),
},
divider: {
background: theme.palette.secondary.dark,
padding: "2px",
width: "20%",
margin: theme.spacing(1, "auto", 1, "auto"),
},
title: {
fontWeight: 800,
},
}));
export default InstructionsCard;
Change like this in your main component
React.Fragment we need to use One time it can't use multiple time
return (
<Container className={classes.root}>
<React.Fragment>
{instructions.map((_instruction, index) => {
<InstructionsCard key={index} item={_instruction} />;
})}
</React.Fragment>
</Container>
);
Thank You
I wish to remove all the styles from my main component.
All the JS style of the material UI is on the same file and it is starting to get long code.
I wish to create a new file that contains all the styles on other file and on Navbar.js I will just call the components, just same as CSS file that I call to class
import styled from "#emotion/styled";
import { Notifications, Pets } from "#mui/icons-material";
import {
AppBar,
Avatar,
Badge,
Box,
InputBase,
Menu,
MenuItem,
Toolbar,
Typography,
} from "#mui/material";
import MailIcon from "#mui/icons-material/Mail";
import React, { useState } from "react";
const StyledToolbar = styled(Toolbar)({
display: "flex",
justifyContent: "space-between",
});
const Search = styled("div")(({ theme }) => ({
backgroundColor: "white",
padding: "0 10px",
borderRadius: theme.shape.borderRadius,
width: "40%",
}));
const Icons = styled(Box)(({ theme }) => ({
display: "none",
gap: "20px",
alignItems: "center",
[theme.breakpoints.up("sm")]: {
display: "flex",
},
}));
const UserBox = styled(Box)(({ theme }) => ({
display: "flex",
gap: "10px",
alignItems: "center",
[theme.breakpoints.up("sm")]: {
display: "none",
},
}));
const Navbar = () => {
const [open, setOpen] = useState(false);
return (
<AppBar position="sticky">
<StyledToolbar>
<Typography variant="h6" sx={{ display: { xs: "none", sm: "block" } }}>
PALSAM
</Typography>
<Pets sx={{ display: { xs: "block", sm: "none" } }} />
<Search>
<InputBase placeholder="search..." />
</Search>
<Icons>
<Badge badgeContent={4} color="error">
<MailIcon />
</Badge>
<Badge badgeContent={4} color="error">
<Notifications />
</Badge>
<Avatar
sx={{ width: 30, height: 30 }}
src="https://images.pexels.com/photos/846741/pexels-photo-846741.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"
onClick={(e) => setOpen(true)}
/>
</Icons>
<UserBox onClick={(e) => setOpen(true)}>
<Avatar
sx={{ width: 30, height: 30 }}
src="https://images.pexels.com/photos/846741/pexels-photo-846741.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"
/>
<Typography variant="span">Jhon</Typography>
</UserBox>
</StyledToolbar>
<Menu
id="demo-positioned-menu"
aria-labelledby="demo-positioned-button"
open={open}
onClose={(e) => setOpen(false)}
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "right",
}}
>
<MenuItem>Profile</MenuItem>
<MenuItem>My account</MenuItem>
<MenuItem>Logout</MenuItem>
</Menu>
</AppBar>
);
};
export default Navbar;
You can create a new js file and add export before each const then you can import them from that file
export const StyledToolbar = styled(Toolbar)({
display: "flex",
justifyContent: "space-between",
});
I am attempting to have a single user be able to choose from 1 of 2 options but when I like one they are all liked for some reason.
This is the result. I have an onClick function on the heart so that it adds the red.
https://i.stack.imgur.com/r73UB.png
Below is the code that runs when I click on the heart. I basically set the usersID into a collection called likes and save thier username
const [likes, setLikes] = useState([])
const [hasLiked, setHasLiked] = useState(false)
const likeFighter = async (e) => {
const docRef = doc(db, 'userpicks', value)
const docSnap = await getDoc(docRef)
const picksRef = doc(db, 'picks', value)
const pickSnap = await getDoc(picksRef)
let fighterID = e.target.id.toLowerCase().replace(' ', '-').replace(' ', '-')
if (hasLiked) {
await deleteDoc(doc(db, 'picks', value, 'likes', userInfo.uid))
}
else {
if (fighterID !== ' ') {
await setDoc(doc(db, 'picks', value, 'likes', userInfo.uid), {
username: userInfo.uid
})
}
}
}
This is what the database looks like. The main collection is called 'picks'. The parent collection that is.
https://i.stack.imgur.com/JVrk3.png
I also have these useEffects with snapshot listeners that run every-time a user likes a particular fighter. How can I have it so that they are only able to like one of the two fighters? If they like the left fighter they cannot like the right and vice versa.
useEffect(() => onSnapshot(collection(db, 'picks', value, 'likes'), (snapshot) =>
setLikes(snapshot.docs)
), [db, value])
useEffect(
() =>
setHasLiked(
likes.findIndex((like) => like.id === userInfo.uid) !== -1
),
[likes]
)
This is the logic that gets the fighter information from the database and renders it on the screen. I tried using the fighters name as an id so that on click it likes that particular fighter but It doesn't work.
const renderFights = fight.map((element, idx) => {
return (
<Paper elevation={0} id={element.fightID} key={idx} sx={{ width: '100%', height: '100%', display: 'flex', justifyContent: 'space-evenly' }} >
<Paper elevation={1} key={element.fighterRed} sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: '50%', m: 3 }}>
<Avatar sx={{ mt: 1 }} src={element.fighterRedImage} />
<Typography variant='body1'> {element.fighterRed} </Typography>
<Typography variant='overline' sx={{ fontSize: '14px' }}> {element.fighterRedRecord} </Typography>
<Paper sx={{ display: 'flex' }} elevation={0} name={element.fightID}>
{hasLiked ?
<FavoriteIcon id={element.fighterRed} onClick={likeFighter} sx={{ cursor: 'pointer', color: 'red' }} /> :
<FavoriteBorder id={element.fighterRed} onClick={likeFighter} sx={{ cursor: 'pointer' }} />
}
{likes.length > 0 && (
<Typography>{likes.length}</Typography>)
}
</Paper>
</Paper>
<Paper elevation={0} sx={{ width: '10%', display: 'flex', flexDirection: 'column', mt: 3, mb: 3, justifyContent: 'space-evenly', alignItems: 'center' }} >
<Typography sx={{ fontSize: '10px', textAlign: 'center' }} variant='overline'> {element.weightClass}</Typography>
<Typography sx={{ fontSize: '10px', textAlign: 'center' }} variant='overline'>vs</Typography>
<Chip label="FINAL U DEC" sx={{ fontSize: '8px' }} />
</Paper>
<Paper elevation={1} key={element.fighterBlue} sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: '50%', m: 3 }}>
<Avatar sx={{ mt: 1 }} src={element.fighterBlueImage} />
<Typography variant='body1'> {element.fighterBlue} </Typography>
<Typography variant='overline' sx={{ fontSize: '14px' }}> {element.fighterBlueRecord} </Typography>
<Paper sx={{ display: 'flex' }} elevation={0}>
{hasLiked ?
<FavoriteIcon id={element.fighterBlue} onClick={likeFighter} sx={{ cursor: 'pointer', color: 'red' }} /> :
<FavoriteBorder id={element.fighterBlue} onClick={likeFighter} sx={{ cursor: 'pointer' }} />
}
{likes.length > 0 && (
<Typography>{likes.length}</Typography>)
}
</Paper>
</Paper>
</Paper>
)
})
I have created a formik form that gets its initial values from the backend database through API calls. Got below error:
Type 'undefined' is not assignable to type 'FormikValues'. TS2322
Not able to resolve the error.
code:
// #ts-ignore
import React, { useState, useEffect } from 'react';
import { Formik, Form, Field } from 'formik';
import {
Typography,
Button,
Grid,
CircularProgress,
Divider,
} from '#material-ui/core';
import * as Yup from 'yup';
import { MyInput } from './comman/MyInput';
import axios from 'axios'
import ThumbUpAltIcon from '#material-ui/icons/ThumbUpAlt';
import Unsubscribed from './Unsubscribed';
const contactSchema = Yup.object().shape({
});
export default function SurveyUnsubscribed(props: any) {
const [isSubmitted, setIsSubmitted] = useState(false);
//const [color, setColor] = useState(true);
const [loading, setLoading] = useState(false);
const [count, setCount] = useState(0);
const [countone, setCountOne] = useState(0);
const [counttwo, setCountTwo] = useState(0);
const [countthree, setCountThree] = useState(0);
const [initialValues, setInitialValues] = useState();
const [state, setState] = useState({
msg: '',
});
let initial:any;
async function getInitialValues() {
try{
const response = await axios.get(`${process.env.REACT_APP_LOCALHOST_DEVELOPMENT_VOTING_API_GET}`)
return response.data;
}catch(error){
console.error(error)
}
}
useEffect(() => {
getInitialValues().then((res:any) => setInitialValues(res));
}, []);
const handleClickCount = () => {
setCount(count + 1);
// setColor(false);
};
const handleClickCountTwo = () => {
setCountTwo(counttwo + 1);
// setColor(false);
}; const handleClickCountThree = () => {
setCountThree(countthree + 1);
// setColor(false);
}; const handleClickCountOne = () => {
setCountOne(countone + 1);
// setColor(false);
};
return (
<React.Fragment>
<Formik
enableReinitialize
initialValues={initialValues}
// count: initial,
// countone: countone,
// counttwo: counttwo,
// countthree: countthree,
// helptext: '',
validationSchema={contactSchema}
onSubmit={(values, { resetForm }) => {
setLoading(true);
console.log(initial)
const data = {
count: values.count,
countone: values.countone,
counttwo: values.counttwo,
countthree: values.countthree,
helptext: values.helptext,
};
console.log(data);
const request = new Request(
`${process.env.REACT_APP_LOCALHOST_DEVELOPMENT_VOTING_API}`,
{
method: 'POST',
headers: new Headers({
'Content-Type': 'application/json',
}),
body: JSON.stringify(data),
},
);
fetch(request)
.then((res) => res.json())
.then((data) => {
if (data.message === 'Thank you for your feedback!') {
setState({
msg: data.message,
});
setIsSubmitted(true);
setTimeout(() => {
setLoading(false);
}, 1500);
} else {
console.log('error');
}
});
setTimeout(() => {
setIsSubmitted(false);
}, 1500);
resetForm();
}}
>
{({ setFieldValue }) => (
<Grid container>
<Grid
item
xs={12}
style={{ paddingLeft: '2em', paddingRight: '2em' }}
>
<Typography
variant="h6"
style={{
marginTop: '1em',
color: '#2F4858',
fontWeight: 'bold',
}}
>
Voting survey </Typography>
<br />
{isSubmitted}
<Form>
<Typography
variant="body1"
style={{
display: 'flex',
alignItems: 'center',
marginBottom: '1em',
}}
>
<Field type="hidden" name="count" component={ThumbUpAltIcon} onClick={handleClickCount} style={{ color: '#C4C4C4', marginRight: '0.4em' }}/>
<Typography
variant="caption"
style={{
position: 'relative',
top: '1.5em',
right: '1.5em',
}}
>
{count}
</Typography>
A </Typography>
<Typography
variant="body1"
style={{
display: 'flex',
alignItems: 'center',
marginBottom: '1em',
}}
>
<Field type="hidden" name="countone" component={ThumbUpAltIcon} onClick={handleClickCountOne} style={{ color: '#C4C4C4', marginRight: '0.4em' }}/>
<Typography
variant="caption"
style={{
position: 'relative',
top: '1.5em',
right: '1.5em',
}}
>
{countone}
</Typography>
B </Typography>
<Typography
variant="body1"
style={{
display: 'flex',
alignItems: 'center',
marginBottom: '1em',
}}
>
<Field type="hidden" name="counttwo" component={ThumbUpAltIcon} onClick={handleClickCountTwo} style={{ color: '#C4C4C4', marginRight: '0.4em' }}/>
<Typography
variant="caption"
style={{
position: 'relative',
top: '1.5em',
right: '1.5em',
}}
>
{counttwo}
</Typography>
C </Typography>
<Typography
variant="body1"
style={{
display: 'flex',
alignItems: 'center',
marginBottom: '1em',
}}
>
<Field type="hidden" name="countthree" component={ThumbUpAltIcon} onClick={handleClickCountThree} style={{ color: '#C4C4C4', marginRight: '0.4em' }}
/>
<Typography
variant="caption"
style={{
position: 'relative',
top: '1.5em',
right: '1.5em',
}}
>
{countthree}
</Typography>
D
</Typography>
<br />
<Typography
variant="h6"
style={{
marginLeft: '2em',
marginTop: '0.5em',
}}
>
What information would help you ?
</Typography>
<Field
id="outlined-multiline-flexible"
type="text"
name="helptext"
component={MyInput}
disabled={isSubmitted}
style={{ marginLeft: '2em' }}
/>
<br />
<br />
<Button
type="submit"
variant="contained"
style={{
background: '#2F4858',
color: 'white',
fontFamily: 'roboto',
fontSize: '1rem',
marginLeft: '2em',
marginBottom: '1em',
}}
>
{loading && (
<CircularProgress
size={25}
color="inherit"
style={{ marginRight: '5px' }}
/>
)}
{loading && <span>submitting</span>}
{!loading && <span>Submit</span>}
</Button>
<br />
{state.msg && (
<Typography
variant="h6"
style={{
color: '#4BB543',
fontFamily: 'roboto-medium',
marginTop: '1em',
}}
>
{' '}
{state.msg}{' '}
</Typography>
)}
</Form>
<Divider
style={{
border: '1px solid #97A1A8',
marginTop: '1em',
marginBottom: '2em',
}}
/>
<Unsubscribed />
</Grid>
</Grid>
)}
</Formik>
</React.Fragment>
);
}
please guys help me with setting formik initial values from the database(Postgresql)
Because you create state initialValues with default value is undefined. So just update default value to an empty object
const [initialValues, setInitialValues] = useState({});