@stoker-platform/web-app 0.5.222 → 0.5.224
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/CHANGELOG.md +19 -0
- package/package.json +4 -4
- package/src/Calendar.tsx +9 -7
- package/src/Cards.tsx +3 -1
- package/src/Collection.tsx +8 -9
- package/src/Filters.tsx +18 -13
- package/src/Form.tsx +20 -9
- package/src/Map.tsx +9 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @stoker-platform/web-app
|
|
2
2
|
|
|
3
|
+
## 0.5.224
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- feat: add field access groups feature
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @stoker-platform/node-client@0.5.87
|
|
10
|
+
- @stoker-platform/web-client@0.5.93
|
|
11
|
+
- @stoker-platform/utils@0.5.78
|
|
12
|
+
|
|
13
|
+
## 0.5.223
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Updated dependencies
|
|
18
|
+
- @stoker-platform/node-client@0.5.86
|
|
19
|
+
- @stoker-platform/web-client@0.5.92
|
|
20
|
+
- @stoker-platform/utils@0.5.77
|
|
21
|
+
|
|
3
22
|
## 0.5.222
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stoker-platform/web-app",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.224",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"scripts": {
|
|
@@ -51,9 +51,9 @@
|
|
|
51
51
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
52
52
|
"@react-google-maps/api": "^2.20.8",
|
|
53
53
|
"@sentry/react": "^10.56.0",
|
|
54
|
-
"@stoker-platform/node-client": "0.5.
|
|
55
|
-
"@stoker-platform/utils": "0.5.
|
|
56
|
-
"@stoker-platform/web-client": "0.5.
|
|
54
|
+
"@stoker-platform/node-client": "0.5.87",
|
|
55
|
+
"@stoker-platform/utils": "0.5.78",
|
|
56
|
+
"@stoker-platform/web-client": "0.5.93",
|
|
57
57
|
"@tanstack/react-table": "^8.21.3",
|
|
58
58
|
"@types/react": "18.3.13",
|
|
59
59
|
"@types/react-dom": "18.3.1",
|
package/src/Calendar.tsx
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
preloadCollection,
|
|
22
22
|
subscribeMany,
|
|
23
23
|
onStokerPermissionsChange,
|
|
24
|
+
getCurrentUser,
|
|
24
25
|
} from "@stoker-platform/web-client"
|
|
25
26
|
import { useGoToRecord } from "./utils/goToRecord"
|
|
26
27
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
|
@@ -259,6 +260,7 @@ export function Calendar({
|
|
|
259
260
|
const customization = getCollectionConfigModule(labels.collection)
|
|
260
261
|
const permissions = getCurrentUserPermissions()
|
|
261
262
|
if (!permissions?.Role) throw new Error("PERMISSION_DENIED")
|
|
263
|
+
const claims = getCurrentUser()?.token.claims ?? {}
|
|
262
264
|
const location = useLocation()
|
|
263
265
|
const goToRecord = useGoToRecord()
|
|
264
266
|
const { toast } = useToast()
|
|
@@ -524,21 +526,21 @@ export function Calendar({
|
|
|
524
526
|
const allDayFieldSchema = getField(allFields, allDayField)
|
|
525
527
|
|
|
526
528
|
const hasStartUpdateAccess = !!(
|
|
527
|
-
canUpdateField(collection, startFieldSchema, permissions) &&
|
|
529
|
+
canUpdateField(collection, startFieldSchema, permissions, claims) &&
|
|
528
530
|
!systemFields.map((field) => field.name).includes(startField)
|
|
529
531
|
)
|
|
530
532
|
const hasAllDayUpdateAccess =
|
|
531
|
-
allDayFieldSchema && !!canUpdateField(collection, allDayFieldSchema, permissions)
|
|
533
|
+
allDayFieldSchema && !!canUpdateField(collection, allDayFieldSchema, permissions, claims)
|
|
532
534
|
setHasStartUpdateAccess(hasStartUpdateAccess && (!allDayField || hasAllDayUpdateAccess))
|
|
533
535
|
if (endField) {
|
|
534
536
|
const hasEndUpdateAccess = !!(
|
|
535
|
-
canUpdateField(collection, endFieldSchema, permissions) &&
|
|
537
|
+
canUpdateField(collection, endFieldSchema, permissions, claims) &&
|
|
536
538
|
!systemFields.map((field) => field.name).includes(endField)
|
|
537
539
|
)
|
|
538
540
|
setHasEndUpdateAccess(hasEndUpdateAccess && (!allDayField || hasAllDayUpdateAccess))
|
|
539
541
|
}
|
|
540
542
|
if (resourceField) {
|
|
541
|
-
const hasResourceUpdateAccess = !!canUpdateField(collection, resourceFieldSchema, permissions)
|
|
543
|
+
const hasResourceUpdateAccess = !!canUpdateField(collection, resourceFieldSchema, permissions, claims)
|
|
542
544
|
setHasResourceUpdateAccess(hasResourceUpdateAccess)
|
|
543
545
|
}
|
|
544
546
|
|
|
@@ -928,17 +930,17 @@ export function Calendar({
|
|
|
928
930
|
const systemFields = getSystemFieldsSchema()
|
|
929
931
|
|
|
930
932
|
const hasAllDayUpdateAccess =
|
|
931
|
-
allDayFieldSchema && !!canUpdateField(additionalSchema, allDayFieldSchema, permissions)
|
|
933
|
+
allDayFieldSchema && !!canUpdateField(additionalSchema, allDayFieldSchema, permissions, claims)
|
|
932
934
|
const hasStartUpdateAccessAdditional =
|
|
933
935
|
!!(
|
|
934
|
-
canUpdateField(additionalSchema, startFieldSchema, permissions) &&
|
|
936
|
+
canUpdateField(additionalSchema, startFieldSchema, permissions, claims) &&
|
|
935
937
|
!systemFields.map((field) => field.name).includes(startField)
|
|
936
938
|
) &&
|
|
937
939
|
(!allDayField || hasAllDayUpdateAccess)
|
|
938
940
|
let hasEndUpdateAccessAdditional = false
|
|
939
941
|
if (endField) {
|
|
940
942
|
hasEndUpdateAccessAdditional = !!(
|
|
941
|
-
canUpdateField(additionalSchema, endFieldSchema, permissions) &&
|
|
943
|
+
canUpdateField(additionalSchema, endFieldSchema, permissions, claims) &&
|
|
942
944
|
!systemFields.map((field) => field.name).includes(endField) &&
|
|
943
945
|
(!allDayField || hasAllDayUpdateAccess)
|
|
944
946
|
)
|
package/src/Cards.tsx
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
import {
|
|
20
20
|
Cursor,
|
|
21
21
|
getCollectionConfigModule,
|
|
22
|
+
getCurrentUser,
|
|
22
23
|
getCurrentUserPermissions,
|
|
23
24
|
onStokerPermissionsChange,
|
|
24
25
|
subscribeOne,
|
|
@@ -94,11 +95,12 @@ function CardItem({ index, style, data }: CardItemProps) {
|
|
|
94
95
|
const systemFields = getSystemFieldsSchema()
|
|
95
96
|
const customization = getCollectionConfigModule(labels.collection)
|
|
96
97
|
const permissions = getCurrentUserPermissions()
|
|
98
|
+
const claims = getCurrentUser()?.token.claims ?? {}
|
|
97
99
|
const location = useLocation()
|
|
98
100
|
if (!permissions) {
|
|
99
101
|
throw new Error("PERMISSION_DENIED")
|
|
100
102
|
}
|
|
101
|
-
const hasUpdateAccess = !!canUpdateField(collection, statusField, permissions)
|
|
103
|
+
const hasUpdateAccess = !!canUpdateField(collection, statusField, permissions, claims)
|
|
102
104
|
const goToRecord = useGoToRecord()
|
|
103
105
|
const [connectionStatus] = useConnection()
|
|
104
106
|
const { isGlobalLoading } = useGlobalLoading()
|
package/src/Collection.tsx
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
SubscribeManyOptions,
|
|
38
38
|
updateRecord,
|
|
39
39
|
getPreloadListeners,
|
|
40
|
+
getCurrentUser,
|
|
40
41
|
} from "@stoker-platform/web-client"
|
|
41
42
|
import { createElement, useCallback, useEffect, useMemo, useRef, useState } from "react"
|
|
42
43
|
import { useLocation, useNavigate } from "react-router"
|
|
@@ -163,6 +164,7 @@ function Collection({
|
|
|
163
164
|
const timezone = getTimezone()
|
|
164
165
|
const [permissions, setPermissions] = useState<StokerPermissions | null>(() => getCurrentUserPermissions())
|
|
165
166
|
if (!permissions?.Role) throw new Error("PERMISSION_DENIED")
|
|
167
|
+
const claims = getCurrentUser()?.token.claims ?? {}
|
|
166
168
|
const { toast } = useToast()
|
|
167
169
|
|
|
168
170
|
const isServerReadOnly = serverReadOnly(collection)
|
|
@@ -720,7 +722,7 @@ function Collection({
|
|
|
720
722
|
}
|
|
721
723
|
}
|
|
722
724
|
if (!isPreloadCacheEnabled || relationList?.loadAll) {
|
|
723
|
-
|
|
725
|
+
loadedKeys.current.add(key)
|
|
724
726
|
if (loadedKeys.current.size === keysLength.current) {
|
|
725
727
|
setIsRouteLoading("-", location.pathname)
|
|
726
728
|
}
|
|
@@ -1596,13 +1598,13 @@ function Collection({
|
|
|
1596
1598
|
if (!relationCollection?.fullTextSearch) return false
|
|
1597
1599
|
const collectionPermissions = permissions.collections?.[relationCollection.labels.collection]
|
|
1598
1600
|
const fullCollectionAccess = collectionPermissions && collectionAccess("Read", collectionPermissions)
|
|
1599
|
-
const dependencyAccess = hasDependencyAccess(relationCollection, schema, permissions)
|
|
1601
|
+
const dependencyAccess = hasDependencyAccess(relationCollection, schema, permissions, claims)
|
|
1600
1602
|
if (!fullCollectionAccess && dependencyAccess.length === 0) return false
|
|
1601
1603
|
}
|
|
1602
1604
|
|
|
1603
1605
|
return true
|
|
1604
1606
|
})
|
|
1605
|
-
}, [filters, excludedFilters, fields, permissions, schema])
|
|
1607
|
+
}, [filters, excludedFilters, fields, permissions, schema, claims])
|
|
1606
1608
|
|
|
1607
1609
|
const hasRangeFilter = useMemo(() => {
|
|
1608
1610
|
return filters.some((filter) => filter.type === "range" && !isPreloadCacheEnabled)
|
|
@@ -1820,11 +1822,8 @@ function Collection({
|
|
|
1820
1822
|
return schema.collections[collectionName] || collection
|
|
1821
1823
|
}, [selectedCreateCollection, labels.collection, schema, collection])
|
|
1822
1824
|
|
|
1823
|
-
const
|
|
1824
|
-
return
|
|
1825
|
-
additionalTitles?.[selectedCreateCollection || labels.collection]?.record ||
|
|
1826
|
-
createCollectionSchema.labels.record
|
|
1827
|
-
)
|
|
1825
|
+
const additionalRecordTitle = useMemo(() => {
|
|
1826
|
+
return additionalTitles?.[selectedCreateCollection || labels.collection]?.record
|
|
1828
1827
|
}, [createCollectionSchema, additionalTitles, selectedCreateCollection, labels.collection])
|
|
1829
1828
|
|
|
1830
1829
|
const createPrePopulatedRecord = useCallback(() => {
|
|
@@ -2778,7 +2777,7 @@ function Collection({
|
|
|
2778
2777
|
className="font-medium leading-none"
|
|
2779
2778
|
>
|
|
2780
2779
|
Add{" "}
|
|
2781
|
-
{
|
|
2780
|
+
{additionalRecordTitle ||
|
|
2782
2781
|
recordTitle}
|
|
2783
2782
|
</h4>
|
|
2784
2783
|
<Button
|
package/src/Filters.tsx
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
} from "@stoker-platform/utils"
|
|
21
21
|
import {
|
|
22
22
|
getCollectionConfigModule,
|
|
23
|
+
getCurrentUser,
|
|
23
24
|
getCurrentUserPermissions,
|
|
24
25
|
getOne,
|
|
25
26
|
getSchema,
|
|
@@ -77,6 +78,7 @@ export function Filters({
|
|
|
77
78
|
const schema = getSchema()
|
|
78
79
|
const permissions = getCurrentUserPermissions()
|
|
79
80
|
if (!permissions?.Role) throw new Error("PERMISSION_DENIED")
|
|
81
|
+
const claims = getCurrentUser()?.token.claims ?? {}
|
|
80
82
|
const customization = getCollectionConfigModule(labels.collection)
|
|
81
83
|
|
|
82
84
|
const { filters, setFilters } = useFilters()
|
|
@@ -125,19 +127,22 @@ export function Filters({
|
|
|
125
127
|
const { offset: keyboardOffset, viewportHeight } = useKeyboardOffset(isMobile && someFilterOpen)
|
|
126
128
|
const sheetHeight = Math.max(240, Math.min(viewportHeight - 16, viewportHeight * 0.9))
|
|
127
129
|
|
|
128
|
-
const hasRelationFilterAccess = useCallback(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
130
|
+
const hasRelationFilterAccess = useCallback(
|
|
131
|
+
(filter: Filter) => {
|
|
132
|
+
if (filter.type === "status" || filter.type === "range") return false
|
|
133
|
+
const field = getField(fields, filter.field)
|
|
134
|
+
if (!field || !isRelationField(field)) return false
|
|
135
|
+
const relationCollection = schema.collections[field.collection]
|
|
136
|
+
if (!relationCollection) return false
|
|
137
|
+
const collectionPermissions = permissions?.collections?.[relationCollection.labels.collection]
|
|
138
|
+
const fullCollectionAccess = collectionPermissions && collectionAccess("Read", collectionPermissions)
|
|
139
|
+
const dependencyAccess = hasDependencyAccess(relationCollection, schema, permissions, claims)
|
|
140
|
+
if (!fullCollectionAccess && dependencyAccess.length === 0) return false
|
|
141
|
+
if (relationList && relationList.field === filter.field) return false
|
|
142
|
+
return true
|
|
143
|
+
},
|
|
144
|
+
[permissions, claims, schema],
|
|
145
|
+
)
|
|
141
146
|
|
|
142
147
|
useEffect(() => {
|
|
143
148
|
const initialize = async () => {
|
package/src/Form.tsx
CHANGED
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
tryFunction,
|
|
34
34
|
getInputSchema,
|
|
35
35
|
getField,
|
|
36
|
+
privateFieldAccess,
|
|
36
37
|
restrictCreateAccess,
|
|
37
38
|
restrictUpdateAccess,
|
|
38
39
|
collectionAccess,
|
|
@@ -42,6 +43,7 @@ import {
|
|
|
42
43
|
addRecord,
|
|
43
44
|
deleteRecord,
|
|
44
45
|
getCollectionConfigModule,
|
|
46
|
+
getCurrentUser,
|
|
45
47
|
getCurrentUserPermissions,
|
|
46
48
|
getOne,
|
|
47
49
|
updateRecord,
|
|
@@ -296,6 +298,7 @@ const RecordFormField = (props: FieldProps) => {
|
|
|
296
298
|
const customization = getFieldCustomization(field, customizationFile)
|
|
297
299
|
const admin = customization.admin
|
|
298
300
|
const permissions = getCurrentUserPermissions()
|
|
301
|
+
const userClaims = getCurrentUser()?.token.claims ?? {}
|
|
299
302
|
if (!permissions?.Role) throw new Error("PERMISSION_DENIED")
|
|
300
303
|
|
|
301
304
|
const [label, setLabel] = useState("")
|
|
@@ -402,7 +405,7 @@ const RecordFormField = (props: FieldProps) => {
|
|
|
402
405
|
return null
|
|
403
406
|
}
|
|
404
407
|
|
|
405
|
-
if (operation === "create" && !restrictCreateAccess(field, permissions)) {
|
|
408
|
+
if (operation === "create" && !restrictCreateAccess(field, permissions, collection.labels.collection, userClaims)) {
|
|
406
409
|
return null
|
|
407
410
|
}
|
|
408
411
|
|
|
@@ -413,7 +416,11 @@ const RecordFormField = (props: FieldProps) => {
|
|
|
413
416
|
if (operation === "update-many") {
|
|
414
417
|
if ("unique" in field && field.unique) return null
|
|
415
418
|
if (customization?.admin && "readOnly" in customization.admin) return null
|
|
416
|
-
if (
|
|
419
|
+
if (
|
|
420
|
+
"restrictUpdate" in field &&
|
|
421
|
+
!restrictUpdateAccess(field, permissions, collection.labels.collection, userClaims)
|
|
422
|
+
)
|
|
423
|
+
return null
|
|
417
424
|
|
|
418
425
|
if (collection.auth && field.name === "Role") return null
|
|
419
426
|
if (connectionStatus === "offline" && collection.auth) return null
|
|
@@ -425,7 +432,7 @@ const RecordFormField = (props: FieldProps) => {
|
|
|
425
432
|
const isReadOnly =
|
|
426
433
|
(readOnly && !isRelationField(field)) ||
|
|
427
434
|
(operation === "update" &&
|
|
428
|
-
(!restrictUpdateAccess(field, permissions) ||
|
|
435
|
+
(!restrictUpdateAccess(field, permissions, collection.labels.collection, userClaims) ||
|
|
429
436
|
(collection.auth && field.name === "Role" && record?.User_ID) ||
|
|
430
437
|
!hasUpdateAccess ||
|
|
431
438
|
isUpdateDisabled))
|
|
@@ -3948,22 +3955,25 @@ function RecordForm({
|
|
|
3948
3955
|
delete values[field.name]
|
|
3949
3956
|
}
|
|
3950
3957
|
|
|
3951
|
-
|
|
3958
|
+
const userClaims = getCurrentUser()?.token.claims ?? {}
|
|
3959
|
+
if (
|
|
3960
|
+
field.access &&
|
|
3961
|
+
permissions?.Role &&
|
|
3962
|
+
!privateFieldAccess(field, permissions, collection, userClaims)
|
|
3963
|
+
) {
|
|
3952
3964
|
delete values[field.name]
|
|
3953
3965
|
}
|
|
3954
3966
|
if (
|
|
3955
3967
|
operation === "create" &&
|
|
3956
3968
|
field.restrictCreate &&
|
|
3957
|
-
(field
|
|
3958
|
-
(permissions?.Role && !field.restrictCreate.includes(permissions.Role)))
|
|
3969
|
+
!restrictCreateAccess(field, permissions, collection.labels.collection, userClaims)
|
|
3959
3970
|
) {
|
|
3960
3971
|
delete values[field.name]
|
|
3961
3972
|
}
|
|
3962
3973
|
if (
|
|
3963
3974
|
operation === "update" &&
|
|
3964
3975
|
field.restrictUpdate &&
|
|
3965
|
-
(field
|
|
3966
|
-
(permissions?.Role && !field.restrictUpdate.includes(permissions.Role)))
|
|
3976
|
+
!restrictUpdateAccess(field, permissions, collection.labels.collection, userClaims)
|
|
3967
3977
|
) {
|
|
3968
3978
|
delete values[field.name]
|
|
3969
3979
|
}
|
|
@@ -4568,11 +4578,12 @@ function RecordForm({
|
|
|
4568
4578
|
const recordToDuplicate: Partial<StokerRecord> = {}
|
|
4569
4579
|
for (const field of fields) {
|
|
4570
4580
|
const fieldCustomization = getFieldCustomization(field, customization)
|
|
4581
|
+
const userClaims = getCurrentUser()?.token.claims ?? {}
|
|
4571
4582
|
if (
|
|
4572
4583
|
field.type !== "Computed" &&
|
|
4573
4584
|
!(field.type === "Number" && field.autoIncrement) &&
|
|
4574
4585
|
!(collection.auth && field.name === "User_ID") &&
|
|
4575
|
-
restrictCreateAccess(field, permissions) &&
|
|
4586
|
+
restrictCreateAccess(field, permissions, collection.labels.collection, userClaims) &&
|
|
4576
4587
|
fieldCustomization.custom?.initialValue === undefined
|
|
4577
4588
|
) {
|
|
4578
4589
|
recordToDuplicate[field.name] = record[field.name]
|
package/src/Map.tsx
CHANGED
|
@@ -11,6 +11,7 @@ import { canUpdateField, getCachedConfigValue, getField, getSystemFieldsSchema }
|
|
|
11
11
|
import {
|
|
12
12
|
getAppCheck,
|
|
13
13
|
getCollectionConfigModule,
|
|
14
|
+
getCurrentUser,
|
|
14
15
|
getCurrentUserPermissions,
|
|
15
16
|
onStokerPermissionsChange,
|
|
16
17
|
updateRecord,
|
|
@@ -135,6 +136,7 @@ export function Map({
|
|
|
135
136
|
if (!permissions) {
|
|
136
137
|
throw new Error("PERMISSION_DENIED")
|
|
137
138
|
}
|
|
139
|
+
const claims = getCurrentUser()?.token.claims ?? {}
|
|
138
140
|
const location = useLocation()
|
|
139
141
|
const [connectionStatus] = useConnection()
|
|
140
142
|
const goToRecord = useGoToRecord()
|
|
@@ -221,11 +223,16 @@ export function Map({
|
|
|
221
223
|
const coordinatesField = mapConfig?.coordinatesField
|
|
222
224
|
if (addressField && fields.map((field) => field.name).includes(addressField)) {
|
|
223
225
|
const addressFieldSchema = getField(allFields, addressField)
|
|
224
|
-
const hasAddressUpdateAccess = !!canUpdateField(collection, addressFieldSchema, permissions)
|
|
226
|
+
const hasAddressUpdateAccess = !!canUpdateField(collection, addressFieldSchema, permissions, claims)
|
|
225
227
|
setHasLocationUpdateAccess(hasAddressUpdateAccess)
|
|
226
228
|
} else if (coordinatesField && fields.map((field) => field.name).includes(coordinatesField)) {
|
|
227
229
|
const coordinatesFieldSchema = getField(allFields, coordinatesField)
|
|
228
|
-
const hasCoordinatesUpdateAccess = !!canUpdateField(
|
|
230
|
+
const hasCoordinatesUpdateAccess = !!canUpdateField(
|
|
231
|
+
collection,
|
|
232
|
+
coordinatesFieldSchema,
|
|
233
|
+
permissions,
|
|
234
|
+
claims,
|
|
235
|
+
)
|
|
229
236
|
setHasLocationUpdateAccess(hasCoordinatesUpdateAccess)
|
|
230
237
|
}
|
|
231
238
|
|