Tìm hiểu Saga, viết class AxiosService Phần 1 (ok)
Last updated
Was this helpful?
Last updated
Was this helpful?
diện
package.json
{
"name": "pi",
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
"@mui/icons-material": "^6.4.1",
"@mui/material": "^6.4.1",
"@mui/styles": "^6.4.1",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^13.0.0",
"@testing-library/user-event": "^13.2.1",
"add": "^2.0.6",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-redux": "^9.2.0",
"react-scripts": "5.0.1",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"web-vitals": "^2.1.0",
"yarn": "^1.22.22"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
src\index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './containers/App';
import myReducer from './reducers/index';
import { thunk } from 'redux-thunk';
import { Provider } from 'react-redux';
import { legacy_createStore as createStore, applyMiddleware, compose } from 'redux'
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
var store = createStore(myReducer,composeEnhancer(applyMiddleware(thunk)));
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}><App /></Provider>
</React.StrictMode>
);
src\commons\Theme\index.js
import { createTheme } from '@mui/material/styles';
var theme = createTheme({
palette: {
secondary: {
main: '#123456'
}
},
borderradius: 4
});
export default theme;
src\constants\index.js
export const STATUSES = [
{
value: 0,
state: 'READY'
},
{
value: 1,
state: 'INPROCESSS'
},
{
value: 2,
state: 'COMPLATED'
}
];
src\containers\App\index.js
import React, { Component } from 'react';
import AddIcon from '@mui/icons-material/AddCircle';
import Container from '@mui/material/Container';
import TaskBoard from '../TaskBoard';
import Button from '@mui/material/Button';
class App extends Component {
render() {
return (
<Container maxWidth="lg">
<Button variant="contained"><AddIcon></AddIcon> Add</Button>
<TaskBoard></TaskBoard>
</Container>
);
}
}
export default App;
src\containers\TaskBoard\index.js
import React, { Component } from 'react';
import {connect} from 'react-redux';
import ProductList from './../../components/ProductList';
import ProductItem from './../../components/ProductItem';
import { withStyles } from '@mui/styles';
import styles from './styles';
const listTask = [
{
id: 1,
title: 'Read book',
description: 'Read material ui book',
status: 0,
},
{
id: 2,
title: 'Play football',
description: 'With my friend',
status: 2,
},
{
id: 3,
title: 'Play game',
description: 'Alone 😊',
status: 1,
},
];
class TaskBoard extends Component {
renderBoard(states) {
let xhtml = null;
xhtml = (states.map((state,index)=> {
var taskFiltered = listTask.filter(
task => task.status === state.value
);
return (
<ProductItem key={index} state={taskFiltered[0]} index={index} status={state}></ProductItem>
);
}));
return xhtml;
}
render() {
var {states} = this.props;
return (
<ProductList>
{this.renderBoard(states)}
</ProductList>
);
}
}
var mapStateToProps = (state) => {
return {
states: state.states
}
}
export default connect(mapStateToProps, null)(withStyles(styles)(TaskBoard));
src\containers\TaskBoard\styles.js
// Some code
src\components\ProductItem\index.js
import React, { Component } from 'react';
import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/Delete';
import Grid from '@mui/material/Grid2';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import CardActions from '@mui/material/CardActions';
import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';
import Fab from '@mui/material/Fab';
import { withStyles } from '@mui/styles';
import styles from './styles';
class ProductItem extends Component {
render() {
var { classes, state, status } = this.props;
return (
<Grid md={4}>
<Card>
<Box my={2} textAlign="center">
<Typography component="h2">
{status.state}
</Typography>
</Box>
<CardContent>
<Grid container>
<Grid md={6}>
<Typography component="h3">
{state.title}
</Typography>
</Grid>
<Grid md={6}>
<Typography component="h4">
{status.state}
</Typography>
</Grid>
<Grid md={12}>
<Typography component="p">
{state.description}
</Typography>
</Grid>
</Grid>
<CardActions className={classes.CardActions}>
<Fab color="primary" aria-label="add">
<EditIcon></EditIcon>
</Fab>
<Fab color="secondary" aria-label="add">
<DeleteIcon></DeleteIcon>
</Fab>
</CardActions>
</CardContent>
</Card>
</Grid>
);
}
}
export default withStyles(styles)(ProductItem);
src\components\ProductItem\styles.js
var styles = (theme) => ({
CardActions: {
justifyContent: "flex-end",
borderRadius: theme.borderradius
}
});
export default styles;
src\components\ProductList\index.js
import React, { Component } from 'react';
import Grid from '@mui/material/Grid2';
class ProductList extends Component {
render() {
return (
<Grid container spacing={2}>
{this.props.children}
</Grid>
);
}
}
export default ProductList;
src\components\TaskForm\index.js
// Some code
src\components\TaskForm\styles.js
// Some code
src\components\TaskItem\index.js
import React, { Component } from 'react';
import { withStyles } from '@mui/styles';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import CardActions from '@mui/material/CardActions';
import Typography from '@mui/material/Typography';
import Grid from '@mui/material/Grid2';
import Fab from '@mui/material/Fab';
import Edit from '@mui/icons-material/Edit';
import styles from './styles';
class TaskItem extends Component {
render() {
const { classes, task, status } = this.props;
const { id, title } = task;
return (
<Card key={id} className={classes.card}>
<CardContent>
<Grid container justify="space-between">
<Grid md={8}>
<Typography component="h2">{title}</Typography>
</Grid>
<Grid md={4}>
{status.label}
</Grid>
</Grid>
<p>{task.description}</p>
</CardContent>
<CardActions className={classes.cardActions}>
<Fab
color="primary"
aria-label="Edit"
className={classes.fab}
size="small"
>
<Edit fontSize="small">edit_icon</Edit>
</Fab>
<Fab
color="primary"
aria-label="Delete"
className={classes.fab}
size="small"
>
<Delete fontSize="small">delete_icon</Delete>
</Fab>
</CardActions>
</Card>
);
}
}
export default withStyles(styles)(TaskItem);
src\components\TaskItem\styles.js
// Some code
src\components\TaskList\index.js
import React, { Component } from 'react';
import { withStyles } from '@mui/styles';
import Grid from '@mui/material/Grid2';
import Box from '@mui/material/Box';
import styles from './styles';
import TaskItem from '../TaskItem';
class TaskList extends Component {
render() {
const { classes, tasks, status } = this.props;
return (
<Grid md={4} xs={12} key={status.value}>
<Box mt={2} mb={2}>
<div className={classes.status}>{status.label}</div>
</Box>
<div className={classes.wrapperListTask}>
{tasks.map(task => {
return <TaskItem task={task} status={status} key={task.id} />;
})}
</div>
</Grid>
);
}
}
export default withStyles(styles)(TaskList);
src\components\TaskList\styles.js
// Some code
src\index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './containers/App';
import myReducer from './reducers/index';
import { thunk } from 'redux-thunk';
import { Provider } from 'react-redux';
import { legacy_createStore as createStore, applyMiddleware, compose } from 'redux';
import { ThemeProvider } from '@mui/material/styles';
import theme from './commons/Theme';
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
var store = createStore(myReducer, composeEnhancer(applyMiddleware(thunk)));
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<ThemeProvider theme={theme}>
<Provider store={store}><App /></Provider>
</ThemeProvider>
</React.StrictMode>
);
src\commons\Theme\index.js
import { createTheme } from '@mui/material/styles';
var theme = createTheme({
palette: {
secondary: {
main: '#123456'
}
},
borderradius: 40
});
export default theme;
src\components\ProductItem\styles.js
var styles = (theme) => ({
CardActions: {
justifyContent: "flex-start",
borderRadius: theme.borderradius,
backgroundColor: theme.palette.secondary.main,
}
});
export default styles;
Tên cũ dùng state dễ bị nhầm xem ảnh để biết thêm
src\containers\TaskBoard\index.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import ProductList from './../../components/ProductList';
import ProductItem from './../../components/ProductItem';
import { withStyles } from '@mui/styles';
import styles from './styles';
const listTask = [
{
id: 1,
title: 'Read book',
description: 'Read material ui book',
status: 0,
},
{
id: 2,
title: 'Play football',
description: 'With my friend',
status: 2,
},
{
id: 3,
title: 'Play game',
description: 'Alone 😊',
status: 1,
}
];
class TaskBoard extends Component {
renderBoard(states) {
let xhtml = null;
xhtml = (states.map((state, index) => {
var taskFiltered = listTask.filter(
task => task.status === state.value
);
return (
<ProductItem key={index} task={taskFiltered[0]} index={index} status={state}></ProductItem>
);
}));
return xhtml;
}
render() {
var { states } = this.props;
return (
<ProductList>
{this.renderBoard(states)}
</ProductList>
);
}
}
var mapStateToProps = (state) => {
return {
states: state.states
}
}
export default connect(mapStateToProps, null)(withStyles(styles)(TaskBoard));
src\components\ProductItem\index.js
import React, { Component } from 'react';
import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/Delete';
import Grid from '@mui/material/Grid2';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import CardActions from '@mui/material/CardActions';
import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';
import Fab from '@mui/material/Fab';
import { withStyles } from '@mui/styles';
import styles from './styles';
class ProductItem extends Component {
render() {
var { classes, task, status } = this.props;
return (
<Grid md={4}>
<Card>
<Box my={2} textAlign="center">
<Typography component="h2">
{status.state}
</Typography>
</Box>
<CardContent>
<Grid container>
<Grid md={6}>
<Typography component="h3">
{task.title}
</Typography>
</Grid>
<Grid md={6}>
<Typography component="h4">
{status.state}
</Typography>
</Grid>
<Grid md={12}>
<Typography component="p">
{task.description}
</Typography>
</Grid>
</Grid>
<CardActions className={classes.CardActions}>
<Fab color="primary" aria-label="add">
<EditIcon></EditIcon>
</Fab>
<Fab color="secondary" aria-label="add">
<DeleteIcon></DeleteIcon>
</Fab>
</CardActions>
</CardContent>
</Card>
</Grid>
);
}
}
export default withStyles(styles)(ProductItem);
src\containers\App\index.js
import React, { Component } from 'react';
import AddIcon from '@mui/icons-material/AddCircle';
import Container from '@mui/material/Container';
import TaskBoard from '../TaskBoard';
import Button from '@mui/material/Button';
import TaskForm from '../../components/TaskForm';
class App extends Component {
constructor(props) {
super(props);
this.state = {
open: false
}
}
setOpen = (bool) => {
this.setState({
open: bool
})
}
handleClickOpen = () => {
this.setOpen(true)
}
handleClose = () => {
this.setOpen(false)
}
render() {
var { open } = this.state;
return (
<Container maxWidth="lg">
<Button variant="contained" color="secondary" onClick={this.handleClickOpen}><AddIcon></AddIcon> Add</Button>
<TaskForm open={open} handleClose={this.handleClose}/>
<TaskBoard></TaskBoard>
</Container>
);
}
}
export default App;
src\components\TaskForm\index.js
import React, { Component } from 'react';
import Button from '@mui/material/Button';
import TextField from '@mui/material/TextField';
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText';
import DialogTitle from '@mui/material/DialogTitle';
class TaskForm extends Component {
handleClose = () => {
return this.props.handleClose()
};
render() {
var { open } = this.props;
return (
<Dialog
open={open}
onClose={this.handleClose}
>
<DialogTitle>Form</DialogTitle>
<DialogContent>
<DialogContentText>
Please enter your email address here. We will send updates occasionally.
</DialogContentText>
<TextField
placeholder="Title"
variant="standard"
fullWidth
/>
<TextField
placeholder="Description"
multiline
variant="standard"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={this.handleClose}>Cancel</Button>
<Button type="submit">Add</Button>
</DialogActions>
</Dialog>
);
}
}
export default TaskForm;
src\components\TaskForm\styles.js
// Some code
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './containers/App';
import myReducer from './reducers/index';
import { thunk } from 'redux-thunk';
import { Provider } from 'react-redux';
import { legacy_createStore as createStore, applyMiddleware, compose } from 'redux'
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
var store = createStore(myReducer,composeEnhancer(applyMiddleware(thunk)));
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}><App /></Provider>
</React.StrictMode>
);
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './containers/App';
import store from './redux/configStore';
import { Provider } from 'react-redux';
import { ThemeProvider } from '@mui/material/styles';
import theme from './commons/Theme';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<ThemeProvider theme={theme}>
<Provider store={store}><App /></Provider>
</ThemeProvider>
</React.StrictMode>
);
src\redux\configStore.js
import { legacy_createStore as createStore, applyMiddleware, compose } from 'redux';
import myReducer from '../reducers';
import { thunk } from 'redux-thunk';
const composeEnhancer = process.env.NODE_ENV !=='production' && (window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose);
const store = createStore(myReducer, composeEnhancer(applyMiddleware(thunk)));
export default store;
Chú ý không dùng cách này được 👇
import { legacy_createStore as createStore, applyMiddleware, compose } from 'redux';
import myReducer from '../reducers';
import { thunk } from 'redux-thunk';
const composeEnhancer = process.env.NODE_ENV !=='production' && (window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose);
const configStore = () => {
const store = createStore(myReducer,composeEnhancer(applyMiddleware(thunk)));
return store;
}
export default configStore;
Hoặc bạn có thể sử dụng như sau
src\redux\configStore.js
import { legacy_createStore as createStore, applyMiddleware, compose } from 'redux';
import myReducer from '../reducers';
import { thunk } from 'redux-thunk';
const composeEnhancer = process.env.NODE_ENV !=='production' && (window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose);
const configStore = () => {
const store = createStore(myReducer,composeEnhancer(applyMiddleware(thunk)));
return store;
}
export default configStore;
src\index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './containers/App';
import store from './redux/configStore';
import { Provider } from 'react-redux';
import { ThemeProvider } from '@mui/material/styles';
import theme from './commons/Theme';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<ThemeProvider theme={theme}>
<Provider store={store()}><App /></Provider>
</ThemeProvider>
</React.StrictMode>
);
src\containers\TaskBoard\index.js thay đổi lại name states thành task
import React, { Component } from 'react';
import { connect } from 'react-redux';
import ProductList from './../../components/ProductList';
import ProductItem from './../../components/ProductItem';
import { withStyles } from '@mui/styles';
import styles from './styles';
const listTask = [
{
id: 1,
title: 'Read book',
description: 'Read material ui book',
status: 0,
},
{
id: 2,
title: 'Play football',
description: 'With my friend',
status: 2,
},
{
id: 3,
title: 'Play game',
description: 'Alone 😊',
status: 1,
}
];
class TaskBoard extends Component {
renderBoard(states) {
let xhtml = null;
xhtml = (states.map((state, index) => {
var taskFiltered = listTask.filter(
task => task.status === state.value
);
return (
<ProductItem key={index} task={taskFiltered[0]} index={index} status={state}></ProductItem>
);
}));
return xhtml;
}
render() {
var { states } = this.props;
return (
<ProductList>
{this.renderBoard(states)}
</ProductList>
);
}
}
var mapStateToProps = (state) => {
return {
states: state.task
}
}
export default connect(mapStateToProps, null)(withStyles(styles)(TaskBoard));
src\commons\axiosService.js
import axios from 'axios';
class AxiosService {
constructor() {
const instance = axios.create();
instance.interceptors.response.use(this.handleSuccess, this.handleError);
this.instance = instance;
}
handleSuccess(response) {
return response;
}
handleError(error) {
return Promise.reject(error);
}
get(url) {
return this.instance.get(url);
}
post(url, body) {
return this.instance.post(url, body);
}
put(url, body) {
return this.instance.put(url, body);
}
delete(url) {
return this.instance.delete(url);
}
}
export default new AxiosService();
src\containers\TaskBoard\index.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as taskActions from './../../actions/task';
import ProductList from './../../components/ProductList';
import ProductItem from './../../components/ProductItem';
import { withStyles } from '@mui/styles';
import { STATUSES } from '../../constants';
import styles from './styles';
class TaskBoard extends Component {
componentDidMount() {
const { taskActionCreators } = this.props;
const { fetchListTaskRequest } = taskActionCreators;
fetchListTaskRequest();
}
renderBoard() {
const { listTask } = this.props;
let xhtml = null;
xhtml = (STATUSES?.map((status, index) => {
var taskFiltered = listTask.filter(
task => task.status === status.value
);
return (
<ProductItem key={index} task={taskFiltered[0]} index={index} status={status}></ProductItem>
);
}));
return xhtml;
}
render() {
return (
<ProductList>
{this.renderBoard()}
</ProductList>
);
}
}
var mapStateToProps = (state) => {
return {
listTask: state.task.listTask
}
}
const mapDispatchToProps = dispatch => {
return {
taskActionCreators: bindActionCreators(taskActions, dispatch)
};
};
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(TaskBoard));
src\actions\task.js
import * as taskConstants from './../constants/task';
import * as taskApi from './../apis/task';
export const fetchListTask = () => {
return {
type: taskConstants.FETCH_TASK
};
};
export const fetchListTaskSuccess = data => {
return {
type: taskConstants.FETCH_TASK_SUCCESS,
payload: {
data,
},
};
};
export const fetchListTaskFailed = error => {
return {
type: taskConstants.FETCH_TASK_FAILED,
payload: {
error,
},
};
};
export const fetchListTaskRequest = () => {
return dispatch => {
taskApi.getList().then(respon => {
const { data } = respon;
dispatch(fetchListTaskSuccess(data))
}).catch(error => {
dispatch(fetchListTaskFailed(error))
})
}
}
src\apis\task.js
import axiosService from '../commons/axiosService';
import { API_ENDPOINT } from '../constants';
const url = 'tasks';
// http://localhost:3000/tasks
export const getList = () => {
return axiosService.get(`${API_ENDPOINT}/${url}`);
};
src\constants\task.js
export const FETCH_TASK = 'FETCH_TASK';
export const FETCH_TASK_SUCCESS = 'FETCH_TASK_SUCCESS';
export const FETCH_TASK_FAILED = 'FETCH_TASK_FAILED';
export const FILTER_TASK = 'FILTER_TASK';
export const FILTER_TASK_SUCCESS = 'FILTER_TASK_SUCCESS';
export const ADD_TASK = 'ADD_TASK';
export const ADD_TASK_SUCCESS = 'ADD_TASK_SUCCESS';
export const ADD_TASK_FAILED = 'ADD_TASK_FAILED';
export const SET_TASK_EDITING = 'SET_TASK_EDITING';
export const UPDATE_TASK = 'UPDATE_TASK';
export const UPDATE_TASK_SUCCESS = 'UPDATE_TASK_SUCCESS';
export const UPDATE_TASK_FAILED = 'UPDATE_TASK_FAILED';
export const DELETE_TASK = 'DELETE_TASK';
export const DELETE_TASK_SUCCESS = 'DELETE_TASK_SUCCESS';
export const DELETE_TASK_FAILED = 'DELETE_TASK_FAILED';
src\reducers\index.js
import { combineReducers } from 'redux';
import task from './task';
var myReducer = combineReducers({
task,
});
export default myReducer;
src\reducers\task.js
import * as taskConstants from '../constants/task';
import { toastError, toastSuccess } from './../helpers/toastHelper';
const initialState = {
listTask: []
};
var task = (state = initialState, action) => {
switch (action.type) {
case taskConstants.FETCH_TASK: {
return {
...state,
listTask: [],
};
}
case taskConstants.FETCH_TASK_SUCCESS: {
const { data } = action.payload;
return {
...state,
listTask: data,
};
}
case taskConstants.FETCH_TASK_FAILED: {
const { error } = action.payload;
toastError(error);
return {
...state,
listTask: [],
};
}
default:
return state;
}
}
export default task;
package.json
{
"name": "pi",
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
"@mui/icons-material": "^6.4.1",
"@mui/material": "^6.4.1",
"@mui/styles": "^6.4.1",
"@reduxjs/toolkit": "^2.5.1",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^13.0.0",
"@testing-library/user-event": "^13.2.1",
"add": "^2.0.6",
"axios": "^1.7.9",
"connected-react-router": "^6.9.3",
"global": "^4.4.0",
"json-server": "^1.0.0-beta.3",
"query-string": "^9.1.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-redux": "^9.2.0",
"react-scripts": "5.0.1",
"react-toastify": "^11.0.3",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"web-vitals": "^2.1.0",
"yarn": "^1.22.22"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
db.json
{
"tasks": [
{
"title": "Learning reactjs",
"description": "Redux form",
"status": 2,
"id": 5
},
{
"title": "Learning SASS",
"description": "Redux form",
"status": 1,
"id": 6
},
{
"title": "Learning Java",
"description": "OOP",
"status": 1,
"id": 7
},
{
"title": "Play game",
"description": "With my friend",
"status": 0,
"id": 9
},
{
"title": "Play game",
"description": "FIFA",
"status": 2,
"id": 11
},
{
"title": "aaaaaaa",
"description": "bbbbbb",
"status": 0,
"id": 12
}
]
}
src\containers\TaskBoard\index.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as taskActions from './../../actions/task';
import Grid from '@mui/material/Grid2';
import TaskList from './../../components/TaskList';
import { withStyles } from '@mui/styles';
import { STATUSES } from '../../constants';
import styles from './styles';
class TaskBoard extends Component {
componentDidMount() {
const { taskActionCreators } = this.props;
const { fetchListTaskRequest } = taskActionCreators;
fetchListTaskRequest();
}
renderBoard() {
const { listTask } = this.props;
let xhtml = null;
xhtml = (
<>
{
STATUSES.map(status => {
const taskFiltered = listTask.filter(
task => task.status === status.value,
);
return (
<TaskList key={status.value} tasks={taskFiltered} status={status} />
);
})
}
</>
);
return xhtml;
}
render() {
return (
<Grid container spacing={2}>
{this.renderBoard()}
</Grid>
);
}
}
var mapStateToProps = (state) => {
return {
listTask: state.task.listTask
}
}
const mapDispatchToProps = dispatch => {
return {
taskActionCreators: bindActionCreators(taskActions, dispatch)
};
};
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(TaskBoard));
src\components\TaskList\index.js
import React, { Component } from 'react';
import { withStyles } from '@mui/styles';
import Grid from '@mui/material/Grid2';
import Box from '@mui/material/Box';
import styles from './styles';
import TaskItem from '../TaskItem';
class TaskList extends Component {
render() {
const { classes, tasks, status } = this.props;
return (
<Grid size={4}>
<Box sx={{ mb: 2 }}>
<div className={classes.status}>{status.label}</div>
</Box>
<Box>
{tasks.map(task => {
return <TaskItem task={task} status={status} key={task.id} />;
})}
</Box>
</Grid>
);
}
}
export default withStyles(styles)(TaskList);
src\components\TaskItem\index.js
import React, { Component } from 'react';
import { withStyles } from '@mui/styles';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import CardActions from '@mui/material/CardActions';
import Typography from '@mui/material/Typography';
import Grid from '@mui/material/Grid2';
import Fab from '@mui/material/Fab';
import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/Delete';
import styles from './styles';
class TaskItem extends Component {
render() {
const { classes, task, status } = this.props;
const { id, title } = task;
return (
<Card key={id} sx={{ mb: 2 }}>
<CardContent>
<Grid container justify="space-between">
<Grid size={8}>
<Typography component="h2">{title}</Typography>
</Grid>
<Grid size={4}>
{status.label}
</Grid>
</Grid>
<p>{task.description}</p>
</CardContent>
<CardActions className={classes.CardActions}>
<Fab color="primary" aria-label="add">
<EditIcon></EditIcon>
</Fab>
<Fab color="secondary" aria-label="add">
<DeleteIcon></DeleteIcon>
</Fab>
</CardActions>
</Card>
);
}
}
export default withStyles(styles)(TaskItem);
src\apis\task.js
import axiosService from '../commons/axiosService';
import { API_ENDPOINT } from '../constants';
const url = 'tasks';
// http://localhost:3000/tasks
export const getList = () => {
return axiosService.get(`${API_ENDPOINT}/${url}`);
};
src\actions\task.js
import * as taskConstants from './../constants/task';
import * as taskApi from './../apis/task';
export const fetchListTaskSuccess = data => {
return {
type: taskConstants.FETCH_TASK_SUCCESS,
payload: {
data,
},
};
};
export const fetchListTaskFailed = error => {
return {
type: taskConstants.FETCH_TASK_FAILED,
payload: {
error,
},
};
};
export const fetchListTaskRequest = () => {
return dispatch => {
taskApi.getList().then(respon => {
const { data } = respon;
dispatch(fetchListTaskSuccess(data))
}).catch(error => {
dispatch(fetchListTaskFailed(error))
})
}
}
src\helpers\toastHelper.js
import { toast } from 'react-toastify';
export const toastError = error => {
let message = null;
if (typeof error === 'object' && error.message) {
({ message } = error);
}
if (message !== null && typeof message !== 'undefined' && message !== '') {
toast.error(message);
}
};
export const toastSuccess = message => {
if (message !== null && typeof message !== 'undefined' && message !== '') {
toast.success(message);
}
};
src\index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './containers/App';
import store from './redux/configStore';
import { Provider } from 'react-redux';
import { ThemeProvider } from '@mui/material/styles';
import theme from './commons/Theme';
import { ToastContainer} from 'react-toastify';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<ThemeProvider theme={theme}>
<Provider store={store()}><App /></Provider>
</ThemeProvider>
<ToastContainer />
</React.StrictMode>
);
src\reducers\task.js
import * as taskConstants from '../constants/task';
import { toastError, toastSuccess } from './../helpers/toastHelper';
const initialState = {
listTask: []
};
var task = (state = initialState, action) => {
switch (action.type) {
case taskConstants.FETCH_TASK_SUCCESS: {
const { data } = action.payload;
toastSuccess(taskConstants.FETCH_TASK_SUCCESS);
return {
...state,
listTask: data,
};
}
case taskConstants.FETCH_TASK_FAILED: {
const { error } = action.payload;
toastError(error);
return {
...state,
listTask: [],
};
}
default:
return state;
}
}
export default task;
File
C:\Users\Administrator\Desktop\pi