@ossy/workspaces 1.16.7 → 1.17.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/README.md +1 -0
- package/package.json +5 -4
- package/src/AuthenticationGuard.jsx +23 -10
- package/src/CreateWorkspacePage.jsx +18 -12
- package/src/Definition.js +1 -17
- package/src/GeneralSettings.jsx +27 -70
- package/src/Invitations.jsx +11 -19
- package/src/Invite.jsx +17 -14
- package/src/SelectWorkspacePage.jsx +17 -22
- package/src/Users.jsx +10 -15
- package/src/accept-invitation.action.js +1 -85
- package/src/accept-invitation.task.js +84 -0
- package/src/create-api-token.action.js +1 -17
- package/src/create-api-token.task.js +16 -0
- package/src/create.action.js +1 -30
- package/src/create.task.js +29 -0
- package/src/disable-service.action.js +1 -28
- package/src/disable-service.task.js +27 -0
- package/src/en.translations.json +72 -0
- package/src/enable-service.action.js +1 -28
- package/src/enable-service.task.js +27 -0
- package/src/get-api-tokens.action.js +1 -11
- package/src/get-api-tokens.task.js +10 -0
- package/src/get-invitations.action.js +1 -11
- package/src/get-invitations.task.js +10 -0
- package/src/get-resource-templates.action.js +1 -19
- package/src/get-resource-templates.task.js +18 -0
- package/src/get-users.action.js +1 -14
- package/src/get-users.task.js +13 -0
- package/src/get.action.js +1 -14
- package/src/get.task.js +13 -0
- package/src/import-resource-templates.action.js +1 -27
- package/src/import-resource-templates.task.js +26 -0
- package/src/index.js +14 -2
- package/src/invite-user.action.js +1 -85
- package/src/invite-user.task.js +84 -0
- package/src/list.action.js +1 -9
- package/src/list.task.js +8 -0
- package/src/remove-member.action.js +1 -17
- package/src/remove-member.task.js +16 -0
- package/src/server.js +3 -0
- package/src/sv.translations.json +72 -0
- package/src/sync-workspace-membership.task.js +4 -3
- package/src/workspaces.spec.js +71 -80
- package/src/current.api.js +0 -30
- package/src/invitations.api.js +0 -46
- package/src/item.api.js +0 -32
- package/src/list.api.js +0 -35
- package/src/members.api.js +0 -42
- package/src/resource-templates.api.js +0 -35
- package/src/services-disable.api.js +0 -23
- package/src/services-enable.api.js +0 -23
- package/src/tokens.api.js +0 -35
package/README.md
CHANGED
|
@@ -26,6 +26,7 @@ Workspaces **feature package** for the Ossy platform — provides workspace crea
|
|
|
26
26
|
| `AuthenticationGuard` | Wraps children, shows error for unauthenticated users |
|
|
27
27
|
| `patchUserWorkspaceId` | Utility to update the active workspace cookie |
|
|
28
28
|
| `Definition` | Feature metadata (id, actions, views) |
|
|
29
|
+
| `ListWorkspaces`, `GetWorkspace`, … | Platform action POJOs — use with `useSdk().read()` / `invoke()` |
|
|
29
30
|
|
|
30
31
|
## Usage
|
|
31
32
|
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ossy/workspaces",
|
|
3
|
-
"description": "Workspaces feature package
|
|
4
|
-
"version": "1.
|
|
3
|
+
"description": "Workspaces feature package - create, select, and manage workspaces, users, and invitations",
|
|
4
|
+
"version": "1.17.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
7
7
|
"module": "./src/index.js",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": "./src/index.js",
|
|
10
|
+
"./server": "./src/server.js",
|
|
10
11
|
"./workspace-invitation.email.jsx": "./src/workspace-invitation.email.jsx"
|
|
11
12
|
},
|
|
12
13
|
"scripts": {
|
|
@@ -25,7 +26,7 @@
|
|
|
25
26
|
},
|
|
26
27
|
"devDependencies": {
|
|
27
28
|
"@jest/globals": "^30.2.0",
|
|
28
|
-
"@ossy/platform": "^1.
|
|
29
|
+
"@ossy/platform": "^1.39.1",
|
|
29
30
|
"casual": "^1.6.2",
|
|
30
31
|
"jest": "^30.2.0"
|
|
31
32
|
},
|
|
@@ -37,5 +38,5 @@
|
|
|
37
38
|
"/src",
|
|
38
39
|
"README.md"
|
|
39
40
|
],
|
|
40
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "c0ba5d90749690634e4dc2705178ff8d89dd3070"
|
|
41
42
|
}
|
|
@@ -1,20 +1,33 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { AuthenticationStatus,
|
|
3
|
-
import {
|
|
1
|
+
import React, { useMemo } from 'react'
|
|
2
|
+
import { AuthenticationStatus, GetCurrentUser } from '@ossy/authentication'
|
|
3
|
+
import { useSdk, AsyncStatus } from '@ossy/sdk-react'
|
|
4
|
+
import { Switch, View, Guide, Icon, useLocale } from '@ossy/design-system'
|
|
4
5
|
|
|
5
6
|
export const AuthenticationGuard = ({ children }) => {
|
|
6
|
-
const {
|
|
7
|
+
const { t } = useLocale()
|
|
8
|
+
const sdk = useSdk()
|
|
9
|
+
const { status, data: user } = sdk.read(GetCurrentUser)
|
|
10
|
+
|
|
11
|
+
const authStatus = useMemo(() => {
|
|
12
|
+
if (status === AsyncStatus.Loading || status === AsyncStatus.NotInitialized) {
|
|
13
|
+
return AuthenticationStatus.Verifying
|
|
14
|
+
}
|
|
15
|
+
if (status === AsyncStatus.Success && user) {
|
|
16
|
+
return AuthenticationStatus.Authenticated
|
|
17
|
+
}
|
|
18
|
+
return AuthenticationStatus.NotAuthenticated
|
|
19
|
+
}, [status, user])
|
|
7
20
|
|
|
8
21
|
return (
|
|
9
|
-
<Switch on={
|
|
22
|
+
<Switch on={authStatus}>
|
|
10
23
|
|
|
11
24
|
<Switch.Case match={[AuthenticationStatus.NotAuthenticated]}>
|
|
12
25
|
<View layout="off-center" style={{ height: '100%' }}>
|
|
13
26
|
<Guide
|
|
14
27
|
style={{ width: '100vw', maxWidth: '300px' }}
|
|
15
28
|
slot="content"
|
|
16
|
-
title=
|
|
17
|
-
text=
|
|
29
|
+
title={t('authentication.guard.notAuthenticated.title')}
|
|
30
|
+
text={t('authentication.guard.notAuthenticated.text')}
|
|
18
31
|
/>
|
|
19
32
|
</View>
|
|
20
33
|
</Switch.Case>
|
|
@@ -24,13 +37,13 @@ export const AuthenticationGuard = ({ children }) => {
|
|
|
24
37
|
<Guide
|
|
25
38
|
style={{ width: '100vw', maxWidth: '300px' }}
|
|
26
39
|
slot="content"
|
|
27
|
-
title=
|
|
28
|
-
text=
|
|
40
|
+
title={t('authentication.guard.error.title')}
|
|
41
|
+
text={t('authentication.guard.error.text')}
|
|
29
42
|
/>
|
|
30
43
|
</View>
|
|
31
44
|
</Switch.Case>
|
|
32
45
|
|
|
33
|
-
<Switch.Case match={[AuthenticationStatus.Verifying, AuthenticationStatus.
|
|
46
|
+
<Switch.Case match={[AuthenticationStatus.Verifying, AuthenticationStatus.NotInitialized]}>
|
|
34
47
|
<View layout="off-center" style={{ height: '100%' }}>
|
|
35
48
|
<Icon
|
|
36
49
|
name="spinner"
|
|
@@ -1,32 +1,37 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
|
-
import {
|
|
2
|
+
import { ListWorkspaces, CreateWorkspace } from '@ossy/workspaces'
|
|
3
|
+
import { useSdk, cacheKey } from '@ossy/sdk-react'
|
|
3
4
|
import {
|
|
4
5
|
Button,
|
|
5
6
|
useInputValue,
|
|
6
7
|
Title,
|
|
7
8
|
Input,
|
|
8
|
-
View
|
|
9
|
+
View,
|
|
10
|
+
useLocale,
|
|
9
11
|
} from '@ossy/design-system'
|
|
10
12
|
import { useRouter } from '@ossy/router-react'
|
|
11
13
|
import { AuthenticationGuard } from './AuthenticationGuard.jsx'
|
|
12
14
|
import { patchUserWorkspaceId } from './patchUserWorkspaceId.js'
|
|
15
|
+
import { metadata as CreateWorkspaceMeta } from './create.action.js'
|
|
13
16
|
|
|
14
17
|
export const CreateWorkspacePage = () => {
|
|
15
18
|
const router = useRouter()
|
|
19
|
+
const { t } = useLocale()
|
|
16
20
|
const [error, setError] = useState()
|
|
17
21
|
const [workspaceName, setWorkspaceName] = useInputValue()
|
|
18
|
-
const
|
|
22
|
+
const sdk = useSdk()
|
|
19
23
|
|
|
20
24
|
const onCreateWorkspace = workspaceName => {
|
|
21
|
-
|
|
22
|
-
.then((workspace) =>
|
|
23
|
-
|
|
25
|
+
sdk.invoke(CreateWorkspace, { name: workspaceName })
|
|
26
|
+
.then((workspace) => {
|
|
27
|
+
sdk.invalidate(cacheKey(ListWorkspaces))
|
|
28
|
+
return patchUserWorkspaceId(workspace.id).then((res) => {
|
|
24
29
|
if (!res.ok) throw new Error('Could not select the new workspace')
|
|
25
30
|
window.location.assign(
|
|
26
31
|
router.getHref({ id: '@storage/home', params: { workspaceId: workspace.id } })
|
|
27
32
|
)
|
|
28
33
|
})
|
|
29
|
-
)
|
|
34
|
+
})
|
|
30
35
|
.catch(err => { setError(err.message) })
|
|
31
36
|
}
|
|
32
37
|
|
|
@@ -34,14 +39,14 @@ export const CreateWorkspacePage = () => {
|
|
|
34
39
|
<AuthenticationGuard>
|
|
35
40
|
<View gap="m">
|
|
36
41
|
|
|
37
|
-
<title>
|
|
38
|
-
<Title variant="primary" className="stack-m">
|
|
42
|
+
<title>{t('profile/workspaces/create.documentTitle')}</title>
|
|
43
|
+
<Title variant="primary" className="stack-m">{t('workspaces.create.title')}</Title>
|
|
39
44
|
|
|
40
45
|
<Input
|
|
41
46
|
id="workspaceName"
|
|
42
47
|
className={error ? 'stack-s' : 'stack-m'}
|
|
43
48
|
type="text"
|
|
44
|
-
placeholder=
|
|
49
|
+
placeholder={t('workspaces.create.namePlaceholder')}
|
|
45
50
|
required
|
|
46
51
|
onChange={setWorkspaceName}
|
|
47
52
|
style={{ width: '100%' }}
|
|
@@ -54,13 +59,14 @@ export const CreateWorkspacePage = () => {
|
|
|
54
59
|
<Button
|
|
55
60
|
variant="link"
|
|
56
61
|
id="cancel"
|
|
57
|
-
href={() => router.back()}>
|
|
62
|
+
href={() => router.back()}>{t('workspaces.create.cancel')}
|
|
58
63
|
</Button>
|
|
59
64
|
|
|
60
65
|
<Button
|
|
61
66
|
id="createWorkspace"
|
|
67
|
+
data-action={CreateWorkspaceMeta.id}
|
|
62
68
|
variant="cta"
|
|
63
|
-
onClick={() => onCreateWorkspace(workspaceName)}>
|
|
69
|
+
onClick={() => onCreateWorkspace(workspaceName)}>{t('workspaces.create.submit')}
|
|
64
70
|
</Button>
|
|
65
71
|
|
|
66
72
|
</View>
|
package/src/Definition.js
CHANGED
|
@@ -2,22 +2,6 @@ export const Definition = {
|
|
|
2
2
|
id: 'workspaces',
|
|
3
3
|
title: 'Workspaces',
|
|
4
4
|
description: 'Workspace lifecycle, members, invitations, and services.',
|
|
5
|
-
|
|
6
|
-
id: 'workspaces',
|
|
7
|
-
enabled: true
|
|
8
|
-
},
|
|
5
|
+
icon: 'users',
|
|
9
6
|
statuses: ['beta'],
|
|
10
|
-
actions: [
|
|
11
|
-
'workspaces.create',
|
|
12
|
-
'workspaces.invite-user',
|
|
13
|
-
'workspaces.enable-service',
|
|
14
|
-
'workspaces.disable-service'
|
|
15
|
-
],
|
|
16
|
-
views: [
|
|
17
|
-
'workspaces.get-all',
|
|
18
|
-
'workspaces.get',
|
|
19
|
-
'workspaces.get-current',
|
|
20
|
-
'workspaces.get-users'
|
|
21
|
-
],
|
|
22
|
-
tasks: []
|
|
23
7
|
}
|
package/src/GeneralSettings.jsx
CHANGED
|
@@ -1,87 +1,44 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { GetWorkspace } from '@ossy/workspaces'
|
|
3
|
+
import { GetCurrentUser } from '@ossy/authentication'
|
|
4
|
+
import { useSdk, AsyncStatus } from '@ossy/sdk-react'
|
|
5
|
+
import { Switch, Title, Button, View, Text, useLocale } from '@ossy/design-system'
|
|
4
6
|
|
|
5
|
-
const formatDate = (timestamp) =>
|
|
6
|
-
|
|
7
|
-
const date = new Date(timestamp)
|
|
8
|
-
return date.toLocaleDateString()
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const FlowStage = {
|
|
12
|
-
View: 'View',
|
|
13
|
-
Edit: 'Edit'
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const availableServices = [
|
|
17
|
-
'@ossy/jobs/visual-content-descriptors',
|
|
18
|
-
'@ossy/jobs/resize-common-web',
|
|
19
|
-
'@ossy/resumes',
|
|
20
|
-
'@ossy/consultancy',
|
|
21
|
-
'@ossy/apps',
|
|
22
|
-
'@ossy/domains',
|
|
23
|
-
]
|
|
7
|
+
const formatDate = (timestamp) => timestamp ? new Date(timestamp).toLocaleDateString() : ''
|
|
8
|
+
const FlowStage = { View: 'View', Edit: 'Edit' }
|
|
24
9
|
|
|
25
10
|
export const GeneralSettings = () => {
|
|
26
|
-
const {
|
|
27
|
-
const
|
|
11
|
+
const { t } = useLocale()
|
|
12
|
+
const sdk = useSdk()
|
|
13
|
+
const { data: workspace } = sdk.read(GetWorkspace)
|
|
14
|
+
const { status: userStatus } = sdk.read(GetCurrentUser)
|
|
28
15
|
const [flowStage, setFlowStage] = useState(FlowStage.View)
|
|
29
|
-
|
|
30
16
|
const services = workspace?.services || {}
|
|
31
17
|
|
|
32
|
-
const isWorkspaceCreator =
|
|
33
|
-
userStatus === AsyncStatus.Success &&
|
|
34
|
-
Boolean(workspace?.createdBy) &&
|
|
35
|
-
Boolean(user?.id) &&
|
|
36
|
-
workspace.createdBy === user.id
|
|
37
|
-
|
|
38
18
|
return (
|
|
39
19
|
<View gap="l" inset="m">
|
|
40
20
|
<View layout="row" gap="s" justifyContent="space-between">
|
|
41
|
-
<Title>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
Edit
|
|
48
|
-
</Button>
|
|
49
|
-
</Switch.Case>
|
|
50
|
-
</Switch>
|
|
51
|
-
</View>
|
|
21
|
+
<Title>{t('workspace.settings.detailsTitle')}</Title>
|
|
22
|
+
<Switch on={flowStage}>
|
|
23
|
+
<Switch.Case match={[FlowStage.View]}>
|
|
24
|
+
<Button prefix={{ size: 's', name: 'pen' }} variant="cta" disabled>{t('workspace.settings.edit')}</Button>
|
|
25
|
+
</Switch.Case>
|
|
26
|
+
</Switch>
|
|
52
27
|
</View>
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
<Text style={{ fontWeight: 'bold' }}>Id:</Text>
|
|
57
|
-
<Text>{workspace.id}</Text>
|
|
58
|
-
</View>
|
|
59
|
-
<View layout="row" gap="s">
|
|
60
|
-
<Text style={{ fontWeight: 'bold' }}>Name:</Text>
|
|
61
|
-
<Text>{workspace.name}</Text>
|
|
62
|
-
</View>
|
|
63
|
-
<View layout="row" gap="s">
|
|
64
|
-
<Text style={{ fontWeight: 'bold' }}>Created:</Text>
|
|
65
|
-
<Text>{formatDate(workspace?.created)}</Text>
|
|
66
|
-
</View>
|
|
67
|
-
</View>
|
|
68
|
-
|
|
69
|
-
{isWorkspaceCreator && (
|
|
28
|
+
{userStatus === AsyncStatus.Loading ? (
|
|
29
|
+
<Text>{t('workspace.settings.loading')}</Text>
|
|
30
|
+
) : (
|
|
70
31
|
<View gap="s">
|
|
71
|
-
<
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
</Text>
|
|
80
|
-
</View>
|
|
81
|
-
))}
|
|
32
|
+
<View layout="row" gap="s"><Text style={{ fontWeight: 'bold' }}>{t('workspace.settings.name')}</Text><Text>{workspace?.name}</Text></View>
|
|
33
|
+
<View layout="row" gap="s"><Text style={{ fontWeight: 'bold' }}>{t('workspace.settings.created')}</Text><Text>{formatDate(workspace?.created)}</Text></View>
|
|
34
|
+
<Title variant="tertiary">{t('workspace.settings.services')}</Title>
|
|
35
|
+
<View gap="xs">
|
|
36
|
+
{Object.entries(services).map(([key, enabled]) => (
|
|
37
|
+
<Text key={key}>{key}: {enabled ? 'on' : 'off'}</Text>
|
|
38
|
+
))}
|
|
39
|
+
</View>
|
|
82
40
|
</View>
|
|
83
41
|
)}
|
|
84
|
-
|
|
85
42
|
</View>
|
|
86
43
|
)
|
|
87
44
|
}
|
package/src/Invitations.jsx
CHANGED
|
@@ -1,44 +1,36 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { GetWorkspace } from '@ossy/workspaces'
|
|
3
|
+
import { useSdk } from '@ossy/sdk-react'
|
|
4
|
+
import { Title, Text, Button, View, Icon, useLocale } from '@ossy/design-system'
|
|
4
5
|
import { useRouter } from '@ossy/router-react'
|
|
5
6
|
|
|
6
7
|
export const Invitations = () => {
|
|
8
|
+
const { t } = useLocale()
|
|
7
9
|
const router = useRouter()
|
|
8
|
-
const
|
|
10
|
+
const sdk = useSdk()
|
|
11
|
+
const { data: workspace } = sdk.read(GetWorkspace)
|
|
9
12
|
const invitations = workspace?.invitations || []
|
|
10
13
|
|
|
11
14
|
return (
|
|
12
15
|
<View gap="m">
|
|
13
|
-
|
|
14
16
|
<View inset="s" gap="s">
|
|
15
|
-
|
|
16
17
|
<View layout="row" justifyContent="space-between" alignItems="center">
|
|
17
|
-
<Title>
|
|
18
|
+
<Title>{t('workspace.invitations.title')}</Title>
|
|
18
19
|
<Button variant="cta" href={router.getHref('@workspace/invitations/add')} prefix="user-add">
|
|
19
|
-
|
|
20
|
+
{t('workspace.invitations.invite')}
|
|
20
21
|
</Button>
|
|
21
22
|
</View>
|
|
22
|
-
|
|
23
|
-
<Text style={{ maxWidth: '400px' }}>
|
|
24
|
-
Invite users to your workspace to collaborate.
|
|
25
|
-
</Text>
|
|
26
|
-
|
|
23
|
+
<Text style={{ maxWidth: '400px' }}>{t('workspace.invitations.description')}</Text>
|
|
27
24
|
</View>
|
|
28
|
-
|
|
29
25
|
<View gap="xs">
|
|
30
26
|
{invitations.map(invite => (
|
|
31
27
|
<View layout="row" roundness="s" alignItems="center" inset="m" gap="m" selectable key={invite.email}>
|
|
32
28
|
<Icon name="mail" />
|
|
33
|
-
<View>
|
|
34
|
-
<Text variant="small" as="span">{invite.email}</Text>
|
|
35
|
-
</View>
|
|
29
|
+
<View><Text variant="small" as="span">{invite.email}</Text></View>
|
|
36
30
|
</View>
|
|
37
31
|
))}
|
|
38
32
|
</View>
|
|
39
|
-
|
|
40
|
-
{invitations.length === 0 && <Text>No invitations</Text>}
|
|
41
|
-
|
|
33
|
+
{invitations.length === 0 && <Text>{t('workspace.invitations.empty')}</Text>}
|
|
42
34
|
</View>
|
|
43
35
|
)
|
|
44
36
|
}
|
package/src/Invite.jsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useState, useRef } from 'react'
|
|
2
|
-
import {
|
|
2
|
+
import { InviteUser } from '@ossy/workspaces'
|
|
3
|
+
import { useSdk } from '@ossy/sdk-react'
|
|
3
4
|
import { useRouter } from '@ossy/router-react'
|
|
4
5
|
import {
|
|
5
6
|
useInputValue,
|
|
@@ -9,7 +10,8 @@ import {
|
|
|
9
10
|
Switch,
|
|
10
11
|
Guide,
|
|
11
12
|
Text,
|
|
12
|
-
View
|
|
13
|
+
View,
|
|
14
|
+
useLocale,
|
|
13
15
|
} from '@ossy/design-system'
|
|
14
16
|
|
|
15
17
|
const FlowStage = {
|
|
@@ -19,15 +21,16 @@ const FlowStage = {
|
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
export const Invite = () => {
|
|
24
|
+
const { t } = useLocale()
|
|
22
25
|
const router = useRouter()
|
|
23
|
-
const
|
|
26
|
+
const sdk = useSdk()
|
|
24
27
|
const [flowStage, setFlowStage] = useState(FlowStage.EnterEmail)
|
|
25
28
|
const [email, setEmail] = useInputValue('')
|
|
26
29
|
const formRef = useRef()
|
|
27
30
|
|
|
28
31
|
const sendInvite = event => {
|
|
29
32
|
event.preventDefault()
|
|
30
|
-
formRef.current.reportValidity() &&
|
|
33
|
+
formRef.current.reportValidity() && sdk.invoke(InviteUser, { email })
|
|
31
34
|
.then(() => setFlowStage(FlowStage.Success))
|
|
32
35
|
.catch(() => setFlowStage(FlowStage.Error))
|
|
33
36
|
}
|
|
@@ -35,13 +38,13 @@ export const Invite = () => {
|
|
|
35
38
|
return (
|
|
36
39
|
<Switch on={flowStage}>
|
|
37
40
|
|
|
38
|
-
<title>
|
|
41
|
+
<title>{t('workspace/invitations/add.documentTitle')}</title>
|
|
39
42
|
|
|
40
43
|
<Switch.Case match={[FlowStage.EnterEmail]}>
|
|
41
44
|
<View gap="m">
|
|
42
|
-
<Title variant="primary">
|
|
45
|
+
<Title variant="primary">{t('workspace.invite.title')}</Title>
|
|
43
46
|
<Text>
|
|
44
|
-
|
|
47
|
+
{t('workspace.invite.description')}
|
|
45
48
|
</Text>
|
|
46
49
|
<form
|
|
47
50
|
ref={formRef}
|
|
@@ -54,7 +57,7 @@ export const Invite = () => {
|
|
|
54
57
|
id="registerField"
|
|
55
58
|
className="stack-m"
|
|
56
59
|
type="email"
|
|
57
|
-
placeholder=
|
|
60
|
+
placeholder={t('workspace.invite.emailPlaceholder')}
|
|
58
61
|
required
|
|
59
62
|
value={email}
|
|
60
63
|
onChange={e => setEmail(e.target.value)}
|
|
@@ -65,14 +68,15 @@ export const Invite = () => {
|
|
|
65
68
|
<Button
|
|
66
69
|
variant="link"
|
|
67
70
|
id="cancelButton"
|
|
68
|
-
href={() => router.back()}>
|
|
71
|
+
href={() => router.back()}>{t('design-system.cancel')}
|
|
69
72
|
</Button>
|
|
70
73
|
|
|
71
74
|
<Button
|
|
72
75
|
variant="cta"
|
|
73
76
|
id="registerButton"
|
|
77
|
+
data-action={InviteUser.id}
|
|
74
78
|
type="submit"
|
|
75
|
-
onClick={sendInvite}>
|
|
79
|
+
onClick={sendInvite}>{t('workspace.invite.submit')}
|
|
76
80
|
</Button>
|
|
77
81
|
</View>
|
|
78
82
|
</View>
|
|
@@ -82,12 +86,11 @@ export const Invite = () => {
|
|
|
82
86
|
|
|
83
87
|
<Switch.Case match={[FlowStage.Success]}>
|
|
84
88
|
<Guide
|
|
85
|
-
title=
|
|
89
|
+
title={t('workspace.invite.successTitle')}
|
|
86
90
|
titleVariant="primary"
|
|
87
91
|
>
|
|
88
92
|
<Text>
|
|
89
|
-
|
|
90
|
-
When they have accepted they will show up in the list of users.
|
|
93
|
+
{t('workspace.invite.successBody')}
|
|
91
94
|
<br/>
|
|
92
95
|
✌️
|
|
93
96
|
</Text>
|
|
@@ -96,7 +99,7 @@ export const Invite = () => {
|
|
|
96
99
|
|
|
97
100
|
<Switch.Case match={[FlowStage.Error]}>
|
|
98
101
|
<Text>
|
|
99
|
-
|
|
102
|
+
{t('workspace.invite.error')}
|
|
100
103
|
</Text>
|
|
101
104
|
</Switch.Case>
|
|
102
105
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { ListWorkspaces } from '@ossy/workspaces'
|
|
3
|
+
import { useSdk, AsyncStatus } from '@ossy/sdk-react'
|
|
4
|
+
import { Title, Text, Switch, Button, View, Guide, Icon, useLocale } from '@ossy/design-system'
|
|
4
5
|
import { useRouter } from '@ossy/router-react'
|
|
5
6
|
import { AuthenticationGuard } from './AuthenticationGuard.jsx'
|
|
6
7
|
import { patchUserWorkspaceId } from './patchUserWorkspaceId.js'
|
|
@@ -14,7 +15,9 @@ const PageFlow = {
|
|
|
14
15
|
|
|
15
16
|
export const SelectWorkspacePage = () => {
|
|
16
17
|
const router = useRouter()
|
|
17
|
-
const {
|
|
18
|
+
const { t } = useLocale()
|
|
19
|
+
const sdk = useSdk()
|
|
20
|
+
const { status: asyncStatus, data: workspaces = [] } = sdk.read(ListWorkspaces)
|
|
18
21
|
|
|
19
22
|
const pageFlow = (asyncStatus === AsyncStatus.Success && workspaces.length === 0)
|
|
20
23
|
? PageFlow.NoWorkspacesCreated
|
|
@@ -30,7 +33,7 @@ export const SelectWorkspacePage = () => {
|
|
|
30
33
|
return (
|
|
31
34
|
<AuthenticationGuard>
|
|
32
35
|
|
|
33
|
-
<title>
|
|
36
|
+
<title>{t('profile/workspaces.documentTitle')}</title>
|
|
34
37
|
|
|
35
38
|
<Switch on={pageFlow}>
|
|
36
39
|
<Switch.Case match={[PageFlow.Success]}>
|
|
@@ -41,14 +44,14 @@ export const SelectWorkspacePage = () => {
|
|
|
41
44
|
marginBottom: 'var(--space-m)',
|
|
42
45
|
}}>
|
|
43
46
|
<Title variant="primary">
|
|
44
|
-
|
|
47
|
+
{t('workspaces.select.title')}
|
|
45
48
|
</Title>
|
|
46
49
|
<Button
|
|
47
50
|
prefix="math-plus"
|
|
48
51
|
variant="cta"
|
|
49
52
|
href={router.getHref('@profile/workspaces/create')}
|
|
50
53
|
className="mobile:d-none">
|
|
51
|
-
|
|
54
|
+
{t('workspaces.select.newWorkspace')}
|
|
52
55
|
</Button>
|
|
53
56
|
</div>
|
|
54
57
|
</Switch.Case>
|
|
@@ -61,16 +64,12 @@ export const SelectWorkspacePage = () => {
|
|
|
61
64
|
<Guide
|
|
62
65
|
slot="content"
|
|
63
66
|
style={{ width: '100vw', maxWidth: '500px' }}
|
|
64
|
-
title=
|
|
65
|
-
text={
|
|
66
|
-
Something went wrong when loading your workspaces.
|
|
67
|
-
Maybe your session have expired, or we messed up somehow.
|
|
68
|
-
Try starting over from our home page.
|
|
69
|
-
`}
|
|
67
|
+
title={t('workspaces.select.errorTitle')}
|
|
68
|
+
text={t('workspaces.select.errorText')}
|
|
70
69
|
actions={[{
|
|
71
70
|
variant: 'cta',
|
|
72
71
|
href: '/',
|
|
73
|
-
label: '
|
|
72
|
+
label: t('workspaces.select.errorAction')
|
|
74
73
|
}]}
|
|
75
74
|
/>
|
|
76
75
|
</View>
|
|
@@ -83,14 +82,14 @@ export const SelectWorkspacePage = () => {
|
|
|
83
82
|
style={{ width: '64px', height: '64px' }}
|
|
84
83
|
className="rotate"
|
|
85
84
|
/>
|
|
86
|
-
<Text>
|
|
85
|
+
<Text>{t('workspaces.select.loading')}</Text>
|
|
87
86
|
</Switch.Case>
|
|
88
87
|
|
|
89
88
|
<Switch.Case match={[PageFlow.Success]}>
|
|
90
89
|
<View gap="m">
|
|
91
90
|
|
|
92
91
|
<Text style={{ maxWidth: '400px' }}>
|
|
93
|
-
|
|
92
|
+
{t('workspaces.select.description')}
|
|
94
93
|
</Text>
|
|
95
94
|
|
|
96
95
|
<View stack gap="xs" style={{ overflowY: 'auto' }}>
|
|
@@ -122,15 +121,11 @@ export const SelectWorkspacePage = () => {
|
|
|
122
121
|
<Guide
|
|
123
122
|
slot="content"
|
|
124
123
|
style={{ padding: 'var(--space-xl) var(--space-m)', width: '100vw', maxWidth: '500px' }}
|
|
125
|
-
title=
|
|
126
|
-
text={
|
|
127
|
-
To get started we need to create a workspace.
|
|
128
|
-
A workspace is an encapsulation of resources, resource templates,
|
|
129
|
-
billing information, and what users have access to these things.
|
|
130
|
-
`}
|
|
124
|
+
title={`🎉 ${t('workspaces.select.emptyTitle')} 🎉`}
|
|
125
|
+
text={t('workspaces.select.emptyText')}
|
|
131
126
|
actions={[{
|
|
132
127
|
variant: 'cta',
|
|
133
|
-
label: '
|
|
128
|
+
label: t('workspaces.select.emptyAction'),
|
|
134
129
|
href: router.getHref('@profile/workspaces/create')
|
|
135
130
|
}]}
|
|
136
131
|
/>
|
package/src/Users.jsx
CHANGED
|
@@ -1,32 +1,28 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { GetUsers } from '@ossy/workspaces'
|
|
3
|
+
import { useSdk, AsyncStatus } from '@ossy/sdk-react'
|
|
4
|
+
import { Text, View, Icon, Title, Button, useLocale } from '@ossy/design-system'
|
|
4
5
|
import { useRouter } from '@ossy/router-react'
|
|
5
6
|
|
|
6
7
|
export const Users = () => {
|
|
8
|
+
const { t } = useLocale()
|
|
7
9
|
const router = useRouter()
|
|
8
|
-
const
|
|
10
|
+
const sdk = useSdk()
|
|
11
|
+
const { status, data: users = [] } = sdk.read(GetUsers)
|
|
9
12
|
|
|
10
|
-
if (status === AsyncStatus.Loading) return <>
|
|
13
|
+
if (status === AsyncStatus.Loading) return <>{t('workspace.users.loading')}</>
|
|
11
14
|
|
|
12
15
|
return (
|
|
13
16
|
<View gap="m">
|
|
14
|
-
|
|
15
17
|
<View inset="s" gap="s">
|
|
16
|
-
|
|
17
18
|
<View layout="row" justifyContent="space-between" alignItems="center">
|
|
18
|
-
<Title>
|
|
19
|
+
<Title>{t('workspace.users.title')}</Title>
|
|
19
20
|
<Button variant="cta" href={router.getHref('@workspace/invitations/add')} prefix="user-add">
|
|
20
|
-
|
|
21
|
+
{t('workspace.users.invite')}
|
|
21
22
|
</Button>
|
|
22
23
|
</View>
|
|
23
|
-
|
|
24
|
-
<Text style={{ maxWidth: '400px' }}>
|
|
25
|
-
Manage users in your workspace.
|
|
26
|
-
</Text>
|
|
27
|
-
|
|
24
|
+
<Text style={{ maxWidth: '400px' }}>{t('workspace.users.description')}</Text>
|
|
28
25
|
</View>
|
|
29
|
-
|
|
30
26
|
<View gap="xs">
|
|
31
27
|
{users.map(user => (
|
|
32
28
|
<View layout="row" roundness="s" alignItems="center" inset="m" gap="m" selectable key={user.id}>
|
|
@@ -41,7 +37,6 @@ export const Users = () => {
|
|
|
41
37
|
</View>
|
|
42
38
|
))}
|
|
43
39
|
</View>
|
|
44
|
-
|
|
45
40
|
</View>
|
|
46
41
|
)
|
|
47
42
|
}
|