378 lines
10 KiB
JavaScript
378 lines
10 KiB
JavaScript
import React from 'react'
|
|
import axios from 'axios'
|
|
|
|
import {
|
|
About,
|
|
Tasks,
|
|
UpdateTask,
|
|
Profile,
|
|
Projects,
|
|
Project,
|
|
Navbar,
|
|
} from './components'
|
|
|
|
const api = process.env.REACT_APP_API || 'http://localhost:1337'
|
|
//console.log(process.env)
|
|
console.log('Using api at ' + api)
|
|
|
|
const defaultState = {
|
|
user: { name: 'Scott' },
|
|
loading: true,
|
|
tasks: [],
|
|
search: '',
|
|
newTask: '',
|
|
newProject: '',
|
|
project: {},
|
|
projects: [],
|
|
component: 'projects',
|
|
}
|
|
|
|
class App extends React.Component {
|
|
constructor() {
|
|
super()
|
|
this.state = defaultState
|
|
this.navigate = this.navigate.bind(this)
|
|
this.handleChange = this.handleChange.bind(this)
|
|
this.handleSubmit = this.handleSubmit.bind(this)
|
|
this.addTask = this.addTask.bind(this)
|
|
this.deleteTask = this.deleteTask.bind(this)
|
|
this.completeTask = this.completeTask.bind(this)
|
|
this.createProject = this.createProject.bind(this)
|
|
this.selectProject = this.selectProject.bind(this)
|
|
this.selectTask = this.selectTask.bind(this)
|
|
this.updateTask = this.updateTask.bind(this)
|
|
this.deleteProject = this.deleteProject.bind(this)
|
|
}
|
|
|
|
async fetchTasks() {
|
|
try {
|
|
const { data } = await axios.get(api + '/api/tasks')
|
|
return data
|
|
} catch (error) {
|
|
console.log(error)
|
|
this.setState({ error })
|
|
}
|
|
}
|
|
|
|
async fetchProjects() {
|
|
try {
|
|
const { data } = await axios.get(api + '/api/projects')
|
|
return data
|
|
} catch (error) {
|
|
console.log(error)
|
|
this.setState({ error })
|
|
}
|
|
}
|
|
|
|
async fetchData() {
|
|
const tasks = await this.fetchTasks()
|
|
const projects = await this.fetchProjects()
|
|
await this.setState({ loading: false, tasks, projects })
|
|
}
|
|
|
|
componentDidMount() {
|
|
this.fetchData()
|
|
}
|
|
|
|
navigate(component) {
|
|
this.setState({ component })
|
|
}
|
|
|
|
handleChange(e) {
|
|
this.setState({ [e.target.name]: e.target.value })
|
|
}
|
|
handleSubmit(e) {
|
|
e.preventDefault()
|
|
this.setState({ search: '' })
|
|
}
|
|
|
|
async addTask(e) {
|
|
e.preventDefault()
|
|
const newTask = {
|
|
desc: this.state.newTask,
|
|
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
|
|
}
|
|
if (newTask.desc === '') {
|
|
alert('Task description is empty.')
|
|
return
|
|
} else if (!newTask.projectName) {
|
|
alert('Select a project for new task.')
|
|
return
|
|
}
|
|
try {
|
|
console.log('Adding task: ', newTask)
|
|
const { data, error } = await axios.post(api + '/api/tasks', newTask)
|
|
if (error) {
|
|
alert('Received error when adding task: ', error)
|
|
} else if (data.desc && data.projectId) {
|
|
console.log('Successfully added task:', data)
|
|
this.setState({ tasks: this.state.tasks.concat(data), newTask: '' })
|
|
} else {
|
|
console.log('Received malformed data for added task: ', data)
|
|
}
|
|
} catch (e) {
|
|
alert(`Failed to add task: ${e}`)
|
|
}
|
|
}
|
|
|
|
async updateTask(e, updatedTask) {
|
|
e.preventDefault()
|
|
console.log('updatedTask:', updatedTask)
|
|
const oldTask = this.state.tasks.find(
|
|
t => t.id === this.state.selectedTaskId,
|
|
)
|
|
if (JSON.stringify(oldTask) === JSON.stringify(updatedTask)) return
|
|
try {
|
|
const { data, error } = await axios.put(
|
|
api + `/api/tasks/${oldTask.id}`,
|
|
updatedTask,
|
|
)
|
|
if (error) {
|
|
alert('Received error when updating task: ', error)
|
|
} else if (data.desc && data.projectId) {
|
|
const oldTasks = this.state.tasks.filter(t => t.id !== updatedTask.id)
|
|
this.setState({ tasks: oldTasks.concat(updatedTask) })
|
|
console.log('Successfully updated task:', data)
|
|
} else {
|
|
console.log('Received malformed data when updating task: ', data)
|
|
}
|
|
} catch (e) {
|
|
alert(`Updating task failed: ${e}`)
|
|
}
|
|
}
|
|
|
|
async deleteTask(id) {
|
|
this.setState({ tasks: this.state.tasks.filter(t => t.id !== id) })
|
|
try {
|
|
const { data, error } = await axios.post(api + `/api/tasks/${id}/delete`)
|
|
if (error) {
|
|
alert('Received error when deleting task: ', error)
|
|
} else {
|
|
console.log('Successfully deleted task: ', data)
|
|
this.setState({
|
|
tasks: this.state.tasks.filter(t => t.id !== data.id),
|
|
newTask: '',
|
|
})
|
|
}
|
|
} catch (e) {
|
|
alert('Failed to delete task: ', e)
|
|
}
|
|
}
|
|
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}`)
|
|
}
|
|
}
|
|
selectTask(id) {
|
|
this.setState({ selectedTaskId: id })
|
|
this.navigate('task')
|
|
}
|
|
|
|
async createProject(e) {
|
|
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({
|
|
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) {
|
|
this.setState({ selectedProjectId })
|
|
this.navigate('project')
|
|
}
|
|
async deleteProject(id) {
|
|
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() {
|
|
return <div>Loading...</div>
|
|
}
|
|
renderError() {
|
|
return <div>Something went wrong. Please try again.</div>
|
|
}
|
|
renderProfile() {
|
|
return <Profile {...this.state} />
|
|
}
|
|
renderProjects() {
|
|
return (
|
|
<Projects
|
|
navigate={this.navigate}
|
|
handleChange={this.handleChange}
|
|
createProject={this.createProject}
|
|
selectProject={this.selectProject}
|
|
deleteProject={this.deleteProject}
|
|
{...this.state}
|
|
/>
|
|
)
|
|
}
|
|
renderProject() {
|
|
return (
|
|
<Project
|
|
handleChange={this.handleChange}
|
|
createProject={this.createProject}
|
|
selectProject={this.selectProject}
|
|
selectTask={this.selectTask}
|
|
deleteProject={this.deleteProject}
|
|
addTask={this.addTask}
|
|
completeTask={this.completeTask}
|
|
deleteTask={this.deleteTask}
|
|
navigate={this.navigate}
|
|
{...this.state}
|
|
/>
|
|
)
|
|
}
|
|
renderAbout() {
|
|
return <About {...this.state} />
|
|
}
|
|
renderTasks(filtered, completed) {
|
|
return (
|
|
<Tasks
|
|
handleChange={this.handleChange}
|
|
handleSubmit={this.handleSubmit}
|
|
addTask={this.addTask}
|
|
completeTask={this.completeTask}
|
|
deleteTask={this.deleteTask}
|
|
filtered={filtered}
|
|
completed={completed}
|
|
selectTask={this.selectTask}
|
|
selectProject={this.selectProject}
|
|
{...this.state}
|
|
/>
|
|
)
|
|
}
|
|
renderUpdateTask() {
|
|
return (
|
|
<UpdateTask
|
|
handleChange={this.handleChange}
|
|
updateTask={this.updateTask}
|
|
navigate={this.navigate}
|
|
{...this.state}
|
|
/>
|
|
)
|
|
}
|
|
render() {
|
|
//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
|
|
? this.renderLoading()
|
|
: this.state.component === 'tasks'
|
|
? this.renderTasks(filtered, completed)
|
|
: this.state.component === 'task'
|
|
? this.renderUpdateTask()
|
|
: this.state.component === 'about'
|
|
? this.renderAbout()
|
|
: this.state.component === 'profile'
|
|
? this.renderProfile()
|
|
: this.state.component === 'projects'
|
|
? this.renderProjects()
|
|
: this.state.component === 'project'
|
|
? this.renderProject()
|
|
: this.renderError()
|
|
|
|
return (
|
|
<div>
|
|
<Navbar
|
|
handleChange={this.handleChange}
|
|
navigate={this.navigate}
|
|
{...this.state}
|
|
/>
|
|
{renderComponent()}
|
|
</div>
|
|
)
|
|
}
|
|
}
|
|
|
|
export default App
|