@kaspernj/api-maker 1.0.384 → 1.0.386

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaspernj/api-maker",
3
- "version": "1.0.384",
3
+ "version": "1.0.386",
4
4
  "type": "module",
5
5
  "description": "",
6
6
  "main": "index.js",
@@ -0,0 +1,35 @@
1
+ import BaseComponent from "../../base-component"
2
+ import {memo} from "react"
3
+ import {shapeComponent} from "set-state-compare/src/shape-component.js"
4
+
5
+ export default memo(shapeComponent(class EditAttributeContent extends BaseComponent {
6
+ render() {
7
+ const {attribute, id, inputs, model, name} = this.props
8
+
9
+ if (!(attribute.attribute in model)) {
10
+ throw new Error(`${attribute.attribute} isn't set on the resource ${model.modelClassData().name}`)
11
+ }
12
+
13
+ const defaultValue = useCallback(() => model[attribute.attribute]() || "")
14
+ const [value, setValue] = useState(() => defaultValue())
15
+ const onChangeValue = useCallback((newValue) => {
16
+ inputs[name] = newValue
17
+ setValue(newValue)
18
+ })
19
+ useMemo(() => {
20
+ inputs[name] = value
21
+ }, [])
22
+
23
+ const contentArgs = () => ({
24
+ inputProps: {
25
+ attribute: attribute.attribute,
26
+ defaultValue: defaultValue(),
27
+ id,
28
+ model
29
+ },
30
+ onChangeValue
31
+ })
32
+
33
+ return attribute.content(contentArgs())
34
+ }
35
+ }))
@@ -0,0 +1,61 @@
1
+ import BaseComponent from "../../base-component"
2
+ import PropTypes from "prop-types"
3
+ import propTypesExact from "prop-types-exact"
4
+ import {shapeComponent} from "set-state-compare/src/shape-component.js"
5
+ import {Text, TextInput, View} from "react-native"
6
+ import {memo, useMemo} from "react"
7
+
8
+ export default memo(shapeComponent(class EditAttributeInput extends BaseComponent {
9
+ static propTypes = propTypesExact({
10
+ inputs: PropTypes.object.isRequired,
11
+ name: PropTypes.string.isRequired
12
+ })
13
+
14
+ setup() {
15
+ const {inputs, name} = this.p
16
+
17
+ this.useStates({
18
+ value: this.defaultValue()
19
+ })
20
+
21
+ useMemo(() => {
22
+ inputs[name] = this.s.value
23
+ }, [])
24
+ }
25
+
26
+ defaultValue = () => this.p.model[this.p.attributeName]() || ""
27
+
28
+ render() {
29
+ const {attributeName, id, label, model, name} = this.p
30
+ const {value} = this.s
31
+
32
+ if (!(attributeName in model)) {
33
+ throw new Error(`${attributeName} isn't set on the resource ${model.modelClassData().name}`)
34
+ }
35
+
36
+ return (
37
+ <View style={{marginBottom: 12}}>
38
+ <Text>{label}</Text>
39
+ <View>
40
+ <TextInput
41
+ dataSet={{
42
+ attribute: attributeName,
43
+ id,
44
+ name
45
+ }}
46
+ onChangeText={this.tt.onChangeText}
47
+ style={{paddingTop: 9, paddingRight: 13, paddingBottom: 9, paddingLeft: 13, borderRadius: 5, backgroundColor: "#fff", border: "1px solid #cecece"}}
48
+ value={value}
49
+ />
50
+ </View>
51
+ </View>
52
+ )
53
+ }
54
+
55
+ onChangeText = (newValue) => {
56
+ const {inputs, name} = this.p
57
+
58
+ inputs[name] = newValue
59
+ this.setState({value: newValue})
60
+ }
61
+ }))
@@ -0,0 +1,51 @@
1
+ import BaseComponent from "../../base-component"
2
+ import {digg} from "diggerize"
3
+ import EditAttributeContent from "./edit-attribute-content"
4
+ import EditAttributeInput from "./edit-attribute-input"
5
+ import * as inflection from "inflection"
6
+ import Locales from "shared/locales"
7
+ import {memo} from "react"
8
+ import {shapeComponent} from "set-state-compare/src/shape-component.js"
9
+
10
+ export default memo(shapeComponent(class EditAttribute extends BaseComponent {
11
+ render() {
12
+ const {attribute, inputs, model, modelClass} = this.props
13
+ const availableLocales = Locales.availableLocales()
14
+ const camelizedLower = digg(modelClass.modelClassData(), "camelizedLower")
15
+
16
+ return (
17
+ <>
18
+ {attribute.content &&
19
+ <EditAttributeContent
20
+ attribute={attribute}
21
+ id={`${inflection.underscore(camelizedLower)}_${inflection.underscore(attribute.attribute)}`}
22
+ inputs={inputs}
23
+ model={model}
24
+ name={inflection.underscore(attribute.attribute)}
25
+ />
26
+ }
27
+ {!attribute.content && attribute.translated && availableLocales.map((locale) =>
28
+ <EditAttributeInput
29
+ attributeName={`${attribute.attribute}${inflection.camelize(locale)}`}
30
+ id={`${inflection.underscore(camelizedLower)}_${inflection.underscore(attribute.attribute)}_${locale}`}
31
+ inputs={inputs}
32
+ label={`${modelClass.humanAttributeName(attribute.attribute)} (${locale})`}
33
+ model={model}
34
+ name={`${inflection.underscore(attribute.attribute)}_${locale}`}
35
+ key={locale}
36
+ />
37
+ )}
38
+ {!attribute.content && !attribute.translated &&
39
+ <EditAttributeInput
40
+ attributeName={attribute.attribute}
41
+ id={`${inflection.underscore(camelizedLower)}_${inflection.underscore(attribute.attribute)}`}
42
+ inputs={inputs}
43
+ label={modelClass.humanAttributeName(attribute.attribute)}
44
+ model={model}
45
+ name={inflection.underscore(attribute.attribute)}
46
+ />
47
+ }
48
+ </>
49
+ )
50
+ }
51
+ }))
@@ -1,196 +1,109 @@
1
+ import BaseComponent from "../base-component"
1
2
  import ConfigReader from "./config-reader.jsx"
2
3
  import {digg} from "diggerize"
4
+ import EditAttribute from "./edit-page/edit-attribute"
3
5
  import * as inflection from "inflection"
4
- import {Pressable, Text, TextInput, View} from "react-native"
6
+ import {Pressable, Text, View} from "react-native"
5
7
  import Locales from "shared/locales"
6
- import {memo, useCallback, useMemo, useState} from "react"
8
+ import {memo} from "react"
9
+ import PropTypes from "prop-types"
10
+ import propTypesExact from "prop-types-exact"
11
+ import {shapeComponent} from "set-state-compare/src/shape-component.js"
7
12
  import useCurrentUser from "../use-current-user"
8
13
  import useModel from "../use-model"
9
14
  import useQueryParams from "on-location-changed/src/use-query-params"
10
15
 
11
- const EditAttributeInput = memo(({attributeName, id, inputs, label, model, name}) => {
12
- if (!(attributeName in model)) {
13
- throw new Error(`${attributeName} isn't set on the resource ${model.modelClassData().name}`)
14
- }
15
-
16
- const defaultValue = useCallback(() => model[attributeName]() || "")
17
- const [value, setValue] = useState(() => defaultValue())
18
-
19
- useMemo(() => {
20
- inputs[name] = value
21
- }, [])
22
-
23
- const onChangeText = useCallback((newValue) => {
24
- inputs[name] = newValue
25
- setValue(newValue)
26
- }, [])
27
-
28
- return (
29
- <View style={{marginBottom: 12}}>
30
- <Text>{label}</Text>
31
- <View>
32
- <TextInput
33
- dataSet={{
34
- attribute: attributeName,
35
- id,
36
- name
37
- }}
38
- onChangeText={onChangeText}
39
- style={{paddingTop: 9, paddingRight: 13, paddingBottom: 9, paddingLeft: 13, borderRadius: 5, backgroundColor: "#fff", border: "1px solid #cecece"}}
40
- value={value}
41
- />
42
- </View>
43
- </View>
44
- )
45
- })
46
-
47
- const EditAttributeContent = memo(({attribute, id, inputs, model, name}) => {
48
- if (!(attribute.attribute in model)) {
49
- throw new Error(`${attribute.attribute} isn't set on the resource ${model.modelClassData().name}`)
50
- }
51
-
52
- const defaultValue = useCallback(() => model[attribute.attribute]() || "")
53
- const [value, setValue] = useState(() => defaultValue())
54
- const onChangeValue = useCallback((newValue) => {
55
- inputs[name] = newValue
56
- setValue(newValue)
57
- })
58
- useMemo(() => {
59
- inputs[name] = value
60
- }, [])
61
-
62
- const contentArgs = () => ({
63
- inputProps: {
64
- attribute: attribute.attribute,
65
- defaultValue: defaultValue(),
66
- id,
67
- model
68
- },
69
- onChangeValue
16
+ export default memo(shapeComponent(class ApiMakerSuperAdminEditPage extends BaseComponent {
17
+ static propTypes = propTypesExact({
18
+ modelClass: PropTypes.func.isRequired
70
19
  })
71
20
 
72
- return attribute.content(contentArgs())
73
- })
74
-
75
- const EditAttribute = memo(({attribute, inputs, model, modelClass}) => {
76
- const availableLocales = Locales.availableLocales()
77
- const camelizedLower = digg(modelClass.modelClassData(), "camelizedLower")
21
+ inputs = {}
22
+
23
+ setup() {
24
+ const {modelClass} = this.p
25
+ const modelClassName = modelClass.modelClassData().name
26
+ const availableLocales = Locales.availableLocales()
27
+ this.configReader = ConfigReader.forModel(modelClass)
28
+ const currentUser = useCurrentUser()
29
+ const queryParams = useQueryParams()
30
+ const selectedModelAttributes = ["id"]
31
+ const selectedAttributes = {}
32
+ this.attributes = this.configReader.modelConfig?.edit?.attributes
33
+
34
+ if (!this.attributes) {
35
+ throw new Error(`No 'attributes' given from edit config for ${modelClass.modelClassData().name}`)
36
+ }
78
37
 
79
- return (
80
- <>
81
- {attribute.content &&
82
- <EditAttributeContent
83
- attribute={attribute}
84
- id={`${inflection.underscore(camelizedLower)}_${inflection.underscore(attribute.attribute)}`}
85
- inputs={inputs}
86
- model={model}
87
- name={inflection.underscore(attribute.attribute)}
88
- />
89
- }
90
- {!attribute.content && attribute.translated && availableLocales.map((locale) =>
91
- <EditAttributeInput
92
- attributeName={`${attribute.attribute}${inflection.camelize(locale)}`}
93
- id={`${inflection.underscore(camelizedLower)}_${inflection.underscore(attribute.attribute)}_${locale}`}
94
- inputs={inputs}
95
- label={`${modelClass.humanAttributeName(attribute.attribute)} (${locale})`}
96
- model={model}
97
- name={`${inflection.underscore(attribute.attribute)}_${locale}`}
98
- key={locale}
99
- />
100
- )}
101
- {!attribute.content && !attribute.translated &&
102
- <EditAttributeInput
103
- attributeName={attribute.attribute}
104
- id={`${inflection.underscore(camelizedLower)}_${inflection.underscore(attribute.attribute)}`}
105
- inputs={inputs}
106
- label={modelClass.humanAttributeName(attribute.attribute)}
107
- model={model}
108
- name={inflection.underscore(attribute.attribute)}
109
- />
38
+ for (const attribute of this.attributes) {
39
+ if (attribute.translated) {
40
+ for (const locale of availableLocales) {
41
+ selectedModelAttributes.push(`${attribute.attribute}${inflection.camelize(locale)}`)
42
+ }
43
+ } else {
44
+ selectedModelAttributes.push(attribute.attribute)
110
45
  }
111
- </>
112
- )
113
- })
46
+ }
114
47
 
115
- const EditPage = ({modelClass}) => {
116
- const availableLocales = Locales.availableLocales()
117
- const currentUser = useCurrentUser()
118
- const queryParams = useQueryParams()
119
- const configReader = ConfigReader.forModel(modelClass)
120
- const inputs = useMemo(() => ({}))
121
- const modelClassName = modelClass.modelClassData().name
122
- const modelIdVarName = `${inflection.camelize(modelClass.modelClassData().name, true)}Id`
123
- const modelVarName = inflection.camelize(modelClass.modelClassData().name, true)
124
- const extraContent = configReader.modelConfig?.edit?.extraContentconst
125
- const attributes = configReader.modelConfig?.edit?.attributes
126
- const selectedModelAttributes = ["id"]
127
- const selectedAttributes = {}
48
+ selectedAttributes[modelClassName] = selectedModelAttributes
128
49
 
129
- selectedAttributes[modelClassName] = selectedModelAttributes
50
+ const useModelResult = useModel(modelClass, {
51
+ cacheArgs: [currentUser?.id()],
52
+ loadByQueryParam: (props) => props.queryParams.model_id,
53
+ newIfNoId: true,
54
+ select: selectedAttributes
55
+ })
130
56
 
131
- if (!attributes) {
132
- throw new Error(`No 'attributes' given from edit config for ${modelClass.modelClassData().name}`)
133
- }
57
+ const modelIdVarName = `${inflection.camelize(modelClass.modelClassData().name, true)}Id`
58
+ const modelVarName = inflection.camelize(modelClass.modelClassData().name, true)
134
59
 
135
- for (const attribute of attributes) {
136
- if (attribute.translated) {
137
- for (const locale of availableLocales) {
138
- selectedModelAttributes.push(`${attribute.attribute}${inflection.camelize(locale)}`)
139
- }
140
- } else {
141
- selectedModelAttributes.push(attribute.attribute)
142
- }
60
+ this.model = digg(useModelResult, modelVarName)
61
+ this.modelId = queryParams.model_id
62
+ this.modelArgs = {}
63
+ this.modelArgs[modelIdVarName] = this.modelId
143
64
  }
144
65
 
145
- const useModelResult = useModel(modelClass, {
146
- cacheArgs: [currentUser?.id()],
147
- loadByQueryParam: (props) => props.queryParams.model_id,
148
- newIfNoId: true,
149
- select: selectedAttributes
150
- })
151
-
152
- const model = digg(useModelResult, modelVarName)
153
- const modelId = queryParams.model_id
154
- const modelArgs = {}
155
-
156
- modelArgs[modelIdVarName] = modelId
66
+ render() {
67
+ const {attributes, model} = this.tt
68
+ const {modelClass} = this.p
69
+ const extraContent = this.configReader.modelConfig?.edit?.extraContentconst
70
+
71
+ return (
72
+ <View dataSet={{class: "super-admin--edit-page"}}>
73
+ {model && attributes?.map((attribute) =>
74
+ <EditAttribute attribute={attribute} inputs={this.inputs} key={attribute.attribute} model={model} modelClass={modelClass} />
75
+ )}
76
+ {extraContent && extraContent(modelArgs)}
77
+ <Pressable
78
+ dataSet={{class: "submit-button"}}
79
+ onPress={this.tt.onSubmit}
80
+ style={{
81
+ paddingTop: 18,
82
+ paddingRight: 24,
83
+ paddingBottom: 18,
84
+ paddingLeft: 24,
85
+ borderRadius: 10,
86
+ backgroundColor: "#4c93ff",
87
+ marginTop: 10
88
+ }}
89
+ >
90
+ <Text style={{color: "#fff"}}>
91
+ Submit
92
+ </Text>
93
+ </Pressable>
94
+ </View>
95
+ )
96
+ }
157
97
 
158
- const onSubmit = useCallback(async () => {
98
+ onSubmit = async () => {
159
99
  try {
160
- model.assignAttributes(inputs)
100
+ const {inputs, model} = this.tt
161
101
 
162
- await model.save(inputs)
102
+ model.assignAttributes(inputs)
103
+ await model.save(this.inputs)
163
104
  Params.changeParams({mode: undefined, model_id: model.id()})
164
105
  } catch (error) {
165
106
  FlashMessage.errorResponse(error)
166
107
  }
167
- }, [model])
168
-
169
- return (
170
- <View dataSet={{class: "super-admin--edit-page"}}>
171
- {model && attributes?.map((attribute) =>
172
- <EditAttribute attribute={attribute} inputs={inputs} key={attribute.attribute} model={model} modelClass={modelClass} />
173
- )}
174
- {extraContent && extraContent(modelArgs)}
175
- <Pressable
176
- dataSet={{class: "submit-button"}}
177
- onPress={onSubmit}
178
- style={{
179
- paddingTop: 18,
180
- paddingRight: 24,
181
- paddingBottom: 18,
182
- paddingLeft: 24,
183
- borderRadius: 10,
184
- backgroundColor: "#4c93ff",
185
- marginTop: "10px"
186
- }}
187
- >
188
- <Text style={{color: "#fff"}}>
189
- Submit
190
- </Text>
191
- </Pressable>
192
- </View>
193
- )
194
- }
195
-
196
- export default memo(EditPage)
108
+ }
109
+ }))
@@ -1,55 +1,64 @@
1
1
  import "./style"
2
+ import BaseComponent from "../../../base-component"
2
3
  import EventListener from "../../../event-listener"
4
+ import {memo, useRef} from "react"
3
5
  import PropTypes from "prop-types"
4
6
  import PropTypesExact from "prop-types-exact"
5
- import {memo, useCallback, useRef} from "react"
6
- import useShape from "set-state-compare/src/use-shape.js"
7
+ import {shapeComponent} from "set-state-compare/src/shape-component.js"
7
8
 
8
- const ApiMakerSuperAdminLayoutHeader = ({actions, onTriggerMenu, title}) => {
9
- const s = useShape()
10
- const headerActionsRef = useRef()
11
- const setHeaderActionsActive = s.useState("headerActionsActive", false)
12
- const onGearsClicked = useCallback((e) => {
13
- e.preventDefault()
14
- setHeaderActionsActive(!s.state.headerActionsActive)
15
- }, [])
9
+ export default memo(shapeComponent(class ApiMakerSuperAdminLayoutHeader extends BaseComponent {
10
+ static propTypes = PropTypesExact({
11
+ actions: PropTypes.node,
12
+ onTriggerMenu: PropTypes.func.isRequired,
13
+ title: PropTypes.string
14
+ })
16
15
 
17
- const onWindowMouseUp = useCallback((e) => {
18
- // Close the header actions menu if clicked happened outside
19
- if (s.state.headerActionsActive && headerActionsRef.current && !headerActionsRef.current.contains(e.target)) setHeaderActionsActive(false)
20
- }, [])
16
+ setup() {
17
+ this.headerActionsRef = useRef()
18
+ this.useStates({
19
+ headerActionsActive: false
20
+ })
21
+ }
21
22
 
22
- return (
23
- <div className="components--admin--layout--header">
24
- <EventListener event="mouseup" onCalled={onWindowMouseUp} target={window} />
25
- <div className="header-title-container">
26
- {title}
27
- </div>
28
- {actions &&
29
- <div className="header-actions-container" data-active={s.state.headerActionsActive}>
30
- <div className="header-actions" ref={headerActionsRef}>
31
- {actions}
32
- </div>
23
+ render() {
24
+ const {actions, onTriggerMenu, title} = this.props
25
+
26
+ return (
27
+ <div className="components--admin--layout--header">
28
+ <EventListener event="mouseup" onCalled={this.tt.onWindowMouseUp} target={window} />
29
+ <div className="header-title-container">
30
+ {title}
33
31
  </div>
34
- }
35
- <div className="burger-menu-container">
36
32
  {actions &&
37
- <a className="actions-link" href="#" onClick={onGearsClicked}>
38
- <i className="fa fa-gear" />
39
- </a>
33
+ <div className="header-actions-container" data-active={this.s.headerActionsActive}>
34
+ <div className="header-actions" ref={this.tt.headerActionsRef}>
35
+ {actions}
36
+ </div>
37
+ </div>
40
38
  }
41
- <a className="burger-menu-link" href="#" onClick={onTriggerMenu}>
42
- <i className="fa fa-bars" />
43
- </a>
39
+ <div className="burger-menu-container">
40
+ {actions &&
41
+ <a className="actions-link" href="#" onClick={this.tt.onGearsClicked}>
42
+ <i className="fa fa-gear" />
43
+ </a>
44
+ }
45
+ <a className="burger-menu-link" href="#" onClick={onTriggerMenu}>
46
+ <i className="fa fa-bars" />
47
+ </a>
48
+ </div>
44
49
  </div>
45
- </div>
46
- )
47
- }
50
+ )
51
+ }
48
52
 
49
- ApiMakerSuperAdminLayoutHeader.propTypes = PropTypesExact({
50
- actions: PropTypes.node,
51
- onTriggerMenu: PropTypes.func.isRequired,
52
- title: PropTypes.string
53
- })
53
+ onGearsClicked = (e) => {
54
+ e.preventDefault()
55
+ this.setState({headerActionsActive: !this.s.headerActionsActive})
56
+ }
54
57
 
55
- export default memo(ApiMakerSuperAdminLayoutHeader)
58
+ onWindowMouseUp = (e) => {
59
+ // Close the header actions menu if clicked happened outside
60
+ if (this.s.headerActionsActive && this.tt.headerActionsRef.current && !this.tt.headerActionsRef.current.contains(e.target)) {
61
+ this.setState({headerActionsActive: false})
62
+ }
63
+ }
64
+ }))
@@ -1,4 +1,5 @@
1
1
  import "./style"
2
+ import BaseComponent from "../../base-component"
2
3
  import classNames from "classnames"
3
4
  import CommandsPool from "../../commands-pool"
4
5
  import config from "super-admin/config"
@@ -7,92 +8,94 @@ import Menu from "./menu"
7
8
  import PropTypes from "prop-types"
8
9
  import PropTypesExact from "prop-types-exact"
9
10
  import {memo, useCallback, useMemo} from "react"
11
+ import {shapeComponent} from "set-state-compare/src/shape-component.js"
10
12
  import useCurrentUser from "../../use-current-user"
11
13
  import useShape from "set-state-compare/src/use-shape.js"
12
14
 
13
15
  const NoAccess = React.lazy(() => import("./no-access"))
14
16
 
15
- const ApiMakerSuperAdminLayout = ({
16
- actions,
17
- active,
18
- children,
19
- className,
20
- currentCustomer,
21
- currentCustomerId,
22
- headerTitle,
23
- menu,
24
- ...restProps
25
- }) => {
26
- const s = useShape()
27
- const currentUser = useCurrentUser()
17
+ export default memo(shapeComponent(class ApiMakerSuperAdminLayout extends BaseComponent {
18
+ static propTypes = PropTypesExact({
19
+ actions: PropTypes.node,
20
+ active: PropTypes.string,
21
+ children: PropTypes.node,
22
+ className: PropTypes.string,
23
+ currentCustomer: PropTypes.instanceOf(User),
24
+ currentCustomerId: PropTypes.string,
25
+ currentUser: PropTypes.instanceOf(User),
26
+ headTitle: PropTypes.string,
27
+ headerTitle: PropTypes.string
28
+ })
28
29
 
29
- useMemo(() => {
30
- CommandsPool.current().globalRequestData.layout = "admin"
31
- CommandsPool.current().globalRequestData.locale = I18n.locale
32
- }, [I18n.locale])
30
+ render() {
31
+ const {
32
+ actions,
33
+ active,
34
+ children,
35
+ className,
36
+ currentCustomer,
37
+ currentCustomerId,
38
+ headerTitle,
39
+ menu,
40
+ ...restProps
41
+ } = this.props
42
+ const s = useShape()
43
+ const currentUser = useCurrentUser()
33
44
 
34
- const headTitle = headTitle || headerTitle
45
+ useMemo(() => {
46
+ CommandsPool.current().globalRequestData.layout = "admin"
47
+ CommandsPool.current().globalRequestData.locale = I18n.locale
48
+ }, [I18n.locale])
35
49
 
36
- if (headTitle) {
37
- document.title = headTitle
38
- } else {
39
- document.title = "Wooftech"
40
- }
41
-
42
- const setMenuTriggered = s.useState("menuTriggered", false)
43
- const noAccess = !currentUser
44
- const onRequestMenuClose = useCallback(() => setMenuTriggered(false), [])
45
- const onTriggerMenu = useCallback((e) => {
46
- e.preventDefault()
47
- setMenuTriggered(!s.state.menuTriggered)
48
- }, [])
50
+ const headTitle = headTitle || headerTitle
49
51
 
50
- return (
51
- <div className={classNames("components--admin--layout", className)} data-menu-triggered={s.state.menuTriggered} {...restProps}>
52
- <Menu
53
- active={active}
54
- noAccess={noAccess}
55
- onRequestMenuClose={onRequestMenuClose}
56
- triggered={s.state.menuTriggered}
57
- />
58
- <Header actions={actions} onTriggerMenu={onTriggerMenu} title={headerTitle} />
59
- <div className="app-layout-content-container">
60
- {noAccess &&
61
- <>
62
- <NoAccess />
63
- {currentUser &&
64
- <>
65
- <div className="mb-4">
66
- {I18n.t("js.api_maker.super_admin.layout.try_signing_out_and_in_with_a_different_user", {defaultValue: "Try signing in with a different user."})}
67
- </div>
68
- </>
69
- }
70
- {!currentUser &&
71
- <>
72
- <div className="mb-4">
73
- {I18n.t("js.api_maker.super_admin.layout.try_signing_in", {defaultValue: "Try signing in."})}
74
- </div>
75
- {config.signInContent()}
76
- </>
77
- }
78
- </>
79
- }
80
- {!noAccess && children}
81
- </div>
82
- </div>
83
- )
84
- }
52
+ if (headTitle) {
53
+ document.title = headTitle
54
+ } else {
55
+ document.title = "Wooftech"
56
+ }
85
57
 
86
- ApiMakerSuperAdminLayout.propTypes = PropTypesExact({
87
- actions: PropTypes.node,
88
- active: PropTypes.string,
89
- children: PropTypes.node,
90
- className: PropTypes.string,
91
- currentCustomer: PropTypes.instanceOf(User),
92
- currentCustomerId: PropTypes.string,
93
- currentUser: PropTypes.instanceOf(User),
94
- headTitle: PropTypes.string,
95
- headerTitle: PropTypes.string
96
- })
58
+ const setMenuTriggered = s.useState("menuTriggered", false)
59
+ const noAccess = !currentUser
60
+ const onRequestMenuClose = useCallback(() => setMenuTriggered(false), [])
61
+ const onTriggerMenu = useCallback((e) => {
62
+ e.preventDefault()
63
+ setMenuTriggered(!s.state.menuTriggered)
64
+ }, [])
97
65
 
98
- export default memo(ApiMakerSuperAdminLayout)
66
+ return (
67
+ <div className={classNames("components--admin--layout", className)} data-menu-triggered={s.state.menuTriggered} {...restProps}>
68
+ <Menu
69
+ active={active}
70
+ noAccess={noAccess}
71
+ onRequestMenuClose={onRequestMenuClose}
72
+ triggered={s.state.menuTriggered}
73
+ />
74
+ <Header actions={actions} onTriggerMenu={onTriggerMenu} title={headerTitle} />
75
+ <div className="app-layout-content-container">
76
+ {noAccess &&
77
+ <>
78
+ <NoAccess />
79
+ {currentUser &&
80
+ <>
81
+ <div className="mb-4">
82
+ {I18n.t("js.api_maker.super_admin.layout.try_signing_out_and_in_with_a_different_user", {defaultValue: "Try signing in with a different user."})}
83
+ </div>
84
+ </>
85
+ }
86
+ {!currentUser &&
87
+ <>
88
+ <div className="mb-4">
89
+ {I18n.t("js.api_maker.super_admin.layout.try_signing_in", {defaultValue: "Try signing in."})}
90
+ </div>
91
+ {config.signInContent()}
92
+ </>
93
+ }
94
+ </>
95
+ }
96
+ {!noAccess && children}
97
+ </div>
98
+ </div>
99
+ )
100
+ }
101
+ }))
@@ -1,17 +1,67 @@
1
1
  import "./style"
2
- import {memo, useCallback, useRef} from "react"
2
+ import BaseComponent from "../../../base-component"
3
+ import {memo, useRef} from "react"
3
4
  import Link from "../../../link"
4
5
  import MenuContent from "./menu-content"
5
6
  import MenuItem from "./menu-item"
6
7
  import PropTypes from "prop-types"
7
8
  import PropTypesExact from "prop-types-exact"
9
+ import {shapeComponent} from "set-state-compare/src/shape-component.js"
8
10
  import useCurrentUser from "../../../use-current-user"
9
11
 
10
- const ComponentsAdminLayoutMenu = ({active, noAccess, triggered}) => {
11
- const currentUser = useCurrentUser()
12
- const rootRef = useRef()
12
+ export default memo(shapeComponent(class ComponentsAdminLayoutMenu extends BaseComponent {
13
+ static propTypes = PropTypesExact({
14
+ active: PropTypes.string,
15
+ noAccess: PropTypes.bool.isRequired,
16
+ onRequestMenuClose: PropTypes.func.isRequired,
17
+ triggered: PropTypes.bool.isRequired
18
+ })
13
19
 
14
- const onSignOutClicked = useCallback(async (e) => {
20
+ render() {
21
+ const {active, noAccess, triggered} = this.props
22
+ const currentUser = useCurrentUser()
23
+ const rootRef = useRef()
24
+
25
+ return (
26
+ <div className="components--admin--layout--menu" data-triggered={triggered} ref={rootRef}>
27
+ <div className="menu-logo">
28
+ <Link className="menu-logo-link" to={Params.withParams({})}>
29
+ Admin
30
+ </Link>
31
+ </div>
32
+ <div className="menu-items-center">
33
+ {!noAccess &&
34
+ <MenuContent active={active} />
35
+ }
36
+ </div>
37
+ <div className="menu-items-bottom">
38
+ {currentUser &&
39
+ <div className="menu-user-section">
40
+ <div className="menu-user-icon">
41
+ <i className="fa fa-user" />
42
+ </div>
43
+ <div className="menu-user-name">
44
+ <div className="menu-user-name-container">
45
+ {currentUser.name()}
46
+ </div>
47
+ </div>
48
+ </div>
49
+ }
50
+ {currentUser &&
51
+ <MenuItem
52
+ active
53
+ className="sign-out-menu-item"
54
+ icon="sign-out-alt"
55
+ label={I18n.t("js.api_maker.super_admin.layout.menu.sign_out", {defaultValue: "Sign out"})}
56
+ onClick={this.tt.onSignOutClicked}
57
+ />
58
+ }
59
+ </div>
60
+ </div>
61
+ )
62
+ }
63
+
64
+ onSignOutClicked = async (e) => {
15
65
  e.preventDefault()
16
66
 
17
67
  try {
@@ -20,52 +70,5 @@ const ComponentsAdminLayoutMenu = ({active, noAccess, triggered}) => {
20
70
  } catch (error) {
21
71
  FlashMessage.errorResponse(error)
22
72
  }
23
- }, [])
24
-
25
- return (
26
- <div className="components--admin--layout--menu" data-triggered={triggered} ref={rootRef}>
27
- <div className="menu-logo">
28
- <Link className="menu-logo-link" to={Params.withParams({})}>
29
- Admin
30
- </Link>
31
- </div>
32
- <div className="menu-items-center">
33
- {!noAccess &&
34
- <MenuContent active={active} />
35
- }
36
- </div>
37
- <div className="menu-items-bottom">
38
- {currentUser &&
39
- <div className="menu-user-section">
40
- <div className="menu-user-icon">
41
- <i className="fa fa-user" />
42
- </div>
43
- <div className="menu-user-name">
44
- <div className="menu-user-name-container">
45
- {currentUser.name()}
46
- </div>
47
- </div>
48
- </div>
49
- }
50
- {currentUser &&
51
- <MenuItem
52
- active
53
- className="sign-out-menu-item"
54
- icon="sign-out-alt"
55
- label={I18n.t("js.api_maker.super_admin.layout.menu.sign_out", {defaultValue: "Sign out"})}
56
- onClick={onSignOutClicked}
57
- />
58
- }
59
- </div>
60
- </div>
61
- )
62
- }
63
-
64
- ComponentsAdminLayoutMenu.propTypes = PropTypesExact({
65
- active: PropTypes.string,
66
- noAccess: PropTypes.bool.isRequired,
67
- onRequestMenuClose: PropTypes.func.isRequired,
68
- triggered: PropTypes.bool.isRequired
69
- })
70
-
71
- export default memo(ComponentsAdminLayoutMenu)
73
+ }
74
+ }))
@@ -1,3 +1,4 @@
1
+ import BaseComponent from "../../../base-component"
1
2
  import {digg} from "diggerize"
2
3
  import {memo, useMemo} from "react"
3
4
  import MenuItem from "./menu-item"
@@ -5,33 +6,35 @@ import models from "../../models"
5
6
  import Params from "../../../params"
6
7
  import PropTypes from "prop-types"
7
8
  import PropTypesExact from "prop-types-exact"
9
+ import {shapeComponent} from "set-state-compare/src/shape-component.js"
8
10
  import useCanCan from "../../../use-can-can"
9
11
 
10
- const ComponentsAdminLayoutMenuContent = ({active}) => {
11
- const canCan = useCanCan(() => models.map((model) => [model, ["index"]]))
12
- const sortedModels = useMemo(
13
- () => models.sort((a, b) => a.modelName().human({count: 2}).toLowerCase().localeCompare(b.modelName().human({count: 2}).toLowerCase())),
14
- [I18n.locale]
15
- )
12
+ export default memo(shapeComponent(class ComponentsAdminLayoutMenuContent extends BaseComponent {
13
+ static propTypes = PropTypesExact({
14
+ active: PropTypes.string
15
+ })
16
16
 
17
- return (
18
- <>
19
- {sortedModels.map((model) => canCan?.can("index", model) &&
20
- <MenuItem
21
- active={active}
22
- icon="sitemap"
23
- identifier={digg(model.modelClassData(), "name")}
24
- label={model.modelName().human({count: 2})}
25
- key={model.modelClassData().name}
26
- to={Params.withParams({model: model.modelClassData().name})}
27
- />
28
- )}
29
- </>
30
- )
31
- }
17
+ render() {
18
+ const {active} = this.p
19
+ const canCan = useCanCan(() => models.map((model) => [model, ["index"]]))
20
+ const sortedModels = useMemo(
21
+ () => models.sort((a, b) => a.modelName().human({count: 2}).toLowerCase().localeCompare(b.modelName().human({count: 2}).toLowerCase())),
22
+ [I18n.locale]
23
+ )
32
24
 
33
- ComponentsAdminLayoutMenuContent.propTypes = PropTypesExact({
34
- active: PropTypes.string
35
- })
36
-
37
- export default memo(ComponentsAdminLayoutMenuContent)
25
+ return (
26
+ <>
27
+ {sortedModels.map((model) => canCan?.can("index", model) &&
28
+ <MenuItem
29
+ active={active}
30
+ icon="sitemap"
31
+ identifier={digg(model.modelClassData(), "name")}
32
+ label={model.modelName().human({count: 2})}
33
+ key={model.modelClassData().name}
34
+ to={Params.withParams({model: model.modelClassData().name})}
35
+ />
36
+ )}
37
+ </>
38
+ )
39
+ }
40
+ }))
@@ -1,29 +1,33 @@
1
1
  import "./style"
2
+ import BaseComponent from "../../../../base-component"
2
3
  import classNames from "classnames"
3
4
  import Link from "../../../../link"
4
5
  import {memo} from "react"
5
6
  import PropTypes from "prop-types"
7
+ import {shapeComponent} from "set-state-compare/src/shape-component.js"
6
8
 
7
- const ComponentsAdminLayoutMenuMenuItem = ({active, children, className, icon, identifier, label, to, ...restProps}) => {
8
- return (
9
- <Link
10
- className={classNames("components--admin--layout--menu--menu-item", className)}
11
- data-active={active === true || active == identifier}
12
- data-identifier={identifier}
13
- to={to || "#"}
14
- {...restProps}
15
- >
16
- <i className={`fa fa-fw fa-${icon} menu-item-icon`} />
17
- {children || label}
18
- </Link>
19
- )
20
- }
9
+ export default memo(shapeComponent(class ComponentsAdminLayoutMenuMenuItem extends BaseComponent {
10
+ static propTypes = {
11
+ active: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
12
+ className: PropTypes.string,
13
+ icon: PropTypes.string.isRequired,
14
+ label: PropTypes.node
15
+ }
21
16
 
22
- ComponentsAdminLayoutMenuMenuItem.propTypes = {
23
- active: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
24
- className: PropTypes.string,
25
- icon: PropTypes.string.isRequired,
26
- label: PropTypes.node
27
- }
17
+ render() {
18
+ const {active, children, className, icon, identifier, label, to, ...restProps} = this.props
28
19
 
29
- export default memo(ComponentsAdminLayoutMenuMenuItem)
20
+ return (
21
+ <Link
22
+ className={classNames("components--admin--layout--menu--menu-item", className)}
23
+ data-active={active === true || active == identifier}
24
+ data-identifier={identifier}
25
+ to={to || "#"}
26
+ {...restProps}
27
+ >
28
+ <i className={`fa fa-fw fa-${icon} menu-item-icon`} />
29
+ {children || label}
30
+ </Link>
31
+ )
32
+ }
33
+ }))
@@ -1,17 +1,19 @@
1
+ import BaseComponent from "../../base-component"
1
2
  import {memo} from "react"
3
+ import {shapeComponent} from "set-state-compare/src/shape-component.js"
2
4
  import useCurrentUser from "../../use-current-user"
3
5
 
4
- const ComponentsAdminLayoutNoAccess = () => {
5
- const currentUser = useCurrentUser()
6
+ export default memo(shapeComponent(class ComponentsAdminLayoutNoAccess extends BaseComponent {
7
+ render() {
8
+ const currentUser = useCurrentUser()
6
9
 
7
- return (
8
- <div
9
- className="components--admin--layout-no-access"
10
- data-user-roles={currentUser?.userRoles()?.loaded()?.map((userRole) => userRole.role()?.identifier()).join(", ")}
11
- >
12
- {I18n.t("js.api_maker.super_admin.layout.no_access.you_dont_have_no_access_to_this_page", {defaultValue: "You don't have access to this page."})}
13
- </div>
14
- )
15
- }
16
-
17
- export default memo(ComponentsAdminLayoutNoAccess)
10
+ return (
11
+ <div
12
+ className="components--admin--layout-no-access"
13
+ data-user-roles={currentUser?.userRoles()?.loaded()?.map((userRole) => userRole.role()?.identifier()).join(", ")}
14
+ >
15
+ {I18n.t("js.api_maker.super_admin.layout.no_access.you_dont_have_no_access_to_this_page", {defaultValue: "You don't have access to this page."})}
16
+ </div>
17
+ )
18
+ }
19
+ }))
@@ -1,23 +1,26 @@
1
1
  import AttributeRow from "../../bootstrap/attribute-row"
2
+ import BaseComponent from "../../base-component"
2
3
  import * as inflection from "inflection"
3
4
  import Link from "../../link"
4
- import Params from "../../params"
5
5
  import {memo} from "react"
6
+ import Params from "../../params"
7
+ import {shapeComponent} from "set-state-compare/src/shape-component.js"
6
8
 
7
- const ApiMakerSuperAdminShowPageBelongsToAttributeRow = ({model, modelClass, reflection}) => {
8
- const reflectionMethodName = inflection.camelize(reflection.name(), true)
9
- const subModel = model[reflectionMethodName]()
10
-
11
- return (
12
- <AttributeRow label={modelClass.humanAttributeName(inflection.camelize(reflection.name(), true))}>
13
- {subModel &&
14
- <Link to={Params.withParams({model: subModel.modelClassData().name, model_id: subModel.primaryKey()})}>
15
- {subModel && "name" in subModel && subModel.name()}
16
- {subModel && !("name" in subModel) && subModel?.id()}
17
- </Link>
18
- }
19
- </AttributeRow>
20
- )
21
- }
9
+ export default memo(shapeComponent(class ApiMakerSuperAdminShowPageBelongsToAttributeRow extends BaseComponent {
10
+ render() {
11
+ const {model, modelClass, reflection} = this.props
12
+ const reflectionMethodName = inflection.camelize(reflection.name(), true)
13
+ const subModel = model[reflectionMethodName]()
22
14
 
23
- export default memo(ApiMakerSuperAdminShowPageBelongsToAttributeRow)
15
+ return (
16
+ <AttributeRow label={modelClass.humanAttributeName(inflection.camelize(reflection.name(), true))}>
17
+ {subModel &&
18
+ <Link to={Params.withParams({model: subModel.modelClassData().name, model_id: subModel.primaryKey()})}>
19
+ {subModel && "name" in subModel && subModel.name()}
20
+ {subModel && !("name" in subModel) && subModel?.id()}
21
+ </Link>
22
+ }
23
+ </AttributeRow>
24
+ )
25
+ }
26
+ }))
@@ -1,31 +1,48 @@
1
+ import BaseComponent from "../base-component"
1
2
  import {digg} from "diggerize"
2
- import {memo} from "react"
3
+ import {memo, useMemo} from "react"
4
+ import PropTypes from "prop-types"
5
+ import propTypesExact from "prop-types-exact"
6
+ import {shapeComponent} from "set-state-compare/src/shape-component.js"
3
7
 
4
- const SuperAdminShowReflectionActions = ({model, modelClass, reflectionName}) => {
5
- const reflection = modelClass.reflections().find((reflection) => reflection.name() == reflectionName)
6
- const modelClassName = digg(reflection, "reflectionData", "className")
7
- const modelData = {}
8
- const dataParamName = inflection.singularize(reflection.reflectionData.collectionName)
9
- const canCan = useCanCan(() => [[reflection.modelClass(), ["new"]]])
8
+ export default memo(shapeComponent(class SuperAdminShowReflectionActions extends BaseComponent {
9
+ static propTypes = propTypesExact({
10
+ model: PropTypes.object,
11
+ modelClass: PropTypes.func,
12
+ reflectionName: PropTypes.string
13
+ })
10
14
 
11
- modelData[reflection.foreignKey()] = model?.id()
15
+ setup() {
16
+ const {modelClass, reflectionName} = this.p
12
17
 
13
- const linkParams = {
14
- model: modelClassName,
15
- mode: "new"
18
+ this.reflection = useMemo(() => modelClass.reflections().find((reflection) => reflection.name() == reflectionName), [modelClass, reflectionName])
19
+ this.canCan = useCanCan(() => [[this.reflection.modelClass(), ["new"]]])
16
20
  }
17
21
 
18
- linkParams[dataParamName] = modelData
22
+ render() {
23
+ const {canCan, reflection} = this.tt
24
+ const {model} = this.p
25
+ const modelClassName = digg(reflection, "reflectionData", "className")
26
+ const modelData = {}
27
+ const dataParamName = inflection.singularize(reflection.reflectionData.collectionName)
19
28
 
20
- return (
21
- <>
22
- {canCan?.can("new", reflection.modelClass()) &&
23
- <Link className="create-new-model-link" to={Params.withParams(linkParams)}>
24
- Create new
25
- </Link>
26
- }
27
- </>
28
- )
29
- }
29
+ modelData[reflection.foreignKey()] = model?.id()
30
30
 
31
- export default memo(SuperAdminShowReflectionActions)
31
+ const linkParams = {
32
+ model: modelClassName,
33
+ mode: "new"
34
+ }
35
+
36
+ linkParams[dataParamName] = modelData
37
+
38
+ return (
39
+ <>
40
+ {canCan?.can("new", reflection.modelClass()) &&
41
+ <Link className="create-new-model-link" to={Params.withParams(linkParams)}>
42
+ Create new
43
+ </Link>
44
+ }
45
+ </>
46
+ )
47
+ }
48
+ }))
@@ -1,41 +1,44 @@
1
+ import BaseComponent from "../base-component"
1
2
  import {digg} from "diggerize"
2
3
  import PropTypes from "prop-types"
3
- import React, {memo} from "react"
4
+ import propTypesExact from "prop-types-exact"
5
+ import {memo} from "react"
4
6
  import ModelClassTable from "./model-class-table"
7
+ import {shapeComponent} from "set-state-compare/src/shape-component.js"
5
8
  import ShowNav from "./show-nav"
6
9
  import useQueryParams from "on-location-changed/src/use-query-params"
7
- import withModel from "../with-model"
8
10
 
9
- const ApiMakerSuperAdminShowReflectionPage = ({modelClass, restProps}) => {
10
- const queryParams = useQueryParams()
11
- const camelizedLower = digg(modelClass.modelClassData(), "camelizedLower")
12
- const useModelResult = useModel(modelClass, {loadByQueryParam: ({queryParams}) => digg(queryParams, "model_id")})
13
- const model = digg(useModelResult, camelizedLower)
14
- const reflections = modelClass.reflections()
15
- const reflection = reflections.find((reflectionI) => reflectionI.name() == queryParams.model_reflection)
16
- const reflectionModelClass = reflection.modelClass()
17
- let collection
11
+ export default memo(shapeComponent(class ApiMakerSuperAdminShowReflectionPage extends BaseComponent {
12
+ static propTypes = propTypesExact({
13
+ modelClass: PropTypes.func.isRequired
14
+ })
18
15
 
19
- if (model) collection = model[reflection.name()]()
16
+ render() {
17
+ const {modelClass} = this.p
18
+ const queryParams = useQueryParams()
19
+ const camelizedLower = digg(modelClass.modelClassData(), "camelizedLower")
20
+ const useModelResult = useModel(modelClass, {loadByQueryParam: ({queryParams}) => digg(queryParams, "model_id")})
21
+ const model = digg(useModelResult, camelizedLower)
22
+ const reflections = modelClass.reflections()
23
+ const reflection = reflections.find((reflectionI) => reflectionI.name() == queryParams.model_reflection)
24
+ const reflectionModelClass = reflection.modelClass()
25
+ let collection
20
26
 
21
- return (
22
- <div className="super-admin--show-page">
23
- {model &&
24
- <ShowNav model={model} modelClass={modelClass} />
25
- }
26
- {collection &&
27
- <ModelClassTable
28
- collection={collection}
29
- key={reflectionModelClass.modelName().human()}
30
- modelClass={reflectionModelClass}
31
- />
32
- }
33
- </div>
34
- )
35
- }
27
+ if (model) collection = model[reflection.name()]()
36
28
 
37
- ApiMakerSuperAdminShowReflectionPage.propTypes = {
38
- modelClass: PropTypes.func.isRequired
39
- }
40
-
41
- export default memo(ApiMakerSuperAdminShowReflectionPage)
29
+ return (
30
+ <div className="super-admin--show-page">
31
+ {model &&
32
+ <ShowNav model={model} modelClass={modelClass} />
33
+ }
34
+ {collection &&
35
+ <ModelClassTable
36
+ collection={collection}
37
+ key={reflectionModelClass.modelName().human()}
38
+ modelClass={reflectionModelClass}
39
+ />
40
+ }
41
+ </div>
42
+ )
43
+ }
44
+ }))
package/src/use-model.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import {useCallback, useLayoutEffect, useMemo, useState} from "react"
1
+ import {useCallback, useLayoutEffect, useMemo} from "react"
2
2
  import Devise from "./devise.mjs"
3
3
  import * as inflection from "inflection"
4
4
  import ModelEvents from "./model-events.mjs"
@@ -17,6 +17,11 @@ const useModel = (modelClassArg, argsArg = {}) => {
17
17
 
18
18
  const s = useShape(args)
19
19
 
20
+ s.useStates({
21
+ model: undefined,
22
+ notFound: undefined
23
+ })
24
+
20
25
  if (typeof modelClassArg == "object") {
21
26
  modelClass = modelClassArg.callback({queryParams})
22
27
  } else {
@@ -28,7 +33,7 @@ const useModel = (modelClassArg, argsArg = {}) => {
28
33
 
29
34
  if (args.loadByQueryParam) {
30
35
  modelId = args.loadByQueryParam({queryParams})
31
- } else {
36
+ } else if (!args.query) {
32
37
  if (!args.match) throw new Error("Both 'loadByQueryParam' and 'match' wasn't given")
33
38
 
34
39
  modelId = args.match.params[paramsVariableName] || args.match.params.id
@@ -36,19 +41,21 @@ const useModel = (modelClassArg, argsArg = {}) => {
36
41
 
37
42
  const modelVariableName = inflection.camelize(modelClass.modelClassData().name, true)
38
43
  const cacheArgs = [modelId]
39
- const [model, setModel] = useState(undefined)
40
- const [notFound, setNotFound] = useState(undefined)
41
44
 
42
45
  if (args.cacheArgs) {
43
46
  cacheArgs.push(...args.cacheArgs)
44
47
  }
45
48
 
46
- s.updateMeta({modelId, modelVariableName, queryParams})
49
+ s.updateMeta({args, modelId, modelVariableName, queryParams})
47
50
 
48
51
  const loadExistingModel = useCallback(async () => {
49
- const query = await modelClass.ransack({id_eq: s.m.modelId})
52
+ let query
50
53
 
51
- if (!modelId) {
54
+ if (s.m.modelId) {
55
+ query = modelClass.ransack({id_eq: s.m.modelId})
56
+ } else if (s.m.args.query) {
57
+ query = s.m.args.query.clone()
58
+ } else {
52
59
  throw new Error(`No model ID was given: ${s.m.modelId} by '${paramsVariableName}' in query params: ${Object.keys(s.props.match.params).join(", ")}`)
53
60
  }
54
61
 
@@ -58,8 +65,7 @@ const useModel = (modelClassArg, argsArg = {}) => {
58
65
 
59
66
  const model = await query.first()
60
67
 
61
- setModel(model)
62
- setNotFound(!model)
68
+ s.set({model, notFound: !model})
63
69
  }, [])
64
70
 
65
71
  const loadNewModel = useCallback(async () => {
@@ -79,13 +85,13 @@ const useModel = (modelClassArg, argsArg = {}) => {
79
85
  data: {a: modelData}
80
86
  })
81
87
 
82
- setModel(model)
88
+ s.set({model})
83
89
  }, [])
84
90
 
85
91
  const loadModel = useCallback(async () => {
86
92
  if (s.props.newIfNoId && !s.m.modelId) {
87
93
  return await loadNewModel()
88
- } else if (!s.props.optional || s.m.modelId) {
94
+ } else if (!s.props.optional || s.m.modelId | s.m.args.query) {
89
95
  return await loadExistingModel()
90
96
  }
91
97
  }, [])
@@ -112,14 +118,14 @@ const useModel = (modelClassArg, argsArg = {}) => {
112
118
  useLayoutEffect(() => {
113
119
  let connectUpdated
114
120
 
115
- if (model && args.eventUpdated) {
116
- connectUpdated = ModelEvents.connectUpdated(model, loadModel)
121
+ if (s.s.model && args.eventUpdated) {
122
+ connectUpdated = ModelEvents.connectUpdated(s.s.model, loadModel)
117
123
  }
118
124
 
119
125
  return () => {
120
126
  connectUpdated?.unsubscribe()
121
127
  }
122
- }, [args.eventUpdated, model?.id()])
128
+ }, [args.eventUpdated, s.s.model?.id()])
123
129
 
124
130
  const onSignedIn = useCallback(() => {
125
131
  loadModel()
@@ -150,20 +156,20 @@ const useModel = (modelClassArg, argsArg = {}) => {
150
156
  useLayoutEffect(() => {
151
157
  let connectDestroyed
152
158
 
153
- if (model && args.onDestroyed) {
154
- connectDestroyed = ModelEvents.connectDestroyed(model, onDestroyed)
159
+ if (s.s.model && args.onDestroyed) {
160
+ connectDestroyed = ModelEvents.connectDestroyed(s.s.model, onDestroyed)
155
161
  }
156
162
 
157
163
  return () => {
158
164
  connectDestroyed?.unsubscribe()
159
165
  }
160
- }, [args.onDestroyed, model?.id()])
166
+ }, [args.onDestroyed, s.s.model?.id()])
161
167
 
162
168
  const result = {}
163
169
 
164
- result[modelVariableName] = model
170
+ result[modelVariableName] = s.s.model
165
171
  result[`${modelVariableName}Id`] = modelId
166
- result[`${modelVariableName}NotFound`] = notFound
172
+ result[`${modelVariableName}NotFound`] = s.s.notFound
167
173
 
168
174
  return result
169
175
  }