@kaspernj/api-maker 1.0.291 → 1.0.292

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/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  ]
17
17
  },
18
18
  "name": "@kaspernj/api-maker",
19
- "version": "1.0.291",
19
+ "version": "1.0.292",
20
20
  "type": "module",
21
21
  "description": "",
22
22
  "main": "index.js",
package/src/config.mjs CHANGED
@@ -16,6 +16,7 @@ const accessors = {
16
16
  history: {required: false},
17
17
  host: {required: false},
18
18
  i18n: {required: false},
19
+ modal: {required: false},
19
20
  routes: {required: false},
20
21
  routeDefinitions: {required: false}
21
22
  }
@@ -33,6 +33,7 @@ class ApiMakerInputsCheckbox extends React.PureComponent {
33
33
  attribute,
34
34
  autoRefresh,
35
35
  autoSubmit,
36
+ checked,
36
37
  defaultChecked,
37
38
  defaultValue,
38
39
  id,
@@ -32,7 +32,13 @@ const inputWrapper = (WrapperComponentClass, wrapperOptions = {}) => {
32
32
  )
33
33
 
34
34
  if (this.handleAsCheckbox()) {
35
- inputProps.defaultChecked = this.inputDefaultChecked()
35
+ if ("checked" in this.props) {
36
+ inputProps.checked = this.props.checked
37
+ }
38
+
39
+ if ("defaultChecked" in this.props || (this.props.attribute && this.props.model)) {
40
+ inputProps.defaultChecked = this.inputDefaultChecked()
41
+ }
36
42
  } else {
37
43
  inputProps.defaultValue = this.inputDefaultValue()
38
44
  }
@@ -339,8 +339,6 @@ export default class ApiMakerTableFiltersFilterForm extends React.PureComponent
339
339
  throw new Error("Dont know if should search for attribute or scope?")
340
340
  }
341
341
 
342
- console.log({newSearchParams})
343
-
344
342
  searchParams[filterIndex] = JSON.stringify(newSearchParams)
345
343
 
346
344
  const newParams = {}
@@ -1,7 +1,9 @@
1
1
  import {digg, digs} from "diggerize"
2
+ import FilterForm from "./filter-form"
3
+ import LoadSearchModal from "./load-search-modal"
4
+ import SaveSearchModal from "./save-search-modal"
2
5
  import PropTypes from "prop-types"
3
6
  import React from "react"
4
- import FilterForm from "./filter-form"
5
7
  import Shape from "set-state-compare/src/shape"
6
8
  import withQueryParams from "on-location-changed/src/with-query-params"
7
9
 
@@ -56,6 +58,7 @@ class ApiMakerTableFilter extends React.PureComponent {
56
58
 
57
59
  class ApiMakerTableFilters extends React.PureComponent {
58
60
  static propTypes = {
61
+ currentUser: PropTypes.object,
59
62
  modelClass: PropTypes.func.isRequired,
60
63
  queryName: PropTypes.string.isRequired,
61
64
  querySName: PropTypes.string.isRequired,
@@ -63,19 +66,19 @@ class ApiMakerTableFilters extends React.PureComponent {
63
66
  }
64
67
 
65
68
  shape = new Shape(this, {
66
- filter: undefined
69
+ filter: undefined,
70
+ showLoadSearchModal: false,
71
+ showSaveSearchModal: false
67
72
  })
68
73
 
69
74
  render() {
70
75
  const {modelClass, querySName} = digs(this.props, "modelClass", "querySName")
71
- const {filter} = digs(this.shape, "filter")
76
+ const {currentUser} = this.props
77
+ const {filter, showLoadSearchModal, showSaveSearchModal} = digs(this.shape, "filter", "showLoadSearchModal", "showSaveSearchModal")
72
78
  const currentFilters = this.currentFilters()
73
79
 
74
80
  return (
75
81
  <div className="api-maker--table--filters">
76
- <button className="add-new-filter-button" onClick={digg(this, "onAddFilterClicked")}>
77
- {I18n.t("js.api_maker.table.filters.add_new_filter", {defaultValue: "Add new filter"})}
78
- </button>
79
82
  {filter &&
80
83
  <FilterForm
81
84
  filter={filter}
@@ -85,24 +88,50 @@ class ApiMakerTableFilters extends React.PureComponent {
85
88
  querySearchName={querySName}
86
89
  />
87
90
  }
91
+ {showLoadSearchModal &&
92
+ <LoadSearchModal
93
+ currentUser={currentUser}
94
+ modelClass={modelClass}
95
+ onRequestClose={digg(this, "onRequestCloseLoadSearchModal")}
96
+ querySearchName={querySName}
97
+ />
98
+ }
99
+ {showSaveSearchModal &&
100
+ <SaveSearchModal currentFilters={digg(this, "currentFilters")} currentUser={currentUser} onRequestClose={digg(this, "onRequestCloseSaveSearchModal")} />
101
+ }
88
102
  {currentFilters?.map((filterData, filterIndex) =>
89
103
  <ApiMakerTableFilter
90
104
  key={filterIndex}
91
105
  filterIndex={filterIndex}
92
106
  onClick={digg(this, "onFilterClicked")}
93
107
  onRemoveClicked={digg(this, "onRemoveClicked")}
94
- {...JSON.parse(filterData)}
108
+ {...filterData}
95
109
  />
96
110
  )}
111
+ <div className="filter-actions">
112
+ <button className="add-new-filter-button" onClick={digg(this, "onAddFilterClicked")}>
113
+ {I18n.t("js.api_maker.table.filters.add_new_filter", {defaultValue: "Add new filter"})}
114
+ </button>
115
+ {currentUser &&
116
+ <>
117
+ <button className="save-search-button" onClick={digg(this, "onSaveSearchClicked")}>
118
+ {I18n.t("js.api_maker.table.filters.save_search", {defaultValue: "Save search"})}
119
+ </button>
120
+ <button className="load-search-button" onClick={digg(this, "onLoadSearchClicked")}>
121
+ {I18n.t("js.api_maker.table.filters.load_search", {defaultValue: "Load search"})}
122
+ </button>
123
+ </>
124
+ }
125
+ </div>
97
126
  </div>
98
127
  )
99
128
  }
100
129
 
101
- currentFilters() {
130
+ currentFilters = () => {
102
131
  const {queryParams, querySName} = digs(this.props, "queryParams", "querySName")
103
132
  const currentFilters = queryParams[querySName] || []
104
133
 
105
- return currentFilters
134
+ return currentFilters.map((currentFilter) => JSON.parse(currentFilter))
106
135
  }
107
136
 
108
137
  onAddFilterClicked = (e) => {
@@ -118,6 +147,14 @@ class ApiMakerTableFilters extends React.PureComponent {
118
147
  }
119
148
 
120
149
  onApplyClicked = () => this.shape.set({filter: undefined})
150
+ onFilterClicked = (args) => this.shape.set({filter: args})
151
+
152
+ onLoadSearchClicked = (e) => {
153
+ e.preventDefault()
154
+ this.shape.set({
155
+ showLoadSearchModal: true
156
+ })
157
+ }
121
158
 
122
159
  onRemoveClicked = ({filterIndex}) => {
123
160
  const {querySName} = digs(this.props, "querySName")
@@ -136,7 +173,20 @@ class ApiMakerTableFilters extends React.PureComponent {
136
173
  })
137
174
  }
138
175
 
139
- onFilterClicked = (args) => this.shape.set({filter: args})
176
+ onRequestCloseLoadSearchModal = () => this.shape.set({showLoadSearchModal: false})
177
+ onRequestCloseSaveSearchModal = () => this.shape.set({showSaveSearchModal: false})
178
+
179
+ onSaveSearchClicked = (e) => {
180
+ e.preventDefault()
181
+
182
+ if (this.hasAnyFilters()) {
183
+ this.shape.set({showSaveSearchModal: true})
184
+ } else {
185
+ FlashMessage.alert(I18n.t("js.api_maker.table.filters.no_filters_has_been_set", {defaultValue: "No filters has been set"}))
186
+ }
187
+ }
188
+
189
+ hasAnyFilters = () => Object.keys(this.currentFilters()).length > 0
140
190
  }
141
191
 
142
192
  export default withQueryParams(ApiMakerTableFilters)
@@ -0,0 +1,60 @@
1
+ import apiMakerConfig from "@kaspernj/api-maker/src/config.mjs"
2
+ import classNames from "classnames"
3
+ import {digg} from "diggerize"
4
+ import {TableSearch} from "../../models.mjs.erb"
5
+ import {useCallback, useEffect, useState} from "react"
6
+
7
+ const SearchLink = (props) => {
8
+ const {onClick, search} = props
9
+ const onSearchClicked = useCallback((e) => {
10
+ e.preventDefault()
11
+ onClick({search})
12
+ })
13
+
14
+ return (
15
+ <a className="load-search-link" href="#" key={search.id()} onClick={onSearchClicked} style={{display: "block"}}>
16
+ {search.name()}
17
+ </a>
18
+ )
19
+ }
20
+
21
+ const ApiMakerTableFiltersLoadSearchModal = (props) => {
22
+ const {className, currentUser, modelClass, onRequestClose, querySearchName, ...restProps} = props
23
+ const Modal = apiMakerConfig.getModal()
24
+ const [searches, setSearches] = useState(undefined)
25
+
26
+ const loadSearches = async () => {
27
+ const userType = digg(currentUser.modelClassData(), "name")
28
+ const searches = await TableSearch
29
+ .ransack({user_id_eq: currentUser.id(), user_type_eq: userType})
30
+ .toArray()
31
+
32
+ setSearches(searches)
33
+ }
34
+
35
+ const onSearchClicked = useCallback(({search}) => {
36
+ const queryParams = search.queryParams()
37
+ const newParams = {}
38
+
39
+ newParams[querySearchName] = queryParams.map((queryParam) => JSON.stringify(queryParam))
40
+
41
+ Params.changeParams(newParams)
42
+
43
+ onRequestClose()
44
+ })
45
+
46
+ useEffect(() => { loadSearches() }, [])
47
+
48
+ return (
49
+ <Modal className={classNames("api-maker--table--filters--load-search-modal", className)} onRequestClose={onRequestClose} {...restProps}>
50
+ <div>
51
+ {I18n.t("js.api_maker.table.filters.load_search_modal.choose_a_search", {defaultValue: "Choose a search"})}
52
+ </div>
53
+ {searches?.map((search) =>
54
+ <SearchLink key={search.id()} onClick={onSearchClicked} search={search} />
55
+ )}
56
+ </Modal>
57
+ )
58
+ }
59
+
60
+ export default ApiMakerTableFiltersLoadSearchModal
@@ -0,0 +1,51 @@
1
+ import apiMakerConfig from "@kaspernj/api-maker/src/config.mjs"
2
+ import {digg} from "diggerize"
3
+ import Input from "../../bootstrap/input"
4
+ import {TableSearch} from "../../models.mjs.erb"
5
+ import {useCallback} from "react"
6
+
7
+ const ApiMakerTableFiltersSaveSearchModal = (props) => {
8
+ const {currentFilters, currentUser, onRequestClose, ...restProps} = props
9
+ const Modal = apiMakerConfig.getModal()
10
+
11
+ const onSaveSearchSubmit = useCallback(async (e) => {
12
+ e.preventDefault()
13
+
14
+ const form = digg(e, "target")
15
+ const formData = new FormData(form)
16
+ const tableSearch = new TableSearch()
17
+
18
+ formData.append("table_search[query_params]", JSON.stringify(currentFilters()))
19
+ formData.append("table_search[user_type]", digg(currentUser.modelClassData(), "className"))
20
+ formData.append("table_search[user_id]", currentUser.id())
21
+
22
+ try {
23
+ await tableSearch.saveRaw(formData, {form})
24
+ onRequestClose()
25
+ } catch (error) {
26
+ FlashMessage.errorResponse(error)
27
+ }
28
+ })
29
+
30
+ return (
31
+ <Modal onRequestClose={onRequestClose} {...restProps}>
32
+ <form onSubmit={onSaveSearchSubmit}>
33
+ <Input
34
+ id="table_search_name"
35
+ label={I18n.t("js.api_maker.table.filters.save_search_modal.search_name", {defaultValue: "Search name"})}
36
+ name="table_search[name]"
37
+ />
38
+ <Checkbox
39
+ id="table_search_public"
40
+ label={I18n.t("js.api_maker.table.filters.save_search_modal.public", {defaultValue: "Public"})}
41
+ name="table_search[public]"
42
+ />
43
+ <button className="save-search-submit-button">
44
+ {I18n.t("js.api_maker.table.filters.save_search_modal.save_search", {defaultValue: "Save search"})}
45
+ </button>
46
+ </form>
47
+ </Modal>
48
+ )
49
+ }
50
+
51
+ export default ApiMakerTableFiltersSaveSearchModal
@@ -34,7 +34,7 @@ class ColumnRow extends React.PureComponent {
34
34
 
35
35
  return (
36
36
  <div className="api-maker--table--settings--column-row">
37
- <label>
37
+ <label style={{whiteSpace: "nowrap"}}>
38
38
  <input
39
39
  className="api-maker--table--setings--column-checkbox"
40
40
  data-identifier={columnIdentifier(column)}
@@ -157,7 +157,7 @@ class ApiMakerTable extends React.PureComponent {
157
157
 
158
158
  render () {
159
159
  const {modelClass, noRecordsAvailableContent, noRecordsFoundContent} = digs(this.props, "modelClass", "noRecordsAvailableContent", "noRecordsFoundContent")
160
- const {collection, defaultParams, onModelsLoaded, selectColumns} = this.props
160
+ const {collection, currentUser, defaultParams, onModelsLoaded, selectColumns} = this.props
161
161
  const {
162
162
  overallCount,
163
163
  preload,
@@ -221,7 +221,7 @@ class ApiMakerTable extends React.PureComponent {
221
221
  </div>
222
222
  }
223
223
  {showFilters &&
224
- <Filters modelClass={modelClass} queryName={queryName} querySName={querySName} />
224
+ <Filters currentUser={currentUser} modelClass={modelClass} queryName={queryName} querySName={querySName} />
225
225
  }
226
226
  {qParams && query && result && models && !showNoRecordsAvailableContent && !showNoRecordsFoundContent &&
227
227
  this.cardOrTable()