import React, { Component } from "react" import PropTypes from "prop-types" import Spinner from "components/Spinner" import Form from "components/Form" import Calendar from "components/Calendar" import { observable, computed } from "mobx" import { Observer, observer } from "mobx-react" import { Spring, animated, config } from "react-spring/renderprops" import { ERROR_UNKNOWN, ERROR_UNAUTHORIZED, ERROR_UPGRADE_REQUIRED, extractAndSetTag, findProjectByValue, findProjectByIdentifier, findTask, formatDate, } from "utils" import InvalidConfigurationError from "components/Errors/InvalidConfigurationError" import UpgradeRequiredError from "components/Errors/UpgradeRequiredError" import UnknownError from "components/Errors/UnknownError" import { parse } from "date-fns" import Header from "./shared/Header" import { head } from "lodash" import TimeInputParser from "utils/TimeInputParser" @observer class App extends Component { static propTypes = { loading: PropTypes.bool, service: PropTypes.shape({ id: PropTypes.string, url: PropTypes.string, name: PropTypes.string, description: PropTypes.string, projectId: PropTypes.string, taskId: PropTypes.string, }), subdomain: PropTypes.string, activities: PropTypes.array, schedules: PropTypes.array, projects: PropTypes.array, lastProjectId: PropTypes.number, lastTaskId: PropTypes.number, roundTimeEntries: PropTypes.bool, fromDate: PropTypes.string, toDate: PropTypes.string, errorType: PropTypes.string, errorMessage: PropTypes.string, } static defaultProps = { activities: [], schedules: [], projects: [], roundTimeEntries: false, } @observable changeset = {} @observable formErrors = {} @computed get project() { const { service, projects, lastProjectId } = this.props return ( findProjectByValue(this.changeset.assignment_id)(projects) || findProjectByIdentifier(service?.projectId)(projects) || findProjectByValue(Number(lastProjectId))(projects) || head(projects) ) } @computed get task() { const { service, lastTaskId } = this.props return ( findTask(this.changeset.task_id || service?.taskId || lastTaskId)(this.project) || head(this.project?.tasks) ) } @computed get billable() { return /\(.+\)/.test(this.changeset.hours) === true ? false : !!this.task?.billable } @computed get changesetWithDefaults() { const { service } = this.props const defaults = { remote_service: service?.name, remote_id: service?.id, remote_url: service?.url, date: formatDate(new Date()), assignment_id: this.project?.value, task_id: this.task?.value, billable: this.billable, hours: "", seconds: this.changeset.hours && new TimeInputParser(this.changeset.hours).parseSeconds(), description: service?.description, tag: "", } return { ...defaults, ...this.changeset } } componentDidMount() { window.addEventListener("keydown", this.handleKeyDown) chrome.runtime.onMessage.addListener(this.handleSetFormErrors) } componentWillUnmount() { window.removeEventListener("keydown", this.handleKeyDown) chrome.runtime.onMessage.removeListener(this.handleSetFormErrors) } handleChange = event => { const { projects } = this.props const { target: { name, value }, } = event this.changeset[name] = value if (name === "assignment_id") { const project = findProjectByValue(value)(projects) this.changeset.task_id = head(project?.tasks)?.value } } handleSelectDate = date => { this.changeset.date = formatDate(date) } handleSubmit = event => { event.preventDefault() const { service } = this.props chrome.runtime.sendMessage({ type: "createActivity", payload: { activity: extractAndSetTag(this.changesetWithDefaults), service, }, }) } handleKeyDown = event => { if (event.keyCode === 27) { event.stopPropagation() chrome.runtime.sendMessage({ type: "closePopup" }) } } handleSetFormErrors = ({ type, payload }) => { if (type === "setFormErrors") { this.formErrors = payload } } render() { const { loading, subdomain, projects, activities, schedules, fromDate, toDate, errorType, errorMessage, } = this.props if (loading) { return } if (errorType === ERROR_UNAUTHORIZED) { return } if (errorType === ERROR_UPGRADE_REQUIRED) { return } if (errorType === ERROR_UNKNOWN) { return } return ( {props => (
{() => ( <>
)} )} ) } } export default App