added apollo client & cache on create todo

This commit is contained in:
Sergey Yarkov 2020-06-14 15:11:12 +03:00
parent 437b18536d
commit 02ad7d86de
12 changed files with 205 additions and 77 deletions

View File

@ -7,20 +7,30 @@ import TrashContainer from './containers/TrashContainer/TrashContainer'
import NotFoundContainer from './containers/NotFoundContainer/NotFoundContainer' import NotFoundContainer from './containers/NotFoundContainer/NotFoundContainer'
import CssBaseLine from '@material-ui/core/CssBaseline' import CssBaseLine from '@material-ui/core/CssBaseline'
import ApolloClient from 'apollo-boost'
import { ApolloProvider } from 'react-apollo'
const client = new ApolloClient({
uri: 'http://localhost:3001'
})
const App = ({ history }) => { const App = ({ history }) => {
return ( return (
<> <ApolloProvider client={client}>
<CssBaseLine /> <>
<Layout history={history}> <CssBaseLine />
<Switch> <Layout history={history}>
<Route history={history} exact path='/category/:id' component={CategoryContainer} /> <Switch>
<Route history={history} exact path='/trash' component={TrashContainer} /> <Route history={history} exact path='/category/:id' component={CategoryContainer} />
<Route history={history} exact path='/' component={TodosContainer} /> <Route history={history} exact path='/trash' component={TrashContainer} />
<Route path='/404' component={NotFoundContainer} /> <Route history={history} exact path='/' component={TodosContainer} />
<Redirect from='*' to='/404' /> <Route path='/404' component={NotFoundContainer} />
</Switch> <Redirect from='*' to='/404' />
</Layout> </Switch>
</> </Layout>
</>
</ApolloProvider>
) )
} }

View File

@ -0,0 +1,18 @@
import gql from 'graphql-tag'
const CREATE_TODO = gql`
mutation CreateTodo($title: String! $description: String! $deadline: String! $categoryId: String!) {
createTodo(title: $title description: $description deadline: $deadline categoryId: $categoryId) {
id
title
description
status
deadline
category {
title
}
}
}
`
export default CREATE_TODO

View File

@ -0,0 +1,12 @@
import gql from 'graphql-tag'
const GET_CATEGORIES = gql`
{
categories {
id
title
}
}
`
export default GET_CATEGORIES

View File

@ -0,0 +1,18 @@
import gql from 'graphql-tag'
const GET_TODOS = gql`
query getTodos {
todos {
id
title
description
status
deadline
category {
title
}
}
}
`;
export default GET_TODOS

View File

@ -0,0 +1,22 @@
import gql from 'graphql-tag'
const GET_TODOS_BY_CATEGORY = gql`
query getTodosByCategory($id: ID!) {
category(id: $id) {
id
title
todos {
id
title
description
status
deadline
category {
title
}
}
}
}
`
export default GET_TODOS_BY_CATEGORY

View File

@ -5,8 +5,8 @@ import { ListItem, ListItemText } from '@material-ui/core'
const Categories = ({ data, history, classes }) => { const Categories = ({ data, history, classes }) => {
return data.map((category, i) => { return data.map((category, i) => {
return ( return (
<Link className={classes.link} key={i} to={`/category/${category._id.$oid}`}> <Link className={classes.link} key={i} to={`/category/${category.id}`}>
<ListItem selected={history.location.pathname === `/category/${category._id.$oid}`} button className={classes.nested}> <ListItem selected={history.location.pathname === `/category/${category.id}`} button className={classes.nested}>
<ListItemText primary={category.title} /> <ListItemText primary={category.title} />
</ListItem> </ListItem>
</Link> </Link>

View File

@ -3,7 +3,11 @@ import useStyles from '../styles'
import { Dialog, DialogContent, DialogTitle, DialogActions, Button, TextField, Select, MenuItem, InputLabel, FormControl } from '@material-ui/core' import { Dialog, DialogContent, DialogTitle, DialogActions, Button, TextField, Select, MenuItem, InputLabel, FormControl } from '@material-ui/core'
import AddIcon from '@material-ui/icons/Add' import AddIcon from '@material-ui/icons/Add'
import categories from '../../../db/categories.json' import { useQuery, useMutation } from '@apollo/react-hooks'
import CREATE_TODO from '../../../apollo/mutations/todos/createTodo';
import GET_TODOS from '../../../apollo/queries/todos/todos';
import GET_CATEGORIES from '../../../apollo/queries/categories/categories'
const CreateTodoModal = ({ isModalOpen, handleCloseModal }) => { const CreateTodoModal = ({ isModalOpen, handleCloseModal }) => {
const classes = useStyles() const classes = useStyles()
@ -14,23 +18,39 @@ const CreateTodoModal = ({ isModalOpen, handleCloseModal }) => {
deadline: '' deadline: ''
}) })
const handleFormSubmit = e => { // create todo mutation
e.preventDefault() const [createTodo] = useMutation(CREATE_TODO, {
const elements = e.target.elements update(cache, { data: { createTodo } }) {
const data = { const { todos } = cache.readQuery({ query: GET_TODOS })
title: elements.title.value, cache.writeQuery({
description: elements.description.value, query: GET_TODOS,
categoryId: elements.category.value, data: { todos: todos.concat([createTodo]) }
deadline: elements.deadline.value });
}
})
const handleFormSubmit = e => {
try {
e.preventDefault()
const elements = e.target.elements
createTodo({
variables: {
title: elements.title.value,
description: elements.description.value,
categoryId: elements.category.value,
deadline: elements.deadline.value
}
})
handleCloseModal()
setFieldsData({
title: '',
description: '',
category: '',
deadline: ''
})
} catch (error) {
console.log(error)
} }
handleCloseModal()
setFieldsData({
title: '',
description: '',
category: '',
deadline: ''
})
console.log('Request on create todo:', data)
} }
const handleFieldChange = e => { const handleFieldChange = e => {
@ -43,6 +63,11 @@ const CreateTodoModal = ({ isModalOpen, handleCloseModal }) => {
}) })
} }
const { loading, error, data } = useQuery(GET_CATEGORIES);
if (loading) return null;
if (error) return `Error! ${error.message}`;
return ( return (
<div> <div>
<Dialog fullWidth={true} open={isModalOpen} onClose={handleCloseModal} color='default'> <Dialog fullWidth={true} open={isModalOpen} onClose={handleCloseModal} color='default'>
@ -87,7 +112,7 @@ const CreateTodoModal = ({ isModalOpen, handleCloseModal }) => {
onChange={handleFieldChange} onChange={handleFieldChange}
label="Категория" label="Категория"
> >
{categories.map((category, i) => <MenuItem key={i} value={category._id.$oid}>{category.title}</MenuItem>)} {data.categories.map((category, i) => <MenuItem key={i} value={category.id}>{category.title}</MenuItem>)}
</Select> </Select>
</FormControl> </FormControl>
<TextField <TextField

View File

@ -12,8 +12,9 @@ import DeleteIcon from '@material-ui/icons/Delete';
import GitHubIcon from '@material-ui/icons/GitHub'; import GitHubIcon from '@material-ui/icons/GitHub';
import ExpandLess from '@material-ui/icons/ExpandLess' import ExpandLess from '@material-ui/icons/ExpandLess'
import ExpandMore from '@material-ui/icons/ExpandMore' import ExpandMore from '@material-ui/icons/ExpandMore'
import { Query } from 'react-apollo'
import categories from '../../../db/categories.json' import GET_CATEGORIES from '../../../apollo/queries/categories/categories'
const NavContent = ({ history, handleOpenModal }) => { const NavContent = ({ history, handleOpenModal }) => {
const classes = useStyles() const classes = useStyles()
@ -53,7 +54,13 @@ const NavContent = ({ history, handleOpenModal }) => {
</ListItem> </ListItem>
<Collapse in={collapseInfo.isCollapseOpen} timeout="auto" unmountOnExit> <Collapse in={collapseInfo.isCollapseOpen} timeout="auto" unmountOnExit>
<List component="div" disablePadding> <List component="div" disablePadding>
<Categories data={categories} history={history} classes={classes} /> <Query query={GET_CATEGORIES}>
{({ loading, error, data }) => {
if (loading) return null;
if (error) return `Error! ${error}`;
return <Categories data={data.categories} history={history} classes={classes} />
}}
</Query>
<ListItem className={classes.addCategoryItem}> <ListItem className={classes.addCategoryItem}>
<IconButton onClick={handleOpenModal} aria-label="add"> <IconButton onClick={handleOpenModal} aria-label="add">
<AddIcon /> <AddIcon />

View File

@ -11,8 +11,6 @@ import DeleteIcon from '@material-ui/icons/Delete'
import AlarmIcon from '@material-ui/icons/Alarm'; import AlarmIcon from '@material-ui/icons/Alarm';
import EditIcon from '@material-ui/icons/Edit'; import EditIcon from '@material-ui/icons/Edit';
import categories from '../../db/categories.json'
const Todos = ({ data, fromTrashContainer }) => { const Todos = ({ data, fromTrashContainer }) => {
const classes = useStyles() const classes = useStyles()
const [selectedTodo, setSelectedTodo] = React.useState({ const [selectedTodo, setSelectedTodo] = React.useState({
@ -27,10 +25,10 @@ const Todos = ({ data, fromTrashContainer }) => {
const handleOpenModal = todo => { const handleOpenModal = todo => {
const selectedTodo = { const selectedTodo = {
id: todo._id.$oid, id: todo.id,
title: todo.title, title: todo.title,
description: todo.description, description: todo.description,
category: todo.categoryId, category: todo.category,
deadline: todo.deadline deadline: todo.deadline
} }
@ -41,10 +39,10 @@ const Todos = ({ data, fromTrashContainer }) => {
const handleOpenDialog = todo => { const handleOpenDialog = todo => {
setSelectedTodo({ setSelectedTodo({
id: todo._id.$oid, id: todo.id,
title: todo.title, title: todo.title,
description: todo.description, description: todo.description,
category: todo.categoryId, category: todo.category,
deadline: todo.deadline deadline: todo.deadline
}) })
setIsOpenDialog(true) setIsOpenDialog(true)
@ -53,10 +51,10 @@ const Todos = ({ data, fromTrashContainer }) => {
const handleOpenRestoreDialog = todo => { const handleOpenRestoreDialog = todo => {
setSelectedTodo({ setSelectedTodo({
id: todo._id.$oid, id: todo.id,
title: todo.title, title: todo.title,
description: todo.description, description: todo.description,
category: todo.categoryId, category: todo.category,
deadline: todo.deadline deadline: todo.deadline
}) })
setIsOpenRestoreDialog(true) setIsOpenRestoreDialog(true)
@ -81,7 +79,7 @@ const Todos = ({ data, fromTrashContainer }) => {
<Card> <Card>
<CardContent> <CardContent>
<Typography className={classes.cardCategoryHeading} color="textSecondary" gutterBottom> <Typography className={classes.cardCategoryHeading} color="textSecondary" gutterBottom>
{categories.find(category => category._id.$oid === todo.categoryId).title} {todo.category.title}
<span className={classes.cardAlarm}> <span className={classes.cardAlarm}>
<AlarmIcon className={classes.alarmIcon} color='action' /> <AlarmIcon className={classes.alarmIcon} color='action' />
{todo.deadline} {todo.deadline}

View File

@ -3,13 +3,15 @@ import Todos from '../../components/Todos/Todos'
import UpdateCategoryModal from '../../components/Modals/Category/UpdateCategoryModal' import UpdateCategoryModal from '../../components/Modals/Category/UpdateCategoryModal'
import DeleteCategoryDialog from '../../components/Dialogs/Category/DeleteCategoryDialog' import DeleteCategoryDialog from '../../components/Dialogs/Category/DeleteCategoryDialog'
import Fab from '../../components/Fab/Fab' import Fab from '../../components/Fab/Fab'
import { Grid, Typography } from '@material-ui/core' import { Grid, Typography, LinearProgress } from '@material-ui/core'
import todos from '../../db/todos.json'
import categories from '../../db/categories.json' import categories from '../../db/categories.json'
import { Query } from 'react-apollo'
import { useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import GET_TODOS_BY_CATEGORY from '../../apollo/queries/todos/todosByCategory'
const Category = () => { const Category = () => {
const findCategoryById = id => categories.find(category => category._id.$oid === id) const findCategoryById = id => categories.find(category => category._id.$oid === id)
@ -29,21 +31,29 @@ const Category = () => {
const handleCloseDialog = () => setIsOpenDialog(false) const handleCloseDialog = () => setIsOpenDialog(false)
return ( return (
<> <Query query={GET_TODOS_BY_CATEGORY} variables={{ id }}>
<UpdateCategoryModal isModalOpen={isModalOpen} handleCloseModal={handleCloseModal} selectedCategory={selectedCategory} setSelectedCategory={setSelectedCategory} /> {({ loading, error, data }) => {
<DeleteCategoryDialog isOpenDialog={isOpenDialog} handleCloseDialog={handleCloseDialog} /> if (loading) return <LinearProgress />
<Fab style={{ marginRight: '6.5rem' }} size='large' ariaLabel='add' typeIcon='edit' onClick={handleOpenModal} /> if (error) return `Error! ${error.message}`;
<Fab size='large' color="secondary" ariaLabel='delete' typeIcon='delete' onClick={handleOpenDialog} /> return (
<Grid container spacing={2}> <>
<Grid item xs={12}> <UpdateCategoryModal isModalOpen={isModalOpen} handleCloseModal={handleCloseModal} selectedCategory={selectedCategory} setSelectedCategory={setSelectedCategory} />
<Typography> <DeleteCategoryDialog isOpenDialog={isOpenDialog} handleCloseDialog={handleCloseDialog} />
Сортируйте свои повседневные дела используя категории. <Fab style={{ marginRight: '6.5rem' }} size='large' ariaLabel='add' typeIcon='edit' onClick={handleOpenModal} />
</Typography> <Fab size='large' color="secondary" ariaLabel='delete' typeIcon='delete' onClick={handleOpenDialog} />
</Grid> <Grid container spacing={2}>
{/* Здесь берем значение slug из query и передаем полученные данные в prop data !!!! */} <Grid item xs={12}>
<Todos data={todos} /> <Typography>
</Grid> Сортируйте свои повседневные дела используя категории.
</> </Typography>
</Grid>
{/* Здесь берем значение slug из query и передаем полученные данные в prop data !!!! */}
<Todos data={data.category.todos} />
</Grid>
</>
)
}}
</Query>
) )
} }

View File

@ -2,27 +2,37 @@ import React from 'react';
import Fab from '../../components/Fab/Fab' import Fab from '../../components/Fab/Fab'
import Todos from '../../components/Todos/Todos' import Todos from '../../components/Todos/Todos'
import CreateTodoModal from '../../components/Modals/Todo/CreateTodoModal' import CreateTodoModal from '../../components/Modals/Todo/CreateTodoModal'
import { Typography, Grid } from '@material-ui/core' import { Query } from 'react-apollo'
import { Typography, Grid, LinearProgress } from '@material-ui/core'
import todos from '../../db/todos.json' import GET_TODOS from '../../apollo/queries/todos/todos'
const TodosContainer = () => { const TodosContainer = () => {
const [isModalOpen, setIsModalOpen] = React.useState(false) const [isModalOpen, setIsModalOpen] = React.useState(false)
const handleOpenModal = () => setIsModalOpen(true) const handleOpenModal = () => setIsModalOpen(true)
const handleCloseModal = () => setIsModalOpen(false) const handleCloseModal = () => setIsModalOpen(false)
return ( return (
<> <Query query={GET_TODOS}>
<CreateTodoModal isModalOpen={isModalOpen} handleCloseModal={handleCloseModal} /> {({ loading, error, data }) => {
<Fab typeIcon='add' color="secondary" size='large' ariaLabel="add" onClick={handleOpenModal} /> if (loading) return <LinearProgress />
<Grid container spacing={2}> if (error) return `Error! ${error.message}`;
<Grid item xs={12}> return (
<Typography> <>
Создавайте, редактируйте или удаляйте ваши повседневные дела. <CreateTodoModal isModalOpen={isModalOpen} handleCloseModal={handleCloseModal} />
</Typography> <Fab typeIcon='add' color="secondary" size='large' ariaLabel="add" onClick={handleOpenModal} />
</Grid> <Grid container spacing={2}>
<Todos data={todos} /> <Grid item xs={12}>
</Grid> <Typography>
</> Создавайте, редактируйте или удаляйте ваши повседневные дела.
</Typography>
</Grid>
<Todos data={data.todos} />
</Grid>
</>
)
}}
</Query>
) )
} }

View File

@ -2,8 +2,6 @@ import React from 'react';
import Todos from '../../components/Todos/Todos' import Todos from '../../components/Todos/Todos'
import { Grid, Typography } from '@material-ui/core' import { Grid, Typography } from '@material-ui/core'
import todos from '../../db/todos.json'
const Trash = () => { const Trash = () => {
return ( return (
<Grid container spacing={2}> <Grid container spacing={2}>
@ -13,7 +11,7 @@ const Trash = () => {
</Typography> </Typography>
</Grid> </Grid>
{/* Здесь также выполняем нужный запрос на удаленные записи в БД !!!!!!!! */} {/* Здесь также выполняем нужный запрос на удаленные записи в БД !!!!!!!! */}
<Todos data={todos} fromTrashContainer={true} /> {/* <Todos data={todos} fromTrashContainer={true} /> */}
</Grid> </Grid>
) )
} }