Sử dụng tailwind viết lại giao diện (ok)
Last updated
Was this helpful?
Last updated
Was this helpful?
src\containers\App.js
import React, { Component } from 'react';
import TaskBoard from './TaskBoard';
import Header from '../components/Header';
class App extends Component {
render() {
return (
<section className="text-gray-600 body-font m-auto">
<Header />
<TaskBoard />
</section>
);
}
}
export default App;
src\containers\TaskBoard.js
import React, { Component } from 'react';
import TaskList from '../components/TaskList';
import { STATUSES } from '../constants';
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 {
render() {
return (
<>
<div className="container px-5 pt-6 mx-auto">
<button className="lg:mb-8 xl:mt-0 flex-shrink-0 inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded">Add</button>
</div>
<div className="container px-5 pt-6 pb-2 mx-auto">
<div className="flex flex-wrap -m-4">
{
STATUSES.map(status => {
const taskFiltered = listTask.filter(
task => task.status === status.value,
);
return (
<TaskList key={status.value} tasks={taskFiltered} status={status} />
);
})
}
</div>
</div>
</>
);
}
}
export default TaskBoard;
src\constants\index.js
export const STATUSES = [
{
value: 0,
state: 'READY'
},
{
value: 1,
state: 'INPROCESSS'
},
{
value: 2,
state: 'COMPLATED'
}
];
src\components\Header.js
import React, { Component } from 'react';
class Header extends Component {
render() {
return (
<header className="text-gray-600 body-font">
<div className="container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center">
<nav className="flex lg:w-2/5 flex-wrap items-center text-base md:ml-auto">
<a className="mr-5 hover:text-gray-900">First Link</a>
<a className="mr-5 hover:text-gray-900">Second Link</a>
<a className="mr-5 hover:text-gray-900">Third Link</a>
<a className="hover:text-gray-900">Fourth Link</a>
</nav>
<a className="flex order-first lg:order-none lg:w-1/5 title-font font-medium items-center text-gray-900 lg:items-center lg:justify-center mb-4 md:mb-0">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" className="w-10 h-10 text-white p-2 bg-indigo-500 rounded-full" viewBox="0 0 24 24">
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"></path>
</svg>
<span className="ml-3 text-xl">Tailblocks</span>
</a>
<div className="lg:w-2/5 inline-flex lg:justify-end ml-5 lg:ml-0">
<button className="inline-flex items-center bg-gray-100 border-0 py-1 px-3 focus:outline-none hover:bg-gray-200 rounded text-base mt-4 md:mt-0">Button
<svg fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" className="w-4 h-4 ml-1" viewBox="0 0 24 24">
<path d="M5 12h14M12 5l7 7-7 7"></path>
</svg>
</button>
</div>
</div>
</header>
);
}
}
export default Header;
src\components\TaskItem.js
import React, { Component } from 'react';
class TaskItem extends Component {
render() {
const { task, status } = this.props;
return (
<div className="p-4 md:w-1/3">
<div className="h-full border-2 border-gray-200 border-opacity-60 rounded-lg overflow-hidden">
<img className="lg:h-48 md:h-36 w-full object-cover object-center" src="./images/99.jpg" alt="blog" />
<div className="p-6">
<h1 className="title-font text-lg font-medium text-gray-900 mb-3">{task.title}</h1>
<p className="leading-relaxed mb-3">{task.description}</p>
<div className="flex items-center justify-end flex-wrap gap-2">
<button className="lg:mt-2 xl:mt-0 flex-shrink-0 inline-flex text-black font-bold bg-blue-50 border-0 py-2 px-6 focus:outline-none rounded-4xl">{status.state}</button>
<button className="lg:mt-2 xl:mt-0 flex-shrink-0 inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded">Edit</button>
<button className="lg:mt-2 xl:mt-0 flex-shrink-0 inline-flex text-white bg-black border-0 py-2 px-6 focus:outline-none hover:bg-red-600 rounded">Delete</button>
</div>
</div>
</div>
</div>
);
}
}
export default TaskItem;
src\components\TaskList.js
import React, { Component } from 'react';
import TaskItem from './TaskItem';
class TaskList extends Component {
render() {
const { tasks, status } = this.props;
return (
<>
{
tasks.map(task => {
return <TaskItem task={task} status={status} key={task.id} />;
})
}
</>
);
}
}
export default TaskList;
src\containers\TaskBoard.js
import React, { Component } from 'react';
import TaskList from '../components/TaskList';
import { STATUSES } from '../constants';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as taskActions from './../actions/task';
class TaskBoard extends Component {
componentDidMount() {
const { taskActionCreators } = this.props;
const { fetchListTaskRequest } = taskActionCreators;
fetchListTaskRequest();
}
render() {
const { listTask } = this.props;
return (
<>
<div className="container px-5 pt-6 mx-auto">
<button className="lg:mb-8 xl:mt-0 flex-shrink-0 inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded">Add</button>
</div>
<div className="container px-5 pt-6 pb-2 mx-auto">
<div className="flex flex-wrap -m-4">
{
STATUSES.map(status => {
const taskFiltered = listTask.filter(
task => task.status === status.value,
);
return (
<TaskList key={status.value} tasks={taskFiltered} status={status} />
);
})
}
</div>
</div>
</>
);
}
}
var mapStateToProps = (state) => {
return {
listTask: state.taskReducer.listTask
}
}
const mapDispatchToProps = dispatch => {
return {
taskActionCreators: bindActionCreators(taskActions, dispatch)
};
};
export default connect(mapStateToProps, mapDispatchToProps)(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\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 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 => {
dispatch(fetchListTask());
taskApi.getList().then(respon => {
const { data } = respon;
dispatch(fetchListTaskSuccess(data))
}).catch(error => {
dispatch(fetchListTaskFailed(error))
})
}
}
Loại bỏ action fetchListTaskRequest sử dụng bởi redux-thunk.
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 => {
// dispatch(fetchListTask());
// taskApi.getList().then(respon => {
// const { data } = respon;
// dispatch(fetchListTaskSuccess(data))
// }).catch(error => {
// dispatch(fetchListTaskFailed(error))
// })
// }
// }
Thay thế bởi action fetchListTask sử dụng cho redux-saga
src\containers\TaskBoard.js
import React, { Component } from 'react';
import TaskList from '../components/TaskList';
import { STATUSES } from '../constants';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as taskActions from './../actions/task';
class TaskBoard extends Component {
componentDidMount() {
const { taskActionCreators } = this.props;
// const { fetchListTaskRequest } = taskActionCreators;
// fetchListTaskRequest();
const { fetchListTask } = taskActionCreators;
fetchListTask();
}
render() {
const { listTask } = this.props;
return (
<>
<div className="container px-5 pt-6 mx-auto">
<button className="lg:mb-8 xl:mt-0 flex-shrink-0 inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded">Add</button>
</div>
<div className="container px-5 pt-6 pb-2 mx-auto">
<div className="flex flex-wrap -m-4">
{
STATUSES.map(status => {
const taskFiltered = listTask.filter(
task => task.status === status.value,
);
return (
<TaskList key={status.value} tasks={taskFiltered} status={status} />
);
})
}
</div>
</div>
</>
);
}
}
var mapStateToProps = (state) => {
return {
listTask: state.taskReducer.listTask
}
}
const mapDispatchToProps = dispatch => {
return {
taskActionCreators: bindActionCreators(taskActions, dispatch)
};
};
export default connect(mapStateToProps, mapDispatchToProps)(TaskBoard);
src\redux\configStore.js
import { legacy_createStore as createStore, applyMiddleware, compose } from 'redux';
import myReducer from '../reducers';
import { thunk } from 'redux-thunk';
import createSagaMiddleware from 'redux-saga';
import rootSaga from '../sagas';
const sagaMiddleware = createSagaMiddleware()
const middlewares = [thunk, sagaMiddleware];
const enhancers = [applyMiddleware(...middlewares)];
const composeEnhancer = process.env.NODE_ENV !== 'production' && (window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose);
const configStore = () => {
const store = createStore(myReducer, composeEnhancer(...enhancers));
sagaMiddleware.run(rootSaga);
return store;
}
export default configStore;
src\sagas\index.js
import { call, fork, take, put } from 'redux-saga/effects';
import * as taskConstants from '../constants/task';
import { getList } from './../apis/task';
import { STATUS_CODE } from '../constants';
import { fetchListTaskSuccess, fetchListTaskFailed } from '../actions/task';
function* watchfetchListTaskAction() {
yield take(taskConstants.FETCH_TASK);
const resp = yield call(getList);
const { status, data } = resp;
if (status === STATUS_CODE.SUCCESS) {
yield put(fetchListTaskSuccess(data))
} else {
yield put(fetchListTaskFailed(data))
}
}
function* rootSaga(){
yield fork(watchfetchListTaskAction);
}
export default rootSaga;