From 29275c9c79b3ac27719f2b334b0c6d694f95ff8a Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Tue, 29 Sep 2020 19:56:00 +0200 Subject: stub episode form and index --- .../app/views/episode/components/episode.form.js | 161 +++++++++++++++++++++ .../app/views/episode/components/episode.menu.js | 57 ++++++++ .../app/views/episode/containers/episode.edit.js | 52 +++++++ .../app/views/episode/containers/episode.index.js | 71 +++++++++ .../app/views/episode/containers/episode.new.js | 62 ++++++++ .../app/views/episode/episode.container.js | 26 ++++ .../frontend/app/views/episode/episode.css | 6 + .../frontend/app/views/episode/episode.reducer.js | 18 +++ 8 files changed, 453 insertions(+) create mode 100644 animism-align/frontend/app/views/episode/components/episode.form.js create mode 100644 animism-align/frontend/app/views/episode/components/episode.menu.js create mode 100644 animism-align/frontend/app/views/episode/containers/episode.edit.js create mode 100644 animism-align/frontend/app/views/episode/containers/episode.index.js create mode 100644 animism-align/frontend/app/views/episode/containers/episode.new.js create mode 100644 animism-align/frontend/app/views/episode/episode.container.js create mode 100644 animism-align/frontend/app/views/episode/episode.css create mode 100644 animism-align/frontend/app/views/episode/episode.reducer.js (limited to 'animism-align/frontend/app/views/episode') diff --git a/animism-align/frontend/app/views/episode/components/episode.form.js b/animism-align/frontend/app/views/episode/components/episode.form.js new file mode 100644 index 0000000..81446f1 --- /dev/null +++ b/animism-align/frontend/app/views/episode/components/episode.form.js @@ -0,0 +1,161 @@ +import React, { Component } from 'react' +import { Link } from 'react-router-dom' + +import { capitalize } from 'app/utils' + +import { TextInput, LabelDescription, Select, TextArea, Checkbox, SubmitButton, Loader } from 'app/common' + +const newEpisode = () => ({ + title: '', + settings: {}, +}) + +export default class EpisodeForm extends Component { + state = { + title: "", + submitTitle: "", + data: { ...newEpisode() }, + errorFields: new Set([]), + } + + constructor(props) { + super(props) + this.handleKeyDown = this.handleKeyDown.bind(this) + this.handleSelect = this.handleSelect.bind(this) + this.handleChange = this.handleChange.bind(this) + this.handleSettingsChange = this.handleSettingsChange.bind(this) + this.handleSettingsChangeEvent = this.handleSettingsChangeEvent.bind(this) + this.handleSubmit = this.handleSubmit.bind(this) + } + + componentDidMount() { + const { data, isNew } = this.props + const title = isNew ? 'New episode' : 'Editing ' + data.title + const submitTitle = isNew ? "Add Episode" : "Save Changes" + this.setState({ + title, + submitTitle, + errorFields: new Set([]), + data: { + ...newEpisode(), + ...data + }, + }) + window.addEventListener('keydown', this.handleKeyDown) + } + + componentWillUnmount() { + window.removeEventListener('keydown', this.handleKeyDown) + } + + handleKeyDown(e) { + // console.log(e, e.keyCode) + if ((e.ctrlKey || e.metaKey) && e.keyCode === 83) { + if (e) { + e.preventDefault() + } + this.handleSubmit() + } + } + + handleChange(e) { + const { name, value } = e.target + this.handleSelect(name, value) + } + + handleSelect(name, value) { + const { errorFields } = this.state + if (errorFields.has(name)) { + errorFields.delete(name) + } + this.setState({ + errorFields, + data: { + ...this.state.data, + [name]: value, + } + }) + } + + handleSettingsChangeEvent(e) { + const { name, value } = e.target + this.handleSettingsChange(name, value) + } + + handleSettingsChange(name, value) { + console.log(name, value) + if (name !== 'multiple') { + value = { [name]: value } + } + this.setState({ + data: { + ...this.state.data, + settings: { + ...this.state.data.settings, + ...value, + } + } + }) + } + + handleSubmit(e) { + if (e) { + e.preventDefault() + } + const { isNew, onSubmit } = this.props + const { data } = this.state + const requiredKeys = "title episode_number".split(" ") + const validKeys = "title settings".split(" ") + const validData = validKeys.reduce((a,b) => { a[b] = data[b]; return a }, {}) + const errorFields = requiredKeys.filter(key => !validData[key]) + if (errorFields.length) { + console.log('error', errorFields, validData) + this.setState({ errorFields: new Set(errorFields) }) + } else { + if (isNew) { + // + } else { + validData.id = data.id + } + console.log('submit', validData) + onSubmit(validData) + } + } + + render() { + const { isNew } = this.props + const { title, submitTitle, errorFields, data } = this.state + // console.log(data) + return ( +
+

{title}

+
+ + + + {!!errorFields.size && + + } + +
+ ) + } +} diff --git a/animism-align/frontend/app/views/episode/components/episode.menu.js b/animism-align/frontend/app/views/episode/components/episode.menu.js new file mode 100644 index 0000000..445084b --- /dev/null +++ b/animism-align/frontend/app/views/episode/components/episode.menu.js @@ -0,0 +1,57 @@ +import React, { Component } from 'react' +import { Route } from 'react-router-dom' +import { connect } from 'react-redux' + +import { history } from 'app/store' +import actions from 'app/actions' +import { MenuButton } from 'app/common' + +const mapStateToProps = state => ({ + episode: state.episode, +}) + +export default class EpisodeMenu extends Component { + render() { + return ( +
+ + + + +
+ ) + } +} + +const EpisodeIndexMenu = () => ([ + , +]) + +const EpisodeShowMenu = connect(mapStateToProps)((props) => ([ + , + , + { + const { res: episode } = props.episode.show + if (confirm("Really delete this episode?")) { + actions.episode.destroy(episode).then(() => { + history.push('/episode/') + }) + } + }} />, +])) + +const EpisodeNewMenu = (props) => ([ + , +]) + +const EpisodeEditMenu = connect(mapStateToProps)((props) => ([ + , + { + const { res: episode } = props.episode.show + if (confirm("Really delete this episode?")) { + actions.episode.destroy(episode).then(() => { + history.push('/episode/') + }) + } + }} />, +])) diff --git a/animism-align/frontend/app/views/episode/containers/episode.edit.js b/animism-align/frontend/app/views/episode/containers/episode.edit.js new file mode 100644 index 0000000..4d7e270 --- /dev/null +++ b/animism-align/frontend/app/views/episode/containers/episode.edit.js @@ -0,0 +1,52 @@ +import React, { Component } from 'react' +import { connect } from 'react-redux' + +import { history } from 'app/store' +import actions from 'app/actions' + +import { Loader } from 'app/common' + +import EpisodeForm from '../components/episode.form' +import EpisodeMenu from '../components/episode.menu' + +class EpisodeEdit extends Component { + componentDidMount() { + console.log(this.props.match.params.id) + actions.episode.show(this.props.match.params.id) + } + + handleSubmit(data) { + actions.episode.update(data) + .then(response => { + // response + console.log(response) + history.push('/episode/') + }) + } + + render() { + const { show } = this.props.episode + if (show.loading || !show.res) { + return ( +
+ +
+ ) + } + return ( +
+ + +
+ ) + } +} + +const mapStateToProps = state => ({ + episode: state.episode, +}) + +export default connect(mapStateToProps)(EpisodeEdit) diff --git a/animism-align/frontend/app/views/episode/containers/episode.index.js b/animism-align/frontend/app/views/episode/containers/episode.index.js new file mode 100644 index 0000000..0cf3b6c --- /dev/null +++ b/animism-align/frontend/app/views/episode/containers/episode.index.js @@ -0,0 +1,71 @@ +import React, { Component } from 'react' +import { Link } from 'react-router-dom' +import { connect } from 'react-redux' + +import { Loader } from 'app/common' +import actions from 'app/actions' + +import EpisodeMenu from '../components/episode.menu' + +// const { result, collectionLookup } = this.props + +class EpisodeIndex extends Component { + componentDidMount() { + this.fetch() + } + + fetch() { + actions.episode.index() + } + + render() { + const { loading, lookup, order } = this.props.episode.index + if (loading) { + return ( +
+ +
+ ) + } + if (!lookup || !order.length) { + return ( +
+
+ +
+

Episodes

+

+ {"No episodes"} +

+
+
+
+ ) + } + return ( +
+
+ +
+

Episodes

+ {order.map(id => ( +
+ {'Episode '}{lookup[id].episode_number}{': '} + + lookup[id].title + +
+ ))} +
+
+ {order.length >= 50 && } +
+ ) + } +} + +const mapStateToProps = state => ({ + episode: state.episode, +}) + +export default connect(mapStateToProps)(EpisodeIndex) diff --git a/animism-align/frontend/app/views/episode/containers/episode.new.js b/animism-align/frontend/app/views/episode/containers/episode.new.js new file mode 100644 index 0000000..42e9837 --- /dev/null +++ b/animism-align/frontend/app/views/episode/containers/episode.new.js @@ -0,0 +1,62 @@ +import React, { Component } from 'react' +import { Link } from 'react-router-dom' +import { connect } from 'react-redux' + +import { history } from 'app/store' +import actions from 'app/actions' + +import EpisodeForm from '../components/episode.form' +import EpisodeMenu from '../components/episode.menu' + +class EpisodeNew extends Component { + state = { + loading: true, + initialData: {}, + } + + componentDidMount() { + this.setState({ loading: false }) + } + + handleSubmit(data) { + console.log(data) + actions.episode.create(data) + .then(res => { + console.log(res) + if (res.res && res.res.id) { + history.push('/episode/') + } + }) + .catch(err => { + console.error('error') + }) + } + + render() { + if (this.state.loading) { + return ( +
+ ) + } + return ( +
+ + +
+ ) + } +} + +const mapStateToProps = state => ({ + episode: state.episode, +}) + +const mapDispatchToProps = dispatch => ({ + // uploadActions: bindActionCreators({ ...uploadActions }, dispatch), +}) + +export default connect(mapStateToProps, mapDispatchToProps)(EpisodeNew) diff --git a/animism-align/frontend/app/views/episode/episode.container.js b/animism-align/frontend/app/views/episode/episode.container.js new file mode 100644 index 0000000..62363ec --- /dev/null +++ b/animism-align/frontend/app/views/episode/episode.container.js @@ -0,0 +1,26 @@ +import React, { Component } from 'react' +import { Route } from 'react-router-dom' +import { connect } from 'react-redux' + +import './episode.css' + +import EpisodeIndex from './containers/episode.index' +import EpisodeNew from './containers/episode.new' +import EpisodeEdit from './containers/episode.edit' + +class Container extends Component { + render() { + return ( +
+ + + +
+ ) + } +} +const mapStateToProps = state => ({ + episode: state.episode, +}) + +export default connect(mapStateToProps)(Container) diff --git a/animism-align/frontend/app/views/episode/episode.css b/animism-align/frontend/app/views/episode/episode.css new file mode 100644 index 0000000..2493315 --- /dev/null +++ b/animism-align/frontend/app/views/episode/episode.css @@ -0,0 +1,6 @@ +.episodeContainer { +} +.episode-index { + margin-top: 1rem; +} + diff --git a/animism-align/frontend/app/views/episode/episode.reducer.js b/animism-align/frontend/app/views/episode/episode.reducer.js new file mode 100644 index 0000000..4c74218 --- /dev/null +++ b/animism-align/frontend/app/views/episode/episode.reducer.js @@ -0,0 +1,18 @@ +// import * as types from 'app/types' + +import { crudState, crudReducer } from 'app/api/crud.reducer' + +const initialState = crudState('episode', { + options: {}, +}) + +const reducer = crudReducer('episode') + +export default function episodeReducer(state = initialState, action) { + // console.log(action.type, action) + state = reducer(state, action) + switch (action.type) { + default: + return state + } +} -- cgit v1.2.3-70-g09d2