Compare commits

...

148 Commits

Author SHA1 Message Date
dependabot-preview[bot]
fb1a4ddd48 Bump mini-css-extract-plugin from 0.9.0 to 0.11.2
Bumps [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) from 0.9.0 to 0.11.2.
- [Release notes](https://github.com/webpack-contrib/mini-css-extract-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/mini-css-extract-plugin/compare/v0.9.0...v0.11.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-02 01:14:49 +00:00
Manuel Bouza
261db3f9db Remove batchingForReactDom 2020-09-11 09:31:18 +02:00
Manuel Bouza
0b53743298 Pump version 2020-09-11 09:31:18 +02:00
Manuel Bouza
5b63b77102 Upgrade packages 2020-09-10 14:51:58 +02:00
Nicola Piccinini
7589e04207 Select first the project with tracked hours (#217) 2020-09-10 14:08:30 +02:00
pic
1aecaec925 Ignore .idea 2020-09-09 10:53:55 +02:00
Manuel Bouza
6f47cf9cd6 feature/monday-service (#198)
* Upgrade packages

* Add monday as service (#197)

* Add monday as service

* Replaced static subdomain with placeholder

* Only use pulse title for description

* Update changelog und pump version

Co-authored-by: markusNahketing <69197867+markusNahketing@users.noreply.github.com>
2020-08-06 11:50:14 +02:00
dependabot-preview[bot]
dcef794fe4 Bump css-loader from 3.5.3 to 4.2.0 (#185)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 3.5.3 to 4.2.0.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v3.5.3...v4.2.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-08-04 11:52:32 +02:00
dependabot-preview[bot]
c5230e6e74 Bump sass-loader from 8.0.2 to 9.0.2 (#186)
Bumps [sass-loader](https://github.com/webpack-contrib/sass-loader) from 8.0.2 to 9.0.2.
- [Release notes](https://github.com/webpack-contrib/sass-loader/releases)
- [Changelog](https://github.com/webpack-contrib/sass-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/sass-loader/compare/v8.0.2...v9.0.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-08-04 11:51:57 +02:00
dependabot-preview[bot]
7370915c27 Bump uuid from 8.1.0 to 8.3.0 (#194)
Bumps [uuid](https://github.com/uuidjs/uuid) from 8.1.0 to 8.3.0.
- [Release notes](https://github.com/uuidjs/uuid/releases)
- [Changelog](https://github.com/uuidjs/uuid/blob/master/CHANGELOG.md)
- [Commits](https://github.com/uuidjs/uuid/compare/v8.1.0...v8.3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-08-04 11:44:11 +02:00
dependabot-preview[bot]
33fe6ad910 Bump date-fns from 2.14.0 to 2.15.0 (#195)
Bumps [date-fns](https://github.com/date-fns/date-fns) from 2.14.0 to 2.15.0.
- [Release notes](https://github.com/date-fns/date-fns/releases)
- [Changelog](https://github.com/date-fns/date-fns/blob/master/CHANGELOG.md)
- [Commits](https://github.com/date-fns/date-fns/compare/v2.14.0...v2.15.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-08-04 11:42:07 +02:00
dependabot-preview[bot]
a8de22b796 Bump @babel/preset-env from 7.10.2 to 7.11.0 (#196)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.10.2 to 7.11.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.11.0/packages/babel-preset-env)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-08-04 11:41:38 +02:00
dependabot-preview[bot]
f38e93bcd6 [Security] Bump elliptic from 6.5.1 to 6.5.3 (#182)
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.1 to 6.5.3. **This update includes a security fix.**
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.1...v6.5.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-07-30 09:09:33 +02:00
Manuel Bouza
061a3d9a89 feature/host-overrides (#161)
* configurable host overrides

* base host overrides on name of service instead of key and hide the options by default

* added unit tests

* review changes

* Refactor options

* Refactor

* Update Readme

* Pump version and update Changelog

Co-authored-by: Tobias Jacksteit <me@xtj7.de>
2020-06-15 17:14:31 +02:00
Manuel Bouza
a13e30784c Use mobx batching 2020-06-05 09:53:28 +02:00
Manuel Bouza
f4c747dd7e Fix html webpack plugin
Html files got deleted after a rebuild
2020-06-05 09:52:44 +02:00
dependabot-preview[bot]
46aa91b736 Bump eslint-plugin-prettier from 3.1.2 to 3.1.3 (#152)
Bumps [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier) from 3.1.2 to 3.1.3.
- [Release notes](https://github.com/prettier/eslint-plugin-prettier/releases)
- [Changelog](https://github.com/prettier/eslint-plugin-prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/eslint-plugin-prettier/compare/v3.1.2...v3.1.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
Co-authored-by: Manuel Bouza <manuel@bouza.ch>
2020-06-04 23:38:58 +02:00
dependabot-preview[bot]
956e40fc4b Bump eslint-plugin-jest from 23.8.2 to 23.13.2 (#153)
Bumps [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) from 23.8.2 to 23.13.2.
- [Release notes](https://github.com/jest-community/eslint-plugin-jest/releases)
- [Changelog](https://github.com/jest-community/eslint-plugin-jest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jest-community/eslint-plugin-jest/compare/v23.8.2...v23.13.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 23:37:49 +02:00
dependabot-preview[bot]
7b0a8276a4 Bump css-loader from 3.4.2 to 3.5.3 (#156)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 3.4.2 to 3.5.3.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v3.4.2...v3.5.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 23:37:35 +02:00
Manuel Bouza
3848055634 Fix uuid 2020-06-04 23:36:54 +02:00
Manuel Bouza
d7e4a01adc Fix copy-webpack-plugin 2020-06-04 23:35:41 +02:00
dependabot-preview[bot]
97e47ad769 Bump copy-webpack-plugin from 5.1.1 to 6.0.2 (#160)
* Bump copy-webpack-plugin from 5.1.1 to 6.0.2

Bumps [copy-webpack-plugin](https://github.com/webpack-contrib/copy-webpack-plugin) from 5.1.1 to 6.0.2.
- [Release notes](https://github.com/webpack-contrib/copy-webpack-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/copy-webpack-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v5.1.1...v6.0.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

* Update copy-webpack-plugin

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
Co-authored-by: Manuel Bouza <manuel@bouza.ch>
2020-06-04 23:29:06 +02:00
dependabot-preview[bot]
5311e6ea2f Bump @babel/plugin-proposal-decorators from 7.8.3 to 7.10.1 (#145)
Bumps [@babel/plugin-proposal-decorators](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-proposal-decorators) from 7.8.3 to 7.10.1.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.10.1/packages/babel-plugin-proposal-decorators)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 23:24:03 +02:00
dependabot-preview[bot]
86191f792b Bump style-loader from 1.1.3 to 1.2.1 (#157)
Bumps [style-loader](https://github.com/webpack-contrib/style-loader) from 1.1.3 to 1.2.1.
- [Release notes](https://github.com/webpack-contrib/style-loader/releases)
- [Changelog](https://github.com/webpack-contrib/style-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/style-loader/compare/v1.1.3...v1.2.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 23:23:13 +02:00
dependabot-preview[bot]
97022dbf97 Bump eslint-config-prettier from 6.10.1 to 6.11.0 (#154)
Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 6.10.1 to 6.11.0.
- [Release notes](https://github.com/prettier/eslint-config-prettier/releases)
- [Changelog](https://github.com/prettier/eslint-config-prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/eslint-config-prettier/compare/v6.10.1...v6.11.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 23:22:42 +02:00
dependabot-preview[bot]
446ad01343 Bump prettier from 2.0.2 to 2.0.5 (#149)
Bumps [prettier](https://github.com/prettier/prettier) from 2.0.2 to 2.0.5.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.0.2...2.0.5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 23:22:22 +02:00
dependabot-preview[bot]
5c531b4917 Bump @babel/preset-env from 7.9.0 to 7.10.2 (#144)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.9.0 to 7.10.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.10.2/packages/babel-preset-env)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 23:21:56 +02:00
dependabot-preview[bot]
616ab6fb0f Bump mobx-react from 6.1.8 to 6.2.2 (#139)
Bumps [mobx-react](https://github.com/mobxjs/mobx-react) from 6.1.8 to 6.2.2.
- [Release notes](https://github.com/mobxjs/mobx-react/releases)
- [Changelog](https://github.com/mobxjs/mobx-react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mobxjs/mobx-react/compare/6.1.8...v6.2.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 23:21:43 +02:00
dependabot-preview[bot]
4fb256fca7 Bump webpack from 4.42.1 to 4.43.0 (#140)
Bumps [webpack](https://github.com/webpack/webpack) from 4.42.1 to 4.43.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v4.42.1...v4.43.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 23:21:30 +02:00
dependabot-preview[bot]
3d3d268bca Bump @babel/polyfill from 7.8.7 to 7.10.1 (#151)
Bumps [@babel/polyfill](https://github.com/babel/babel/tree/HEAD/packages/babel-polyfill) from 7.8.7 to 7.10.1.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.10.1/packages/babel-polyfill)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 23:21:09 +02:00
dependabot-preview[bot]
b05d00e823 Bump eslint-plugin-react from 7.19.0 to 7.20.0 (#146)
Bumps [eslint-plugin-react](https://github.com/yannickcr/eslint-plugin-react) from 7.19.0 to 7.20.0.
- [Release notes](https://github.com/yannickcr/eslint-plugin-react/releases)
- [Changelog](https://github.com/yannickcr/eslint-plugin-react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yannickcr/eslint-plugin-react/compare/v7.19.0...v7.20.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 23:20:46 +02:00
dependabot-preview[bot]
eac2e86733 Bump date-fns from 2.11.1 to 2.14.0 (#143)
Bumps [date-fns](https://github.com/date-fns/date-fns) from 2.11.1 to 2.14.0.
- [Release notes](https://github.com/date-fns/date-fns/releases)
- [Changelog](https://github.com/date-fns/date-fns/blob/master/CHANGELOG.md)
- [Commits](https://github.com/date-fns/date-fns/compare/v2.11.1...v2.14.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 23:20:32 +02:00
dependabot-preview[bot]
38e33f1c55 Bump html-webpack-plugin from 4.0.4 to 4.3.0 (#142)
Bumps [html-webpack-plugin](https://github.com/jantimon/html-webpack-plugin) from 4.0.4 to 4.3.0.
- [Release notes](https://github.com/jantimon/html-webpack-plugin/releases)
- [Changelog](https://github.com/jantimon/html-webpack-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jantimon/html-webpack-plugin/compare/v4.0.4...v4.3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 23:20:16 +02:00
dependabot-preview[bot]
409bb9bab5 Bump query-string from 6.11.1 to 6.12.1 (#147)
Bumps [query-string](https://github.com/sindresorhus/query-string) from 6.11.1 to 6.12.1.
- [Release notes](https://github.com/sindresorhus/query-string/releases)
- [Commits](https://github.com/sindresorhus/query-string/compare/v6.11.1...v6.12.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 23:19:49 +02:00
dependabot-preview[bot]
cf1a696fec Bump @babel/preset-react from 7.9.4 to 7.10.1 (#141)
Bumps [@babel/preset-react](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-react) from 7.9.4 to 7.10.1.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.10.1/packages/babel-preset-react)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 23:19:34 +02:00
dependabot-preview[bot]
f26ed7ce02 Bump copyfiles from 2.2.0 to 2.3.0 (#138)
Bumps [copyfiles](https://github.com/calvinmetcalf/copyfiles) from 2.2.0 to 2.3.0.
- [Release notes](https://github.com/calvinmetcalf/copyfiles/releases)
- [Commits](https://github.com/calvinmetcalf/copyfiles/compare/v2.2.0...v2.3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 23:18:55 +02:00
dependabot-preview[bot]
3756848ba7 Bump uuid from 7.0.3 to 8.1.0 (#137)
Bumps [uuid](https://github.com/uuidjs/uuid) from 7.0.3 to 8.1.0.
- [Release notes](https://github.com/uuidjs/uuid/releases)
- [Changelog](https://github.com/uuidjs/uuid/blob/master/CHANGELOG.md)
- [Commits](https://github.com/uuidjs/uuid/compare/v7.0.3...v8.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 23:15:03 +02:00
dependabot-preview[bot]
0ba606d5d8 Bump jest from 25.2.6 to 26.0.1 (#136)
Bumps [jest](https://github.com/facebook/jest) from 25.2.6 to 26.0.1.
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/compare/v25.2.6...v26.0.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 23:14:03 +02:00
dependabot-preview[bot]
5f0bd963be Bump @babel/core from 7.9.0 to 7.10.2 (#135)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.9.0 to 7.10.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.10.2/packages/babel-core)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 22:44:31 +02:00
dependabot-preview[bot]
13bbaf1ad3 Bump node-sass from 4.13.1 to 4.14.1 (#134)
Bumps [node-sass](https://github.com/sass/node-sass) from 4.13.1 to 4.14.1.
- [Release notes](https://github.com/sass/node-sass/releases)
- [Changelog](https://github.com/sass/node-sass/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sass/node-sass/compare/v4.13.1...v4.14.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 22:44:14 +02:00
Thomas Ritter
dc4918ad91 Bump version 2020-06-02 09:05:15 +02:00
Adrian Görisch
f763553739 Add support for Gitlab merge-request and issues (#133)
Co-authored-by: Adrian Görisch <adrian.goerisch@10m.de>
2020-04-27 10:57:20 +02:00
dependabot-preview[bot]
c412b1711c [Security] Bump minimist from 1.2.0 to 1.2.5 (#131)
Bumps [minimist](https://github.com/substack/minimist) from 1.2.0 to 1.2.5. **This update includes security fixes.**
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.0...1.2.5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-05 09:10:32 +02:00
dependabot-preview[bot]
342134a039 [Security] Bump acorn from 6.3.0 to 6.4.1 (#130)
Bumps [acorn](https://github.com/acornjs/acorn) from 6.3.0 to 6.4.1. **This update includes security fixes.**
- [Release notes](https://github.com/acornjs/acorn/releases)
- [Commits](https://github.com/acornjs/acorn/compare/6.3.0...6.4.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-05 09:10:19 +02:00
Manuel Bouza
97dd2651ce Merge branch 'dependabot/npm_and_yarn/babel/preset-env-7.9.0' 2020-04-03 08:54:57 +02:00
dependabot-preview[bot]
46610a1842 Bump @babel/preset-env from 7.7.7 to 7.9.0
Bumps [@babel/preset-env](https://github.com/babel/babel) from 7.7.7 to 7.9.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.7.7...v7.9.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-03 08:53:27 +02:00
dependabot-preview[bot]
768cf84080 Bump @babel/plugin-proposal-nullish-coalescing-operator (#80)
Bumps [@babel/plugin-proposal-nullish-coalescing-operator](https://github.com/babel/babel) from 7.7.4 to 7.8.3.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.7.4...v7.8.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
Co-authored-by: Manuel Bouza <manuel@bouza.ch>
2020-04-03 08:50:03 +02:00
dependabot-preview[bot]
0797e764f7 Bump @babel/plugin-proposal-decorators from 7.7.4 to 7.8.3 (#78)
Bumps [@babel/plugin-proposal-decorators](https://github.com/babel/babel) from 7.7.4 to 7.8.3.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.7.4...v7.8.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:49:16 +02:00
dependabot-preview[bot]
3e9f739b17 Bump @babel/core from 7.7.7 to 7.9.0 (#115)
Bumps [@babel/core](https://github.com/babel/babel) from 7.7.7 to 7.9.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.7.7...v7.9.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:49:01 +02:00
dependabot-preview[bot]
a4fc429476 Bump html-webpack-plugin from 3.2.0 to 4.0.4 (#126)
Bumps [html-webpack-plugin](https://github.com/jantimon/html-webpack-plugin) from 3.2.0 to 4.0.4.
- [Release notes](https://github.com/jantimon/html-webpack-plugin/releases)
- [Changelog](https://github.com/jantimon/html-webpack-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jantimon/html-webpack-plugin/compare/v3.2.0...v4.0.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:47:35 +02:00
dependabot-preview[bot]
fdaad0660a Bump prettier from 1.19.1 to 2.0.2 (#109)
Bumps [prettier](https://github.com/prettier/prettier) from 1.19.1 to 2.0.2.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/1.19.1...2.0.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:47:20 +02:00
dependabot-preview[bot]
1d67790f47 Bump @babel/preset-react from 7.7.4 to 7.9.4 (#117)
Bumps [@babel/preset-react](https://github.com/babel/babel) from 7.7.4 to 7.9.4.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.7.4...v7.9.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:47:02 +02:00
dependabot-preview[bot]
4a2b00d984 Bump uuid from 3.3.3 to 7.0.3 (#114)
Bumps [uuid](https://github.com/uuidjs/uuid) from 3.3.3 to 7.0.3.
- [Release notes](https://github.com/uuidjs/uuid/releases)
- [Changelog](https://github.com/uuidjs/uuid/blob/master/CHANGELOG.md)
- [Commits](https://github.com/uuidjs/uuid/compare/v3.3.3...v7.0.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:46:07 +02:00
dependabot-preview[bot]
455e3f26d2 Bump copyfiles from 2.1.1 to 2.2.0 (#74)
Bumps [copyfiles](https://github.com/calvinmetcalf/copyfiles) from 2.1.1 to 2.2.0.
- [Release notes](https://github.com/calvinmetcalf/copyfiles/releases)
- [Commits](https://github.com/calvinmetcalf/copyfiles/compare/v2.1.1...v2.2.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
Co-authored-by: Manuel Bouza <manuel@bouza.ch>
2020-04-03 08:45:51 +02:00
dependabot-preview[bot]
ee80ade613 Bump css-loader from 3.4.0 to 3.4.2 (#88)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 3.4.0 to 3.4.2.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v3.4.0...v3.4.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:44:56 +02:00
dependabot-preview[bot]
84d758167f Bump webpack-cli from 3.3.10 to 3.3.11 (#94)
Bumps [webpack-cli](https://github.com/webpack/webpack-cli) from 3.3.10 to 3.3.11.
- [Release notes](https://github.com/webpack/webpack-cli/releases)
- [Changelog](https://github.com/webpack/webpack-cli/blob/next/CHANGELOG_v3.md)
- [Commits](https://github.com/webpack/webpack-cli/compare/v3.3.10...v3.3.11)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:44:30 +02:00
dependabot-preview[bot]
2e37b5b9ee Bump eslint-config-prettier from 6.9.0 to 6.10.1 (#110)
Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 6.9.0 to 6.10.1.
- [Release notes](https://github.com/prettier/eslint-config-prettier/releases)
- [Changelog](https://github.com/prettier/eslint-config-prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/eslint-config-prettier/compare/v6.9.0...v6.10.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:44:13 +02:00
dependabot-preview[bot]
63714129cc Bump svg-inline-loader from 0.8.0 to 0.8.2 (#101)
Bumps [svg-inline-loader](https://github.com/sairion/svg-inline-loader) from 0.8.0 to 0.8.2.
- [Release notes](https://github.com/sairion/svg-inline-loader/releases)
- [Changelog](https://github.com/webpack-contrib/svg-inline-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sairion/svg-inline-loader/compare/v0.8.0...v0.8.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:43:54 +02:00
dependabot-preview[bot]
cb5d81b65c Bump sass-loader from 8.0.0 to 8.0.2 (#89)
Bumps [sass-loader](https://github.com/webpack-contrib/sass-loader) from 8.0.0 to 8.0.2.
- [Release notes](https://github.com/webpack-contrib/sass-loader/releases)
- [Changelog](https://github.com/webpack-contrib/sass-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/sass-loader/compare/v8.0.0...v8.0.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:43:38 +02:00
dependabot-preview[bot]
ff61028b88 Bump babel-eslint from 10.0.3 to 10.1.0 (#104)
Bumps [babel-eslint](https://github.com/babel/babel-eslint) from 10.0.3 to 10.1.0.
- [Release notes](https://github.com/babel/babel-eslint/releases)
- [Commits](https://github.com/babel/babel-eslint/compare/v10.0.3...v10.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:43:22 +02:00
dependabot-preview[bot]
6486419419 Bump query-string from 6.9.0 to 6.11.1 (#111)
Bumps [query-string](https://github.com/sindresorhus/query-string) from 6.9.0 to 6.11.1.
- [Release notes](https://github.com/sindresorhus/query-string/releases)
- [Commits](https://github.com/sindresorhus/query-string/compare/v6.9.0...v6.11.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:43:06 +02:00
dependabot-preview[bot]
c58f6fba6a Bump @babel/polyfill from 7.7.0 to 7.8.7 (#107)
Bumps [@babel/polyfill](https://github.com/babel/babel) from 7.7.0 to 7.8.7.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.7.0...v7.8.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:42:45 +02:00
dependabot-preview[bot]
d6316032db Bump @babel/plugin-proposal-optional-chaining from 7.7.5 to 7.9.0 (#108)
Bumps [@babel/plugin-proposal-optional-chaining](https://github.com/babel/babel) from 7.7.5 to 7.9.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.7.5...v7.9.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:42:18 +02:00
dependabot-preview[bot]
b283288337 Bump react-select from 3.0.8 to 3.1.0 (#112)
Bumps [react-select](https://github.com/JedWatson/react-select) from 3.0.8 to 3.1.0.
- [Release notes](https://github.com/JedWatson/react-select/releases)
- [Changelog](https://github.com/JedWatson/react-select/blob/master/.sweet-changelogs.js)
- [Commits](https://github.com/JedWatson/react-select/compare/react-select@3.0.8...react-select@3.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:42:00 +02:00
dependabot-preview[bot]
0b4896d613 Bump @babel/plugin-proposal-class-properties from 7.7.4 to 7.8.3 (#73)
Bumps [@babel/plugin-proposal-class-properties](https://github.com/babel/babel) from 7.7.4 to 7.8.3.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.7.4...v7.8.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:41:37 +02:00
dependabot-preview[bot]
ca07be7856 Bump node-sass from 4.13.0 to 4.13.1 (#72)
Bumps [node-sass](https://github.com/sass/node-sass) from 4.13.0 to 4.13.1.
- [Release notes](https://github.com/sass/node-sass/releases)
- [Changelog](https://github.com/sass/node-sass/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sass/node-sass/compare/v4.13.0...v4.13.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:41:21 +02:00
dependabot-preview[bot]
8b20feb255 Bump file-loader from 5.0.2 to 6.0.0 (#113)
Bumps [file-loader](https://github.com/webpack-contrib/file-loader) from 5.0.2 to 6.0.0.
- [Release notes](https://github.com/webpack-contrib/file-loader/releases)
- [Changelog](https://github.com/webpack-contrib/file-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/file-loader/compare/v5.0.2...v6.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:40:50 +02:00
dependabot-preview[bot]
213c21d19c Bump webpack from 4.41.5 to 4.42.1 (#116)
Bumps [webpack](https://github.com/webpack/webpack) from 4.41.5 to 4.42.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v4.41.5...v4.42.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:39:36 +02:00
dependabot-preview[bot]
cb490a391a Bump jest from 24.9.0 to 25.2.6 (#127)
Bumps [jest](https://github.com/facebook/jest) from 24.9.0 to 25.2.6.
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/compare/v24.9.0...v25.2.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:39:04 +02:00
dependabot-preview[bot]
abc63d4361 Bump mobx-react from 6.1.4 to 6.1.8 (#129)
Bumps [mobx-react](https://github.com/mobxjs/mobx-react) from 6.1.4 to 6.1.8.
- [Release notes](https://github.com/mobxjs/mobx-react/releases)
- [Changelog](https://github.com/mobxjs/mobx-react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mobxjs/mobx-react/compare/6.1.4...6.1.8)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:38:26 +02:00
dependabot-preview[bot]
7e64ed6599 Bump eslint-plugin-jest from 23.2.0 to 23.8.2 (#120)
Bumps [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) from 23.2.0 to 23.8.2.
- [Release notes](https://github.com/jest-community/eslint-plugin-jest/releases)
- [Changelog](https://github.com/jest-community/eslint-plugin-jest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jest-community/eslint-plugin-jest/compare/v23.2.0...v23.8.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:38:11 +02:00
dependabot-preview[bot]
c6e14d77d5 Bump style-loader from 1.1.2 to 1.1.3 (#79)
Bumps [style-loader](https://github.com/webpack-contrib/style-loader) from 1.1.2 to 1.1.3.
- [Release notes](https://github.com/webpack-contrib/style-loader/releases)
- [Changelog](https://github.com/webpack-contrib/style-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/style-loader/compare/v1.1.2...v1.1.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:37:55 +02:00
dependabot-preview[bot]
67640613e2 Bump react-dom from 16.12.0 to 16.13.1 (#128)
Bumps [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) from 16.12.0 to 16.13.1.
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v16.13.1/packages/react-dom)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:35:56 +02:00
dependabot-preview[bot]
5334358c95 Bump mobx from 5.15.1 to 5.15.4 (#84)
Bumps [mobx](https://github.com/mobxjs/mobx) from 5.15.1 to 5.15.4.
- [Release notes](https://github.com/mobxjs/mobx/releases)
- [Changelog](https://github.com/mobxjs/mobx/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mobxjs/mobx/compare/5.15.1...5.15.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:35:40 +02:00
dependabot-preview[bot]
43e700cbf9 Bump @babel/preset-env from 7.7.7 to 7.9.0
Bumps [@babel/preset-env](https://github.com/babel/babel) from 7.7.7 to 7.9.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.7.7...v7.9.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-03 06:29:49 +00:00
dependabot-preview[bot]
4da136e0d7 Bump axios from 0.19.0 to 0.19.2 (#82)
Bumps [axios](https://github.com/axios/axios) from 0.19.0 to 0.19.2.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/master/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.19.0...v0.19.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:29:22 +02:00
dependabot-preview[bot]
72828a6e9d Bump react from 16.12.0 to 16.13.1 (#121)
Bumps [react](https://github.com/facebook/react/tree/HEAD/packages/react) from 16.12.0 to 16.13.1.
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v16.13.1/packages/react)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:28:47 +02:00
dependabot-preview[bot]
c8fc9f9af9 Bump eslint-plugin-react from 7.17.0 to 7.19.0 (#122)
Bumps [eslint-plugin-react](https://github.com/yannickcr/eslint-plugin-react) from 7.17.0 to 7.19.0.
- [Release notes](https://github.com/yannickcr/eslint-plugin-react/releases)
- [Changelog](https://github.com/yannickcr/eslint-plugin-react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yannickcr/eslint-plugin-react/compare/v7.17.0...v7.19.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:28:06 +02:00
dependabot-preview[bot]
da98ffcb6f Bump babel-loader from 8.0.6 to 8.1.0 (#124)
Bumps [babel-loader](https://github.com/babel/babel-loader) from 8.0.6 to 8.1.0.
- [Release notes](https://github.com/babel/babel-loader/releases)
- [Changelog](https://github.com/babel/babel-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel-loader/compare/v8.0.6...v8.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:27:44 +02:00
dependabot-preview[bot]
2940776284 Bump date-fns from 2.8.1 to 2.11.1 (#125)
Bumps [date-fns](https://github.com/date-fns/date-fns) from 2.8.1 to 2.11.1.
- [Release notes](https://github.com/date-fns/date-fns/releases)
- [Changelog](https://github.com/date-fns/date-fns/blob/master/CHANGELOG.md)
- [Commits](https://github.com/date-fns/date-fns/compare/v2.8.1...v2.11.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-03 08:27:30 +02:00
Manuel Bouza
33dd3299d9 Asana: read task title from single task pane 2020-01-09 19:20:06 +01:00
dependabot-preview[bot]
58bc2ff7fa Bump eslint-config-prettier from 6.3.0 to 6.9.0 (#61)
Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 6.3.0 to 6.9.0.
- [Release notes](https://github.com/prettier/eslint-config-prettier/releases)
- [Changelog](https://github.com/prettier/eslint-config-prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/eslint-config-prettier/compare/v6.3.0...v6.9.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-08 09:07:56 +01:00
dependabot-preview[bot]
41650f0eab Bump @babel/preset-env from 7.7.6 to 7.7.7 (#62)
Bumps [@babel/preset-env](https://github.com/babel/babel) from 7.7.6 to 7.7.7.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.7.6...v7.7.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-08 09:07:43 +01:00
dependabot-preview[bot]
0c7a32ac3e Bump webpack from 4.41.4 to 4.41.5 (#63)
Bumps [webpack](https://github.com/webpack/webpack) from 4.41.4 to 4.41.5.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v4.41.4...v4.41.5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-08 09:07:28 +01:00
dependabot-preview[bot]
96f5d69028 Bump date-fns from 2.4.1 to 2.8.1 (#64)
Bumps [date-fns](https://github.com/date-fns/date-fns) from 2.4.1 to 2.8.1.
- [Release notes](https://github.com/date-fns/date-fns/releases)
- [Changelog](https://github.com/date-fns/date-fns/blob/master/CHANGELOG.md)
- [Commits](https://github.com/date-fns/date-fns/compare/v2.4.1...v2.8.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-08 09:07:10 +01:00
dependabot-preview[bot]
78a102a6e6 Bump @babel/core from 7.6.2 to 7.7.7 (#65)
Bumps [@babel/core](https://github.com/babel/babel) from 7.6.2 to 7.7.7.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.6.2...v7.7.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-08 09:06:34 +01:00
dependabot-preview[bot]
400e251d88 Bump mini-css-extract-plugin from 0.8.0 to 0.9.0 (#66)
Bumps [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) from 0.8.0 to 0.9.0.
- [Release notes](https://github.com/webpack-contrib/mini-css-extract-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/mini-css-extract-plugin/compare/v0.8.0...v0.9.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-08 09:06:06 +01:00
dependabot-preview[bot]
f92db975fe Bump style-loader from 1.0.1 to 1.1.2 (#67)
Bumps [style-loader](https://github.com/webpack-contrib/style-loader) from 1.0.1 to 1.1.2.
- [Release notes](https://github.com/webpack-contrib/style-loader/releases)
- [Changelog](https://github.com/webpack-contrib/style-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/style-loader/compare/v1.0.1...v1.1.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-08 09:05:50 +01:00
dependabot-preview[bot]
1abfdf0e33 Bump eslint-plugin-jest from 23.1.1 to 23.2.0 (#68)
Bumps [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) from 23.1.1 to 23.2.0.
- [Release notes](https://github.com/jest-community/eslint-plugin-jest/releases)
- [Changelog](https://github.com/jest-community/eslint-plugin-jest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jest-community/eslint-plugin-jest/compare/v23.1.1...v23.2.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-08 09:05:26 +01:00
Manuel Bouza
f787f88337 Update placeholder text and color 2019-12-24 11:42:43 +01:00
dependabot-preview[bot]
77653eff13 Bump eslint-plugin-react from 7.15.1 to 7.17.0 (#48)
Bumps [eslint-plugin-react](https://github.com/yannickcr/eslint-plugin-react) from 7.15.1 to 7.17.0.
- [Release notes](https://github.com/yannickcr/eslint-plugin-react/releases)
- [Changelog](https://github.com/yannickcr/eslint-plugin-react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yannickcr/eslint-plugin-react/compare/v7.15.1...v7.17.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-23 15:45:28 +01:00
dependabot-preview[bot]
6a1725e655 Bump eslint from 6.5.1 to 6.8.0 (#53)
Bumps [eslint](https://github.com/eslint/eslint) from 6.5.1 to 6.8.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v6.5.1...v6.8.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-23 15:45:12 +01:00
dependabot-preview[bot]
a7e3199eb6 Bump @babel/polyfill from 7.6.0 to 7.7.0 (#52)
Bumps [@babel/polyfill](https://github.com/babel/babel) from 7.6.0 to 7.7.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.6.0...v7.7.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-23 15:44:52 +01:00
dependabot-preview[bot]
4f5531548d Bump prettier from 1.18.2 to 1.19.1 (#51)
Bumps [prettier](https://github.com/prettier/prettier) from 1.18.2 to 1.19.1.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/1.18.2...1.19.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-23 15:44:37 +01:00
dependabot-preview[bot]
df70dbe047 Bump react-dom from 16.10.1 to 16.12.0 (#50)
Bumps [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) from 16.10.1 to 16.12.0.
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v16.12.0/packages/react-dom)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-23 15:44:20 +01:00
dependabot-preview[bot]
51c9730d1a Bump query-string from 6.8.3 to 6.9.0 (#47)
Bumps [query-string](https://github.com/sindresorhus/query-string) from 6.8.3 to 6.9.0.
- [Release notes](https://github.com/sindresorhus/query-string/releases)
- [Commits](https://github.com/sindresorhus/query-string/compare/v6.8.3...v6.9.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-23 15:43:55 +01:00
dependabot-preview[bot]
4b29fe3aaa Bump copy-webpack-plugin from 5.0.5 to 5.1.1 (#46)
Bumps [copy-webpack-plugin](https://github.com/webpack-contrib/copy-webpack-plugin) from 5.0.5 to 5.1.1.
- [Release notes](https://github.com/webpack-contrib/copy-webpack-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/copy-webpack-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v5.0.5...v5.1.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-23 15:43:40 +01:00
dependabot-preview[bot]
ac4dcb259a Bump eslint-plugin-jest from 22.17.0 to 23.1.1 (#45)
Bumps [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) from 22.17.0 to 23.1.1.
- [Release notes](https://github.com/jest-community/eslint-plugin-jest/releases)
- [Changelog](https://github.com/jest-community/eslint-plugin-jest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jest-community/eslint-plugin-jest/compare/v22.17.0...v23.1.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-23 15:43:28 +01:00
dependabot-preview[bot]
88f7f04fa8 Bump babel-plugin-module-resolver from 3.2.0 to 4.0.0 (#44)
Bumps [babel-plugin-module-resolver](https://github.com/tleunen/babel-plugin-module-resolver) from 3.2.0 to 4.0.0.
- [Release notes](https://github.com/tleunen/babel-plugin-module-resolver/releases)
- [Changelog](https://github.com/tleunen/babel-plugin-module-resolver/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tleunen/babel-plugin-module-resolver/compare/v3.2.0...v4.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-23 15:43:15 +01:00
dependabot-preview[bot]
c310f48ae9 Bump @babel/preset-react from 7.0.0 to 7.7.4 (#43)
Bumps [@babel/preset-react](https://github.com/babel/babel) from 7.0.0 to 7.7.4.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.0.0...v7.7.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-23 15:42:55 +01:00
dependabot-preview[bot]
f061b81fc8 Bump @babel/plugin-proposal-decorators from 7.6.0 to 7.7.4 (#42)
Bumps [@babel/plugin-proposal-decorators](https://github.com/babel/babel) from 7.6.0 to 7.7.4.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.6.0...v7.7.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-23 15:42:20 +01:00
dependabot-preview[bot]
3387c00d07 Bump @babel/plugin-proposal-optional-chaining from 7.6.0 to 7.7.5 (#41)
Bumps [@babel/plugin-proposal-optional-chaining](https://github.com/babel/babel) from 7.6.0 to 7.7.5.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.6.0...v7.7.5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-23 15:41:48 +01:00
dependabot-preview[bot]
cfb71d2877 Bump webpack from 4.41.2 to 4.41.4 (#54)
Bumps [webpack](https://github.com/webpack/webpack) from 4.41.2 to 4.41.4.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v4.41.2...v4.41.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: Manuel Bouza <manuel@bouza.ch>
2019-12-23 15:41:14 +01:00
dependabot-preview[bot]
1ed1d29c99 Bump node-sass from 4.12.0 to 4.13.0 (#55)
Bumps [node-sass](https://github.com/sass/node-sass) from 4.12.0 to 4.13.0.
- [Release notes](https://github.com/sass/node-sass/releases)
- [Changelog](https://github.com/sass/node-sass/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sass/node-sass/compare/v4.12.0...v4.13.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-23 15:37:14 +01:00
dependabot-preview[bot]
aa8abf381d Bump webpack-cli from 3.3.9 to 3.3.10 (#56)
Bumps [webpack-cli](https://github.com/webpack/webpack-cli) from 3.3.9 to 3.3.10.
- [Release notes](https://github.com/webpack/webpack-cli/releases)
- [Changelog](https://github.com/webpack/webpack-cli/blob/v3.3.10/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-cli/compare/v3.3.9...v3.3.10)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-23 15:36:52 +01:00
dependabot-preview[bot]
9e1a5a713c Bump mobx from 5.14.0 to 5.15.1 (#57)
Bumps [mobx](https://github.com/mobxjs/mobx) from 5.14.0 to 5.15.1.
- [Release notes](https://github.com/mobxjs/mobx/releases)
- [Changelog](https://github.com/mobxjs/mobx/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mobxjs/mobx/compare/5.14.0...5.15.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-23 15:36:40 +01:00
dependabot-preview[bot]
f881e2fb3d Bump eslint-plugin-prettier from 3.1.1 to 3.1.2 (#58)
Bumps [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier) from 3.1.1 to 3.1.2.
- [Release notes](https://github.com/prettier/eslint-plugin-prettier/releases)
- [Changelog](https://github.com/prettier/eslint-plugin-prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/eslint-plugin-prettier/compare/v3.1.1...v3.1.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-23 15:36:18 +01:00
dependabot-preview[bot]
2a5f3db563 Bump mobx-react from 6.1.3 to 6.1.4 (#59)
Bumps [mobx-react](https://github.com/mobxjs/mobx-react) from 6.1.3 to 6.1.4.
- [Release notes](https://github.com/mobxjs/mobx-react/releases)
- [Changelog](https://github.com/mobxjs/mobx-react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mobxjs/mobx-react/compare/6.1.3...6.1.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-23 11:08:27 +01:00
dependabot-preview[bot]
eb2422ba7a Bump css-loader from 3.3.0 to 3.4.0 (#60)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 3.3.0 to 3.4.0.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v3.3.0...v3.4.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-23 11:08:02 +01:00
dependabot-preview[bot]
a2f3c16aca Bump css-loader from 3.2.0 to 3.3.0 (#39)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 3.2.0 to 3.3.0.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v3.2.0...v3.3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-11 08:52:05 +01:00
dependabot-preview[bot]
36d5bf2a7e Bump @babel/preset-env from 7.6.2 to 7.7.6 (#40)
Bumps [@babel/preset-env](https://github.com/babel/babel) from 7.6.2 to 7.7.6.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.6.2...v7.7.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-11 08:51:43 +01:00
dependabot-preview[bot]
9ecc561d4a Bump @babel/plugin-proposal-nullish-coalescing-operator (#30)
Bumps [@babel/plugin-proposal-nullish-coalescing-operator](https://github.com/babel/babel) from 7.4.4 to 7.7.4.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.4.4...v7.7.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-11 08:48:11 +01:00
dependabot-preview[bot]
df1a21242b Bump react from 16.10.1 to 16.12.0 (#32)
Bumps [react](https://github.com/facebook/react/tree/HEAD/packages/react) from 16.10.1 to 16.12.0.
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v16.12.0/packages/react)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-11 08:47:58 +01:00
dependabot-preview[bot]
e46263657e Bump file-loader from 4.2.0 to 5.0.2 (#31)
Bumps [file-loader](https://github.com/webpack-contrib/file-loader) from 4.2.0 to 5.0.2.
- [Release notes](https://github.com/webpack-contrib/file-loader/releases)
- [Changelog](https://github.com/webpack-contrib/file-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/file-loader/compare/v4.2.0...v5.0.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-11 08:47:22 +01:00
dependabot-preview[bot]
0461b31c36 Bump style-loader from 1.0.0 to 1.0.1 (#28)
Bumps [style-loader](https://github.com/webpack-contrib/style-loader) from 1.0.0 to 1.0.1.
- [Release notes](https://github.com/webpack-contrib/style-loader/releases)
- [Changelog](https://github.com/webpack-contrib/style-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/style-loader/compare/v1.0.0...v1.0.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-11 08:46:54 +01:00
dependabot-preview[bot]
ad772b7900 Bump dotenv from 8.1.0 to 8.2.0 (#29)
Bumps [dotenv](https://github.com/motdotla/dotenv) from 8.1.0 to 8.2.0.
- [Release notes](https://github.com/motdotla/dotenv/releases)
- [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md)
- [Commits](https://github.com/motdotla/dotenv/compare/v8.1.0...v8.2.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-11 08:46:39 +01:00
dependabot-preview[bot]
fd4911ec72 Bump @babel/plugin-proposal-class-properties from 7.5.5 to 7.7.4 (#34)
Bumps [@babel/plugin-proposal-class-properties](https://github.com/babel/babel) from 7.5.5 to 7.7.4.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.5.5...v7.7.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-11 08:46:07 +01:00
dependabot-preview[bot]
3b3816067e Bump css-loader from 3.2.0 to 3.2.1 (#35)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 3.2.0 to 3.2.1.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v3.2.0...v3.2.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-11 08:45:04 +01:00
dependabot-preview[bot]
31eb6094e8 Bump webpack from 4.41.0 to 4.41.2 (#36)
Bumps [webpack](https://github.com/webpack/webpack) from 4.41.0 to 4.41.2.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v4.41.0...v4.41.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-11 08:44:34 +01:00
dependabot-preview[bot]
0e29686b2d Bump copy-webpack-plugin from 5.0.4 to 5.0.5 (#37)
Bumps [copy-webpack-plugin](https://github.com/webpack-contrib/copy-webpack-plugin) from 5.0.4 to 5.0.5.
- [Release notes](https://github.com/webpack-contrib/copy-webpack-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/copy-webpack-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v5.0.4...v5.0.5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-11 08:44:15 +01:00
Manuel Bouza
1447fd6116 fix/trello-and-asana (#38)
* Attach click event listener to window

* Update development server port

* Asana: Read project identifier from topbar page header

* Pump version and update changelog
2019-12-10 21:00:22 +01:00
Manuel Bouza
1dcda94483 feature/project-identifier-in-trello-board-title (#27)
* Read project identifier from Trello board title

* Refactor

* Update changelog and pump version
2019-10-25 11:13:50 +02:00
manubo
7e249202e5 Remove focused button border in firefox 2019-10-17 18:51:53 +02:00
Manuel Bouza
12c8b8e3eb fix/focus-timer-view (#26)
* Auto focus button in timer view

* Revert find projects by identifier without alphanumerical characters

* Update changelog
2019-10-17 11:47:05 +02:00
manubo
76d57729f4 Update changelog 2019-10-10 15:18:59 +02:00
Manuel Bouza
72626a6c42 qw/timer (#23)
* Rename logo and add 32x32 version

* Set timer icon if a timer is running

* Do not query activities on initialization

* Show timer in bubble if timed activity exists

* Pass timed activity to App

* Code cleanup

* Show timer view and stop timer

* Make hours optional

* Use booked seconds instead of hours

* Add type submit to form button

* Define colors as sass variables⎄

* Style timer view

* Show start timer submit label

* Update view layouts and content

* Update version and changelog

* Dyanically set iframe height

* Reduce h1 font size

* Add svg webpack loader

* Parse empty string (TimeInputParser)

* Forward ref in Popup component

* Start time on current day only, format buttons

* Improve styling

* Set standard height as iframe default height, validate form

* Upgrade packages to supress react warning

* Show activity form in popup after timer was stoped

* Use stop-watch icon in timer view

* Fix empty description

* Close TimerView if timer stopped for current service

* Style timerview

* Improve timer view styling

* qw/setting-time-tracking-hh-mm (#24)

* Format duration depending on settingTimeTrackingHHMM

* Fix formatDuation without second argument

* Fix time format after updating bubble

* Add tests for formatDuration
2019-10-10 14:57:01 +02:00
Manuel Bouza
7023b4b482 feature/strip-identifier (#25)
* Ignore non-alphanumeric chars finding project by identifier

* Add babel plugin nullish coallescing operator

* Refactor

* Add projectId to remote services
2019-10-10 14:38:28 +02:00
manubo
53be150788 Update .env.example 2019-09-20 10:28:48 +02:00
manubo
6980df91d7 Update changelog and pump version 2019-09-20 10:18:43 +02:00
manubo
83faab7fd4 Fix deletion of manifest.json after every build 2019-09-19 09:19:48 +02:00
Manuel Bouza
8a72f242f9 Preselect default task (#22) 2019-09-18 12:54:54 +02:00
Manuel Bouza
5e62e16751 Upgrade packages (#21) 2019-09-18 05:53:17 +02:00
Manuel Bouza
986fc64998 Make description of activity optional (#20) 2019-06-26 10:13:59 +02:00
Manuel Bouza
8b2e21c3cf Update eslint cofig 2019-06-26 09:27:31 +02:00
Manuel Bouza
fd04d6bf6c Remove bugsnag (#19) 2019-05-24 13:34:15 +02:00
Manuel Bouza
23c9af90b3 Support EU-hosted wrike.com 2019-05-03 08:47:19 +02:00
Manuel Bouza
a9d1726707 feature/wrike (#17)
* Fix code styles

* Add support for WRIKE

* Add tests
2019-04-26 13:05:14 +02:00
Manuel Bouza
25773cc661 fix/annoying-closing-of-trello-card (#16)
* Fix unexpected closing of trello card when clicking on bubble

* Have latest change at the top in changelog
2019-04-24 15:21:36 +02:00
Manuel Bouza
cd9f94423c Fix path of remoteServices in README 2019-04-18 18:42:48 +02:00
Manuel Bouza
505e3a32ab feature/show-customer-in-project-select (#15)
* Fix code styles

* Show customer name in select control if props.data.customerName is defined.

* Pump version and update changelock
2019-04-12 05:40:44 +02:00
Manuel Bouza
81c7d0ca5d fix/asana-refactor (#14)
* Add packages eslint-plugin-prettier and eslint-config-prettier

These packages add better code formatting support in VS Code

* Fix code styles

* Update projectId query selector for asana service

* Extract constants to own computed getter methods

* Update changelog, bump version
2019-04-10 07:45:05 +02:00
Manuel Bouza
173a1d8e62 Update changelog 2019-04-06 12:44:07 +02:00
Manuel Bouza
4bebae9abe fix/hours-in-brackets-unbillable (#13)
* Set billable to false if hours are entere in brackets

* Fix code style

* Add TODO comment for refactoring
2019-04-06 12:42:54 +02:00
Manuel Bouza
97cea77b7a fix/missing-bugsnag-key-error (#12)
* Only instantiate bugsnag client if BUGSNAG_API_KEY is defined

* Pump version, update CHANGELOG
2019-04-04 20:15:17 +02:00
Manuel Bouza
e57caa8563 feature/meistertask-project-from-title (#11)
* Add .prettierrc

* Read project identifier from card title in the meistertask service
2019-04-04 16:08:08 +02:00
Manuel Bouza
e582f99a94 Add .prettierrc 2019-04-04 09:51:24 +02:00
Manuel Bouza
16d41fc2d4 Update changelog 2019-04-01 17:43:31 +02:00
Manuel Bouza
1d2e336e3d Fix project preselection when project identifier is not defined (#9) 2019-04-01 17:41:54 +02:00
59 changed files with 6293 additions and 4986 deletions

View File

@@ -3,6 +3,7 @@
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }],
["@babel/plugin-proposal-optional-chaining"]
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-proposal-nullish-coalescing-operator"
]
}

View File

@@ -1,2 +1 @@
BUGSNAG_API_KEY=
APPLICATION_ID=

View File

@@ -1,5 +1,5 @@
{
"extends": ["eslint:recommended", "plugin:react/recommended"],
"extends": ["eslint:recommended", "plugin:react/recommended", "plugin:prettier/recommended"],
"env": {
"browser": true,
"commonjs": true,
@@ -22,6 +22,11 @@
},
"sourceType": "module"
},
"settings": {
"react": {
"version": "detect"
}
},
"rules": {
"strict": 0,
"semi": ["error", "never"],
@@ -68,6 +73,7 @@
"userStore"
]
}
]
],
"prettier/prettier": "error"
}
}

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
node_modules/
build/
.env
/.idea

1
.nvmrc Normal file
View File

@@ -0,0 +1 @@
14

5
.prettierrc Normal file
View File

@@ -0,0 +1,5 @@
{
"semi": false,
"trailingComma": "all",
"printWidth": 100
}

View File

@@ -1,42 +1,179 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
- Add support for starting/stopping a timer
- Show hours as HH:MM or decimal in the Bubble, depending on setting in MOCO
## [1.0.18] - 2019-03-23
### Added
- First release of version 1
## [1.0.19] - 2019-03-26
### Changed
- Position Bubble in the bottom right by default
## [1.5.2] - 2020-09-10
### Fixed
- Set default value of subdomain to `__unset__` to prevent network error if it is empty
## [1.0.20] - 2019-03-26
- Remember last tracked project and task on card
## [1.5.1] - 2020-08-04
### Added
- Add support for tags in description
## [1.0.21] - 2019-03-26
### Changed
- Update README with example configuration and instructions for local installation
- Add support for Monday
## [1.5.0] - 2020-06-15
### Added
- Allow to override hosts for Jira, Youtrack and Gitlab in options (implemented by yay-digital.de)
## [1.4.0] - 2020-04-27
### Added
- Add support for Gitlab merge requests and issues
## [1.3.4] - 2020-01-09
### Added
- Asana: read task title from single task pane
## [1.3.3] - 2019-10-17
### Fixed
- Fix an issue on Trello where the card closes when clicking the MOCO bubble
- Asana: read project title from page heading
## [1.3.2] - 2019-10-24
### Added
- Read project identifier from Trello board title
## [1.3.1] - 2019-10-17
### Fixed
- Set propper focus on timer view
### Removed
- Find projects by identifier without alphanumerical characters
## [1.3.0] - 2019-10-11
### Added
- Start a new timer or stop a running timer
- Format time as set in time tracking
- Add support for project identifier in Github Issue, Trello, Wunderlist, Youtrack
- Find projects by identifier without alphanumerical characters
## [1.2.4] - 2019-09-20
## [1.0.22] - 2019-03-28
### Changed
- Change the default value of subdomain to `unset` to have a well-formed URL.
- Preselect last used task per project
## [1.2.3] - 2019-06-26
### Changed
- Description of activities are optional
## [1.2.2] - 2019-05-24
### Removed
- Bugsnag client
## [1.2.1] - 2019-05-03
### Fixed
- Support EU-hosted wrike.com (app-eu.wrike.com)
## [1.2.0] - 2019-04-26
### Added
- Add support for wrike.com
## [1.1.5] - 2019-04-24
### Fixed
- Unexpected closing of Trello card when clicking on Bubble
## [1.1.4] - 2019-04-11
### Added
- Show customer name in the project select box
## [1.1.3] - 2019-04-10
### Fixed
- Read projected identifier in Asana's "My tasks"-view
## [1.1.2] - 2019-04-06
### Fixed
- Allow production build without BUGSNAG_API_KEY
- Hours entered in brackets must be non-billable
### Changed
- Read project identifier also from card title in the meistertask service
## [1.1.1] - 2019-04-01
### Fixed
- Discard projects with undefined identifier for preselecting
## [1.1.0] - 2019-03-30
### Added
- Read project identifier from Asana project title
- Add support for meistertask.com
### Fixed
- Link logo in modal to MOCO activities page
- Set full url on service, including query params
## [1.0.22] - 2019-03-28
### Changed
- Change the default value of subdomain to `unset` to have a well-formed URL.
## [1.0.21] - 2019-03-26
### Changed
- Update README with example configuration and instructions for local installation
## [1.0.20] - 2019-03-26
### Added
- Add support for tags in description
## [1.0.19] - 2019-03-26
### Changed
- Position Bubble in the bottom right by default
### Fixed
- Set default value of subdomain to `__unset__` to prevent network error if it is empty
## [1.0.18] - 2019-03-23
### Added
- First release of version 1

View File

@@ -2,33 +2,34 @@
## Development
* run `yarn`
* run `yarn start:chrome` or `yarn start:firefox` (`yarn start` is an alias for `yarn start:chrome`)
* load extension into browser:
* Chrome: visit `chrome://extensions` and load unpacked extension from `build/chrome`
* Firefox: visit `about:debugging` and load temporary Add-on from `build/firefox`
* reload browser extension after change
- run `yarn`
- run `yarn start:chrome` or `yarn start:firefox` (`yarn start` is an alias for `yarn start:chrome`)
- load extension into browser:
- Chrome: visit `chrome://extensions` and load unpacked extension from `build/chrome`
- Firefox: visit `about:debugging` and load temporary Add-on from `build/firefox/manifest.json`
- the browser should automatically pick up your changes but from time to time it may be useful to reload the extension
## Production Build
* bump version in `package.json`
* run `yarn build`
* The Chrome and Firefox extensions are available as ZIP-files in `build/chrome` and `build/firefox` respectively
- bump version in `package.json`
- Update `CHANGELOG.md`
- run `yarn build`
- The Chrome and Firefox extensions are available as ZIP-files in `build/chrome` and `build/firefox` respectively
## Install Local Builds
### Chrome
1. `yarn build:chrome`
1. Visit `chrome://extensions`
2. Enable `Developer mode`
3. `Load unpacked` and select the `build/chrome` folder.
- `yarn build:chrome`
- Visit `chrome://extensions`
- Enable `Developer mode`
- `Load unpacked` and select the `build/chrome` folder.
### Firefox
1. `yarn build:firefox`
1. Visit `about:debugging`
2. Click on `Load temporary Add-on` and select the ZIP-file in `build/firefox`
- `yarn build:firefox`
- Visit `about:debugging`
- Click on `Load temporary Add-on` and select the ZIP-file in `build/firefox`
Only signed extensions can be permantly installed in Firefox (unless you are using <em>Firefox Developer Edition</em>). To sign the build, proceed as described in [Getting started with web-ext](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Getting_started_with_web-ext).
@@ -38,7 +39,7 @@ You can keep the extension settings between builds by providing a stable `APPLIC
## Remote Service Configuration
Remote services are configured in `src/remoteServices.js`.
Remote services are configured in `src/js/remoteServices.js`.
A remote service is configured as follows:
@@ -46,9 +47,10 @@ A remote service is configured as follows:
{
service_key: {
name: "service_name",
host: "https://:subdomain.example.com",
urlPatterns: [
"https:\\://:subdomain.example.com/card/:id",
[/^https:\/\/(\w+).example.com\/card\/(\d+), ["subdomain", "id"]],
":host:/card/:id",
[/^:host:\/card\/(\d+), ["subdomain", "id"]],
],
queryParams: {
projectId: "currentList"
@@ -63,16 +65,17 @@ A remote service is configured as follows:
projectId: (document, service, { subdomain, id, projectId }) => {
return projectId
},
position: { left: "50%", transform: "translate(-50%)" }
position: { left: "50%", transform: "translate(-50%)" },
allowHostOverride: false,
}
}
```
| Parameter | Description |
|--------------|:-------------|
| ------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| service_key | `string` &mdash; Unique identifier for the service |
| service_name | `string` &mdash; Must be one of the registered services `trello`, `jira`, `asana`, `wunderlist`, `github` or `youtrack` |
| urlPatterns | `string` \| `RegEx` &mdash; A valid URL pattern or regular expression, as described in the [url-pattern](https://www.npmjs.com/package/url-pattern) package. |
| urlPatterns | `string` \| `RegEx` &mdash; A valid URL pattern or regular expression, as described in the [url-pattern](https://www.npmjs.com/package/url-pattern) package. `:host:` will be replaced with the configured host before applying the pattern (can be configured in the settings if `allowHostOverride` is true. |
| queryParams | `Object` &mdash; The object value is the name of the query parameter and the key the property it will available on, e.g. the value of the query parameter `currentList` will be available under `projectId`. Matches in `urlPatterns` have precedence over matches in `queryParams`. |
| description | `undefined` \| `string` \| `function` &mdash; The default description of the service. If it is a function, it will receive `window.document`, the current `service` and an object with the URL `matches` as arguments, and the return value set as the default description. |
| projectId | `undefined` \| `string` \| `function` &mdash; The pre-selected project of the service matching the MOCO project identifier. If it is a function, it will receive `window.document`, the current `service` and an object with the URL `matches` as arguments, and must return the MOCO project identifier or `undefined`. |

View File

@@ -1,7 +1,7 @@
{
"name": "moco-browser-extensions",
"description": "Browser plugin for MOCO",
"version": "1.1.0",
"version": "1.5.2",
"license": "MIT",
"scripts": {
"start": "yarn start:chrome",
@@ -16,53 +16,54 @@
"test:watch": "node_modules/.bin/jest --watch"
},
"dependencies": {
"@babel/polyfill": "^7.4.0",
"@bugsnag/js": "^5.2.0",
"@bugsnag/plugin-react": "^5.2.0",
"axios": "^0.18.0",
"@babel/polyfill": "^7.10.1",
"axios": "^0.19.2",
"classnames": "^2.2.6",
"date-fns": "^1.30.1",
"dotenv": "^7.0.0",
"date-fns": "^2.15.0",
"dotenv": "^8.2.0",
"lodash": "^4.17.11",
"mobx": "^5.5.0",
"mobx-react": "^5.2.8",
"mobx": "^5.15.4",
"mobx-react": "^6.2.2",
"prop-types": "^15.6.2",
"query-string": "^6.2.0",
"react": "^16.8.0",
"react-dom": "^16.8.0",
"react-select": "^2.3.0",
"query-string": "^6.12.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-select": "^3.1.0",
"react-spring": "^8.0.7",
"url-pattern": "^1.0.3"
},
"devDependencies": {
"@babel/core": "^7.2.2",
"@babel/plugin-proposal-class-properties": "^7.2.2",
"@babel/plugin-proposal-decorators": "^7.2.2",
"@babel/plugin-proposal-optional-chaining": "^7.2.0",
"@babel/preset-env": "^7.2.2",
"@babel/preset-react": "^7.0.0",
"babel-eslint": "^10.0.1",
"babel-loader": "^8.0.4",
"babel-plugin-module-resolver": "^3.1.1",
"clean-webpack-plugin": "^1.0.1",
"copy-webpack-plugin": "^4.6.0",
"copyfiles": "^2.1.0",
"css-loader": "^2.1.0",
"eslint": "^5.7.0",
"eslint-plugin-jest": "^22.2.2",
"eslint-plugin-react": "^7.11.1",
"file-loader": "^3.0.1",
"html-webpack-plugin": "^3.2.0",
"jest": "^24.1.0",
"mini-css-extract-plugin": "^0.5.0",
"node-sass": "^4.11.0",
"prettier": "^1.16.4",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.1",
"uuid": "^3.3.2",
"webpack": "^4.15.0",
"webpack-bugsnag-plugins": "^1.3.0",
"webpack-cli": "^3.0.8",
"@babel/core": "^7.10.2",
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/plugin-proposal-decorators": "^7.10.1",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3",
"@babel/plugin-proposal-optional-chaining": "^7.9.0",
"@babel/preset-env": "^7.11.0",
"@babel/preset-react": "^7.10.1",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0",
"babel-plugin-module-resolver": "^4.0.0",
"clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "^6.0.2",
"copyfiles": "^2.3.0",
"css-loader": "^4.2.0",
"eslint": "7.6.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-jest": "^23.13.2",
"eslint-plugin-prettier": "^3.1.3",
"eslint-plugin-react": "^7.20.0",
"file-loader": "^6.0.0",
"html-webpack-plugin": "^4.3.0",
"jest": "^26.0.1",
"mini-css-extract-plugin": "^0.11.2",
"node-sass": "^4.14.1",
"prettier": "^2.0.5",
"sass-loader": "^9.0.2",
"style-loader": "^1.2.1",
"svg-inline-loader": "^0.8.2",
"uuid": "^8.3.0",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"zip-webpack-plugin": "^3.0.0"
}
}

View File

@@ -7,17 +7,25 @@ button.moco-bx-btn {
white-space: nowrap;
color: white;
background-image: none;
background-color: #7dc332;
border-color: #7dc332;
background-color: $green;
border-color: $green;
border-radius: 0;
border-style: solid;
box-shadow: none;
font-size: 100%;
cursor: pointer;
&:focus {
outline: none;
}
&::-moz-focus-inner {
border: 0;
}
&:hover:not(:disabled) {
background-color: #639a28;
border-color: #639a28;
background-color: $green-dark;
border-color: $green-dark;
}
&:disabled {
@@ -40,3 +48,17 @@ button.moco-bx-btn {
margin-left: 0.5rem;
}
}
.moco-bx-btn__secondary {
color: $blue;
border: none;
background: none;
text-decoration: none;
&:hover {
cursor: pointer;
color: $blue;
border: none;
background-color: transparent;
}
}

View File

@@ -1,3 +1,4 @@
@import "variables";
@import "button";
input {
@@ -15,7 +16,8 @@ input {
margin-bottom: 0.25rem;
}
input, textarea {
input,
textarea {
padding: 6px 12px;
background-color: white;
border-color: #cccccc;
@@ -24,6 +26,10 @@ input {
border-style: solid;
border-width: 1px;
min-height: 20px;
&::placeholder {
color: #ccc;
}
}
.text-muted {
@@ -31,13 +37,14 @@ input {
}
&.has-error {
input, textarea {
border-color: #FB3A2F;
input,
textarea {
border-color: $red;
}
}
.form-error {
color: #FB3A2F;
color: $red;
}
.input-group {
@@ -56,8 +63,13 @@ input {
text-align: center;
background-color: #eeeeee;
border: 1px solid #cccccc;
border-left: none;
line-height: 18px;
&--right {
border-left: none;
}
&--left {
border-right: none;
}
}
}
}
@@ -71,8 +83,8 @@ input[name="hours"] {
outline: 0 !important;
&:focus {
border: 1px solid #38b5eb;
box-shadow: 0 0 0 1px #38b5eb;
border: 1px solid $blue;
box-shadow: 0 0 0 1px $blue;
}
}
@@ -84,8 +96,7 @@ textarea[name="description"] {
outline: 0 !important;
&:focus {
border: 1px solid #38b5eb;
box-shadow: 0 0 0 1px #38b5eb;
border: 1px solid $blue;
box-shadow: 0 0 0 1px $blue;
}
}

View File

@@ -1,5 +1,10 @@
$font-family: Arial, sans-serif;
$font-family: Roboto, Arial, sans-serif;
$font-color: #191919;
$popup-width: 420px;
$popup-height: 463px;
$green: #7dc332;
$green-dark: #639a28;
$blue: #38b5eb;
$red: #fb3a2f;
$gray-base: #a3a3a3;

View File

@@ -6,6 +6,10 @@
color: $font-color;
pointer-events: all;
.text-red {
color: $red;
}
.moco-bx-bubble {
box-sizing: content-box;
position: fixed;
@@ -14,8 +18,7 @@
width: 60px;
background-color: white;
border-radius: 50%;
box-shadow: -1px -1px 15px 4px rgba(0, 0, 0, 0.05),
2px 2px 15px 4px rgba(0, 0, 0, 0.05);
box-shadow: -1px -1px 15px 4px rgba(0, 0, 0, 0.05), 2px 2px 15px 4px rgba(0, 0, 0, 0.05);
padding: 5px;
z-index: 9999;
@@ -69,6 +72,7 @@
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.4);
z-index: 9999;
.moco-bx-popup-content {
background-color: white;

View File

@@ -11,6 +11,11 @@
.moco-bx-options {
padding: 0rem 2rem 2rem;
a {
color: $blue;
text-decoration: none;
}
p {
margin: 0.5rem 0;
}
@@ -20,6 +25,11 @@
margin: 1rem 0 2rem;
}
h3 {
font-size: 1.1rem;
margin: 1rem 0 1rem;
}
label {
font-weight: normal;
margin-bottom: 5px;
@@ -32,11 +42,21 @@
}
.text-success {
color: #7DC332;
color: $green;
}
.text-danger {
color: #FB3A2F;
color: $red;
}
&__host-overrides {
margin-bottom: 1.5rem;
text-align: center;
font-weight: normal;
}
small {
font-size: 0.8rem;
}
}
}

View File

@@ -1,6 +1,6 @@
@import "variables";
@import "form";
@import "spinner";
@import "variables";
html {
overflow: hidden;
@@ -14,33 +14,48 @@ html {
#moco-bx-root {
min-width: 516px;
h1 {
font-size: 24px;
font-weight: normal;
line-height: 1.5;
margin-top: 1rem;
margin-bottom: 3rem;
}
h2 {
font-size: 20px;
font-weight: normal;
line-height: 1.5;
margin-top: 1rem;
margin-bottom: 3rem;
}
.text-red {
color: $red;
}
.text-secondary {
color: $gray-base;
}
.moco-bx-app-container {
width: 324px;
padding: 3rem 6rem;
.moco-bx-logo__container {
display: flex;
justify-content: center;
margin-bottom: 3rem;
text-align: center;
img.moco-bx-logo {
flex: 0 0 48px;
width: 48px;
height: 48px;
}
h1 {
line-height: 48px;
margin: 0;
}
}
.moco-bx-calendar {
display: flex;
justify-content: space-between;
margin-bottom: 3rem;
margin-bottom: 2rem;
.moco-bx-calendar__day {
display: flex;
@@ -66,12 +81,11 @@ html {
flex: 0 0 42px;
color: white;
background-color: #eee;
}
&.moco-bx-calendar__day--filled {
.moco-bx-calendar__hours {
background-color: #7dc332;
background-color: $green;
}
}
@@ -84,43 +98,73 @@ html {
&.moco-bx-calendar__day--active {
.moco-bx-calendar__hours {
background-color: #38b5eb;
background-color: $blue;
}
}
}
}
.moco-bx-timer-view {
text-align: center;
margin-top: 3rem;
h2 {
margin-top: 2rem;
max-height: 90px;
overflow: hidden;
}
p {
margin-top: 1rem;
margin-bottom: 1rem;
line-height: 1.2rem;
}
span.moco-bx-single-line {
display: inline-block;
max-height: 19px;
overflow: hidden;
}
.timer {
margin-top: 2rem;
}
.btn-stop-timer {
margin-top: 1.5rem;
background-color: $red;
border: none;
border-radius: 50%;
width: 60px;
height: 60px;
}
}
}
.moco-bx-error-container {
font-size: 18px;
line-height: 1.5;
width: 420px;
padding: 3rem;
text-align: center;
h1 {
font-size: 35px;
font-weight: normal;
margin-top: 0;
line-height: 1.3;
}
img {
width: auto;
max-width: 100%;
margin-top: 1.5rem;
margin-bottom: 2rem;
&.moco-bx-logo {
width: 48px;
margin-bottom: 2rem;
}
}
ol {
text-align: left;
&.firefox-addons {
margin-top: 0;
width: auto;
}
}
button {
margin-top: 1.5rem;
margin-bottom: 1.5rem;
}
}
}

View File

@@ -0,0 +1 @@
<svg aria-hidden="true" focusable="false" data-prefix="fal" data-icon="stopwatch" class="svg-inline--fa fa-stopwatch fa-w-14" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M393.3 141.3l17.5-17.5c4.7-4.7 4.7-12.3 0-17l-5.7-5.7c-4.7-4.7-12.3-4.7-17 0l-17.5 17.5c-35.8-31-81.5-50.9-131.7-54.2V32h25c6.6 0 12-5.4 12-12v-8c0-6.6-5.4-12-12-12h-80c-6.6 0-12 5.4-12 12v8c0 6.6 5.4 12 12 12h23v32.6C91.2 73.3 0 170 0 288c0 123.7 100.3 224 224 224s224-100.3 224-224c0-56.1-20.6-107.4-54.7-146.7zM224 480c-106.1 0-192-85.9-192-192S117.9 96 224 96s192 85.9 192 192-85.9 192-192 192zm4-128h-8c-6.6 0-12-5.4-12-12V172c0-6.6 5.4-12 12-12h8c6.6 0 12 5.4 12 12v168c0 6.6-5.4 12-12 12z"></path></svg>

After

Width:  |  Height:  |  Size: 733 B

View File

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
src/images/moco-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -3,19 +3,15 @@ import { formatDate } from "utils"
const baseURL = subdomain => {
if (process.env.NODE_ENV === "production") {
return `https://${encodeURIComponent(
subdomain
)}.mocoapp.com/api/browser_extensions`
return `https://${encodeURIComponent(subdomain)}.mocoapp.com/api/browser_extensions`
} else {
return `http://${encodeURIComponent(
subdomain
)}.mocoapp.localhost:3001/api/browser_extensions`
return `http://${encodeURIComponent(subdomain)}.mocoapp.localhost:3000/api/browser_extensions`
}
}
export default class Client {
#client;
#apiKey;
#client
#apiKey
constructor({ subdomain, apiKey, version }) {
this.#apiKey = apiKey
@@ -25,9 +21,9 @@ export default class Client {
headers: {
common: {
"x-api-key": apiKey,
"x-extension-version": version
}
}
"x-extension-version": version,
},
},
})
}
@@ -35,29 +31,31 @@ export default class Client {
this.#client.post("session", {
api_key: this.#apiKey,
remote_service: service?.name,
remote_id: service?.id
});
remote_id: service?.id,
})
projects = () => this.#client.get("projects");
projects = () => this.#client.get("projects")
schedules = (fromDate, toDate) =>
this.#client.get("schedules", {
params: { date: `${formatDate(fromDate)}:${formatDate(toDate)}` }
});
params: { date: `${formatDate(fromDate)}:${formatDate(toDate)}` },
})
activities = (fromDate, toDate) =>
this.#client.get("activities", {
params: { date: `${formatDate(fromDate)}:${formatDate(toDate)}` }
});
params: { date: `${formatDate(fromDate)}:${formatDate(toDate)}` },
})
bookedHours = service => {
activitiesStatus = service => {
if (!service) {
return Promise.resolve({ data: { hours: 0 } })
}
return this.#client.get("activities/tags", {
params: { selection: [service.id], remote_service: service.name }
return this.#client.get("activities/status", {
params: { remote_id: service.id, remote_service: service.name },
})
};
createActivity = activity => this.#client.post("activities", { activity });
}
createActivity = activity => this.#client.post("activities", { activity })
stopTimer = timedActivity => this.#client.get(`activities/${timedActivity.id}/stop_timer`)
}

View File

@@ -1,20 +1,40 @@
import "@babel/polyfill"
import ApiClient from "api/Client"
import {
isChrome,
getCurrentTab,
getSettings,
isBrowserTab
} from "utils/browser"
import { isChrome, getCurrentTab, getSettings, isBrowserTab } from "utils/browser"
import { BackgroundMessenger } from "utils/messaging"
import {
tabUpdated,
settingsChanged,
togglePopup
} from "utils/messageHandlers"
import { tabUpdated, settingsChanged, togglePopup, openPopup } from "utils/messageHandlers"
import { isNil } from "lodash"
const messenger = new BackgroundMessenger()
function timerStoppedForCurrentService(service, timedActivity) {
return timedActivity.service_id && timedActivity.service_id === service?.id
}
function resetBubble({ tab, settings, service, timedActivity }) {
const apiClient = new ApiClient(settings)
apiClient
.activitiesStatus(service)
.then(({ data }) => {
messenger.postMessage(tab, {
type: "showBubble",
payload: {
bookedSeconds: data.seconds,
timedActivity: data.timed_activity,
settingTimeTrackingHHMM: settings.settingTimeTrackingHHMM,
service,
},
})
})
.then(() => {
if (isNil(timedActivity) || timerStoppedForCurrentService(service, timedActivity)) {
messenger.postMessage(tab, { type: "closePopup" })
} else {
openPopup(tab, { service, messenger })
}
})
}
messenger.on("togglePopup", () => {
getCurrentTab().then(tab => {
if (tab && !isBrowserTab(tab)) {
@@ -40,23 +60,12 @@ chrome.runtime.onMessage.addListener(action => {
const apiClient = new ApiClient(settings)
apiClient
.createActivity(activity)
.then(() => {
messenger.postMessage(tab, { type: "closePopup" })
apiClient.bookedHours(service).then(({ data }) => {
messenger.postMessage(tab, {
type: "showBubble",
payload: {
bookedHours: parseFloat(data[0]?.hours) || 0,
service
}
})
})
})
.then(() => resetBubble({ tab, settings, service }))
.catch(error => {
if (error.response?.status === 422) {
chrome.runtime.sendMessage({
type: "setFormErrors",
payload: error.response.data
payload: error.response.data,
})
}
})
@@ -64,6 +73,19 @@ chrome.runtime.onMessage.addListener(action => {
})
}
if (action.type === "stopTimer") {
const { timedActivity, service } = action.payload
getCurrentTab().then(tab => {
getSettings().then(settings => {
const apiClient = new ApiClient(settings)
apiClient
.stopTimer(timedActivity)
.then(() => resetBubble({ tab, settings, service, timedActivity }))
.catch(() => null)
})
})
}
if (action.type === "openOptions") {
let url
if (isChrome()) {

View File

@@ -3,6 +3,7 @@ import PropTypes from "prop-types"
import Spinner from "components/Spinner"
import Form from "components/Form"
import Calendar from "components/Calendar"
import TimerView from "components/App/TimerView"
import { observable, computed } from "mobx"
import { Observer, observer } from "mobx-react"
import { Spring, animated, config } from "react-spring/renderprops"
@@ -14,15 +15,17 @@ import {
findProjectByValue,
findProjectByIdentifier,
findTask,
formatDate
defaultTask,
formatDate,
} from "utils"
import { parseISO } from "date-fns"
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"
import {get} from "lodash/fp";
@observer
class App extends Component {
@@ -34,58 +37,74 @@ class App extends Component {
name: PropTypes.string,
description: PropTypes.string,
projectId: PropTypes.string,
taskId: PropTypes.string
taskId: PropTypes.string,
}),
subdomain: PropTypes.string,
activities: PropTypes.array,
schedules: PropTypes.array,
projects: PropTypes.array,
timedActivity: PropTypes.shape({
customer_name: PropTypes.string.isRequired,
assignment_name: PropTypes.string.isRequired,
task_name: PropTypes.string.isRequired,
timer_started_at: PropTypes.string.isRequired,
seconds: PropTypes.number.isRequired,
}),
lastProjectId: PropTypes.number,
lastTaskId: PropTypes.number,
roundTimeEntries: PropTypes.bool,
fromDate: PropTypes.string,
toDate: PropTypes.string,
errorType: PropTypes.string,
errorMessage: PropTypes.string
};
errorMessage: PropTypes.string,
}
static defaultProps = {
activities: [],
schedules: [],
projects: [],
roundTimeEntries: false
};
}
@observable changeset = {};
@observable formErrors = {};
@observable changeset = {}
@observable formErrors = {}
@computed get project() {
const { service, projects, lastProjectId } = this.props
return (
findProjectByValue(this.changeset.assignment_id)(projects) ||
findProjectByValue(Number(lastProjectId))(projects) ||
findProjectByIdentifier(service?.projectId)(projects) ||
head(projects.flatMap(get("options")))
)
}
@computed get task() {
const { service, lastTaskId } = this.props
return (
findTask(this.changeset.task_id || service?.taskId || lastTaskId)(this.project) ||
defaultTask(this.project?.tasks)
)
}
@computed get billable() {
return /\(.+\)/.test(this.changeset.hours) === true ? false : !!this.task?.billable
}
@computed get changesetWithDefaults() {
const { service, projects, lastProjectId, lastTaskId } = this.props
const project =
findProjectByValue(this.changeset.assignment_id)(projects) ||
findProjectByIdentifier(service?.projectId)(projects) ||
findProjectByValue(Number(lastProjectId))(projects) ||
head(projects)
const task =
findTask(this.changeset.task_id || service?.taskId || lastTaskId)(project) ||
head(project?.tasks)
const { service } = this.props
const defaults = {
remote_service: service?.name,
remote_id: service?.id,
remote_url: service?.url,
date: formatDate(new Date()),
assignment_id: project?.value,
task_id: task?.value,
billable: task?.billable,
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: ""
seconds: new TimeInputParser(this.changeset.hours).parseSeconds(),
description: service?.description || "",
tag: "",
}
return { ...defaults, ...this.changeset }
@@ -93,6 +112,7 @@ class App extends Component {
componentDidMount() {
window.addEventListener("keydown", this.handleKeyDown)
parent.postMessage({ __mocoBX: { iFrameHeight: window.document.body.scrollHeight } }, "*")
chrome.runtime.onMessage.addListener(this.handleSetFormErrors)
}
@@ -101,25 +121,34 @@ class App extends Component {
chrome.runtime.onMessage.removeListener(this.handleSetFormErrors)
}
handleChange = event => {
handleChange = (event) => {
const { projects } = this.props
const {
target: { name, value }
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
this.changeset.task_id = defaultTask(project?.tasks)?.value
}
}
};
handleSelectDate = date => {
handleSelectDate = (date) => {
this.changeset.date = formatDate(date)
};
}
handleSubmit = event => {
handleStopTimer = (timedActivity) => {
const { service } = this.props
chrome.runtime.sendMessage({
type: "stopTimer",
payload: { timedActivity, service },
})
}
handleSubmit = (event) => {
event.preventDefault()
const { service } = this.props
@@ -127,35 +156,36 @@ class App extends Component {
type: "createActivity",
payload: {
activity: extractAndSetTag(this.changesetWithDefaults),
service
}
service,
},
})
};
}
handleKeyDown = event => {
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,
timedActivity,
activities,
schedules,
fromDate,
toDate,
errorType,
errorMessage
errorMessage,
} = this.props
if (loading) {
@@ -175,21 +205,19 @@ class App extends Component {
}
return (
<Spring
native
from={{ opacity: 0 }}
to={{ opacity: 1 }}
config={config.stiff}
>
{props => (
<Spring native from={{ opacity: 0 }} to={{ opacity: 1 }} config={config.stiff}>
{(props) => (
<animated.div className="moco-bx-app-container" style={props}>
<Header subdomain={subdomain} />
<Observer>
{() => (
{() =>
timedActivity ? (
<TimerView timedActivity={timedActivity} onStopTimer={this.handleStopTimer} />
) : (
<>
<Calendar
fromDate={parse(fromDate)}
toDate={parse(toDate)}
fromDate={parseISO(fromDate)}
toDate={parseISO(toDate)}
activities={activities}
schedules={schedules}
selectedDate={new Date(this.changesetWithDefaults.date)}
@@ -203,7 +231,8 @@ class App extends Component {
onSubmit={this.handleSubmit}
/>
</>
)}
)
}
</Observer>
</animated.div>
)}

View File

@@ -0,0 +1,45 @@
import React from "react"
import PropTypes from "prop-types"
import Timer from "components/shared/Timer"
import { parseISO } from "date-fns"
import StopWatch from "components/shared/StopWatch"
export default function TimerView({ timedActivity, onStopTimer }) {
const handleStopTimer = () => {
onStopTimer(timedActivity)
}
return (
<div className="moco-bx-timer-view">
<p>
<span className="moco-bx-single-line text-secondary">{timedActivity.customer_name}</span>
<br />
<span className="moco-bx-single-line">{timedActivity.assignment_name}</span>
<br />
<span className="moco-bx-single-line">{timedActivity.task_name}</span>
</p>
<h2>{timedActivity.description}</h2>
<Timer
className="timer text-red"
startedAt={parseISO(timedActivity.timer_started_at)}
offset={timedActivity.seconds}
style={{ fontSize: "36px", display: "inline-block" }}
/>
<button className="moco-bx-btn btn-stop-timer" onClick={handleStopTimer} autoFocus>
<StopWatch />
</button>
</div>
)
}
TimerView.propTypes = {
timedActivity: PropTypes.shape({
customer_name: PropTypes.string.isRequired,
assignment_name: PropTypes.string.isRequired,
task_name: PropTypes.string.isRequired,
description: PropTypes.string,
timer_started_at: PropTypes.string.isRequired,
seconds: PropTypes.number.isRequired,
}).isRequired,
onStopTimer: PropTypes.func.isRequired,
}

View File

@@ -1,22 +1,46 @@
import React from "react"
import PropTypes from "prop-types"
import logoUrl from "images/logo.png"
import mocoLogo from "images/moco-32x32.png"
import mocoTimerLogo from "images/moco-timer-32x32.png"
import { parseISO } from "date-fns"
import { formatDuration } from "utils"
import Timer from "./shared/Timer"
const Bubble = ({ bookedHours }) => (
const Bubble = ({ bookedSeconds, timedActivity, settingTimeTrackingHHMM }) => {
const logo = timedActivity ? mocoTimerLogo : mocoLogo
return (
<div className="moco-bx-bubble-inner">
<img className="moco-bx-logo" src={chrome.extension.getURL(logoUrl)} />
{bookedHours > 0 ? (
<span className="moco-bx-booked-hours">{bookedHours.toFixed(2)}</span>
) : null}
<img className="moco-bx-logo" src={chrome.extension.getURL(logo)} />
{!timedActivity && bookedSeconds > 0 && (
<span className="moco-bx-booked-hours">
{formatDuration(bookedSeconds, { settingTimeTrackingHHMM, showSeconds: false })}
</span>
)}
{timedActivity && (
<Timer
className="text-red"
startedAt={parseISO(timedActivity.timer_started_at)}
offset={timedActivity.seconds}
style={{ marginBottom: "3px", fontSize: "12px" }}
/>
)}
</div>
)
}
Bubble.propTypes = {
bookedHours: PropTypes.number
bookedSeconds: PropTypes.number,
timedActivity: PropTypes.shape({
timer_started_at: PropTypes.string.isRequired,
seconds: PropTypes.number.isRequired,
}),
settingTimeTrackingHHMM: PropTypes.bool,
}
Bubble.defaultProps = {
bookedHours: 0
bookedSeconds: 0,
settingTimeTrackingHHMM: false,
}
export default Bubble

View File

@@ -10,15 +10,11 @@ const Day = ({ date, hours, absence, active, onClick }) => {
return (
<div
className={cn(
"moco-bx-calendar__day",
`moco-bx-calendar__day--week-day-${getDay(date)}`,
{
className={cn("moco-bx-calendar__day", `moco-bx-calendar__day--week-day-${getDay(date)}`, {
"moco-bx-calendar__day--active": active,
"moco-bx-calendar__day--filled": hours > 0,
"moco-bx-calendar__day--absence": absence
}
)}
"moco-bx-calendar__day--absence": absence,
})}
onClick={handleClick}
>
<span className="moco-bx-calendar__day-of-week">
@@ -34,7 +30,7 @@ Day.propTypes = {
hours: PropTypes.number.isRequired,
absence: PropTypes.object,
active: PropTypes.bool.isRequired,
onClick: PropTypes.func.isRequired
onClick: PropTypes.func.isRequired,
}
export default Day

View File

@@ -35,9 +35,9 @@ Hours.propTypes = {
hours: PropTypes.number.isRequired,
absence: PropTypes.shape({
assignment_code: PropTypes.string,
assignment_color: PropTypes.string
assignment_color: PropTypes.string,
}),
active: PropTypes.bool.isRequired
active: PropTypes.bool.isRequired,
}
export default Hours

View File

@@ -2,27 +2,19 @@ import React from "react"
import PropTypes from "prop-types"
import Day from "./Day"
import { formatDate } from "utils"
import { eachDay } from "date-fns"
import { eachDayOfInterval } from "date-fns"
import { pathEq } from "lodash/fp"
const findAbsence = (date, schedules) =>
schedules.find(pathEq("date", formatDate(date)))
const findAbsence = (date, schedules) => schedules.find(pathEq("date", formatDate(date)))
const hoursAtDate = (date, activities) =>
activities
.filter(pathEq("date", formatDate(date)))
.reduce((acc, activity) => acc + activity.hours, 0)
const Calendar = ({
fromDate,
toDate,
selectedDate,
activities,
schedules,
onChange
}) => (
const Calendar = ({ fromDate, toDate, selectedDate, activities, schedules, onChange }) => (
<div className="moco-bx-calendar">
{eachDay(fromDate, toDate).map(date => (
{eachDayOfInterval({ start: fromDate, end: toDate }).map(date => (
<Day
key={date}
date={date}
@@ -44,17 +36,17 @@ Calendar.propTypes = {
id: PropTypes.number.isRequired,
date: PropTypes.string.isRequired,
hours: PropTypes.number.isRequired,
timer_started_at: PropTypes.string
}).isRequired
timer_started_at: PropTypes.string,
}).isRequired,
),
schedules: PropTypes.arrayOf(
PropTypes.shape({
date: PropTypes.string,
assignment_code: PropTypes.string,
assignment_color: PropTypes.string
})
assignment_color: PropTypes.string,
}),
).isRequired,
onChange: PropTypes.func.isRequired
onChange: PropTypes.func.isRequired,
}
export default Calendar

View File

@@ -3,25 +3,23 @@ import settingsUrl from "images/settings.png"
const InvalidConfigurationError = () => (
<div className="moco-bx-error-container">
<h1>Bitte Einstellungen aktualisieren</h1>
<ol>
<li>Internetadresse eintragen</li>
<li>Persönlichen API-Schlüssel eintragen</li>
</ol>
<h1>MOCO verbinden</h1>
<p>
Dazu trägst Du in den Einstellungen Deine Account-Internetadresse und Deinen API-Schlüssel
ein.
</p>
<img
src={chrome.extension.getURL(settingsUrl)}
alt="Browser extension configuration settings"
style={{ cursor: "pointer", width: "185px", height: "195px" }}
onClick={() => chrome.runtime.sendMessage({ type: "openOptions" })}
/>
<button
className="moco-bx-btn"
onClick={() => chrome.runtime.sendMessage({ type: "openOptions" })}
>
Einstellungen öffnen
Weiter zu den Einstellungen
</button>
<br />
<br />
<img
src={chrome.extension.getURL(settingsUrl)}
alt="Browser extension configuration settings"
style={{ cursor: "pointer" }}
onClick={() => chrome.runtime.sendMessage({ type: "openOptions" })}
/>
</div>
)

View File

@@ -1,21 +1,24 @@
import React from "react"
import PropTypes from "prop-types"
import logo from "images/logo.png"
import logo from "images/moco-159x159.png"
const UnknownError = ({ message = "Unbekannter Fehler" }) => (
<div className="moco-bx-error-container">
<img className="moco-bx-logo" src={logo} alt="MOCO logo" />
<img
className="moco-bx-logo"
src={logo}
style={{ width: "48px", height: "48px" }}
alt="MOCO logo"
/>
<h1>Ups, es ist ein Fehler passiert!</h1>
<p>Bitte überprüfe deine Internetverbindung.</p>
<p>Wir wurden per Email benachrichtigt und untersuchen den Vorfall.</p>
<br />
<p>Fehlermeldung:</p>
<pre>{message}</pre>
</div>
)
UnknownError.propTypes = {
message: PropTypes.string
message: PropTypes.string,
}
export default UnknownError

View File

@@ -1,16 +1,18 @@
import React from "react"
import { isChrome } from "utils/browser"
import logo from "images/logo.png"
import logo from "images/moco-159x159.png"
import firefoxAddons from "images/firefox_addons.png"
const UpgradeRequiredError = () => (
<div className="moco-bx-error-container">
<img className="moco-bx-logo" src={logo} alt="MOCO logo" />
<h1>Upgrade erforderlich</h1>
<p>
Die installierte MOCO Browser-Erweiterung ist veraltet &mdash; bitte
aktualisieren.
</p>
<img
className="moco-bx-logo"
src={logo}
style={{ width: "48px", height: "48px" }}
alt="MOCO logo"
/>
<h1>Bitte aktualisieren</h1>
<p>Die installierte MOCO Browser-Erweiterung ist veraltet &mdash; bitte aktualisieren.</p>
{isChrome() ? (
<button
className="moco-bx-btn"
@@ -20,11 +22,11 @@ const UpgradeRequiredError = () => (
</button>
) : (
<>
<br />
<p>Unter folgender URL:</p>
<img
className="firefox-addons"
src={firefoxAddons}
style={{ width: "292px", height: "40px" }}
alt="about:addons"
/>
</>

View File

@@ -1,31 +1,71 @@
import React, { Component } from "react"
import PropTypes from "prop-types"
import Select from "components/Select"
import { formatDate } from "utils"
import cn from "classnames"
import StopWatch from "components/shared/StopWatch"
class Form extends Component {
static propTypes = {
changeset: PropTypes.shape({
project: PropTypes.object,
task: PropTypes.object,
hours: PropTypes.string
assignment_id: PropTypes.number.isRequired,
billable: PropTypes.bool.isRequired,
date: PropTypes.string.isRequired,
task_id: PropTypes.number.isRequired,
description: PropTypes.string,
remote_id: PropTypes.string,
remote_service: PropTypes.string,
remote_url: PropTypes.string,
seconds: PropTypes.number,
hours: PropTypes.string,
}).isRequired,
errors: PropTypes.object,
projects: PropTypes.array.isRequired,
onChange: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired
};
onSubmit: PropTypes.func.isRequired,
}
static defaultProps = {
inline: true
};
inline: true,
}
isValid = () => {
isValid() {
const { changeset } = this.props
return ["assignment_id", "task_id", "hours", "description"]
.map(prop => changeset[prop])
.every(Boolean)
};
return (
["assignment_id", "task_id"].map(prop => changeset[prop]).every(Boolean) &&
(changeset.date === formatDate(new Date()) || changeset.seconds > 0)
)
}
get isTimerStartable() {
const {
changeset: { seconds, date },
} = this.props
return date === formatDate(new Date()) && seconds === 0
}
buttonStyle() {
const styleMap = {
true: {
border: "none",
borderRadius: "50%",
width: "60px",
height: "60px",
marginTop: "22px",
transition: "all 0.2s ease-in-out",
},
false: {
border: "none",
width: "50px",
height: "36px",
marginTop: "35px",
transition: "all 0.2s ease-in-out",
},
}
return styleMap[this.isTimerStartable]
}
handleTextareaKeyDown = event => {
const { onSubmit } = this.props
@@ -34,7 +74,7 @@ class Form extends Component {
event.preventDefault()
this.isValid() && onSubmit(event)
}
};
}
render() {
const { projects, changeset, errors, onChange, onSubmit } = this.props
@@ -44,7 +84,7 @@ class Form extends Component {
<form onSubmit={onSubmit}>
<div
className={cn("form-group", {
"has-error": errors.assignment_id || errors.task_id
"has-error": errors.assignment_id || errors.task_id,
})}
>
<Select
@@ -69,9 +109,7 @@ class Form extends Component {
{errors.assignment_id ? (
<div className="form-error">{errors.assignment_id.join("; ")}</div>
) : null}
{errors.task_id ? (
<div className="form-error">{errors.task_id.join("; ")}</div>
) : null}
{errors.task_id ? <div className="form-error">{errors.task_id.join("; ")}</div> : null}
</div>
<div className={cn("form-group", { "has-error": errors.hours })}>
<input
@@ -83,16 +121,14 @@ class Form extends Component {
autoComplete="off"
autoFocus
/>
{errors.hours ? (
<div className="form-error">{errors.hours.join("; ")}</div>
) : null}
{errors.hours ? <div className="form-error">{errors.hours.join("; ")}</div> : null}
</div>
<div className={cn("form-group", { "has-error": errors.description })}>
<textarea
name="description"
onChange={onChange}
value={changeset.description}
placeholder="Beschreibung der Tätigkeit - mind. 3 Zeichen"
placeholder="Beschreibung der Tätigkeit optional"
maxLength={255}
rows={3}
onKeyDown={this.handleTextareaKeyDown}
@@ -102,8 +138,13 @@ class Form extends Component {
) : null}
</div>
<button className="moco-bx-btn" disabled={!this.isValid()}>
OK
<button
type="submit"
className="moco-bx-btn"
disabled={!this.isValid()}
style={this.buttonStyle()}
>
{this.isTimerStartable ? <StopWatch /> : "OK"}
</button>
</form>
)

View File

@@ -3,68 +3,96 @@ import { observable } from "mobx"
import { observer } from "mobx-react"
import { isChrome, getSettings, setStorage } from "utils/browser"
import ApiClient from "api/Client"
import { pipe, toPairs, fromPairs, map } from "lodash/fp"
function upperCaseFirstLetter(input) {
return input[0].toUpperCase() + input.slice(1)
}
function removePathFromUrl(url) {
return url.replace(/(\.[a-z]+)\/.*$/, "$1")
}
@observer
class Options extends Component {
@observable subdomain = "";
@observable apiKey = "";
@observable errorMessage = null;
@observable isSuccess = false;
@observable subdomain = ""
@observable apiKey = ""
@observable hostOverrides = {}
@observable errorMessage = null
@observable isSuccess = false
@observable showHostOverrideOptions = false
componentDidMount() {
getSettings(false).then(({ subdomain, apiKey }) => {
this.subdomain = subdomain || ""
this.apiKey = apiKey || ""
getSettings(false).then((settings) => {
this.subdomain = settings.subdomain || ""
this.apiKey = settings.apiKey || ""
this.hostOverrides = settings.hostOverrides
})
}
onChange = event => {
handleChange = (event) => {
this[event.target.name] = event.target.value.trim()
};
}
handleSubmit = _event => {
handleChangeHostOverrides = (event) => {
this.hostOverrides[event.target.name] = event.target.value.trim()
}
toggleHostOverrideOptions = () => {
this.showHostOverrideOptions = !this.showHostOverrideOptions
}
handleSubmit = (_event) => {
this.isSuccess = false
this.errorMessage = null
setStorage({ subdomain: this.subdomain, apiKey: this.apiKey }).then(() => {
setStorage({
subdomain: this.subdomain,
apiKey: this.apiKey,
settingTimeTrackingHHMM: false,
hostOverrides: pipe(
toPairs,
map(([key, url]) => [key, removePathFromUrl(url)]),
fromPairs,
)(this.hostOverrides),
}).then(() => {
const { version } = chrome.runtime.getManifest()
const apiClient = new ApiClient({
subdomain: this.subdomain,
apiKey: this.apiKey,
version
version,
})
apiClient
.login()
.then(({ data }) =>
setStorage({ settingTimeTrackingHHMM: data.setting_time_tracking_hh_mm }),
)
.then(() => {
this.isSuccess = true
this.closeWindow()
})
.catch(error => {
this.errorMessage =
error.response?.data?.message || "Anmeldung fehlgeschlagen"
.catch((error) => {
this.errorMessage = error.response?.data?.message || "Anmeldung fehlgeschlagen"
})
})
};
}
handleInputKeyDown = event => {
handleInputKeyDown = (event) => {
if (event.key === "Enter") {
this.handleSubmit()
}
};
}
closeWindow = () => {
isChrome() && window.close()
};
}
render() {
return (
<div className="moco-bx-options">
<h2 style={{ textAlign: "center" }}>Einstellungen</h2>
{this.errorMessage && (
<div className="text-danger">{this.errorMessage}</div>
)}
{this.isSuccess && (
<div className="text-success">Anmeldung erfolgreich</div>
)}
{this.errorMessage && <div className="text-danger">{this.errorMessage}</div>}
{this.isSuccess && <div className="text-success">Anmeldung erfolgreich</div>}
<div className="form-group">
<label>Internetadresse</label>
<div className="input-group">
@@ -73,9 +101,9 @@ class Options extends Component {
name="subdomain"
value={this.subdomain}
onKeyDown={this.handleInputKeyDown}
onChange={this.onChange}
onChange={this.handleChange}
/>
<span className="input-group-addon">.mocoapp.com</span>
<span className="input-group-addon input-group-addon--right">.mocoapp.com</span>
</div>
</div>
<div className="form-group">
@@ -85,13 +113,59 @@ class Options extends Component {
name="apiKey"
value={this.apiKey}
onKeyDown={this.handleInputKeyDown}
onChange={this.onChange}
onChange={this.handleChange}
/>
<p className="text-muted">
Den API-Schlüssel findest du in deinem Profil unter
&quot;Integrationen&quot;.
Den API-Schlüssel findest du in deinem Profil unter &quot;Integrationen&quot;.
</p>
</div>
{!this.showHostOverrideOptions && (
<div className="moco-bx-options__host-overrides">
<a href="#" className="moco-bx-btn__secondary" onClick={this.toggleHostOverrideOptions}>
Service-URLs überschreiben?
</a>
</div>
)}
{this.showHostOverrideOptions && (
<div style={{ marginBottom: "1.5rem" }}>
<h3 style={{ marginBottom: 0 }}>Service-URLs</h3>
<small>
Doppelpunkt für Platzhalter verwenden, z.B.{" "}
<span style={{ backgroundColor: "rgba(100, 100, 100, 0.1)" }}>:org</span>. Siehe{" "}
<a
href="https://github.com/hundertzehn/mocoapp-browser-extension#remote-service-configuration"
target="_blank"
rel="noopener noreferrer"
>
Online-Doku
</a>
.
</small>
<br />
{pipe(
Object.entries,
Array.from,
)(this.hostOverrides).map(([name, host]) => (
<div className="form-group" key={name} style={{ margin: "0.5rem 0" }}>
<div className="input-group">
<span
className="input-group-addon input-group-addon--left"
style={{ display: "inline-block", width: "70px", textAlign: "left" }}
>
{upperCaseFirstLetter(name)}
</span>
<input
type="text"
name={name}
value={host}
onKeyDown={this.handleInputKeyDown}
onChange={this.handleChangeHostOverrides}
/>
</div>
</div>
))}
</div>
)}
<button className="moco-bx-btn" onClick={this.handleSubmit}>
OK
</button>

View File

@@ -1,88 +1,65 @@
import React, { Component } from "react"
import React, { useEffect, useRef, forwardRef } from "react"
import PropTypes from "prop-types"
import queryString from "query-string"
import {
ERROR_UNKNOWN,
ERROR_UNAUTHORIZED,
ERROR_UPGRADE_REQUIRED,
serializeProps
} from "utils"
import { isChrome } from "utils/browser"
import { serializeProps } from "utils"
function getStyles(errorType) {
return {
width: "516px",
height:
errorType === ERROR_UNAUTHORIZED
? "834px"
: errorType === ERROR_UPGRADE_REQUIRED
? isChrome()
? "369px"
: "461px"
: errorType === ERROR_UNKNOWN
? "550px"
: "558px"
}
}
const Popup = forwardRef((props, ref) => {
const iFrameRef = useRef()
class Popup extends Component {
static propTypes = {
service: PropTypes.object,
errorType: PropTypes.string,
onRequestClose: PropTypes.func.isRequired
};
handleRequestClose = event => {
const handleRequestClose = event => {
if (event.target.classList.contains("moco-bx-popup")) {
this.props.onRequestClose()
props.onRequestClose()
}
};
componentDidMount() {
// Document might lose focus when clicking the browser action.
// Document might be out of focus when hitting the shortcut key.
// This puts the focus back to the document and ensures that:
// - the autofocus on the hours input field is triggered
// - the ESC key closes the popup without closing anything else
window.focus()
document.activeElement?.blur()
}
render() {
const handleMessage = event => {
if (iFrameRef.current && event.data?.__mocoBX?.iFrameHeight > 300) {
iFrameRef.current.style.height = `${event.data.__mocoBX.iFrameHeight}px`
}
}
useEffect(() => {
window.addEventListener("message", handleMessage)
return () => {
window.removeEventListener("message", handleMessage)
}
}, [])
const serializedProps = serializeProps([
"loading",
"service",
"subdomain",
"lastProjectId",
"lastTaskId",
"roundTimeEntries",
"projects",
"activities",
"schedules",
"timedActivity",
"lastProjectId",
"lastTaskId",
"fromDate",
"toDate",
"errorType",
"errorMessage"
])(this.props)
const styles = getStyles(this.props.errorType)
"errorMessage",
])(props)
return (
<div className="moco-bx-popup" onClick={this.handleRequestClose}>
<div className="moco-bx-popup-content" style={styles}>
<div ref={ref} className="moco-bx-popup" onClick={handleRequestClose}>
<div className="moco-bx-popup-content" style={{ width: "516px" }}>
<iframe
src={chrome.extension.getURL(
`popup.html?${queryString.stringify(serializedProps)}`
)}
width={styles.width}
height={styles.height}
ref={iFrameRef}
src={chrome.extension.getURL(`popup.html?${queryString.stringify(serializedProps)}`)}
style={{ width: "516px", height: "576px", transition: "height 0.1s ease-in-out" }}
/>
</div>
</div>
)
}
})
Popup.displayName = "Popup"
Popup.propTypes = {
service: PropTypes.object,
errorType: PropTypes.string,
onRequestClose: PropTypes.func.isRequired,
}
export default Popup

View File

@@ -1,6 +1,6 @@
import React, { Component } from "react"
import PropTypes from "prop-types"
import ReactSelect, { createFilter } from "react-select"
import ReactSelect, { createFilter, components } from "react-select"
import {
values,
isString,
@@ -10,11 +10,11 @@ import {
compose,
property,
flatMap,
pathEq
pathEq,
isNil,
} from "lodash/fp"
const hasOptionGroups = options =>
options.some(option => Boolean(option.options))
const hasOptionGroups = options => options.some(option => Boolean(option.options))
const customTheme = theme => ({
...theme,
@@ -22,30 +22,30 @@ const customTheme = theme => ({
spacing: {
...theme.spacing,
baseUnit: 3,
controlHeight: 32
controlHeight: 32,
},
colors: {
...theme.colors,
primary: "#38b5eb",
primary75: "rgba(56, 181, 235, 0.25)",
primary50: "#38b5eb",
primary25: "#38b5eb"
}
primary25: "#38b5eb",
},
})
const customStyles = props => ({
control: (base, _state) => ({
...base,
borderColor: props.hasError ? "#FB3A2F" : base.borderColor
borderColor: props.hasError ? "#fb3a2f" : base.borderColor,
}),
valueContainer: base => ({
...base,
padding: "4px 12px"
padding: "4px 12px",
}),
input: base => ({
...base,
border: "0 !important",
boxShadow: "0 !important"
boxShadow: "0 !important",
}),
groupHeading: (base, _state) => ({
...base,
@@ -53,16 +53,14 @@ const customStyles = props => ({
textTransform: "none",
fontWeight: "bold",
fontSize: "100%",
padding: "2px 7px 4px"
padding: "2px 7px 4px",
}),
option: (base, state) => ({
...base,
padding: hasOptionGroups(state.options)
? "4px 7px 4px 20px"
: "4px 7px 4px",
padding: hasOptionGroups(state.options) ? "4px 7px 4px 20px" : "4px 7px 4px",
backgroundColor: state.isFocused ? "#38b5eb" : "none",
color: state.isFocused ? "white" : "hsl(0, 0%, 20%)"
})
color: state.isFocused ? "white" : "hsl(0, 0%, 20%)",
}),
})
const filterOption = createFilter({
@@ -70,27 +68,38 @@ const filterOption = createFilter({
join(" "),
filter(value => isString(value) || isNumber(value)),
values,
property("data")
)
property("data"),
),
})
SingleValue.propTypes = {
children: PropTypes.string.isRequired,
data: PropTypes.shape({
customerName: PropTypes.string,
}).isRequired,
}
function SingleValue({ children, ...props }) {
const label = isNil(props.data.customerName)
? children
: `${props.data.customerName}: ${children}`
return <components.SingleValue {...props}>{label}</components.SingleValue>
}
export default class Select extends Component {
static propTypes = {
name: PropTypes.string.isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
options: PropTypes.array,
hasError: PropTypes.bool,
onChange: PropTypes.func.isRequired
};
onChange: PropTypes.func.isRequired,
}
static findOptionByValue = (selectOptions, value) => {
const options = flatMap(
option => (option.options ? option.options : option),
selectOptions
)
const options = flatMap(option => (option.options ? option.options : option), selectOptions)
return options.find(pathEq("value", value)) || null
};
}
constructor(props) {
super(props)
@@ -101,7 +110,7 @@ export default class Select extends Component {
const { name, onChange } = this.props
const { value } = option
onChange({ target: { name, value } })
};
}
handleKeyDown = event => {
if (!this.select.current) {
@@ -111,7 +120,7 @@ export default class Select extends Component {
if (!this.select.current.state.menuIsOpen && event.key === "Enter") {
this.select.current.setState({ menuIsOpen: true })
}
};
}
render() {
const { value, ...passThroughProps } = this.props
@@ -121,11 +130,12 @@ export default class Select extends Component {
{...passThroughProps}
ref={this.select}
value={Select.findOptionByValue(this.props.options, value)}
onChange={this.handleChange}
onKeyDown={this.handleKeyDown}
filterOption={filterOption}
theme={customTheme}
styles={customStyles(this.props)}
components={{ SingleValue }}
onChange={this.handleChange}
onKeyDown={this.handleKeyDown}
/>
)
}

View File

@@ -1,14 +1,14 @@
import React from 'react'
import PropTypes from 'prop-types'
import React from "react"
import PropTypes from "prop-types"
const Spinner = ({ style }) => (
<div className='moco-bx-spinner__container' style={style}>
<div className='moco-bx-spinner' role='status' />
<div className="moco-bx-spinner__container" style={style}>
<div className="moco-bx-spinner" role="status" />
</div>
)
Spinner.propTypes = {
style: PropTypes.object
style: PropTypes.object,
}
export default Spinner

View File

@@ -1,20 +1,21 @@
import React from 'react'
import PropTypes from 'prop-types'
import logoUrl from "images/logo.png"
import React from "react"
import PropTypes from "prop-types"
import logoUrl from "images/moco-159x159.png"
const Header = ({ subdomain }) => (
<div className="moco-bx-logo__container">
<a href={`https://${subdomain}.mocoapp.com/activities`} target="_blank" rel="noopener noreferrer">
<img
className="moco-bx-logo"
src={chrome.extension.getURL(logoUrl)}
/>
<a
href={`https://${subdomain}.mocoapp.com/activities`}
target="_blank"
rel="noopener noreferrer"
>
<img className="moco-bx-logo" src={chrome.extension.getURL(logoUrl)} />
</a>
</div>
)
Header.propTypes = {
subdomain: PropTypes.string
subdomain: PropTypes.string,
}
export default Header

View File

@@ -0,0 +1,11 @@
import React from "react"
import stopWatch from "images/icons/stopwatch-light.svg"
export default function StopWatch() {
return (
<i
dangerouslySetInnerHTML={{ __html: stopWatch }}
style={{ width: "22px", color: "white", display: "inline-block" }}
/>
)
}

View File

@@ -0,0 +1,29 @@
import React, { useState } from "react"
import PropTypes from "prop-types"
import { useInterval } from "./hooks"
import { differenceInSeconds } from "date-fns"
import { formatDuration } from "utils"
Timer.propTypes = {
startedAt: PropTypes.instanceOf(Date).isRequired,
offset: PropTypes.number,
onTick: PropTypes.func,
}
function Timer({ startedAt, offset = 0, onTick, ...domProps }) {
const [timerLabel, setTimerLabel] = useState(formattedTimerLabel(startedAt, offset))
useInterval(() => {
setTimerLabel(formattedTimerLabel(startedAt, offset))
onTick && onTick()
}, 1000)
return <span {...domProps}>{timerLabel}</span>
}
function formattedTimerLabel(startedAt, offset) {
const seconds = differenceInSeconds(new Date(), startedAt) + offset
return formatDuration(seconds)
}
export default Timer

View File

@@ -0,0 +1,18 @@
import { useEffect, useRef } from "react"
export function useInterval(callback, delay) {
const savedCallback = useRef()
useEffect(() => {
savedCallback.current = callback
})
useEffect(() => {
function tick() {
savedCallback.current()
}
let id = setInterval(tick, delay)
return () => clearInterval(id)
}, [delay])
}

View File

@@ -5,29 +5,40 @@ import Bubble from "./components/Bubble"
import Popup from "components/Popup"
import { createServiceFinder } from "utils/urlMatcher"
import remoteServices from "./remoteServices"
import { ErrorBoundary } from "utils/notifier"
import { ContentMessenger } from "utils/messaging"
import "../css/content.scss"
import { getSettings } from "./utils/browser"
const popupRef = createRef()
const findService = createServiceFinder(remoteServices)(document)
let findService
getSettings().then((settings) => {
findService = createServiceFinder(remoteServices, settings.hostOverrides)(document)
})
chrome.runtime.onConnect.addListener(function (port) {
const messenger = new ContentMessenger(port)
function clickHandler(event) {
if (event.target.closest(".moco-bx-bubble")) {
event.stopPropagation()
messenger.postMessage({ type: "togglePopup" })
}
}
port.onDisconnect.addListener(() => {
messenger.stop()
window.removeEventListener("click", clickHandler, true)
})
function updateBubble({ service, bookedHours } = {}) {
function updateBubble({ service, bookedSeconds, settingTimeTrackingHHMM, timedActivity } = {}) {
if (!document.getElementById("moco-bx-root")) {
const domRoot = document.createElement("div")
domRoot.setAttribute("id", "moco-bx-root")
document.body.appendChild(domRoot)
window.addEventListener("click", clickHandler, true)
}
ReactDOM.render(
<ErrorBoundary>
<Transition
native
items={service}
@@ -36,25 +47,22 @@ chrome.runtime.onConnect.addListener(function(port) {
leave={{ opacity: "0" }}
config={config.stiff}
>
{service =>
{(service) =>
service &&
// eslint-disable-next-line react/display-name
(props => (
<animated.div
className="moco-bx-bubble"
style={{ ...props, ...service.position }}
onClick={event => {
event.stopPropagation()
messenger.postMessage({ type: "togglePopup" })
}}
>
<Bubble key={service.url} bookedHours={bookedHours} />
((props) => (
<animated.div className="moco-bx-bubble" style={{ ...props, ...service.position }}>
<Bubble
key={service.url}
bookedSeconds={bookedSeconds}
settingTimeTrackingHHMM={settingTimeTrackingHHMM}
timedActivity={timedActivity}
/>
</animated.div>
))
}
</Transition>
</ErrorBoundary>,
document.getElementById("moco-bx-root")
</Transition>,
document.getElementById("moco-bx-root"),
)
}
@@ -66,10 +74,8 @@ chrome.runtime.onConnect.addListener(function(port) {
}
ReactDOM.render(
<ErrorBoundary>
<Popup ref={popupRef} {...payload} onRequestClose={closePopup} />
</ErrorBoundary>,
document.getElementById("moco-bx-popup-root")
<Popup ref={popupRef} {...payload} onRequestClose={closePopup} />,
document.getElementById("moco-bx-popup-root"),
)
}
@@ -86,12 +92,12 @@ chrome.runtime.onConnect.addListener(function(port) {
const service = findService(window.location.href)
messenger.postMessage({
type: "newService",
payload: { isOpen: !!popupRef.current, service }
payload: { isOpen: !!popupRef.current, service },
})
})
messenger.on("showBubble", ({ payload: { service, bookedHours } }) => {
updateBubble({ service, bookedHours })
messenger.on("showBubble", ({ payload }) => {
updateBubble(payload)
})
messenger.on("hideBubble", () => {
@@ -105,8 +111,4 @@ chrome.runtime.onConnect.addListener(function(port) {
messenger.on("closePopup", () => {
closePopup()
})
messenger.on("activityCreated", () => {
closePopup()
})
})

View File

@@ -1,14 +1,8 @@
import React from 'react'
import ReactDOM from 'react-dom'
import Options from './components/Options'
import { ErrorBoundary } from 'utils/notifier'
import '../css/options.scss'
import React from "react"
import ReactDOM from "react-dom"
import Options from "./components/Options"
import "../css/options.scss"
const domContainer = document.querySelector('#moco-bx-root')
const domContainer = document.querySelector("#moco-bx-root")
ReactDOM.render(
<ErrorBoundary>
<Options />
</ErrorBoundary>,
domContainer
)
ReactDOM.render(<Options />, domContainer)

View File

@@ -3,7 +3,6 @@ import ReactDOM from "react-dom"
import App from "./components/App"
import queryString from "query-string"
import { parseProps } from "utils"
import { ErrorBoundary } from "utils/notifier"
import "../css/popup.scss"
const parsedProps = parseProps([
@@ -13,20 +12,13 @@ const parsedProps = parseProps([
"projects",
"activities",
"schedules",
"lastProjectId",
"lastTaskId",
"roundTimeEntries",
"timedActivity",
"lastProjectId",
"lastTaskId",
"fromDate",
"toDate",
"errorType",
"errorMessage"
"errorMessage",
])(queryString.parse(location.search))
ReactDOM.render(
<ErrorBoundary>
<App {...parsedProps} />
</ErrorBoundary>,
document.querySelector("#moco-bx-root")
)
ReactDOM.render(<App {...parsedProps} />, document.querySelector("#moco-bx-root"))

View File

@@ -1,68 +1,58 @@
const projectRegex = /\[([\w-]+)\]/
const projectIdentifierBySelector = (selector) => (document) =>
document.querySelector(selector)?.textContent?.trim()?.match(projectRegex)?.[1]
export default {
asana: {
name: "asana",
host: "https://app.asana.com",
urlPatterns: [
[/^https:\/\/app.asana.com\/0\/([^/]+)\/(\d+)/, ["domainUserId", "id"]],
[
/^https:\/\/app.asana.com\/0\/search\/([^/]+)\/(\d+)/,
["domainUserId", "id"]
]
[/^:host:\/0\/([^/]+)\/(\d+)/, ["domainUserId", "id"]],
[/^:host:\/0\/search\/([^/]+)\/(\d+)/, ["domainUserId", "id"]],
],
description: document =>
document
.querySelector(".ItemRow--highlighted textarea")
?.textContent?.trim() ||
document
.querySelector(".ItemRow--focused textarea")
?.textContent?.trim() ||
document.querySelector(".SingleTaskPane textarea")?.textContent?.trim(),
projectId: document => {
const match = document
.querySelector(".ProjectPageHeader-projectName")
?.textContent?.trim()
?.match(projectRegex)
return match && match[1]
}
description: (document) =>
document.querySelector(".ItemRow--highlighted textarea")?.textContent?.trim() ||
document.querySelector(".ItemRow--focused textarea")?.textContent?.trim() ||
document.querySelector(".SingleTaskPane textarea")?.textContent?.trim() ||
document.querySelector(".SingleTaskTitleInput-taskName textarea")?.textContent?.trim(),
projectId: projectIdentifierBySelector(".TopbarPageHeaderStructure-titleRow h1"),
allowHostOverride: false,
},
"github-pr": {
name: "github",
urlPatterns: ["https\\://github.com/:org/:repo/pull/:id(/:tab)"],
id: (document, service, { org, repo, id }) =>
[service.key, org, repo, id].join("."),
description: (document, service, { org, repo, id }) =>
document.querySelector(".js-issue-title")?.textContent?.trim(),
projectId: document => {
const match = document
.querySelector(".js-issue-title")
?.textContent.trim()
?.match(projectRegex)
return match && match[1]
}
host: "https://github.com",
urlPatterns: [":host:/:org/:repo/pull/:id(/:tab)"],
id: (document, service, { org, repo, id }) => [service.key, org, repo, id].join("."),
description: (document) => document.querySelector(".js-issue-title")?.textContent?.trim(),
projectId: projectIdentifierBySelector(".js-issue-title"),
allowHostOverride: false,
},
"github-issue": {
name: "github",
urlPatterns: ["https\\://github.com/:org/:repo/issues/:id"],
id: (document, service, { org, repo, id }) =>
[service.key, org, repo, id].join("."),
host: "https://github.com",
urlPatterns: [":host:/:org/:repo/issues/:id"],
id: (document, service, { org, repo, id }) => [service.key, org, repo, id].join("."),
description: (document, service, { org, repo, id }) =>
document.querySelector(".js-issue-title")?.textContent?.trim()
document.querySelector(".js-issue-title")?.textContent?.trim(),
projectId: projectIdentifierBySelector(".js-issue-title"),
allowHostOverride: false,
},
jira: {
name: "jira",
host: "https://:org.atlassian.net",
urlPatterns: [
"https\\://:org.atlassian.net/secure/RapidBoard.jspa",
"https\\://:org.atlassian.net/browse/:id",
"https\\://:org.atlassian.net/jira/software/projects/:projectId/boards/:board",
"https\\://:org.atlassian.net/jira/software/projects/:projectId/boards/:board/backlog"
":host:/secure/RapidBoard.jspa",
":host:/browse/:id",
":host:/jira/software/projects/:projectId/boards/:board",
":host:/jira/software/projects/:projectId/boards/:board/backlog",
],
queryParams: {
id: "selectedIssue",
projectId: "projectKey"
projectId: "projectKey",
},
description: (document, service, { id }) => {
const title =
@@ -70,51 +60,115 @@ export default {
.querySelector('[aria-label="Edit Summary"]')
?.parentNode?.querySelector("h1")
?.textContent?.trim() ||
document
.querySelector(".ghx-selected .ghx-summary")
?.textContent?.trim()
document.querySelector(".ghx-selected .ghx-summary")?.textContent?.trim()
return `#${id} ${title || ""}`
}
},
allowHostOverride: true,
},
meistertask: {
name: "meistertask",
urlPatterns: ["https\\://www.meistertask.com/app/task/:id/:slug"],
description: document => {
const json =
document.getElementById("mt-toggl-data")?.dataset?.togglJson || "{}"
host: "https://www.meistertask.com",
urlPatterns: [":host:/app/task/:id/:slug"],
description: (document) => {
const json = document.getElementById("mt-toggl-data")?.dataset?.togglJson || "{}"
const data = JSON.parse(json)
return data.taskName
},
projectId: document => {
const json =
document.getElementById("mt-toggl-data")?.dataset?.togglJson || "{}"
projectId: (document) => {
const json = document.getElementById("mt-toggl-data")?.dataset?.togglJson || "{}"
const data = JSON.parse(json)
const match = data.projectName?.match(projectRegex)
const match = data.taskName?.match(projectRegex) || data.projectName?.match(projectRegex)
return match && match[1]
}
},
allowHostOverride: false,
},
trello: {
name: "trello",
urlPatterns: ["https\\://trello.com/c/:id/:title"],
host: "https://trello.com",
urlPatterns: [":host:/c/:id/:title"],
description: (document, service, { title }) =>
document.querySelector(".js-title-helper")?.textContent?.trim() || title
document.querySelector(".js-title-helper")?.textContent?.trim() || title,
projectId: (document) =>
projectIdentifierBySelector(".js-title-helper")(document) ||
projectIdentifierBySelector(".js-board-editing-target")(document),
allowHostOverride: false,
},
youtrack: {
name: "youtrack",
urlPatterns: ["https\\://:org.myjetbrains.com/youtrack/issue/:id"],
description: document =>
document.querySelector("yt-issue-body h1")?.textContent?.trim()
host: "https://:org.myjetbrains.com",
urlPatterns: [":host:/youtrack/issue/:id"],
description: (document) => document.querySelector("yt-issue-body h1")?.textContent?.trim(),
projectId: projectIdentifierBySelector("yt-issue-body h1"),
allowHostOverride: true,
},
wrike: {
name: "wrike",
host: "https://www.wrike.com",
urlPatterns: [
":host:/workspace.htm#path=mywork",
":host:/workspace.htm#path=folder",
"https\\://app-eu.wrike.com/workspace.htm#path=mywork",
"https\\://app-eu.wrike.com/workspace.htm#path=folder",
],
queryParams: {
id: ["t", "ot"],
},
description: (document) => document.querySelector(".title-field-ghost")?.textContent?.trim(),
projectId: projectIdentifierBySelector(".header-title__main"),
allowHostOverride: false,
},
wunderlist: {
name: "wunderlist",
urlPatterns: ["https\\://www.wunderlist.com/(webapp)#/tasks/:id(/*)"],
description: document =>
host: "https://www.wunderlist.com",
urlPatterns: [":host:/(webapp)#/tasks/:id(/*)"],
description: (document) =>
document
.querySelector(".taskItem.selected .taskItem-titleWrapper-title")
?.textContent?.trim()
}
?.textContent?.trim(),
projectId: projectIdentifierBySelector(".taskItem.selected .taskItem-titleWrapper-title"),
allowHostOverride: false,
},
"gitlab-mr": {
name: "gitlab",
host: "https://gitlab.com",
urlPatterns: [
":host:/:org/:group/:projectId/-/merge_requests/:id",
":host:/:org/:projectId/-/merge_requests/:id",
],
description: (document, service, { id }) => {
const title = document.querySelector("h2.title")?.textContent?.trim()
return `#${id} ${title || ""}`.trim()
},
allowHostOverride: true,
},
"gitlab-issues": {
name: "gitlab",
host: "https://gitlab.com",
urlPatterns: [
":host:/:org/:group/:projectId/-/issues/:id",
":host:/:org/:projectId/-/issues/:id",
],
description: (document, service, { id }) => {
const title = document.querySelector("h2.title")?.textContent?.trim()
return `#${id} ${title || ""}`.trim()
},
allowHostOverride: true,
},
monday: {
name: "monday",
host: "https://:org.monday.com",
urlPatterns: [":host:/boards/:board/pulses/:id"],
description: (document, service, { id }) => {
return document.querySelector(".pulse_title")?.textContent?.trim()
},
allowHostOverride: false,
},
}

View File

@@ -1,12 +1,14 @@
export default class TimeInputParser {
#input;
#input
constructor(input) {
this.#input = input.toLowerCase().replace(/[\s()]/g, "")
this.#input = (input ?? "").toLowerCase().replace(/[\s()]/g, "")
}
parseSeconds() {
if (this.#isDecimal()) {
if (this.#input === "") {
return 0
} else if (this.#isDecimal()) {
return Math.round(parseFloat(this.#parseDecimal()) * 3600)
} else if (this.#isTime()) {
return this.#parseTimeAsSeconds()
@@ -25,11 +27,11 @@ export default class TimeInputParser {
const calculated = hours * 3600 + minutes * 60
return isNegative ? -calculated : calculated
};
}
#parseDecimal = () => {
return this.#input.replace(/[.,]/g, ".")
};
}
#parseTimeAsSeconds = () => {
const match = this.#isTime()
@@ -39,12 +41,12 @@ export default class TimeInputParser {
const minutes = parseInt(match[3])
return this.#calculateFromHoursAndMinutes(hours, minutes, isNegative)
};
}
#parseMinutesAsSeconds = () => {
const minutes = parseInt(this.#isMinutes()[1])
return minutes * 60
};
}
#parseRange = () => {
const match = this.#isRange()
@@ -54,7 +56,7 @@ export default class TimeInputParser {
const to_hours = parseInt(match[3])
const to_minutes = parseInt(match[4])
return (to_hours - from_hours) * 3600 + (to_minutes - from_minutes) * 60
};
}
#parseHoursAndMinutes = () => {
const match = this.#isHoursAndMinutes()
@@ -64,28 +66,26 @@ export default class TimeInputParser {
const minutes = parseInt(match[3])
return this.#calculateFromHoursAndMinutes(hours, minutes, isNegative)
};
}
#isDecimal = () => {
return this.#input.match(/^([-]?[0-9]{0,2})[.,]{1}([0-9]{1,2})$/)
};
}
#isTime = () => {
return this.#input.match(/^([-]?)([0-9]{1,2}):([0-9]{2})$/)
};
}
#isMinutes = () => {
return this.#input.match(/^([-]?[0-9]{1,3})(m|mins?)$/)
};
}
#isRange = () => {
return this.#input.match(
/^([0-9]{1,2})[:.]{0,1}([0-9]{2})-([0-9]{1,2})[:.]{0,1}([0-9]{2})$/
)
};
return this.#input.match(/^([0-9]{1,2})[:.]{0,1}([0-9]{2})-([0-9]{1,2})[:.]{0,1}([0-9]{2})$/)
}
#isHoursAndMinutes = () => {
// 1h 14m(in)
return this.#input.match(/^([-]?)([0-9]{1,2})h([0-9]{1,2})(m|mins?)$/)
};
}
}

View File

@@ -1,34 +1,53 @@
import { head } from "lodash/fp"
export const isChrome = () => typeof browser === "undefined" && chrome
export const isFirefox = () => typeof browser !== "undefined" && chrome
import { head, pick, reduce, filter, prop, pipe } from "lodash/fp"
import remoteServices from "../remoteServices"
const DEFAULT_SUBDOMAIN = "unset"
export const isChrome = () => typeof browser === "undefined" && chrome
export const isFirefox = () => typeof browser !== "undefined" && chrome
export const defaultHostOverrides = pipe(
filter(prop("allowHostOverride")),
reduce((acc, remoteService) => {
acc[remoteService.name] = remoteService.host
return acc
}, {}),
)(remoteServices)
// We pick only the keys defined in `defaultHostOverrides`, so that
// deleted host overrides get cleared from the settings
const getHostOverrides = (settings) => ({
...defaultHostOverrides,
...pick(Object.keys(defaultHostOverrides), settings.hostOverrides || {}),
})
export const getSettings = (withDefaultSubdomain = true) => {
const keys = ["subdomain", "apiKey"]
const keys = ["subdomain", "apiKey", "settingTimeTrackingHHMM", "hostOverrides"]
const { version } = chrome.runtime.getManifest()
if (isChrome()) {
return new Promise(resolve => {
chrome.storage.sync.get(keys, data => {
return new Promise((resolve) => {
chrome.storage.sync.get(keys, (settings) => {
if (withDefaultSubdomain) {
data.subdomain = data.subdomain || DEFAULT_SUBDOMAIN
settings.subdomain = settings.subdomain || DEFAULT_SUBDOMAIN
}
resolve({ ...data, version })
settings.hostOverrides = getHostOverrides(settings)
resolve({ ...settings, version })
})
})
} else {
return browser.storage.sync.get(keys).then(data => {
return browser.storage.sync.get(keys).then((settings) => {
if (withDefaultSubdomain) {
data.subdomain = data.subdomain || DEFAULT_SUBDOMAIN
settings.subdomain = settings.subdomain || DEFAULT_SUBDOMAIN
}
return { ...data, version }
settings.hostOverrides = getHostOverrides(settings)
return { ...settings, version }
})
}
}
export const setStorage = items => {
export const setStorage = (items) => {
if (isChrome()) {
return new Promise(resolve => {
return new Promise((resolve) => {
chrome.storage.sync.set(items, resolve)
})
} else {
@@ -36,9 +55,9 @@ export const setStorage = items => {
}
}
export const queryTabs = queryInfo => {
export const queryTabs = (queryInfo) => {
if (isChrome()) {
return new Promise(resolve => chrome.tabs.query(queryInfo, resolve))
return new Promise((resolve) => chrome.tabs.query(queryInfo, resolve))
} else {
return browser.tabs.query(queryInfo)
}
@@ -48,4 +67,4 @@ export const getCurrentTab = () => {
return queryTabs({ currentWindow: true, active: true }).then(head)
}
export const isBrowserTab = tab => /^(?:chrome|about):/.test(tab.url)
export const isBrowserTab = (tab) => /^(?:chrome|about):/.test(tab.url)

View File

@@ -9,71 +9,71 @@ import {
get,
find,
curry,
pick
pick,
head,
defaultTo,
padCharsStart,
} from "lodash/fp"
import { startOfWeek, endOfWeek } from "date-fns"
import { format } from "date-fns"
const nilToArray = input => input || []
const nilToArray = (input) => input || []
export const ERROR_UNAUTHORIZED = "unauthorized"
export const ERROR_UPGRADE_REQUIRED = "upgrade-required"
export const ERROR_UNKNOWN = "unknown"
export const noop = () => null
export const asArray = (input) => (Array.isArray(input) ? input : [input])
export const findProjectBy = (prop) => (val) => (projects) => {
if (!val) {
return undefined
}
return compose(find(pathEq(prop, val)), flatMap(get("options")))(projects)
}
export const findProjectBy = prop => val =>
compose(
find(pathEq(prop, val)),
flatMap(get("options"))
)
export const findProjectByIdentifier = findProjectBy("identifier")
export const findProjectByValue = findProjectBy("value")
export const findTask = id =>
compose(
find(pathEq("value", Number(id))),
get("tasks")
)
export const findTask = (id) => compose(find(pathEq("value", Number(id))), get("tasks"))
export const defaultTask = (tasks) =>
compose(defaultTo(head(tasks)), find(pathEq("isDefault", true)), nilToArray)(tasks)
function taskOptions(tasks) {
return tasks.map(({ id, name, billable }) => ({
return tasks.map(({ id, name, billable, default: isDefault }) => ({
label: billable ? name : `(${name})`,
value: id,
billable
billable,
isDefault,
}))
}
export function projectOptions(projects) {
return projects.map(project => ({
return projects.map((project) => ({
value: project.id,
label: project.intern ? `(${project.name})` : project.name,
identifier: project.identifier,
customerName: project.customer_name,
tasks: taskOptions(project.tasks)
tasks: taskOptions(project.tasks),
}))
}
export const groupedProjectOptions = compose(
map(([customerName, projects]) => ({
label: customerName,
options: projectOptions(projects)
options: projectOptions(projects),
})),
toPairs,
groupBy("customer_name"),
nilToArray
nilToArray,
)
export const serializeProps = attrs =>
compose(
mapValues(JSON.stringify),
pick(attrs)
)
export const serializeProps = (attrs) => compose(mapValues(JSON.stringify), pick(attrs))
export const parseProps = attrs =>
compose(
mapValues(JSON.parse),
pick(attrs)
)
export const parseProps = (attrs) => compose(mapValues(JSON.parse), pick(attrs))
export const trace = curry((tag, value) => {
// eslint-disable-next-line no-console
@@ -82,12 +82,13 @@ export const trace = curry((tag, value) => {
})
export const weekStartsOn = 1
export const formatDate = date => format(date, "YYYY-MM-DD")
export const formatDate = (date) => format(date, "yyyy-MM-dd")
export const getStartOfWeek = () => startOfWeek(new Date(), { weekStartsOn })
export const getEndOfWeek = () => endOfWeek(new Date(), { weekStartsOn })
export const extensionSettingsUrl = () =>
`chrome://extensions/?id=${chrome.runtime.id}`
export const extensionSettingsUrl = () => `chrome://extensions/?id=${chrome.runtime.id}`
export const extractAndSetTag = changeset => {
export const extractAndSetTag = (changeset) => {
let { description } = changeset
const match = description.match(/^#(\S+)/)
if (!match) {
@@ -96,6 +97,25 @@ export const extractAndSetTag = changeset => {
return {
...changeset,
description: description.replace(/^#\S+\s/, ""),
tag: match[1]
tag: match[1],
}
}
export const formatDuration = (
durationInSeconds,
{ settingTimeTrackingHHMM = true, showSeconds = true } = {},
) => {
if (settingTimeTrackingHHMM) {
const hours = Math.floor(durationInSeconds / 3600)
const minutes = Math.floor((durationInSeconds % 3600) / 60)
const result = `${hours}:${padCharsStart("0", 2, minutes)}`
if (!showSeconds) {
return result
} else {
const seconds = durationInSeconds % 60
return result + `:${padCharsStart("0", 2, seconds)}`
}
} else {
return (durationInSeconds / 3600).toFixed(2)
}
}

View File

@@ -4,45 +4,55 @@ import {
ERROR_UPGRADE_REQUIRED,
ERROR_UNKNOWN,
groupedProjectOptions,
weekStartsOn
getStartOfWeek,
getEndOfWeek,
} from "utils"
import { get, forEach, reject, isNil } from "lodash/fp"
import { startOfWeek, endOfWeek } from "date-fns"
import { createMatcher } from "utils/urlMatcher"
import remoteServices from "remoteServices"
import { queryTabs, isBrowserTab, getSettings } from "utils/browser"
import { queryTabs, isBrowserTab, getSettings, setStorage } from "utils/browser"
const getStartOfWeek = () => startOfWeek(new Date(), { weekStartsOn })
const getEndOfWeek = () => endOfWeek(new Date(), { weekStartsOn })
const matcher = createMatcher(remoteServices)
let matcher
const initMatcher = (settings) => {
matcher = createMatcher(remoteServices, settings.hostOverrides)
}
getSettings().then((settings) => {
initMatcher(settings)
})
export function tabUpdated(tab, { messenger, settings }) {
messenger.connectTab(tab)
const service = matcher(tab.url)
const apiClient = new ApiClient(settings)
if (service?.match?.id) {
messenger.postMessage(tab, { type: "requestService" })
messenger.once("newService", ({ payload: { service } }) => {
const apiClient = new ApiClient(settings)
apiClient
.bookedHours(service)
.activitiesStatus(service)
.then(({ data }) => {
messenger.postMessage(tab, {
type: "showBubble",
payload: {
bookedHours: parseFloat(data[0]?.hours) || 0,
service
}
bookedSeconds: data.seconds,
settingTimeTrackingHHMM: settings.settingTimeTrackingHHMM,
timedActivity: data.timed_activity,
service,
},
})
})
.catch(() => {
messenger.postMessage(tab, {
type: "showBubble",
payload: {
bookedHours: 0,
service
}
bookedSeconds: 0,
settingTimeTrackingHHMM: settings.settingTimeTrackingHHMM,
service,
},
})
})
})
@@ -52,13 +62,15 @@ export function tabUpdated(tab, { messenger, settings }) {
}
export function settingsChanged(settings, { messenger }) {
initMatcher(settings)
queryTabs({ currentWindow: true })
.then(reject(isBrowserTab))
.then(
forEach(tab => {
messenger.postMessage(tab, { type: "closePopup" })
tabUpdated(tab, { settings, messenger })
})
}),
)
}
@@ -76,25 +88,36 @@ export function togglePopup(tab, { messenger }) {
}
}
async function openPopup(tab, { service, messenger }) {
export async function openPopup(tab, { service, messenger }) {
messenger.postMessage(tab, { type: "openPopup", payload: { loading: true } })
const fromDate = getStartOfWeek()
const toDate = getEndOfWeek()
const settings = await getSettings()
const apiClient = new ApiClient(settings)
const responses = []
try {
const responses = await Promise.all([
apiClient.login(service),
responses.push(await apiClient.login(service))
// we can forgo the following calls if a timed activity exists
if (!responses[0].data.timed_activity) {
responses.push(
...(await Promise.all([
apiClient.projects(),
apiClient.activities(fromDate, toDate),
apiClient.schedules(fromDate, toDate)
])
apiClient.schedules(fromDate, toDate),
])),
)
}
const settingTimeTrackingHHMM = get("[0].data.setting_time_tracking_hh_mm", responses)
!isNil(settingTimeTrackingHHMM) && setStorage({ settingTimeTrackingHHMM })
const action = {
type: "openPopup",
payload: {
service,
subdomain: settings.subdomain,
timedActivity: get("[0].data.timed_activity", responses),
lastProjectId: get("[0].data.last_project_id", responses),
lastTaskId: get("[0].data.last_task_id", responses),
roundTimeEntries: get("[0].data.round_time_entries", responses),
@@ -103,8 +126,8 @@ async function openPopup(tab, { service, messenger }) {
schedules: get("[3].data", responses),
fromDate,
toDate,
loading: false
}
loading: false,
},
}
messenger.postMessage(tab, action)
} catch (error) {
@@ -119,7 +142,7 @@ async function openPopup(tab, { service, messenger }) {
}
messenger.postMessage(tab, {
type: "openPopup",
payload: { errorType, errorMessage }
payload: { errorType, errorMessage },
})
}
}

View File

@@ -1,14 +1,14 @@
export class BackgroundMessenger {
#ports = new Map();
#handlers = new Map();
#onceHandlers = new Map();
#ports = new Map()
#handlers = new Map()
#onceHandlers = new Map()
#handler = action => {
const handler = this.#handlers.get(action.type)
if (handler) {
handler(action)
}
};
}
#onceHandler = action => {
const handler = this.#onceHandlers.get(action.type)
@@ -16,7 +16,7 @@ export class BackgroundMessenger {
if (handler) {
handler(action)
}
};
}
#registerPort = (tabId, port) => {
this.#ports.set(tabId, port)
@@ -25,14 +25,14 @@ export class BackgroundMessenger {
port.onDisconnect.addListener(() => {
this.#unregisterPort(tabId, port)
})
};
}
#unregisterPort = (tabId, port) => {
port.onMessage.removeListener(this.#handler)
port.onMessage.removeListener(this.#onceHandler)
port.disconnect()
this.#ports.delete(tabId)
};
}
connectTab = tab => {
const currentPort = this.#ports.get(tab.id)
@@ -40,41 +40,41 @@ export class BackgroundMessenger {
const port = chrome.tabs.connect(tab.id)
this.#registerPort(tab.id, port)
}
};
}
disconnectTab = tabId => {
const port = this.#ports.get(tabId)
if (port) {
this.#unregisterPort(tabId, port)
}
};
}
postMessage = (tab, action) => {
const port = this.#ports.get(tab.id)
if (port) {
port.postMessage(action)
}
};
}
once = (type, handler) => {
this.#onceHandlers.set(type, handler)
};
}
on = (type, handler) => {
this.#handlers.set(type, handler)
};
}
}
export class ContentMessenger {
#port;
#handlers = new Map();
#port
#handlers = new Map()
#handler = action => {
const handler = this.#handlers.get(action.type)
if (handler) {
handler(action)
}
};
}
constructor(port) {
this.#port = port
@@ -85,15 +85,15 @@ export class ContentMessenger {
if (this.#port) {
this.#port.postMessage(action)
}
};
}
on = (type, handler) => {
this.#handlers.set(type, handler)
};
}
stop = () => {
this.#port.onMessage.removeListener(this.#handler)
this.#port = null
this.#handlers.clear()
};
}
}

View File

@@ -1,39 +0,0 @@
import React from "react"
import bugsnag from "@bugsnag/js"
import bugsnagReact from "@bugsnag/plugin-react"
import { includes } from "lodash/fp"
function getAppVersion() {
try {
return chrome.runtime.getManifest().version
} catch (error) {
return
}
}
const filterReport = report => {
const appVersion = getAppVersion()
if (!appVersion) {
return false
}
const scripts = ["background", "content", "options", "popup"].map(
file => `${chrome.extension.getURL(file)}.${appVersion}.js`
)
return scripts.some(script => report.stacktrace.some(includes(script)))
}
const bugsnagClient = bugsnag({
apiKey: process.env.BUGSNAG_API_KEY,
appVersion: getAppVersion(),
collectUserIp: false,
beforeSend: filterReport,
releaseStage: process.env.NODE_ENV,
notifyReleaseStages: ["production"]
})
bugsnagClient.use(bugsnagReact, React)
export default bugsnagClient
export const ErrorBoundary = bugsnagClient.getPlugin("react")

View File

@@ -1,22 +1,33 @@
import UrlPattern from "url-pattern"
import {
isFunction,
isUndefined,
compose,
toPairs,
map,
pipe
} from "lodash/fp"
import { isFunction, isUndefined, compose, toPairs, map, pipe, isNil, reduce } from "lodash/fp"
import { asArray } from "./index"
import queryString from "query-string"
const extractQueryParams = (queryParams, query) => {
return toPairs(queryParams).reduce((acc, [key, param]) => {
function parseUrl(url) {
const urlObject = new URL(url)
const { origin, pathname, search } = urlObject
let { hash } = urlObject
const query = {
...queryString.parse(search),
...queryString.parse(hash),
}
if (hash) {
hash = hash.match(/#[^&]+/)[0]
}
return { origin, pathname, hash, query }
}
function extractQueryParams(queryParams, query) {
return toPairs(queryParams).reduce((acc, [key, params]) => {
const param = asArray(params).find((param) => !isNil(query[param]))
if (param) {
acc[key] = query[param]
}
return acc
}, {})
}
const createEvaluator = args => fnOrValue => {
const createEvaluator = (args) => (fnOrValue) => {
if (isUndefined(fnOrValue)) {
return
}
@@ -28,21 +39,35 @@ const createEvaluator = args => fnOrValue => {
return fnOrValue
}
const prepareHostForRegExp = (host) => {
return host.replace(":", "\\:")
}
const replaceHostInPattern = (host, pattern) => {
if (typeof pattern === "string") {
return pattern.replace(":host:", prepareHostForRegExp(host))
} else if (pattern instanceof RegExp) {
return new RegExp(pattern.source.replace(":host:", prepareHostForRegExp(host)))
} else {
return pattern
}
}
const parseServices = compose(
map(([key, config]) => ({
...config,
key,
patterns: config.urlPatterns.map(pattern => {
patterns: config.urlPatterns.map((pattern) => {
if (Array.isArray(pattern)) {
return new UrlPattern(...pattern)
return new UrlPattern(...pattern.map((p) => replaceHostInPattern(config.host, p)))
}
return new UrlPattern(pattern)
})
return new UrlPattern(replaceHostInPattern(config.host, pattern))
}),
})),
toPairs
toPairs,
)
export const createEnhancer = document => service => {
export const createEnhancer = (document) => (service) => {
if (!service) {
return
}
@@ -57,31 +82,41 @@ export const createEnhancer = document => service => {
description: evaluate(service.description),
projectId: evaluate(service.projectId),
taskId: evaluate(service.taskId),
position: service.position || { right: "calc(2rem + 5px)" }
position: service.position || { right: "calc(2rem + 5px)" },
}
}
export const createMatcher = remoteServices => {
const services = parseServices(remoteServices)
return tabUrl => {
const { origin, pathname, hash, search } = new URL(tabUrl)
const applyHostOverrides = (remoteServices, hostOverrides) =>
pipe(
toPairs,
reduce((acc, [key, remoteService]) => {
acc[key] = {
...remoteService,
key,
host: hostOverrides[remoteService.name] || remoteService.host,
}
return acc
}, {}),
)(remoteServices)
export const createMatcher = (remoteServices, hostOverrides) => {
const services = parseServices(applyHostOverrides(remoteServices, hostOverrides))
return (tabUrl) => {
const { origin, pathname, hash, query } = parseUrl(tabUrl)
const url = `${origin}${pathname}${hash}`
const query = queryString.parse(search)
const service = services.find(service =>
service.patterns.some(pattern => pattern.match(url))
)
const service = services.find((service) => {
return service.patterns.some((pattern) => pattern.match(url))
})
if (!service) {
return
}
const pattern = service.patterns.find(pattern => pattern.match(url))
const pattern = service.patterns.find((pattern) => pattern.match(url))
let match = pattern.match(url)
if (service.queryParams) {
const extractedQueryParams = extractQueryParams(
service.queryParams,
query
)
const extractedQueryParams = extractQueryParams(service.queryParams, query)
match = { ...extractedQueryParams, ...match }
}
@@ -89,16 +124,13 @@ export const createMatcher = remoteServices => {
...match,
...service,
url: tabUrl,
match
match,
}
}
}
export const createServiceFinder = remoteServices => document => {
const matcher = createMatcher(remoteServices)
export const createServiceFinder = (remoteServices, hostOverrides) => (document) => {
const matcher = createMatcher(remoteServices, hostOverrides)
const enhancer = createEnhancer(document)
return pipe(
matcher,
enhancer
)
return pipe(matcher, enhancer)
}

View File

@@ -4,26 +4,21 @@
"description": "MOCO Zeiterfassung Plugin",
"version": "0.9.20",
"manifest_version": 2,
"description": "MOCO Time Tracking Plugin",
"icons": {
"16": "src/images/logo.png",
"32": "src/images/logo.png",
"48": "src/images/logo.png",
"128": "src/images/logo.png"
"16": "src/images/moco-32x32.png",
"32": "src/images/moco-32x32.png",
"48": "src/images/moco-159x159.png",
"128": "src/images/moco-159x159.png"
},
"options_ui": {
"page": "options.html"
},
"permissions": [
"https://*.mocoapp.com/*",
"storage",
"tabs"
],
"permissions": ["https://*.mocoapp.com/*", "storage", "tabs"],
"background": {
"page": "background.html"
},
"browser_action": {
"default_icon": "src/images/logo.png"
"default_icon": "src/images/moco-32x32.png"
},
"content_scripts": [
{

View File

@@ -9,14 +9,16 @@ export const projects = [
{
id: 2733682,
name: "Bugfixing",
billable: true
billable: true,
default: false,
},
{
id: 2733681,
name: "Development",
billable: true
}
]
billable: true,
default: true,
},
],
},
{
id: 944724773,
@@ -28,24 +30,28 @@ export const projects = [
{
id: 1621304,
name: "Roadmap Features",
billable: true
billable: true,
default: false,
},
{
id: 1621310,
name: "Bugfixing",
billable: true
billable: true,
default: false,
},
{
id: 1621305,
name: "Quickwins",
billable: true
billable: true,
default: true,
},
{
id: 1621323,
name: "Refactorings",
billable: true
}
]
billable: true,
default: false,
},
],
},
{
id: 944837106,
@@ -57,24 +63,28 @@ export const projects = [
{
id: 2500080,
name: "Intercom & Mails",
billable: false
billable: false,
default: false,
},
{
id: 2500081,
name: "Demos",
billable: false
billable: false,
default: true,
},
{
id: 2506050,
name: "Calls",
billable: false
billable: false,
default: false,
},
{
id: 2500084,
name: "Importe",
billable: false
}
]
billable: false,
default: false,
},
],
},
{
id: 944621413,
@@ -86,23 +96,27 @@ export const projects = [
{
id: 874014,
name: "Entwicklung",
billable: true
billable: true,
default: false,
},
{
id: 874015,
name: "Grafik",
billable: true
billable: true,
default: false,
},
{
id: 874016,
name: "Konzept",
billable: true
billable: true,
default: false,
},
{
id: 874017,
name: "Projektleitung",
billable: true
}
]
}
billable: true,
default: false,
},
],
},
]

View File

@@ -3,16 +3,26 @@ import {
findProjectByValue,
findProjectByIdentifier,
findTask,
defaultTask,
groupedProjectOptions,
extractAndSetTag
extractAndSetTag,
formatDuration,
} from "../../src/js/utils"
import { map } from "lodash/fp"
import { map, compose } from "lodash/fp"
const getProjectBy = finder => key =>
compose(
finder(key),
groupedProjectOptions,
)(projects)
const getProjectByValue = getProjectBy(findProjectByValue)
const getProjectByIdentifier = getProjectBy(findProjectByIdentifier)
describe("utils", () => {
describe("findProjectByValue", () => {
it("finds an existing project", () => {
const options = groupedProjectOptions(projects)
const project = findProjectByValue(944837106)(options)
const project = getProjectByValue(944837106)
expect(project.value).toEqual(944837106)
expect(project.label).toEqual("Support")
expect(project.customerName).toEqual("MOCO APP")
@@ -20,14 +30,12 @@ describe("utils", () => {
})
it("returns undefined if project is not found", () => {
const options = groupedProjectOptions(projects)
const project = findProjectByValue(123)(options)
const project = getProjectByValue(123)
expect(project).toBe(undefined)
})
it("returns undefined for undefined id", () => {
const options = groupedProjectOptions(projects)
const project = findProjectByValue(undefined)(options)
const project = getProjectByValue(undefined)
expect(project).toBe(undefined)
})
})
@@ -57,16 +65,14 @@ describe("utils", () => {
describe("findTask", () => {
it("find an existing task", () => {
const options = groupedProjectOptions(projects)
const project = findProjectByValue(944837106)(options)
const project = getProjectByValue(944837106)
const task = findTask(2506050)(project)
expect(task.value).toEqual(2506050)
expect(task.label).toEqual("(Calls)")
})
it("returns undefined if task is not found", () => {
const options = groupedProjectOptions(projects)
const project = findProjectByValue(944837106)(options)
const project = getProjectByValue(944837106)
const task = findTask(123)(project)
expect(task).toBe(undefined)
})
@@ -77,14 +83,32 @@ describe("utils", () => {
})
})
describe("defaultTask", () => {
it("find a default task", () => {
const project = getProjectByValue(944837106)
const task = defaultTask(project.tasks)
expect(task.label).toBe("(Demos)")
})
it("returns first task if no default is defined", () => {
const project = getProjectByValue(944621413)
const task = defaultTask(project.tasks)
expect(task.label).toBe("Entwicklung")
})
it("return undefined if no tasks given", () => {
let task = defaultTask(null)
expect(task).toBeUndefined()
task = defaultTask([])
expect(task).toBeUndefined()
})
})
describe("groupedProjectOptions", () => {
it("transforms projects into grouped options by company", () => {
const result = groupedProjectOptions(projects)
expect(map("label", result)).toEqual([
"Simplificator",
"MOCO APP",
"sharoo"
])
expect(map("label", result)).toEqual(["Simplificator", "MOCO APP", "sharoo"])
})
})
@@ -92,19 +116,19 @@ describe("utils", () => {
it("sets the correct tag and updates description", () => {
const changeset = {
description: "#meeting Lorem ipsum",
tag: ""
tag: "",
}
expect(extractAndSetTag(changeset)).toEqual({
description: "Lorem ipsum",
tag: "meeting"
tag: "meeting",
})
})
it("only matches tag at the beginning", () => {
const changeset = {
description: "Lorem #meeting ipsum",
tag: ""
tag: "",
}
expect(extractAndSetTag(changeset)).toEqual(changeset)
@@ -113,10 +137,27 @@ describe("utils", () => {
it("returns the changeset if not tag is set", () => {
const changeset = {
description: "Without tag",
tag: ""
tag: "",
}
expect(extractAndSetTag(changeset)).toEqual(changeset)
})
})
describe("formatDuration", () => {
it("format with defaults", () => {
expect(formatDuration(3600)).toBe("1:00:00")
expect(formatDuration(3661)).toBe("1:01:01")
})
it("format without seconds", () => {
expect(formatDuration(3600, { showSeconds: false })).toBe("1:00")
expect(formatDuration(3661, { showSeconds: false })).toBe("1:01")
})
it("format in decimals", () => {
expect(formatDuration(3600, { settingTimeTrackingHHMM: false })).toBe("1.00")
expect(formatDuration(3661, { settingTimeTrackingHHMM: false })).toBe("1.02")
})
})
})

View File

@@ -6,21 +6,19 @@ describe("utils", () => {
let matcher
beforeEach(() => {
matcher = createMatcher(remoteServices)
matcher = createMatcher(remoteServices, {})
})
describe("createMatcher", () => {
it("matches host and path", () => {
const service = matcher(
"https://github.com/hundertzehn/mocoapp/pull/123"
)
const service = matcher("https://github.com/hundertzehn/mocoapp/pull/123")
expect(service.key).toEqual("github-pr")
expect(service.name).toEqual("github")
})
it("matches query string", () => {
let service = matcher(
"https://moco-bx.atlassian.net/secure/RapidBoard.jspa?rapidView=2&projectKey=TEST1&modal=detail&selectedIssue=TEST1-1"
"https://moco-bx.atlassian.net/secure/RapidBoard.jspa?rapidView=2&projectKey=TEST1&modal=detail&selectedIssue=TEST1-1",
)
expect(service.key).toEqual("jira")
expect(service.name).toEqual("jira")
@@ -32,7 +30,7 @@ describe("utils", () => {
expect(service.match.id).toEqual("TEST1-1")
service = matcher(
"https://moco-bx.atlassian.net/secure/RapidBoard.jspa?rapidView=2&projectKey=TEST1&modal=detail"
"https://moco-bx.atlassian.net/secure/RapidBoard.jspa?rapidView=2&projectKey=TEST1&modal=detail",
)
expect(service.key).toEqual("jira")
expect(service.name).toEqual("jira")
@@ -44,7 +42,7 @@ describe("utils", () => {
expect(service.match.id).toBeUndefined()
service = matcher(
"https://moco-bx.atlassian.net/secure/RapidBoard.jspa?rapidView=2&projectKey=TEST1&modal=detail&selectedIssue="
"https://moco-bx.atlassian.net/secure/RapidBoard.jspa?rapidView=2&projectKey=TEST1&modal=detail&selectedIssue=",
)
expect(service.key).toEqual("jira")
expect(service.name).toEqual("jira")
@@ -55,9 +53,7 @@ describe("utils", () => {
expect(service.match.projectId).toEqual("TEST1")
expect(service.match.id).toEqual("")
service = matcher(
"https://moco-bx.atlassian.net/secure/RapidBoard.jspa"
)
service = matcher("https://moco-bx.atlassian.net/secure/RapidBoard.jspa")
expect(service.key).toEqual("jira")
expect(service.name).toEqual("jira")
expect(service.match.org).toEqual("moco-bx")
@@ -65,7 +61,7 @@ describe("utils", () => {
expect(service.match.id).toBeUndefined()
service = matcher(
"https://moco-bx.atlassian.net/secure/RapidBoard.jspa?rapidView=2&modal=detail&selectedIssue=TEST2-1"
"https://moco-bx.atlassian.net/secure/RapidBoard.jspa?rapidView=2&modal=detail&selectedIssue=TEST2-1",
)
expect(service.key).toEqual("jira")
expect(service.name).toEqual("jira")
@@ -78,29 +74,84 @@ describe("utils", () => {
})
it("matches url with hash", () => {
let service = matcher(
"https://www.wunderlist.com/webapp#/tasks/4771178545"
)
let service = matcher("https://www.wunderlist.com/webapp#/tasks/4771178545")
expect(service.key).toEqual("wunderlist")
expect(service.name).toEqual("wunderlist")
expect(service.match.id).toEqual("4771178545")
})
it("does not match different host", () => {
const service = matcher(
"https://trello.com/hundertzehn/mocoapp/pull/123"
)
const service = matcher("https://trello.com/hundertzehn/mocoapp/pull/123")
expect(service).toBeFalsy()
})
it("matches query string in the hash", () => {
const service = matcher(
"https://www.wrike.com/workspace.htm?acc=2771711#path=folder&id=342769537&p=342762920&a=2771711&c=board&ot=342769562&so=10&bso=10&sd=0&st=space-342762920",
)
expect(service.key).toEqual("wrike")
expect(service.name).toEqual("wrike")
expect(service.id).toEqual("342769562")
expect(service.match.id).toEqual("342769562")
})
it("matches query parameter with different names", () => {
expect(
matcher(
"https://www.wrike.com/workspace.htm?acc=2771711#path=mywork&id=342769537&p=342762920&a=2771711&c=board&ot=1234&so=10&bso=10&sd=0&st=space-342762920",
).id,
).toEqual("1234")
expect(
matcher(
"https://www.wrike.com/workspace.htm?acc=2771711#path=folder&id=342769537&p=342762920&a=2771711&c=board&t=1234&so=10&bso=10&sd=0&st=space-342762920",
).id,
).toEqual("1234")
})
it("should match gitlab-mergerequest url", () => {
const service = matcher(
"https://gitlab.com/testorganisatzion/testproject/-/merge_requests/1",
)
expect(service.id).toEqual("1")
expect(service.match.id).toEqual("1")
expect(service.name).toEqual("gitlab")
expect(service.projectId).toEqual("testproject")
})
it("should match gitlab-mergerequest url with group", () => {
const service = matcher(
"https://gitlab.com/testorganisatzion/test-group/testproject/-/merge_requests/1",
)
expect(service.id).toEqual("1")
expect(service.match.id).toEqual("1")
expect(service.name).toEqual("gitlab")
expect(service.projectId).toEqual("testproject")
})
it("should match gitlab-issue url", () => {
const service = matcher("https://gitlab.com/testorganisatzion/testproject/-/issues/1")
expect(service.id).toEqual("1")
expect(service.match.id).toEqual("1")
expect(service.name).toEqual("gitlab")
expect(service.projectId).toEqual("testproject")
})
it("should match gitlab-issue url with group", () => {
const service = matcher(
"https://gitlab.com/testorganisatzion/test-group/testproject/-/issues/1",
)
expect(service.id).toEqual("1")
expect(service.match.id).toEqual("1")
expect(service.name).toEqual("gitlab")
expect(service.projectId).toEqual("testproject")
})
})
describe("createEnhancer", () => {
it("enhances a services", () => {
const url = "https://github.com/hundertzehn/mocoapp/pull/123"
const document = {
querySelector: jest
.fn()
.mockReturnValue({ textContent: "[4321] Foo" })
querySelector: jest.fn().mockReturnValue({ textContent: "[4321] Foo" }),
}
const service = matcher(url)
const enhancedService = createEnhancer(document)(service)
@@ -111,4 +162,27 @@ describe("utils", () => {
})
})
})
describe("urlMatcher with overrideHosts", () => {
let matcher
beforeEach(() => {
matcher = createMatcher(remoteServices, {
github: "https://my-custom-github-url.com",
})
})
describe("createMatcher", () => {
it("matches overridden host and path", () => {
const service = matcher("https://my-custom-github-url.com/hundertzehn/mocoapp/pull/123")
expect(service.key).toEqual("github-pr")
expect(service.name).toEqual("github")
})
it("doesn't match default host and path", () => {
const service = matcher("https://github.com/hundertzehn/mocoapp/pull/123")
expect(service).toBe(undefined)
})
})
})
})

View File

@@ -2,27 +2,22 @@ require("dotenv").config()
const path = require("path")
const webpack = require("webpack")
const CleanWebpackPlugin = require("clean-webpack-plugin")
const { CleanWebpackPlugin } = require("clean-webpack-plugin")
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const RemoveSourceMapPlugin = require("./webpack/RemoveSourceMapPlugin")
const ZipPlugin = require("zip-webpack-plugin")
const {
BugsnagBuildReporterPlugin,
BugsnagSourceMapUploaderPlugin
} = require("webpack-bugsnag-plugins")
module.exports = env => {
module.exports = (env) => {
const config = {
entry: {
background: "./src/js/background.js",
content: "./src/js/content.js",
popup: "./src/js/popup.js",
options: "./src/js/options.js"
options: "./src/js/options.js",
},
output: {
path: path.join(__dirname, `build/${env.browser}`),
filename: `[name].${process.env.npm_package_version}.js`
filename: `[name].${process.env.npm_package_version}.js`,
},
module: {
rules: [
@@ -30,104 +25,85 @@ module.exports = env => {
test: /\.scss$/,
use: [
{
loader: MiniCssExtractPlugin.loader
loader: MiniCssExtractPlugin.loader,
},
"css-loader",
{
loader: "sass-loader",
options: {
includePaths: [path.join(__dirname, "src/css")]
}
}
sassOptions: {
includePaths: [path.join(__dirname, "src/css")],
},
},
},
],
exclude: /node_modules/
exclude: /node_modules/,
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
loader: "babel-loader",
},
},
{
test: /\.(jpg|png)$/,
loader: "file-loader",
options: {
name: "[path][name].[ext]"
name: "[path][name].[ext]",
},
exclude: /node_modules/
}
]
exclude: /node_modules/,
},
{
test: /\.svg$/,
loader: "svg-inline-loader",
},
],
},
plugins: [
new CleanWebpackPlugin([`build/${env.browser}`]),
new CleanWebpackPlugin({
cleanAfterEveryBuildPatterns: ["!manifest.json", "!*.html"],
}),
new webpack.DefinePlugin({
"process.env.NODE_ENV": JSON.stringify(env.NODE_ENV),
"process.env.BUGSNAG_API_KEY": JSON.stringify(
process.env.BUGSNAG_API_KEY
)
}),
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
chunkFilename: "[id].css",
}),
new HtmlWebpackPlugin({
template: path.join(__dirname, "src", "background.html"),
filename: "background.html",
chunks: ["background"]
filename: path.resolve(`build/${env.browser}`, "background.html"),
chunks: ["background"],
}),
new HtmlWebpackPlugin({
template: path.join(__dirname, "src", "popup.html"),
filename: "popup.html",
chunks: ["popup"]
filename: path.resolve(`build/${env.browser}`, "popup.html"),
chunks: ["popup"],
}),
new HtmlWebpackPlugin({
template: path.join(__dirname, "src", "options.html"),
filename: "options.html",
chunks: ["options"]
})
filename: path.resolve(`build/${env.browser}`, "options.html"),
chunks: ["options"],
}),
],
resolve: {
modules: [path.join(__dirname, "src/js"), "node_modules"],
alias: {
images: path.join(__dirname, "src/images")
}
images: path.join(__dirname, "src/images"),
},
},
mode: env.NODE_ENV || "development",
devtool: "cheap-module-source-map"
devtool: "cheap-module-source-map",
}
if (env.NODE_ENV === "production") {
config.devtool = "source-maps"
if (process.env.BUGSNAG_API_KEY) {
config.devtool = "none"
config.plugins.push(
new BugsnagBuildReporterPlugin({
apiKey: process.env.BUGSNAG_API_KEY,
appVersion: process.env.npm_package_version,
releaseStage: "production"
}),
// important: upload sourcemaps before removing source mapping url
new BugsnagSourceMapUploaderPlugin({
apiKey: process.env.BUGSNAG_API_KEY,
appVersion: process.env.npm_package_version,
publicPath:
env.browser === "firefox"
? "moz-extension*://*/"
: "chrome-extension*://*/", // extra asterisk after protocol needed
overwrite: true
})
)
}
config.plugins.push(
new RemoveSourceMapPlugin(),
new ZipPlugin({
filename: `moco-bx-${env.browser}-v${
process.env.npm_package_version
}.zip`,
exclude: [/\.map$/]
})
filename: `moco-bx-${env.browser}-v${process.env.npm_package_version}.zip`,
exclude: [/\.map$/],
}),
)
}

View File

@@ -3,39 +3,37 @@ const { compact } = require("lodash/fp")
const baseConfig = require("./webpack.base.config")
module.exports = env => {
module.exports = (env) => {
const config = baseConfig(env)
config.plugins.unshift(
new CopyWebpackPlugin([
new CopyWebpackPlugin({
patterns: [
{
from: "src/manifest.json",
transform: function (content, _path) {
const manifest = JSON.parse(
content
.toString()
.replace(/\[version\]/g, process.env.npm_package_version)
content.toString().replace(/\[version\]/g, process.env.npm_package_version),
)
return Buffer.from(
JSON.stringify({
...manifest,
permissions: compact([
...manifest.permissions,
env.NODE_ENV === "development"
? "http://*.mocoapp.localhost/*"
: null
env.NODE_ENV === "development" ? "http://*.mocoapp.localhost/*" : null,
]),
options_ui: {
...manifest.options_ui,
chrome_style: true
chrome_style: true,
},
description: process.env.npm_package_description,
version: process.env.npm_package_version
})
version: process.env.npm_package_version,
}),
)
}
}
])
},
},
],
}),
)
return config

View File

@@ -1,47 +1,45 @@
const uuidv4 = require("uuid/v4")
const { v4: uuidv4 } = require("uuid")
const CopyWebpackPlugin = require("copy-webpack-plugin")
const { compact } = require("lodash/fp")
const baseConfig = require("./webpack.base.config")
module.exports = env => {
module.exports = (env) => {
const config = baseConfig(env)
config.plugins.unshift(
new CopyWebpackPlugin([
new CopyWebpackPlugin({
patterns: [
{
from: "src/manifest.json",
transform: function (content, _path) {
const manifest = JSON.parse(
content
.toString()
.replace(/\[version\]/g, process.env.npm_package_version)
content.toString().replace(/\[version\]/g, process.env.npm_package_version),
)
return Buffer.from(
JSON.stringify({
...manifest,
permissions: compact([
...manifest.permissions,
env.NODE_ENV === "development"
? "http://*.mocoapp.localhost/*"
: null
env.NODE_ENV === "development" ? "http://*.mocoapp.localhost/*" : null,
]),
options_ui: {
...manifest.options_ui,
browser_style: true
browser_style: true,
},
browser_specific_settings: {
gecko: {
id: process.env.APPLICATION_ID || `{${uuidv4()}}`
}
id: process.env.APPLICATION_ID || `{${uuidv4()}}`,
},
},
description: process.env.npm_package_description,
version: process.env.npm_package_version
})
version: process.env.npm_package_version,
}),
)
}
}
])
},
},
],
}),
)
return config

View File

@@ -1,23 +0,0 @@
module.exports = class RemoveSourceMapPlugin {
constructor(options = {}) {
this.test = options.test || /\.(js|css)$/
}
apply(compiler) {
compiler.hooks.afterEmit.tap("RemoveSourceMapPlugin", compilation => {
Object.keys(compilation.assets)
.filter(key => this.test.test(key))
.forEach(key => {
const asset = compilation.assets[key]
const source = asset
.source()
.replace(/# sourceMappingURL=(.*\.map)/g, "# $1")
compilation.assets[key] = Object.assign(asset, {
source: function() {
return source
}
})
})
})
}
}

8504
yarn.lock

File diff suppressed because it is too large Load Diff