summaryrefslogtreecommitdiff
path: root/animism-align/frontend/app/views
diff options
context:
space:
mode:
Diffstat (limited to 'animism-align/frontend/app/views')
-rw-r--r--animism-align/frontend/app/views/auth/auth.gate.js1
-rw-r--r--animism-align/frontend/app/views/dashboard/dashboard.container.js127
-rw-r--r--animism-align/frontend/app/views/dashboard/dashboard.css52
-rw-r--r--animism-align/frontend/app/views/episode/components/episode.form.js48
-rw-r--r--animism-align/frontend/app/views/episode/components/episode.menu.js6
-rw-r--r--animism-align/frontend/app/views/episode/containers/episode.edit.js20
-rw-r--r--animism-align/frontend/app/views/episode/containers/episode.new.js24
-rw-r--r--animism-align/frontend/app/views/episode/episode.container.js6
-rw-r--r--animism-align/frontend/app/views/episode/episode.css6
-rw-r--r--animism-align/frontend/app/views/nav/header.component.js25
-rw-r--r--animism-align/frontend/app/views/nav/nav.css7
-rw-r--r--animism-align/frontend/app/views/project/components/project.form.js7
-rw-r--r--animism-align/frontend/app/views/project/components/project.menu.js6
-rw-r--r--animism-align/frontend/app/views/project/containers/project.edit.js2
-rw-r--r--animism-align/frontend/app/views/project/containers/project.new.js2
-rw-r--r--animism-align/frontend/app/views/project/project.container.js2
-rw-r--r--animism-align/frontend/app/views/site/site.actions.js1
17 files changed, 308 insertions, 34 deletions
diff --git a/animism-align/frontend/app/views/auth/auth.gate.js b/animism-align/frontend/app/views/auth/auth.gate.js
index e07316e..5e759a6 100644
--- a/animism-align/frontend/app/views/auth/auth.gate.js
+++ b/animism-align/frontend/app/views/auth/auth.gate.js
@@ -21,6 +21,7 @@ class AuthGate extends Component {
load() {
if (!this.props.user.id) return
+ this.props.onAuthenticate()
}
render() {
diff --git a/animism-align/frontend/app/views/dashboard/dashboard.container.js b/animism-align/frontend/app/views/dashboard/dashboard.container.js
new file mode 100644
index 0000000..6befaae
--- /dev/null
+++ b/animism-align/frontend/app/views/dashboard/dashboard.container.js
@@ -0,0 +1,127 @@
+import React, { Component } from 'react'
+import { connect } from 'react-redux'
+import { Link } from 'react-router-dom'
+
+import './dashboard.css'
+
+// import actions from 'app/actions'
+import { Loader } from 'app/common'
+import { groupResponseBy } from 'app/utils/api.utils'
+
+class DashboardContainer extends Component {
+ state = {
+ ready: false,
+ episodes: {},
+ venues: {},
+ }
+
+ constructor(props) {
+ super(props)
+ }
+
+ componentDidMount() {
+ this.build()
+ }
+
+ componentDidUpdate(prevProps) {
+ if (
+ !this.state.ready ||
+ (this.props.project !== prevProps.project) ||
+ (this.props.episode !== prevProps.episode) ||
+ (this.props.venue !== prevProps.venue)
+ ) {
+ this.build()
+ }
+ }
+
+ build() {
+ if (this.props.loading) return
+ const episodes = groupResponseBy(this.props.episodes, 'project_id')
+ const venues = groupResponseBy(this.props.venues, 'project_id')
+ this.setState({ ready: true, episodes, venues })
+ }
+
+ render() {
+ const { loading, projects } = this.props
+ const { episodes, venues, ready } = this.state
+ if (loading || !ready) {
+ return (
+ <div className='dashboard'>
+ <Loader />
+ </div>
+ )
+ }
+ return (
+ <div className='dashboard'>
+ <div className='project-top'>
+ <div className='row project-heading'>
+ <div className='title'>
+ <h1>Projects</h1>
+ </div>
+ <div className='links'>
+ <Link to={`/project/new/`}>+ Add Project</Link>
+ </div>
+ </div>
+ </div>
+ {projects.order.map((project_id) => {
+ const project = projects.lookup[project_id]
+ console.log(project)
+ return (
+ <div className='project-entry' key={project.id}>
+ <div className='row project-heading'>
+ <div className='title'>
+ <h2>{project.title}</h2>
+ {project.is_live && <span className='published'>{"(published)"}</span>}
+ </div>
+ <div className='links'>
+ <Link to={`/project/${project.id}/new-episode/`}>+ Add Episode</Link>
+ <Link to={`/project/${project.id}/venues/`}>Venues</Link>
+ <Link to={`/project/${project.id}/edit/`}>Settings</Link>
+ </div>
+ </div>
+ {(episodes[project.id] || []).map(episode => (
+ <div className='episode-entry' key={episode.id}>
+ <div className='row'>
+ <div className='title'>
+ <Link to={`/editor/${episode.id}/viewer/`}>
+ {'Episode #' + episode.episode_number}
+ </Link>
+ {episode.is_live && <span className='published'>{"(published)"}</span>}
+ </div>
+ <div className='links'>
+ <Link to={`/editor/${episode.id}/viewer/`}>Viewer</Link>
+ <Link to={`/editor/${episode.id}/timeline/`}>Timeline</Link>
+ <Link to={`/editor/${episode.id}/transcript/`}>Transcript</Link>
+ <Link to={`/episode/${episode.id}/edit/`}>Settings</Link>
+ </div>
+ </div>
+ <div className='row'>
+ <div className='title'>
+ <i>{episode.title}</i>
+ </div>
+ </div>
+ </div>
+ ))}
+ </div>
+ )
+ })}
+ </div>
+ )
+ }
+}
+
+const mapStateToProps = state => ({
+ loading: (
+ !state.project.index.lookup ||
+ !state.episode.index.lookup ||
+ !state.venue.index.lookup ||
+ state.project.index.loading ||
+ state.episode.index.loading ||
+ state.venue.index.loading
+ ),
+ projects: state.project.index,
+ episodes: state.episode.index,
+ venues: state.venue.index,
+})
+
+export default connect(mapStateToProps)(DashboardContainer)
diff --git a/animism-align/frontend/app/views/dashboard/dashboard.css b/animism-align/frontend/app/views/dashboard/dashboard.css
new file mode 100644
index 0000000..34bcb66
--- /dev/null
+++ b/animism-align/frontend/app/views/dashboard/dashboard.css
@@ -0,0 +1,52 @@
+.dashboard {
+ padding: 1.5rem;
+}
+
+.project-top {
+ padding: 1rem;
+}
+.project-top h1 {
+ margin: 0;
+}
+
+.dashboard .project-entry {
+ padding: 1rem;
+ margin: 0 0 1rem 0;
+ background: #111;
+}
+
+.dashboard .episode-entry {
+ margin-bottom: 0.5rem;
+}
+.dashboard .project-entry .row.project-heading {
+ margin-bottom: 1rem;
+}
+.dashboard h2 {
+ display: inline-block;
+ margin: 0;
+}
+.dashboard .title {
+ width: 25rem;
+ color: #fff;
+ text-overflow: ellipsis;
+ white-space: pre;
+ overflow: hidden;
+}
+.dashboard .row {
+ margin-bottom: 0.25rem;
+ align-items: center;
+}
+.dashboard .links a {
+ margin-right: 0.5rem;
+ text-decoration: none;
+}
+.dashboard .links a:hover {
+ text-decoration: underline;
+}
+.dashboard .published {
+ color: #888;
+ margin-left: 0.5rem;
+}
+.dashboard i {
+ color: #bbb;
+} \ No newline at end of file
diff --git a/animism-align/frontend/app/views/episode/components/episode.form.js b/animism-align/frontend/app/views/episode/components/episode.form.js
index 63d55e7..c60d3b2 100644
--- a/animism-align/frontend/app/views/episode/components/episode.form.js
+++ b/animism-align/frontend/app/views/episode/components/episode.form.js
@@ -20,6 +20,7 @@ const newEpisode = () => ({
export default class EpisodeForm extends Component {
state = {
+ project_id: 0,
title: "",
submitTitle: "",
data: { ...newEpisode() },
@@ -37,8 +38,10 @@ export default class EpisodeForm extends Component {
}
componentDidMount() {
- const { data, isNew } = this.props
- const title = isNew ? 'New episode' : 'Editing ' + data.title
+ const { data, isNew, project } = this.props
+ const title = isNew
+ ? 'Add episode to project ' + project.title
+ : 'Editing Episode ' + data.episode_number + ": " + data.title
const submitTitle = isNew ? "Add Episode" : "Save Changes"
this.setState({
title,
@@ -112,8 +115,8 @@ export default class EpisodeForm extends Component {
}
const { isNew, onSubmit } = this.props
const { data } = this.state
- const requiredKeys = "episode_number release_date".split(" ")
- const validKeys = "title episode_number release_date is_live settings".split(" ")
+ const requiredKeys = "project_id episode_number release_date".split(" ")
+ const validKeys = "project_id title episode_number release_date is_live settings".split(" ")
const validData = validKeys.reduce((a,b) => { a[b] = data[b]; return a }, {})
if (!data.title) {
data.title = "TBD"
@@ -141,10 +144,8 @@ export default class EpisodeForm extends Component {
<div className='form'>
<h1>{title}</h1>
<form onSubmit={this.handleSubmit}>
- <SubmitButton
- title={submitTitle}
- onClick={this.handleSubmit}
- />
+ <h2>Episode information</h2>
+
<TextInput
title="Title"
name="title"
@@ -176,6 +177,37 @@ export default class EpisodeForm extends Component {
onChange={this.handleSelect}
/>
+ <h2>Build configuration</h2>
+
+ <TextInput
+ title="HTML title"
+ name="page_title"
+ placeholder="HTML title tag"
+ data={data.settings}
+ onChange={this.handleSettingsChangeEvent}
+ autoComplete="off"
+ />
+
+ <TextArea
+ title="Page description"
+ name="page_desc"
+ placeholder="Social media / search engine snippet"
+ data={data.settings}
+ onChange={this.handleSettingsChangeEvent}
+ autoComplete="off"
+ />
+
+ <TextInput
+ title="Build folder"
+ name="url_path"
+ placeholder="Folder to build into, e.g. episode1"
+ data={data.settings}
+ onChange={this.handleSettingsChangeEvent}
+ autoComplete="off"
+ />
+
+ <h2>Credits</h2>
+
<TextArea
title="Artists"
name="artists"
diff --git a/animism-align/frontend/app/views/episode/components/episode.menu.js b/animism-align/frontend/app/views/episode/components/episode.menu.js
index 445084b..4b75ec1 100644
--- a/animism-align/frontend/app/views/episode/components/episode.menu.js
+++ b/animism-align/frontend/app/views/episode/components/episode.menu.js
@@ -28,7 +28,7 @@ const EpisodeIndexMenu = () => ([
])
const EpisodeShowMenu = connect(mapStateToProps)((props) => ([
- <MenuButton key='back' name="back" href="/episode/" />,
+ <MenuButton key='back' name="back" href="/" />,
<MenuButton key='edit' name="edit" href={"/episode/" + props.match.params.id + "/edit/"} />,
<MenuButton key='delete' name="delete" onClick={() => {
const { res: episode } = props.episode.show
@@ -41,11 +41,11 @@ const EpisodeShowMenu = connect(mapStateToProps)((props) => ([
]))
const EpisodeNewMenu = (props) => ([
- <MenuButton key='back' name="back" href="/episode/" />,
+ <MenuButton key='back' name="back" href="/" />,
])
const EpisodeEditMenu = connect(mapStateToProps)((props) => ([
- <MenuButton key='back' name="back" href="/episode/" />,
+ <MenuButton key='back' name="back" href="/" />,
<MenuButton key='delete' name="delete" onClick={() => {
const { res: episode } = props.episode.show
if (confirm("Really delete this 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
index 4d7e270..8ba16ae 100644
--- a/animism-align/frontend/app/views/episode/containers/episode.edit.js
+++ b/animism-align/frontend/app/views/episode/containers/episode.edit.js
@@ -10,9 +10,25 @@ import EpisodeForm from '../components/episode.form'
import EpisodeMenu from '../components/episode.menu'
class EpisodeEdit extends Component {
+ state = {
+ project: {},
+ }
componentDidMount() {
+ this.ready()
+ }
+ componentDidUpdate(prevProps) {
+ if (this.props.project.lookup !== prevProps.project.lookup) {
+ this.ready()
+ }
+ }
+ ready() {
+ if (!this.props.project.lookup) return
console.log(this.props.match.params.id)
actions.episode.show(this.props.match.params.id)
+ .then(episode => {
+ const project = this.props.project.lookup[episode.project_id]
+ this.setState({ project })
+ })
}
handleSubmit(data) {
@@ -20,7 +36,7 @@ class EpisodeEdit extends Component {
.then(response => {
// response
console.log(response)
- history.push('/episode/')
+ history.push('/')
})
}
@@ -38,6 +54,7 @@ class EpisodeEdit extends Component {
<EpisodeMenu episodeActions={this.props.episodeActions} />
<EpisodeForm
data={show.res}
+ project={this.state.project}
onSubmit={this.handleSubmit.bind(this)}
/>
</div>
@@ -47,6 +64,7 @@ class EpisodeEdit extends Component {
const mapStateToProps = state => ({
episode: state.episode,
+ project: state.episode.index,
})
export default connect(mapStateToProps)(EpisodeEdit)
diff --git a/animism-align/frontend/app/views/episode/containers/episode.new.js b/animism-align/frontend/app/views/episode/containers/episode.new.js
index 42e9837..2ff279b 100644
--- a/animism-align/frontend/app/views/episode/containers/episode.new.js
+++ b/animism-align/frontend/app/views/episode/containers/episode.new.js
@@ -12,10 +12,26 @@ class EpisodeNew extends Component {
state = {
loading: true,
initialData: {},
+ project: {},
}
-
componentDidMount() {
- this.setState({ loading: false })
+ this.ready()
+ }
+ componentDidUpdate(prevProps) {
+ if (this.props.project.lookup !== prevProps.project.lookup) {
+ this.ready()
+ }
+ }
+ ready() {
+ if (!this.props.project.lookup) return
+ const { project_id } = this.props.match.params
+ const project = this.props.project.lookup[project_id]
+ this.setState({
+ loading: false,
+ initialData: { project_id },
+ project,
+ })
+ console.log(this.props.match.params.project_id)
}
handleSubmit(data) {
@@ -24,7 +40,7 @@ class EpisodeNew extends Component {
.then(res => {
console.log(res)
if (res.res && res.res.id) {
- history.push('/episode/')
+ history.push('/')
}
})
.catch(err => {
@@ -44,6 +60,7 @@ class EpisodeNew extends Component {
<EpisodeForm
isNew
data={this.state.initialData}
+ project={this.state.project}
onSubmit={this.handleSubmit.bind(this)}
/>
</div>
@@ -53,6 +70,7 @@ class EpisodeNew extends Component {
const mapStateToProps = state => ({
episode: state.episode,
+ project: state.project.index,
})
const mapDispatchToProps = dispatch => ({
diff --git a/animism-align/frontend/app/views/episode/episode.container.js b/animism-align/frontend/app/views/episode/episode.container.js
index 62363ec..9cd70f8 100644
--- a/animism-align/frontend/app/views/episode/episode.container.js
+++ b/animism-align/frontend/app/views/episode/episode.container.js
@@ -13,7 +13,6 @@ class Container extends Component {
return (
<div className='episodeContainer'>
<Route exact path='/episode/:id/edit/' component={EpisodeEdit} />
- <Route exact path='/episode/new/' component={EpisodeNew} />
<Route exact path='/episode/' component={EpisodeIndex} />
</div>
)
@@ -24,3 +23,8 @@ const mapStateToProps = state => ({
})
export default connect(mapStateToProps)(Container)
+
+/*
+ // episodes are now added via the project
+ <Route exact path='/episode/new/' component={EpisodeNew} />
+*/ \ No newline at end of file
diff --git a/animism-align/frontend/app/views/episode/episode.css b/animism-align/frontend/app/views/episode/episode.css
index e0747ab..96806da 100644
--- a/animism-align/frontend/app/views/episode/episode.css
+++ b/animism-align/frontend/app/views/episode/episode.css
@@ -7,3 +7,9 @@
.episode-index {
margin-top: 1rem;
}
+
+.episodeContainer .formContainer h2 {
+ color: #888;
+ margin-top: 1.5rem;
+ font-style: italic;
+}
diff --git a/animism-align/frontend/app/views/nav/header.component.js b/animism-align/frontend/app/views/nav/header.component.js
index bc04629..c90e592 100644
--- a/animism-align/frontend/app/views/nav/header.component.js
+++ b/animism-align/frontend/app/views/nav/header.component.js
@@ -14,30 +14,35 @@ function Header(props) {
return null
}
if (props.router.location.pathname.match("/editor")) {
- const { episode_id } = this.props.match.params
+ const episode_id = props.router.location.pathname.split('/')[1]
return (
<header>
- <PlayButton playing={props.playing} />
<div>
- <Link to={`/`}>{'<'}</Link>
+ <PlayButton playing={props.playing} />
+ <Link to="/">Home</Link>
+ </div>
+ <div>
<Link to={`/editor/${episode_id}/timeline/`}>Timeline</Link>
<Link to={`/editor/${episode_id}/transcript/`}>Transcript</Link>
<Link to={`/editor/${episode_id}/media/`}>Media</Link>
<Link to={`/editor/${episode_id}/viewer/`}>Viewer</Link>
- <Link to="/episode/">Episodes</Link>
- <Link to="/venue/">Venues</Link>
</div>
</header>
)
}
return (
<header>
- <PlayButton playing={props.playing} />
<div>
- <Link to="/project/">Projects</Link>
- <Link to="/episode/">Episodes</Link>
- <Link to="/venue/">Venues</Link>
- {props.currentUser.is_admin && <Link to="/users">Users</Link>}
+ <PlayButton playing={props.playing} />
+ {props.router.location.pathname !== '/' && (
+ <Link to="/">Home</Link>
+ )}
+ </div>
+ <div>
+ <span className='salutation'>
+ Hi {props.currentUser.username}
+ </span>
+ {props.currentUser.is_admin && <Link to="/users/">Users</Link>}
<a href="#" onClick={actions.auth.logout}>
Logout
</a>
diff --git a/animism-align/frontend/app/views/nav/nav.css b/animism-align/frontend/app/views/nav/nav.css
index 485ace2..0c9a992 100644
--- a/animism-align/frontend/app/views/nav/nav.css
+++ b/animism-align/frontend/app/views/nav/nav.css
@@ -25,7 +25,7 @@ header > div:first-child {
display: flex;
justify-content: flex-start;
align-items: center;
- padding-left: 1.5rem;
+ padding-left: 0.5rem;
}
header > div:last-child {
padding-right: 1.5rem;
@@ -70,4 +70,9 @@ header a.navbar-brand {
header .username {
cursor: pointer;
+}
+header .salutation {
+ color: #888;
+ font-style: italic;
+ margin: 0 1rem;
} \ No newline at end of file
diff --git a/animism-align/frontend/app/views/project/components/project.form.js b/animism-align/frontend/app/views/project/components/project.form.js
index f4d1749..0393d81 100644
--- a/animism-align/frontend/app/views/project/components/project.form.js
+++ b/animism-align/frontend/app/views/project/components/project.form.js
@@ -110,7 +110,7 @@ export default class ProjectForm extends Component {
}
const { isNew, onSubmit } = this.props
const { data } = this.state
- const requiredKeys = "title is_live".split(" ")
+ const requiredKeys = "title".split(" ")
const validKeys = "title is_live settings".split(" ")
const validData = validKeys.reduce((a,b) => { a[b] = data[b]; return a }, {})
if (!data.title) {
@@ -173,21 +173,24 @@ export default class ProjectForm extends Component {
title="Base URL"
name="base_href"
data={data.settings}
+ placeholder="https://animism.e-flux.com"
onChange={this.handleSettingsChangeEvent}
autoComplete="off"
/>
<TextInput
title="FTP URL"
name="ftp_url"
+ placeholder="ftp://..."
data={data.settings}
onChange={this.handleSettingsChangeEvent}
autoComplete="off"
/>
<TextInput
- title="FTP Base Path"
+ title="FTP Local Path"
name="ftp_base_path"
data={data.settings}
+ placeholder="./data_store/exports/animism"
onChange={this.handleSettingsChangeEvent}
autoComplete="off"
/>
diff --git a/animism-align/frontend/app/views/project/components/project.menu.js b/animism-align/frontend/app/views/project/components/project.menu.js
index a29a451..8e3abea 100644
--- a/animism-align/frontend/app/views/project/components/project.menu.js
+++ b/animism-align/frontend/app/views/project/components/project.menu.js
@@ -28,7 +28,7 @@ const ProjectIndexMenu = () => ([
])
const ProjectShowMenu = connect(mapStateToProps)((props) => ([
- <MenuButton key='back' name="back" href="/project/" />,
+ <MenuButton key='back' name="back" href="/" />,
<MenuButton key='edit' name="edit" href={"/project/" + props.match.params.id + "/edit/"} />,
<MenuButton key='delete' name="delete" onClick={() => {
const { res: project } = props.project.show
@@ -41,11 +41,11 @@ const ProjectShowMenu = connect(mapStateToProps)((props) => ([
]))
const ProjectNewMenu = (props) => ([
- <MenuButton key='back' name="back" href="/project/" />,
+ <MenuButton key='back' name="back" href="/" />,
])
const ProjectEditMenu = connect(mapStateToProps)((props) => ([
- <MenuButton key='back' name="back" href="/project/" />,
+ <MenuButton key='back' name="back" href="/" />,
<MenuButton key='delete' name="delete" onClick={() => {
const { res: project } = props.project.show
if (confirm("Really delete this project?")) {
diff --git a/animism-align/frontend/app/views/project/containers/project.edit.js b/animism-align/frontend/app/views/project/containers/project.edit.js
index dbb384d..b0b7bef 100644
--- a/animism-align/frontend/app/views/project/containers/project.edit.js
+++ b/animism-align/frontend/app/views/project/containers/project.edit.js
@@ -20,7 +20,7 @@ class ProjectEdit extends Component {
.then(response => {
// response
console.log(response)
- history.push('/project/')
+ history.push('/')
})
}
diff --git a/animism-align/frontend/app/views/project/containers/project.new.js b/animism-align/frontend/app/views/project/containers/project.new.js
index c8a2152..149ee67 100644
--- a/animism-align/frontend/app/views/project/containers/project.new.js
+++ b/animism-align/frontend/app/views/project/containers/project.new.js
@@ -24,7 +24,7 @@ class ProjectNew extends Component {
.then(res => {
console.log(res)
if (res.res && res.res.id) {
- history.push('/project/')
+ history.push('/')
}
})
.catch(err => {
diff --git a/animism-align/frontend/app/views/project/project.container.js b/animism-align/frontend/app/views/project/project.container.js
index 0fba8c9..a3701e2 100644
--- a/animism-align/frontend/app/views/project/project.container.js
+++ b/animism-align/frontend/app/views/project/project.container.js
@@ -3,6 +3,7 @@ import { Route } from 'react-router-dom'
import './project.css'
+import EpisodeNew from 'app/views/episode/containers/episode.new'
import ProjectIndex from './containers/project.index'
import ProjectNew from './containers/project.new'
import ProjectEdit from './containers/project.edit'
@@ -11,6 +12,7 @@ export default class Container extends Component {
render() {
return (
<div className='projectContainer'>
+ <Route exact path='/project/:project_id/new-episode/' component={EpisodeNew} />
<Route exact path='/project/:id/edit/' component={ProjectEdit} />
<Route exact path='/project/new/' component={ProjectNew} />
<Route exact path='/project/' component={ProjectIndex} />
diff --git a/animism-align/frontend/app/views/site/site.actions.js b/animism-align/frontend/app/views/site/site.actions.js
index 1c51b53..f5ce861 100644
--- a/animism-align/frontend/app/views/site/site.actions.js
+++ b/animism-align/frontend/app/views/site/site.actions.js
@@ -11,6 +11,7 @@ export const loadSite = () => dispatch => {
actions.episode.index(),
actions.venue.index(),
]).then(() => {
+ console.log('Site ready')
}).catch(err => {
console.error(err)
})