@kaspernj/api-maker 1.0.236 → 1.0.238

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/.eslintrc.cjs CHANGED
@@ -100,6 +100,7 @@ module.exports = {
100
100
  "line-comment-position": "off",
101
101
  "lines-around-comment": "error",
102
102
  "lines-between-class-members": "off",
103
+ "logical-assignment-operators": "error",
103
104
  "max-classes-per-file": "off",
104
105
  "max-depth": "error",
105
106
  "max-nested-callbacks": "error",
package/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  ]
17
17
  },
18
18
  "name": "@kaspernj/api-maker",
19
- "version": "1.0.236",
19
+ "version": "1.0.238",
20
20
  "type": "module",
21
21
  "description": "",
22
22
  "main": "index.js",
@@ -26,12 +26,13 @@ class ApiMakerBootstrapSortLink extends PureComponent {
26
26
  }
27
27
 
28
28
  href () {
29
+ const qParams = this.qParams()
29
30
  const {queryParams} = digs(this.props, "queryParams")
30
31
  const {searchKey} = digs(this, "searchKey")
31
32
 
32
- if (!queryParams[searchKey]) queryParams[searchKey] = {}
33
+ qParams.s = `${this.attribute()} ${this.sortMode()}` // eslint-disable-line id-length
33
34
 
34
- queryParams[searchKey].s = `${this.attribute()} ${this.sortMode()}` // eslint-disable-line id-length
35
+ queryParams[searchKey] = JSON.stringify(qParams)
35
36
 
36
37
  const newParams = qs.stringify(queryParams)
37
38
  const newPath = `${location.pathname}?${newParams}`
@@ -40,9 +41,7 @@ class ApiMakerBootstrapSortLink extends PureComponent {
40
41
  }
41
42
 
42
43
  isSortedByAttribute () {
43
- const {queryParams} = digs(this.props, "queryParams")
44
- const {searchKey} = digs(this, "searchKey")
45
- const params = queryParams[searchKey] || {}
44
+ const params = this.qParams()
46
45
 
47
46
  if (params.s == this.attribute()) return true
48
47
  if (params.s == `${this.attribute()} asc`) return true
@@ -83,6 +82,15 @@ class ApiMakerBootstrapSortLink extends PureComponent {
83
82
  return Link
84
83
  }
85
84
 
85
+ qParams() {
86
+ const {queryParams} = digs(this.props, "queryParams")
87
+ const {searchKey} = digs(this, "searchKey")
88
+
89
+ if (searchKey in queryParams) return JSON.parse(queryParams[searchKey])
90
+
91
+ return {}
92
+ }
93
+
86
94
  sortMode () {
87
95
  if (this.isSortedByAttribute()) return "desc"
88
96
 
@@ -4,12 +4,11 @@ import {digg, digs} from "diggerize"
4
4
  import EventCreated from "./event-created"
5
5
  import EventDestroyed from "./event-destroyed"
6
6
  import EventUpdated from "./event-updated"
7
- import {LocationChanged} from "on-location-changed/src/location-changed-component"
8
- import Params from "./params"
9
7
  import PropTypes from "prop-types"
10
8
  import React from "react"
9
+ import withQueryParams from "on-location-changed/src/with-query-params"
11
10
 
12
- export default class CollectionLoader extends React.PureComponent {
11
+ class CollectionLoader extends React.PureComponent {
13
12
  static defaultProps = {
14
13
  destroyEnabled: true,
15
14
  groupBy: ["id"],
@@ -52,6 +51,7 @@ export default class CollectionLoader extends React.PureComponent {
52
51
  overallCount: undefined,
53
52
  query: undefined,
54
53
  queryName,
54
+ queryPerKey: `${queryName}_per`,
55
55
  queryQName: `${queryName}_q`,
56
56
  querySName: `${queryName}_s`,
57
57
  queryPageName: `${queryName}_page`,
@@ -72,6 +72,27 @@ export default class CollectionLoader extends React.PureComponent {
72
72
  if (noRecordsAvailableContent) this.loadOverallCount()
73
73
  }
74
74
 
75
+ componentDidUpdate(prevProps) {
76
+ const {queryPageName, queryPerKey, queryQName, querySName} = digs(this.shape, "queryPageName", "queryPerKey", "queryQName", "querySName")
77
+ let changed = false
78
+
79
+ // Only load models again if certain things in the URL changes
80
+ if (prevProps.queryParams[queryQName] != this.props.queryParams[queryQName]) {
81
+ changed = true
82
+ } else if (prevProps.queryParams[queryPageName] != this.props.queryParams[queryPageName]) {
83
+ changed = true
84
+ } else if (prevProps.queryParams[queryPerKey] != this.props.queryParams[queryPerKey]) {
85
+ changed = true
86
+ } else if (prevProps.queryParams[querySName] != this.props.queryParams[querySName]) {
87
+ changed = true
88
+ }
89
+
90
+ if (changed) {
91
+ this.loadQParams()
92
+ this.loadModels()
93
+ }
94
+ }
95
+
75
96
  async loadOverallCount () {
76
97
  const baseQuery = this.props.collection || this.props.modelClass.all()
77
98
  const overallCount = await baseQuery.count()
@@ -83,14 +104,32 @@ export default class CollectionLoader extends React.PureComponent {
83
104
  })
84
105
  }
85
106
 
107
+ hasQParams() {
108
+ const {queryParams} = digs(this.props, "queryParams")
109
+ const {queryQName} = digs(this.shape, "queryQName")
110
+
111
+ if (queryQName in queryParams) return true
112
+
113
+ return false
114
+ }
115
+
116
+ qParams() {
117
+ const {queryParams} = digs(this.props, "queryParams")
118
+ const {queryQName} = digs(this.shape, "queryQName")
119
+
120
+ if (this.hasQParams()) return JSON.parse(digg(queryParams, queryQName))
121
+
122
+ return {}
123
+ }
124
+
86
125
  loadQParams () {
87
- const {queryQName, querySName} = digs(this.shape, "queryQName", "querySName")
88
- const params = Params.parse()
89
- const qParams = Object.assign({}, this.props.defaultParams, params[queryQName])
126
+ const {queryParams} = digs(this.props, "queryParams")
127
+ const {querySName} = digs(this.shape, "querySName")
128
+ const qParams = this.hasQParams() ? this.qParams() : Object.assign({}, this.props.defaultParams)
90
129
  const searchParams = []
91
130
 
92
- if (params[querySName]) {
93
- for (const rawSearchParam of params[querySName]) {
131
+ if (queryParams[querySName]) {
132
+ for (const rawSearchParam of queryParams[querySName]) {
94
133
  const parsedSearchParam = JSON.parse(rawSearchParam)
95
134
 
96
135
  searchParams.push(parsedSearchParam)
@@ -101,24 +140,24 @@ export default class CollectionLoader extends React.PureComponent {
101
140
  }
102
141
 
103
142
  loadModels = async () => {
104
- const params = Params.parse()
143
+ const {queryParams} = digs(this.props, "queryParams")
105
144
  const {abilities, collection, groupBy, modelClass, onModelsLoaded, preloads, select, selectColumns} = this.props
106
145
  const {
107
146
  qParams,
108
- queryName,
109
147
  queryPageName,
148
+ queryPerKey,
110
149
  queryQName,
111
150
  searchParams
112
151
  } = digs(
113
152
  this.shape,
114
153
  "qParams",
115
- "queryName",
116
154
  "queryPageName",
155
+ "queryPerKey",
117
156
  "queryQName",
118
157
  "searchParams"
119
158
  )
120
- const perKey = `${queryName}_per`
121
- let per = params[perKey] || 30
159
+ const page = queryParams[queryPageName] || 1
160
+ let per = queryParams[queryPerKey] || 30
122
161
 
123
162
  if (per == "all") {
124
163
  per = 999_999_999
@@ -134,10 +173,10 @@ export default class CollectionLoader extends React.PureComponent {
134
173
  .ransack(qParams)
135
174
  .search(searchParams)
136
175
  .searchKey(queryQName)
137
- .page(params[queryPageName])
176
+ .page(page)
138
177
  .pageKey(queryPageName)
139
178
  .per(per)
140
- .perKey(perKey)
179
+ .perKey(queryPerKey)
141
180
  .preload(preloads)
142
181
  .select(select)
143
182
 
@@ -184,11 +223,6 @@ export default class CollectionLoader extends React.PureComponent {
184
223
  if (foundModel) this.loadModelsDebounce()
185
224
  }
186
225
 
187
- onLocationChanged = () => {
188
- this.loadQParams()
189
- this.loadModels()
190
- }
191
-
192
226
  showNoRecordsAvailableContent (args) {
193
227
  const {noRecordsAvailableContent} = digs(this.props, "noRecordsAvailableContent")
194
228
  let models, overallCount
@@ -239,7 +273,6 @@ export default class CollectionLoader extends React.PureComponent {
239
273
  return (
240
274
  <>
241
275
  <EventCreated modelClass={modelClass} onCreated={digg(this, "onModelCreated")} />
242
- <LocationChanged onChanged={digg(this, "onLocationChanged")} />
243
276
  {models && models.map((model) =>
244
277
  <React.Fragment key={model.id()}>
245
278
  <EventDestroyed model={model} onDestroyed={digg(this, "onModelDestroyed")} />
@@ -250,3 +283,5 @@ export default class CollectionLoader extends React.PureComponent {
250
283
  )
251
284
  }
252
285
  }
286
+
287
+ export default withQueryParams(CollectionLoader)
@@ -16,7 +16,7 @@ export default class Serializer {
16
16
  }
17
17
 
18
18
  serializeArgument (arg) {
19
- if (typeof arg == "object" && arg.constructor.apiMakerType == "BaseModel") {
19
+ if (typeof arg == "object" && arg && arg.constructor.apiMakerType == "BaseModel") {
20
20
  return {
21
21
  api_maker_type: "model",
22
22
  model_class_name: digg(arg.modelClassData(), "name"),
@@ -44,7 +44,7 @@ export default class Serializer {
44
44
  }
45
45
  } else if (Array.isArray(arg)) {
46
46
  return this.serializeArray(arg)
47
- } else if (typeof arg == "object" && arg.constructor && arg.constructor.apiMakerType == "Collection") {
47
+ } else if (typeof arg == "object" && arg && arg.constructor && arg.constructor.apiMakerType == "Collection") {
48
48
  return {
49
49
  api_maker_type: "collection",
50
50
  value: this.serializeObject(arg)
@@ -57,6 +57,7 @@ class ApiMakerTableFilters extends React.PureComponent {
57
57
  static propTypes = {
58
58
  modelClass: PropTypes.func.isRequired,
59
59
  queryName: PropTypes.string.isRequired,
60
+ querySName: PropTypes.string.isRequired,
60
61
  queryParams: PropTypes.object.isRequired
61
62
  }
62
63
 
@@ -65,7 +66,7 @@ class ApiMakerTableFilters extends React.PureComponent {
65
66
  })
66
67
 
67
68
  render() {
68
- const {modelClass} = this.props
69
+ const {modelClass, querySName} = digs(this.props, "modelClass", "querySName")
69
70
  const {filter} = digs(this.shape, "filter")
70
71
  const currentFilters = this.currentFilters()
71
72
 
@@ -80,7 +81,7 @@ class ApiMakerTableFilters extends React.PureComponent {
80
81
  key={`filter-${filter.filterIndex}`}
81
82
  modelClass={modelClass}
82
83
  onApplyClicked={digg(this, "onApplyClicked")}
83
- querySearchName={this.querySearchName()}
84
+ querySearchName={querySName}
84
85
  />
85
86
  }
86
87
  {currentFilters?.map((filterData, filterIndex) =>
@@ -97,8 +98,8 @@ class ApiMakerTableFilters extends React.PureComponent {
97
98
  }
98
99
 
99
100
  currentFilters() {
100
- const {queryParams} = this.props
101
- const currentFilters = queryParams[this.querySearchName()] || []
101
+ const {queryParams, querySName} = digs(this.props, "queryParams", "querySName")
102
+ const currentFilters = queryParams[querySName] || []
102
103
 
103
104
  return currentFilters
104
105
  }
@@ -118,13 +119,14 @@ class ApiMakerTableFilters extends React.PureComponent {
118
119
  onApplyClicked = () => this.shape.set({filter: undefined})
119
120
 
120
121
  onRemoveClicked = ({filterIndex}) => {
121
- const searchParams = Params.parse()[this.querySearchName()] || {}
122
+ const {querySName} = digs(this.props, "querySName")
123
+ const searchParams = Params.parse()[querySName] || {}
122
124
 
123
125
  delete searchParams[filterIndex]
124
126
 
125
127
  const newParams = {}
126
128
 
127
- newParams[this.querySearchName()] = searchParams
129
+ newParams[querySName] = searchParams
128
130
 
129
131
  Params.changeParams(newParams)
130
132
 
@@ -134,7 +136,6 @@ class ApiMakerTableFilters extends React.PureComponent {
134
136
  }
135
137
 
136
138
  onFilterClicked = (args) => this.shape.set({filter: args})
137
- querySearchName = () => `${this.props.queryName}_s`
138
139
  }
139
140
 
140
141
  export default withQueryParams(ApiMakerTableFilters)
@@ -4,6 +4,7 @@ import {digg} from "diggerize"
4
4
  import inflection from "inflection"
5
5
  import {serialize as objectToFormData} from "object-to-formdata"
6
6
  import {TableSetting} from "../models.mjs.erb"
7
+ import {v4 as uuidv4} from "uuid"
7
8
 
8
9
  export default class ApiMakerTableSettings {
9
10
  constructor({table}) {
@@ -62,13 +63,13 @@ export default class ApiMakerTableSettings {
62
63
  }
63
64
 
64
65
  loadTableSetting = async () => {
65
- if (!TableSetting) throw new Error("TableSetting model couldn't be imported")
66
+ if (!TableSetting) throw new Error("TableSetting model isn't globally available")
66
67
 
67
68
  const tableSetting = await TableSetting
68
69
  .ransack({
69
70
  identifier_eq: this.identifier(),
70
- user_id_eq: this.currentUser().id(),
71
- user_type_eq: digg(this.currentUser().modelClassData(), "name")
71
+ user_id_eq: this.currentUserIdOrFallback(),
72
+ user_type_eq: this.currentUserTypeOrFallback()
72
73
  })
73
74
  .preload("columns")
74
75
  .first()
@@ -76,11 +77,39 @@ export default class ApiMakerTableSettings {
76
77
  return tableSetting
77
78
  }
78
79
 
80
+ currentUserIdOrFallback() {
81
+ const currentUser = this.currentUser()
82
+
83
+ if (currentUser) return currentUser.id()
84
+
85
+ return this.anonymouseUserId()
86
+ }
87
+
88
+ currentUserTypeOrFallback() {
89
+ const currentUser = this.currentUser()
90
+
91
+ if (currentUser) return digg(currentUser.modelClassData(), "name")
92
+
93
+ return null
94
+ }
95
+
96
+ anonymouseUserId() {
97
+ const variableName = `ApiMakerTableAnonymousUserId-${this.identifier()}`
98
+
99
+ if (!(variableName in localStorage)) {
100
+ const generatedId = uuidv4()
101
+
102
+ localStorage[variableName] = generatedId
103
+ }
104
+
105
+ return digg(localStorage, variableName)
106
+ }
107
+
79
108
  createInitialTableSetting = async () => {
80
109
  const tableSettingData = {
81
110
  identifier: this.identifier(),
82
- user_id: this.currentUser().id(),
83
- user_type: digg(this.currentUser().modelClassData(), "name"),
111
+ user_id: this.currentUserIdOrFallback(),
112
+ user_type: this.currentUserTypeOrFallback(),
84
113
  columns_attributes: {}
85
114
  }
86
115
 
@@ -116,9 +145,6 @@ export default class ApiMakerTableSettings {
116
145
  }
117
146
 
118
147
  updateTableSetting = async (tableSetting) => {
119
- // This should remove columns no longer found
120
- // This should update columns that have changed
121
-
122
148
  const columns = this.columns()
123
149
  const columnsData = {}
124
150
  const tableSettingData = {columns_attributes: columnsData}
@@ -149,7 +175,7 @@ export default class ApiMakerTableSettings {
149
175
  const column = columns.find((column) => columnIdentifier(column) == tableSettingColumn.identifier())
150
176
 
151
177
  if (column) {
152
- // Update column if changed
178
+ // TODO: Update column if changed
153
179
  } else {
154
180
  // Removed saved columns no longer found
155
181
  const columnKey = ++columnsKeyCount
@@ -158,6 +184,7 @@ export default class ApiMakerTableSettings {
158
184
  id: tableSettingColumn.id(),
159
185
  _destroy: true
160
186
  }
187
+ changed = true
161
188
  }
162
189
  }
163
190
 
@@ -165,9 +192,6 @@ export default class ApiMakerTableSettings {
165
192
  const tableSettingFormData = objectToFormData({table_setting: tableSettingData})
166
193
 
167
194
  await tableSetting.saveRaw(tableSettingFormData)
168
-
169
- // Maybe not necessary?
170
- // tableSetting = this.loadTableSetting()
171
195
  }
172
196
 
173
197
  return tableSetting
@@ -96,6 +96,7 @@ class ApiMakerTable extends React.PureComponent {
96
96
  queryName,
97
97
  queryQName: `${queryName}_q`,
98
98
  queryPageName: `${queryName}_page`,
99
+ querySName: `${queryName}_s`,
99
100
  qParams: undefined,
100
101
  result: undefined,
101
102
  showFilters: false,
@@ -143,6 +144,7 @@ class ApiMakerTable extends React.PureComponent {
143
144
  qParams,
144
145
  query,
145
146
  queryName,
147
+ querySName,
146
148
  result,
147
149
  models,
148
150
  showFilters,
@@ -155,6 +157,7 @@ class ApiMakerTable extends React.PureComponent {
155
157
  "qParams",
156
158
  "query",
157
159
  "queryName",
160
+ "querySName",
158
161
  "result",
159
162
  "models",
160
163
  "showFilters",
@@ -189,7 +192,7 @@ class ApiMakerTable extends React.PureComponent {
189
192
  </div>
190
193
  }
191
194
  {showFilters &&
192
- <Filters modelClass={modelClass} queryName={queryName} />
195
+ <Filters modelClass={modelClass} queryName={queryName} querySName={querySName} />
193
196
  }
194
197
  {qParams && query && result && models && !showNoRecordsAvailableContent && !showNoRecordsFoundContent &&
195
198
  this.cardOrTable()
@@ -546,10 +549,10 @@ class ApiMakerTable extends React.PureComponent {
546
549
  const filterForm = digg(filterFormRef, "current")
547
550
  const {appHistory} = this.props
548
551
  const qParams = Params.serializeForm(filterForm)
549
- const {queryQName} = this.shape
552
+ const {queryQName} = digs(this.shape, "queryQName")
550
553
  const changeParamsParams = {}
551
554
 
552
- changeParamsParams[queryQName] = qParams
555
+ changeParamsParams[queryQName] = JSON.stringify(qParams)
553
556
 
554
557
  Params.changeParams(changeParamsParams, {appHistory})
555
558
  }