Admin: sports edition

This commit is contained in:
Sam
2018-04-30 20:08:18 +02:00
parent b89b40a4de
commit b69a55362d
13 changed files with 445 additions and 85 deletions

View File

@ -7,25 +7,43 @@ export const setData = (target, data) => ({
target,
})
export const setError = (target, error) => ({
export const setError = message => ({
type: 'SET_ERROR',
error,
target,
message,
})
export function getData(target) {
export function getData(target, id = null) {
return function(dispatch) {
if (id !== null && isNaN(id)) {
return dispatch(setError(target, `${target}: Incorrect id`))
}
return mpwoApi
.getData(target)
.getData(target, id)
.then(ret => {
if (ret.status === 'success') {
dispatch(setData(target, ret.data))
} else {
dispatch(setError(target, ret.message))
dispatch(setError(`${target}: ${ret.status}`))
}
})
.catch(error => {
throw error
})
.catch(error => dispatch(setError(`${target}: ${error}`)))
}
}
export function updateData(target, data) {
return function(dispatch) {
if (isNaN(data.id)) {
return dispatch(setError(target, `${target}: Incorrect id`))
}
return mpwoApi
.updateData(target, data)
.then(ret => {
if (ret.status === 'success') {
dispatch(setData(target, ret.data))
} else {
dispatch(setError(`${target}: ${ret.status}`))
}
})
.catch(error => dispatch(setError(`${target}: ${error}`)))
}
}

View File

@ -1,59 +0,0 @@
import React from 'react'
import { Helmet } from 'react-helmet'
export default function AdminPage(props) {
const { target, data } = props
const error = data.error
const results = data.data
const tbKeys = []
if (results.length > 0) {
Object.keys(results[0]).map(key => tbKeys.push(key))
}
const title = target.charAt(0).toUpperCase() + target.slice(1)
return (
<div>
<Helmet>
<title>mpwo - Admin</title>
</Helmet>
{error && (
<code>{error}</code>
)}
<h1 className="page-title">
Administration - {title}
</h1>
<div className="container">
<div className="row">
<div className="col-md-2" />
<div className="col-md-8 card">
<table className="table">
<thead>
<tr>
{tbKeys.map(
tbKey => <th key={tbKey} scope="col">{tbKey}</th>
)}
</tr>
</thead>
<tbody>
{ results.map((result, idx) => (
<tr key={idx}>
{ Object.keys(result).map(key => {
if (key === 'id') {
return <th key={key} scope="row">{result[key]}</th>
}
return <td key={key}>{result[key]}</td>
}) }
</tr>
))}
</tbody>
</table>
</div>
<div className="col-md-2" />
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,37 @@
import React from 'react'
import { connect } from 'react-redux'
import { getData } from '../../actions/index'
import AdminDetail from './generic/AdminDetail'
class AdminSports extends React.Component {
componentDidMount() {
this.props.loadSport(
this.props.location.pathname.replace('/admin/sport/', '')
)
}
render() {
const { sports } = this.props
return (
<div>
<AdminDetail
results={sports}
target="sports"
/>
</div>
)
}
}
export default connect(
state => ({
sports: state.sports.data,
user: state.user,
}),
dispatch => ({
loadSport: sportId => {
dispatch(getData('sports', sportId))
},
})
)(AdminSports)

View File

@ -2,18 +2,20 @@ import React from 'react'
import { connect } from 'react-redux'
import { getData } from '../../actions/index'
import AdminPage from './AdminPage'
import AdminPage from './generic/AdminPage'
class AdminSports extends React.Component {
componentDidMount() {
this.props.loadSport()
this.props.loadSports()
}
render() {
const { sports } = this.props
return (
<div>
<AdminPage
data={sports}
detailLink="sport"
target="sports"
/>
</div>
@ -27,7 +29,7 @@ export default connect(
user: state.user,
}),
dispatch => ({
loadSport: () => {
loadSports: () => {
dispatch(getData('sports'))
},
})

View File

@ -0,0 +1,130 @@
import React from 'react'
import { Helmet } from 'react-helmet'
import { connect } from 'react-redux'
import { Link } from 'react-router-dom'
import { updateData } from '../../../actions/index'
class AdminDetail extends React.Component {
constructor(props, context) {
super(props, context)
this.state = {
isInEdition: false,
}
}
render() {
const {
message,
onDataUpdate,
results,
target,
} = this.props
const { isInEdition } = this.state
const title = target.charAt(0).toUpperCase() + target.slice(1)
return (
<div>
<Helmet>
<title>mpwo - Admin</title>
</Helmet>
<h1 className="page-title">
Administration - {title}
</h1>
{message ? (
<code>{message}</code>
) : (
results.length === 1 && (
<div className="container">
<div className="row">
<div className="col-md-2" />
<div className="col-md-8 card">
<div className="card-body">
<form onSubmit={event =>
event.preventDefault()}
>
{ results.map(result => (
Object.keys(result).map(key => (
<div className="form-group" key={key}>
<label>
{key}:
<input
className="form-control input-lg"
name={key}
readOnly={key === 'id' || !isInEdition}
defaultValue={result[key]}
/>
</label>
</div>
))
))}
{isInEdition ? (
<div>
<input
type="submit"
className="btn btn-primary btn-lg btn-block"
onClick={event => {
onDataUpdate(event, target)
this.setState({ isInEdition: false })
}
}
value="Submit"
/>
<input
type="submit"
className="btn btn-secondary btn-lg btn-block"
onClick={event => {
event.target.form.reset()
this.setState({ isInEdition: false })
}}
value="Cancel"
/>
</div>
) : (
<div>
<input
type="submit"
className="btn btn-primary btn-lg btn-block"
onClick={() => this.setState({ isInEdition: true })}
value="Edit"
/>
<input
type="submit"
className="btn btn-danger btn-lg btn-block"
value="Delete"
/>
</div>
)}
</form>
<Link to={`/admin/${target}`}>Back to the list</Link>
</div>
</div>
<div className="col-md-2" />
</div>
</div>
)
)}
</div>
)
}
}
export default connect(
state => ({
message: state.message,
}),
dispatch => ({
onDataUpdate: (e, target) => {
const data = [].slice
.call(e.target.form.elements)
.reduce(function(map, obj) {
if (obj.name) {
map[obj.name] = obj.value
}
return map
}, {})
dispatch(updateData(target, data))
},
})
)(AdminDetail)

View File

@ -0,0 +1,67 @@
import React from 'react'
import { Helmet } from 'react-helmet'
import { Link } from 'react-router-dom'
export default function AdminPage(props) {
const { data, detailLink, target } = props
const { error } = data
const results = data.data
const tbKeys = []
if (results.length > 0) {
Object.keys(results[0]).map(key => tbKeys.push(key))
}
const title = target.charAt(0).toUpperCase() + target.slice(1)
return (
<div>
<Helmet>
<title>mpwo - Admin</title>
</Helmet>
<h1 className="page-title">
Administration - {title}
</h1>
{error ? (
<code>{error}</code>
) : (
<div className="container">
<div className="row">
<div className="col-md-2" />
<div className="col-md-8 card">
<table className="table">
<thead>
<tr>
{tbKeys.map(
tbKey => <th key={tbKey} scope="col">{tbKey}</th>
)}
</tr>
</thead>
<tbody>
{ results.map((result, idx) => (
<tr key={idx}>
{ Object.keys(result).map(key => {
if (key === 'id') {
return (
<th key={key} scope="row">
<Link to={`/admin/${detailLink}/${result[key]}`}>
{result[key]}
</Link>
</th>
)
}
return <td key={key}>{result[key]}</td>
})
}
</tr>
))}
</tbody>
</table>
</div>
<div className="col-md-2" />
</div>
</div>
)}
</div>
)
}

View File

@ -4,6 +4,7 @@ import { connect } from 'react-redux'
import { Redirect, Route, Switch } from 'react-router-dom'
import AdminMenu from './AdminMenu'
import AdminSport from './AdminSport'
import AdminSports from './AdminSports'
import AccessDenied from './../Others/AccessDenied'
import NotFound from './../Others/NotFound'
@ -22,7 +23,8 @@ class Admin extends React.Component {
user.isAdmin ? (
<Switch>
<Route exact path="/admin" component={AdminMenu} />
<Route path="/admin/sports" component={AdminSports} />
<Route exact path="/admin/sports" component={AdminSports} />
<Route path="/admin/sport" component={AdminSport} />
<Route component={NotFound} />
</Switch>
) : (

View File

@ -2,8 +2,8 @@ import { apiUrl } from '../utils'
export default class MpwoApi {
static getData(target) {
const request = new Request(`${apiUrl}${target}`, {
static getData(target, id = null) {
const request = new Request(`${apiUrl}${target}${id ? `/${id}` : ''}`, {
method: 'GET',
headers: new Headers({
'Content-Type': 'application/json',
@ -14,4 +14,18 @@ export default class MpwoApi {
.then(response => response.json())
.catch(error => error)
}
static updateData(target, data) {
const request = new Request(`${apiUrl}${target}/${data.id}`, {
method: 'PATCH',
headers: new Headers({
'Content-Type': 'application/json',
Authorization: `Bearer ${window.localStorage.getItem('authToken')}`,
}),
body: JSON.stringify(data)
})
return fetch(request)
.then(response => response.json())
.catch(error => error)
}
}

View File

@ -13,13 +13,6 @@ const handleDataAndError = (state, type, action) => {
return {
...state,
data: action.data[action.target],
error: null,
}
case 'SET_ERROR':
return {
...state,
data: { ...initial[type].data },
error: action.error,
}
default:
return state
@ -76,9 +69,11 @@ const message = (state = initial.message, action) => {
case 'PROFILE_ERROR':
case 'PROFILE_UPDATE_ERROR':
case 'PICTURE_ERROR':
case 'SET_ERROR':
return action.message
case 'LOGOUT':
case 'PROFILE_SUCCESS':
case 'SET_RESULTS':
case '@@router/LOCATION_CHANGE':
return ''
default:

View File

@ -1,6 +1,5 @@
const emptyData = {
data: [],
error: null,
}
export default {