@ossy/resources 1.11.6 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ossy/resources",
3
3
  "description": "Resource domain — aggregate and events for the Ossy resource model",
4
- "version": "1.11.6",
4
+ "version": "1.12.0",
5
5
  "private": false,
6
6
  "type": "module",
7
7
  "main": "./src/index.js",
@@ -19,7 +19,7 @@
19
19
  },
20
20
  "devDependencies": {
21
21
  "@jest/globals": "^30.2.0",
22
- "@ossy/platform": "^1.38.6",
22
+ "@ossy/platform": "^1.39.0",
23
23
  "casual": "^1.6.2",
24
24
  "jest": "^30.2.0"
25
25
  },
@@ -31,5 +31,5 @@
31
31
  "/src",
32
32
  "README.md"
33
33
  ],
34
- "gitHead": "72d150cf20ff7ba25b328bc5b429b065c2840b2b"
34
+ "gitHead": "d9d31182be64a448d575da432af2830d909ad4b9"
35
35
  }
@@ -1,6 +1,6 @@
1
1
  import React from 'react'
2
2
  import { useResource } from '@ossy/sdk-react'
3
- import { Stack, View } from '@ossy/design-system'
3
+ import { View } from '@ossy/design-system'
4
4
 
5
5
  export const AudioResource = ({
6
6
  resourceId,
@@ -9,8 +9,8 @@ export const AudioResource = ({
9
9
  const { resource } = useResource(resourceId)
10
10
 
11
11
  return (
12
- <Stack bordered>
13
- <Stack.Item fill style={{ padding: '16px 8px', height: '100%' }}>
12
+ <View stack bordered>
13
+ <View.Item fill style={{ padding: '16px 8px', height: '100%' }}>
14
14
 
15
15
  <View layout="off-center-s" inset="m" style={{ height: '100%'}}>
16
16
  <View
@@ -21,7 +21,7 @@ export const AudioResource = ({
21
21
  />
22
22
  </View>
23
23
 
24
- </Stack.Item>
25
- </Stack>
24
+ </View.Item>
25
+ </View>
26
26
  )
27
27
  }
@@ -6,11 +6,13 @@ import {
6
6
  Button,
7
7
  View,
8
8
  useInputValue,
9
- Text
9
+ Text,
10
+ useLocale,
10
11
  } from '@ossy/design-system'
11
12
  import { useRouter } from '@ossy/router-react'
12
13
 
13
14
  export const CreateDirectory = () => {
15
+ const { t } = useLocale()
14
16
  const router = useRouter()
15
17
  const { createDirectory } = useResources()
16
18
  const [directoryName, setDirectoryName] = useInputValue('')
@@ -24,11 +26,11 @@ export const CreateDirectory = () => {
24
26
 
25
27
 
26
28
  if (!directoryName || directoryName.trim() === '') {
27
- return setError('Directory name cannot be empty')
29
+ return setError(t('resources.createDirectory.errorEmpty'))
28
30
  }
29
31
 
30
32
  if (directoryName?.startsWith?.('@ossy') || directoryName?.startsWith?.('/@ossy')) {
31
- setError('Directory name cannot start with @ossy')
33
+ setError(t('resources.createDirectory.errorOssyPrefix'))
32
34
  return
33
35
  }
34
36
 
@@ -48,11 +50,10 @@ export const CreateDirectory = () => {
48
50
  return (
49
51
  <View gap="s" style={{ height: '100%' }}>
50
52
 
51
- <Title variant="primary" style={{ marginBottom: 'var(--space-m)' }}>Create a new directory</Title>
53
+ <Title variant="primary" style={{ marginBottom: 'var(--space-m)' }}>{t('resources.createDirectory.title')}</Title>
52
54
 
53
55
  <Text style={{ marginBottom: 'var(--space-m)' }}>
54
- Create a new directory to organize your resources.
55
- The new directory will be created in the location: {directoryLocation}
56
+ {t('resources.createDirectory.description', { location: directoryLocation })}
56
57
  </Text>
57
58
 
58
59
  <form
@@ -64,7 +65,7 @@ export const CreateDirectory = () => {
64
65
  <Input
65
66
  id="path"
66
67
  type="text"
67
- placeholder="Directory name"
68
+ placeholder={t('resources.createDirectory.namePlaceholder')}
68
69
  value={directoryName}
69
70
  onChange={setDirectoryName}
70
71
  style={{ width: '100%', marginBottom: 'var(--space-l)' }}
@@ -78,14 +79,14 @@ export const CreateDirectory = () => {
78
79
  <Button
79
80
  id="cancel"
80
81
  onClick={() => router.navigate('@back')}
81
- >Cancel
82
+ >{t('design-system.cancel')}
82
83
  </Button>
83
84
 
84
85
  <Button
85
86
  id="createDirectory"
86
87
  variant="cta"
87
88
  onClick={onCreateDirectory}
88
- >Create directory
89
+ >{t('resources.createDirectory.submit')}
89
90
  </Button>
90
91
 
91
92
  </View>
@@ -95,4 +96,3 @@ export const CreateDirectory = () => {
95
96
  )
96
97
  }
97
98
 
98
-
@@ -1,73 +1,37 @@
1
1
  import React, { useState } from 'react'
2
- import { useResources, useResourceTemplate } from '@ossy/sdk-react'
3
- import {
4
- Alert,
5
- InputTitle,
6
- Button,
7
- useInputValue,
8
- Fields,
9
- applyFieldChange,
10
- } from '@ossy/design-system'
2
+ import { useResources } from '@ossy/sdk-react'
3
+ import { Alert, useLocale } from '@ossy/design-system'
11
4
  import { useRouter } from '@ossy/router-react'
5
+ import { GenericResourceForm } from './GenericResourceForm.jsx'
12
6
 
13
7
  export const CreateDocument = () => {
8
+ const { t } = useLocale()
14
9
  const router = useRouter()
15
10
  const templateId = router.searchParams.templateId
16
11
  const location = router.searchParams.location
17
- const template = useResourceTemplate(templateId)
18
- const [documentData, setDocumentData] = useState({})
19
12
  const { createDocument } = useResources()
20
- const [documentName, setDocumentName] = useInputValue()
21
13
  const [error, setError] = useState()
22
14
 
23
15
  const onCancel = () => {
24
- setDocumentData({})
25
16
  const search = new URLSearchParams({ location }).toString()
26
17
  router.navigate(`@storage/home?${search}`)
27
18
  }
28
19
 
29
- const onFinish = () => {
30
- setDocumentData({})
31
- const search = new URLSearchParams({ location }).toString()
32
- router.navigate(`@storage/home?${search}`)
33
- }
34
-
35
- const onCreateDocument = () => {
36
- if (documentName === '' || documentName === undefined) {
37
- setError('Document name needs to be set')
38
- return ''
39
- }
40
-
41
- createDocument({
42
- type: templateId,
43
- location: location,
44
- name: documentName,
45
- content: documentData
46
- })
47
- .then(() => onFinish())
48
- .catch(error => { setError(error.message) })
49
- }
50
-
51
- const updateFieldData = event => {
52
- setDocumentData(prev => applyFieldChange(prev, event))
20
+ const onSubmit = ({ name, content, type }) => {
21
+ createDocument({ type, location, name, content })
22
+ .then(() => onCancel())
23
+ .catch((err) => setError(err?.message || t('resources.form.createFailed')))
53
24
  }
54
25
 
55
26
  return (
56
27
  <>
57
- <InputTitle
58
- id="document-name"
59
- type="text"
60
- className="d-block"
61
- placeholder="Untitled document"
62
- value={documentName}
63
- onChange={setDocumentName}
28
+ <GenericResourceForm
29
+ templateId={templateId}
30
+ mode="create"
31
+ onSubmit={onSubmit}
32
+ onCancel={onCancel}
64
33
  />
65
- <Fields data={documentData} onChange={updateFieldData} fields={template?.fields || []} />
66
- { error && <Alert>{error}</Alert>}
67
- <div style={{ display: 'flex', justifyContent: 'flex-end', paddingTop: 'var(--space-l)'}}>
68
- <Button variant="link" onClick={onCancel}>Cancel</Button>
69
- <Button variant="cta" onClick={onCreateDocument}>Create {template?.name}</Button>
70
- </div>
34
+ {error && <Alert>{error}</Alert>}
71
35
  </>
72
36
  )
73
37
  }
package/src/Definition.js CHANGED
@@ -8,12 +8,6 @@ export const Definition = {
8
8
  explore the gallery below to discover valuable assets that align with your vision.
9
9
  We hope you find them useful, and we're here if you have any questions!
10
10
  `,
11
- module: {
12
- id: 'resources',
13
- enabled: true
14
- },
11
+ icon: 'image',
15
12
  statuses: ['beta'],
16
- actions: ['resources.create', 'resources.update-content', 'resources.remove'],
17
- views: ['home.page', 'resources.list', 'resources.get'],
18
- tasks: []
19
- }
13
+ }
@@ -0,0 +1,38 @@
1
+ import React from 'react'
2
+ import { useResource } from '@ossy/sdk-react'
3
+ import { Slot, resourceSlot, View, Title, Text, DelayedRender } from '@ossy/design-system'
4
+
5
+ function GenericResourceCardFallback ({ resource }) {
6
+ if (!resource) return null
7
+ return (
8
+ <View surface="secondary" roundness="m" inset="m" gap="xs">
9
+ <Title variant="secondary">{resource.name}</Title>
10
+ {resource.content?.description && (
11
+ <Text size="s" color="secondary">{resource.content.description}</Text>
12
+ )}
13
+ </View>
14
+ )
15
+ }
16
+
17
+ /** Card-sized resource preview — `resource:{type}/card`. */
18
+ export function GenericResourceCard ({ resourceId, resource: resourceProp }) {
19
+ const { resource: loaded } = useResource(resourceId)
20
+ const resource = resourceProp ?? loaded
21
+
22
+ if (!resource) {
23
+ return (
24
+ <DelayedRender>
25
+ <Text>Loading…</Text>
26
+ </DelayedRender>
27
+ )
28
+ }
29
+
30
+ return (
31
+ <Slot
32
+ name={resourceSlot(resource.type, 'card')}
33
+ resourceId={resourceId}
34
+ resource={resource}
35
+ fallback={<GenericResourceCardFallback resource={resource} />}
36
+ />
37
+ )
38
+ }
@@ -0,0 +1,61 @@
1
+ import React from 'react'
2
+ import { useResource, useResourceTemplate } from '@ossy/sdk-react'
3
+ import {
4
+ Slot,
5
+ resourceSlot,
6
+ View,
7
+ Title,
8
+ Text,
9
+ DelayedRender,
10
+ Fields,
11
+ } from '@ossy/design-system'
12
+ import { ResourceGenericView } from './ResourceGenericView.jsx'
13
+
14
+ function GenericResourceDetailFallback ({ resourceId }) {
15
+ const { resource } = useResource(resourceId)
16
+ const template = useResourceTemplate(resource?.type)
17
+
18
+ if (!resource) {
19
+ return (
20
+ <DelayedRender>
21
+ <Text>Loading…</Text>
22
+ </DelayedRender>
23
+ )
24
+ }
25
+
26
+ if (template?.fields?.length) {
27
+ return (
28
+ <View stack gap="m" inset="m">
29
+ <Title>{resource.name}</Title>
30
+ <Fields data={resource.content || {}} fields={template.fields} />
31
+ </View>
32
+ )
33
+ }
34
+
35
+ return <ResourceGenericView resourceId={resourceId} />
36
+ }
37
+
38
+ /**
39
+ * Generic resource detail host — resolves `resource:{type}/detail` from the slot
40
+ * registry; falls back to template fields or a minimal placeholder.
41
+ */
42
+ export function GenericResourceDetail ({ resourceId }) {
43
+ const { resource } = useResource(resourceId)
44
+
45
+ if (!resource) {
46
+ return (
47
+ <DelayedRender>
48
+ <Text>Loading…</Text>
49
+ </DelayedRender>
50
+ )
51
+ }
52
+
53
+ return (
54
+ <Slot
55
+ name={resourceSlot(resource.type, 'detail')}
56
+ resourceId={resourceId}
57
+ resource={resource}
58
+ fallback={<GenericResourceDetailFallback resourceId={resourceId} />}
59
+ />
60
+ )
61
+ }
@@ -0,0 +1,110 @@
1
+ import React, { useState } from 'react'
2
+ import { useResourceTemplate } from '@ossy/sdk-react'
3
+ import {
4
+ Slot,
5
+ resourceSlot,
6
+ View,
7
+ Title,
8
+ Text,
9
+ InputTitle,
10
+ Button,
11
+ Alert,
12
+ Fields,
13
+ applyFieldChange,
14
+ useInputValue,
15
+ useLocale,
16
+ } from '@ossy/design-system'
17
+
18
+ function GenericResourceFormFallback ({
19
+ templateId,
20
+ mode = 'create',
21
+ initialName = '',
22
+ initialContent = {},
23
+ onSubmit,
24
+ onCancel,
25
+ submitLabel,
26
+ }) {
27
+ const { t } = useLocale()
28
+ const template = useResourceTemplate(templateId)
29
+ const [name, setName] = useInputValue(initialName)
30
+ const [content, setContent] = useState(initialContent)
31
+ const [error, setError] = useState()
32
+
33
+ const handleSubmit = () => {
34
+ if (!name?.trim()) {
35
+ setError(t('resources.form.nameRequired'))
36
+ return
37
+ }
38
+ setError(undefined)
39
+ onSubmit?.({ name: name.trim(), content, type: templateId })
40
+ }
41
+
42
+ return (
43
+ <View stack gap="m">
44
+ <InputTitle
45
+ id="resource-name"
46
+ type="text"
47
+ placeholder={t('resources.form.untitled')}
48
+ value={name}
49
+ onChange={setName}
50
+ />
51
+ <Fields
52
+ data={content}
53
+ onChange={(event) => setContent((prev) => applyFieldChange(prev, event))}
54
+ fields={template?.fields || []}
55
+ />
56
+ {error && <Alert>{error}</Alert>}
57
+ <View layout="row" gap="s" justifyContent="flex-end">
58
+ {onCancel && <Button variant="link" onClick={onCancel}>{t('design-system.cancel')}</Button>}
59
+ <Button variant="cta" onClick={handleSubmit}>
60
+ {submitLabel || (mode === 'edit' ? t('design-system.save') : (template?.name ? `${t('design-system.add')} ${template.name}` : t('resources.form.createResource')))}
61
+ </Button>
62
+ </View>
63
+ </View>
64
+ )
65
+ }
66
+
67
+ /**
68
+ * Generic resource create/edit host — resolves `resource:{type}/form` from the
69
+ * slot registry; falls back to template-driven Fields.
70
+ */
71
+ export function GenericResourceForm ({
72
+ templateId,
73
+ mode = 'create',
74
+ resourceId,
75
+ resource,
76
+ initialName,
77
+ initialContent,
78
+ onSubmit,
79
+ onCancel,
80
+ submitLabel,
81
+ }) {
82
+ const { t } = useLocale()
83
+
84
+ if (!templateId) {
85
+ return <Text>{t('resources.form.selectType')}</Text>
86
+ }
87
+
88
+ return (
89
+ <Slot
90
+ name={resourceSlot(templateId, 'form')}
91
+ templateId={templateId}
92
+ mode={mode}
93
+ resourceId={resourceId}
94
+ resource={resource}
95
+ onSubmit={onSubmit}
96
+ onCancel={onCancel}
97
+ fallback={
98
+ <GenericResourceFormFallback
99
+ templateId={templateId}
100
+ mode={mode}
101
+ initialName={initialName ?? resource?.name}
102
+ initialContent={initialContent ?? resource?.content ?? {}}
103
+ onSubmit={onSubmit}
104
+ onCancel={onCancel}
105
+ submitLabel={submitLabel}
106
+ />
107
+ }
108
+ />
109
+ )
110
+ }
@@ -1,7 +1,6 @@
1
1
  import React from 'react'
2
2
  import { useResource } from '@ossy/sdk-react'
3
3
  import {
4
- Stack,
5
4
  Image
6
5
  } from '@ossy/design-system'
7
6
 
@@ -1,6 +1,6 @@
1
1
  import React from 'react'
2
2
  import { useResource } from '@ossy/sdk-react'
3
- import { Stack } from '@ossy/design-system'
3
+ import { View } from '@ossy/design-system'
4
4
 
5
5
  export const PDFResource = ({
6
6
  resourceId
@@ -8,13 +8,13 @@ export const PDFResource = ({
8
8
  const { resource } = useResource(resourceId)
9
9
 
10
10
  return (
11
- <Stack bordered>
12
- <Stack.Item fill>
13
- <Stack bordered>
11
+ <View stack bordered>
12
+ <View.Item fill>
13
+ <View stack bordered>
14
14
 
15
15
 
16
16
 
17
- <Stack.Item fill style={{ padding: '16px 8px' }}>
17
+ <View.Item fill style={{ padding: '16px 8px' }}>
18
18
 
19
19
  <div style={{ display: 'flex', justifyContent: 'center' }}>
20
20
  <embed
@@ -24,10 +24,10 @@ export const PDFResource = ({
24
24
  height="800px"
25
25
  />
26
26
  </div>
27
- </Stack.Item>
27
+ </View.Item>
28
28
 
29
- </Stack>
30
- </Stack.Item>
31
- </Stack>
29
+ </View>
30
+ </View.Item>
31
+ </View>
32
32
  )
33
33
  }
@@ -55,7 +55,7 @@ export const ResourceContentPage = (props) => {
55
55
 
56
56
  <View layout="row" style={{ justifyContent: 'space-between', borderRadius: 'var(--space-l)', padding: 'var(--space-xxs)', background: 'transparent' }}>
57
57
  <Button variant="neutral" onClick={() => router.back()} style={{ padding: 'var(--space-xs)', borderRadius: '50%' }}>
58
- <Icon name="Previous" />
58
+ <Icon name="chevron-left" />
59
59
  </Button>
60
60
  </View>
61
61
 
@@ -1,13 +1,13 @@
1
1
  import React from 'react'
2
2
  import { useResource, AsyncStatus, useWorkspace } from '@ossy/sdk-react'
3
3
  import { Guide, Text, DelayedRender } from '@ossy/design-system'
4
- import { DocumentView } from './DocumentView.jsx'
5
4
  import { DocumentEdit } from './DocumentEdit.jsx'
6
5
  import { ImageResource } from './ImageResource.jsx'
7
6
  import { AudioResource } from './AudioResource.jsx'
8
7
  import { VideoResource } from './VideoResource.jsx'
9
8
  import { PDFResource } from './PDFResource.jsx'
10
9
  import { ResourceGenericView } from './ResourceGenericView.jsx'
10
+ import { GenericResourceDetail } from './GenericResourceDetail.jsx'
11
11
 
12
12
  export const ResourceFactory = ({
13
13
  resourceId,
@@ -39,8 +39,12 @@ export const ResourceFactory = ({
39
39
  }
40
40
 
41
41
  if (workspace?.resourceTemplates?.find(({ id }) => id === resource.type)) {
42
- if (mode === 'View') return <DocumentView resourceId={resourceId} onClose={_onClose} mode={mode} />
43
- if (mode === 'Edit') return <DocumentEdit resourceId={resourceId} onClose={_onClose} mode={mode} form={form} />
42
+ if (mode === 'View') {
43
+ return <GenericResourceDetail resourceId={resourceId} />
44
+ }
45
+ if (mode === 'Edit') {
46
+ return <DocumentEdit resourceId={resourceId} form={form} />
47
+ }
44
48
  }
45
49
 
46
50
  if (['image/jpeg', 'image/png'].includes(resource.type)) {
@@ -1,9 +1,8 @@
1
1
  import React from 'react'
2
2
  import { useResource } from '@ossy/sdk-react'
3
3
  import {
4
- Stack,
5
4
  View,
6
- Icon2,
5
+ Icon,
7
6
  } from '@ossy/design-system'
8
7
 
9
8
  export const ResourceGenericView = ({
@@ -12,21 +11,21 @@ export const ResourceGenericView = ({
12
11
  const { resource } = useResource(resourceId)
13
12
 
14
13
  return (
15
- <Stack bordered>
16
- <Stack.Item fill>
17
- <Stack bordered>
14
+ <View stack bordered>
15
+ <View.Item fill>
16
+ <View stack bordered>
18
17
 
19
- <Stack.Item fill style={{ padding: '16px 8px' }}>
18
+ <View.Item fill style={{ padding: '16px 8px' }}>
20
19
 
21
20
  <View alignItems="center" justifyContent="center" style={{ height: '100%', maxHeight: '400px' }}>
22
21
  <View roundness="m" style={{ border: '1px solid var(--separator)', padding: 'var(--space-xl) var(--space-xl)'}}>
23
- <Icon2 size="xl" name="file" />
22
+ <Icon size="xl" name="file" />
24
23
  </View>
25
24
  </View>
26
- </Stack.Item>
25
+ </View.Item>
27
26
 
28
- </Stack>
29
- </Stack.Item>
30
- </Stack>
27
+ </View>
28
+ </View.Item>
29
+ </View>
31
30
  )
32
31
  }
@@ -4,7 +4,7 @@ import { useWorkspace } from '@ossy/sdk-react'
4
4
  import {
5
5
  Dropdown,
6
6
  DropZone,
7
- Icon2,
7
+ Icon,
8
8
  Image,
9
9
  View,
10
10
  Text,
@@ -73,7 +73,7 @@ function InlineFolderRow({ name, onChange, onSave, onCancel }) {
73
73
  }}
74
74
  >
75
75
  <View gap="m" layout="row" alignItems="center" style={{ flexGrow: 1, padding: '12px 0 12px 20px' }}>
76
- <Icon2 size="s" name="folder" style={{ fill: 'hsl(0, 0%, 60%)' }} />
76
+ <Icon size="s" name="folder" style={{ fill: 'hsl(0, 0%, 60%)' }} />
77
77
  <input
78
78
  ref={inputRef}
79
79
  value={name}
@@ -163,7 +163,7 @@ function ResourceListItem({
163
163
 
164
164
  if (resource?.type === 'directory') {
165
165
  return (
166
- <Icon2 size="s" name="folder" style={{ fill: 'hsl(0, 0%, 60%)'}} />
166
+ <Icon size="s" name="folder" style={{ fill: 'hsl(0, 0%, 60%)'}} />
167
167
  )
168
168
  }
169
169
 
@@ -178,7 +178,7 @@ function ResourceListItem({
178
178
  }
179
179
 
180
180
  return (
181
- <Icon2 size="s" name={resourceTemplates[resource.type]?.icon || 'file'} style={{ fill: 'hsl(0, 0%, 80%)'}} />
181
+ <Icon size="s" name={resourceTemplates[resource.type]?.icon || 'file'} style={{ fill: 'hsl(0, 0%, 80%)'}} />
182
182
  )
183
183
  }
184
184
 
@@ -4,7 +4,6 @@ import {
4
4
  Title,
5
5
  Overlay,
6
6
  Switch,
7
- Stack,
8
7
  Guide,
9
8
  View,
10
9
  Text,
@@ -133,7 +132,7 @@ export const ResourcePanel = ({
133
132
  }
134
133
 
135
134
  return (
136
- <Stack surface="primary" bordered style={{ height: '100%', width: '50%' }}>
135
+ <View stack surface="primary" bordered style={{ height: '100%', width: '50%' }}>
137
136
 
138
137
  <style href="@design-system/scroll" precedence='low'>{`
139
138
  [data-scroll-hide] {
@@ -148,23 +147,23 @@ export const ResourcePanel = ({
148
147
 
149
148
  {
150
149
  !!error && (
151
- <Stack.Item>
150
+ <View.Item>
152
151
  {error}
153
- </Stack.Item>
152
+ </View.Item>
154
153
  )
155
154
  }
156
155
 
157
- <Stack.Item>
156
+ <View.Item>
158
157
 
159
- <Stack horizontal style={{ height: '48px', alignItems: 'center', gap: '4px' }}>
158
+ <View stack horizontal style={{ height: '48px', alignItems: 'center', gap: '4px' }}>
160
159
 
161
160
  <Switch on={panelViewModes['panel-header']}>
162
161
 
163
162
  <Switch.Case match={[ViewMode.View]}>
164
163
 
165
- <Stack.Item fill surface="primary" style={{ padding: '4px 8px' }}>
164
+ <View.Item fill surface="primary" style={{ padding: '4px 8px' }}>
166
165
  <Title as="h3" variant="tertiary">{resourceName}</Title>
167
- </Stack.Item>
166
+ </View.Item>
168
167
 
169
168
  <Button prefix="trash-empty" variant="command-danger" onClick={onRemoveResource}/>
170
169
  <Button prefix="pen" variant="command" onClick={() => setPanelViewModes(x => ({ ...x, 'panel-header': ViewMode.Edit }))} />
@@ -174,7 +173,7 @@ export const ResourcePanel = ({
174
173
 
175
174
  <Switch.Case match={[ViewMode.Edit]}>
176
175
 
177
- <Stack.Item fill surface="primary" style={{ padding: '4px 8px' }}>
176
+ <View.Item fill surface="primary" style={{ padding: '4px 8px' }}>
178
177
  <InputTitle
179
178
  id="document-name"
180
179
  type="text"
@@ -183,7 +182,7 @@ export const ResourcePanel = ({
183
182
  onChange={setResourceName}
184
183
  onBlur={onRenameResource}
185
184
  />
186
- </Stack.Item>
185
+ </View.Item>
187
186
 
188
187
  <Button prefix="close" variant="command" onClick={() => setPanelViewModes(x => ({ ...x, 'panel-header': ViewMode.View }))} />
189
188
  <Button prefix="check" variant="command" onClick={onRenameResource}/>
@@ -192,11 +191,11 @@ export const ResourcePanel = ({
192
191
 
193
192
  </Switch>
194
193
 
195
- </Stack>
194
+ </View>
196
195
 
197
- </Stack.Item>
196
+ </View.Item>
198
197
 
199
- <Stack.Item fill={true} data-scroll-hide style={{ display: 'flex', flexDirection: 'column', height: '100%', overflowY: 'auto' }}>
198
+ <View.Item fill={true} data-scroll-hide style={{ display: 'flex', flexDirection: 'column', height: '100%', overflowY: 'auto' }}>
200
199
 
201
200
  {panels.map(({ content: Content, ...panel }) => {
202
201
  const isExpanded = [ViewMode.View, ViewMode.Edit].includes(panelViewModes[panel.id])
@@ -239,7 +238,7 @@ export const ResourcePanel = ({
239
238
  )
240
239
  })}
241
240
 
242
- </Stack.Item>
241
+ </View.Item>
243
242
 
244
243
  {
245
244
  overlay === Overlays.RemoveDirectory && (
@@ -267,6 +266,6 @@ export const ResourcePanel = ({
267
266
  )
268
267
  }
269
268
 
270
- </Stack>
269
+ </View>
271
270
  )
272
271
  }
@@ -1,9 +1,8 @@
1
1
  import React from 'react'
2
2
  import { SDK } from '@ossy/sdk'
3
3
  import { useResources, AsyncStatus, WorkspaceProvider } from '@ossy/sdk-react'
4
- import { Title, Text, Switch, View, ImageCard, Tags } from '@ossy/design-system'
4
+ import { Title, Text, Switch, View, ImageCard, Tags, useLocale } from '@ossy/design-system'
5
5
  import { useRouter } from '@ossy/router-react'
6
- import { Definition } from './Definition.js'
7
6
 
8
7
  const sdk = SDK.of({
9
8
  /** Ossy.se workspaceID - used to fetch free resources */
@@ -18,6 +17,7 @@ const freeResourcesGridStyles = {
18
17
 
19
18
  export const ResourcesPage= () => {
20
19
  const router = useRouter()
20
+ const { t } = useLocale()
21
21
  const { status, resources } = useResources('/publications/images/')
22
22
  const tags = resources.flatMap(resource => resource?.content?.tags || [])
23
23
  const tagsCount = tags.reduce((acc, t) => ({ ...acc, [t]: (acc?.[t] ?? 0) + 1 }), {})
@@ -34,8 +34,8 @@ export const ResourcesPage= () => {
34
34
  <View gap="l" style={{ margin: '0 auto' }}>
35
35
 
36
36
  <View gap="m">
37
- <Title>{Definition.title}</Title>
38
- <Text style={{ maxWidth: '900px'}}>{Definition.description}</Text>
37
+ <Title>{t('resources.home.title')}</Title>
38
+ <Text style={{ maxWidth: '900px'}}>{t('resources.home.description')}</Text>
39
39
  </View>
40
40
 
41
41
  <WorkspaceProvider sdk={sdk}>
package/src/Upload.jsx CHANGED
@@ -1,5 +1,5 @@
1
1
  import React, { useState, useEffect } from 'react'
2
- import { Button, View, Text, Upload as _Upload, Icon2 } from '@ossy/design-system'
2
+ import { Button, View, Text, Upload as _Upload, Icon } from '@ossy/design-system'
3
3
  import { useRouter } from '@ossy/router-react'
4
4
 
5
5
  const FlowStage = {
@@ -155,9 +155,9 @@ function UploadImgPreview({ file, size = "32px" }) {
155
155
 
156
156
  function UploadStatus({ status }) {
157
157
  if (status === 'preview') return <></>
158
- if (status === 'uploading') return <Icon2 name="spinner" size="s" animation="rotate" />
159
- if (status === 'uploaded') return <Icon2 name="check" size="s" />
160
- if (status === 'error') return <Icon2 name="close" size="s" />
158
+ if (status === 'uploading') return <Icon name="spinner" size="s" animation="rotate" />
159
+ if (status === 'uploaded') return <Icon name="check" size="s" />
160
+ if (status === 'error') return <Icon name="close" size="s" />
161
161
  }
162
162
 
163
163
  function getFlowStage(files = []) {
@@ -1,26 +1,23 @@
1
1
  import React from 'react'
2
2
  import { useResources } from '@ossy/sdk-react'
3
- import { Title, View, Text, Upload as _Upload } from '@ossy/design-system'
3
+ import { Title, View, Text, useLocale } from '@ossy/design-system'
4
4
  import { useRouter } from '@ossy/router-react'
5
5
  import { Upload } from './Upload.jsx'
6
6
 
7
7
  export const UploadResources = () => {
8
+ const { t } = useLocale()
8
9
  const router = useRouter()
9
10
  const location = router.searchParams.location
10
11
  const { uploadFile } = useResources()
11
12
 
12
13
  return (
13
14
  <View gap="s" style={{ height: '100%' }}>
14
-
15
15
  <View gap="s">
16
- <Title variant="tertiary" style={{ marginBottom: '0' }}>
17
- Upload
18
- </Title>
16
+ <Title variant="tertiary" style={{ marginBottom: '0' }}>{t('resources.upload.title')}</Title>
19
17
  <View layout="row" gap="s">
20
- <Text variant="small" >Location: {location}</Text>
18
+ <Text variant="small">{t('resources.upload.location', { location })}</Text>
21
19
  </View>
22
20
  </View>
23
-
24
21
  <Upload
25
22
  onUpload={(file) => uploadFile(location, file)}
26
23
  onCancel={() => router.navigate(`@storage/home?location=${location}`)}
@@ -28,4 +25,4 @@ export const UploadResources = () => {
28
25
  />
29
26
  </View>
30
27
  )
31
- }
28
+ }
@@ -1,6 +1,6 @@
1
1
  import React from 'react'
2
2
  import { useResource } from '@ossy/sdk-react'
3
- import { Stack } from '@ossy/design-system'
3
+ import { View } from '@ossy/design-system'
4
4
 
5
5
  export const VideoResource = ({
6
6
  resourceId
@@ -8,8 +8,8 @@ export const VideoResource = ({
8
8
  const { resource } = useResource(resourceId)
9
9
 
10
10
  return (
11
- <Stack bordered>
12
- <Stack.Item fill style={{ padding: '16px 8px' }}>
11
+ <View stack bordered>
12
+ <View.Item fill style={{ padding: '16px 8px' }}>
13
13
 
14
14
  <div style={{ display: 'flex', justifyContent: 'center' }}>
15
15
  <video
@@ -18,7 +18,7 @@ export const VideoResource = ({
18
18
  style={{ width: 'auto', height: '400px', margin: 'var(--space-l) auto' }}
19
19
  />
20
20
  </div>
21
- </Stack.Item>
22
- </Stack>
21
+ </View.Item>
22
+ </View>
23
23
  )
24
24
  }
@@ -0,0 +1,48 @@
1
+ {
2
+ "resources.home.documentTitle": "Free Resources",
3
+ "resources.home.title": "Free Resources",
4
+ "resources.home.description": "We believe in providing value at every step of your journey with us. That's why we've curated a collection of free resources. Whether you're looking to enhance your creative projects or simply seeking inspiration, explore the gallery below to discover valuable assets that align with your vision. We hope you find them useful, and we're here if you have any questions!",
5
+ "resources.form.nameRequired": "Name is required",
6
+ "resources.form.untitled": "Untitled",
7
+ "resources.form.selectType": "Select a resource type to continue.",
8
+ "resources.form.createFailed": "Create failed",
9
+ "resources.form.createResource": "Create resource",
10
+ "resources/create.label": "Create resource",
11
+ "resources/create.description": "Create a new resource in the workspace",
12
+ "resources/list.label": "List resources",
13
+ "resources/list.description": "List resources in a workspace location",
14
+ "resources/get.label": "Get resource",
15
+ "resources/get.description": "Fetch a single resource by id",
16
+ "resources/search.label": "Search resources",
17
+ "resources/search.description": "Search resources in a workspace",
18
+ "resources/delete.label": "Delete resource",
19
+ "resources/delete.description": "Delete a resource from the workspace",
20
+ "resources/update-name.label": "Update resource name",
21
+ "resources/update-name.description": "Rename a workspace resource",
22
+ "resources/update-content.label": "Update resource content",
23
+ "resources/update-content.description": "Update the content of a workspace resource",
24
+ "resources/update-location.label": "Update resource location",
25
+ "resources/update-location.description": "Move a resource to a new location",
26
+ "resources/update-access.label": "Update resource access",
27
+ "resources/update-access.description": "Change access settings for a resource",
28
+ "resources/upload-named-version.label": "Upload named version",
29
+ "resources/upload-named-version.description": "Upload a named version of a resource file",
30
+ "resources/create-page-view.label": "Create page view",
31
+ "resources/create-page-view.description": "Record a page view analytics event",
32
+ "resources/get-page-view-stats.label": "Get page view stats",
33
+ "resources/get-page-view-stats.description": "Fetch aggregated page view statistics",
34
+ "resources.create.generic.documentTitle": "Create resource",
35
+ "createDirectory.documentTitle": "Create directory",
36
+ "createDocument.documentTitle": "Create document",
37
+ "uploadMedia.documentTitle": "Upload",
38
+ "@resource.documentTitle": "Resource",
39
+ "resources.upload.title": "Upload",
40
+ "resources.upload.location": "Location: {location}",
41
+ "resources.createDirectory.title": "Create directory",
42
+ "resources.createDocument.title": "Create document",
43
+ "resources.createDirectory.description": "Create a new directory to organize your resources. The new directory will be created in the location: {location}",
44
+ "resources.createDirectory.namePlaceholder": "Directory name",
45
+ "resources.createDirectory.submit": "Create directory",
46
+ "resources.createDirectory.errorEmpty": "Directory name cannot be empty",
47
+ "resources.createDirectory.errorOssyPrefix": "Directory name cannot start with @ossy"
48
+ }
package/src/index.js CHANGED
@@ -15,6 +15,9 @@ export * from './ResourceDescription.jsx'
15
15
  export * from './ResourceDetails.jsx'
16
16
  export * from './ResourceDialogMove.jsx'
17
17
  export * from './ResourceFactory.jsx'
18
+ export * from './GenericResourceDetail.jsx'
19
+ export * from './GenericResourceForm.jsx'
20
+ export * from './GenericResourceCard.jsx'
18
21
  export * from './ResourceGenericView.jsx'
19
22
  export * from './ResourceList.jsx'
20
23
  export * from './ResourcePage.jsx'
@@ -0,0 +1,48 @@
1
+ import React, { useState } from 'react'
2
+ import { useResources } from '@ossy/sdk-react'
3
+ import { View, useLocale } from '@ossy/design-system'
4
+ import { useRouter } from '@ossy/router-react'
5
+ import { GenericResourceForm } from './GenericResourceForm.jsx'
6
+
7
+ export const metadata = {
8
+ id: 'resources/create/generic',
9
+ path: {
10
+ en: '/resources/create/:templateId',
11
+ sv: '/resources/skapa/:templateId',
12
+ },
13
+ }
14
+
15
+ /** Generic resource create page — slot lookup with template Fields fallback. */
16
+ export default function GenericResourceCreatePage () {
17
+ const router = useRouter()
18
+ const { t } = useLocale()
19
+ const templateId = decodeURIComponent(router.params.templateId || '')
20
+ const location = router.searchParams.location
21
+ const { createDocument } = useResources()
22
+ const [error, setError] = useState()
23
+
24
+ const onCancel = () => {
25
+ const search = location ? new URLSearchParams({ location }).toString() : ''
26
+ router.navigate(`@storage/home${search ? `?${search}` : ''}`)
27
+ }
28
+
29
+ const onSubmit = ({ name, content, type }) => {
30
+ createDocument({ type, location, name, content })
31
+ .then(() => onCancel())
32
+ .catch((err) => setError(err?.message || t('resources.form.createFailed')))
33
+ }
34
+
35
+ return (
36
+ <View layout="off-center-m" style={{ height: '100%' }}>
37
+ <View slot="content" surface="primary" roundness="m" inset="m">
38
+ <GenericResourceForm
39
+ templateId={templateId}
40
+ mode="create"
41
+ onSubmit={onSubmit}
42
+ onCancel={onCancel}
43
+ />
44
+ {error && <View inset="s"><span style={{ color: 'var(--danger)' }}>{error}</span></View>}
45
+ </View>
46
+ </View>
47
+ )
48
+ }
@@ -0,0 +1,25 @@
1
+ import React from 'react'
2
+ import { View } from '@ossy/design-system'
3
+ import { useRouter } from '@ossy/router-react'
4
+ import { GenericResourceDetail } from './GenericResourceDetail.jsx'
5
+
6
+ export const metadata = {
7
+ id: '@resource',
8
+ path: {
9
+ en: '/resources/item/:resourceId',
10
+ sv: '/resources/objekt/:resourceId',
11
+ },
12
+ }
13
+
14
+ export default function ResourceDetailPage () {
15
+ const router = useRouter()
16
+ const resourceId = router.params.resourceId
17
+
18
+ return (
19
+ <View layout="off-center-m" style={{ height: '100%' }}>
20
+ <View slot="content" surface="primary" roundness="m" inset="m">
21
+ <GenericResourceDetail resourceId={resourceId} />
22
+ </View>
23
+ </View>
24
+ )
25
+ }
@@ -0,0 +1,48 @@
1
+ {
2
+ "resources.home.documentTitle": "Gratis resurser",
3
+ "resources.home.title": "Gratis resurser",
4
+ "resources.home.description": "Vi tror på att leverera värde i varje steg av din resa med oss. Därför har vi samlat gratis resurser. Oavsett om du vill förbättra dina kreativa projekt eller bara söker inspiration kan du utforska galleriet nedan. Vi hoppas att du hittar något användbart — hör av dig om du har frågor!",
5
+ "resources.form.nameRequired": "Namn krävs",
6
+ "resources.form.untitled": "Namnlös",
7
+ "resources.form.selectType": "Välj en resurstyp för att fortsätta.",
8
+ "resources.form.createFailed": "Skapandet misslyckades",
9
+ "resources.form.createResource": "Skapa resurs",
10
+ "resources/create.label": "Skapa resurs",
11
+ "resources/create.description": "Skapa en ny resurs i workspace",
12
+ "resources/list.label": "Lista resurser",
13
+ "resources/list.description": "Lista resurser på en plats i workspace",
14
+ "resources/get.label": "Hämta resurs",
15
+ "resources/get.description": "Hämta en enskild resurs via id",
16
+ "resources/search.label": "Sök resurser",
17
+ "resources/search.description": "Sök resurser i en workspace",
18
+ "resources/delete.label": "Ta bort resurs",
19
+ "resources/delete.description": "Ta bort en resurs från workspace",
20
+ "resources/update-name.label": "Uppdatera resursnamn",
21
+ "resources/update-name.description": "Byt namn på en resurs i workspace",
22
+ "resources/update-content.label": "Uppdatera resursinnehåll",
23
+ "resources/update-content.description": "Uppdatera innehållet i en resurs",
24
+ "resources/update-location.label": "Uppdatera resursplats",
25
+ "resources/update-location.description": "Flytta en resurs till en ny plats",
26
+ "resources/update-access.label": "Uppdatera resursåtkomst",
27
+ "resources/update-access.description": "Ändra åtkomstinställningar för en resurs",
28
+ "resources/upload-named-version.label": "Ladda upp namngiven version",
29
+ "resources/upload-named-version.description": "Ladda upp en namngiven version av en resursfil",
30
+ "resources/create-page-view.label": "Skapa sidvisning",
31
+ "resources/create-page-view.description": "Registrera en sidvisningshändelse för analys",
32
+ "resources/get-page-view-stats.label": "Hämta sidvisningsstatistik",
33
+ "resources/get-page-view-stats.description": "Hämta aggregerad statistik för sidvisningar",
34
+ "resources.create.generic.documentTitle": "Skapa resurs",
35
+ "createDirectory.documentTitle": "Skapa mapp",
36
+ "createDocument.documentTitle": "Skapa dokument",
37
+ "uploadMedia.documentTitle": "Ladda upp",
38
+ "@resource.documentTitle": "Resurs",
39
+ "resources.upload.title": "Ladda upp",
40
+ "resources.upload.location": "Plats: {location}",
41
+ "resources.createDirectory.title": "Skapa mapp",
42
+ "resources.createDocument.title": "Skapa dokument",
43
+ "resources.createDirectory.description": "Skapa en ny mapp för att organisera dina resurser. Den nya mappen skapas på platsen: {location}",
44
+ "resources.createDirectory.namePlaceholder": "Mappnamn",
45
+ "resources.createDirectory.submit": "Skapa mapp",
46
+ "resources.createDirectory.errorEmpty": "Mappnamn får inte vara tomt",
47
+ "resources.createDirectory.errorOssyPrefix": "Mappnamn får inte börja med @ossy"
48
+ }