Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"react-addons-transition-group": "^15.4.1",
"react-block-ui": "^0.2.2",
"react-bootstrap": "^0.30.7",
"react-date-picker": "^5.3.28",
"react-dom": "^15.3.2",
"react-fa": "^4.1.2",
"react-fontawesome": "^1.4.0",
Expand Down
31 changes: 31 additions & 0 deletions src/api/Foogl.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,37 @@ class FooglApi {
return error;
});
}

static updateTimeEntry(timeEntry) {
const body = JSON.stringify({
id: timeEntry.id,
title: timeEntry.title,
user_id: timeEntry.user_id,
project_id: timeEntry.project_id,
time_start: timeEntry.time_start,
time_end: timeEntry.time_end
})

return fetch(`${API_URL}/time_entries/${timeEntry.id}`, {
method: 'patch',
body
})
.then(response => {
return response.json();
})
.catch(error => {
return error;
});
}

static deleteTimeEntry(timeEntryId) {
return fetch(`${API_URL}/time_entries/${timeEntryId}`, { method: 'delete' })
.then(response => {
return response.json();
}).catch(error => {
return error;
});
}
}

export default FooglApi;
5 changes: 5 additions & 0 deletions src/layout/utils.scss
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,8 @@ a.nostyle:visited {
color: inherit;
cursor: auto;
}

.hover-pointer {
cursor: pointer;
cursor: hand;
}
157 changes: 136 additions & 21 deletions src/pages/Dashboard/Components/TimeEntry/TimeEntry.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,140 @@
import React, { Component } from 'react';
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { Col, Popover, OverlayTrigger, FormGroup,
ControlLabel, Modal, Button } from 'react-bootstrap'
import Icon from 'react-fa'
import moment from 'moment'
import { DateField, Calendar, TransitionView } from 'react-date-picker'
import 'react-date-picker/index.css'

import { deleteTimeEntry, updateTimeEntry } from '~/src/store/actions';

import { connect } from 'react-redux';
import {Col} from 'react-bootstrap';
import './TimeEntry.scss';

class TimeEntry extends Component {

constructor(props) {
super(props);

this.state = {
showModal: false
}
}

toggleShowModal() {
this.setState({ showModal: !this.state.showModal });
}

deleteTimeEntry() {
this.props.deleteTimeEntry(this.props.entry.id);
this.toggleShowModal();
}

onChange(dateString, attribute) {
let timeEntry = this.props.entry
timeEntry[attribute] = moment(dateString, 'DD-MM-YYYY HH:mm:ss').format()
this.props.updateTimeEntry(timeEntry)
}

render () {
const { showModal } = this.state;
const toggleShowModal = ::this.toggleShowModal
const deleteTimeEntry = ::this.deleteTimeEntry
// const onChange = (dateString, attribute) => {
// let timeEntry = this.props.entry
// timeEntry[attribute] = moment(dateString, 'DD-MM-YYYY HH:mm:ss').format()
// this.props.updateTimeEntry(timeEntry)
// }

const editPopover = (
<Popover title="Edit Time Entry">
<form>
<FormGroup controlId="formControlStart">
<ControlLabel>Start:</ControlLabel>
<DateField
forceValidDate
defaultValue={moment(this.props.entry.time_start).format("DD-MM-YYYY HH:mm:ss")}
dateFormat="DD-MM-YYYY HH:mm:ss"
onChange={(dateString) => { onChange(dateString, 'time_start') }}
/>
</FormGroup>

<FormGroup controlId="formControlEnd">
<ControlLabel>End:</ControlLabel>
<DateField
forceValidDate
defaultValue={moment(this.props.entry.time_end).format("DD-MM-YYYY HH:mm:ss")}
dateFormat="DD-MM-YYYY HH:mm:ss"
onChange={(dateString) => { onChange(dateString, 'time_end') }}
/>
</FormGroup>
</form>
</Popover>
);

const deleteModal = (
<div className="static-modal">
<Modal show={showModal}>
<Modal.Header>
<Modal.Title>Delete Time Entry</Modal.Title>
</Modal.Header>

<Modal.Body>
Are you sure you want to delete this Time Entry?
</Modal.Body>

<Modal.Footer>
<Button onClick={ toggleShowModal }>Cancel</Button>
<Button bsStyle="danger" onClick={ deleteTimeEntry }>Delete</Button>
</Modal.Footer>

</Modal>
</div>
);

return (
<div className="time-entry">
<Col md={6} className="inherit-padding">
{this.props.entry.title}
</Col>

<Col md={2} className="time-entry-start inherit-padding">
{this.props.project.name}
</Col>

<Col md={2} className="time-entry-start inherit-padding">
{this.props.entry.time_start}
</Col>

<Col md={2} className="time-entry-end inherit-padding">
{this.props.entry.time_end}
</Col>
</div>
<div className="time-entry">
<Col md={5} className="inherit-padding">
{this.props.entry.title}
</Col>

<Col md={2} className="time-entry-start inherit-padding">
{this.props.project.name}
</Col>

<Col md={2} className="time-entry-start inherit-padding">
{this.props.entry.time_start}
</Col>

<Col md={2} className="time-entry-end inherit-padding">
{this.props.entry.time_end}
</Col>

<Col md={1} className="time-entry-options inherit-padding">
<Col md={6} className="hover-pointer">
<OverlayTrigger
trigger="click"
placement="left"
overlay={ editPopover }>
<Icon
name='cogs'
size='lg'
alt='asdf'
/>
</OverlayTrigger>
</Col>

<Col md={6} className="hover-pointer">
<Icon
name="trash"
size="lg"
onClick={ toggleShowModal }
/>
</Col>
</Col>

{ deleteModal }
</div>
);
}
};
Expand All @@ -37,4 +147,9 @@ const mapStateToProps = (state, ownProps) => {
};
}

export default connect(mapStateToProps)(TimeEntry);
const mapDispatchToProps = {
deleteTimeEntry,
updateTimeEntry
}

export default connect(mapStateToProps, mapDispatchToProps)(TimeEntry);
31 changes: 31 additions & 0 deletions src/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,37 @@ const ACTION_HANDLERS = {
};
},

'ALTER_TIME_ENTRY': (state, action) => {
const timeEntry = action.timeEntry;
const timeEntryIndex = state.timeEntries.findIndex((elem) => (
elem.id == timeEntry.id
));
const timeEntries = timeEntryIndex >= 0
? state.timeEntries.slice(0).splice(timeEntryIndex + 1, 1, timeEntry)
: state.timeEntries

return {
...state,
timeEntries
};
},

'REMOVE_TIME_ENTRY': (state, action) => {
const timeEntryId = action.timeEntryId;
const timeEntryIndex = state.timeEntries
.findIndex(
(timeEntry) => timeEntry.id == timeEntryId
)
const timeEntries = timeEntryIndex >= 0
? state.timeEntries.slice(0).splice(timeEntryIndex + 1, 1)
: state.timeEntries

return {
...state,
timeEntries
};
},

'LOADING': (state, action) => {
const component = action.component
let newLoading = {}
Expand Down
60 changes: 53 additions & 7 deletions src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,22 +71,52 @@ export const loadTimeEntries = (userId) => {
}
}

export const loadProjects = (userId) => {
export const updateTimeEntry = (timeEntry) => {
return function(dispatch, getState) {
dispatch(loading({ name: 'projectList' }))
dispatch(loading({ name: 'timeEntryList' }));

return FooglApi.projects(userId)
return FooglApi.updateTimeEntry(timeEntry)
.then((response) => {
dispatch(finishedLoading({ name: 'projectList' }))
dispatch(setProjects(response.projects))
dispatch(alterTimeEntry(timeEntry))
dispatch(finishedLoading({ name: 'timeEntryList' }))
})
.catch((error) => {
dispatch(finishedLoading({ name: 'projectList' }))
dispatch(finishedLoading({ name: 'timeEntryList' }))
dispatch(displayError(error))
});
}
}

export const alterTimeEntry = (timeEntry) => {
return {
type: 'ALTER_TIME_ENTRY',
timeEntry
}
}

export const deleteTimeEntry = (timeEntryId) => {
return function(dispatch, getState) {
dispatch(loading({ name: 'timeEntryList' }));

return FooglApi.deleteTimeEntry(timeEntryId)
.then((response) => {
dispatch(removeTimeEntry(timeEntryId))
dispatch(finishedLoading({ name: 'timeEntryList' }))
})
.catch((error) => {
dispatch(finishedLoading({ name: 'timeEntryList' }))
dispatch(displayError(error))
});
}
}

export const removeTimeEntry = (timeEntryId) => {
return {
type: 'REMOVE_TIME_ENTRY',
timeEntryId
}
}

// Login.
export const attemptLogin = (credentials) => {
return function(dispatch, getState) {
Expand All @@ -97,8 +127,8 @@ export const attemptLogin = (credentials) => {
dispatch(finishedLoading({ name: 'loginForm' }))
dispatch(setLoggedUser(response.user))

dispatch(loadTimeEntries(response.user.id))
dispatch(loadProjects(response.user.id))
dispatch(loadTimeEntries(response.user.id))

browserHistory.push('/dashboard')
})
Expand Down Expand Up @@ -144,6 +174,22 @@ export const addProjectToStore = (project) => {
}
}

export const loadProjects = (userId) => {
return function(dispatch, getState) {
dispatch(loading({ name: 'projectList' }))

return FooglApi.projects(userId)
.then((response) => {
dispatch(finishedLoading({ name: 'projectList' }))
dispatch(setProjects(response.projects))
})
.catch((error) => {
dispatch(finishedLoading({ name: 'projectList' }))
dispatch(displayError(error))
});
}
}

export const setProjects = (projects) => {
return {
type: 'SET_PROJECTS',
Expand Down