@kaspernj/api-maker 1.0.385 → 1.0.387
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 +1 -1
- package/src/base-model/attribute.mjs +1 -0
- package/src/super-admin/config-reader.jsx +8 -2
- package/src/super-admin/edit-page/edit-attribute-content.jsx +35 -0
- package/src/super-admin/edit-page/edit-attribute-input.jsx +61 -0
- package/src/super-admin/edit-page/edit-attribute.jsx +51 -0
- package/src/super-admin/edit-page.jsx +84 -171
- package/src/super-admin/layout/header/index.jsx +51 -42
- package/src/super-admin/layout/index.jsx +81 -78
- package/src/super-admin/layout/menu/index.jsx +57 -54
- package/src/super-admin/layout/menu/menu-content.jsx +29 -26
- package/src/super-admin/layout/menu/menu-item/index.jsx +25 -21
- package/src/super-admin/layout/no-access.jsx +15 -13
- package/src/super-admin/show-page/belongs-to-attribute-row.jsx +20 -17
- package/src/super-admin/show-reflection-actions.jsx +40 -23
- package/src/super-admin/show-reflection-page.jsx +35 -32
- package/src/use-model.mjs +1 -1
package/package.json
CHANGED
|
@@ -60,14 +60,20 @@ export default class ApiMakerSuperAdminConfigReader {
|
|
|
60
60
|
const columns = []
|
|
61
61
|
|
|
62
62
|
for (const attribute of attributes) {
|
|
63
|
-
if (!attribute.isSelectedByDefault())
|
|
63
|
+
if (!attribute.isSelectedByDefault() && attribute.name() != "name") {
|
|
64
|
+
continue
|
|
65
|
+
}
|
|
64
66
|
|
|
65
67
|
const camelizedName = inflection.camelize(attribute.name(), true)
|
|
66
68
|
const column = {
|
|
67
69
|
attribute: camelizedName
|
|
68
70
|
}
|
|
69
71
|
|
|
70
|
-
if (attribute.isColumn())
|
|
72
|
+
if (attribute.isColumn()) {
|
|
73
|
+
column.sortKey = camelizedName
|
|
74
|
+
} else if (attribute.isTranslated()) {
|
|
75
|
+
column.sortKey = `currentTranslation${camelizedName}`
|
|
76
|
+
}
|
|
71
77
|
|
|
72
78
|
columns.push(column)
|
|
73
79
|
}
|
|
@@ -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,
|
|
6
|
+
import {Pressable, Text, View} from "react-native"
|
|
5
7
|
import Locales from "shared/locales"
|
|
6
|
-
import {memo
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
57
|
+
const modelIdVarName = `${inflection.camelize(modelClass.modelClassData().name, true)}Id`
|
|
58
|
+
const modelVarName = inflection.camelize(modelClass.modelClassData().name, true)
|
|
134
59
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
98
|
+
onSubmit = async () => {
|
|
159
99
|
try {
|
|
160
|
-
model.
|
|
100
|
+
const {inputs, model} = this.tt
|
|
161
101
|
|
|
162
|
-
|
|
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
|
-
}
|
|
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 {
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
setup() {
|
|
17
|
+
this.headerActionsRef = useRef()
|
|
18
|
+
this.useStates({
|
|
19
|
+
headerActionsActive: false
|
|
20
|
+
})
|
|
21
|
+
}
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
<
|
|
38
|
-
<
|
|
39
|
-
|
|
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
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
50
|
+
)
|
|
51
|
+
}
|
|
48
52
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
})
|
|
53
|
+
onGearsClicked = (e) => {
|
|
54
|
+
e.preventDefault()
|
|
55
|
+
this.setState({headerActionsActive: !this.s.headerActionsActive})
|
|
56
|
+
}
|
|
54
57
|
|
|
55
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
45
|
+
useMemo(() => {
|
|
46
|
+
CommandsPool.current().globalRequestData.layout = "admin"
|
|
47
|
+
CommandsPool.current().globalRequestData.locale = I18n.locale
|
|
48
|
+
}, [I18n.locale])
|
|
35
49
|
|
|
36
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
23
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5
|
-
|
|
6
|
+
export default memo(shapeComponent(class ComponentsAdminLayoutNoAccess extends BaseComponent {
|
|
7
|
+
render() {
|
|
8
|
+
const currentUser = useCurrentUser()
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
15
|
+
setup() {
|
|
16
|
+
const {modelClass, reflectionName} = this.p
|
|
12
17
|
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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