diff --git a/package.json b/package.json
index 44fa99c..3ada377 100644
--- a/package.json
+++ b/package.json
@@ -24,6 +24,7 @@
"react": "^16.8.0",
"react-dom": "^16.8.0",
"react-select": "^2.3.0",
+ "react-spring": "^8.0.7",
"route-parser": "^0.0.5"
},
"devDependencies": {
diff --git a/src/css/_form.scss b/src/css/_form.scss
index 4cd5a05..6c42f16 100644
--- a/src/css/_form.scss
+++ b/src/css/_form.scss
@@ -66,7 +66,7 @@ input[name="hours"] {
textarea[name="description"] {
resize: none;
- width: calc(100% - 1rem);
+ width: calc(100% - 1rem - 2px);
}
button {
diff --git a/src/css/content.scss b/src/css/content.scss
index 8678241..fcf5dda 100644
--- a/src/css/content.scss
+++ b/src/css/content.scss
@@ -60,8 +60,8 @@
box-sizing: content-box;
background-color: white;
width: 600px;
- height: 400px;
- padding: 2rem;
+ height: 540px;
+ padding: 3rem;
margin: 0 auto;
}
}
diff --git a/src/css/popup.scss b/src/css/popup.scss
index e85df2f..6c32ff4 100644
--- a/src/css/popup.scss
+++ b/src/css/popup.scss
@@ -5,6 +5,7 @@ html {
height: 100%;
body {
+ margin: 0;
height: 100%;
#moco-bx-root {
@@ -27,6 +28,54 @@ html {
margin: 0;
}
}
+
+ .moco-bx-calendar {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 3rem;
+
+ .moco-bx-calendar__day {
+ display: flex;
+ flex-flow: column nowrap;
+ align-items: center;
+
+ flex: 0 0 48px;
+ width: 48px;
+ height: 60px;
+ cursor: pointer;
+
+ .moco-bx-calendar__hours {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 48px;
+ height: 48px;
+ flex: 0 0 48px;
+ color: white;
+ background-color: #eee;
+
+ }
+
+ &.moco-bx-calendar__day--filled {
+ .moco-bx-calendar__hours {
+ background-color: #7dc332;
+ }
+ }
+
+ &.moco-bx-calendar__day--week-day-6,
+ &.moco-bx-calendar__day--week-day-0 {
+ .moco-bx-calendar__hours {
+ background-color: #bbb;
+ }
+ }
+
+ &.moco-bx-calendar__day--active {
+ .moco-bx-calendar__hours {
+ background-color: #38b5eb;
+ }
+ }
+ }
+ }
}
}
}
diff --git a/src/js/api/Client.js b/src/js/api/Client.js
index 87125dd..ca9e076 100644
--- a/src/js/api/Client.js
+++ b/src/js/api/Client.js
@@ -1,4 +1,5 @@
import axios from "axios"
+import { formatDate } from "utils"
export default class Client {
#client;
@@ -20,14 +21,17 @@ export default class Client {
})
}
- login = () => this.#client.post("session", { api_key: this.#apiKey });
-
projects = () => this.#client.get("projects");
+ activities = (fromDate, toDate) =>
+ this.#client.get("activities", {
+ params: { date: `${formatDate(fromDate)}:${formatDate(toDate)}` }
+ })
+
bookedHours = service =>
this.#client.get("activities/tags", {
params: { selection: [service.id], remote_service: service.name }
- });
+ })
createActivity = activity => this.#client.post("activities", { activity });
}
diff --git a/src/js/components/App.js b/src/js/components/App.js
index 9ada49e..28d2d16 100644
--- a/src/js/components/App.js
+++ b/src/js/components/App.js
@@ -2,17 +2,19 @@ import React, { Component } from "react"
import PropTypes from "prop-types"
import ApiClient from "api/Client"
import Form from "components/Form"
+import Calendar from "components/Calendar"
import Spinner from "components/Spinner"
-import { observable, computed, toJS } from "mobx"
-import { observer, disposeOnUnmount } from "mobx-react"
+import { observable, computed } from "mobx"
+import { observer } from "mobx-react"
import {
findLastProject,
findLastTask,
groupedProjectOptions,
- currentDate,
+ formatDate,
secondsFromHours
} from "utils"
-import logoUrl from 'images/logo.png'
+import { startOfWeek, endOfWeek } from "date-fns"
+import logoUrl from "images/logo.png"
import { head } from "lodash"
@observer
@@ -31,13 +33,14 @@ class App extends Component {
apiKey: PropTypes.string,
version: PropTypes.string
})
- };
+ }
@observable projects = []
+ @observable activities = []
@observable lastProjectId
@observable lastTaskId
- @observable changeset = {};
- @observable formErrors = {};
+ @observable changeset = {}
+ @observable formErrors = {}
@observable isLoading = true
@computed get changesetWithDefaults() {
@@ -55,7 +58,7 @@ class App extends Component {
remote_service: service.name,
remote_id: service.id,
remote_url: service.url,
- date: currentDate(),
+ date: formatDate(new Date()),
assignment_id: project?.value,
task_id: task?.value,
billable: task?.billable,
@@ -78,7 +81,8 @@ class App extends Component {
}
componentDidMount() {
- this.fetchProjects()
+ Promise.all([this.fetchProjects(), this.fetchActivities()])
+ .then(() => this.isLoading = false)
window.addEventListener("keydown", this.handleKeyDown)
}
@@ -90,23 +94,31 @@ class App extends Component {
this.#apiClient = new ApiClient(settings)
}
- fetchProjects = () => {
- this.isLoading = true
+ fromDate = () => startOfWeek(new Date(), { weekStartsOn: 1 })
+ toDate = () => endOfWeek(new Date(), { weekStartsOn: 1 })
- return this.#apiClient
+ fetchProjects = () =>
+ this.#apiClient
.projects()
.then(({ data }) => {
this.projects = groupedProjectOptions(data.projects)
this.lastProjectId = data.last_project_id
this.lastTaskId = data.lastTaskId
})
- .catch(error => {
- this.sendMessage({ type: 'closeForm' })
+ .catch(() => {
+ this.sendMessage({ type: "closeForm" })
})
- .finally(() => {
- this.isLoading = false
+
+ fetchActivities = () => {
+ return this.#apiClient
+ .activities(this.fromDate(), this.toDate())
+ .then(({ data }) => {
+ this.activities = data
})
- };
+ .catch(() => {
+ this.sendMessage({ type: "closeForm" })
+ })
+ }
createActivity = () => {
this.isLoading = true
@@ -116,9 +128,10 @@ class App extends Component {
.then(({ data }) => {
this.changeset = {}
this.formErrors = {}
- this.sendMessage(
- { type: 'activityCreated', payload: { hours: data.hours} }
- )
+ this.sendMessage({
+ type: "activityCreated",
+ payload: { hours: data.hours }
+ })
})
.catch(error => {
if (error.response?.status === 422) {
@@ -128,15 +141,15 @@ class App extends Component {
this.unauthorizedError = true
}
})
- .finally(() => this.isLoading = false)
+ .finally(() => (this.isLoading = false))
}
handleKeyDown = event => {
event.stopPropagation()
if (event.keyCode === 27) {
- this.sendMessage({ type: 'closeForm' })
+ this.sendMessage({ type: "closeForm" })
}
- };
+ }
handleChange = event => {
const {
@@ -148,7 +161,11 @@ class App extends Component {
if (name === "assignment_id") {
this.changeset.task_id = null
}
- };
+ }
+
+ handleSelectDate = date => {
+ this.changeset.date = formatDate(date)
+ }
handleSubmit = event => {
event.preventDefault()
@@ -156,13 +173,12 @@ class App extends Component {
}
handleCancel = () => {
- this.sendMessage({ type: 'closeForm' })
+ this.sendMessage({ type: "closeForm" })
}
sendMessage = action =>
- chrome.tabs.query(
- { active: true, currentWindow: true },
- tabs => chrome.tabs.sendMessage(tabs[0].id, action)
+ chrome.tabs.query({ active: true, currentWindow: true }, tabs =>
+ chrome.tabs.sendMessage(tabs[0].id, action)
)
render() {
@@ -170,15 +186,23 @@ class App extends Component {
return