cozy-harvest-lib 12.2.0 → 12.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,25 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [12.3.0](https://github.com/cozy/cozy-libs/compare/cozy-harvest-lib@12.2.0...cozy-harvest-lib@12.3.0) (2023-01-17)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **harvest:** Add missing props for ConfigurationTab ([b80b961](https://github.com/cozy/cozy-libs/commit/b80b961e4643e4b246686961e7001f61e04f31ac))
12
+ * **harvest:** Add missing translation for launchTrigger lastSync format ([b8fe298](https://github.com/cozy/cozy-libs/commit/b8fe298a514fccefb396ed2211a785d32b4901e1))
13
+ * **harvest:** WithRouter now handles route even without baseRoute ([6486e7f](https://github.com/cozy/cozy-libs/commit/6486e7ffa1ca61551ee8f59aed3e350b559f093e))
14
+
15
+
16
+ ### Features
17
+
18
+ * **harvest:** Add `/config` route on routerV4 and remove tabs ([79c6947](https://github.com/cozy/cozy-libs/commit/79c694721e23678238b3a06c06601ebf476314f1))
19
+ * **harvest:** Add `configure` option in LaunchTriggerAlert menu ([a3be41c](https://github.com/cozy/cozy-libs/commit/a3be41c0816b10166cfaabdeadfd53df6e72ec92))
20
+
21
+
22
+
23
+
24
+
6
25
  # [12.2.0](https://github.com/cozy/cozy-libs/compare/cozy-harvest-lib@12.1.0...cozy-harvest-lib@12.2.0) (2023-01-11)
7
26
 
8
27
 
@@ -0,0 +1,38 @@
1
+ import React from 'react';
2
+ import DialogContent from '@material-ui/core/DialogContent';
3
+ import useBreakpoints from 'cozy-ui/transpiled/react/hooks/useBreakpoints';
4
+ import FlowProvider from '../../FlowProvider';
5
+ import TriggerError from '../TriggerError';
6
+
7
+ var AccountModalContentWrapper = function AccountModalContentWrapper(_ref) {
8
+ var children = _ref.children,
9
+ trigger = _ref.trigger,
10
+ account = _ref.account,
11
+ konnector = _ref.konnector;
12
+
13
+ var _useBreakpoints = useBreakpoints(),
14
+ isMobile = _useBreakpoints.isMobile;
15
+
16
+ return /*#__PURE__*/React.createElement(DialogContent, {
17
+ className: isMobile ? 'u-p-0' : 'u-pt-0'
18
+ }, /*#__PURE__*/React.createElement(FlowProvider, {
19
+ initialTrigger: trigger,
20
+ konnector: konnector
21
+ }, function (_ref2) {
22
+ var flow = _ref2.flow;
23
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(TriggerError, {
24
+ flow: flow,
25
+ konnector: konnector,
26
+ account: account,
27
+ trigger: trigger
28
+ }), React.Children.map(children, function (child) {
29
+ return /*#__PURE__*/React.isValidElement(child) ? /*#__PURE__*/React.cloneElement(child, {
30
+ flow: flow,
31
+ trigger: trigger,
32
+ account: account
33
+ }) : null;
34
+ }));
35
+ }));
36
+ };
37
+
38
+ export default AccountModalContentWrapper;
@@ -0,0 +1,68 @@
1
+ import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
2
+ var _excluded = ["data"];
3
+ import React from 'react';
4
+ import PropTypes from 'prop-types';
5
+ import DialogContent from '@material-ui/core/DialogContent';
6
+ import { useQuery, isQueryLoading } from 'cozy-client';
7
+ import Spinner from 'cozy-ui/transpiled/react/Spinner';
8
+ import { buildAccountQueryById } from '../../../connections/accounts';
9
+ import { withMountPointProps } from '../../MountPointContext';
10
+ import { getMatchingTrigger } from '../helpers';
11
+ import AccountModalHeader from '../AccountModalHeader';
12
+ import Error from '../Error';
13
+
14
+ var AccountModalWithoutTabs = function AccountModalWithoutTabs(_ref) {
15
+ var accountsAndTriggers = _ref.accountsAndTriggers,
16
+ konnector = _ref.konnector,
17
+ accountId = _ref.accountId,
18
+ children = _ref.children;
19
+ var matchingTrigger = getMatchingTrigger(accountsAndTriggers, accountId);
20
+ var matchingAccountId = matchingTrigger ? accountId : undefined;
21
+
22
+ var _buildAccountQueryByI = buildAccountQueryById(matchingAccountId),
23
+ definition = _buildAccountQueryByI.definition,
24
+ options = _buildAccountQueryByI.options;
25
+
26
+ var _useQuery = useQuery(definition, options),
27
+ accounts = _useQuery.data,
28
+ accountQueryResult = _objectWithoutProperties(_useQuery, _excluded);
29
+
30
+ var isLoading = isQueryLoading(accountQueryResult) || accountQueryResult.hasMore;
31
+ var isError = !isLoading && (!matchingTrigger || !accounts || (accounts === null || accounts === void 0 ? void 0 : accounts.length) === 0);
32
+ var account = accounts === null || accounts === void 0 ? void 0 : accounts[0];
33
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AccountModalHeader, {
34
+ konnector: konnector,
35
+ account: account,
36
+ accountsAndTriggers: accountsAndTriggers
37
+ }), (isError || isLoading) && /*#__PURE__*/React.createElement(DialogContent, {
38
+ className: "u-pb-2"
39
+ }, isError && /*#__PURE__*/React.createElement(Error, {
40
+ accountId: accountId,
41
+ accountsAndTriggers: accountsAndTriggers,
42
+ trigger: matchingTrigger,
43
+ lastError: accountQueryResult.lastError
44
+ }), isLoading && /*#__PURE__*/React.createElement(Spinner, {
45
+ className: "u-flex u-flex-justify-center",
46
+ size: "xxlarge"
47
+ })), !isError && !isLoading && React.Children.map(children, function (child) {
48
+ return /*#__PURE__*/React.isValidElement(child) ? /*#__PURE__*/React.cloneElement(child, {
49
+ trigger: matchingTrigger,
50
+ account: account,
51
+ konnector: konnector
52
+ }) : null;
53
+ }));
54
+ };
55
+
56
+ AccountModalWithoutTabs.propTypes = {
57
+ konnector: PropTypes.object.isRequired,
58
+
59
+ /**
60
+ * @type {{ account: 'io.cozy.accounts', trigger: 'io.cozy.triggers' }[]} - An array of objects containing an account and its associated trigger
61
+ */
62
+ accountsAndTriggers: PropTypes.arrayOf(PropTypes.shape({
63
+ account: PropTypes.object.isRequired,
64
+ trigger: PropTypes.object.isRequired
65
+ })).isRequired,
66
+ accountId: PropTypes.string.isRequired
67
+ };
68
+ export default withMountPointProps(AccountModalWithoutTabs);
@@ -278,7 +278,7 @@ var ConfigurationTab = function ConfigurationTab(_ref2) {
278
278
 
279
279
  ConfigurationTab.propTypes = {
280
280
  konnector: PropTypes.object.isRequired,
281
- account: PropTypes.object.isRequired,
281
+ account: PropTypes.object,
282
282
  error: PropTypes.object,
283
283
  flow: PropTypes.object,
284
284
  addAccount: PropTypes.func.isRequired,
@@ -27,6 +27,7 @@ var styles = {
27
27
  };
28
28
  export var DataTab = function DataTab(_ref) {
29
29
  var konnector = _ref.konnector,
30
+ konnectorRoot = _ref.konnectorRoot,
30
31
  trigger = _ref.trigger,
31
32
  client = _ref.client,
32
33
  flow = _ref.flow,
@@ -56,6 +57,7 @@ export var DataTab = function DataTab(_ref) {
56
57
  maintenanceMessages = _useMaintenanceStatus2.messages;
57
58
 
58
59
  return /*#__PURE__*/React.createElement("div", null, flag('harvest.inappconnectors.enabled') && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(LaunchTriggerCard, {
60
+ konnectorRoot: konnectorRoot,
59
61
  flow: flow,
60
62
  disabled: isInMaintenance
61
63
  }), isMobile && /*#__PURE__*/React.createElement(Divider, {
@@ -90,7 +92,10 @@ export var DataTab = function DataTab(_ref) {
90
92
  };
91
93
  DataTab.propTypes = {
92
94
  konnector: PropTypes.object.isRequired,
95
+ konnectorRoot: PropTypes.string,
93
96
  trigger: PropTypes.object.isRequired,
94
- client: PropTypes.object.isRequired
97
+ client: PropTypes.object.isRequired,
98
+ flow: PropTypes.object,
99
+ account: PropTypes.object
95
100
  };
96
101
  export default withClient(DataTab);
@@ -7,11 +7,17 @@ import NewAccountModal from '../NewAccountModal';
7
7
  import EditAccountModal from '../EditAccountModal';
8
8
  import KonnectorSuccess from '../KonnectorSuccess';
9
9
  import HarvestModalRoot from '../HarvestModalRoot';
10
+ import AccountModalWithoutTabs from '../AccountModalWithoutTabs/FovV4Router/AccountModalWithoutTabs';
11
+ import AccountModalContentWrapper from '../AccountModalWithoutTabs/FovV4Router/AccountModalContentWrapper';
12
+ import DataTab from '../KonnectorConfiguration/DataTab';
13
+ import ConfigurationTab from '../KonnectorConfiguration/ConfigurationTab';
14
+ import withAdaptiveRouter from '../hoc/withRouter';
10
15
 
11
16
  var RoutesV4 = function RoutesV4(_ref) {
12
17
  var konnectorRoot = _ref.konnectorRoot,
13
18
  konnectorWithTriggers = _ref.konnectorWithTriggers,
14
19
  accountsAndTriggers = _ref.accountsAndTriggers,
20
+ historyAction = _ref.historyAction,
15
21
  onSuccess = _ref.onSuccess,
16
22
  onDismiss = _ref.onDismiss;
17
23
  return /*#__PURE__*/React.createElement(Switch, null, /*#__PURE__*/React.createElement(Route, {
@@ -23,11 +29,51 @@ var RoutesV4 = function RoutesV4(_ref) {
23
29
  konnector: konnectorWithTriggers
24
30
  });
25
31
  }
26
- }), /*#__PURE__*/React.createElement(Route, {
32
+ }), flag('harvest.inappconnectors.enabled') ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Route, {
27
33
  path: "".concat(konnectorRoot, "/accounts/:accountId"),
28
34
  exact: true,
29
35
  render: function render(_ref2) {
30
36
  var match = _ref2.match;
37
+ return /*#__PURE__*/React.createElement(AccountModalWithoutTabs, {
38
+ konnector: konnectorWithTriggers,
39
+ accountId: match.params.accountId,
40
+ accountsAndTriggers: accountsAndTriggers,
41
+ showNewAccountButton: !konnectorWithTriggers.clientSide,
42
+ showAccountSelection: !konnectorWithTriggers.clientSide,
43
+ onDismiss: onDismiss
44
+ }, /*#__PURE__*/React.createElement(AccountModalContentWrapper, null, /*#__PURE__*/React.createElement(DataTab, {
45
+ konnectorRoot: "".concat(konnectorRoot, "/accounts/").concat(match.params.accountId),
46
+ konnector: konnectorWithTriggers,
47
+ showNewAccountButton: !konnectorWithTriggers.clientSide,
48
+ onDismiss: onDismiss
49
+ })));
50
+ }
51
+ }), /*#__PURE__*/React.createElement(Route, {
52
+ path: "".concat(konnectorRoot, "/accounts/:accountId/config"),
53
+ exact: true,
54
+ render: function render(_ref3) {
55
+ var match = _ref3.match;
56
+ return /*#__PURE__*/React.createElement(AccountModalWithoutTabs, {
57
+ konnector: konnectorWithTriggers,
58
+ accountId: match.params.accountId,
59
+ accountsAndTriggers: accountsAndTriggers,
60
+ showNewAccountButton: !konnectorWithTriggers.clientSide,
61
+ showAccountSelection: !konnectorWithTriggers.clientSide,
62
+ onDismiss: onDismiss
63
+ }, /*#__PURE__*/React.createElement(AccountModalContentWrapper, null, /*#__PURE__*/React.createElement(ConfigurationTab, {
64
+ konnector: konnectorWithTriggers,
65
+ showNewAccountButton: !konnectorWithTriggers.clientSide,
66
+ onAccountDeleted: onDismiss,
67
+ addAccount: function addAccount() {
68
+ return historyAction("".concat(konnectorRoot, "/new"), 'replace');
69
+ }
70
+ })));
71
+ }
72
+ })) : /*#__PURE__*/React.createElement(Route, {
73
+ path: "".concat(konnectorRoot, "/accounts/:accountId"),
74
+ exact: true,
75
+ render: function render(_ref4) {
76
+ var match = _ref4.match;
31
77
  return /*#__PURE__*/React.createElement(AccountModal, {
32
78
  konnector: konnectorWithTriggers,
33
79
  accountId: match.params.accountId,
@@ -40,8 +86,8 @@ var RoutesV4 = function RoutesV4(_ref) {
40
86
  }), /*#__PURE__*/React.createElement(Route, {
41
87
  path: "".concat(konnectorRoot, "/accounts/:accountId/edit"),
42
88
  exact: true,
43
- render: function render(_ref3) {
44
- var match = _ref3.match;
89
+ render: function render(_ref5) {
90
+ var match = _ref5.match;
45
91
  return /*#__PURE__*/React.createElement(EditAccountModal, {
46
92
  konnector: konnectorWithTriggers,
47
93
  accountId: match.params.accountId,
@@ -67,8 +113,8 @@ var RoutesV4 = function RoutesV4(_ref) {
67
113
  }), !flag('harvest.inappconnectors.enabled') && /*#__PURE__*/React.createElement(Route, {
68
114
  path: "".concat(konnectorRoot, "/accounts/:accountId/success"),
69
115
  exact: true,
70
- render: function render(_ref4) {
71
- var match = _ref4.match;
116
+ render: function render(_ref6) {
117
+ var match = _ref6.match;
72
118
  return /*#__PURE__*/React.createElement(KonnectorSuccess, {
73
119
  konnector: konnectorWithTriggers,
74
120
  accountId: match.params.accountId,
@@ -82,4 +128,4 @@ var RoutesV4 = function RoutesV4(_ref) {
82
128
  }));
83
129
  };
84
130
 
85
- export default RoutesV4;
131
+ export default withAdaptiveRouter(RoutesV4);
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { Routes, Route, Navigate, useParams } from 'react-router-dom';
2
+ import { Routes, Route, Navigate, useParams, useNavigate } from 'react-router-dom';
3
3
  import flag from 'cozy-flags';
4
4
  import { ViewerModal } from '../../datacards/ViewerModal';
5
5
  import AccountModal from '../AccountModal';
@@ -22,6 +22,7 @@ var RoutesV6 = function RoutesV6(_ref) {
22
22
  accountsAndTriggers = _ref.accountsAndTriggers,
23
23
  onSuccess = _ref.onSuccess,
24
24
  onDismiss = _ref.onDismiss;
25
+ var navigate = useNavigate();
25
26
  return /*#__PURE__*/React.createElement(Routes, null, /*#__PURE__*/React.createElement(Route, {
26
27
  path: "/",
27
28
  element: /*#__PURE__*/React.createElement(HarvestModalRoot, {
@@ -59,7 +60,12 @@ var RoutesV6 = function RoutesV6(_ref) {
59
60
  element: /*#__PURE__*/React.createElement(AccountModalContentWrapper, null, /*#__PURE__*/React.createElement(ConfigurationTab, {
60
61
  konnector: konnectorWithTriggers,
61
62
  showNewAccountButton: !konnectorWithTriggers.clientSide,
62
- onDismiss: onDismiss
63
+ onAccountDeleted: onDismiss,
64
+ addAccount: function addAccount() {
65
+ return navigate('new', {
66
+ replace: true
67
+ });
68
+ }
63
69
  }))
64
70
  })) : /*#__PURE__*/React.createElement(Route, {
65
71
  path: "accounts/:accountId",
@@ -1,5 +1,6 @@
1
1
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
2
  import React, { useRef, useState, useEffect } from 'react';
3
+ import PropTypes from 'prop-types';
3
4
  import ActionMenu, { ActionMenuItem } from 'cozy-ui/transpiled/react/ActionMenu';
4
5
  import Alert from 'cozy-ui/transpiled/react/Alert';
5
6
  import Button from 'cozy-ui/transpiled/react/Buttons';
@@ -10,17 +11,29 @@ import Spinner from 'cozy-ui/transpiled/react/Spinner';
10
11
  import Snackbar from 'cozy-ui/transpiled/react/Snackbar';
11
12
  import DotsIcon from 'cozy-ui/transpiled/react/Icons/Dots';
12
13
  import SyncIcon from 'cozy-ui/transpiled/react/Icons/Sync';
14
+ import GearIcon from 'cozy-ui/transpiled/react/Icons/Gear';
13
15
  import { getLastSuccessDate, getKonnectorSlug } from '../../helpers/triggers';
14
16
  import { isRunnable } from '../../helpers/konnectors';
15
17
  import { useFlowState } from '../../models/withConnectionFlow';
16
18
  import { SUCCESS } from '../../models/flowEvents';
19
+ import withAdaptiveRouter from '../hoc/withRouter';
17
20
  import KonnectorIcon from '../KonnectorIcon';
18
21
  import { makeLabel } from './helpers';
22
+ var styles = {
23
+ // tricks to put 48px wide button inside 32px height container
24
+ // without enlarging the container
25
+ iconButtonWrapper: {
26
+ height: 32,
27
+ width: 46
28
+ }
29
+ };
19
30
  export var LaunchTriggerAlert = function LaunchTriggerAlert(_ref) {
20
31
  var flow = _ref.flow,
21
32
  f = _ref.f,
22
33
  t = _ref.t,
23
- disabled = _ref.disabled;
34
+ disabled = _ref.disabled,
35
+ konnectorRoot = _ref.konnectorRoot,
36
+ historyAction = _ref.historyAction;
24
37
 
25
38
  var _useState = useState(false),
26
39
  _useState2 = _slicedToArray(_useState, 2),
@@ -53,6 +66,7 @@ export var LaunchTriggerAlert = function LaunchTriggerAlert(_ref) {
53
66
  }
54
67
  }, [status]);
55
68
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Alert, {
69
+ className: "u-pr-half",
56
70
  color: "var(--paperBackground)",
57
71
  icon: running ? /*#__PURE__*/React.createElement(Spinner, {
58
72
  className: "u-flex",
@@ -71,15 +85,17 @@ export var LaunchTriggerAlert = function LaunchTriggerAlert(_ref) {
71
85
  autoSuccessTimer: false
72
86
  });
73
87
  }
74
- }), /*#__PURE__*/React.createElement(IconButton, {
88
+ }), /*#__PURE__*/React.createElement("div", {
89
+ className: "u-flex u-flex-items-center",
90
+ style: styles.iconButtonWrapper
91
+ }, /*#__PURE__*/React.createElement(IconButton, {
75
92
  ref: anchorRef,
76
93
  onClick: function onClick() {
77
94
  return setShowOptions(true);
78
- },
79
- className: "u-p-half"
95
+ }
80
96
  }, /*#__PURE__*/React.createElement(Icon, {
81
97
  icon: DotsIcon
82
- })), showOptions && /*#__PURE__*/React.createElement(ActionMenu, {
98
+ }))), showOptions && /*#__PURE__*/React.createElement(ActionMenu, {
83
99
  anchorElRef: anchorRef,
84
100
  autoclose: true,
85
101
  onClose: function onClose() {
@@ -95,7 +111,14 @@ export var LaunchTriggerAlert = function LaunchTriggerAlert(_ref) {
95
111
  });
96
112
  setShowOptions(false);
97
113
  }
98
- }, t('card.launchTrigger.button.label'))))
114
+ }, t('card.launchTrigger.button.label')), /*#__PURE__*/React.createElement(ActionMenuItem, {
115
+ left: /*#__PURE__*/React.createElement(Icon, {
116
+ icon: GearIcon
117
+ }),
118
+ onClick: function onClick() {
119
+ return historyAction("".concat(konnectorRoot, "/config"), 'push');
120
+ }
121
+ }, t('card.launchTrigger.configure'))))
99
122
  }, /*#__PURE__*/React.createElement(Typography, {
100
123
  variant: "caption"
101
124
  }, makeLabel({
@@ -118,4 +141,15 @@ export var LaunchTriggerAlert = function LaunchTriggerAlert(_ref) {
118
141
  }
119
142
  }, t('card.launchTrigger.success'))));
120
143
  };
121
- export default LaunchTriggerAlert;
144
+ LaunchTriggerAlert.defaultProps = {
145
+ konnectorRoot: ''
146
+ };
147
+ LaunchTriggerAlert.propTypes = {
148
+ flow: PropTypes.object,
149
+ f: PropTypes.function,
150
+ t: PropTypes.function,
151
+ disabled: PropTypes.bool,
152
+ konnectorRoot: PropTypes.string,
153
+ historyAction: PropTypes.function
154
+ };
155
+ export default withAdaptiveRouter(LaunchTriggerAlert);
@@ -23,7 +23,8 @@ export var historyAction = function historyAction(props, historyOrNavigate) {
23
23
  });
24
24
  }
25
25
 
26
- var segments = '/'.concat(baseRoute.split('/').concat(route.split('/')).filter(Boolean).join('/'));
26
+ var splitBaseRoute = (baseRoute === null || baseRoute === void 0 ? void 0 : baseRoute.split('/')) || [];
27
+ var segments = '/'.concat(splitBaseRoute.concat(route.split('/')).filter(Boolean).join('/'));
27
28
  return historyOrNavigate[method](segments);
28
29
  };
29
30
  };
@@ -74,4 +74,12 @@ describe('historyAction', function () {
74
74
  expect(historyMock.replace).toHaveBeenCalledWith('/base/route/one/two');
75
75
  historyMock.replace.mockReset();
76
76
  });
77
+ it('should handle even if no base route', function () {
78
+ setup({
79
+ baseRoute: undefined,
80
+ route: '/one'
81
+ });
82
+ expect(historyMock.replace).toHaveBeenCalledWith('/one');
83
+ historyMock.replace.mockReset();
84
+ });
77
85
  });
@@ -77,6 +77,7 @@
77
77
  "button": {
78
78
  "label": "Run again now"
79
79
  },
80
+ "configure": "Configure",
80
81
  "success": "Successful synchronization",
81
82
  "error": "An error occured.",
82
83
  "frequency": {
@@ -92,6 +93,7 @@
92
93
  "syncing": "Data recovery…",
93
94
  "unknown": "Unknown",
94
95
  "afterSomeTimes": "Sync. %{times} ago",
96
+ "format": "MM/DD/YYYY",
95
97
  "justNow": "Sync. just now"
96
98
  }
97
99
  },
@@ -77,6 +77,7 @@
77
77
  "button": {
78
78
  "label": "Mettre à jour"
79
79
  },
80
+ "configure": "Configurer",
80
81
  "success": "Synchronisation réussie",
81
82
  "error": "Une erreur est survenue.",
82
83
  "frequency": {
@@ -92,6 +93,7 @@
92
93
  "syncing": "Récupération des données…",
93
94
  "unknown": "Indéterminée",
94
95
  "afterSomeTimes": "Sync. il y a %{times}",
96
+ "format": "DD/MM/YYYY",
95
97
  "justNow": "Sync. à l'instant"
96
98
  }
97
99
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-harvest-lib",
3
- "version": "12.2.0",
3
+ "version": "12.3.0",
4
4
  "description": "Provides logic, modules and components for Cozy's harvest applications.",
5
5
  "main": "dist/index.js",
6
6
  "author": "Cozy",
@@ -88,5 +88,5 @@
88
88
  "react-router-dom": ">=4.3.1"
89
89
  },
90
90
  "sideEffects": false,
91
- "gitHead": "53527f83fa5f4cacc7202f90e4afe22a43b24311"
91
+ "gitHead": "b33303f0e197336675f8b4817576de6c353e6015"
92
92
  }
@@ -0,0 +1,40 @@
1
+ import React from 'react'
2
+ import DialogContent from '@material-ui/core/DialogContent'
3
+
4
+ import useBreakpoints from 'cozy-ui/transpiled/react/hooks/useBreakpoints'
5
+
6
+ import FlowProvider from '../../FlowProvider'
7
+ import TriggerError from '../TriggerError'
8
+
9
+ const AccountModalContentWrapper = ({
10
+ children,
11
+ trigger,
12
+ account,
13
+ konnector
14
+ }) => {
15
+ const { isMobile } = useBreakpoints()
16
+
17
+ return (
18
+ <DialogContent className={isMobile ? 'u-p-0' : 'u-pt-0'}>
19
+ <FlowProvider initialTrigger={trigger} konnector={konnector}>
20
+ {({ flow }) => (
21
+ <>
22
+ <TriggerError
23
+ flow={flow}
24
+ konnector={konnector}
25
+ account={account}
26
+ trigger={trigger}
27
+ />
28
+ {React.Children.map(children, child =>
29
+ React.isValidElement(child)
30
+ ? React.cloneElement(child, { flow, trigger, account })
31
+ : null
32
+ )}
33
+ </>
34
+ )}
35
+ </FlowProvider>
36
+ </DialogContent>
37
+ )
38
+ }
39
+
40
+ export default AccountModalContentWrapper
@@ -0,0 +1,88 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import DialogContent from '@material-ui/core/DialogContent'
4
+
5
+ import { useQuery, isQueryLoading } from 'cozy-client'
6
+ import Spinner from 'cozy-ui/transpiled/react/Spinner'
7
+
8
+ import { buildAccountQueryById } from '../../../connections/accounts'
9
+ import { withMountPointProps } from '../../MountPointContext'
10
+ import { getMatchingTrigger } from '../helpers'
11
+ import AccountModalHeader from '../AccountModalHeader'
12
+ import Error from '../Error'
13
+
14
+ const AccountModalWithoutTabs = ({
15
+ accountsAndTriggers,
16
+ konnector,
17
+ accountId,
18
+ children
19
+ }) => {
20
+ const matchingTrigger = getMatchingTrigger(accountsAndTriggers, accountId)
21
+ const matchingAccountId = matchingTrigger ? accountId : undefined
22
+
23
+ const { definition, options } = buildAccountQueryById(matchingAccountId)
24
+ const { data: accounts, ...accountQueryResult } = useQuery(
25
+ definition,
26
+ options
27
+ )
28
+
29
+ const isLoading =
30
+ isQueryLoading(accountQueryResult) || accountQueryResult.hasMore
31
+
32
+ const isError =
33
+ !isLoading && (!matchingTrigger || !accounts || accounts?.length === 0)
34
+
35
+ const account = accounts?.[0]
36
+
37
+ return (
38
+ <>
39
+ <AccountModalHeader
40
+ konnector={konnector}
41
+ account={account}
42
+ accountsAndTriggers={accountsAndTriggers}
43
+ />
44
+ {(isError || isLoading) && (
45
+ <DialogContent className="u-pb-2">
46
+ {isError && (
47
+ <Error
48
+ accountId={accountId}
49
+ accountsAndTriggers={accountsAndTriggers}
50
+ trigger={matchingTrigger}
51
+ lastError={accountQueryResult.lastError}
52
+ />
53
+ )}
54
+ {isLoading && (
55
+ <Spinner className="u-flex u-flex-justify-center" size="xxlarge" />
56
+ )}
57
+ </DialogContent>
58
+ )}
59
+ {!isError &&
60
+ !isLoading &&
61
+ React.Children.map(children, child =>
62
+ React.isValidElement(child)
63
+ ? React.cloneElement(child, {
64
+ trigger: matchingTrigger,
65
+ account,
66
+ konnector
67
+ })
68
+ : null
69
+ )}
70
+ </>
71
+ )
72
+ }
73
+
74
+ AccountModalWithoutTabs.propTypes = {
75
+ konnector: PropTypes.object.isRequired,
76
+ /**
77
+ * @type {{ account: 'io.cozy.accounts', trigger: 'io.cozy.triggers' }[]} - An array of objects containing an account and its associated trigger
78
+ */
79
+ accountsAndTriggers: PropTypes.arrayOf(
80
+ PropTypes.shape({
81
+ account: PropTypes.object.isRequired,
82
+ trigger: PropTypes.object.isRequired
83
+ })
84
+ ).isRequired,
85
+ accountId: PropTypes.string.isRequired
86
+ }
87
+
88
+ export default withMountPointProps(AccountModalWithoutTabs)
@@ -233,7 +233,7 @@ const ConfigurationTab = ({
233
233
 
234
234
  ConfigurationTab.propTypes = {
235
235
  konnector: PropTypes.object.isRequired,
236
- account: PropTypes.object.isRequired,
236
+ account: PropTypes.object,
237
237
  error: PropTypes.object,
238
238
  flow: PropTypes.object,
239
239
  addAccount: PropTypes.func.isRequired,
@@ -26,7 +26,14 @@ const styles = {
26
26
  }
27
27
  }
28
28
 
29
- export const DataTab = ({ konnector, trigger, client, flow, account }) => {
29
+ export const DataTab = ({
30
+ konnector,
31
+ konnectorRoot,
32
+ trigger,
33
+ client,
34
+ flow,
35
+ account
36
+ }) => {
30
37
  const { isMobile } = useBreakpoints()
31
38
  const flowState = flow.getState()
32
39
  const { error } = flowState
@@ -51,7 +58,11 @@ export const DataTab = ({ konnector, trigger, client, flow, account }) => {
51
58
  <div>
52
59
  {flag('harvest.inappconnectors.enabled') && (
53
60
  <>
54
- <LaunchTriggerCard flow={flow} disabled={isInMaintenance} />
61
+ <LaunchTriggerCard
62
+ konnectorRoot={konnectorRoot}
63
+ flow={flow}
64
+ disabled={isInMaintenance}
65
+ />
55
66
  {isMobile && <Divider style={styles.divider} />}
56
67
  </>
57
68
  )}
@@ -90,8 +101,11 @@ export const DataTab = ({ konnector, trigger, client, flow, account }) => {
90
101
 
91
102
  DataTab.propTypes = {
92
103
  konnector: PropTypes.object.isRequired,
104
+ konnectorRoot: PropTypes.string,
93
105
  trigger: PropTypes.object.isRequired,
94
- client: PropTypes.object.isRequired
106
+ client: PropTypes.object.isRequired,
107
+ flow: PropTypes.object,
108
+ account: PropTypes.object
95
109
  }
96
110
 
97
111
  export default withClient(DataTab)
@@ -9,11 +9,17 @@ import NewAccountModal from '../NewAccountModal'
9
9
  import EditAccountModal from '../EditAccountModal'
10
10
  import KonnectorSuccess from '../KonnectorSuccess'
11
11
  import HarvestModalRoot from '../HarvestModalRoot'
12
+ import AccountModalWithoutTabs from '../AccountModalWithoutTabs/FovV4Router/AccountModalWithoutTabs'
13
+ import AccountModalContentWrapper from '../AccountModalWithoutTabs/FovV4Router/AccountModalContentWrapper'
14
+ import DataTab from '../KonnectorConfiguration/DataTab'
15
+ import ConfigurationTab from '../KonnectorConfiguration/ConfigurationTab'
16
+ import withAdaptiveRouter from '../hoc/withRouter'
12
17
 
13
18
  const RoutesV4 = ({
14
19
  konnectorRoot,
15
20
  konnectorWithTriggers,
16
21
  accountsAndTriggers,
22
+ historyAction,
17
23
  onSuccess,
18
24
  onDismiss
19
25
  }) => {
@@ -29,20 +35,74 @@ const RoutesV4 = ({
29
35
  />
30
36
  )}
31
37
  />
32
- <Route
33
- path={`${konnectorRoot}/accounts/:accountId`}
34
- exact
35
- render={({ match }) => (
36
- <AccountModal
37
- konnector={konnectorWithTriggers}
38
- accountId={match.params.accountId}
39
- accountsAndTriggers={accountsAndTriggers}
40
- onDismiss={onDismiss}
41
- showNewAccountButton={!konnectorWithTriggers.clientSide}
42
- showAccountSelection={!konnectorWithTriggers.clientSide}
38
+ {flag('harvest.inappconnectors.enabled') ? (
39
+ <>
40
+ <Route
41
+ path={`${konnectorRoot}/accounts/:accountId`}
42
+ exact
43
+ render={({ match }) => (
44
+ <AccountModalWithoutTabs
45
+ konnector={konnectorWithTriggers}
46
+ accountId={match.params.accountId}
47
+ accountsAndTriggers={accountsAndTriggers}
48
+ showNewAccountButton={!konnectorWithTriggers.clientSide}
49
+ showAccountSelection={!konnectorWithTriggers.clientSide}
50
+ onDismiss={onDismiss}
51
+ >
52
+ <AccountModalContentWrapper>
53
+ <DataTab
54
+ konnectorRoot={`${konnectorRoot}/accounts/${match.params.accountId}`}
55
+ konnector={konnectorWithTriggers}
56
+ showNewAccountButton={!konnectorWithTriggers.clientSide}
57
+ onDismiss={onDismiss}
58
+ />
59
+ </AccountModalContentWrapper>
60
+ </AccountModalWithoutTabs>
61
+ )}
43
62
  />
44
- )}
45
- />
63
+ <Route
64
+ path={`${konnectorRoot}/accounts/:accountId/config`}
65
+ exact
66
+ render={({ match }) => (
67
+ <AccountModalWithoutTabs
68
+ konnector={konnectorWithTriggers}
69
+ accountId={match.params.accountId}
70
+ accountsAndTriggers={accountsAndTriggers}
71
+ showNewAccountButton={!konnectorWithTriggers.clientSide}
72
+ showAccountSelection={!konnectorWithTriggers.clientSide}
73
+ onDismiss={onDismiss}
74
+ >
75
+ <AccountModalContentWrapper>
76
+ <ConfigurationTab
77
+ konnector={konnectorWithTriggers}
78
+ showNewAccountButton={!konnectorWithTriggers.clientSide}
79
+ onAccountDeleted={onDismiss}
80
+ addAccount={() =>
81
+ historyAction(`${konnectorRoot}/new`, 'replace')
82
+ }
83
+ />
84
+ </AccountModalContentWrapper>
85
+ </AccountModalWithoutTabs>
86
+ )}
87
+ />
88
+ </>
89
+ ) : (
90
+ <Route
91
+ path={`${konnectorRoot}/accounts/:accountId`}
92
+ exact
93
+ render={({ match }) => (
94
+ <AccountModal
95
+ konnector={konnectorWithTriggers}
96
+ accountId={match.params.accountId}
97
+ accountsAndTriggers={accountsAndTriggers}
98
+ onDismiss={onDismiss}
99
+ showNewAccountButton={!konnectorWithTriggers.clientSide}
100
+ showAccountSelection={!konnectorWithTriggers.clientSide}
101
+ />
102
+ )}
103
+ />
104
+ )}
105
+
46
106
  <Route
47
107
  path={`${konnectorRoot}/accounts/:accountId/edit`}
48
108
  exact
@@ -91,4 +151,4 @@ const RoutesV4 = ({
91
151
  )
92
152
  }
93
153
 
94
- export default RoutesV4
154
+ export default withAdaptiveRouter(RoutesV4)
@@ -1,5 +1,11 @@
1
1
  import React from 'react'
2
- import { Routes, Route, Navigate, useParams } from 'react-router-dom'
2
+ import {
3
+ Routes,
4
+ Route,
5
+ Navigate,
6
+ useParams,
7
+ useNavigate
8
+ } from 'react-router-dom'
3
9
 
4
10
  import flag from 'cozy-flags'
5
11
 
@@ -25,6 +31,8 @@ const RoutesV6 = ({
25
31
  onSuccess,
26
32
  onDismiss
27
33
  }) => {
34
+ const navigate = useNavigate()
35
+
28
36
  return (
29
37
  <Routes>
30
38
  <Route
@@ -85,7 +93,8 @@ const RoutesV6 = ({
85
93
  <ConfigurationTab
86
94
  konnector={konnectorWithTriggers}
87
95
  showNewAccountButton={!konnectorWithTriggers.clientSide}
88
- onDismiss={onDismiss}
96
+ onAccountDeleted={onDismiss}
97
+ addAccount={() => navigate('new', { replace: true })}
89
98
  />
90
99
  </AccountModalContentWrapper>
91
100
  }
@@ -1,4 +1,5 @@
1
1
  import React, { useRef, useState, useEffect } from 'react'
2
+ import PropTypes from 'prop-types'
2
3
 
3
4
  import ActionMenu, { ActionMenuItem } from 'cozy-ui/transpiled/react/ActionMenu'
4
5
  import Alert from 'cozy-ui/transpiled/react/Alert'
@@ -10,15 +11,30 @@ import Spinner from 'cozy-ui/transpiled/react/Spinner'
10
11
  import Snackbar from 'cozy-ui/transpiled/react/Snackbar'
11
12
  import DotsIcon from 'cozy-ui/transpiled/react/Icons/Dots'
12
13
  import SyncIcon from 'cozy-ui/transpiled/react/Icons/Sync'
14
+ import GearIcon from 'cozy-ui/transpiled/react/Icons/Gear'
13
15
 
14
16
  import { getLastSuccessDate, getKonnectorSlug } from '../../helpers/triggers'
15
17
  import { isRunnable } from '../../helpers/konnectors'
16
18
  import { useFlowState } from '../../models/withConnectionFlow'
17
19
  import { SUCCESS } from '../../models/flowEvents'
20
+ import withAdaptiveRouter from '../hoc/withRouter'
18
21
  import KonnectorIcon from '../KonnectorIcon'
19
22
  import { makeLabel } from './helpers'
20
23
 
21
- export const LaunchTriggerAlert = ({ flow, f, t, disabled }) => {
24
+ const styles = {
25
+ // tricks to put 48px wide button inside 32px height container
26
+ // without enlarging the container
27
+ iconButtonWrapper: { height: 32, width: 46 }
28
+ }
29
+
30
+ export const LaunchTriggerAlert = ({
31
+ flow,
32
+ f,
33
+ t,
34
+ disabled,
35
+ konnectorRoot,
36
+ historyAction
37
+ }) => {
22
38
  const [showSuccessSnackbar, setShowSuccessSnackbar] = useState(false)
23
39
  const { trigger, running, expectingTriggerLaunch, status } =
24
40
  useFlowState(flow)
@@ -38,6 +54,7 @@ export const LaunchTriggerAlert = ({ flow, f, t, disabled }) => {
38
54
  return (
39
55
  <>
40
56
  <Alert
57
+ className="u-pr-half"
41
58
  color="var(--paperBackground)"
42
59
  icon={
43
60
  running ? (
@@ -59,13 +76,17 @@ export const LaunchTriggerAlert = ({ flow, f, t, disabled }) => {
59
76
  label={t('card.launchTrigger.button.label')}
60
77
  onClick={() => launch({ autoSuccessTimer: false })}
61
78
  />
62
- <IconButton
63
- ref={anchorRef}
64
- onClick={() => setShowOptions(true)}
65
- className="u-p-half"
79
+ <div
80
+ className="u-flex u-flex-items-center"
81
+ style={styles.iconButtonWrapper}
66
82
  >
67
- <Icon icon={DotsIcon} />
68
- </IconButton>
83
+ <IconButton
84
+ ref={anchorRef}
85
+ onClick={() => setShowOptions(true)}
86
+ >
87
+ <Icon icon={DotsIcon} />
88
+ </IconButton>
89
+ </div>
69
90
  {showOptions && (
70
91
  <ActionMenu
71
92
  anchorElRef={anchorRef}
@@ -83,6 +104,14 @@ export const LaunchTriggerAlert = ({ flow, f, t, disabled }) => {
83
104
  {t('card.launchTrigger.button.label')}
84
105
  </ActionMenuItem>
85
106
  )}
107
+ <ActionMenuItem
108
+ left={<Icon icon={GearIcon} />}
109
+ onClick={() =>
110
+ historyAction(`${konnectorRoot}/config`, 'push')
111
+ }
112
+ >
113
+ {t('card.launchTrigger.configure')}
114
+ </ActionMenuItem>
86
115
  </ActionMenu>
87
116
  )}
88
117
  </>
@@ -116,4 +145,17 @@ export const LaunchTriggerAlert = ({ flow, f, t, disabled }) => {
116
145
  )
117
146
  }
118
147
 
119
- export default LaunchTriggerAlert
148
+ LaunchTriggerAlert.defaultProps = {
149
+ konnectorRoot: ''
150
+ }
151
+
152
+ LaunchTriggerAlert.propTypes = {
153
+ flow: PropTypes.object,
154
+ f: PropTypes.function,
155
+ t: PropTypes.function,
156
+ disabled: PropTypes.bool,
157
+ konnectorRoot: PropTypes.string,
158
+ historyAction: PropTypes.function
159
+ }
160
+
161
+ export default withAdaptiveRouter(LaunchTriggerAlert)
@@ -16,8 +16,9 @@ export const historyAction = (props, historyOrNavigate) => (route, method) => {
16
16
  return historyOrNavigate(path, { replace: method === 'replace' })
17
17
  }
18
18
 
19
+ const splitBaseRoute = baseRoute?.split('/') || []
19
20
  const segments = '/'.concat(
20
- baseRoute.split('/').concat(route.split('/')).filter(Boolean).join('/')
21
+ splitBaseRoute.concat(route.split('/')).filter(Boolean).join('/')
21
22
  )
22
23
 
23
24
  return historyOrNavigate[method](segments)
@@ -48,4 +48,10 @@ describe('historyAction', () => {
48
48
  expect(historyMock.replace).toHaveBeenCalledWith('/base/route/one/two')
49
49
  historyMock.replace.mockReset()
50
50
  })
51
+
52
+ it('should handle even if no base route', () => {
53
+ setup({ baseRoute: undefined, route: '/one' })
54
+ expect(historyMock.replace).toHaveBeenCalledWith('/one')
55
+ historyMock.replace.mockReset()
56
+ })
51
57
  })
@@ -77,6 +77,7 @@
77
77
  "button": {
78
78
  "label": "Run again now"
79
79
  },
80
+ "configure": "Configure",
80
81
  "success": "Successful synchronization",
81
82
  "error": "An error occured.",
82
83
  "frequency": {
@@ -92,6 +93,7 @@
92
93
  "syncing": "Data recovery…",
93
94
  "unknown": "Unknown",
94
95
  "afterSomeTimes": "Sync. %{times} ago",
96
+ "format": "MM/DD/YYYY",
95
97
  "justNow": "Sync. just now"
96
98
  }
97
99
  },
@@ -77,6 +77,7 @@
77
77
  "button": {
78
78
  "label": "Mettre à jour"
79
79
  },
80
+ "configure": "Configurer",
80
81
  "success": "Synchronisation réussie",
81
82
  "error": "Une erreur est survenue.",
82
83
  "frequency": {
@@ -92,6 +93,7 @@
92
93
  "syncing": "Récupération des données…",
93
94
  "unknown": "Indéterminée",
94
95
  "afterSomeTimes": "Sync. il y a %{times}",
96
+ "format": "DD/MM/YYYY",
95
97
  "justNow": "Sync. à l'instant"
96
98
  }
97
99
  },