@rh-support/cases 1.0.41-beta.49 → 1.0.41-beta.58

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.
@@ -1 +1 @@
1
- {"version":3,"file":"AccountsBookmarkedGroupFilter.d.ts","sourceRoot":"","sources":["../../../../../src/components/case-list/case-list-filters/AccountsBookmarkedGroupFilter.tsx"],"names":[],"mappings":"AAmBA,eAAO,MAAM,YAAY,eAAgB,MAAM,gBAS9C,CAAC;AAEF,UAAU,MAAM;IACZ,uBAAuB,EAAE,OAAO,CAAC;IACjC,wBAAwB,EAAE,OAAO,CAAC;IAClC,yBAAyB,EAAE,OAAO,CAAC;CACtC;AAED,eAAO,MAAM,QAAQ,cAAe,MAAM,eAAe,MAAM,WACiB,CAAC;AAEjF,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,MAAM,eAkH1D"}
1
+ {"version":3,"file":"AccountsBookmarkedGroupFilter.d.ts","sourceRoot":"","sources":["../../../../../src/components/case-list/case-list-filters/AccountsBookmarkedGroupFilter.tsx"],"names":[],"mappings":"AAcA,eAAO,MAAM,YAAY,eAAgB,MAAM,gBAS9C,CAAC;AAEF,UAAU,MAAM;IACZ,uBAAuB,EAAE,OAAO,CAAC;IACjC,wBAAwB,EAAE,OAAO,CAAC;IAClC,yBAAyB,EAAE,OAAO,CAAC;CACtC;AAOD,eAAO,MAAM,QAAQ,cAAe,MAAM,eAAe,MAAM,WACiB,CAAC;AAEjF,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,MAAM,eAgJ1D"}
@@ -1,15 +1,11 @@
1
- import { Label, Tooltip } from '@patternfly/react-core';
1
+ import { Label, Select, SelectOption, SelectVariant, Tooltip } from '@patternfly/react-core';
2
2
  import InfoCircleIcon from '@patternfly/react-icons/dist/js/icons/info-circle-icon';
3
- import { MultiSelectDropDownList } from '@rh-support/components';
4
3
  import { GlobalMetadataStateContext } from '@rh-support/react-context';
5
- import { getDropdownBtnPlaceholder, toOptions } from '@rh-support/utils';
6
- import groupBy from 'lodash/groupBy';
7
- import isEmpty from 'lodash/isEmpty';
8
- import sortBy from 'lodash/sortBy';
9
- import React, { useContext } from 'react';
4
+ import { getDropdownBtnPlaceholder } from '@rh-support/utils';
5
+ import { groupBy, isEmpty, isEqual, some, sortBy } from 'lodash';
6
+ import React, { useContext, useMemo, useState } from 'react';
10
7
  import { Trans, useTranslation } from 'react-i18next';
11
8
  import { Link } from 'react-router-dom';
12
- import { FILTER_SEARCHABLE_MODE_LIMIT } from '../../../enums/caseSearch';
13
9
  import { SolrKeys } from '../../../enums/filters';
14
10
  import { filterNamesMap } from '../../../utils/constants';
15
11
  import { CaseListFilterDispatchContext, CaseListFilterStateContext } from '../CaseListFilterContext';
@@ -27,27 +23,22 @@ export function AccountsBookmarkedGroupFilter(props) {
27
23
  const { globalMetadataState: { managedAccounts, bookmarkedGroupAccounts }, } = useContext(GlobalMetadataStateContext);
28
24
  const { filterInfo } = useContext(CaseListFilterStateContext);
29
25
  const dispatch = useContext(CaseListFilterDispatchContext);
30
- const onAccountsFilterChange = (selectedAccounts) => {
31
- updateFilter(dispatch, {
32
- filterKey: SolrKeys.accountNumber,
33
- values: selectedAccounts.map((i) => ({ value: i.value.value, key: i.value.key })),
34
- });
35
- };
36
- const managedAccountsList = !isEmpty(managedAccounts.data) ? managedAccounts.data : [];
37
- const getBookmarkDropdownOptions = (accounts) => {
38
- return accounts.map((account) => ({
39
- children: React.createElement(React.Fragment, null,
40
- " ",
41
- getLabel(account.accountNumber || account.accountNum, account.name)),
42
- value: getLabel(account.accountNumber || account.accountNum, account.name),
43
- key: account.accountNumber || account.accountNum,
44
- isNotSearchable: false,
45
- isNonActionable: false,
46
- isNotSelectable: false,
47
- }));
48
- };
49
- const filterValues = canAccessManagedAccounts ? getBookmarkDropdownOptions(managedAccountsList) : [];
50
- const groupedBookmarks = groupBy(bookmarkedGroupAccounts.data.filter((item) => item.isBookmarkedAndHasGroup), 'bookmarkGroupName');
26
+ const [isOpen, setIsOpen] = useState(false);
27
+ const managedAccountsList = !isEmpty(managedAccounts.data)
28
+ ? managedAccounts.data.map((account) => ({
29
+ value: getLabel(account.accountNum, account.name),
30
+ key: account.accountNum,
31
+ }))
32
+ : [];
33
+ const groupedBookmarks = useMemo(() => groupBy(bookmarkedGroupAccounts.data.filter((item) => item.isBookmarkedAndHasGroup), 'bookmarkGroupName'), [bookmarkedGroupAccounts.data]);
34
+ const groupedBookmarksOptions = Object.keys(groupedBookmarks).map((groupName) => ({
35
+ value: groupName,
36
+ key: groupedBookmarks[groupName].map((item) => item.accountNumber),
37
+ }));
38
+ const groupedBookmarksOptionsSorted = sortBy(groupedBookmarksOptions, (g) => g.value);
39
+ const dropdownOptions = [...groupedBookmarksOptionsSorted, ...managedAccountsList];
40
+ const selectedBookmarks = filterInfo[SolrKeys.accountNumber].filter((item) => Array.isArray(item.key));
41
+ const selectedAccounts = filterInfo[SolrKeys.accountNumber].filter((item) => !Array.isArray(item.key));
51
42
  const getInfoToolTip = (groupName) => {
52
43
  const list = groupedBookmarks[groupName];
53
44
  return (React.createElement("ul", { className: "no-list-style pf-u-pl-0" }, list.map((b) => {
@@ -58,39 +49,50 @@ export function AccountsBookmarkedGroupFilter(props) {
58
49
  ")"));
59
50
  })));
60
51
  };
61
- const groupAccountsArrObj = Object.keys(groupedBookmarks).map((groupName) => ({
62
- children: React.createElement(React.Fragment, null, groupName),
63
- actionItem: (React.createElement(Tooltip, { content: getInfoToolTip(groupName) },
64
- React.createElement(Label, { color: "cyan", className: "pf-u-ml-auto pf-u-mr-sm", icon: React.createElement(InfoCircleIcon, null) }, groupedBookmarks[groupName].length))),
65
- value: groupName,
66
- key: groupedBookmarks[groupName].map((item) => item.accountNumber),
67
- isNotSearchable: false,
68
- isNonActionable: false,
69
- isNotSelectable: false,
70
- }));
71
- const groupAccountsArrObjSorted = sortBy(groupAccountsArrObj, (g) => g.value);
72
- const list = [
73
- ...groupAccountsArrObjSorted,
74
- ...filterValues,
75
- {
76
- children: canManageBookmarkAccounts ? appendToMenu((bookmarkedGroupAccounts.data || []).length) : React.createElement(React.Fragment, null),
77
- isNotSearchable: true,
78
- isNonActionable: true,
79
- isNotSelectable: true,
80
- },
81
- ];
52
+ const getBookmarksOptions = (options) => {
53
+ return [
54
+ ...options.map((b, id) => (React.createElement(SelectOption, { key: id, value: b, className: "pf-c-select__menu-wrapper", role: "presentation" },
55
+ React.createElement("div", { className: "pf-c-select__menu-item-main" },
56
+ React.createElement("input", { className: "pf-c-check__input pf-u-mr-sm", type: "checkbox", checked: some(filterInfo[SolrKeys.accountNumber], b) }),
57
+ React.createElement("span", null, b.value)),
58
+ Array.isArray(b.key) && (React.createElement(Tooltip, { content: getInfoToolTip(b.value), "aria-label": t('Bookmarked Accounts List') },
59
+ React.createElement(Label, { color: "cyan", className: "pf-u-ml-auto pf-u-mr-sm", icon: React.createElement(InfoCircleIcon, null), "aria-label": t('Bookmarked Accounts') }, b.key.length)))))),
60
+ ...(canManageBookmarkAccounts ? [appendToMenu((bookmarkedGroupAccounts.data || []).length)] : []),
61
+ ];
62
+ };
63
+ const onBookmarksToggle = (open) => {
64
+ setIsOpen(open);
65
+ };
66
+ const onBookmarksSelect = (e, selection) => {
67
+ let selected = filterInfo[SolrKeys.accountNumber];
68
+ if (some(selected, selection)) {
69
+ selected = selected.filter((b) => !isEqual(b, selection));
70
+ }
71
+ else {
72
+ selected = [...selected, selection];
73
+ }
74
+ updateFilter(dispatch, {
75
+ filterKey: SolrKeys.accountNumber,
76
+ values: selected.map((i) => ({ value: i.value, key: i.key })),
77
+ });
78
+ };
79
+ const onBookmarksClear = () => {
80
+ updateFilter(dispatch, {
81
+ filterKey: SolrKeys.accountNumber,
82
+ values: [],
83
+ });
84
+ };
85
+ const onBookmarksFilter = (_, value) => {
86
+ if (!value)
87
+ return getBookmarksOptions(dropdownOptions);
88
+ const input = new RegExp(value, 'i');
89
+ const newOptions = dropdownOptions.filter((b) => input.test(b.value));
90
+ return getBookmarksOptions(newOptions);
91
+ };
82
92
  if (!canReadBookmarkAccounts && !canAccessManagedAccounts) {
83
93
  return null;
84
94
  }
85
- const listLength = groupAccountsArrObj.length + filterValues.length;
86
- const selectedBookmarks = filterInfo[SolrKeys.accountNumber].filter((item) => Array.isArray(item.key));
87
- const selectedAccounts = filterInfo[SolrKeys.accountNumber].filter((item) => !Array.isArray(item.key));
88
- return (React.createElement(MultiSelectDropDownList, { searchable: listLength > FILTER_SEARCHABLE_MODE_LIMIT, selectedItems: toOptions(filterInfo[SolrKeys.accountNumber], { labelKey: 'value' }), placeholder: getDropdownBtnPlaceholder(t(canAccessManagedAccounts ? 'Search for an account' : 'Search for a bookmark'), canAccessManagedAccounts ? selectedAccounts : selectedBookmarks, '', listLength, canAccessManagedAccounts ? t('All accounts') : t('All bookmarks')), "data-tracking-id": "accounts-filter", id: "case-list-group-filter", title: canAccessManagedAccounts ? t('Accounts') : t(filterNamesMap[SolrKeys.accountNumber]), label: canAccessManagedAccounts ? t('Accounts') : t(filterNamesMap[SolrKeys.accountNumber]), onChange: onAccountsFilterChange, list: toOptions(list, {
89
- labelKey: 'value',
90
- childrenKey: 'children',
91
- actionItemKey: 'actionItem',
92
- nonSearchableItemKey: 'isNotSearchable',
93
- nonActionableItemKey: 'isNonActionable',
94
- nonSelectableItemKey: 'isNotSelectable',
95
- }) }));
95
+ return (React.createElement("div", { className: "pf-c-select" },
96
+ React.createElement("label", { htmlFor: "case-list-group-filter" }, canAccessManagedAccounts ? t('Accounts') : t(filterNamesMap[SolrKeys.accountNumber])),
97
+ React.createElement(Select, { variant: SelectVariant.typeaheadMulti, onToggle: onBookmarksToggle, onSelect: onBookmarksSelect, onClear: onBookmarksClear, isOpen: isOpen, onFilter: onBookmarksFilter, placeholderText: getDropdownBtnPlaceholder(t(canAccessManagedAccounts ? 'Search for an account' : 'Search for a bookmark'), canAccessManagedAccounts ? selectedAccounts : selectedBookmarks, '', dropdownOptions.length, canAccessManagedAccounts ? t('All accounts') : t('All bookmarks')), "data-tracking-id": "accounts-filter", id: "case-list-group-filter" }, getBookmarksOptions(dropdownOptions))));
96
98
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsFilter.d.ts","sourceRoot":"","sources":["../../../../../src/components/case-list/case-list-filters/ProductsFilter.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAe,MAAM,0BAA0B,CAAC;AAUvE,UAAU,MAAM;IACZ,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,WAAW,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,eA0E3C"}
1
+ {"version":3,"file":"ProductsFilter.d.ts","sourceRoot":"","sources":["../../../../../src/components/case-list/case-list-filters/ProductsFilter.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAW1D,UAAU,MAAM;IACZ,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,WAAW,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,eA8G3C"}
@@ -1,6 +1,6 @@
1
- import { Select, SelectOption, SelectVariant } from '@patternfly/react-core';
2
- import { MultiSelectDropDownList } from '@rh-support/components';
3
- import { getDropdownBtnPlaceholder, toOptions } from '@rh-support/utils';
1
+ import { Checkbox, Select, SelectOption, SelectVariant } from '@patternfly/react-core';
2
+ import { getDropdownBtnPlaceholder } from '@rh-support/utils';
3
+ import { isUndefined } from 'lodash';
4
4
  import React, { useContext, useState } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
  import { SolrKeys, SolrPivotKeys } from '../../../enums/filters';
@@ -11,16 +11,17 @@ export function ProductsFilter(props) {
11
11
  const { t } = useTranslation();
12
12
  const dispatch = useContext(CaseListFilterDispatchContext);
13
13
  const { filterInfo } = useContext(CaseListFilterStateContext);
14
+ const { allProducts, filterValues, isSolrSearchDown } = props;
14
15
  const [isOpen, setIsOpen] = useState(false);
15
16
  const toggleIsOpen = (isExpanded) => setIsOpen(isExpanded);
16
- const onFilterChangeMultipleSelect = (selectedProducts) => {
17
- selectedProducts = selectedProducts.map((selectedProduct) => ({
18
- field: SolrKeys.product,
19
- value: selectedProduct.label,
20
- [SolrKeys.version]: [],
21
- }));
22
- updateFilter(dispatch, { filterKey: SolrPivotKeys.product_version, values: selectedProducts });
23
- };
17
+ const selectedProducts = !isUndefined(filterInfo) && !isUndefined(filterInfo[SolrPivotKeys.product_version])
18
+ ? filterInfo[SolrPivotKeys.product_version]
19
+ : [];
20
+ const productOptions = filterValues.map((product) => ({
21
+ field: SolrKeys.product,
22
+ value: product.value,
23
+ [SolrKeys.version]: [],
24
+ }));
24
25
  const onFilterChangeSingleSelect = (event, selectedProduct) => {
25
26
  updateFilter(dispatch, {
26
27
  filterKey: SolrPivotKeys.product_version,
@@ -33,12 +34,36 @@ export function ProductsFilter(props) {
33
34
  // we get filter values from hydra metadata api
34
35
  const singleSelectSolrIsDown = () => {
35
36
  var _a, _b;
36
- return (React.createElement(Select, { variant: SelectVariant.single, "aria-label": "Select Input", onToggle: toggleIsOpen, onSelect: onFilterChangeSingleSelect, selections: (_b = (_a = filterInfo[SolrPivotKeys.product_version]) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.value, isOpen: isOpen, "aria-labelledby": titleId, placeholderText: t('Select a product'), "data-tracking-id": "products-filter" }, props.allProducts.map((product) => (React.createElement(SelectOption, { key: product, value: product })))));
37
+ return (React.createElement(Select, { variant: SelectVariant.single, "aria-label": t('Select Input'), onToggle: toggleIsOpen, onSelect: onFilterChangeSingleSelect, selections: (_b = (_a = filterInfo[SolrPivotKeys.product_version]) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.value, isOpen: isOpen, "aria-labelledby": titleId, placeholderText: t('Select a product'), "data-tracking-id": "products-filter", id: "products-filter" }, allProducts.map((product) => (React.createElement(SelectOption, { key: product, value: product })))));
38
+ };
39
+ const onSelect = (e, selection) => {
40
+ let newSelectedProducts = [];
41
+ if (selectedProducts === null || selectedProducts === void 0 ? void 0 : selectedProducts.some((product) => selection.value === product.value)) {
42
+ newSelectedProducts = selectedProducts.filter((product) => selection.value !== product.value);
43
+ }
44
+ else {
45
+ newSelectedProducts = [
46
+ ...selectedProducts,
47
+ { field: SolrKeys.product, value: selection.value, [SolrKeys.version]: [] },
48
+ ];
49
+ }
50
+ updateFilter(dispatch, { filterKey: SolrPivotKeys.product_version, values: newSelectedProducts });
51
+ };
52
+ const getProductSelectOptions = (options) => {
53
+ return options.map((p, id) => (React.createElement(SelectOption, { key: id, value: p },
54
+ React.createElement("span", null,
55
+ React.createElement(Checkbox, { id: "product-option-check", checked: selectedProducts === null || selectedProducts === void 0 ? void 0 : selectedProducts.some((product) => p.value === product.value) })),
56
+ React.createElement("span", null, p.value))));
57
+ };
58
+ const onFilter = (_, value) => {
59
+ if (!value)
60
+ return getProductSelectOptions(productOptions);
61
+ const input = new RegExp(value, 'i');
62
+ const newOptions = productOptions.filter((product) => input.test(product.value));
63
+ return getProductSelectOptions(newOptions);
37
64
  };
38
- const multiSelect = () => (React.createElement(MultiSelectDropDownList, { placeholder: getDropdownBtnPlaceholder(t('Select a product'), filterInfo[SolrPivotKeys.product_version].map((i) => i.value), '', props.filterValues.length, t('All selected')), "data-tracking-id": "products-filter", id: "case-list-products-filter", title: t(filterNamesMap[SolrKeys.product]), onChange: onFilterChangeMultipleSelect, selectedItems: toOptions(filterInfo[SolrPivotKeys.product_version], { labelKey: 'value' }), list: toOptions(props.filterValues, {
39
- labelKey: 'value',
40
- }), searchable: true }));
65
+ const multiSelectNew = () => (React.createElement(Select, { "aria-label": t('Select Input'), id: titleId, variant: SelectVariant.typeaheadMulti, onToggle: toggleIsOpen, onSelect: onSelect, isOpen: isOpen, placeholderText: getDropdownBtnPlaceholder(t('Select a product'), filterInfo[SolrPivotKeys.product_version].map((i) => i.value), '', props.filterValues.length, t('All selected')), onFilter: onFilter }, getProductSelectOptions(productOptions)));
41
66
  return (React.createElement("div", { className: "pf-c-select" },
42
- React.createElement("label", { id: titleId }, t(filterNamesMap[SolrKeys.product])),
43
- props.isSolrSearchDown ? singleSelectSolrIsDown() : multiSelect()));
67
+ React.createElement("label", { htmlFor: titleId }, t(filterNamesMap[SolrKeys.product])),
68
+ isSolrSearchDown ? singleSelectSolrIsDown() : multiSelectNew()));
44
69
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ExportCaseListCSV.d.ts","sourceRoot":"","sources":["../../../../../src/components/case-list/case-list-table/ExportCaseListCSV.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAMzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAgD7D,UAAU,MAAM;IACZ,uBAAuB,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAC;IAC1D,mBAAmB,CAAC,EAAE,OAAO,CAAC;CACjC;AAID,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,eAkG9C"}
1
+ {"version":3,"file":"ExportCaseListCSV.d.ts","sourceRoot":"","sources":["../../../../../src/components/case-list/case-list-table/ExportCaseListCSV.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAMzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAgD7D,UAAU,MAAM;IACZ,uBAAuB,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAC;IAC1D,mBAAmB,CAAC,EAAE,OAAO,CAAC;CACjC;AAID,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,eAoG9C"}
@@ -73,6 +73,7 @@ export function ExportCaseListCSV(props) {
73
73
  const filterState = useContext(CaseListFilterStateContext);
74
74
  const { globalMetadataState: { loggedInUserRights, loggedInUsersAccount }, } = useContext(GlobalMetadataStateContext);
75
75
  const isOrgAdmin = loggedInUserRights.data.isOrgAdmin();
76
+ // CSV For Secure Support Users
76
77
  const onExportCSVClickSecureSupport = () => __awaiter(this, void 0, void 0, function* () {
77
78
  try {
78
79
  const sfdcFilter = createQueryForCSVDownload(filterState, loggedInUserRights.data, loggedInUsersAccount.data);
@@ -90,6 +91,7 @@ export function ExportCaseListCSV(props) {
90
91
  }
91
92
  });
92
93
  const partnerSearch = loggedInUserRights.data.hasManagedAccounts();
94
+ // CSV For Normal Users
93
95
  const onExportCSVClick = () => __awaiter(this, void 0, void 0, function* () {
94
96
  try {
95
97
  const res = yield request(createQueryForCSVDownload(filterState, loggedInUserRights.data, loggedInUsersAccount.data), partnerSearch, null, loggedInUsersAccount.data.secureSupport);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rh-support/cases",
3
- "version": "1.0.41-beta.49",
3
+ "version": "1.0.41-beta.58",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "registry": "https://registry.npmjs.org"
@@ -51,11 +51,11 @@
51
51
  "@patternfly/pfe-collapse": "1.12.3",
52
52
  "@patternfly/react-core": "4.264.0",
53
53
  "@patternfly/react-table": "4.111.33",
54
- "@rh-support/components": "1.2.22-beta.49",
55
- "@rh-support/react-context": "1.0.30-beta.49",
56
- "@rh-support/types": "0.2.1-beta.33",
57
- "@rh-support/user-permissions": "1.0.12-beta.49",
58
- "@rh-support/utils": "1.0.10-beta.39",
54
+ "@rh-support/components": "1.2.22-beta.58",
55
+ "@rh-support/react-context": "1.0.30-beta.58",
56
+ "@rh-support/types": "0.2.1-beta.58",
57
+ "@rh-support/user-permissions": "1.0.12-beta.58",
58
+ "@rh-support/utils": "1.0.10-beta.58",
59
59
  "i18next": "^19.0.1",
60
60
  "localforage": "^1.7.3",
61
61
  "lodash": "^4.17.21",
@@ -99,5 +99,5 @@
99
99
  "not ie <= 11",
100
100
  "not op_mini all"
101
101
  ],
102
- "gitHead": "0fa0968e831c199572669184c466e9fecd781d12"
102
+ "gitHead": "52af82c690605efacf567d6657ee7c5707fb3fdc"
103
103
  }