@stapel/workspaces-react 0.6.0 → 0.8.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/CHANGELOG.md +56 -0
- package/dist/api/generated/schema.d.ts +774 -7
- package/dist/api/generated/schema.d.ts.map +1 -1
- package/dist/api/types.d.ts +104 -7
- package/dist/api/types.d.ts.map +1 -1
- package/dist/api/workspacesApi.d.ts +55 -1
- package/dist/api/workspacesApi.d.ts.map +1 -1
- package/dist/api/workspacesApi.js +23 -0
- package/dist/api/workspacesApi.js.map +1 -1
- package/dist/default/InviteAcceptPage.d.ts +28 -0
- package/dist/default/InviteAcceptPage.d.ts.map +1 -0
- package/dist/default/InviteAcceptPage.js +141 -0
- package/dist/default/InviteAcceptPage.js.map +1 -0
- package/dist/default/MembersManager.d.ts +4 -4
- package/dist/default/MembersManager.d.ts.map +1 -1
- package/dist/default/MembersManager.js +61 -56
- package/dist/default/MembersManager.js.map +1 -1
- package/dist/default/index.d.ts +2 -0
- package/dist/default/index.d.ts.map +1 -1
- package/dist/default/index.js +1 -0
- package/dist/default/index.js.map +1 -1
- package/dist/flows/inviteAcceptFlow.d.ts +155 -0
- package/dist/flows/inviteAcceptFlow.d.ts.map +1 -0
- package/dist/flows/inviteAcceptFlow.js +126 -0
- package/dist/flows/inviteAcceptFlow.js.map +1 -0
- package/dist/headless/Can.d.ts +40 -0
- package/dist/headless/Can.d.ts.map +1 -0
- package/dist/headless/Can.js +30 -0
- package/dist/headless/Can.js.map +1 -0
- package/dist/headless/InviteAcceptFlow.d.ts +44 -0
- package/dist/headless/InviteAcceptFlow.d.ts.map +1 -0
- package/dist/headless/InviteAcceptFlow.js +67 -0
- package/dist/headless/InviteAcceptFlow.js.map +1 -0
- package/dist/headless/RoleSelect.d.ts +41 -0
- package/dist/headless/RoleSelect.d.ts.map +1 -0
- package/dist/headless/RoleSelect.js +34 -0
- package/dist/headless/RoleSelect.js.map +1 -0
- package/dist/i18n/generated/errors.gen.d.ts +48 -0
- package/dist/i18n/generated/errors.gen.d.ts.map +1 -1
- package/dist/i18n/generated/errors.gen.js +24 -0
- package/dist/i18n/generated/errors.gen.js.map +1 -1
- package/dist/i18n/generated/errors.ru.gen.d.ts.map +1 -1
- package/dist/i18n/generated/errors.ru.gen.js +8 -0
- package/dist/i18n/generated/errors.ru.gen.js.map +1 -1
- package/dist/i18n/keys.d.ts +28 -0
- package/dist/i18n/keys.d.ts.map +1 -1
- package/dist/i18n/keys.js +64 -0
- package/dist/i18n/keys.js.map +1 -1
- package/dist/i18n/ru.d.ts.map +1 -1
- package/dist/i18n/ru.js +30 -0
- package/dist/i18n/ru.js.map +1 -1
- package/dist/index.d.ts +17 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -2
- package/dist/index.js.map +1 -1
- package/dist/model/capabilities.d.ts +21 -0
- package/dist/model/capabilities.d.ts.map +1 -0
- package/dist/model/capabilities.js +32 -0
- package/dist/model/capabilities.js.map +1 -0
- package/dist/model/emailMask.d.ts +22 -0
- package/dist/model/emailMask.d.ts.map +1 -0
- package/dist/model/emailMask.js +36 -0
- package/dist/model/emailMask.js.map +1 -0
- package/dist/model/mutations.d.ts +107 -3
- package/dist/model/mutations.d.ts.map +1 -1
- package/dist/model/mutations.js +190 -1
- package/dist/model/mutations.js.map +1 -1
- package/dist/model/queries.d.ts +108 -2
- package/dist/model/queries.d.ts.map +1 -1
- package/dist/model/queries.js +139 -1
- package/dist/model/queries.js.map +1 -1
- package/dist/model/queryKeys.d.ts +12 -1
- package/dist/model/queryKeys.d.ts.map +1 -1
- package/dist/model/queryKeys.js +25 -0
- package/dist/model/queryKeys.js.map +1 -1
- package/dist/model/stepUp.d.ts +25 -0
- package/dist/model/stepUp.d.ts.map +1 -0
- package/dist/model/stepUp.js +79 -0
- package/dist/model/stepUp.js.map +1 -0
- package/llms.txt +49 -15
- package/manifest.json +346 -3
- package/package.json +5 -5
- package/src/analytics/generated/events.json +1 -1
- package/src/api/generated/schema.ts +774 -7
- package/src/api/types.ts +113 -7
- package/src/api/workspacesApi.ts +137 -0
- package/src/default/InviteAcceptPage.tsx +373 -0
- package/src/default/MembersManager.tsx +155 -145
- package/src/default/index.ts +2 -0
- package/src/flows/inviteAcceptFlow.ts +289 -0
- package/src/headless/Can.tsx +49 -0
- package/src/headless/InviteAcceptFlow.tsx +105 -0
- package/src/headless/RoleSelect.tsx +58 -0
- package/src/i18n/generated/errors.gen.ts +24 -0
- package/src/i18n/generated/errors.json +62 -0
- package/src/i18n/generated/errors.ru.gen.ts +8 -0
- package/src/i18n/keys.ts +68 -0
- package/src/i18n/ru.ts +34 -0
- package/src/index.ts +54 -1
- package/src/model/capabilities.ts +37 -0
- package/src/model/emailMask.ts +37 -0
- package/src/model/mutations.ts +248 -4
- package/src/model/queries.ts +205 -2
- package/src/model/queryKeys.ts +45 -1
- package/src/model/stepUp.ts +96 -0
|
@@ -5,73 +5,78 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
5
5
|
* инвайты)"). Built entirely on this pair's EXISTING `Members` headless
|
|
6
6
|
* wrapper (`useMembers`/`useInviteMembers`/`useUpdateMemberRole`/
|
|
7
7
|
* `useRemoveMember`) — no new backend surface.
|
|
8
|
+
*
|
|
9
|
+
* Roles come from the EFFECTIVE registry via the `RoleSelect` headless
|
|
10
|
+
* (GET /roles, org-program §A2) — not a hardcoded builtin four: a deployment
|
|
11
|
+
* that overlays `STAPEL_WORKSPACES["ROLES"]` (e.g. a `secretary`) sees its
|
|
12
|
+
* roles here with `workspaces.role.<key>` labels (client-bundle merge,
|
|
13
|
+
* raw-name fallback). `owner` is offered only in the member-row select (the
|
|
14
|
+
* backend enforces "only an owner grants owner" and last-owner protection);
|
|
15
|
+
* the invite dialog filters it out, as before.
|
|
8
16
|
*/
|
|
9
17
|
import { useState } from "react";
|
|
10
18
|
import { Alert, Button, Card, Input, Modal, Popconfirm, Select, Table, Typography } from "antd";
|
|
11
19
|
import { useT } from "@stapel/core";
|
|
12
20
|
import { Members } from "../headless/Members.js";
|
|
21
|
+
import { RoleSelect } from "../headless/RoleSelect.js";
|
|
13
22
|
import { WORKSPACES_I18N_KEYS } from "../i18n/keys.js";
|
|
14
|
-
const
|
|
15
|
-
const ROLE_KEY = {
|
|
16
|
-
owner: "roleOwner",
|
|
17
|
-
admin: "roleAdmin",
|
|
18
|
-
member: "roleMember",
|
|
19
|
-
viewer: "roleViewer",
|
|
20
|
-
};
|
|
21
|
-
function isWorkspaceRole(value) {
|
|
22
|
-
return ROLES.includes(value);
|
|
23
|
-
}
|
|
23
|
+
const DEFAULT_INVITE_ROLE = "member";
|
|
24
24
|
export function MembersManager(props) {
|
|
25
25
|
const t = useT();
|
|
26
26
|
const canManage = props.canManage ?? true;
|
|
27
27
|
const [inviteOpen, setInviteOpen] = useState(false);
|
|
28
28
|
const [emailsText, setEmailsText] = useState("");
|
|
29
|
-
const [inviteRole, setInviteRole] = useState(
|
|
30
|
-
return (_jsx(Members, { workspaceId: props.workspaceId, children: ({ members, isLoading, isError, error, invite, isInviting, updateRole, remove }) => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
29
|
+
const [inviteRole, setInviteRole] = useState(DEFAULT_INVITE_ROLE);
|
|
30
|
+
return (_jsx(RoleSelect, { children: (rolesBag) => (_jsx(Members, { workspaceId: props.workspaceId, children: ({ members, isLoading, isError, error, invite, isInviting, updateRole, remove }) => {
|
|
31
|
+
const { labelFor } = rolesBag;
|
|
32
|
+
const roleOptions = rolesBag.roles.map((r) => ({
|
|
33
|
+
value: r.role,
|
|
34
|
+
label: labelFor(r.role),
|
|
35
|
+
}));
|
|
36
|
+
const inviteRoleOptions = roleOptions.filter((o) => o.value !== "owner");
|
|
37
|
+
function openInvite() {
|
|
38
|
+
setEmailsText("");
|
|
39
|
+
setInviteRole(DEFAULT_INVITE_ROLE);
|
|
40
|
+
setInviteOpen(true);
|
|
41
|
+
}
|
|
42
|
+
function submitInvite() {
|
|
43
|
+
const emails = emailsText
|
|
44
|
+
.split(/[,\s]+/)
|
|
45
|
+
.map((e) => e.trim())
|
|
46
|
+
.filter((e) => e.length > 0);
|
|
47
|
+
if (emails.length === 0)
|
|
48
|
+
return;
|
|
49
|
+
invite({ emails, role: inviteRole });
|
|
50
|
+
setInviteOpen(false);
|
|
51
|
+
}
|
|
52
|
+
const columns = [
|
|
53
|
+
{
|
|
54
|
+
title: "Email",
|
|
55
|
+
dataIndex: "email",
|
|
56
|
+
key: "email",
|
|
57
|
+
render: (value) => value ?? "—",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
title: t(WORKSPACES_I18N_KEYS.membersInviteRoleLabel),
|
|
61
|
+
key: "role",
|
|
62
|
+
render: (_, member) => canManage ? (_jsx(Select, { value: member.role, style: { width: 160 }, loading: rolesBag.isLoading, onChange: (next) => updateRole({ userId: member.user_id, role: next }),
|
|
63
|
+
// A member's CURRENT role may be missing from the
|
|
64
|
+
// registry options (a deployment removed an overlay
|
|
65
|
+
// role) — antd shows the raw value then, which matches
|
|
66
|
+
// labelFor's raw-name fallback contract.
|
|
67
|
+
options: roleOptions })) : (_jsx("span", { children: labelFor(member.role) })),
|
|
59
68
|
},
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
value: r,
|
|
73
|
-
label: t(WORKSPACES_I18N_KEYS[ROLE_KEY[r]]),
|
|
74
|
-
})) })] })] }) })] }));
|
|
75
|
-
} }));
|
|
69
|
+
...(canManage
|
|
70
|
+
? [
|
|
71
|
+
{
|
|
72
|
+
title: "",
|
|
73
|
+
key: "actions",
|
|
74
|
+
render: (_, member) => (_jsx(Popconfirm, { title: t(WORKSPACES_I18N_KEYS.membersRemoveConfirm), onConfirm: () => remove(member.user_id), children: _jsx(Button, { danger: true, type: "link", children: t(WORKSPACES_I18N_KEYS.membersRemove) }) })),
|
|
75
|
+
},
|
|
76
|
+
]
|
|
77
|
+
: []),
|
|
78
|
+
];
|
|
79
|
+
return (_jsxs(Card, { "data-testid": "members-manager", children: [_jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 16 }, children: [_jsxs("div", { children: [_jsx(Typography.Title, { level: 4, style: { marginTop: 0 }, children: t(WORKSPACES_I18N_KEYS.membersTitle) }), _jsx(Typography.Text, { type: "secondary", children: t(WORKSPACES_I18N_KEYS.membersSubtitle) })] }), canManage && (_jsx(Button, { type: "primary", onClick: openInvite, "data-analytics": "none", "data-analytics-reason": "local-ui-open-invite-dialog", children: t(WORKSPACES_I18N_KEYS.membersInvite) }))] }), isError && error && (_jsx(Alert, { style: { marginTop: 12 }, type: "error", showIcon: true, message: error.message })), _jsx(Table, { style: { marginTop: 16 }, size: "small", loading: isLoading, rowKey: (member) => member.id, dataSource: members, columns: columns, pagination: false }), _jsx(Modal, { title: t(WORKSPACES_I18N_KEYS.membersInviteDialogTitle), open: inviteOpen, onCancel: () => setInviteOpen(false), onOk: submitInvite, okButtonProps: { loading: isInviting, disabled: emailsText.trim().length === 0 }, children: _jsxs("div", { style: { display: "grid", gap: 12 }, children: [_jsxs("div", { children: [_jsx(Typography.Text, { children: t(WORKSPACES_I18N_KEYS.membersInviteEmailsLabel) }), _jsx(Input, { value: emailsText, onChange: (e) => setEmailsText(e.target.value), placeholder: t(WORKSPACES_I18N_KEYS.membersInviteEmailsPlaceholder) })] }), _jsxs("div", { children: [_jsx(Typography.Text, { children: t(WORKSPACES_I18N_KEYS.membersInviteRoleLabel) }), _jsx(Select, { value: inviteRole, onChange: setInviteRole, style: { width: "100%" }, loading: rolesBag.isLoading, options: inviteRoleOptions })] })] }) })] }));
|
|
80
|
+
} })) }));
|
|
76
81
|
}
|
|
77
82
|
//# sourceMappingURL=MembersManager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MembersManager.js","sourceRoot":"","sources":["../../src/default/MembersManager.tsx"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"MembersManager.js","sourceRoot":"","sources":["../../src/default/MembersManager.tsx"],"names":[],"mappings":"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"}
|
package/dist/default/index.d.ts
CHANGED
|
@@ -15,4 +15,6 @@ export { WorkspaceSettings } from "./WorkspaceSettings.js";
|
|
|
15
15
|
export type { WorkspaceSettingsProps } from "./WorkspaceSettings.js";
|
|
16
16
|
export { MembersManager } from "./MembersManager.js";
|
|
17
17
|
export type { MembersManagerProps } from "./MembersManager.js";
|
|
18
|
+
export { InviteAcceptPage } from "./InviteAcceptPage.js";
|
|
19
|
+
export type { InviteAcceptPageProps } from "./InviteAcceptPage.js";
|
|
18
20
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,YAAY,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,YAAY,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,YAAY,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/default/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type { Analytics } from "@stapel/core";
|
|
2
|
+
import type { FlowMachine } from "@stapel/core";
|
|
3
|
+
import type { WorkspacesApi } from "../api/workspacesApi.js";
|
|
4
|
+
import type { InvitationPreview, Member } from "../api/types.js";
|
|
5
|
+
import type { FlowError } from "./errors.js";
|
|
6
|
+
/**
|
|
7
|
+
* The `/invite/{token}` journey (org-program §B4) as a flow machine:
|
|
8
|
+
*
|
|
9
|
+
* ```
|
|
10
|
+
* preview → (session && email match) → acceptPrompt [accept/decline]
|
|
11
|
+
* → (session && email mismatch) → wrongAccount (logout CTA)
|
|
12
|
+
* → (no session, email_registered)→ loginRequired (host's login slot)
|
|
13
|
+
* → (no session, new user) → newUser → claim →
|
|
14
|
+
* grantIssued (grant_token handed OUT) → [host exchanges at auth]
|
|
15
|
+
* → basicData (host's initial-setup slot) → acceptPrompt
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* Non-`pending` invitations (expired / revoked / accepted / declined) park in
|
|
19
|
+
* the terminal `unavailable` step — the page renders the matching copy.
|
|
20
|
+
*
|
|
21
|
+
* ## The grant_token seam (deliberate pair boundary)
|
|
22
|
+
*
|
|
23
|
+
* `claim` mints a login grant that must be exchanged at AUTH's
|
|
24
|
+
* `POST /grant/exchange/` — an endpoint of the auth pair, and pairs do NOT
|
|
25
|
+
* depend on each other (no auth-react peer dependency here). The flow
|
|
26
|
+
* therefore parks in `grantIssued` and hands `grantToken` OUT via the
|
|
27
|
+
* `onLoginGrant` callback; the HOST connects the two pairs:
|
|
28
|
+
*
|
|
29
|
+
* ```tsx
|
|
30
|
+
* <InviteAcceptFlow token={token}
|
|
31
|
+
* onLoginGrant={async (grant) => {
|
|
32
|
+
* const r = await authApi.exchangeLoginGrant(grant); // auth-react
|
|
33
|
+
* session.adopt(r); // full session
|
|
34
|
+
* }}>
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* then calls `grantExchanged()` (the default `InviteAcceptPage` does this
|
|
38
|
+
* automatically when the host's `onLoginGrant` promise resolves), which moves
|
|
39
|
+
* the flow to `basicData` — the host's profiles-react `InitialSetupPrompt`
|
|
40
|
+
* slot — and `completeBasicData()` finally reaches `acceptPrompt`. The
|
|
41
|
+
* account created by the exchange IS the invited email's account (verified by
|
|
42
|
+
* construction), so no re-match happens on that path.
|
|
43
|
+
*
|
|
44
|
+
* ## Session/email detection
|
|
45
|
+
*
|
|
46
|
+
* The pair has no auth dependency, so the CALLER supplies the signed-in
|
|
47
|
+
* email (`sessionEmail`, e.g. auth-react's `useAuthSessionState().user?.email`
|
|
48
|
+
* — or any other session source; null/undefined = no session). Match against
|
|
49
|
+
* the preview's MASKED email is decided client-side by masking the known
|
|
50
|
+
* email with the same algorithm (see `model/emailMask.ts`); the backend's
|
|
51
|
+
* real email-match on accept/decline stays the enforcement point — a mask
|
|
52
|
+
* collision merely shows the prompt, the accept then surfaces the 403.
|
|
53
|
+
*/
|
|
54
|
+
export type InviteAcceptState = {
|
|
55
|
+
readonly step: "idle";
|
|
56
|
+
} | {
|
|
57
|
+
readonly step: "loadingPreview";
|
|
58
|
+
} | {
|
|
59
|
+
readonly step: "previewError";
|
|
60
|
+
readonly error: FlowError;
|
|
61
|
+
}
|
|
62
|
+
/** Terminal: the invitation is not pending (expired/revoked/accepted/declined). */
|
|
63
|
+
| {
|
|
64
|
+
readonly step: "unavailable";
|
|
65
|
+
readonly preview: InvitationPreview;
|
|
66
|
+
readonly status: string;
|
|
67
|
+
}
|
|
68
|
+
/** Signed in with a non-matching account — logout CTA (host slot). */
|
|
69
|
+
| {
|
|
70
|
+
readonly step: "wrongAccount";
|
|
71
|
+
readonly preview: InvitationPreview;
|
|
72
|
+
}
|
|
73
|
+
/** No session, account exists — the host's login slot; call
|
|
74
|
+
* `sessionEstablished(email)` once signed in. */
|
|
75
|
+
| {
|
|
76
|
+
readonly step: "loginRequired";
|
|
77
|
+
readonly preview: InvitationPreview;
|
|
78
|
+
}
|
|
79
|
+
/** No session, no account — deliberate CTA before minting the grant. */
|
|
80
|
+
| {
|
|
81
|
+
readonly step: "newUser";
|
|
82
|
+
readonly preview: InvitationPreview;
|
|
83
|
+
} | {
|
|
84
|
+
readonly step: "claiming";
|
|
85
|
+
readonly preview: InvitationPreview;
|
|
86
|
+
} | {
|
|
87
|
+
readonly step: "claimError";
|
|
88
|
+
readonly preview: InvitationPreview;
|
|
89
|
+
readonly error: FlowError;
|
|
90
|
+
}
|
|
91
|
+
/** The grant is out via `onLoginGrant` — waiting for the host to exchange
|
|
92
|
+
* it at auth and call `grantExchanged()`. */
|
|
93
|
+
| {
|
|
94
|
+
readonly step: "grantIssued";
|
|
95
|
+
readonly preview: InvitationPreview;
|
|
96
|
+
readonly grantToken: string;
|
|
97
|
+
}
|
|
98
|
+
/** Host's basic-data slot (profiles-react InitialSetupPrompt canon, §B5). */
|
|
99
|
+
| {
|
|
100
|
+
readonly step: "basicData";
|
|
101
|
+
readonly preview: InvitationPreview;
|
|
102
|
+
} | {
|
|
103
|
+
readonly step: "acceptPrompt";
|
|
104
|
+
readonly preview: InvitationPreview;
|
|
105
|
+
} | {
|
|
106
|
+
readonly step: "accepting";
|
|
107
|
+
readonly preview: InvitationPreview;
|
|
108
|
+
} | {
|
|
109
|
+
readonly step: "declining";
|
|
110
|
+
readonly preview: InvitationPreview;
|
|
111
|
+
} | {
|
|
112
|
+
readonly step: "acceptError";
|
|
113
|
+
readonly preview: InvitationPreview;
|
|
114
|
+
readonly error: FlowError;
|
|
115
|
+
} | {
|
|
116
|
+
readonly step: "accepted";
|
|
117
|
+
readonly member: Member;
|
|
118
|
+
readonly preview: InvitationPreview;
|
|
119
|
+
} | {
|
|
120
|
+
readonly step: "declined";
|
|
121
|
+
readonly preview: InvitationPreview;
|
|
122
|
+
};
|
|
123
|
+
export interface InviteAcceptFlow {
|
|
124
|
+
readonly machine: FlowMachine<InviteAcceptState>;
|
|
125
|
+
/** Fetch the preview and route per §B4. `sessionEmail` = the signed-in
|
|
126
|
+
* account's email, or null when there is no session. */
|
|
127
|
+
load(sessionEmail: string | null): Promise<void>;
|
|
128
|
+
/** From `newUser`/`claimError`: mint the login grant (`onLoginGrant` fires
|
|
129
|
+
* with the token; the flow parks in `grantIssued`). */
|
|
130
|
+
claim(): Promise<void>;
|
|
131
|
+
/** From `loginRequired`/`wrongAccount`: a session now exists (host's login
|
|
132
|
+
* slot finished, or the user switched accounts) — re-route on its email. */
|
|
133
|
+
sessionEstablished(email: string): void;
|
|
134
|
+
/** From `grantIssued`: the host exchanged the grant at auth — proceed to
|
|
135
|
+
* the basic-data step. */
|
|
136
|
+
grantExchanged(): void;
|
|
137
|
+
/** From `basicData`: the initial-setup slot finished — show the accept prompt. */
|
|
138
|
+
completeBasicData(): void;
|
|
139
|
+
accept(): Promise<void>;
|
|
140
|
+
decline(): Promise<void>;
|
|
141
|
+
reset(): void;
|
|
142
|
+
}
|
|
143
|
+
export interface InviteAcceptFlowDeps {
|
|
144
|
+
readonly api: WorkspacesApi;
|
|
145
|
+
/** The invite token from the `/invite/{token}` URL — a bearer secret. */
|
|
146
|
+
readonly token: string;
|
|
147
|
+
readonly analytics?: Analytics | null;
|
|
148
|
+
/** The grant_token hand-off seam (see the module doc). A credential:
|
|
149
|
+
* exchange it, never log it. */
|
|
150
|
+
readonly onLoginGrant?: (grantToken: string) => void;
|
|
151
|
+
readonly onAccepted?: (member: Member) => void;
|
|
152
|
+
readonly onDeclined?: () => void;
|
|
153
|
+
}
|
|
154
|
+
export declare function createInviteAcceptFlow(deps: InviteAcceptFlowDeps): InviteAcceptFlow;
|
|
155
|
+
//# sourceMappingURL=inviteAcceptFlow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inviteAcceptFlow.d.ts","sourceRoot":"","sources":["../../src/flows/inviteAcceptFlow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { createFlowMachine } from "@stapel/core";
|
|
2
|
+
import { emailMatchesMask } from "../model/emailMask.js";
|
|
3
|
+
import { toFlowError } from "./errors.js";
|
|
4
|
+
/**
|
|
5
|
+
* Flow id: stapel-workspaces annotates no `@flow_step` yet (its
|
|
6
|
+
* `docs/flows.json` is empty — the pair's flow registry is the zero-flow
|
|
7
|
+
* shim), so this id is pair-local until the backend documents the flow;
|
|
8
|
+
* switch to the generated registry id when it lands.
|
|
9
|
+
*/
|
|
10
|
+
const FLOW_ID = "workspaces.invite_accept";
|
|
11
|
+
function routeSignedIn(preview, email) {
|
|
12
|
+
return emailMatchesMask(email, preview.email_masked)
|
|
13
|
+
? { step: "acceptPrompt", preview }
|
|
14
|
+
: { step: "wrongAccount", preview };
|
|
15
|
+
}
|
|
16
|
+
function routePreview(preview, sessionEmail) {
|
|
17
|
+
if (preview.status !== "pending") {
|
|
18
|
+
return { step: "unavailable", preview, status: preview.status };
|
|
19
|
+
}
|
|
20
|
+
if (sessionEmail !== null && sessionEmail !== "") {
|
|
21
|
+
return routeSignedIn(preview, sessionEmail);
|
|
22
|
+
}
|
|
23
|
+
return preview.email_registered
|
|
24
|
+
? { step: "loginRequired", preview }
|
|
25
|
+
: { step: "newUser", preview };
|
|
26
|
+
}
|
|
27
|
+
export function createInviteAcceptFlow(deps) {
|
|
28
|
+
const machine = createFlowMachine({
|
|
29
|
+
id: FLOW_ID,
|
|
30
|
+
initial: { step: "idle" },
|
|
31
|
+
analytics: deps.analytics ?? null,
|
|
32
|
+
});
|
|
33
|
+
async function load(sessionEmail) {
|
|
34
|
+
await machine.run({ step: "loadingPreview" }, () => deps.api.getInvitationPreview(deps.token), {
|
|
35
|
+
resolve: (preview) => routePreview(preview, sessionEmail),
|
|
36
|
+
reject: (error) => ({
|
|
37
|
+
step: "previewError",
|
|
38
|
+
error: toFlowError(error),
|
|
39
|
+
}),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
async function claim() {
|
|
43
|
+
const s = machine.getState();
|
|
44
|
+
if (s.step !== "newUser" && s.step !== "claimError")
|
|
45
|
+
return;
|
|
46
|
+
const { preview } = s;
|
|
47
|
+
await machine.run({ step: "claiming", preview }, () => deps.api.claimInvitation(deps.token), {
|
|
48
|
+
resolve: (r) => {
|
|
49
|
+
deps.onLoginGrant?.(r.grant_token);
|
|
50
|
+
return { step: "grantIssued", preview, grantToken: r.grant_token };
|
|
51
|
+
},
|
|
52
|
+
reject: (error) => ({
|
|
53
|
+
step: "claimError",
|
|
54
|
+
preview,
|
|
55
|
+
error: toFlowError(error),
|
|
56
|
+
}),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function sessionEstablished(email) {
|
|
60
|
+
const s = machine.getState();
|
|
61
|
+
if (s.step !== "loginRequired" && s.step !== "wrongAccount")
|
|
62
|
+
return;
|
|
63
|
+
machine.to(routeSignedIn(s.preview, email));
|
|
64
|
+
}
|
|
65
|
+
function grantExchanged() {
|
|
66
|
+
const s = machine.getState();
|
|
67
|
+
if (s.step !== "grantIssued")
|
|
68
|
+
return;
|
|
69
|
+
machine.to({ step: "basicData", preview: s.preview });
|
|
70
|
+
}
|
|
71
|
+
function completeBasicData() {
|
|
72
|
+
const s = machine.getState();
|
|
73
|
+
if (s.step !== "basicData")
|
|
74
|
+
return;
|
|
75
|
+
machine.to({ step: "acceptPrompt", preview: s.preview });
|
|
76
|
+
}
|
|
77
|
+
async function accept() {
|
|
78
|
+
const s = machine.getState();
|
|
79
|
+
if (s.step !== "acceptPrompt" && s.step !== "acceptError")
|
|
80
|
+
return;
|
|
81
|
+
const { preview } = s;
|
|
82
|
+
await machine.run({ step: "accepting", preview }, () => deps.api.acceptInvitation({ token: deps.token }), {
|
|
83
|
+
resolve: (member) => {
|
|
84
|
+
deps.onAccepted?.(member);
|
|
85
|
+
return { step: "accepted", member, preview };
|
|
86
|
+
},
|
|
87
|
+
reject: (error) => ({
|
|
88
|
+
step: "acceptError",
|
|
89
|
+
preview,
|
|
90
|
+
error: toFlowError(error),
|
|
91
|
+
}),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
async function decline() {
|
|
95
|
+
const s = machine.getState();
|
|
96
|
+
if (s.step !== "acceptPrompt" && s.step !== "acceptError")
|
|
97
|
+
return;
|
|
98
|
+
const { preview } = s;
|
|
99
|
+
await machine.run({ step: "declining", preview }, () => deps.api.declineInvitation(deps.token), {
|
|
100
|
+
resolve: () => {
|
|
101
|
+
deps.onDeclined?.();
|
|
102
|
+
return { step: "declined", preview };
|
|
103
|
+
},
|
|
104
|
+
reject: (error) => ({
|
|
105
|
+
step: "acceptError",
|
|
106
|
+
preview,
|
|
107
|
+
error: toFlowError(error),
|
|
108
|
+
}),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
function reset() {
|
|
112
|
+
machine.to({ step: "idle" });
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
machine,
|
|
116
|
+
load,
|
|
117
|
+
claim,
|
|
118
|
+
sessionEstablished,
|
|
119
|
+
grantExchanged,
|
|
120
|
+
completeBasicData,
|
|
121
|
+
accept,
|
|
122
|
+
decline,
|
|
123
|
+
reset,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=inviteAcceptFlow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inviteAcceptFlow.js","sourceRoot":"","sources":["../../src/flows/inviteAcceptFlow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
/** Render-prop bag for {@link Can}'s function-children variant. */
|
|
3
|
+
export interface CanBag {
|
|
4
|
+
/** The wildcard-aware verdict for `capability` in `workspaceId`. */
|
|
5
|
+
readonly allowed: boolean;
|
|
6
|
+
/** The underlying read is still in flight (verdict is `false` meanwhile —
|
|
7
|
+
* deny-by-default; render a skeleton off this if flicker matters). */
|
|
8
|
+
readonly isLoading: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Capability gate (org-program §A2) — the mandate model's UI affordance:
|
|
12
|
+
*
|
|
13
|
+
* ```tsx
|
|
14
|
+
* <Can capability="meetings.spotlight" workspaceId={wsId}>
|
|
15
|
+
* <SpotlightButton />
|
|
16
|
+
* </Can>
|
|
17
|
+
* // or the render-prop variant, when "disabled" beats "hidden":
|
|
18
|
+
* <Can capability="members.invite" workspaceId={wsId}>
|
|
19
|
+
* {({ allowed }) => <Button disabled={!allowed}>…</Button>}
|
|
20
|
+
* </Can>
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* Reads the caller's `my_capabilities` from the workspace detail
|
|
24
|
+
* ({@link useCapabilities}) and matches with the backend's ported wildcard
|
|
25
|
+
* matcher (`*` / `prefix.*`). Deny-by-default: nothing renders (or
|
|
26
|
+
* `allowed: false`) while loading, on error, for a non-member, or against a
|
|
27
|
+
* pre-0.6 backend. UI convenience ONLY — the backend re-checks every
|
|
28
|
+
* capability on every operation; hiding a button is not access control.
|
|
29
|
+
*/
|
|
30
|
+
export declare function Can(props: {
|
|
31
|
+
/** Namespaced capability string, e.g. `"meetings.spotlight"`. */
|
|
32
|
+
capability: string;
|
|
33
|
+
workspaceId: string;
|
|
34
|
+
/** Static children render when allowed; function children always render,
|
|
35
|
+
* receiving the {@link CanBag}. */
|
|
36
|
+
children: ReactNode | ((bag: CanBag) => ReactNode);
|
|
37
|
+
/** Rendered when the verdict is a definite "no" (static children only). */
|
|
38
|
+
fallback?: ReactNode;
|
|
39
|
+
}): ReactNode;
|
|
40
|
+
//# sourceMappingURL=Can.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Can.d.ts","sourceRoot":"","sources":["../../src/headless/Can.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,mEAAmE;AACnE,MAAM,WAAW,MAAM;IACrB,oEAAoE;IACpE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B;0EACsE;IACtE,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,GAAG,CAAC,KAAK,EAAE;IACzB,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB;uCACmC;IACnC,QAAQ,EAAE,SAAS,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC;IACnD,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB,GAAG,SAAS,CAOZ"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useCapabilities } from "../model/queries.js";
|
|
2
|
+
/**
|
|
3
|
+
* Capability gate (org-program §A2) — the mandate model's UI affordance:
|
|
4
|
+
*
|
|
5
|
+
* ```tsx
|
|
6
|
+
* <Can capability="meetings.spotlight" workspaceId={wsId}>
|
|
7
|
+
* <SpotlightButton />
|
|
8
|
+
* </Can>
|
|
9
|
+
* // or the render-prop variant, when "disabled" beats "hidden":
|
|
10
|
+
* <Can capability="members.invite" workspaceId={wsId}>
|
|
11
|
+
* {({ allowed }) => <Button disabled={!allowed}>…</Button>}
|
|
12
|
+
* </Can>
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Reads the caller's `my_capabilities` from the workspace detail
|
|
16
|
+
* ({@link useCapabilities}) and matches with the backend's ported wildcard
|
|
17
|
+
* matcher (`*` / `prefix.*`). Deny-by-default: nothing renders (or
|
|
18
|
+
* `allowed: false`) while loading, on error, for a non-member, or against a
|
|
19
|
+
* pre-0.6 backend. UI convenience ONLY — the backend re-checks every
|
|
20
|
+
* capability on every operation; hiding a button is not access control.
|
|
21
|
+
*/
|
|
22
|
+
export function Can(props) {
|
|
23
|
+
const caps = useCapabilities(props.workspaceId);
|
|
24
|
+
const allowed = caps.can(props.capability);
|
|
25
|
+
if (typeof props.children === "function") {
|
|
26
|
+
return props.children({ allowed, isLoading: caps.isLoading });
|
|
27
|
+
}
|
|
28
|
+
return allowed ? props.children : (props.fallback ?? null);
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=Can.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Can.js","sourceRoot":"","sources":["../../src/headless/Can.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAWtD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,GAAG,CAAC,KASnB;IACC,MAAM,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC3C,IAAI,OAAO,KAAK,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;QACzC,OAAO,KAAK,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { InviteAcceptState } from "../flows/inviteAcceptFlow.js";
|
|
3
|
+
import type { Member } from "../api/types.js";
|
|
4
|
+
/** Render-prop bag for {@link InviteAcceptFlow}. */
|
|
5
|
+
export interface InviteAcceptFlowBag {
|
|
6
|
+
readonly state: InviteAcceptState;
|
|
7
|
+
/** From `newUser`/`claimError`: mint the login grant. */
|
|
8
|
+
claim(): void;
|
|
9
|
+
/** From `loginRequired`/`wrongAccount`: a session now exists — re-route on
|
|
10
|
+
* its email. (Also fired automatically when the `sessionEmail` prop
|
|
11
|
+
* becomes non-null while parked in either step.) */
|
|
12
|
+
sessionEstablished(email: string): void;
|
|
13
|
+
/** From `grantIssued`: the host exchanged the grant at auth. */
|
|
14
|
+
grantExchanged(): void;
|
|
15
|
+
/** From `basicData`: the initial-setup slot finished. */
|
|
16
|
+
completeBasicData(): void;
|
|
17
|
+
accept(): void;
|
|
18
|
+
decline(): void;
|
|
19
|
+
/** Re-fetch the preview and re-route (e.g. after a logout on wrongAccount). */
|
|
20
|
+
reload(): void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Headless `/invite/{token}` journey (org-program §B4) — the flow machine
|
|
24
|
+
* (`flows/inviteAcceptFlow.ts`, see ITS doc for the full state graph and the
|
|
25
|
+
* grant_token hand-off seam) wired to this pair's runtime. Renderless: every
|
|
26
|
+
* screen (accept modal, wrong-account, the login and initial-setup SLOTS) is
|
|
27
|
+
* the host's; the default `InviteAcceptPage` is the reference skin.
|
|
28
|
+
*
|
|
29
|
+
* The pair knows nothing about auth: pass the signed-in email (or null) as
|
|
30
|
+
* `sessionEmail`, and connect `onLoginGrant` to auth-react's
|
|
31
|
+
* `exchangeLoginGrant` yourself — then call `grantExchanged()`.
|
|
32
|
+
*/
|
|
33
|
+
export declare function InviteAcceptFlow(props: {
|
|
34
|
+
/** The invite token from the URL — a bearer secret, never log it. */
|
|
35
|
+
token: string;
|
|
36
|
+
/** The signed-in account's email, or null/undefined when no session. */
|
|
37
|
+
sessionEmail?: string | null;
|
|
38
|
+
children: (bag: InviteAcceptFlowBag) => ReactNode;
|
|
39
|
+
/** The grant_token hand-off (a credential — exchange, never log). */
|
|
40
|
+
onLoginGrant?: (grantToken: string) => void;
|
|
41
|
+
onAccepted?: (member: Member) => void;
|
|
42
|
+
onDeclined?: () => void;
|
|
43
|
+
}): ReactNode;
|
|
44
|
+
//# sourceMappingURL=InviteAcceptFlow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InviteAcceptFlow.d.ts","sourceRoot":"","sources":["../../src/headless/InviteAcceptFlow.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAG9C,oDAAoD;AACpD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;IAClC,yDAAyD;IACzD,KAAK,IAAI,IAAI,CAAC;IACd;;wDAEoD;IACpD,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,gEAAgE;IAChE,cAAc,IAAI,IAAI,CAAC;IACvB,yDAAyD;IACzD,iBAAiB,IAAI,IAAI,CAAC;IAC1B,MAAM,IAAI,IAAI,CAAC;IACf,OAAO,IAAI,IAAI,CAAC;IAChB,+EAA+E;IAC/E,MAAM,IAAI,IAAI,CAAC;CAChB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,CAAC,GAAG,EAAE,mBAAmB,KAAK,SAAS,CAAC;IAClD,qEAAqE;IACrE,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB,GAAG,SAAS,CAwDZ"}
|