@ossy/workspaces 1.17.3 → 3.0.2
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/README.md +4 -0
- package/package.json +6 -3
- package/src/AuthenticationGuard.jsx +3 -3
- package/src/Definition.js +1 -1
- package/src/Invitations.page.jsx +1 -1
- package/src/Invite.page.jsx +1 -1
- package/src/SelectWorkspacePage.jsx +4 -4
- package/src/WorkspaceSettings.page.jsx +1 -1
- package/src/accept-invitation.task.js +1 -1
- package/src/create.task.js +1 -1
- package/src/get-schemas.task.js +1 -1
- package/src/import-schemas.task.js +1 -1
- package/src/invite-user.task.js +1 -1
- package/src/accept-invitation.api.js +0 -46
- package/src/select-workspace.api.js +0 -50
- package/src/stories/Users.stories.jsx +0 -35
- package/src/stories/decorators.jsx +0 -59
package/README.md
CHANGED
|
@@ -37,6 +37,10 @@ npm run build
|
|
|
37
37
|
|
|
38
38
|
`@ossy/app` discovers the pages automatically by reading the `"ossy": { "src": "./src" }` field in this package's `package.json`.
|
|
39
39
|
|
|
40
|
+
## Preview
|
|
41
|
+
|
|
42
|
+
Workspace pages are exercised in **`@ossy/app-test`** on the production build path. Open workspace routes from the sidebar or inspect capabilities at `/packages/workspaces`. See [`packages/app-test/README.md`](../app-test/README.md).
|
|
43
|
+
|
|
40
44
|
## Peer dependencies
|
|
41
45
|
|
|
42
46
|
| Package | Version |
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ossy/workspaces",
|
|
3
3
|
"description": "Workspaces feature package - create, select, and manage workspaces, users, and invitations",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "3.0.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
7
7
|
"module": "./src/index.js",
|
|
@@ -22,6 +22,10 @@
|
|
|
22
22
|
"ossy": {
|
|
23
23
|
"src": "./src"
|
|
24
24
|
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@ossy/config": "^3.0.2",
|
|
27
|
+
"@ossy/schema": "^3.0.2"
|
|
28
|
+
},
|
|
25
29
|
"peerDependencies": {
|
|
26
30
|
"@ossy/design-system": ">=1.0.0",
|
|
27
31
|
"@ossy/router-react": ">=1.0.0",
|
|
@@ -30,7 +34,6 @@
|
|
|
30
34
|
},
|
|
31
35
|
"devDependencies": {
|
|
32
36
|
"@jest/globals": "^30.2.0",
|
|
33
|
-
"@ossy/platform": "^1.39.3",
|
|
34
37
|
"casual": "^1.6.2",
|
|
35
38
|
"jest": "^30.2.0"
|
|
36
39
|
},
|
|
@@ -42,5 +45,5 @@
|
|
|
42
45
|
"/src",
|
|
43
46
|
"README.md"
|
|
44
47
|
],
|
|
45
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "f0a8af3c370bbaffe4f7388c14f9345a5c6c56e5"
|
|
46
49
|
}
|
|
@@ -25,7 +25,7 @@ export const AuthenticationGuard = ({ children }) => {
|
|
|
25
25
|
<View layout="off-center" style={{ height: '100%' }}>
|
|
26
26
|
<Guide
|
|
27
27
|
style={{ width: '100vw', maxWidth: '300px' }}
|
|
28
|
-
|
|
28
|
+
data-region="content"
|
|
29
29
|
title={t('authentication.guard.notAuthenticated.title')}
|
|
30
30
|
text={t('authentication.guard.notAuthenticated.text')}
|
|
31
31
|
/>
|
|
@@ -36,7 +36,7 @@ export const AuthenticationGuard = ({ children }) => {
|
|
|
36
36
|
<View layout="off-center" style={{ height: '100%' }}>
|
|
37
37
|
<Guide
|
|
38
38
|
style={{ width: '100vw', maxWidth: '300px' }}
|
|
39
|
-
|
|
39
|
+
data-region="content"
|
|
40
40
|
title={t('authentication.guard.error.title')}
|
|
41
41
|
text={t('authentication.guard.error.text')}
|
|
42
42
|
/>
|
|
@@ -49,7 +49,7 @@ export const AuthenticationGuard = ({ children }) => {
|
|
|
49
49
|
name="spinner"
|
|
50
50
|
style={{ width: '64px', height: '64px' }}
|
|
51
51
|
className="rotate fill-50"
|
|
52
|
-
|
|
52
|
+
data-region="content"
|
|
53
53
|
/>
|
|
54
54
|
</View>
|
|
55
55
|
</Switch.Case>
|
package/src/Definition.js
CHANGED
package/src/Invitations.page.jsx
CHANGED
|
@@ -13,7 +13,7 @@ export const metadata = {
|
|
|
13
13
|
export const InvitationsPage = () => {
|
|
14
14
|
return (
|
|
15
15
|
<View layout="off-center-m" style={{ height: '100%' }}>
|
|
16
|
-
<View
|
|
16
|
+
<View data-region="content" surface="primary" roundness="m" inset="m">
|
|
17
17
|
<Invitations />
|
|
18
18
|
</View>
|
|
19
19
|
</View>
|
package/src/Invite.page.jsx
CHANGED
|
@@ -13,7 +13,7 @@ export const metadata = {
|
|
|
13
13
|
export const InvitePage = () => {
|
|
14
14
|
return (
|
|
15
15
|
<View layout="off-center-m" style={{ height: '100%' }}>
|
|
16
|
-
<View
|
|
16
|
+
<View data-region="content" surface="primary" roundness="m" inset="m">
|
|
17
17
|
<Invite />
|
|
18
18
|
</View>
|
|
19
19
|
</View>
|
|
@@ -24,7 +24,7 @@ export const SelectWorkspacePage = () => {
|
|
|
24
24
|
: asyncStatus
|
|
25
25
|
|
|
26
26
|
const setWorkspace = workspaceId => {
|
|
27
|
-
selectWorkspace(workspaceId, router.getHref('@home/
|
|
27
|
+
selectWorkspace(workspaceId, router.getHref('@home') || '/')
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
return (
|
|
@@ -59,7 +59,7 @@ export const SelectWorkspacePage = () => {
|
|
|
59
59
|
<Switch.Case match={[PageFlow.Error]}>
|
|
60
60
|
<View layout="off-center" style={{ height: '100%' }}>
|
|
61
61
|
<Guide
|
|
62
|
-
|
|
62
|
+
data-region="content"
|
|
63
63
|
style={{ width: '100vw', maxWidth: '500px' }}
|
|
64
64
|
title={t('workspaces.select.errorTitle')}
|
|
65
65
|
text={t('workspaces.select.errorText')}
|
|
@@ -74,7 +74,7 @@ export const SelectWorkspacePage = () => {
|
|
|
74
74
|
|
|
75
75
|
<Switch.Case match={[PageFlow.Loading]}>
|
|
76
76
|
<Icon
|
|
77
|
-
|
|
77
|
+
data-region="content"
|
|
78
78
|
name="spinner"
|
|
79
79
|
style={{ width: '64px', height: '64px' }}
|
|
80
80
|
className="rotate"
|
|
@@ -116,7 +116,7 @@ export const SelectWorkspacePage = () => {
|
|
|
116
116
|
<Switch.Case match={[PageFlow.NoWorkspacesCreated]}>
|
|
117
117
|
<View layout="vertical-top" style={{ flexGrow: '1' }}>
|
|
118
118
|
<Guide
|
|
119
|
-
|
|
119
|
+
data-region="content"
|
|
120
120
|
style={{ padding: 'var(--space-xl) var(--space-m)', width: '100vw', maxWidth: '500px' }}
|
|
121
121
|
title={`🎉 ${t('workspaces.select.emptyTitle')} 🎉`}
|
|
122
122
|
text={t('workspaces.select.emptyText')}
|
|
@@ -13,7 +13,7 @@ export const metadata = {
|
|
|
13
13
|
export const WorkspaceSettingsPage = () => {
|
|
14
14
|
return (
|
|
15
15
|
<View layout="off-center-m" style={{ height: '100%' }}>
|
|
16
|
-
<View
|
|
16
|
+
<View data-region="content" surface="primary" roundness="m" inset="m">
|
|
17
17
|
<GeneralSettings />
|
|
18
18
|
</View>
|
|
19
19
|
</View>
|
|
@@ -4,7 +4,7 @@ import { UserSchema } from '@ossy/users/server'
|
|
|
4
4
|
import { Workspace, WorkspacesEvents } from '@ossy/workspaces/server'
|
|
5
5
|
import { User, UsersEvents } from '@ossy/users/server'
|
|
6
6
|
import { Token, TokenEvents } from '@ossy/tokens/server'
|
|
7
|
-
import { ConfigService } from '@ossy/
|
|
7
|
+
import { ConfigService } from '@ossy/config'
|
|
8
8
|
import { createLogger } from '@ossy/observability'
|
|
9
9
|
|
|
10
10
|
export const metadata = { id: '@ossy/workspaces/tasks/accept-invitation' }
|
package/src/create.task.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Aggregate } from '@ossy/event-store'
|
|
2
2
|
import { Workspace, WorkspacesEvents } from '@ossy/workspaces/server'
|
|
3
|
-
import { ConfigService } from '@ossy/
|
|
3
|
+
import { ConfigService } from '@ossy/config'
|
|
4
4
|
|
|
5
5
|
export const metadata = { id: '@ossy/workspaces/tasks/create' }
|
|
6
6
|
|
package/src/get-schemas.task.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Aggregate } from '@ossy/event-store'
|
|
2
2
|
import { Workspace } from '@ossy/workspaces/server'
|
|
3
|
-
import { getSystemSchemas } from '@ossy/
|
|
3
|
+
import { getSystemSchemas } from '@ossy/schema'
|
|
4
4
|
import { mergeWorkspaceSchemas } from './merge-workspace-schemas.js'
|
|
5
5
|
|
|
6
6
|
export const metadata = { id: '@ossy/workspaces/tasks/get-schemas' }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Aggregate } from '@ossy/event-store'
|
|
2
2
|
import { Workspace, WorkspacesEvents } from '@ossy/workspaces/server'
|
|
3
|
-
import { validateSchemasForImport } from '@ossy/
|
|
3
|
+
import { validateSchemasForImport } from '@ossy/schema'
|
|
4
4
|
|
|
5
5
|
export const metadata = { id: '@ossy/workspaces/tasks/import-schemas' }
|
|
6
6
|
|
package/src/invite-user.task.js
CHANGED
|
@@ -4,7 +4,7 @@ import { UserSchema } from '@ossy/users/server'
|
|
|
4
4
|
import { Workspace, WorkspacesEvents } from '@ossy/workspaces/server'
|
|
5
5
|
import { Token, TokenEvents } from '@ossy/tokens/server'
|
|
6
6
|
import { EmailRenderer } from '@ossy/email'
|
|
7
|
-
import { ConfigService } from '@ossy/
|
|
7
|
+
import { ConfigService } from '@ossy/config'
|
|
8
8
|
import { createLogger } from '@ossy/observability'
|
|
9
9
|
import WorkspaceInvitationEmail, { subject as workspaceInvitationSubject } from './workspace-invitation.email.jsx'
|
|
10
10
|
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ActionService,
|
|
3
|
-
mergeUserAppSettingsCookie,
|
|
4
|
-
setAuthCookie,
|
|
5
|
-
} from '@ossy/platform'
|
|
6
|
-
|
|
7
|
-
export const metadata = {
|
|
8
|
-
id: 'users.accept-invitation',
|
|
9
|
-
path: '/api/v0/users/accept-invitation',
|
|
10
|
-
action: '@ossy/workspaces/actions/accept-invitation',
|
|
11
|
-
method: 'GET',
|
|
12
|
-
query: ['token', 'workspaceId', 'redirect'],
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export default async function handle (req, res) {
|
|
16
|
-
if (req.method !== 'GET') {
|
|
17
|
-
res.setHeader('Allow', 'GET')
|
|
18
|
-
res.status(405).json({ error: 'Method Not Allowed' })
|
|
19
|
-
return
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
const { authToken, workspaceId } = await ActionService.invoke('@ossy/workspaces/actions/accept-invitation', {
|
|
24
|
-
payload: {
|
|
25
|
-
token: req.query.token,
|
|
26
|
-
workspaceId: req.query.workspaceId,
|
|
27
|
-
},
|
|
28
|
-
req,
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
setAuthCookie(res, authToken)
|
|
32
|
-
if (workspaceId) {
|
|
33
|
-
mergeUserAppSettingsCookie(req, res, { workspaceId })
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const redirect = req.query.redirect
|
|
37
|
-
if (redirect && typeof redirect === 'string') {
|
|
38
|
-
res.redirect(302, redirect)
|
|
39
|
-
return
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
res.status(200).json('')
|
|
43
|
-
} catch (err) {
|
|
44
|
-
res.status(err.status || 401).json('')
|
|
45
|
-
}
|
|
46
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ActionService,
|
|
3
|
-
mergeUserAppSettingsCookie,
|
|
4
|
-
} from '@ossy/platform'
|
|
5
|
-
|
|
6
|
-
export const metadata = {
|
|
7
|
-
id: 'users.select-workspace',
|
|
8
|
-
path: '/api/v0/users/select-workspace',
|
|
9
|
-
method: 'GET',
|
|
10
|
-
query: ['workspaceId', 'redirect'],
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export default async function handle (req, res) {
|
|
14
|
-
if (req.method !== 'GET') {
|
|
15
|
-
res.setHeader('Allow', 'GET')
|
|
16
|
-
res.status(405).json({ error: 'Method Not Allowed' })
|
|
17
|
-
return
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (!req.userId || req.userId === 'anonymous') {
|
|
21
|
-
res.status(401).json({ error: 'Unauthorized' })
|
|
22
|
-
return
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const workspaceId = req.query.workspaceId
|
|
26
|
-
if (!workspaceId || typeof workspaceId !== 'string') {
|
|
27
|
-
res.status(400).json({ message: 'No workspaceId provided' })
|
|
28
|
-
return
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
const workspaces = await ActionService.invoke('@ossy/workspaces/actions/list', { req })
|
|
33
|
-
if (!workspaces.some((w) => w.id === workspaceId)) {
|
|
34
|
-
res.status(403).json({ message: 'Forbidden' })
|
|
35
|
-
return
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
mergeUserAppSettingsCookie(req, res, { workspaceId })
|
|
39
|
-
|
|
40
|
-
const redirect = req.query.redirect
|
|
41
|
-
if (redirect && typeof redirect === 'string') {
|
|
42
|
-
res.redirect(302, redirect)
|
|
43
|
-
return
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
res.status(200).json('')
|
|
47
|
-
} catch (err) {
|
|
48
|
-
res.status(err.status || 500).json({ message: err.message || 'Internal Server Error' })
|
|
49
|
-
}
|
|
50
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { View } from '@ossy/design-system'
|
|
3
|
-
import { Users } from '../Users.jsx'
|
|
4
|
-
import { withMockRouter, withMockSdk } from './decorators.jsx'
|
|
5
|
-
|
|
6
|
-
export default {
|
|
7
|
-
title: 'Workspaces/Pages/Users',
|
|
8
|
-
component: Users,
|
|
9
|
-
decorators: [withMockRouter, withMockSdk],
|
|
10
|
-
parameters: {
|
|
11
|
-
layout: 'padded',
|
|
12
|
-
docs: {
|
|
13
|
-
story: {
|
|
14
|
-
inline: true,
|
|
15
|
-
height: 'auto',
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const Default = () => (
|
|
22
|
-
<View style={{ maxWidth: 720, width: '100%' }}>
|
|
23
|
-
<Users />
|
|
24
|
-
</View>
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
export const Empty = () => (
|
|
28
|
-
<View style={{ maxWidth: 720, width: '100%' }}>
|
|
29
|
-
<Users />
|
|
30
|
-
</View>
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
Empty.parameters = {
|
|
34
|
-
mockSdk: { users: [] },
|
|
35
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createMockRouterDecorator,
|
|
3
|
-
createMockSdkDecorator,
|
|
4
|
-
} from '../../../../.storybook/decorators/index.jsx'
|
|
5
|
-
|
|
6
|
-
export const sampleWorkspaceUsers = [
|
|
7
|
-
{ id: 'user-1', firstName: 'Anna', lastName: 'Svensson', email: 'anna@example.com', type: 'User' },
|
|
8
|
-
{ id: 'user-2', firstName: 'Erik', lastName: 'Lind', email: 'erik@example.com', type: 'User' },
|
|
9
|
-
{ id: 'bot-1', firstName: 'Deploy', lastName: 'Bot', email: 'bot@example.com', type: 'Bot' },
|
|
10
|
-
]
|
|
11
|
-
|
|
12
|
-
export const sampleWorkspaces = [
|
|
13
|
-
{ id: 'ws-demo-1', name: 'Demo Workspace' },
|
|
14
|
-
{ id: 'ws-demo-2', name: 'Acme Corp' },
|
|
15
|
-
]
|
|
16
|
-
|
|
17
|
-
export const sampleCurrentUser = {
|
|
18
|
-
id: 'user-1',
|
|
19
|
-
firstName: 'Anna',
|
|
20
|
-
lastName: 'Svensson',
|
|
21
|
-
email: 'anna@example.com',
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function createWorkspaceHandlers (overrides = {}) {
|
|
25
|
-
const users = 'users' in overrides ? overrides.users : sampleWorkspaceUsers
|
|
26
|
-
const workspaces = 'workspaces' in overrides ? overrides.workspaces : sampleWorkspaces
|
|
27
|
-
const currentUser = 'currentUser' in overrides ? overrides.currentUser : sampleCurrentUser
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
'@ossy/workspaces/actions/get-users': async () => users,
|
|
31
|
-
'@ossy/workspaces/actions/list': async () => workspaces,
|
|
32
|
-
'@ossy/authentication/actions/get-current-user': async () => currentUser,
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const defaultMockRouter = {
|
|
37
|
-
href: 'http://localhost/en/workspaces',
|
|
38
|
-
pages: [],
|
|
39
|
-
language: 'en',
|
|
40
|
-
defaultLanguage: 'en',
|
|
41
|
-
supportedLanguages: ['en', 'sv'],
|
|
42
|
-
params: {},
|
|
43
|
-
searchParams: {},
|
|
44
|
-
navigate: () => {},
|
|
45
|
-
getHref: (target) => {
|
|
46
|
-
const id = typeof target === 'string' ? target : target?.id
|
|
47
|
-
if (id === '@workspace/invitations/add') return '/en/invitations/add'
|
|
48
|
-
if (id === '@profile/workspaces/create') return '/en/profile/workspaces/create'
|
|
49
|
-
if (id === '@home/home') return '/en/home'
|
|
50
|
-
if (id === '@storage/home' && target?.params?.workspaceId) {
|
|
51
|
-
return `/en/storage/${target.params.workspaceId}`
|
|
52
|
-
}
|
|
53
|
-
return `/en/${String(id ?? '').replace(/\//g, '-')}`
|
|
54
|
-
},
|
|
55
|
-
back: () => {},
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export const withMockSdk = createMockSdkDecorator(createWorkspaceHandlers)
|
|
59
|
-
export const withMockRouter = createMockRouterDecorator(defaultMockRouter)
|