studiokit-scaffolding-js 5.0.0 → 5.1.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.
@@ -1,14 +1,14 @@
1
1
  import React, { ComponentClass, ComponentType } from 'react';
2
2
  import { RouteComponentProps } from 'react-router';
3
3
  import { MODEL_STATUS } from '../../constants/modelStatus';
4
- import { BaseReduxState, Model } from '../../types';
5
- import { CollectionCommonProps, CollectionCommonState, CollectionCreateParams, CollectionDeleteParams, CollectionLoadParams, CollectionMethodConfiguration, CollectionMethods, CollectionReduxResponse, CollectionUpdateParams } from '../../types/Collection';
4
+ import { BaseReduxState, Model, ModelCollection } from '../../types';
5
+ import { CollectionCommonProps, CollectionCommonState, CollectionCreateParams, CollectionDeleteParams, CollectionDerivedProps, CollectionLoadParams, CollectionMethodConfiguration, CollectionMethods, CollectionReduxResponse, CollectionUpdateParams } from '../../types/Collection';
6
6
  import { GuidComponentWrappedProps } from './GuidComponent';
7
7
  /** The props passed into `CollectionComponent` from the user and other HOCs. */
8
8
  export interface CollectionComponentProps<TModel extends Model> extends CollectionCommonProps, GuidComponentWrappedProps, RouteComponentProps, CollectionReduxResponse<TModel> {
9
9
  }
10
10
  /** The props passed down to the `WrappedComponent`. */
11
- export interface CollectionComponentWrappedProps<TModel extends Model> extends Omit<CollectionComponentProps<TModel>, keyof RouteComponentProps>, CollectionMethods, CollectionCommonState {
11
+ export interface CollectionComponentWrappedProps<TModel extends Model> extends Omit<CollectionComponentProps<TModel>, keyof RouteComponentProps>, CollectionDerivedProps<TModel>, CollectionMethods, CollectionCommonState {
12
12
  }
13
13
  export declare function configureCollectionComponent<TModel extends Model, TOwnProps extends {}>(WrappedComponent: ComponentType<TOwnProps & CollectionComponentWrappedProps<TModel>>, LoaderComponent?: ComponentType): {
14
14
  new (props: TOwnProps & CollectionComponentProps<TModel>): {
@@ -22,6 +22,7 @@ export declare function configureCollectionComponent<TModel extends Model, TOwnP
22
22
  create: (params: CollectionCreateParams) => void;
23
23
  update: (params: CollectionUpdateParams) => void;
24
24
  delete: (params: CollectionDeleteParams) => void;
25
+ getModelArray: import("memoize-one").MemoizedFn<(model: ModelCollection<TModel>, guid?: string | undefined) => TModel[]>;
25
26
  render(): JSX.Element;
26
27
  context: any;
27
28
  setState<K extends "modelStatus" | "previousModelStatus" | "fetchingId">(state: CollectionCommonState | ((prevState: Readonly<CollectionCommonState>, props: Readonly<TOwnProps & CollectionComponentProps<TModel>>) => CollectionCommonState | Pick<CollectionCommonState, K> | null) | Pick<CollectionCommonState, K> | null, callback?: (() => void) | undefined): void;
@@ -58,6 +58,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
58
58
  };
59
59
  Object.defineProperty(exports, "__esModule", { value: true });
60
60
  exports.configureMapStateToProps = exports.configureCollectionComponent = void 0;
61
+ var memoize_one_1 = __importDefault(require("memoize-one"));
61
62
  var react_1 = __importStar(require("react"));
62
63
  var react_redux_1 = require("react-redux");
63
64
  var react_router_1 = require("react-router");
@@ -110,6 +111,7 @@ function configureCollectionComponent(WrappedComponent, LoaderComponent) {
110
111
  _this.delete = function (params) {
111
112
  collection_1.deleteItemFromCollection(_this.getCollectionMethodConfig(), params);
112
113
  };
114
+ _this.getModelArray = memoize_one_1.default(function (model, guid) { return model_1.getModelArray(model, guid); });
113
115
  _this.state = {
114
116
  // initializing until model is loaded
115
117
  modelStatus: modelStatus_1.MODEL_STATUS.UNINITIALIZED,
@@ -139,7 +141,7 @@ function configureCollectionComponent(WrappedComponent, LoaderComponent) {
139
141
  if (modelStatus === modelStatus_1.MODEL_STATUS.UNINITIALIZED) {
140
142
  return react_1.default.createElement(LoaderComponent, null);
141
143
  }
142
- return (react_1.default.createElement(WrappedComponent, __assign({}, otherProps, { modelStatus: modelStatus, previousModelStatus: previousModelStatus, fetchingId: fetchingId, load: this.load, stopPeriodicLoad: this.stopPeriodicLoad, create: this.create, update: this.update, delete: this.delete })));
144
+ return (react_1.default.createElement(WrappedComponent, __assign({}, otherProps, { modelArray: this.getModelArray(this.props.model, this.props.guid), modelStatus: modelStatus, previousModelStatus: previousModelStatus, fetchingId: fetchingId, load: this.load, stopPeriodicLoad: this.stopPeriodicLoad, create: this.create, update: this.update, delete: this.delete })));
143
145
  };
144
146
  return CollectionComponent;
145
147
  }(react_1.Component));
@@ -1,8 +1,8 @@
1
1
  import React, { ComponentClass, ComponentType } from 'react';
2
- import { CollectionItemReduxResponse, Model } from '../../types';
2
+ import { CollectionItemDerivedProps, CollectionItemReduxResponse, Model, ModelCollection } from '../../types';
3
3
  import { CollectionComponentWrappedProps } from './CollectionComponent';
4
4
  /** The props passed into `WrappedComponent`. */
5
- export interface CollectionFirstItemComponentWrappedProps<TModel extends Model> extends Omit<CollectionComponentWrappedProps<TModel>, 'model'>, CollectionItemReduxResponse<TModel> {
5
+ export interface CollectionFirstItemComponentWrappedProps<TModel extends Model> extends Omit<CollectionComponentWrappedProps<TModel>, 'model'>, CollectionItemReduxResponse<TModel>, CollectionItemDerivedProps<TModel> {
6
6
  }
7
7
  /**
8
8
  * HOC meant to pass the first collection item to the wrapped component as its model.
@@ -10,6 +10,8 @@ export interface CollectionFirstItemComponentWrappedProps<TModel extends Model>
10
10
  */
11
11
  export declare function configureCollectionFirstItemComponent<TModel extends Model, TOwnProps extends {}>(WrappedComponent: ComponentType<TOwnProps & CollectionFirstItemComponentWrappedProps<TModel>>): {
12
12
  new (props: (TOwnProps & CollectionComponentWrappedProps<TModel>) | Readonly<TOwnProps & CollectionComponentWrappedProps<TModel>>): {
13
+ getModelArray: import("memoize-one").MemoizedFn<(model: ModelCollection<TModel>, guid?: string | undefined) => TModel[]>;
14
+ getModelMinusRelations: import("memoize-one").MemoizedFn<(model: TModel) => Partial<TModel>>;
13
15
  getFirstItem: () => TModel;
14
16
  render(): JSX.Element;
15
17
  context: any;
@@ -36,6 +38,8 @@ export declare function configureCollectionFirstItemComponent<TModel extends Mod
36
38
  UNSAFE_componentWillUpdate?(nextProps: Readonly<TOwnProps & CollectionComponentWrappedProps<TModel>>, nextState: Readonly<{}>, nextContext: any): void;
37
39
  };
38
40
  new (props: TOwnProps & CollectionComponentWrappedProps<TModel>, context: any): {
41
+ getModelArray: import("memoize-one").MemoizedFn<(model: ModelCollection<TModel>, guid?: string | undefined) => TModel[]>;
42
+ getModelMinusRelations: import("memoize-one").MemoizedFn<(model: TModel) => Partial<TModel>>;
39
43
  getFirstItem: () => TModel;
40
44
  render(): JSX.Element;
41
45
  context: any;
@@ -49,8 +49,12 @@ var __spreadArrays = (this && this.__spreadArrays) || function () {
49
49
  r[k] = a[j];
50
50
  return r;
51
51
  };
52
+ var __importDefault = (this && this.__importDefault) || function (mod) {
53
+ return (mod && mod.__esModule) ? mod : { "default": mod };
54
+ };
52
55
  Object.defineProperty(exports, "__esModule", { value: true });
53
56
  exports.configureCollectionFirstItemComponent = void 0;
57
+ var memoize_one_1 = __importDefault(require("memoize-one"));
54
58
  var react_1 = __importStar(require("react"));
55
59
  var model_1 = require("../../utils/model");
56
60
  /**
@@ -62,8 +66,11 @@ function configureCollectionFirstItemComponent(WrappedComponent) {
62
66
  __extends(CollectionFirstItemComponent, _super);
63
67
  function CollectionFirstItemComponent() {
64
68
  var _this = _super !== null && _super.apply(this, arguments) || this;
69
+ _this.getModelArray = memoize_one_1.default(function (model, guid) { return model_1.getModelArray(model, guid); });
70
+ _this.getModelMinusRelations = memoize_one_1.default(function (model) { return model_1.getModelMinusRelations(model); });
65
71
  _this.getFirstItem = function () {
66
- var _a = _this.props, model = _a.model, modelArray = _a.modelArray, guid = _a.guid;
72
+ var _a = _this.props, model = _a.model, guid = _a.guid;
73
+ var modelArray = _this.getModelArray(model, guid);
67
74
  var singleItem = modelArray.length > 0
68
75
  ? modelArray[0]
69
76
  : model[guid]
@@ -76,7 +83,7 @@ function configureCollectionFirstItemComponent(WrappedComponent) {
76
83
  CollectionFirstItemComponent.prototype.render = function () {
77
84
  var pathParams = this.props.pathParams;
78
85
  var firstItem = this.getFirstItem();
79
- var modelMinusRelations = model_1.getModelMinusRelations(firstItem);
86
+ var modelMinusRelations = this.getModelMinusRelations(firstItem);
80
87
  var p = __spreadArrays(pathParams);
81
88
  if (firstItem.id) {
82
89
  p.push(firstItem.id.toString());
@@ -1,13 +1,13 @@
1
1
  import React, { ComponentClass, ComponentType } from 'react';
2
2
  import { RouteComponentProps } from 'react-router';
3
3
  import { MODEL_STATUS } from '../../constants/modelStatus';
4
- import { BaseReduxState, CollectionCommonProps, CollectionCommonState, CollectionCreateParams, CollectionItemDeleteParams, CollectionItemLoadParams, CollectionItemMethods, CollectionItemReduxResponse, CollectionItemUpdateParams, CollectionMethodConfiguration, Model } from '../../types';
4
+ import { BaseReduxState, CollectionCommonProps, CollectionCommonState, CollectionCreateParams, CollectionItemDeleteParams, CollectionItemDerivedProps, CollectionItemLoadParams, CollectionItemMethods, CollectionItemReduxResponse, CollectionItemUpdateParams, CollectionMethodConfiguration, Model } from '../../types';
5
5
  import { GuidComponentWrappedProps } from './GuidComponent';
6
6
  /** The props passed into `CollectionItemComponent` from the user and other HOCs. */
7
7
  export interface CollectionItemComponentProps<TModel extends Model> extends CollectionCommonProps, GuidComponentWrappedProps, RouteComponentProps, CollectionItemReduxResponse<TModel> {
8
8
  }
9
9
  /** The props passed down to the `WrappedComponent`. */
10
- export interface CollectionItemComponentWrappedProps<TModel extends Model> extends Omit<CollectionItemComponentProps<TModel>, keyof RouteComponentProps>, CollectionItemMethods, CollectionCommonState {
10
+ export interface CollectionItemComponentWrappedProps<TModel extends Model> extends Omit<CollectionItemComponentProps<TModel>, keyof RouteComponentProps>, CollectionItemDerivedProps<TModel>, CollectionItemMethods, CollectionCommonState {
11
11
  }
12
12
  export declare function configureCollectionItemComponent<TModel extends Model, TOwnProps extends {}>(WrappedComponent: ComponentType<TOwnProps & CollectionItemComponentWrappedProps<TModel>>, LoaderComponent?: ComponentType): {
13
13
  new (props: TOwnProps & CollectionItemComponentProps<TModel>): {
@@ -20,6 +20,7 @@ export declare function configureCollectionItemComponent<TModel extends Model, T
20
20
  create: (params: CollectionCreateParams) => void;
21
21
  update: (params: CollectionItemUpdateParams) => void;
22
22
  delete: (params?: CollectionItemDeleteParams) => void;
23
+ getModelMinusRelations: import("memoize-one").MemoizedFn<(model: TModel) => Partial<TModel>>;
23
24
  render(): JSX.Element;
24
25
  context: any;
25
26
  setState<K extends "modelStatus" | "previousModelStatus" | "fetchingId">(state: CollectionCommonState | ((prevState: Readonly<CollectionCommonState>, props: Readonly<TOwnProps & CollectionItemComponentProps<TModel>>) => CollectionCommonState | Pick<CollectionCommonState, K> | null) | Pick<CollectionCommonState, K> | null, callback?: (() => void) | undefined): void;
@@ -58,6 +58,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
58
58
  };
59
59
  Object.defineProperty(exports, "__esModule", { value: true });
60
60
  exports.configureMapStateToProps = exports.configureCollectionItemComponent = void 0;
61
+ var memoize_one_1 = __importDefault(require("memoize-one"));
61
62
  var react_1 = __importStar(require("react"));
62
63
  var react_redux_1 = require("react-redux");
63
64
  var react_router_1 = require("react-router");
@@ -113,6 +114,7 @@ function configureCollectionItemComponent(WrappedComponent, LoaderComponent) {
113
114
  var model = _this.props.model;
114
115
  collection_1.deleteCollectionItem(model, _this.getCollectionMethodConfig(), params);
115
116
  };
117
+ _this.getModelMinusRelations = memoize_one_1.default(function (model) { return model_1.getModelMinusRelations(model); });
116
118
  _this.state = {
117
119
  // initializing until model is loaded, or if no model
118
120
  modelStatus: modelStatus_1.MODEL_STATUS.UNINITIALIZED,
@@ -136,7 +138,7 @@ function configureCollectionItemComponent(WrappedComponent, LoaderComponent) {
136
138
  if (modelStatus === modelStatus_1.MODEL_STATUS.UNINITIALIZED) {
137
139
  return react_1.default.createElement(LoaderComponent, null);
138
140
  }
139
- return (react_1.default.createElement(WrappedComponent, __assign({}, otherProps, { model: this.props.model, modelMinusRelations: this.props.modelMinusRelations, modelStatus: modelStatus, previousModelStatus: previousModelStatus, load: this.load, stopPeriodicLoad: this.stopPeriodicLoad, create: this.create, update: this.update, delete: this.delete })));
141
+ return (react_1.default.createElement(WrappedComponent, __assign({}, otherProps, { model: this.props.model, modelMinusRelations: this.getModelMinusRelations(this.props.model), modelStatus: modelStatus, previousModelStatus: previousModelStatus, load: this.load, stopPeriodicLoad: this.stopPeriodicLoad, create: this.create, update: this.update, delete: this.delete })));
140
142
  };
141
143
  return CollectionItemComponent;
142
144
  }(react_1.Component));
@@ -1,4 +1,5 @@
1
1
  import { ComponentClass, ComponentType } from 'react';
2
+ import { SortingRule } from 'react-table';
2
3
  import { Dispatch } from 'redux';
3
4
  import { BaseReduxState, Search } from '../../types';
4
5
  export interface SearchPersistorProps<T extends Search = Search> {
@@ -7,13 +8,16 @@ export interface SearchPersistorProps<T extends Search = Search> {
7
8
  }
8
9
  export interface SearchPersistorMethods<T extends Search = Search> {
9
10
  setSearchDefaults: (doSearch: () => void, search: T) => void;
10
- updateAndPersistSearch: (search?: Search | T, callback?: () => void) => void;
11
+ updateAndPersistSearch: (search?: T, callback?: () => void) => void;
12
+ resetSearch: () => void;
11
13
  handleSearchClick: () => void;
12
- handleChangeTab: (selectedTab: number) => void;
13
- handleKeywordsChange: (event: any) => void;
14
+ setKeywords: (event: any) => void;
14
15
  handleKeywordsKeyDown: (event: any) => void;
15
- handleQueryAllChange: (event: any) => void;
16
- resetSearch: () => void;
16
+ setQueryAll: (event: any) => void;
17
+ setSelectedTab: (selectedTab: number) => void;
18
+ setSortingRules: (newSortingRules: SortingRule[]) => void;
19
+ setPageSize: (newPageSize: number, newPage: number) => void;
20
+ setPage: (newPage: number) => void;
17
21
  }
18
22
  export interface SearchPersistorWrappedProps<T extends Search = Search> extends SearchPersistorProps<T>, SearchPersistorMethods<T> {
19
23
  search?: T;
@@ -44,6 +44,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
44
44
  };
45
45
  Object.defineProperty(exports, "__esModule", { value: true });
46
46
  exports.configureMapDispatchToProps = exports.configureMapStateToProps = exports.configureSearchPersistorComponent = void 0;
47
+ var lodash_1 = require("lodash");
47
48
  var react_1 = __importStar(require("react"));
48
49
  var react_redux_1 = require("react-redux");
49
50
  var actions_1 = require("../../redux/actions");
@@ -93,28 +94,72 @@ function configureSearchPersistorComponent(WrappedComponent) {
93
94
  invalidKeywords: false
94
95
  }, _this.doSearch);
95
96
  };
96
- _this.handleChangeTab = function (selectedTab) {
97
- _this.updateAndPersistSearch({ selectedTab: selectedTab });
97
+ _this.resetSearch = function () {
98
+ var _a;
99
+ var search = (_a = _this.state.search) !== null && _a !== void 0 ? _a : {};
100
+ _this.updateAndPersistSearch(
101
+ // preserve items related to filtering results
102
+ lodash_1.merge({}, _this.state.defaultSearch, {
103
+ selectedTab: search.selectedTab,
104
+ sortingRules: search.sortingRules,
105
+ pageSize: search.pageSize,
106
+ pageByTab: search.pageByTab
107
+ }),
108
+ // do not trigger a re-load unless queryAll is true
109
+ search.queryAll ? _this.doSearch : undefined);
110
+ };
111
+ _this.setSelectedTab = function (selectedTab) {
112
+ var _a, _b;
113
+ var pageByTab = __assign({}, ((_b = (_a = _this.state.search) === null || _a === void 0 ? void 0 : _a.pageByTab) !== null && _b !== void 0 ? _b : {}));
114
+ // if not set, default page to zero when switching tabs
115
+ if (!pageByTab[selectedTab])
116
+ pageByTab[selectedTab] = 0;
117
+ _this.updateAndPersistSearch({
118
+ selectedTab: selectedTab,
119
+ pageByTab: pageByTab
120
+ });
98
121
  };
99
- _this.handleKeywordsChange = function (event) {
122
+ _this.setKeywords = function (event) {
100
123
  _this.updateAndPersistSearch({
101
124
  keywords: event.target.value
102
125
  });
103
126
  };
104
127
  _this.handleKeywordsKeyDown = function (event) {
105
- if (event.key === 'Enter') {
128
+ var _a;
129
+ if (event.key === 'Enter' && ((_a = _this.state.search) === null || _a === void 0 ? void 0 : _a.queryAll)) {
106
130
  _this.handleSearchClick();
107
131
  }
108
132
  };
109
- _this.handleQueryAllChange = function (event) {
133
+ _this.setQueryAll = function (event) {
110
134
  var queryAll = event.target.checked;
111
135
  _this.updateAndPersistSearch({
112
136
  queryAll: queryAll,
113
137
  requiredMessage: !queryAll ? null : _this.state.search ? _this.state.search.requiredMessage : null
138
+ }, _this.doSearch);
139
+ };
140
+ _this.setSortingRules = function (newSortingRules) {
141
+ _this.updateAndPersistSearch({
142
+ sortingRules: newSortingRules
114
143
  });
115
144
  };
116
- _this.resetSearch = function () {
117
- _this.updateAndPersistSearch(_this.state.defaultSearch, _this.doSearch);
145
+ _this.setPageSize = function (newPageSize, newPage) {
146
+ var _a, _b, _c, _d;
147
+ var pageByTab = __assign({}, ((_b = (_a = _this.state.search) === null || _a === void 0 ? void 0 : _a.pageByTab) !== null && _b !== void 0 ? _b : {}));
148
+ var selectedTab = (_d = (_c = _this.state.search) === null || _c === void 0 ? void 0 : _c.selectedTab) !== null && _d !== void 0 ? _d : 1;
149
+ pageByTab[selectedTab] = newPage;
150
+ _this.updateAndPersistSearch({
151
+ pageSize: newPageSize,
152
+ pageByTab: pageByTab
153
+ });
154
+ };
155
+ _this.setPage = function (newPage) {
156
+ var _a, _b, _c, _d;
157
+ var pageByTab = __assign({}, ((_b = (_a = _this.state.search) === null || _a === void 0 ? void 0 : _a.pageByTab) !== null && _b !== void 0 ? _b : {}));
158
+ var selectedTab = (_d = (_c = _this.state.search) === null || _c === void 0 ? void 0 : _c.selectedTab) !== null && _d !== void 0 ? _d : 1;
159
+ pageByTab[selectedTab] = newPage;
160
+ _this.updateAndPersistSearch({
161
+ pageByTab: pageByTab
162
+ });
118
163
  };
119
164
  _this.state = {
120
165
  search: undefined,
@@ -130,7 +175,7 @@ function configureSearchPersistorComponent(WrappedComponent) {
130
175
  };
131
176
  //#endregion handlers
132
177
  SearchPersistorComponent.prototype.render = function () {
133
- return (react_1.default.createElement(WrappedComponent, __assign({ updateAndPersistSearch: this.updateAndPersistSearch, setSearchDefaults: this.setSearchDefaults, search: this.state.search, handleSearchClick: this.handleSearchClick, handleChangeTab: this.handleChangeTab, handleKeywordsChange: this.handleKeywordsChange, handleKeywordsKeyDown: this.handleKeywordsKeyDown, handleQueryAllChange: this.handleQueryAllChange, resetSearch: this.resetSearch }, this.props)));
178
+ return (react_1.default.createElement(WrappedComponent, __assign({ search: this.state.search, setSearchDefaults: this.setSearchDefaults, updateAndPersistSearch: this.updateAndPersistSearch, resetSearch: this.resetSearch, handleSearchClick: this.handleSearchClick, setKeywords: this.setKeywords, handleKeywordsKeyDown: this.handleKeywordsKeyDown, setQueryAll: this.setQueryAll, setSelectedTab: this.setSelectedTab, setSortingRules: this.setSortingRules, setPageSize: this.setPageSize, setPage: this.setPage }, this.props)));
134
179
  };
135
180
  return SearchPersistorComponent;
136
181
  }(react_1.Component));
@@ -0,0 +1,19 @@
1
+ /// <reference types="react" />
2
+ import { Column, SortingRule } from 'react-table';
3
+ import { Model, Search } from '../types';
4
+ export interface ManageTableProps<TModel, TSearchDataParams> {
5
+ search: Search;
6
+ isFiltered: boolean;
7
+ setSelectedTab: (selectedTab: number) => void;
8
+ setSortingRules: (newSortingRules: SortingRule[]) => void;
9
+ setPageSize: (newPageSize: number, newPage: number) => void;
10
+ setPage: (newPage: number) => void;
11
+ tabs: number[];
12
+ getTabName: (tab?: number) => string;
13
+ searchData: (params: TSearchDataParams) => Record<number, TModel[]>;
14
+ searchDataParams: TSearchDataParams;
15
+ columns: Column<TModel>[];
16
+ entityName: string;
17
+ canQueryAll?: boolean;
18
+ }
19
+ export declare const ManageTable: <TModel extends Model, TSearchDataParams extends {}>({ search: { hasSearched, queryAll, selectedTab, sortingRules, pageSize, pageByTab }, isFiltered, setSelectedTab, setSortingRules, setPageSize, setPage, tabs, getTabName, searchData, searchDataParams, columns, entityName, canQueryAll }: ManageTableProps<TModel, TSearchDataParams>) => JSX.Element;
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ManageTable = void 0;
7
+ var lodash_1 = require("lodash");
8
+ var react_1 = require("react");
9
+ var react_2 = __importDefault(require("react"));
10
+ var react_bootstrap_1 = require("react-bootstrap");
11
+ var react_table_1 = __importDefault(require("react-table"));
12
+ var table_1 = require("../constants/table");
13
+ var RefreshIndicator_1 = require("./RefreshIndicator");
14
+ var ManageTable = function (_a) {
15
+ //#region Data
16
+ var _b = _a.search, hasSearched = _b.hasSearched, queryAll = _b.queryAll, selectedTab = _b.selectedTab, sortingRules = _b.sortingRules, pageSize = _b.pageSize, pageByTab = _b.pageByTab, isFiltered = _a.isFiltered, setSelectedTab = _a.setSelectedTab, setSortingRules = _a.setSortingRules, setPageSize = _a.setPageSize, setPage = _a.setPage, tabs = _a.tabs, getTabName = _a.getTabName, searchData = _a.searchData, searchDataParams = _a.searchDataParams, columns = _a.columns, entityName = _a.entityName, canQueryAll = _a.canQueryAll;
17
+ var _c = react_1.useState(), dataByTab = _c[0], setDataByTab = _c[1];
18
+ var isInitialized = dataByTab !== undefined;
19
+ // search and set state, which can be debounced as a single unit of work
20
+ var searchAndSetData = react_1.useCallback(function (params, setDataByTab) {
21
+ setDataByTab(searchData(params));
22
+ }, [searchData]);
23
+ // search is debounced to prevent it being rapidly called
24
+ // useMemo with no dependencies ensures we keep the same debounced function between renders
25
+ var debouncedSearchAndSetData = react_1.useMemo(function () { return lodash_1.debounce(searchAndSetData); }, [searchAndSetData]);
26
+ // search once without debounce to initialize dataByTab state
27
+ react_1.useEffect(function () {
28
+ if (!isInitialized) {
29
+ setDataByTab(searchData(searchDataParams));
30
+ }
31
+ }, [isInitialized, searchData, searchDataParams]);
32
+ // search using debounced method when searchDataParams changes, after initialization
33
+ // use `isMounted` to not call the callback unless the component is mounted
34
+ react_1.useEffect(function () {
35
+ var isMounted = true;
36
+ if (isInitialized) {
37
+ debouncedSearchAndSetData(searchDataParams, function (dataByTab) {
38
+ if (isMounted)
39
+ setDataByTab(dataByTab);
40
+ });
41
+ }
42
+ return function () {
43
+ isMounted = false;
44
+ };
45
+ }, [debouncedSearchAndSetData, isInitialized, searchDataParams]);
46
+ //#endregion Data
47
+ var noResultsFound = isFiltered && !!dataByTab && Object.values(dataByTab).every(function (d) { return d.length === 0; });
48
+ var currentTab = selectedTab !== null && selectedTab !== void 0 ? selectedTab : 1;
49
+ var data = dataByTab === null || dataByTab === void 0 ? void 0 : dataByTab[currentTab];
50
+ //#region Tabs
51
+ var onTabSelect = react_1.useCallback(function (eventKey) {
52
+ setSelectedTab(Number(eventKey));
53
+ }, [setSelectedTab]);
54
+ var dataLengthByTab = react_1.useMemo(function () {
55
+ var value = {};
56
+ tabs.forEach(function (tab) {
57
+ var _a;
58
+ value[tab] = (_a = dataByTab === null || dataByTab === void 0 ? void 0 : dataByTab[tab].length) !== null && _a !== void 0 ? _a : 0;
59
+ });
60
+ return value;
61
+ }, [dataByTab, tabs]);
62
+ var SearchTabs = react_1.useMemo(function () { return (react_2.default.createElement(react_bootstrap_1.Tabs, { activeKey: selectedTab, id: "search-tabs", onSelect: onTabSelect }, tabs.map(function (tab) { return (react_2.default.createElement(react_bootstrap_1.Tab, { key: tab, eventKey: tab.toString(), title: getTabName(tab) + " (" + dataLengthByTab[tab] + ")", disabled: noResultsFound })); }))); }, [dataLengthByTab, getTabName, noResultsFound, onTabSelect, selectedTab, tabs]);
63
+ //#endregion Tabs
64
+ //#region Table
65
+ var NoDataComponent = react_1.useMemo(function () {
66
+ return hasSearched ? (react_2.default.createElement("p", { className: "pt4 tc" },
67
+ "No ",
68
+ entityName.toLocaleLowerCase(),
69
+ " matched your search.",
70
+ canQueryAll && !queryAll && (react_2.default.createElement(react_2.default.Fragment, null,
71
+ " You might want to try again with \"Search All ",
72
+ entityName,
73
+ "\" checked.")))) : (react_2.default.createElement("p", { className: "pt4 tc" },
74
+ "No ",
75
+ getTabName(selectedTab).toLowerCase(),
76
+ " ",
77
+ entityName.toLocaleLowerCase(),
78
+ ' ',
79
+ isFiltered ? 'matched your search' : 'found',
80
+ "."));
81
+ }, [canQueryAll, entityName, getTabName, hasSearched, isFiltered, queryAll, selectedTab]);
82
+ var NoDataComponentCallback = react_1.useCallback(function () { return NoDataComponent; }, [NoDataComponent]);
83
+ var Table = react_1.useMemo(function () {
84
+ var _a;
85
+ var showPagination = !!data && data.length > table_1.TABLE_DEFAULT_PAGE_SIZE;
86
+ // if pagination is not enabled, or page is not set, ensure page is 0, the first page
87
+ var pageForTab = showPagination && pageByTab && selectedTab ? pageByTab[selectedTab] : 0;
88
+ // if there are less items to display than the page size, shrink the page size so there are not empty rows
89
+ var currentPageSize = Math.min((_a = data === null || data === void 0 ? void 0 : data.length) !== null && _a !== void 0 ? _a : table_1.TABLE_DEFAULT_PAGE_SIZE, pageSize !== null && pageSize !== void 0 ? pageSize : table_1.TABLE_DEFAULT_PAGE_SIZE);
90
+ var pageSizeOptions = table_1.TABLE_PAGE_SIZE_OPTIONS.filter(function (s) { return !(data === null || data === void 0 ? void 0 : data.length) || s <= (data === null || data === void 0 ? void 0 : data.length); });
91
+ return (react_2.default.createElement(react_table_1.default, { "aria-describedby": "search-results-label", className: "manage-table -striped cb" + ((data === null || data === void 0 ? void 0 : data.length) === 1 ? ' single-row-action-button' : ''), data: data, showPagination: showPagination, showPaginationTop: true, sorted: sortingRules, pageSize: currentPageSize, page: pageForTab, pageSizeOptions: pageSizeOptions, columns: columns, onSortedChange: setSortingRules, onPageSizeChange: setPageSize, onPageChange: setPage, NoDataComponent: NoDataComponentCallback }));
92
+ }, [
93
+ NoDataComponentCallback,
94
+ columns,
95
+ data,
96
+ pageByTab,
97
+ pageSize,
98
+ selectedTab,
99
+ setPage,
100
+ setPageSize,
101
+ setSortingRules,
102
+ sortingRules
103
+ ]);
104
+ //#endregion Table
105
+ if (!dataByTab)
106
+ return react_2.default.createElement(RefreshIndicator_1.RefreshIndicator, { label: "Loading...", labelClassName: "color-white" });
107
+ return (react_2.default.createElement(react_2.default.Fragment, null,
108
+ react_2.default.createElement("h3", { id: "search-results-label", className: "visually-hidden" }, "Search Results"),
109
+ SearchTabs,
110
+ Table));
111
+ };
112
+ exports.ManageTable = ManageTable;
@@ -1,7 +1,7 @@
1
1
  import { FunctionComponent } from 'react';
2
2
  import { Search } from '../types';
3
3
  import { SearchPersistorMethods } from './HOC/SearchPersistorComponent';
4
- interface SearchControlsMethods extends Pick<SearchPersistorMethods, 'handleSearchClick' | 'handleKeywordsChange' | 'handleKeywordsKeyDown' | 'resetSearch'>, Partial<Pick<SearchPersistorMethods, 'handleQueryAllChange'>> {
4
+ interface SearchControlsMethods extends Pick<SearchPersistorMethods, 'handleSearchClick' | 'setKeywords' | 'handleKeywordsKeyDown' | 'resetSearch'>, Partial<Pick<SearchPersistorMethods, 'setQueryAll'>> {
5
5
  }
6
6
  export interface SearchControlsProps extends Search, SearchControlsMethods {
7
7
  keywordPlaceholder: string;
@@ -12,24 +12,24 @@ var react_bootstrap_1 = require("react-bootstrap");
12
12
  var AlertWithIcon_1 = require("./AlertWithIcon");
13
13
  var DateField_1 = require("./Forms/DateField");
14
14
  var SearchControls = function (_a) {
15
- var keywords = _a.keywords, invalidKeywords = _a.invalidKeywords, dateString = _a.dateString, queryAll = _a.queryAll, requiredMessage = _a.requiredMessage, handleKeywordsChange = _a.handleKeywordsChange, handleKeywordsKeyDown = _a.handleKeywordsKeyDown, handleSearchClick = _a.handleSearchClick, resetSearch = _a.resetSearch, handleQueryAllChange = _a.handleQueryAllChange, keywordPlaceholder = _a.keywordPlaceholder, handleDateChange = _a.handleDateChange, queryAllText = _a.queryAllText, canReadAnyGlobally = _a.canReadAnyGlobally, canReadSome = _a.canReadSome;
15
+ var keywords = _a.keywords, invalidKeywords = _a.invalidKeywords, dateString = _a.dateString, queryAll = _a.queryAll, requiredMessage = _a.requiredMessage, setKeywords = _a.setKeywords, handleKeywordsKeyDown = _a.handleKeywordsKeyDown, handleSearchClick = _a.handleSearchClick, resetSearch = _a.resetSearch, setQueryAll = _a.setQueryAll, keywordPlaceholder = _a.keywordPlaceholder, handleDateChange = _a.handleDateChange, queryAllText = _a.queryAllText, canReadAnyGlobally = _a.canReadAnyGlobally, canReadSome = _a.canReadSome;
16
16
  return (react_1.default.createElement(react_1.default.Fragment, null,
17
17
  !!requiredMessage && (react_1.default.createElement(AlertWithIcon_1.AlertWithIcon, { variant: "danger", className: "mb0", id: "requiredMessageAlert" },
18
18
  react_1.default.createElement("p", null, requiredMessage))),
19
19
  react_1.default.createElement(react_bootstrap_1.FormGroup, { controlId: "keywords", className: "fl-ns mw5-ns mr3-ns mb3 mb0-ns w-100-lt-xs" },
20
20
  react_1.default.createElement(react_bootstrap_1.FormLabel, { className: "f7 b" }, "Keywords"),
21
- react_1.default.createElement(react_bootstrap_1.FormControl, { type: "text", placeholder: keywordPlaceholder, name: "keywords", onChange: handleKeywordsChange, onKeyDown: handleKeywordsKeyDown, value: keywords, isInvalid: invalidKeywords }),
21
+ react_1.default.createElement(react_bootstrap_1.FormControl, { type: "text", placeholder: keywordPlaceholder, name: "keywords", onChange: setKeywords, onKeyDown: handleKeywordsKeyDown, value: keywords, isInvalid: invalidKeywords }),
22
22
  invalidKeywords && (react_1.default.createElement(react_bootstrap_1.FormText, { className: "i f6", id: "invalidKeywords", muted: true }, "Please enter at least 3 characters."))),
23
23
  !!handleDateChange && (react_1.default.createElement(react_1.default.Fragment, null,
24
24
  react_1.default.createElement("span", { id: "and-or-label", className: "i mw3-ns mr3-ns mt3-ns pv3-ns fl-ns tc w-100-lt-xs date-align" }, "and / or"),
25
25
  react_1.default.createElement(DateField_1.DateField, { id: "date", onChange: handleDateChange, label: "Date", "aria-label": "Active date", className: "fl-ns mw5-ns mr3-ns mb3 mb0-ns w-100-lt-xs date-align", value: dateString }))),
26
26
  react_1.default.createElement("div", { className: "fl-ns mt3-ns pt3-ns w-100-lt-xs" },
27
- canReadAnyGlobally && !!handleQueryAllChange && (react_1.default.createElement(react_bootstrap_1.FormCheck, { name: "queryAll", id: "queryAll", checked: queryAll, disabled: canReadAnyGlobally && !canReadSome, onChange: handleQueryAllChange, className: "fl-ns f6 mr2-ns mb3 mt2 w-100-lt-xs", label: react_1.default.createElement("span", { className: "b black" }, queryAllText || 'Search All') })),
28
- react_1.default.createElement(Button_1.default, { className: "btn-primary fr-ns ml2-ns mb3 w-100-lt-xs", onClick: handleSearchClick, color: "primary" },
29
- react_1.default.createElement(Search_1.default, { className: "fill-white v-mid" }),
30
- "Search"),
27
+ canReadAnyGlobally && !!setQueryAll && (react_1.default.createElement(react_bootstrap_1.FormCheck, { name: "queryAll", id: "queryAll", checked: queryAll, disabled: canReadAnyGlobally && !canReadSome, onChange: setQueryAll, className: "fl-ns f6 mr2-ns mb3 mt2 w-100-lt-xs", label: react_1.default.createElement("span", { className: "b black" }, queryAllText || 'Search All') })),
28
+ canReadAnyGlobally && queryAll && (react_1.default.createElement(Button_1.default, { className: "btn-primary fr-ns ml2-ns mb3 w-100-lt-xs search-button", onClick: handleSearchClick, color: "primary", disabled: !keywords || keywords.length < 3 || !!invalidKeywords },
29
+ react_1.default.createElement(Search_1.default, null),
30
+ "Search")),
31
31
  react_1.default.createElement(Button_1.default, { className: "btn-text color-pink fr-ns mb3 w-100-lt-xs", onClick: resetSearch },
32
- react_1.default.createElement(Autorenew_1.default, { className: "fill-pink v-mid" }),
32
+ react_1.default.createElement(Autorenew_1.default, null),
33
33
  "Reset"))));
34
34
  };
35
35
  exports.SearchControls = SearchControls;
@@ -107,5 +107,5 @@ export declare class UserRoles extends Component<UserRolesProps, UserRolesState>
107
107
  render(): JSX.Element;
108
108
  }
109
109
  export declare const mapStateToProps: (state: BaseReduxState, ownProps: UserRolesOwnProps) => UserRolesReduxProps;
110
- declare const _default: import("react-redux").ConnectedComponent<typeof UserRoles, Pick<React.ClassAttributes<UserRoles> & UserRolesProps, "ref" | "queryParams" | "modelName" | "externalProviders" | "model" | "key" | "readOnly" | "guid" | "load" | "pathParams" | "modelStatus" | "disableAutoLoad" | "modelArray" | "stopPeriodicLoad" | "create" | "update" | "delete" | "previousModelStatus" | "fetchingId" | "textForRole" | "entityName" | "defaultRole" | "roleDescriptions" | "renderAddDescription" | "isUpdateDisabled" | "isDeleteDisabled" | "allowMultipleRoles" | "requiredRole" | "isAddDisabled" | "modifyUserRoleActivityName" | "deleteOwnUserRoleActivityName" | "addRoleExcludeList" | "entity" | "filterUsers" | "onAdd" | "onUpdate" | "onRemove" | "renderTableDescription" | "lowercaseTextForRole" | "singularArticleForRole"> & UserRolesOwnProps>;
110
+ declare const _default: import("react-redux").ConnectedComponent<typeof UserRoles, Pick<React.ClassAttributes<UserRoles> & UserRolesProps, "ref" | "queryParams" | "modelName" | "externalProviders" | "model" | "key" | "readOnly" | "guid" | "entityName" | "load" | "pathParams" | "modelStatus" | "disableAutoLoad" | "modelArray" | "stopPeriodicLoad" | "create" | "update" | "delete" | "previousModelStatus" | "fetchingId" | "textForRole" | "defaultRole" | "roleDescriptions" | "renderAddDescription" | "isUpdateDisabled" | "isDeleteDisabled" | "allowMultipleRoles" | "requiredRole" | "isAddDisabled" | "modifyUserRoleActivityName" | "deleteOwnUserRoleActivityName" | "addRoleExcludeList" | "entity" | "filterUsers" | "onAdd" | "onUpdate" | "onRemove" | "renderTableDescription" | "lowercaseTextForRole" | "singularArticleForRole"> & UserRolesOwnProps>;
111
111
  export default _default;
@@ -7,5 +7,6 @@ export * from './modelStatus';
7
7
  export * from './notificationType';
8
8
  export * from './operatingSystem';
9
9
  export * from './shard';
10
+ export * from './table';
10
11
  export * from './tier';
11
12
  export * from './userRole';
@@ -19,5 +19,6 @@ __exportStar(require("./modelStatus"), exports);
19
19
  __exportStar(require("./notificationType"), exports);
20
20
  __exportStar(require("./operatingSystem"), exports);
21
21
  __exportStar(require("./shard"), exports);
22
+ __exportStar(require("./table"), exports);
22
23
  __exportStar(require("./tier"), exports);
23
24
  __exportStar(require("./userRole"), exports);
@@ -0,0 +1,15 @@
1
+ export declare const TABLE_DEFAULT_PAGE_SIZE = 25;
2
+ export declare const TABLE_PAGE_SIZE_OPTIONS: number[];
3
+ export declare enum MANAGE_TABLE_TAB {
4
+ AVAILABLE = 1,
5
+ DELETED = 2
6
+ }
7
+ /** Iterable array of tabs */
8
+ export declare const MANAGE_TABLE_TABS: MANAGE_TABLE_TAB[];
9
+ export declare enum GROUP_MANAGE_TABLE_TAB {
10
+ CURRENT = 1,
11
+ PAST = 2,
12
+ DELETED = 3
13
+ }
14
+ /** Iterable array of tabs */
15
+ export declare const GROUP_MANAGE_TABLE_TABS: GROUP_MANAGE_TABLE_TAB[];
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GROUP_MANAGE_TABLE_TABS = exports.GROUP_MANAGE_TABLE_TAB = exports.MANAGE_TABLE_TABS = exports.MANAGE_TABLE_TAB = exports.TABLE_PAGE_SIZE_OPTIONS = exports.TABLE_DEFAULT_PAGE_SIZE = void 0;
4
+ exports.TABLE_DEFAULT_PAGE_SIZE = 25;
5
+ exports.TABLE_PAGE_SIZE_OPTIONS = [25, 50, 100, 150, 200];
6
+ var MANAGE_TABLE_TAB;
7
+ (function (MANAGE_TABLE_TAB) {
8
+ MANAGE_TABLE_TAB[MANAGE_TABLE_TAB["AVAILABLE"] = 1] = "AVAILABLE";
9
+ MANAGE_TABLE_TAB[MANAGE_TABLE_TAB["DELETED"] = 2] = "DELETED";
10
+ })(MANAGE_TABLE_TAB = exports.MANAGE_TABLE_TAB || (exports.MANAGE_TABLE_TAB = {}));
11
+ /** Iterable array of tabs */
12
+ exports.MANAGE_TABLE_TABS = [MANAGE_TABLE_TAB.AVAILABLE, MANAGE_TABLE_TAB.DELETED];
13
+ var GROUP_MANAGE_TABLE_TAB;
14
+ (function (GROUP_MANAGE_TABLE_TAB) {
15
+ GROUP_MANAGE_TABLE_TAB[GROUP_MANAGE_TABLE_TAB["CURRENT"] = 1] = "CURRENT";
16
+ GROUP_MANAGE_TABLE_TAB[GROUP_MANAGE_TABLE_TAB["PAST"] = 2] = "PAST";
17
+ GROUP_MANAGE_TABLE_TAB[GROUP_MANAGE_TABLE_TAB["DELETED"] = 3] = "DELETED";
18
+ })(GROUP_MANAGE_TABLE_TAB = exports.GROUP_MANAGE_TABLE_TAB || (exports.GROUP_MANAGE_TABLE_TAB = {}));
19
+ /** Iterable array of tabs */
20
+ exports.GROUP_MANAGE_TABLE_TABS = [
21
+ GROUP_MANAGE_TABLE_TAB.CURRENT,
22
+ GROUP_MANAGE_TABLE_TAB.PAST,
23
+ GROUP_MANAGE_TABLE_TAB.DELETED
24
+ ];
@@ -1,5 +1,5 @@
1
- import { CollectionCommonProps, CollectionCommonState, CollectionMethods, CollectionReduxResponse, Model } from '../types';
2
- export interface UseCollectionResponse<TModel extends Model> extends CollectionCommonState, CollectionReduxResponse<TModel>, CollectionMethods {
1
+ import { CollectionCommonProps, CollectionCommonState, CollectionDerivedProps, CollectionMethods, CollectionReduxResponse, Model } from '../types';
2
+ export interface UseCollectionResponse<TModel extends Model> extends CollectionCommonState, CollectionReduxResponse<TModel>, CollectionDerivedProps<TModel>, CollectionMethods {
3
3
  guid: string;
4
4
  }
5
5
  export declare function useCollection<TModel extends Model>(props: CollectionCommonProps): UseCollectionResponse<TModel>;
@@ -6,6 +6,7 @@ var react_redux_1 = require("react-redux");
6
6
  var react_router_1 = require("react-router");
7
7
  var modelStatus_1 = require("../constants/modelStatus");
8
8
  var model_1 = require("../utils/model");
9
+ var route_1 = require("../utils/route");
9
10
  var useGuid_1 = require("./useGuid");
10
11
  var usePrevious_1 = require("./usePrevious");
11
12
  /**
@@ -23,13 +24,20 @@ function useCollectionConfiguration(props, selectorFunction) {
23
24
  var _b = react_1.useState(modelStatus_1.MODEL_STATUS.UNINITIALIZED), modelStatus = _b[0], setModelStatus = _b[1];
24
25
  var previousModelStatus = usePrevious_1.usePrevious(modelStatus);
25
26
  var _c = react_1.useState(), fetchingId = _c[0], setFetchingId = _c[1];
27
+ var pathParams = react_1.useMemo(function () { return propPathParams || route_1.getPathParamsFromRouteMatchParams(routeMatchParams, modelName); }, [
28
+ modelName,
29
+ propPathParams,
30
+ routeMatchParams
31
+ ]);
26
32
  var _d = react_redux_1.useSelector(function (state) {
27
- return selectorFunction({ guid: guid, modelName: modelName, pathParams: propPathParams, routeMatchParams: routeMatchParams, state: state });
28
- }), pathParams = _d.pathParams, model = _d.model, modelArray = _d.modelArray, modelMinusRelations = _d.modelMinusRelations;
33
+ return selectorFunction({ guid: guid, modelName: modelName, pathParams: pathParams, routeMatchParams: routeMatchParams, state: state });
34
+ }), model = _d.model, isCollectionItem = _d.isCollectionItem;
29
35
  var previousModelName = usePrevious_1.usePrevious(modelName);
30
36
  var previousModel = usePrevious_1.usePrevious(model);
31
37
  var previousPathParams = usePrevious_1.usePrevious(pathParams);
32
38
  var previousQueryParams = usePrevious_1.usePrevious(queryParams);
39
+ var modelArray = react_1.useMemo(function () { return (!isCollectionItem ? model_1.getModelArray(model, guid) : undefined); }, [guid, isCollectionItem, model]);
40
+ var modelMinusRelations = react_1.useMemo(function () { return (isCollectionItem ? model_1.getModelMinusRelations(model) : undefined); }, [isCollectionItem, model]);
33
41
  var changeModelStatus = function (newModelStatus, newFetchingId) {
34
42
  setFetchingId(newFetchingId);
35
43
  setModelStatus(newModelStatus);
@@ -1,5 +1,5 @@
1
- import { CollectionCommonProps, CollectionCommonState, CollectionItemMethods, CollectionItemReduxResponse, Model } from '../types';
2
- export interface UseCollectionItemResponse<TModel extends Model> extends CollectionCommonState, CollectionItemReduxResponse<TModel>, CollectionItemMethods {
1
+ import { CollectionCommonProps, CollectionCommonState, CollectionItemDerivedProps, CollectionItemMethods, CollectionItemReduxResponse, Model } from '../types';
2
+ export interface UseCollectionItemResponse<TModel extends Model> extends CollectionCommonState, CollectionItemReduxResponse<TModel>, CollectionItemDerivedProps<TModel>, CollectionItemMethods {
3
3
  guid: string;
4
4
  }
5
5
  export declare function useCollectionItem<TModel extends Model>(props: CollectionCommonProps): UseCollectionItemResponse<TModel>;
@@ -109,9 +109,10 @@ exports.merge = merge;
109
109
  * @param metadata
110
110
  */
111
111
  function updateMetadataForChildCollections(data, metadata) {
112
+ var isDataCollection = isCollection(data);
112
113
  Object.keys(data).forEach(function (key) {
113
114
  var value = data[key];
114
- if ((isCollection(data) && key !== '_metadata' && lodash_1.default.isPlainObject(value)) || isCollection(value)) {
115
+ if ((isDataCollection && key !== '_metadata' && lodash_1.default.isPlainObject(value)) || isCollection(value)) {
115
116
  value._metadata = metadata;
116
117
  updateMetadataForChildCollections(value, metadata);
117
118
  }
@@ -164,9 +165,17 @@ function fetchReducer(state, action) {
164
165
  };
165
166
  var incoming = !lodash_1.default.isPlainObject(action.data) && !lodash_1.default.isArray(action.data) ? { response: action.data } : action.data;
166
167
  updateMetadataForChildCollections(incoming, metadataUpdate);
167
- valueAtPath = merge(valueAtPath, incoming);
168
+ // merging the incoming with existing
169
+ // otherwise completely replace the existing value in redux
170
+ if (!action.replaceValue) {
171
+ valueAtPath = merge(valueAtPath, incoming);
172
+ }
173
+ else {
174
+ valueAtPath = lodash_1.default.merge({}, incoming);
175
+ }
168
176
  valueAtPath._metadata = lodash_1.default.merge({}, metadata, metadataUpdate);
169
- return fp_1.default.setWith(Object, path, valueAtPath, state);
177
+ var result = fp_1.default.setWith(Object, path, valueAtPath, state);
178
+ return result;
170
179
  }
171
180
  case actions_1.NET_ACTION.FETCH_FAILED:
172
181
  // Retain the object, update the metadata to reflect the fact
@@ -410,6 +410,7 @@ function fetchData(action) {
410
410
  resultAction = actionCreator_1.createAction(storeAction, {
411
411
  modelName: modelNameLevels.join('.') + "." + data.id,
412
412
  guid: action.guid,
413
+ replaceValue: action.replaceValue,
413
414
  data: data
414
415
  });
415
416
  return [4 /*yield*/, effects_1.put(resultAction)
@@ -427,6 +428,7 @@ function fetchData(action) {
427
428
  resultReceivedAction = actionCreator_1.createAction(storeAction, {
428
429
  modelName: modelName,
429
430
  guid: action.guid,
431
+ replaceValue: action.replaceValue,
430
432
  data: data
431
433
  });
432
434
  return [4 /*yield*/, effects_1.put(resultReceivedAction)];
@@ -30,8 +30,7 @@ export interface CollectionSelectorMethodResponse<TModel extends Model> {
30
30
  modelName: string;
31
31
  pathParams: string[];
32
32
  model: TModel | ModelCollection<TModel>;
33
- modelArray?: TModel[];
34
- modelMinusRelations?: Partial<TModel>;
33
+ isCollectionItem: boolean;
35
34
  }
36
35
  /** A method used to select a collection or collection item model from redux. */
37
36
  export declare type CollectionSelectorMethod = <TModel extends Model>(params: CollectionSelectorMethodParams) => CollectionSelectorMethodResponse<TModel>;
@@ -51,6 +50,8 @@ export interface CollectionItemLoadParams {
51
50
  period?: number;
52
51
  /** The taskId to set for later cancellation. */
53
52
  taskId?: string;
53
+ /** If true, the fetchReducer will replace the existing value in redux, instead of merging the incoming with existing. */
54
+ replaceValue?: boolean;
54
55
  }
55
56
  export interface CollectionLoadParams {
56
57
  /** Id of an item in the collection to load. Otherwise entire collection is loaded. */
@@ -63,6 +64,8 @@ export interface CollectionLoadParams {
63
64
  period?: number;
64
65
  /** The taskId to set for later cancellation. */
65
66
  taskId?: string;
67
+ /** If true, the fetchReducer will replace the existing value in redux, instead of merging the incoming with existing. */
68
+ replaceValue?: boolean;
66
69
  }
67
70
  export interface CollectionCreateParams {
68
71
  /** The body to send in the request. */
@@ -154,6 +157,8 @@ export interface CollectionMethods {
154
157
  export interface CollectionItemReduxResponse<TModel extends Model> {
155
158
  /** The collection item model loaded from redux, or loaded into redux using `load()`. */
156
159
  model: TModel;
160
+ }
161
+ export interface CollectionItemDerivedProps<TModel extends Model> {
157
162
  /** The collection item model, without any of its nested relations. */
158
163
  modelMinusRelations: Partial<TModel>;
159
164
  }
@@ -161,6 +166,8 @@ export interface CollectionItemReduxResponse<TModel extends Model> {
161
166
  export interface CollectionReduxResponse<TModel extends Model> {
162
167
  /** The collection model loaded from redux if available, or loaded into redux using `load()`. */
163
168
  model: ModelCollection<TModel>;
169
+ }
170
+ export interface CollectionDerivedProps<TModel extends Model> {
164
171
  /** The collection model converted to an array of all collection items. */
165
172
  modelArray: TModel[];
166
173
  }
@@ -1,3 +1,4 @@
1
+ import { SortingRule } from 'react-table';
1
2
  export interface Search {
2
3
  keywords?: string;
3
4
  invalidKeywords?: boolean;
@@ -5,7 +6,13 @@ export interface Search {
5
6
  dateString?: string;
6
7
  date?: Date | null;
7
8
  queryAll?: boolean;
9
+ includeIfSoftDeleted?: boolean;
8
10
  requiredMessage?: string | null;
9
11
  selectedTab?: number;
10
- includeIfSoftDeleted?: boolean;
12
+ /** react-table sorting rules */
13
+ sortingRules?: SortingRule[];
14
+ /** react-table page size, for pagination */
15
+ pageSize?: number;
16
+ /** react-table currently selected page for a given tab, for pagination */
17
+ pageByTab?: Record<string, number | undefined>;
11
18
  }
@@ -32,6 +32,8 @@ export interface FetchAction extends Action<NET_ACTION> {
32
32
  noRetry?: boolean;
33
33
  /** The contentType to be set in the headers. Defaults to `application/json` */
34
34
  contentType?: string;
35
+ /** If true, the fetchReducer will replace the existing value in redux, instead of merging the incoming with existing. */
36
+ replaceValue?: boolean;
35
37
  /** The data returned from a request. */
36
38
  data?: any;
37
39
  /** The error data returned from a failed request. */
@@ -1,17 +1,23 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.cleanupCollectionGuidKey = exports.handleCollectionParamsChange = exports.handleCollectionItemParamsChange = exports.initializeCollection = exports.initializeCollectionItem = exports.deleteCollectionItem = exports.deleteItemFromCollection = exports.updateCollectionItem = exports.updateItemInCollection = exports.createCollectionItem = exports.createItemInCollection = exports.loadCollectionItem = exports.loadCollection = exports.stopCollectionPeriodicLoad = exports.selectCollectionFromState = exports.selectCollectionItemFromState = void 0;
4
7
  var lodash_1 = require("lodash");
8
+ var memoize_one_1 = __importDefault(require("memoize-one"));
5
9
  var constants_1 = require("../constants");
6
10
  var actionCreator_1 = require("../redux/actionCreator");
7
11
  var actions_1 = require("../redux/actions");
8
- var model_1 = require("./model");
9
12
  var route_1 = require("./route");
10
13
  //#region Redux Methods
14
+ var getPathParams = memoize_one_1.default(function (pathParams, routeMatchParams, modelName) {
15
+ return pathParams || route_1.getPathParamsFromRouteMatchParams(routeMatchParams, modelName);
16
+ });
11
17
  function selectCollectionItemFromState(params) {
12
18
  var guid = params.guid, modelName = params.modelName, pathParams = params.pathParams, routeMatchParams = params.routeMatchParams, state = params.state;
13
19
  var modelNameLevelCount = route_1.getMinRequiredPathParamsCount(modelName);
14
- var p = pathParams || route_1.getPathParamsFromRouteMatchParams(routeMatchParams, modelName);
20
+ var p = getPathParams(pathParams, routeMatchParams, modelName);
15
21
  var reduxModelName = route_1.getReduxModelName(p, modelName);
16
22
  var model = {};
17
23
  // find `model` using `guid` as its key, to match new item created in `create()`
@@ -28,31 +34,27 @@ function selectCollectionItemFromState(params) {
28
34
  else {
29
35
  // find `model` using pathParams
30
36
  var reduxModel = (lodash_1.get(state.models, reduxModelName) || {});
31
- model = (Object.keys(reduxModel).length > 0 ? lodash_1.merge({}, reduxModel) : {});
37
+ model = (Object.keys(reduxModel).length > 0 ? reduxModel : {});
32
38
  }
33
- // convenient way to access model without relations, for use in PUT requests
34
- var modelMinusRelations = model_1.getModelMinusRelations(model);
35
39
  return {
36
40
  modelName: modelName,
37
41
  pathParams: p,
38
42
  model: model,
39
- modelMinusRelations: modelMinusRelations
43
+ isCollectionItem: true
40
44
  };
41
45
  }
42
46
  exports.selectCollectionItemFromState = selectCollectionItemFromState;
43
47
  function selectCollectionFromState(params) {
44
- var guid = params.guid, modelName = params.modelName, pathParams = params.pathParams, routeMatchParams = params.routeMatchParams, state = params.state;
45
- var p = pathParams || route_1.getPathParamsFromRouteMatchParams(routeMatchParams, modelName);
48
+ var modelName = params.modelName, pathParams = params.pathParams, routeMatchParams = params.routeMatchParams, state = params.state;
49
+ var p = getPathParams(pathParams, routeMatchParams, modelName);
46
50
  var reduxModelName = route_1.getReduxModelName(p, modelName);
47
51
  var model = lodash_1.get(state.models, reduxModelName);
48
- model = model && Object.keys(model).length > 0 ? lodash_1.merge({}, model) : {};
49
- // convenient way to access collection as an array
50
- var modelArray = model_1.getModelArray(model, guid);
52
+ model = model && Object.keys(model).length > 0 ? model : {};
51
53
  return {
52
54
  modelName: modelName,
53
55
  pathParams: p,
54
56
  model: model,
55
- modelArray: modelArray
57
+ isCollectionItem: false
56
58
  };
57
59
  }
58
60
  exports.selectCollectionFromState = selectCollectionFromState;
@@ -69,7 +71,7 @@ exports.stopCollectionPeriodicLoad = stopCollectionPeriodicLoad;
69
71
  function loadCollection(config, params) {
70
72
  if (params === void 0) { params = {}; }
71
73
  var modelName = config.modelName, modelStatus = config.modelStatus, configPathParams = config.pathParams, configQueryParams = config.queryParams, changeModelStatus = config.changeModelStatus;
72
- var id = params.id, pathParams = params.pathParams, queryParams = params.queryParams, period = params.period, taskId = params.taskId;
74
+ var id = params.id, pathParams = params.pathParams, queryParams = params.queryParams, period = params.period, taskId = params.taskId, replaceValue = params.replaceValue;
73
75
  var p = lodash_1.concat([], pathParams || configPathParams, id ? [id] : []);
74
76
  if (p.length !== route_1.getMinRequiredPathParamsCount(modelName) + (id ? 1 : 0)) {
75
77
  throw new Error('pathParams length does not match length of path components');
@@ -82,7 +84,8 @@ function loadCollection(config, params) {
82
84
  modelName: modelName,
83
85
  method: 'GET',
84
86
  pathParams: p,
85
- queryParams: queryParams || configQueryParams
87
+ queryParams: queryParams || configQueryParams,
88
+ replaceValue: replaceValue
86
89
  });
87
90
  }
88
91
  else {
@@ -92,7 +95,8 @@ function loadCollection(config, params) {
92
95
  pathParams: p,
93
96
  queryParams: queryParams || configQueryParams,
94
97
  period: period,
95
- taskId: taskId
98
+ taskId: taskId,
99
+ replaceValue: replaceValue
96
100
  });
97
101
  }
98
102
  }
@@ -100,7 +104,7 @@ exports.loadCollection = loadCollection;
100
104
  function loadCollectionItem(config, params) {
101
105
  if (params === void 0) { params = {}; }
102
106
  var modelName = config.modelName, modelStatus = config.modelStatus, configPathParams = config.pathParams, configQueryParams = config.queryParams, changeModelStatus = config.changeModelStatus;
103
- var pathParams = params.pathParams, queryParams = params.queryParams, period = params.period, taskId = params.taskId;
107
+ var pathParams = params.pathParams, queryParams = params.queryParams, period = params.period, taskId = params.taskId, replaceValue = params.replaceValue;
104
108
  var p = pathParams || configPathParams;
105
109
  if (p && p.length < route_1.getMinRequiredPathParamsCount(modelName) + 1) {
106
110
  throw new Error('pathParams length does not match length of path components');
@@ -113,7 +117,8 @@ function loadCollectionItem(config, params) {
113
117
  modelName: modelName,
114
118
  method: 'GET',
115
119
  pathParams: p,
116
- queryParams: queryParams || configQueryParams
120
+ queryParams: queryParams || configQueryParams,
121
+ replaceValue: replaceValue
117
122
  });
118
123
  }
119
124
  else {
@@ -123,7 +128,8 @@ function loadCollectionItem(config, params) {
123
128
  pathParams: p,
124
129
  queryParams: queryParams || configQueryParams,
125
130
  period: period,
126
- taskId: taskId
131
+ taskId: taskId,
132
+ replaceValue: replaceValue
127
133
  });
128
134
  }
129
135
  }
@@ -0,0 +1,2 @@
1
+ export declare const getManageTableTabName: (selectedTab?: number | undefined) => "" | "Available" | "Deleted";
2
+ export declare const getGroupManageTableTabName: (selectedTab?: number | undefined) => "" | "Deleted" | "Current" | "Past";
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getGroupManageTableTabName = exports.getManageTableTabName = void 0;
4
+ var table_1 = require("../constants/table");
5
+ var getManageTableTabName = function (selectedTab) {
6
+ switch (selectedTab) {
7
+ case table_1.MANAGE_TABLE_TAB.AVAILABLE:
8
+ return 'Available';
9
+ case table_1.MANAGE_TABLE_TAB.DELETED:
10
+ return 'Deleted';
11
+ default:
12
+ return '';
13
+ }
14
+ };
15
+ exports.getManageTableTabName = getManageTableTabName;
16
+ var getGroupManageTableTabName = function (selectedTab) {
17
+ switch (selectedTab) {
18
+ case table_1.GROUP_MANAGE_TABLE_TAB.CURRENT:
19
+ return 'Current';
20
+ case table_1.GROUP_MANAGE_TABLE_TAB.DELETED:
21
+ return 'Deleted';
22
+ case table_1.GROUP_MANAGE_TABLE_TAB.PAST:
23
+ return 'Past';
24
+ default:
25
+ return '';
26
+ }
27
+ };
28
+ exports.getGroupManageTableTabName = getGroupManageTableTabName;
@@ -0,0 +1,28 @@
1
+ import { EntityUser, ExternalTerm, Group, ModelCollection } from '../types';
2
+ export declare const getKeywordsList: (keywords: string | undefined) => string[];
3
+ export declare const isEntityUserMatchedByKeyword: (owner: EntityUser, keyword: string) => boolean;
4
+ export declare const isEntityMatchedByKeyword: (entity: {
5
+ id: number;
6
+ name: string;
7
+ owners: EntityUser[];
8
+ }, keyword: string) => boolean;
9
+ export declare const isEntityMatchedByKeywords: (entity: {
10
+ id: number;
11
+ name: string;
12
+ owners: EntityUser[];
13
+ }, keywordsList: string[]) => boolean;
14
+ export declare const searchEntities: <T extends {
15
+ isDeleted: boolean;
16
+ activities: string[];
17
+ }>(entityArray: T[], entityMatchFunction: (entity: T, keywordsList: string[]) => boolean, readActivity: string, canReadAnyGlobally?: boolean | undefined, keywords?: string | undefined) => Record<number, T[]>;
18
+ export interface SearchModelArrayParams<TModel> {
19
+ modelArray: TModel[];
20
+ canReadyAnyGlobally?: boolean;
21
+ keywords?: string;
22
+ }
23
+ export interface SearchGroupsParams extends SearchModelArrayParams<Group> {
24
+ groups: ModelCollection<Group>;
25
+ externalTerms: ModelCollection<ExternalTerm>;
26
+ dateString?: string;
27
+ }
28
+ export declare const searchGroups: ({ groups, modelArray: groupsArray, externalTerms, canReadyAnyGlobally, keywords, dateString }: SearchGroupsParams) => Record<number, Group[]>;
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.searchGroups = exports.searchEntities = exports.isEntityMatchedByKeywords = exports.isEntityMatchedByKeyword = exports.isEntityUserMatchedByKeyword = exports.getKeywordsList = void 0;
4
+ var table_1 = require("../constants/table");
5
+ var date_1 = require("./date");
6
+ var groupDates_1 = require("./groupDates");
7
+ var groupRoles_1 = require("./groupRoles");
8
+ var getKeywordsList = function (keywords) {
9
+ if (!keywords)
10
+ return [];
11
+ return keywords
12
+ .trim()
13
+ .toLowerCase()
14
+ .split(' ')
15
+ .filter(function (k) { return !!k; });
16
+ };
17
+ exports.getKeywordsList = getKeywordsList;
18
+ var isEntityUserMatchedByKeyword = function (owner, keyword) {
19
+ var _a, _b, _c, _d;
20
+ return owner.id.toString().toLowerCase().includes(keyword) ||
21
+ !!((_a = owner.firstName) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(keyword)) ||
22
+ !!((_b = owner.lastName) === null || _b === void 0 ? void 0 : _b.toString().toLowerCase().includes(keyword)) ||
23
+ !!((_c = owner.email) === null || _c === void 0 ? void 0 : _c.toString().toLowerCase().includes(keyword)) ||
24
+ !!((_d = owner.uid) === null || _d === void 0 ? void 0 : _d.toString().toLowerCase().includes(keyword));
25
+ };
26
+ exports.isEntityUserMatchedByKeyword = isEntityUserMatchedByKeyword;
27
+ var isEntityMatchedByKeyword = function (entity, keyword) {
28
+ return entity.id.toString().toLowerCase().includes(keyword) ||
29
+ entity.name.toLowerCase().includes(keyword) ||
30
+ entity.owners.some(function (owner) { return exports.isEntityUserMatchedByKeyword(owner, keyword); });
31
+ };
32
+ exports.isEntityMatchedByKeyword = isEntityMatchedByKeyword;
33
+ var isEntityMatchedByKeywords = function (entity, keywordsList) { return !keywordsList || keywordsList.length === 0 || keywordsList.every(function (k) { return exports.isEntityMatchedByKeyword(entity, k); }); };
34
+ exports.isEntityMatchedByKeywords = isEntityMatchedByKeywords;
35
+ var searchEntities = function (entityArray, entityMatchFunction, readActivity, canReadAnyGlobally, keywords) {
36
+ var _a;
37
+ var keywordsList = exports.getKeywordsList(keywords);
38
+ var filteredEntities = entityArray.reduce(function (acc, e) {
39
+ var _a;
40
+ // exclude if cannot read any entity and does not have the read activity
41
+ if (!canReadAnyGlobally && !e.activities.includes(readActivity))
42
+ return acc;
43
+ // exclude if does not match, if any
44
+ if (!entityMatchFunction(e, keywordsList))
45
+ return acc;
46
+ // add entity to the corresponding tab filtered array
47
+ var tab = e.isDeleted ? table_1.MANAGE_TABLE_TAB.DELETED : table_1.MANAGE_TABLE_TAB.AVAILABLE;
48
+ var tabArray = (_a = acc[tab]) !== null && _a !== void 0 ? _a : [];
49
+ tabArray.push(e);
50
+ acc[tab] = tabArray;
51
+ return acc;
52
+ }, (_a = {}, _a[table_1.MANAGE_TABLE_TAB.AVAILABLE] = [], _a[table_1.MANAGE_TABLE_TAB.DELETED] = [], _a));
53
+ return filteredEntities;
54
+ };
55
+ exports.searchEntities = searchEntities;
56
+ var getGroupTab = function (group, endDate) {
57
+ if (group.isDeleted) {
58
+ return table_1.GROUP_MANAGE_TABLE_TAB.DELETED;
59
+ }
60
+ else if (!!group.id && date_1.isNowBeforeDate(endDate)) {
61
+ return table_1.GROUP_MANAGE_TABLE_TAB.CURRENT;
62
+ }
63
+ return table_1.GROUP_MANAGE_TABLE_TAB.PAST;
64
+ };
65
+ var searchGroups = function (_a) {
66
+ var _b;
67
+ var groups = _a.groups, groupsArray = _a.modelArray, externalTerms = _a.externalTerms, canReadyAnyGlobally = _a.canReadyAnyGlobally, keywords = _a.keywords, dateString = _a.dateString;
68
+ var keywordsList = exports.getKeywordsList(keywords);
69
+ var visibleGroups = canReadyAnyGlobally ? groupsArray : groupRoles_1.groupsAsAnythingButLearner(groups);
70
+ var filteredGroups = visibleGroups.reduce(function (acc, g) {
71
+ var _a;
72
+ // exclude if does not match keywords, if any
73
+ if (!exports.isEntityMatchedByKeywords(g, keywordsList))
74
+ return acc;
75
+ var startDate = groupDates_1.getStartDate(externalTerms, g);
76
+ var endDate = groupDates_1.getEndDate(externalTerms, g);
77
+ // exclude if search dateString is not between group start and end dates
78
+ if (!!dateString &&
79
+ (date_1.getLocalMomentFromUtc(dateString) < date_1.getLocalMomentFromUtc(startDate) ||
80
+ date_1.getLocalMomentFromUtc(dateString) > date_1.getLocalMomentFromUtc(endDate))) {
81
+ return acc;
82
+ }
83
+ var tab = getGroupTab(g, endDate);
84
+ var tabArray = (_a = acc[tab]) !== null && _a !== void 0 ? _a : [];
85
+ tabArray.push(g);
86
+ acc[tab] = tabArray;
87
+ return acc;
88
+ }, (_b = {},
89
+ _b[table_1.GROUP_MANAGE_TABLE_TAB.CURRENT] = [],
90
+ _b[table_1.GROUP_MANAGE_TABLE_TAB.PAST] = [],
91
+ _b[table_1.GROUP_MANAGE_TABLE_TAB.DELETED] = [],
92
+ _b));
93
+ return filteredGroups;
94
+ };
95
+ exports.searchGroups = searchGroups;
96
+ //#endregion Groups
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "studiokit-scaffolding-js",
3
- "version": "5.0.0",
3
+ "version": "5.1.0",
4
4
  "description": "Common scaffolding for Studio apps at Purdue",
5
5
  "repository": "https://gitlab.com/purdue-informatics/studiokit/studiokit-scaffolding-js",
6
6
  "license": "MIT",
@@ -126,6 +126,7 @@
126
126
  "history": "^4.10.1",
127
127
  "local-storage-fallback": "^4.1.1",
128
128
  "lodash": "^4.17.20",
129
+ "memoize-one": "^6.0.0",
129
130
  "moment-timezone": "^0.5.32",
130
131
  "parchment": "^1.1.4",
131
132
  "pluralize": "^8.0.0",