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) {
e.preventDefault()
console.log('Search term: ', this.state.search)
this.setState({ search: '' })
}
@ -81,7 +80,8 @@ class App extends React.Component {
e.preventDefault()
const 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
// 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
@ -105,7 +105,7 @@ class App extends React.Component {
console.log('Received malformed data for added task: ', data)
}
} catch (e) {
alert('Failed to add task: ', e)
alert(`Failed to add task: ${e}`)
}
}
async deleteTask(id) {
@ -122,23 +122,48 @@ class App extends React.Component {
alert('Failed to delete task: ', e)
}
}
completeTask(id) {
const otherTasks = this.state.tasks.filter(t => t.id !== id)
const completedTask = this.state.tasks.find(t => t.id === id)
completedTask.completed = !completedTask.completed
console.log(completedTask)
this.setState({ tasks: otherTasks.concat(completedTask) })
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)
this.setState({ tasks: otherTasks.concat(data) })
console.log(`Completed task:`, data)
} else {
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()
this.setState({
projects: this.state.projects.concat({
id: this.state.projects.length,
name: this.state.newProject,
}),
newProject: '',
})
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({
projects: this.state.projects.concat(data),
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) {
@ -146,9 +171,34 @@ class App extends React.Component {
this.navigate('project')
}
async deleteProject(id) {
//this.setState({ projects: this.state.projects.filter(p => p.id !== id) })
await axios.post(`/api/projects/${id}/delete`)
// TODO: some delete success function
const project = this.state.projects.find(p => p.id === id)
if (!project.id) {
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() {
@ -182,6 +232,7 @@ class App extends React.Component {
addTask={this.addTask}
completeTask={this.completeTask}
deleteTask={this.deleteTask}
navigate={this.navigate}
{...this.state}
/>
)
@ -205,13 +256,18 @@ class App extends React.Component {
)
}
render() {
console.log(this.state)
const completed = this.state.tasks.filter(task => task.completed === true)
const filtered = this.state.tasks.filter(
task =>
task.completed !== true &&
this.state.search === task.desc.slice(0, this.state.search.length),
)
//console.log(this.state)
// TODO refactor task filtering
const completed =
(this.state.tasks && this.state.tasks.filter(task => task.completed === true)) || []
const filtered =
(this.state.tasks &&
this.state.tasks.filter(
task =>
task.completed !== true &&
this.state.search === task.desc.slice(0, this.state.search.length),
)) ||
[]
const renderComponent = () =>
this.state.loading

View File

@ -11,22 +11,29 @@ function FilterTask(props) {
value={props.newTask}
onChange={props.handleChange}
/>
{props.component === 'project' ? null : (
{props.component === 'project' ? (
''
) : (
<select
className="form-control custom-select col"
name="selectedProject"
value={props.project.name}
name="selectedProjectId"
value={props.selectedProjectId}
onChange={props.handleChange}
>
<option value='' defaultValue>select project</option>
{props.projects.map(p => (
<option key={p.id} value={p.name}>
{p.name}
</option>
))}
>
<option value="" defaultValue>
select project
</option>
{props.projects &&
props.projects.map(p => (
<option key={p.id} value={p.id}>
{p.name}
</option>
))}
</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>
</li>
)

View File

@ -2,20 +2,23 @@ import React from 'react'
import Tasks from './tasks'
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 =
props.projects.find(p => p.id === props.selectedProjectId).name || 'Default'
const filtered = props.tasks.filter(
t => t.projectId === props.selectedProjectId && !t.completed
)
props.projects.find(p => p.id === props.selectedProjectId)['name'] || 'Default'
const filtered = props.tasks.filter(t => t.projectId === props.selectedProjectId && !t.completed)
const completed = props.tasks.filter(
t =>
(t.projectId === props.selectedProjectId && t.completed === true) || null
t => (t.projectId === props.selectedProjectId && t.completed === true) || null,
)
return (
<div>
<h2>{projectName}</h2>
<Tasks {...props} filtered={filtered} completed={completed} />
<Tasks filtered={filtered} completed={completed} {...props} />
</div>
)
}

View File

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

View File

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

View File

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