@kaspernj/api-maker 1.0.350 → 1.0.352
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 +2 -2
- package/src/api.mjs +33 -33
- package/src/commands-pool.mjs +12 -12
- package/src/devise.mjs +20 -16
- package/src/logger.mjs +7 -1
- package/src/session-status-updater.mjs +73 -24
- package/src/super-admin/edit-page.jsx +1 -1
- package/src/super-admin/has-edit-config.js +15 -0
- package/src/super-admin/index.jsx +13 -7
- package/src/super-admin/model-class-table.jsx +3 -2
- package/src/super-admin/show-page/index.jsx +1 -1
- package/src/use-can-can.mjs +1 -0
- package/src/use-current-user.mjs +21 -18
- package/src/use-screen-layout.mjs +49 -0
package/package.json
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
]
|
|
17
17
|
},
|
|
18
18
|
"name": "@kaspernj/api-maker",
|
|
19
|
-
"version": "1.0.
|
|
19
|
+
"version": "1.0.352",
|
|
20
20
|
"type": "module",
|
|
21
21
|
"description": "",
|
|
22
22
|
"main": "index.js",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"babel-jest": "^29.0.1",
|
|
71
71
|
"eslint": "^8.2.0",
|
|
72
72
|
"eslint-find-rules": "^4.0.0",
|
|
73
|
-
"eslint-plugin-jest": "^
|
|
73
|
+
"eslint-plugin-jest": "^28.2.0",
|
|
74
74
|
"eslint-plugin-react": "^7.23.2",
|
|
75
75
|
"i18n-on-steroids": "^1.0.7",
|
|
76
76
|
"jest": "^29.0.1",
|
package/src/api.mjs
CHANGED
|
@@ -1,26 +1,21 @@
|
|
|
1
1
|
import config from "./config.mjs"
|
|
2
2
|
import CustomError from "./custom-error.mjs"
|
|
3
3
|
import FormDataObjectizer from "form-data-objectizer"
|
|
4
|
+
import Logger from "./logger.mjs"
|
|
4
5
|
import qs from "qs"
|
|
6
|
+
import SessionStatusUpdater from "./session-status-updater.mjs"
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
static get(path, pathParams = null) {
|
|
8
|
-
return Api.requestLocal({path, pathParams, method: "GET"})
|
|
9
|
-
}
|
|
8
|
+
const logger = new Logger({name: "ApiMaker / Api"})
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
return Api.requestLocal({path, pathParams, method: "DELETE"})
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
static patch(path, data = {}) {
|
|
16
|
-
return Api.requestLocal({path, data, method: "PATCH"})
|
|
17
|
-
}
|
|
10
|
+
// logger.setDebug(true)
|
|
18
11
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
12
|
+
export default class Api {
|
|
13
|
+
static get = async (path, pathParams = null) => await Api.requestLocal({path, pathParams, method: "GET"})
|
|
14
|
+
static delete = async (path, pathParams = null) => await Api.requestLocal({path, pathParams, method: "DELETE"})
|
|
15
|
+
static patch = async (path, data = {}) => await Api.requestLocal({path, data, method: "PATCH"})
|
|
16
|
+
static post = async (path, data = {}) => await Api.requestLocal({path, data, method: "POST"})
|
|
22
17
|
|
|
23
|
-
static request({data, headers, method, path, pathParams}) {
|
|
18
|
+
static async request({data, headers, method, path, pathParams}) {
|
|
24
19
|
let requestPath = ""
|
|
25
20
|
if (config.getHost()) requestPath += config.getHost()
|
|
26
21
|
requestPath += path
|
|
@@ -30,16 +25,24 @@ export default class Api {
|
|
|
30
25
|
requestPath += `?${pathParamsString}`
|
|
31
26
|
}
|
|
32
27
|
|
|
33
|
-
|
|
34
|
-
const xhr = new XMLHttpRequest()
|
|
35
|
-
xhr.open(method, requestPath, true)
|
|
28
|
+
const xhr = new XMLHttpRequest()
|
|
36
29
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
30
|
+
xhr.open(method, requestPath, true)
|
|
31
|
+
xhr.withCredentials = true
|
|
32
|
+
|
|
33
|
+
if (headers) {
|
|
34
|
+
for (const headerName in headers) {
|
|
35
|
+
xhr.setRequestHeader(headerName, headers[headerName])
|
|
41
36
|
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const response = await Api.executeXhr(xhr, data)
|
|
40
|
+
|
|
41
|
+
return response
|
|
42
|
+
}
|
|
42
43
|
|
|
44
|
+
static executeXhr(xhr, data) {
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
43
46
|
xhr.onload = () => {
|
|
44
47
|
const response = this._parseResponse(xhr)
|
|
45
48
|
|
|
@@ -62,12 +65,14 @@ export default class Api {
|
|
|
62
65
|
})
|
|
63
66
|
}
|
|
64
67
|
|
|
65
|
-
static requestLocal(args) {
|
|
68
|
+
static async requestLocal(args) {
|
|
66
69
|
if (!args.headers) {
|
|
67
70
|
args.headers = {}
|
|
68
71
|
}
|
|
69
72
|
|
|
70
|
-
const token = this._token()
|
|
73
|
+
const token = await this._token()
|
|
74
|
+
|
|
75
|
+
logger.debug(() => `Got token: ${token}`)
|
|
71
76
|
|
|
72
77
|
if (token) {
|
|
73
78
|
args.headers["X-CSRF-Token"] = token
|
|
@@ -82,19 +87,14 @@ export default class Api {
|
|
|
82
87
|
args.data = args.rawData
|
|
83
88
|
}
|
|
84
89
|
|
|
85
|
-
return this.request(args)
|
|
90
|
+
return await this.request(args)
|
|
86
91
|
}
|
|
87
92
|
|
|
88
|
-
static put(path, data = {}) {
|
|
89
|
-
return this.requestLocal({path, data, method: "PUT"})
|
|
93
|
+
static async put(path, data = {}) {
|
|
94
|
+
return await this.requestLocal({path, data, method: "PUT"})
|
|
90
95
|
}
|
|
91
96
|
|
|
92
|
-
static _token()
|
|
93
|
-
const tokenElement = document.querySelector("meta[name='csrf-token']")
|
|
94
|
-
|
|
95
|
-
if (tokenElement)
|
|
96
|
-
return tokenElement.getAttribute("content")
|
|
97
|
-
}
|
|
97
|
+
static _token = async () => await SessionStatusUpdater.current().getCsrfToken()
|
|
98
98
|
|
|
99
99
|
static _parseResponse(xhr) {
|
|
100
100
|
const responseType = xhr.getResponseHeader("content-type")
|
package/src/commands-pool.mjs
CHANGED
|
@@ -14,7 +14,7 @@ import {ValidationErrors} from "./validation-errors.mjs"
|
|
|
14
14
|
const shared = {}
|
|
15
15
|
|
|
16
16
|
export default class ApiMakerCommandsPool {
|
|
17
|
-
static addCommand
|
|
17
|
+
static addCommand(data, args = {}) {
|
|
18
18
|
let pool
|
|
19
19
|
|
|
20
20
|
if (args.instant) {
|
|
@@ -34,17 +34,17 @@ export default class ApiMakerCommandsPool {
|
|
|
34
34
|
return promiseResult
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
static current
|
|
37
|
+
static current() {
|
|
38
38
|
if (!shared.currentApiMakerCommandsPool) shared.currentApiMakerCommandsPool = new ApiMakerCommandsPool()
|
|
39
39
|
|
|
40
40
|
return shared.currentApiMakerCommandsPool
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
static flush
|
|
43
|
+
static flush() {
|
|
44
44
|
ApiMakerCommandsPool.current().flush()
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
constructor
|
|
47
|
+
constructor() {
|
|
48
48
|
this.flushCount = 0
|
|
49
49
|
this.pool = {}
|
|
50
50
|
this.poolData = {}
|
|
@@ -52,7 +52,7 @@ export default class ApiMakerCommandsPool {
|
|
|
52
52
|
this.globalRequestData = {}
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
addCommand
|
|
55
|
+
addCommand(data) {
|
|
56
56
|
return new Promise((resolve, reject) => {
|
|
57
57
|
const id = this.currentId
|
|
58
58
|
this.currentId += 1
|
|
@@ -87,11 +87,11 @@ export default class ApiMakerCommandsPool {
|
|
|
87
87
|
})
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
commandsCount
|
|
90
|
+
commandsCount() {
|
|
91
91
|
return Object.keys(this.pool)
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
async sendRequest
|
|
94
|
+
async sendRequest({commandSubmitData, url}) {
|
|
95
95
|
let response
|
|
96
96
|
|
|
97
97
|
for (let i = 0; i < 3; i++) {
|
|
@@ -113,7 +113,7 @@ export default class ApiMakerCommandsPool {
|
|
|
113
113
|
throw new Error("Couldnt successfully execute request")
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
async flush
|
|
116
|
+
async flush() {
|
|
117
117
|
if (this.commandsCount() == 0) {
|
|
118
118
|
return
|
|
119
119
|
}
|
|
@@ -166,7 +166,7 @@ export default class ApiMakerCommandsPool {
|
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
handleFailedResponse
|
|
169
|
+
handleFailedResponse(commandData, commandResponseData) {
|
|
170
170
|
let error
|
|
171
171
|
|
|
172
172
|
if (commandResponseData.error_type == "destroy_error") {
|
|
@@ -190,13 +190,13 @@ export default class ApiMakerCommandsPool {
|
|
|
190
190
|
commandData.reject(error)
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
clearTimeout
|
|
193
|
+
clearTimeout() {
|
|
194
194
|
if (this.flushTimeout) {
|
|
195
195
|
clearTimeout(this.flushTimeout)
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
isActive
|
|
199
|
+
isActive() {
|
|
200
200
|
if (this.commandsCount() > 0) {
|
|
201
201
|
return true
|
|
202
202
|
}
|
|
@@ -208,7 +208,7 @@ export default class ApiMakerCommandsPool {
|
|
|
208
208
|
return false
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
setFlushTimeout
|
|
211
|
+
setFlushTimeout() {
|
|
212
212
|
this.clearTimeout()
|
|
213
213
|
this.flushTimeout = setTimeout(() => this.flush(), 0)
|
|
214
214
|
}
|
package/src/devise.mjs
CHANGED
|
@@ -9,11 +9,11 @@ import Services from "./services.mjs"
|
|
|
9
9
|
const shared = {}
|
|
10
10
|
|
|
11
11
|
export default class ApiMakerDevise {
|
|
12
|
-
static callSignOutEvent
|
|
12
|
+
static callSignOutEvent(args) {
|
|
13
13
|
events.emit("onDeviseSignOut", {args})
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
static current
|
|
16
|
+
static current() {
|
|
17
17
|
if (!shared.currentApiMakerDevise) {
|
|
18
18
|
shared.currentApiMakerDevise = new ApiMakerDevise()
|
|
19
19
|
}
|
|
@@ -21,11 +21,11 @@ export default class ApiMakerDevise {
|
|
|
21
21
|
return shared.currentApiMakerDevise
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
static events
|
|
24
|
+
static events() {
|
|
25
25
|
return events
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
static addUserScope
|
|
28
|
+
static addUserScope(scope) {
|
|
29
29
|
const currentMethodName = `current${inflection.camelize(scope)}`
|
|
30
30
|
|
|
31
31
|
ApiMakerDevise[currentMethodName] = () => ApiMakerDevise.current().getCurrentScope(scope)
|
|
@@ -35,7 +35,7 @@ export default class ApiMakerDevise {
|
|
|
35
35
|
ApiMakerDevise[isSignedInMethodName] = () => Boolean(ApiMakerDevise.current().getCurrentScope(scope))
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
static async signIn
|
|
38
|
+
static async signIn(username, password, args = {}) {
|
|
39
39
|
if (!args.scope) args.scope = "user"
|
|
40
40
|
|
|
41
41
|
const postData = {username, password, args}
|
|
@@ -53,21 +53,25 @@ export default class ApiMakerDevise {
|
|
|
53
53
|
return {model, response}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
static updateSession
|
|
57
|
-
if (!args.scope)
|
|
58
|
-
args.scope = digg(model.modelClassData(), "name")
|
|
59
|
-
}
|
|
56
|
+
static updateSession(model, args = {}) {
|
|
57
|
+
if (!args.scope) args.scope = "user"
|
|
60
58
|
|
|
61
59
|
const camelizedScopeName = inflection.camelize(args.scope, true)
|
|
62
60
|
|
|
63
61
|
ApiMakerDevise.current().currents[camelizedScopeName] = model
|
|
64
62
|
}
|
|
65
63
|
|
|
66
|
-
|
|
64
|
+
hasCurrentScope(scope) {
|
|
65
|
+
const camelizedScopeName = inflection.camelize(scope, true)
|
|
66
|
+
|
|
67
|
+
return camelizedScopeName in ApiMakerDevise.current().currents
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static setSignedOut(args) {
|
|
67
71
|
ApiMakerDevise.current().currents[inflection.camelize(args.scope, true)] = null
|
|
68
72
|
}
|
|
69
73
|
|
|
70
|
-
static async signOut
|
|
74
|
+
static async signOut(args = {}) {
|
|
71
75
|
if (!args.scope) {
|
|
72
76
|
args.scope = "user"
|
|
73
77
|
}
|
|
@@ -87,11 +91,11 @@ export default class ApiMakerDevise {
|
|
|
87
91
|
return response
|
|
88
92
|
}
|
|
89
93
|
|
|
90
|
-
constructor
|
|
94
|
+
constructor() {
|
|
91
95
|
this.currents = {}
|
|
92
96
|
}
|
|
93
97
|
|
|
94
|
-
getCurrentScope
|
|
98
|
+
getCurrentScope(scope) {
|
|
95
99
|
if (!(scope in this.currents)) {
|
|
96
100
|
this.currents[scope] = this.loadCurrentScope(scope)
|
|
97
101
|
}
|
|
@@ -99,7 +103,7 @@ export default class ApiMakerDevise {
|
|
|
99
103
|
return this.currents[scope]
|
|
100
104
|
}
|
|
101
105
|
|
|
102
|
-
|
|
106
|
+
hasGlobalCurrentScope(scope) {
|
|
103
107
|
if (globalThis.apiMakerDeviseCurrent && scope in globalThis.apiMakerDeviseCurrent) {
|
|
104
108
|
return true
|
|
105
109
|
}
|
|
@@ -107,8 +111,8 @@ export default class ApiMakerDevise {
|
|
|
107
111
|
return false
|
|
108
112
|
}
|
|
109
113
|
|
|
110
|
-
loadCurrentScope
|
|
111
|
-
if (!this.
|
|
114
|
+
loadCurrentScope(scope) {
|
|
115
|
+
if (!this.hasGlobalCurrentScope(scope)) {
|
|
112
116
|
return null
|
|
113
117
|
}
|
|
114
118
|
|
package/src/logger.mjs
CHANGED
|
@@ -1,38 +1,82 @@
|
|
|
1
|
+
import config from "./config.mjs"
|
|
1
2
|
import Devise from "./devise.mjs"
|
|
2
3
|
import * as inflection from "inflection"
|
|
3
4
|
import Logger from "./logger.mjs"
|
|
4
5
|
import wakeEvent from "wake-event"
|
|
5
6
|
|
|
6
7
|
const logger = new Logger({name: "ApiMaker / SessionStatusUpdater"})
|
|
8
|
+
const shared = {}
|
|
9
|
+
|
|
10
|
+
// logger.setDebug(true)
|
|
7
11
|
|
|
8
12
|
export default class ApiMakerSessionStatusUpdater {
|
|
9
|
-
static current
|
|
10
|
-
if (!
|
|
11
|
-
|
|
13
|
+
static current(args) {
|
|
14
|
+
if (!shared.apiMakerSessionStatusUpdater) {
|
|
15
|
+
shared.apiMakerSessionStatusUpdater = new ApiMakerSessionStatusUpdater(args)
|
|
16
|
+
}
|
|
12
17
|
|
|
13
|
-
return
|
|
18
|
+
return shared.apiMakerSessionStatusUpdater
|
|
14
19
|
}
|
|
15
20
|
|
|
16
|
-
constructor
|
|
21
|
+
constructor(args = {}) {
|
|
17
22
|
this.events = {}
|
|
18
23
|
this.timeout = args.timeout || 600000
|
|
24
|
+
this.useMetaElement = ("useMetaElement" in args) ? args.useMetaElement : true
|
|
19
25
|
|
|
20
26
|
this.connectOnlineEvent()
|
|
21
27
|
this.connectWakeEvent()
|
|
22
28
|
}
|
|
23
29
|
|
|
24
|
-
connectOnlineEvent
|
|
25
|
-
window.addEventListener("online",
|
|
30
|
+
connectOnlineEvent() {
|
|
31
|
+
window.addEventListener("online", this.updateSessionStatus, false)
|
|
26
32
|
}
|
|
27
33
|
|
|
28
|
-
connectWakeEvent
|
|
29
|
-
wakeEvent(
|
|
34
|
+
connectWakeEvent() {
|
|
35
|
+
wakeEvent(this.updateSessionStatus)
|
|
30
36
|
}
|
|
31
37
|
|
|
32
|
-
async
|
|
38
|
+
async getCsrfToken() {
|
|
39
|
+
if (this.csrfToken) {
|
|
40
|
+
logger.debug(`Get CSRF token from set variable: ${this.csrfToken}`)
|
|
41
|
+
|
|
42
|
+
return this.csrfToken
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (this.useMetaElement) {
|
|
46
|
+
const csrfTokenElement = document.querySelector("meta[name='csrf-token']")
|
|
47
|
+
|
|
48
|
+
if (csrfTokenElement) {
|
|
49
|
+
logger.debug(() => `Get CSRF token from meta element: ${csrfTokenElement.getAttribute("content")}`)
|
|
50
|
+
|
|
51
|
+
this.csrfToken = csrfTokenElement.getAttribute("content")
|
|
52
|
+
|
|
53
|
+
return this.csrfToken
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
logger.debug("Updating session status because no CSRF token set yet")
|
|
58
|
+
await this.updateSessionStatus()
|
|
59
|
+
|
|
60
|
+
if (this.csrfToken) {
|
|
61
|
+
logger.debug(() => `Returning CSRF token after updating session status: ${this.csrfToken}`)
|
|
62
|
+
|
|
63
|
+
return this.csrfToken
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
throw new Error("CSRF token hasn't been set")
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
sessionStatus() {
|
|
33
70
|
return new Promise((resolve) => {
|
|
71
|
+
const host = config.getHost()
|
|
72
|
+
let requestPath = ""
|
|
73
|
+
|
|
74
|
+
if (host) requestPath += host
|
|
75
|
+
|
|
76
|
+
requestPath += "/api_maker/session_statuses"
|
|
77
|
+
|
|
34
78
|
const xhr = new XMLHttpRequest()
|
|
35
|
-
xhr.open("POST",
|
|
79
|
+
xhr.open("POST", requestPath, true)
|
|
36
80
|
xhr.onload = () => {
|
|
37
81
|
const response = JSON.parse(xhr.responseText)
|
|
38
82
|
resolve(response)
|
|
@@ -41,11 +85,11 @@ export default class ApiMakerSessionStatusUpdater {
|
|
|
41
85
|
})
|
|
42
86
|
}
|
|
43
87
|
|
|
44
|
-
onSignedOut
|
|
88
|
+
onSignedOut(callback) {
|
|
45
89
|
this.addEvent("onSignedOut", callback)
|
|
46
90
|
}
|
|
47
91
|
|
|
48
|
-
startTimeout
|
|
92
|
+
startTimeout() {
|
|
49
93
|
logger.debug("startTimeout")
|
|
50
94
|
|
|
51
95
|
if (this.updateTimeout)
|
|
@@ -60,12 +104,12 @@ export default class ApiMakerSessionStatusUpdater {
|
|
|
60
104
|
)
|
|
61
105
|
}
|
|
62
106
|
|
|
63
|
-
stopTimeout
|
|
107
|
+
stopTimeout() {
|
|
64
108
|
if (this.updateTimeout)
|
|
65
109
|
clearTimeout(this.updateTimeout)
|
|
66
110
|
}
|
|
67
111
|
|
|
68
|
-
async
|
|
112
|
+
updateSessionStatus = async () => {
|
|
69
113
|
logger.debug("updateSessionStatus")
|
|
70
114
|
|
|
71
115
|
const result = await this.sessionStatus()
|
|
@@ -75,25 +119,30 @@ export default class ApiMakerSessionStatusUpdater {
|
|
|
75
119
|
this.updateUserSessionsFromResult(result)
|
|
76
120
|
}
|
|
77
121
|
|
|
78
|
-
updateMetaElementsFromResult
|
|
122
|
+
updateMetaElementsFromResult(result) {
|
|
79
123
|
logger.debug("updateMetaElementsFromResult")
|
|
80
|
-
const csrfTokenElement = document.querySelector("meta[name='csrf-token']")
|
|
81
124
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
125
|
+
this.csrfToken = result.csrf_token
|
|
126
|
+
|
|
127
|
+
if (this.useMetaElement) {
|
|
128
|
+
const csrfTokenElement = document.querySelector("meta[name='csrf-token']")
|
|
129
|
+
|
|
130
|
+
if (csrfTokenElement) {
|
|
131
|
+
logger.debug(() => `Changing token from "${csrfTokenElement.getAttribute("content")}" to "${result.csrf_token}"`)
|
|
132
|
+
csrfTokenElement.setAttribute("content", result.csrf_token)
|
|
133
|
+
} else {
|
|
134
|
+
logger.debug("csrf token element couldn't be found")
|
|
135
|
+
}
|
|
87
136
|
}
|
|
88
137
|
}
|
|
89
138
|
|
|
90
|
-
updateUserSessionsFromResult
|
|
139
|
+
updateUserSessionsFromResult(result) {
|
|
91
140
|
for (const scopeName in result.scopes) {
|
|
92
141
|
this.updateUserSessionScopeFromResult(scopeName, result.scopes[scopeName])
|
|
93
142
|
}
|
|
94
143
|
}
|
|
95
144
|
|
|
96
|
-
updateUserSessionScopeFromResult
|
|
145
|
+
updateUserSessionScopeFromResult(scopeName, scope) {
|
|
97
146
|
const deviseIsSignedInMethodName = `is${inflection.camelize(scopeName)}SignedIn`
|
|
98
147
|
|
|
99
148
|
if (!(deviseIsSignedInMethodName in Devise)) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import ConfigReader from "./config-reader.jsx"
|
|
2
|
+
|
|
3
|
+
const hasEditConfig = (modelClass) => {
|
|
4
|
+
const configReader = ConfigReader.forModel(modelClass)
|
|
5
|
+
const extraContent = configReader.modelConfig?.edit?.extraContentconst
|
|
6
|
+
const attributes = configReader.modelConfig?.edit?.attributes
|
|
7
|
+
|
|
8
|
+
if (attributes || extraContent) {
|
|
9
|
+
return true
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return false
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default hasEditConfig
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import EditPage from "./edit-page"
|
|
2
|
+
import hasEditConfig from "./has-edit-config.js"
|
|
2
3
|
import IndexPage from "./index-page"
|
|
3
4
|
import Layout from "./layout"
|
|
4
5
|
import Link from "../link"
|
|
@@ -9,9 +10,11 @@ import ShowPage from "./show-page"
|
|
|
9
10
|
import ShowReflectionActions from "./show-reflection-actions"
|
|
10
11
|
import ShowReflectionPage from "./show-reflection-page"
|
|
11
12
|
import useCanCan from "../use-can-can"
|
|
13
|
+
import useCurrentUser from "../use-current-user.mjs"
|
|
12
14
|
import useQueryParams from "on-location-changed/src/use-query-params"
|
|
13
15
|
|
|
14
16
|
const ApiMakerSuperAdmin = () => {
|
|
17
|
+
const currentUser = useCurrentUser()
|
|
15
18
|
const queryParams = useQueryParams()
|
|
16
19
|
let modelClass, pageToShow
|
|
17
20
|
|
|
@@ -20,13 +23,16 @@ const ApiMakerSuperAdmin = () => {
|
|
|
20
23
|
const modelId = queryParams.model_id
|
|
21
24
|
const modelName = modelClass?.modelClassData()?.name
|
|
22
25
|
const [model, setModel] = useState()
|
|
23
|
-
const canCan = useCanCan(
|
|
24
|
-
|
|
26
|
+
const canCan = useCanCan(
|
|
27
|
+
() => {
|
|
28
|
+
const abilities = []
|
|
25
29
|
|
|
26
|
-
|
|
30
|
+
if (modelClass) abilities.push([modelClass, ["new"]])
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
return abilities
|
|
33
|
+
},
|
|
34
|
+
[currentUser?.id(), modelClass]
|
|
35
|
+
)
|
|
30
36
|
|
|
31
37
|
const loadModel = useCallback(async () => {
|
|
32
38
|
if (modelId && modelClass) {
|
|
@@ -82,7 +88,7 @@ const ApiMakerSuperAdmin = () => {
|
|
|
82
88
|
() => <>
|
|
83
89
|
{modelClass && pageToShow == "index" &&
|
|
84
90
|
<>
|
|
85
|
-
{canCan?.can("new", modelClass) &&
|
|
91
|
+
{canCan?.can("new", modelClass) && hasEditConfig(modelClass) &&
|
|
86
92
|
<Link className="create-new-model-link" to={Params.withParams({model: modelName, mode: "new"})}>
|
|
87
93
|
Create new
|
|
88
94
|
</Link>
|
|
@@ -91,7 +97,7 @@ const ApiMakerSuperAdmin = () => {
|
|
|
91
97
|
}
|
|
92
98
|
{model && pageToShow == "show" &&
|
|
93
99
|
<>
|
|
94
|
-
{model.can("edit") &&
|
|
100
|
+
{model.can("edit") && hasEditConfig(modelClass) &&
|
|
95
101
|
<Link className="edit-model-link" to={Params.withParams({model: modelName, model_id: modelId, mode: "edit"})}>
|
|
96
102
|
Edit
|
|
97
103
|
</Link>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import ConfigReader from "./config-reader"
|
|
1
|
+
import ConfigReader from "./config-reader.jsx"
|
|
2
2
|
import {digg} from "diggerize"
|
|
3
|
+
import hasEditConfig from "./has-edit-config.js"
|
|
3
4
|
import * as inflection from "inflection"
|
|
4
5
|
import Params from "../params"
|
|
5
6
|
import PropTypes from "prop-types"
|
|
@@ -45,7 +46,7 @@ const ApiMakerSuperAdminModelClassTable = (props) => {
|
|
|
45
46
|
<Table
|
|
46
47
|
columns={columns}
|
|
47
48
|
currentUser={currentUser}
|
|
48
|
-
editModelPath={editModelPath}
|
|
49
|
+
editModelPath={hasEditConfig(modelClass) ? editModelPath : undefined}
|
|
49
50
|
modelClass={modelClass}
|
|
50
51
|
viewModelPath={viewModelPath}
|
|
51
52
|
{...tableProps}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import AttributeRow from "../../bootstrap/attribute-row"
|
|
2
2
|
import BelongsToAttributeRow from "./belongs-to-attribute-row"
|
|
3
|
-
import ConfigReader from "../config-reader"
|
|
3
|
+
import ConfigReader from "../config-reader.jsx"
|
|
4
4
|
import {digg} from "diggerize"
|
|
5
5
|
import * as inflection from "inflection"
|
|
6
6
|
import PropTypes from "prop-types"
|
package/src/use-can-can.mjs
CHANGED
package/src/use-current-user.mjs
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import {useCallback, useEffect, useMemo} from "react"
|
|
2
2
|
import {camelize} from "inflection"
|
|
3
3
|
import Devise from "./devise.mjs"
|
|
4
|
+
import Logger from "./logger.mjs"
|
|
4
5
|
import Services from "./services.mjs"
|
|
5
6
|
import useEventEmitter from "./use-event-emitter.mjs"
|
|
6
7
|
import useShape from "set-state-compare/src/use-shape.js"
|
|
7
8
|
|
|
9
|
+
const logger = new Logger({name: "ApiMaker / useCurrentUser"})
|
|
10
|
+
|
|
11
|
+
// logger.setDebug(true)
|
|
12
|
+
|
|
8
13
|
const useCurrentUser = (args) => {
|
|
9
14
|
const s = useShape(args || {})
|
|
10
15
|
const scope = args?.scope || "user"
|
|
@@ -13,20 +18,10 @@ const useCurrentUser = (args) => {
|
|
|
13
18
|
s.meta.scope = scope
|
|
14
19
|
s.meta.scopeName = scopeName
|
|
15
20
|
|
|
16
|
-
const debugs = useCallback((debugCallback) => {
|
|
17
|
-
if (s.props.debug) {
|
|
18
|
-
let debugArgs = debugCallback()
|
|
19
|
-
|
|
20
|
-
if (!Array.isArray(debugArgs)) debugArgs = [debugArgs]
|
|
21
|
-
|
|
22
|
-
console.log("useCurrentUser", ...debugArgs)
|
|
23
|
-
}
|
|
24
|
-
})
|
|
25
|
-
|
|
26
21
|
const loadCurrentUserFromRequest = useCallback(async () => {
|
|
27
22
|
const {scope, scopeName} = s.m
|
|
28
23
|
|
|
29
|
-
|
|
24
|
+
logger.debug(() => `Loading ${scope} with request`)
|
|
30
25
|
|
|
31
26
|
const result = await Services.current().sendRequest("Devise::Current", {scope})
|
|
32
27
|
const current = digg(result, "current")
|
|
@@ -41,21 +36,28 @@ const useCurrentUser = (args) => {
|
|
|
41
36
|
|
|
42
37
|
const defaultCurrentUser = useCallback(() => {
|
|
43
38
|
const {scope, scopeName} = s.m
|
|
39
|
+
let current
|
|
44
40
|
|
|
45
|
-
if (Devise.current().hasCurrentScope(scope)) {
|
|
46
|
-
|
|
41
|
+
if (Devise.current().hasCurrentScope(s.m.scope)) {
|
|
42
|
+
current = Devise.current().getCurrentScope(scope)
|
|
47
43
|
|
|
48
|
-
|
|
44
|
+
logger.debug(() => `Setting ${scope} from current scope: ${current?.id()}`)
|
|
45
|
+
} else if (Devise.current().hasGlobalCurrentScope(scope)) {
|
|
46
|
+
current = Devise[scopeName]()
|
|
49
47
|
|
|
50
|
-
|
|
48
|
+
logger.debug(() => `Setting ${scope} from global current scope: ${current?.id()}`)
|
|
49
|
+
}
|
|
51
50
|
|
|
52
|
-
|
|
51
|
+
if (current && s.props.onCurrentUserLoaded) {
|
|
52
|
+
setTimeout(() => s.props.onCurrentUserLoaded(current), 0)
|
|
53
53
|
}
|
|
54
|
+
|
|
55
|
+
return current
|
|
54
56
|
}, [])
|
|
55
57
|
|
|
56
58
|
const useStatesArgument = {}
|
|
57
59
|
|
|
58
|
-
useStatesArgument[scopeName] = defaultCurrentUser()
|
|
60
|
+
useStatesArgument[scopeName] = () => defaultCurrentUser()
|
|
59
61
|
|
|
60
62
|
s.useStates(useStatesArgument)
|
|
61
63
|
|
|
@@ -68,7 +70,8 @@ const useCurrentUser = (args) => {
|
|
|
68
70
|
}, [])
|
|
69
71
|
|
|
70
72
|
useEffect(() => {
|
|
71
|
-
if (!Devise.current().hasCurrentScope(s.m.scope)) {
|
|
73
|
+
if (!Devise.current().hasGlobalCurrentScope(s.m.scope) && !Devise.current().hasCurrentScope(s.m.scope)) {
|
|
74
|
+
logger.debug(() => `Devise hasn't got current scope ${s.m.scope} so loading from request`)
|
|
72
75
|
loadCurrentUserFromRequest()
|
|
73
76
|
}
|
|
74
77
|
}, [])
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {Platform, useWindowDimensions} from "react-native"
|
|
2
|
+
|
|
3
|
+
const getWindowLayout = (width) => {
|
|
4
|
+
if (width <= 575) {
|
|
5
|
+
return "xs"
|
|
6
|
+
} else if (width <= 767) {
|
|
7
|
+
return "sm"
|
|
8
|
+
} else if (width <= 991) {
|
|
9
|
+
return "md"
|
|
10
|
+
} else if (width <= 1199) {
|
|
11
|
+
return "lg"
|
|
12
|
+
} else if (width <= 1399) {
|
|
13
|
+
return "xl"
|
|
14
|
+
} else if (width >= 1400) {
|
|
15
|
+
return "xxl"
|
|
16
|
+
} else {
|
|
17
|
+
console.error(`Couldn't determine window layout from width: ${width}`)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const useScreenSize = () => {
|
|
22
|
+
if (Platform.OS == "web") {
|
|
23
|
+
const shared = useMemo(() => ({}))
|
|
24
|
+
|
|
25
|
+
shared.width = window.innerWidth
|
|
26
|
+
|
|
27
|
+
const [screenLayout, setScreenLayout] = useState(() => getWindowLayout(shared.width))
|
|
28
|
+
|
|
29
|
+
const onResize = useCallback(() => {
|
|
30
|
+
const newWindowLayout = getWindowLayout(window.innerWidth)
|
|
31
|
+
|
|
32
|
+
if (shared.screenlayout != newWindowLayout) {
|
|
33
|
+
setScreenLayout(newWindowLayout)
|
|
34
|
+
}
|
|
35
|
+
}, [])
|
|
36
|
+
|
|
37
|
+
useEventListener(window, "resize", onResize)
|
|
38
|
+
|
|
39
|
+
shared.screenLayout = screenLayout
|
|
40
|
+
|
|
41
|
+
return shared.screenLayout
|
|
42
|
+
} else {
|
|
43
|
+
const windowDimensions = useWindowDimensions()
|
|
44
|
+
|
|
45
|
+
return getWindowLayout(windowDimensions.width)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default useScreenSize
|