async requests + error handling

This commit is contained in:
data 2019-06-22 13:27:08 -04:00
parent c0f136bee3
commit 07f79c1006
6 changed files with 125 additions and 63 deletions

View File

@ -73,7 +73,6 @@ class App extends React.Component {
} }
handleSubmit(e) { handleSubmit(e) {
e.preventDefault() e.preventDefault()
console.log('Search term: ', this.state.search)
this.setState({ search: '' }) this.setState({ search: '' })
} }
@ -81,7 +80,8 @@ class App extends React.Component {
e.preventDefault() e.preventDefault()
const newTask = { const newTask = {
desc: this.state.newTask, desc: this.state.newTask,
projectName: this.state.selectedProject, projectName: this.state.projects.find(p => p.id === this.state.selectedProjectId)['name'],
// TODO
// the backend expects projectName because it creates the project on the fly if none exists with that name // the backend expects projectName because it creates the project on the fly if none exists with that name
// this feature comes at the price that there can't be two independent projects with the same name // this feature comes at the price that there can't be two independent projects with the same name
// which could be handy if users want to have private projects // which could be handy if users want to have private projects
@ -105,7 +105,7 @@ class App extends React.Component {
console.log('Received malformed data for added task: ', data) console.log('Received malformed data for added task: ', data)
} }
} catch (e) { } catch (e) {
alert('Failed to add task: ', e) alert(`Failed to add task: ${e}`)
} }
} }
async deleteTask(id) { async deleteTask(id) {
@ -122,23 +122,48 @@ class App extends React.Component {
alert('Failed to delete task: ', e) alert('Failed to delete task: ', e)
} }
} }
completeTask(id) { async completeTask(id) {
const task = this.state.tasks.find(t => t.id === id)
if (!task.id) {
alert(`Couldn't find task with id ${id}`)
return
}
try {
task.completed = !task.completed
const { data, error } = await axios.put(api + `/api/tasks/${task.id}`, task)
if (error) {
alert(`Received error when completing task ${task.desc}: ${error}`)
} else if (data.id) {
const otherTasks = this.state.tasks.filter(t => t.id !== id) const otherTasks = this.state.tasks.filter(t => t.id !== id)
const completedTask = this.state.tasks.find(t => t.id === id) this.setState({ tasks: otherTasks.concat(data) })
completedTask.completed = !completedTask.completed console.log(`Completed task:`, data)
console.log(completedTask) } else {
this.setState({ tasks: otherTasks.concat(completedTask) }) alert(`Failed to complete task with id ${id}`)
}
} catch (e) {
alert(`Failed to complete task ${task.desc}: ${e}`)
}
} }
createProject(e) { async createProject(e) {
e.preventDefault() e.preventDefault()
const project = { name: this.state.newProject }
try {
const { data, error } = await axios.post(api + '/api/projects', project)
if (error) {
alert(`Received error when creating project ${project.name}: ${error}`)
} else if (data.id) {
this.setState({ this.setState({
projects: this.state.projects.concat({ projects: this.state.projects.concat(data),
id: this.state.projects.length,
name: this.state.newProject,
}),
newProject: '', newProject: '',
}) })
console.log(`Added project: `, data)
} else {
alert(`Failed to add project '${project}'.`)
}
} catch (e) {
alert(`Failed to add project '${this.state.newProject}'.`)
}
} }
selectProject(selectedProjectId) { selectProject(selectedProjectId) {
@ -146,9 +171,34 @@ class App extends React.Component {
this.navigate('project') this.navigate('project')
} }
async deleteProject(id) { async deleteProject(id) {
//this.setState({ projects: this.state.projects.filter(p => p.id !== id) }) const project = this.state.projects.find(p => p.id === id)
await axios.post(`/api/projects/${id}/delete`) if (!project.id) {
// TODO: some delete success function alert(`Could not find project with id ${id}.`)
return
}
const tasks = this.state.tasks.filter(t => t.projectId === project.id)
if (tasks.length > 0) {
if (window.confirm(`Project ${project.name} has ${tasks.length} tasks. Are you sure?`)) {
this.setState({ tasks: this.state.tasks.filter(t => t.projectId !== project.id) })
console.log(`Removed ${tasks.length} tasks of project '${project.name}'.`)
} else {
return
}
}
try {
const { data, error } = await axios.post(api + `/api/projects/${project.id}/delete`)
if (error) {
alert(`Received error when deleting project ${project.name}: ${error}`)
} else if (data.name) {
console.log('Deleted project:', data)
this.setState({ projects: this.state.projects.filter(p => p.id !== project.id) })
this.navigate('projects')
} else {
alert(`Failed to delete project '${project.name}'.`)
}
} catch (e) {
alert(`Failed to delete project '${project.name}': ${e}`)
}
} }
renderLoading() { renderLoading() {
@ -182,6 +232,7 @@ class App extends React.Component {
addTask={this.addTask} addTask={this.addTask}
completeTask={this.completeTask} completeTask={this.completeTask}
deleteTask={this.deleteTask} deleteTask={this.deleteTask}
navigate={this.navigate}
{...this.state} {...this.state}
/> />
) )
@ -205,13 +256,18 @@ class App extends React.Component {
) )
} }
render() { render() {
console.log(this.state) //console.log(this.state)
const completed = this.state.tasks.filter(task => task.completed === true) // TODO refactor task filtering
const filtered = this.state.tasks.filter( const completed =
(this.state.tasks && this.state.tasks.filter(task => task.completed === true)) || []
const filtered =
(this.state.tasks &&
this.state.tasks.filter(
task => task =>
task.completed !== true && task.completed !== true &&
this.state.search === task.desc.slice(0, this.state.search.length), this.state.search === task.desc.slice(0, this.state.search.length),
) )) ||
[]
const renderComponent = () => const renderComponent = () =>
this.state.loading this.state.loading

View File

@ -11,22 +11,29 @@ function FilterTask(props) {
value={props.newTask} value={props.newTask}
onChange={props.handleChange} onChange={props.handleChange}
/> />
{props.component === 'project' ? null : ( {props.component === 'project' ? (
''
) : (
<select <select
className="form-control custom-select col" className="form-control custom-select col"
name="selectedProject" name="selectedProjectId"
value={props.project.name} value={props.selectedProjectId}
onChange={props.handleChange} onChange={props.handleChange}
> >
<option value='' defaultValue>select project</option> <option value="" defaultValue>
{props.projects.map(p => ( select project
<option key={p.id} value={p.name}> </option>
{props.projects &&
props.projects.map(p => (
<option key={p.id} value={p.id}>
{p.name} {p.name}
</option> </option>
))} ))}
</select> </select>
)} )}
<button className="form-control col-sm-1 btn" type="submit" onClick={props.addTask}>Add</button> <button className="form-control col-sm-1 btn" type="submit" onClick={props.addTask}>
Add
</button>
</form> </form>
</li> </li>
) )

View File

@ -2,20 +2,23 @@ import React from 'react'
import Tasks from './tasks' import Tasks from './tasks'
function Project(props) { function Project(props) {
if (!props.selectedProjectId) {
props.navigate('projects')
return null
}
// TODO re-fetch project list if no project name is found instead of setting 'Default'
const projectName = const projectName =
props.projects.find(p => p.id === props.selectedProjectId).name || 'Default' props.projects.find(p => p.id === props.selectedProjectId)['name'] || 'Default'
const filtered = props.tasks.filter(
t => t.projectId === props.selectedProjectId && !t.completed const filtered = props.tasks.filter(t => t.projectId === props.selectedProjectId && !t.completed)
)
const completed = props.tasks.filter( const completed = props.tasks.filter(
t => t => (t.projectId === props.selectedProjectId && t.completed === true) || null,
(t.projectId === props.selectedProjectId && t.completed === true) || null
) )
return ( return (
<div> <div>
<h2>{projectName}</h2> <h2>{projectName}</h2>
<Tasks {...props} filtered={filtered} completed={completed} /> <Tasks filtered={filtered} completed={completed} {...props} />
</div> </div>
) )
} }

View File

@ -1,12 +1,12 @@
import React from 'react' import React from 'react'
function Projects(props) { function Projects(props) {
console.log('PROJECTS PROPS', props)
return ( return (
<div> <div>
<h3 className="mt-4">Projects</h3> <h3 className="mt-4">Projects</h3>
<ul className="list-group"> <ul className="list-group">
{props.projects.map(project => ( {props.projects &&
props.projects.map(project => (
<li className="list-group-item" key={project.id}> <li className="list-group-item" key={project.id}>
<button <button
className="btn btn-outline-danger mr-1" className="btn btn-outline-danger mr-1"
@ -14,11 +14,7 @@ function Projects(props) {
> >
X X
</button> </button>
{props.selectedProjectId ? null : ( <span onClick={() => props.selectProject(project.id)}>{project.name} </span>
<span onClick={() => props.selectProject(project.id)}>
{project.name}{' '}
</span>
)}
</li> </li>
))} ))}
<form className="" onSubmit={props.createProject}> <form className="" onSubmit={props.createProject}>

View File

@ -11,7 +11,7 @@ const TaskRow = props => {
</button> </button>
<span>{task.desc}</span> <span>{task.desc}</span>
{props.selectedProjectId ? null : ( {props.component === 'project' ? null : (
<button <button
className="btn btn-outline-dark ml-5" className="btn btn-outline-dark ml-5"
onClick={() => props.selectProject(task.projectId)} onClick={() => props.selectProject(task.projectId)}

View File

@ -4,7 +4,7 @@ import FilterTask from './filter-task'
import TaskList from './task-list' import TaskList from './task-list'
function Tasks(props) { function Tasks(props) {
const { tasks } = props const tasks = props.tasks || []
const filtered = props.filtered || [] const filtered = props.filtered || []
const completed = props.completed || [] const completed = props.completed || []