@ossy/resources 1.12.2 → 3.0.1
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 +16 -4
- package/src/AudioResource.jsx +2 -2
- package/src/CreateDirectory.jsx +2 -10
- package/src/CreateDirectory.page.jsx +1 -1
- package/src/CreateDocument.jsx +2 -2
- package/src/CreateDocument.page.jsx +1 -1
- package/src/Definition.js +3 -2
- package/src/DocumentEdit.jsx +10 -38
- package/src/DocumentView.jsx +11 -22
- package/src/GenericResourceCard.jsx +25 -7
- package/src/GenericResourceDetail.jsx +6 -60
- package/src/GenericResourceForm.jsx +171 -37
- package/src/ImageResource.jsx +1 -1
- package/src/PDFResource.jsx +1 -1
- package/src/PlatformFileField.jsx +239 -0
- package/src/ResourceContentPage.jsx +5 -5
- package/src/ResourceDescription.jsx +1 -1
- package/src/ResourceDetails.jsx +3 -4
- package/src/ResourceDialogMove.jsx +3 -3
- package/src/ResourceFactory.jsx +11 -64
- package/src/ResourceGenericView.jsx +1 -1
- package/src/ResourceList.jsx +89 -106
- package/src/ResourcePanel.jsx +40 -57
- package/src/ResourceTags.jsx +1 -1
- package/src/ResourcesPage.jsx +26 -35
- package/src/SchemaDetailView.jsx +97 -0
- package/src/SchemaFormDefault.jsx +86 -0
- package/src/SchemaPresenter.jsx +207 -0
- package/src/Upload.jsx +6 -25
- package/src/Upload.page.jsx +1 -1
- package/src/UploadResources.jsx +2 -2
- package/src/VideoResource.jsx +1 -1
- package/src/access-filter.spec.js +1 -1
- package/src/create-page-view.action.js +1 -1
- package/src/create-page-view.task.js +10 -9
- package/src/create.action.js +1 -1
- package/src/create.task.js +27 -29
- package/src/delete.action.js +1 -1
- package/src/delete.task.js +9 -13
- package/src/en.translations.json +25 -24
- package/src/get-page-view-stats.action.js +1 -1
- package/src/get-page-view-stats.task.js +3 -4
- package/src/get-resource-alias.api.js +8 -0
- package/src/get-resource-info-alias.api.js +8 -0
- package/src/get-resource-info.api.js +36 -0
- package/src/get-resource-variant.api.js +8 -0
- package/src/get-resource.api.js +51 -0
- package/src/get.action.js +1 -1
- package/src/get.task.js +6 -6
- package/src/index.js +10 -0
- package/src/list.action.js +1 -1
- package/src/list.task.js +1 -1
- package/src/platform-file-field.component.jsx +5 -0
- package/src/platform-image-field.component.jsx +5 -0
- package/src/resource-create.page.jsx +5 -5
- package/src/resource-detail.page.jsx +1 -1
- package/src/resource-read.helpers.js +151 -0
- package/src/resource-stream.helpers.js +20 -0
- package/src/resource-stream.js +108 -0
- package/src/resource.helpers.js +9 -9
- package/src/resources.attach-media-urls.js +9 -3
- package/src/resources.events.js +42 -81
- package/src/{resources.spec.js → resources.integration.spec.js} +19 -19
- package/src/resources.queries.js +5 -2
- package/src/schema-detail.component.jsx +24 -0
- package/src/schema-form-default.component.jsx +14 -0
- package/src/schemaFormSlots.js +6 -0
- package/src/schemaViewSlots.js +6 -0
- package/src/search.action.js +1 -1
- package/src/search.task.js +1 -1
- package/src/server.js +2 -1
- package/src/sv.translations.json +25 -24
- package/src/update-access.action.js +1 -1
- package/src/update-access.task.js +9 -8
- package/src/update-content.action.js +1 -1
- package/src/update-content.task.js +25 -27
- package/src/update-location.action.js +1 -1
- package/src/update-location.task.js +9 -8
- package/src/update-name.action.js +1 -1
- package/src/update-name.task.js +9 -8
- package/src/upload-named-version.action.js +1 -1
- package/src/upload-named-version.task.js +16 -22
- package/src/useActivePath.jsx +3 -8
- package/src/useDocumentValidator.js +16 -0
- package/src/useSchemaEngine.js +9 -0
- package/src/useSchemas.js +29 -0
- package/src/useSelectedResourceId.jsx +36 -0
- package/src/ResourceContentPage.stories.jsx +0 -19
- package/src/resource.aggregate.js +0 -86
package/package.json
CHANGED
|
@@ -1,31 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ossy/resources",
|
|
3
3
|
"description": "Resource domain — aggregate and events for the Ossy resource model",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "3.0.1",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./src/index.js",
|
|
8
8
|
"module": "./src/index.js",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": "./src/index.js",
|
|
11
|
+
"./schemaViewSlots.js": "./src/schemaViewSlots.js",
|
|
12
|
+
"./schemaFormSlots.js": "./src/schemaFormSlots.js",
|
|
13
|
+
"./resource.helpers.js": "./src/resource.helpers.js",
|
|
11
14
|
"./server": "./src/server.js"
|
|
12
15
|
},
|
|
13
16
|
"scripts": {
|
|
14
|
-
"test": "NODE_OPTIONS=--experimental-vm-modules jest --verbose"
|
|
17
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest --verbose",
|
|
18
|
+
"test:integration": "NODE_OPTIONS=--experimental-vm-modules jest --config jest.integration.config.js --verbose"
|
|
15
19
|
},
|
|
16
20
|
"author": "Ossy <yourfriends@ossy.se> (https://ossy.se)",
|
|
17
21
|
"license": "MIT",
|
|
18
22
|
"ossy": {
|
|
19
23
|
"src": "./src"
|
|
20
24
|
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@ossy/event-store": "^3.0.1",
|
|
27
|
+
"@ossy/fold": "^3.0.1",
|
|
28
|
+
"@ossy/platform": "^3.0.1",
|
|
29
|
+
"@ossy/schema": "^3.0.1"
|
|
30
|
+
},
|
|
21
31
|
"peerDependencies": {
|
|
32
|
+
"@ossy/design-system": ">=1.0.0",
|
|
33
|
+
"@ossy/schema": "*",
|
|
22
34
|
"@ossy/sdk-react": ">=1.0.0",
|
|
23
35
|
"@ossy/workspaces": ">=1.0.0",
|
|
24
36
|
"react": ">=19.0.0 <20.0.0"
|
|
25
37
|
},
|
|
26
38
|
"devDependencies": {
|
|
27
39
|
"@jest/globals": "^30.2.0",
|
|
28
|
-
"@ossy/platform": "^
|
|
40
|
+
"@ossy/platform": "^3.0.0",
|
|
29
41
|
"casual": "^1.6.2",
|
|
30
42
|
"jest": "^30.2.0"
|
|
31
43
|
},
|
|
@@ -37,5 +49,5 @@
|
|
|
37
49
|
"/src",
|
|
38
50
|
"README.md"
|
|
39
51
|
],
|
|
40
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "4a70ec216448680d2fddc57b2a8a0077489720ae"
|
|
41
53
|
}
|
package/src/AudioResource.jsx
CHANGED
|
@@ -8,7 +8,7 @@ export const AudioResource = ({
|
|
|
8
8
|
onClose: _onClose
|
|
9
9
|
}) => {
|
|
10
10
|
const sdk = useSdk()
|
|
11
|
-
const { data: resource } = sdk.read(GetResource, {
|
|
11
|
+
const { data: resource } = sdk.read(GetResource, { resourceId }, { enabled: !!resourceId })
|
|
12
12
|
|
|
13
13
|
return (
|
|
14
14
|
<View stack bordered>
|
|
@@ -18,7 +18,7 @@ export const AudioResource = ({
|
|
|
18
18
|
<View
|
|
19
19
|
as="audio"
|
|
20
20
|
controls
|
|
21
|
-
|
|
21
|
+
data-region="content"
|
|
22
22
|
src={resource.content.src}
|
|
23
23
|
/>
|
|
24
24
|
</View>
|
package/src/CreateDirectory.jsx
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
import React, { useState, useRef } from 'react'
|
|
2
2
|
import { createDirectory } from './resource.helpers.js'
|
|
3
3
|
import { useSdk } from '@ossy/sdk-react'
|
|
4
|
-
import {
|
|
5
|
-
Input,
|
|
6
|
-
Title,
|
|
7
|
-
Button,
|
|
8
|
-
View,
|
|
9
|
-
useInputValue,
|
|
10
|
-
Text,
|
|
11
|
-
useLocale,
|
|
12
|
-
} from '@ossy/design-system'
|
|
4
|
+
import { Input, Button, View, useInputValue, Text, useLocale } from '@ossy/design-system'
|
|
13
5
|
import { useRouter } from '@ossy/router-react'
|
|
14
6
|
|
|
15
7
|
export const CreateDirectory = () => {
|
|
@@ -51,7 +43,7 @@ export const CreateDirectory = () => {
|
|
|
51
43
|
return (
|
|
52
44
|
<View gap="s" style={{ height: '100%' }}>
|
|
53
45
|
|
|
54
|
-
<
|
|
46
|
+
<Text variant="heading-primary" style={{ marginBottom: 'var(--space-m)' }} as="h1" text="resources.createDirectory.title" />
|
|
55
47
|
|
|
56
48
|
<Text style={{ marginBottom: 'var(--space-m)' }}>
|
|
57
49
|
{t('resources.createDirectory.description', { location: directoryLocation })}
|
|
@@ -14,7 +14,7 @@ export const metadata = {
|
|
|
14
14
|
export const CreateDirectoryPage = () => {
|
|
15
15
|
return (
|
|
16
16
|
<View layout="off-center-s" style={{ height: '100%' }}>
|
|
17
|
-
<View
|
|
17
|
+
<View data-region="content" surface="primary" roundness="m" inset="m">
|
|
18
18
|
<CreateDirectory/>
|
|
19
19
|
</View>
|
|
20
20
|
</View>
|
package/src/CreateDocument.jsx
CHANGED
|
@@ -8,7 +8,7 @@ import { GenericResourceForm } from './GenericResourceForm.jsx'
|
|
|
8
8
|
export const CreateDocument = () => {
|
|
9
9
|
const { t } = useLocale()
|
|
10
10
|
const router = useRouter()
|
|
11
|
-
const
|
|
11
|
+
const schemaId = router.searchParams.schemaId
|
|
12
12
|
const location = router.searchParams.location
|
|
13
13
|
const sdk = useSdk()
|
|
14
14
|
const [error, setError] = useState()
|
|
@@ -27,7 +27,7 @@ export const CreateDocument = () => {
|
|
|
27
27
|
return (
|
|
28
28
|
<>
|
|
29
29
|
<GenericResourceForm
|
|
30
|
-
|
|
30
|
+
schemaId={schemaId}
|
|
31
31
|
mode="create"
|
|
32
32
|
onSubmit={onSubmit}
|
|
33
33
|
onCancel={onCancel}
|
|
@@ -14,7 +14,7 @@ export const metadata = {
|
|
|
14
14
|
export const CreateDocumentPage = () => {
|
|
15
15
|
return (
|
|
16
16
|
<View layout="off-center-m" style={{ height: '100%' }}>
|
|
17
|
-
<View
|
|
17
|
+
<View data-region="content" surface='primary' roundness='m' inset="m">
|
|
18
18
|
<CreateDocument />
|
|
19
19
|
</View>
|
|
20
20
|
</View>
|
package/src/Definition.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const Definition = {
|
|
2
2
|
id: 'resources',
|
|
3
|
-
title: '
|
|
3
|
+
title: 'Resources',
|
|
4
4
|
description: `
|
|
5
5
|
We believe in providing value at every step of your journey with us.
|
|
6
6
|
That's why we've curated a collection of free resources.
|
|
@@ -9,5 +9,6 @@ export const Definition = {
|
|
|
9
9
|
We hope you find them useful, and we're here if you have any questions!
|
|
10
10
|
`,
|
|
11
11
|
icon: 'image',
|
|
12
|
-
|
|
12
|
+
status: ['beta'],
|
|
13
|
+
entitlementRequired: false,
|
|
13
14
|
}
|
package/src/DocumentEdit.jsx
CHANGED
|
@@ -1,51 +1,23 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { metadata as GetResource } from './get.action.js'
|
|
3
|
-
import { GetWorkspace } from '@ossy/workspaces'
|
|
4
3
|
import { useSdk } from '@ossy/sdk-react'
|
|
5
|
-
import {
|
|
6
|
-
Text,
|
|
7
|
-
View,
|
|
8
|
-
DelayedRender,
|
|
9
|
-
Fields,
|
|
10
|
-
} from '@ossy/design-system'
|
|
4
|
+
import { GenericResourceForm } from './GenericResourceForm.jsx'
|
|
11
5
|
|
|
6
|
+
/** @deprecated Prefer SchemaPresenter flow="Edit" or GenericResourceForm directly. */
|
|
12
7
|
export const DocumentEdit = ({
|
|
13
8
|
resourceId,
|
|
14
9
|
form,
|
|
15
10
|
}) => {
|
|
16
11
|
const sdk = useSdk()
|
|
17
|
-
const { data: resource } = sdk.read(GetResource, {
|
|
18
|
-
const { data: workspace } = sdk.read(GetWorkspace)
|
|
19
|
-
const template = workspace?.resourceTemplates?.find(t => t.id === resource?.type)
|
|
20
|
-
|
|
21
|
-
if (template?.fields && form.data) {
|
|
22
|
-
return (
|
|
23
|
-
<View style={{ padding: '16px 0' }}>
|
|
24
|
-
<Fields data={form.data} onChange={form.onChange} fields={template.fields} />
|
|
25
|
-
</View>
|
|
26
|
-
)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Loading
|
|
30
|
-
if (template?.fields && !form.data) {
|
|
31
|
-
return (
|
|
32
|
-
<DelayedRender>
|
|
33
|
-
<View style={{ padding: '16px 0' }}>
|
|
34
|
-
Loading...
|
|
35
|
-
</View>
|
|
36
|
-
</DelayedRender>
|
|
37
|
-
)
|
|
38
|
-
}
|
|
12
|
+
const { data: resource } = sdk.read(GetResource, { resourceId }, { enabled: !!resourceId })
|
|
39
13
|
|
|
40
14
|
return (
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
15
|
+
<GenericResourceForm
|
|
16
|
+
schemaId={resource?.type}
|
|
17
|
+
mode="edit"
|
|
18
|
+
resourceId={resourceId}
|
|
19
|
+
resource={resource}
|
|
20
|
+
form={form}
|
|
21
|
+
/>
|
|
48
22
|
)
|
|
49
|
-
|
|
50
|
-
|
|
51
23
|
}
|
package/src/DocumentView.jsx
CHANGED
|
@@ -1,36 +1,25 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import { pipe, keys, map } from 'ramda'
|
|
3
2
|
import { metadata as GetResource } from './get.action.js'
|
|
4
|
-
import { GetWorkspace } from '@ossy/workspaces'
|
|
5
3
|
import { useSdk } from '@ossy/sdk-react'
|
|
6
|
-
import {
|
|
4
|
+
import { useSchema } from './useSchemas.js'
|
|
5
|
+
import { View } from '@ossy/design-system'
|
|
6
|
+
import { SchemaDetailView } from './SchemaDetailView.jsx'
|
|
7
7
|
|
|
8
8
|
export const DocumentView = ({
|
|
9
9
|
resourceId
|
|
10
10
|
}) => {
|
|
11
11
|
const sdk = useSdk()
|
|
12
|
-
const { data: resource } = sdk.read(GetResource, {
|
|
13
|
-
const
|
|
14
|
-
const template = workspace?.resourceTemplates?.find(t => t.id === resource?.type)
|
|
12
|
+
const { data: resource } = sdk.read(GetResource, { resourceId }, { enabled: !!resourceId })
|
|
13
|
+
const template = useSchema(resource?.type)
|
|
15
14
|
|
|
16
15
|
return (
|
|
17
16
|
<View style={{ padding: '16px 0' }}>
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
<Text style={{ fontWeight: 'bold' }} >{field.name}</Text>
|
|
25
|
-
{
|
|
26
|
-
typeof field.value === 'object'
|
|
27
|
-
? pipe(keys, map(key => field.value[key] && <div>{key}</div>))(field.value)
|
|
28
|
-
: <Text style={{ maxWidth: '500px' }}>{field.value}</Text>
|
|
29
|
-
}
|
|
30
|
-
</View>
|
|
31
|
-
))
|
|
32
|
-
}
|
|
33
|
-
</View>
|
|
17
|
+
<SchemaDetailView
|
|
18
|
+
schemaId={resource?.type}
|
|
19
|
+
fields={template?.fields}
|
|
20
|
+
data={resource?.content ?? {}}
|
|
21
|
+
title={resource?.name}
|
|
22
|
+
/>
|
|
34
23
|
</View>
|
|
35
24
|
)
|
|
36
25
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import React from 'react'
|
|
1
|
+
import React, { useMemo } from 'react'
|
|
2
2
|
import { metadata as GetResource } from './get.action.js'
|
|
3
3
|
import { useSdk } from '@ossy/sdk-react'
|
|
4
|
-
import {
|
|
4
|
+
import { resolveViewComponentChain } from '@ossy/schema/resolve'
|
|
5
|
+
import { Slot, View, Text, DelayedRender } from '@ossy/design-system'
|
|
5
6
|
|
|
6
7
|
function GenericResourceCardFallback ({ resource }) {
|
|
7
8
|
if (!resource) return null
|
|
8
9
|
return (
|
|
9
10
|
<View surface="secondary" roundness="m" inset="m" gap="xs">
|
|
10
|
-
<
|
|
11
|
+
<Text variant="heading-secondary" as="h2">{resource.name}</Text>
|
|
11
12
|
{resource.content?.description && (
|
|
12
13
|
<Text size="s" color="secondary">{resource.content.description}</Text>
|
|
13
14
|
)}
|
|
@@ -15,11 +16,15 @@ function GenericResourceCardFallback ({ resource }) {
|
|
|
15
16
|
)
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
/** Card-
|
|
19
|
+
/** Card projection for a schema-backed resource — ADR 0006. */
|
|
19
20
|
export function GenericResourceCard ({ resourceId, resource: resourceProp }) {
|
|
20
21
|
const sdk = useSdk()
|
|
21
|
-
const { data: loaded } = sdk.read(GetResource, {
|
|
22
|
+
const { data: loaded } = sdk.read(GetResource, { resourceId }, { enabled: !!resourceId && !resourceProp })
|
|
22
23
|
const resource = resourceProp ?? loaded
|
|
24
|
+
const chain = useMemo(
|
|
25
|
+
() => (resource?.type ? resolveViewComponentChain(resource.type, 'card') : []),
|
|
26
|
+
[resource?.type],
|
|
27
|
+
)
|
|
23
28
|
|
|
24
29
|
if (!resource) {
|
|
25
30
|
return (
|
|
@@ -29,12 +34,25 @@ export function GenericResourceCard ({ resourceId, resource: resourceProp }) {
|
|
|
29
34
|
)
|
|
30
35
|
}
|
|
31
36
|
|
|
37
|
+
const [primary, secondary] = chain
|
|
38
|
+
|
|
32
39
|
return (
|
|
33
40
|
<Slot
|
|
34
|
-
|
|
41
|
+
view={primary}
|
|
35
42
|
resourceId={resourceId}
|
|
36
43
|
resource={resource}
|
|
37
|
-
|
|
44
|
+
schemaId={resource.type}
|
|
45
|
+
projection="card"
|
|
46
|
+
fallback={
|
|
47
|
+
<Slot
|
|
48
|
+
view={secondary}
|
|
49
|
+
resourceId={resourceId}
|
|
50
|
+
resource={resource}
|
|
51
|
+
schemaId={resource.type}
|
|
52
|
+
projection="card"
|
|
53
|
+
fallback={<GenericResourceCardFallback resource={resource} />}
|
|
54
|
+
/>
|
|
55
|
+
}
|
|
38
56
|
/>
|
|
39
57
|
)
|
|
40
58
|
}
|
|
@@ -1,66 +1,12 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { metadata as GetResource } from './get.action.js'
|
|
3
|
-
import { GetWorkspace } from '@ossy/workspaces'
|
|
4
|
-
import { useSdk } from '@ossy/sdk-react'
|
|
5
|
-
import {
|
|
6
|
-
Slot,
|
|
7
|
-
resourceSlot,
|
|
8
|
-
View,
|
|
9
|
-
Title,
|
|
10
|
-
Text,
|
|
11
|
-
DelayedRender,
|
|
12
|
-
Fields,
|
|
13
|
-
} from '@ossy/design-system'
|
|
14
|
-
import { ResourceGenericView } from './ResourceGenericView.jsx'
|
|
15
|
-
|
|
16
|
-
function GenericResourceDetailFallback ({ resourceId }) {
|
|
17
|
-
const sdk = useSdk()
|
|
18
|
-
const { data: resource } = sdk.read(GetResource, { id: resourceId }, { enabled: !!resourceId })
|
|
19
|
-
const { data: workspace } = sdk.read(GetWorkspace)
|
|
20
|
-
const template = workspace?.resourceTemplates?.find(t => t.id === resource?.type)
|
|
21
|
-
|
|
22
|
-
if (!resource) {
|
|
23
|
-
return (
|
|
24
|
-
<DelayedRender>
|
|
25
|
-
<Text>Loading…</Text>
|
|
26
|
-
</DelayedRender>
|
|
27
|
-
)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (template?.fields?.length) {
|
|
31
|
-
return (
|
|
32
|
-
<View stack gap="m" inset="m">
|
|
33
|
-
<Title>{resource.name}</Title>
|
|
34
|
-
<Fields data={resource.content || {}} fields={template.fields} />
|
|
35
|
-
</View>
|
|
36
|
-
)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return <ResourceGenericView resourceId={resourceId} />
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Generic resource detail host — resolves `resource:{type}/detail` from the slot
|
|
44
|
-
* registry; falls back to template fields or a minimal placeholder.
|
|
45
|
-
*/
|
|
46
|
-
export function GenericResourceDetail ({ resourceId }) {
|
|
47
|
-
const sdk = useSdk()
|
|
48
|
-
const { data: resource } = sdk.read(GetResource, { id: resourceId }, { enabled: !!resourceId })
|
|
49
|
-
|
|
50
|
-
if (!resource) {
|
|
51
|
-
return (
|
|
52
|
-
<DelayedRender>
|
|
53
|
-
<Text>Loading…</Text>
|
|
54
|
-
</DelayedRender>
|
|
55
|
-
)
|
|
56
|
-
}
|
|
1
|
+
import { SchemaPresenter } from './SchemaPresenter.jsx'
|
|
57
2
|
|
|
3
|
+
/** Read view for a schema-backed resource — ADR 0006 component id chain. */
|
|
4
|
+
export function GenericResourceDetail ({ resourceId, projection }) {
|
|
58
5
|
return (
|
|
59
|
-
<
|
|
60
|
-
name={resourceSlot(resource.type, 'detail')}
|
|
6
|
+
<SchemaPresenter
|
|
61
7
|
resourceId={resourceId}
|
|
62
|
-
|
|
63
|
-
|
|
8
|
+
flow="View"
|
|
9
|
+
projection={projection}
|
|
64
10
|
/>
|
|
65
11
|
)
|
|
66
12
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import React, { useState } from 'react'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import React, { useMemo, useState } from 'react'
|
|
2
|
+
import { useSchema } from './useSchemas.js'
|
|
3
|
+
import { useSchemaEngine } from './useSchemaEngine.js'
|
|
4
|
+
import { validateContent } from '@ossy/schema'
|
|
5
|
+
import { resolveFormComponentChain } from '@ossy/schema/resolve'
|
|
4
6
|
import {
|
|
5
7
|
Slot,
|
|
6
|
-
resourceSlot,
|
|
7
8
|
View,
|
|
8
|
-
Title,
|
|
9
9
|
Text,
|
|
10
10
|
InputTitle,
|
|
11
11
|
Button,
|
|
@@ -15,10 +15,11 @@ import {
|
|
|
15
15
|
useInputValue,
|
|
16
16
|
useLocale,
|
|
17
17
|
} from '@ossy/design-system'
|
|
18
|
+
import { SchemaFormDefault } from './SchemaFormDefault.jsx'
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
/** CMS create-resource form — name + schema fields (resources-specific tertiary fallback). */
|
|
21
|
+
function ResourceCreateFormFallback ({
|
|
22
|
+
schemaId,
|
|
22
23
|
initialName = '',
|
|
23
24
|
initialContent = {},
|
|
24
25
|
onSubmit,
|
|
@@ -26,9 +27,8 @@ function GenericResourceFormFallback ({
|
|
|
26
27
|
submitLabel,
|
|
27
28
|
}) {
|
|
28
29
|
const { t } = useLocale()
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
const template = workspace?.resourceTemplates?.find(t => t.id === templateId)
|
|
30
|
+
const template = useSchema(schemaId)
|
|
31
|
+
const engine = useSchemaEngine()
|
|
32
32
|
const [name, setName] = useInputValue(initialName)
|
|
33
33
|
const [content, setContent] = useState(initialContent)
|
|
34
34
|
const [error, setError] = useState()
|
|
@@ -38,8 +38,18 @@ function GenericResourceFormFallback ({
|
|
|
38
38
|
setError(t('resources.form.nameRequired'))
|
|
39
39
|
return
|
|
40
40
|
}
|
|
41
|
+
if (template) {
|
|
42
|
+
const result = validateContent(engine, template, content)
|
|
43
|
+
if (!result.ok) {
|
|
44
|
+
setError(result.message)
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
setError(undefined)
|
|
48
|
+
onSubmit?.({ name: name.trim(), content: result.data, type: schemaId })
|
|
49
|
+
return
|
|
50
|
+
}
|
|
41
51
|
setError(undefined)
|
|
42
|
-
onSubmit?.({ name: name.trim(), content, type:
|
|
52
|
+
onSubmit?.({ name: name.trim(), content, type: schemaId })
|
|
43
53
|
}
|
|
44
54
|
|
|
45
55
|
return (
|
|
@@ -52,62 +62,186 @@ function GenericResourceFormFallback ({
|
|
|
52
62
|
onChange={setName}
|
|
53
63
|
/>
|
|
54
64
|
<Fields
|
|
65
|
+
schemaId={schemaId}
|
|
55
66
|
data={content}
|
|
56
|
-
onChange={(event) =>
|
|
67
|
+
onChange={(event) => {
|
|
68
|
+
setError(undefined)
|
|
69
|
+
setContent((prev) => applyFieldChange(prev, event))
|
|
70
|
+
}}
|
|
57
71
|
fields={template?.fields || []}
|
|
58
72
|
/>
|
|
59
|
-
{error && <Alert>{error}</Alert>}
|
|
73
|
+
{error && <Alert variant="danger">{error}</Alert>}
|
|
60
74
|
<View layout="row" gap="s" justifyContent="flex-end">
|
|
61
|
-
{onCancel && <Button variant="link" onClick={onCancel}
|
|
75
|
+
{onCancel && <Button variant="link" onClick={onCancel} label="design-system.cancel" />}
|
|
62
76
|
<Button variant="cta" onClick={handleSubmit}>
|
|
63
|
-
{submitLabel || (
|
|
77
|
+
{submitLabel || (template?.name ? `${t('design-system.add')} ${template.name}` : t('resources.form.createResource'))}
|
|
64
78
|
</Button>
|
|
65
79
|
</View>
|
|
66
80
|
</View>
|
|
67
81
|
)
|
|
68
82
|
}
|
|
69
83
|
|
|
84
|
+
function ResourceFormFallback ({
|
|
85
|
+
schemaId,
|
|
86
|
+
mode = 'create',
|
|
87
|
+
schemaFormOnly = false,
|
|
88
|
+
form,
|
|
89
|
+
resource,
|
|
90
|
+
initialName,
|
|
91
|
+
initialContent,
|
|
92
|
+
onSubmit,
|
|
93
|
+
onCancel,
|
|
94
|
+
submitLabel,
|
|
95
|
+
fields,
|
|
96
|
+
}) {
|
|
97
|
+
if (form && mode === 'edit' && !fields?.length) {
|
|
98
|
+
return (
|
|
99
|
+
<View style={{ padding: '16px 0' }}>
|
|
100
|
+
<View inset="m">
|
|
101
|
+
<Text text="resources.form.typeRemoved" />
|
|
102
|
+
</View>
|
|
103
|
+
</View>
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (mode === 'create' && !schemaFormOnly) {
|
|
108
|
+
return (
|
|
109
|
+
<ResourceCreateFormFallback
|
|
110
|
+
schemaId={schemaId}
|
|
111
|
+
initialName={initialName ?? resource?.name}
|
|
112
|
+
initialContent={initialContent ?? resource?.content ?? {}}
|
|
113
|
+
onSubmit={onSubmit}
|
|
114
|
+
onCancel={onCancel}
|
|
115
|
+
submitLabel={submitLabel}
|
|
116
|
+
/>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<View inset="m">
|
|
122
|
+
<SchemaFormDefault
|
|
123
|
+
schemaId={schemaId}
|
|
124
|
+
fields={fields}
|
|
125
|
+
mode={mode}
|
|
126
|
+
form={form}
|
|
127
|
+
resource={resource}
|
|
128
|
+
initialContent={initialContent ?? resource?.content ?? {}}
|
|
129
|
+
onSubmit={onSubmit}
|
|
130
|
+
onCancel={onCancel}
|
|
131
|
+
submitLabel={submitLabel}
|
|
132
|
+
/>
|
|
133
|
+
</View>
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
|
|
70
137
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
138
|
+
* Resolves `@ossy/…/form/{concept}` → `@ossy/resources/form/default` (edit) → fallback.
|
|
139
|
+
*
|
|
140
|
+
* Create mode skips the design-system default and falls through to {@link ResourceCreateFormFallback}
|
|
141
|
+
* unless `schemaFormOnly` is set (e.g. Storybook schema preview).
|
|
73
142
|
*/
|
|
74
|
-
|
|
75
|
-
|
|
143
|
+
function SchemaFormSlotPresenter ({
|
|
144
|
+
schemaId,
|
|
76
145
|
mode = 'create',
|
|
146
|
+
schemaFormOnly = false,
|
|
77
147
|
resourceId,
|
|
78
148
|
resource,
|
|
149
|
+
form,
|
|
79
150
|
initialName,
|
|
80
151
|
initialContent,
|
|
81
152
|
onSubmit,
|
|
82
153
|
onCancel,
|
|
83
154
|
submitLabel,
|
|
84
155
|
}) {
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
156
|
+
const template = useSchema(schemaId)
|
|
157
|
+
const chain = useMemo(() => {
|
|
158
|
+
if (!schemaId) return []
|
|
159
|
+
const full = resolveFormComponentChain(schemaId)
|
|
160
|
+
if (mode === 'create' && !schemaFormOnly) return [full[0]]
|
|
161
|
+
return full
|
|
162
|
+
}, [schemaId, mode, schemaFormOnly])
|
|
163
|
+
const [primary, secondary] = chain
|
|
164
|
+
const slotProps = {
|
|
165
|
+
schemaId,
|
|
166
|
+
mode,
|
|
167
|
+
resourceId,
|
|
168
|
+
resource,
|
|
169
|
+
form,
|
|
170
|
+
fields: template?.fields,
|
|
171
|
+
initialContent: initialContent ?? resource?.content ?? {},
|
|
172
|
+
onSubmit,
|
|
173
|
+
onCancel,
|
|
174
|
+
submitLabel,
|
|
89
175
|
}
|
|
90
176
|
|
|
177
|
+
const tertiaryFallback = (
|
|
178
|
+
<ResourceFormFallback
|
|
179
|
+
schemaId={schemaId}
|
|
180
|
+
mode={mode}
|
|
181
|
+
schemaFormOnly={schemaFormOnly}
|
|
182
|
+
form={form}
|
|
183
|
+
resource={resource}
|
|
184
|
+
initialName={initialName ?? resource?.name}
|
|
185
|
+
initialContent={initialContent ?? resource?.content ?? {}}
|
|
186
|
+
onSubmit={onSubmit}
|
|
187
|
+
onCancel={onCancel}
|
|
188
|
+
submitLabel={submitLabel}
|
|
189
|
+
fields={template?.fields}
|
|
190
|
+
/>
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
const fallback = secondary
|
|
194
|
+
? (
|
|
195
|
+
<Slot
|
|
196
|
+
view={secondary}
|
|
197
|
+
{...slotProps}
|
|
198
|
+
fallback={tertiaryFallback}
|
|
199
|
+
/>
|
|
200
|
+
)
|
|
201
|
+
: tertiaryFallback
|
|
202
|
+
|
|
91
203
|
return (
|
|
92
204
|
<Slot
|
|
93
|
-
|
|
94
|
-
|
|
205
|
+
view={primary}
|
|
206
|
+
{...slotProps}
|
|
207
|
+
fallback={fallback}
|
|
208
|
+
/>
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Generic resource create/edit host — ADR 0006 form component chain with CMS create fallback.
|
|
214
|
+
*/
|
|
215
|
+
export function GenericResourceForm ({
|
|
216
|
+
schemaId,
|
|
217
|
+
mode = 'create',
|
|
218
|
+
schemaFormOnly = false,
|
|
219
|
+
resourceId,
|
|
220
|
+
resource,
|
|
221
|
+
form,
|
|
222
|
+
initialName,
|
|
223
|
+
initialContent,
|
|
224
|
+
onSubmit,
|
|
225
|
+
onCancel,
|
|
226
|
+
submitLabel,
|
|
227
|
+
}) {
|
|
228
|
+
if (!schemaId) {
|
|
229
|
+
return <Text text="resources.form.selectType" />
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return (
|
|
233
|
+
<SchemaFormSlotPresenter
|
|
234
|
+
schemaId={schemaId}
|
|
95
235
|
mode={mode}
|
|
236
|
+
schemaFormOnly={schemaFormOnly}
|
|
96
237
|
resourceId={resourceId}
|
|
97
238
|
resource={resource}
|
|
239
|
+
form={form}
|
|
240
|
+
initialName={initialName}
|
|
241
|
+
initialContent={initialContent}
|
|
98
242
|
onSubmit={onSubmit}
|
|
99
243
|
onCancel={onCancel}
|
|
100
|
-
|
|
101
|
-
<GenericResourceFormFallback
|
|
102
|
-
templateId={templateId}
|
|
103
|
-
mode={mode}
|
|
104
|
-
initialName={initialName ?? resource?.name}
|
|
105
|
-
initialContent={initialContent ?? resource?.content ?? {}}
|
|
106
|
-
onSubmit={onSubmit}
|
|
107
|
-
onCancel={onCancel}
|
|
108
|
-
submitLabel={submitLabel}
|
|
109
|
-
/>
|
|
110
|
-
}
|
|
244
|
+
submitLabel={submitLabel}
|
|
111
245
|
/>
|
|
112
246
|
)
|
|
113
247
|
}
|