@stapel/workspaces-react 0.13.0 → 0.14.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/default/MembersManager.d.ts.map +1 -1
  3. package/dist/default/MembersManager.js +20 -6
  4. package/dist/default/MembersManager.js.map +1 -1
  5. package/dist/default/WorkspaceSettings.d.ts.map +1 -1
  6. package/dist/default/WorkspaceSettings.js +7 -3
  7. package/dist/default/WorkspaceSettings.js.map +1 -1
  8. package/dist/headless/Can.d.ts +8 -0
  9. package/dist/headless/Can.d.ts.map +1 -1
  10. package/dist/headless/Can.js +5 -1
  11. package/dist/headless/Can.js.map +1 -1
  12. package/dist/headless/Members.d.ts +14 -13
  13. package/dist/headless/Members.d.ts.map +1 -1
  14. package/dist/headless/Members.js +4 -12
  15. package/dist/headless/Members.js.map +1 -1
  16. package/dist/headless/RoleSelect.d.ts +15 -9
  17. package/dist/headless/RoleSelect.d.ts.map +1 -1
  18. package/dist/headless/RoleSelect.js +12 -8
  19. package/dist/headless/RoleSelect.js.map +1 -1
  20. package/dist/headless/WorkspaceList.d.ts +25 -10
  21. package/dist/headless/WorkspaceList.d.ts.map +1 -1
  22. package/dist/headless/WorkspaceList.js +14 -5
  23. package/dist/headless/WorkspaceList.js.map +1 -1
  24. package/dist/i18n/keys.d.ts +13 -0
  25. package/dist/i18n/keys.d.ts.map +1 -1
  26. package/dist/i18n/keys.js +23 -0
  27. package/dist/i18n/keys.js.map +1 -1
  28. package/dist/i18n/ru.d.ts.map +1 -1
  29. package/dist/i18n/ru.js +7 -0
  30. package/dist/i18n/ru.js.map +1 -1
  31. package/dist/model/queries.d.ts +13 -7
  32. package/dist/model/queries.d.ts.map +1 -1
  33. package/dist/model/queries.js +9 -9
  34. package/dist/model/queries.js.map +1 -1
  35. package/dist/model/selection.d.ts +20 -7
  36. package/dist/model/selection.d.ts.map +1 -1
  37. package/dist/model/selection.js +32 -12
  38. package/dist/model/selection.js.map +1 -1
  39. package/llms.txt +1 -1
  40. package/manifest.json +9 -2
  41. package/package.json +2 -2
  42. package/src/analytics/generated/events.json +1 -1
  43. package/src/default/MembersManager.tsx +69 -16
  44. package/src/default/WorkspaceSettings.tsx +41 -5
  45. package/src/headless/Can.tsx +13 -1
  46. package/src/headless/Members.tsx +18 -26
  47. package/src/headless/RoleSelect.tsx +17 -14
  48. package/src/headless/WorkspaceList.tsx +31 -14
  49. package/src/i18n/keys.ts +26 -0
  50. package/src/i18n/ru.ts +10 -0
  51. package/src/model/queries.ts +26 -16
  52. package/src/model/selection.tsx +73 -21
package/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # @stapel/workspaces-react
2
2
 
3
+ ## 0.14.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 400f9e6: The pair the 2026-08-09 incident happened in.
8
+
9
+ `WorkspaceSelection` — the surface products actually consume — gains
10
+ `state: LoadState<readonly Workspace[]>` and LOSES `workspaces` and `loading`.
11
+ It previously had no error field at all, so a host saw `loading: false`,
12
+ `workspaces: []`, `current: null` for a 404 and could not tell that apart from
13
+ a person who belongs to no workspace. `current` stays, documented as null in
14
+ three different situations, which is why a screen must branch on `state`.
15
+
16
+ `WorkspaceListBag`, `MembersBag`, `RoleSelectBag` and `useCapabilities` take
17
+ the same cutover: one `state`, no flattened array, no `isLoading`/`isError`
18
+ read fields. `MembersBag` splits the read failure from `writeError` (an
19
+ invite/role/removal that failed is a different sentence). `CanBag` gains
20
+ `isUnknown` — deny-by-default still holds on a failed capability read, but a
21
+ skin can now say which of the two it is. `RecordingListBag` gains `state` and
22
+ loses `recordings`/`isLoading`/`isError`/`error`.
23
+
24
+ `<MembersManager/>` renders the roster through `matchList`, so a failed read no
25
+ longer produces an error banner AND antd's built-in "No data" illustration at
26
+ the same time; the role registry gets its own sentence rather than silently
27
+ yielding an empty picker. `<WorkspaceSettings/>` no longer greys out the name
28
+ field and Save with no explanation: `useActionGate` + `firstBlock` state
29
+ either "only the owner can change these settings" or "enter a workspace name"
30
+ as visible text.
31
+
32
+ New keys (en + ru): `workspaces.list.load_failed`,
33
+ `workspaces.members.load_failed`, `workspaces.members.empty`,
34
+ `workspaces.roles.load_failed`, `workspaces.retry`,
35
+ `workspaces.settings.blocked.not_owner`,
36
+ `workspaces.settings.blocked.name_required`,
37
+ `recordings.list.load_failed`, `recordings.retry`.
38
+
3
39
  ## 0.13.0
4
40
 
5
41
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"MembersManager.d.ts","sourceRoot":"","sources":["../../src/default/MembersManager.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAW1C,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAID,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,YAAY,CA4JvE"}
1
+ {"version":3,"file":"MembersManager.d.ts","sourceRoot":"","sources":["../../src/default/MembersManager.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAW1C,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAID,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,YAAY,CAiNvE"}
@@ -15,8 +15,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
15
15
  * the invite dialog filters it out, as before.
16
16
  */
17
17
  import { useState } from "react";
18
- import { Button, Card, Input, Modal, Popconfirm, Select, Table, Typography } from "antd";
19
- import { useErrorDisplay, useT } from "@stapel/core";
18
+ import { Button, Card, Empty, Input, Modal, Popconfirm, Select, Table, Typography } from "antd";
19
+ import { loadedRowsOrEmpty, matchList, useErrorDisplay, useT } from "@stapel/core";
20
20
  import { Members } from "../headless/Members.js";
21
21
  import { RoleSelect } from "../headless/RoleSelect.js";
22
22
  import { WORKSPACES_I18N_KEYS } from "../i18n/keys.js";
@@ -32,9 +32,15 @@ export function MembersManager(props) {
32
32
  const [inviteOpen, setInviteOpen] = useState(false);
33
33
  const [emailsText, setEmailsText] = useState("");
34
34
  const [inviteRole, setInviteRole] = useState(DEFAULT_INVITE_ROLE);
35
- return (_jsx(RoleSelect, { children: (rolesBag) => (_jsx(Members, { workspaceId: props.workspaceId, children: ({ members, isLoading, isError, error, invite, isInviting, updateRole, remove }) => {
35
+ return (_jsx(RoleSelect, { children: (rolesBag) => (_jsx(Members, { workspaceId: props.workspaceId, children: ({ state, writeError, invite, isInviting, updateRole, remove, refetch }) => {
36
36
  const { labelFor } = rolesBag;
37
- const roleOptions = rolesBag.roles.map((r) => ({
37
+ // The registry read is a SEPARATE load from the roster's, and it
38
+ // gets its own sentence below rather than silently producing an
39
+ // empty picker — a role menu with nothing in it and a role menu
40
+ // that could not be fetched look identical to a person.
41
+ const rolesFailed = rolesBag.state.status === "failed";
42
+ const rolesLoading = rolesBag.state.status === "loading";
43
+ const roleOptions = loadedRowsOrEmpty(rolesBag.state).map((r) => ({
38
44
  value: r.role,
39
45
  label: labelFor(r.role),
40
46
  }));
@@ -64,7 +70,7 @@ export function MembersManager(props) {
64
70
  {
65
71
  title: t(WORKSPACES_I18N_KEYS.membersInviteRoleLabel),
66
72
  key: "role",
67
- render: (_, member) => canManage ? (_jsx(Select, { value: member.role, style: { width: 160 }, loading: rolesBag.isLoading, onChange: (next) => updateRole({ userId: member.user_id, role: next }),
73
+ render: (_, member) => canManage ? (_jsx(Select, { value: member.role, style: { width: 160 }, loading: rolesLoading, onChange: (next) => updateRole({ userId: member.user_id, role: next }),
68
74
  // A member's CURRENT role may be missing from the
69
75
  // registry options (a deployment removed an overlay
70
76
  // role) — antd shows the raw value then, which matches
@@ -81,7 +87,15 @@ export function MembersManager(props) {
81
87
  ]
82
88
  : []),
83
89
  ];
84
- 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 && _jsx(ErrorAlert, { error: errorDisplay(error), style: { marginTop: 12 } }), _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 })] })] }) })] }));
90
+ 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) }))] }), writeError !== null && (_jsx(ErrorAlert, { error: errorDisplay(writeError), style: { marginTop: 12 } })), rolesFailed && (_jsx(ErrorAlert, { testId: "members-roles-error", error: { message: t(WORKSPACES_I18N_KEYS.rolesLoadFailed), detail: undefined }, style: { marginTop: 12 } })), matchList(state, {
91
+ loading: () => (_jsx(Table, { style: { marginTop: 16 }, size: "small", loading: true, rowKey: (member) => member.id, dataSource: [], columns: columns, pagination: false })),
92
+ // An antd Table with no rows renders "No data". On a failed
93
+ // read that is the empty-state lie with a built-in
94
+ // illustration, so the table does not get rendered at all.
95
+ failed: (error) => (_jsxs("div", { style: { marginTop: 16 }, "data-testid": "members-list-error", children: [_jsx(ErrorAlert, { error: errorDisplay(error) }), _jsx(Button, { style: { marginTop: 12 }, onClick: refetch, "data-analytics": "none", "data-analytics-reason": "local-ui-refetch-after-a-stated-read-failure", children: t(WORKSPACES_I18N_KEYS.retry) })] })),
96
+ empty: () => (_jsx(Empty, { style: { marginTop: 16 }, "data-testid": "members-list-empty", description: t(WORKSPACES_I18N_KEYS.membersEmpty) })),
97
+ ready: (members) => (_jsx(Table, { style: { marginTop: 16 }, size: "small", rowKey: (member) => member.id, dataSource: [...members], columns: columns, pagination: false })),
98
+ }), _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: rolesLoading, options: inviteRoleOptions })] })] }) })] }));
85
99
  } })) }));
86
100
  }
87
101
  //# sourceMappingURL=MembersManager.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MembersManager.js","sourceRoot":"","sources":["../../src/default/MembersManager.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MembersManager.js","sourceRoot":"","sources":["../../src/default/MembersManager.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"WorkspaceSettings.d.ts","sourceRoot":"","sources":["../../src/default/WorkspaceSettings.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAQ1C,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB;gEAC4D;IAC5D,SAAS,CAAC,IAAI,IAAI,CAAC;CACpB;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG,YAAY,CAwG7E"}
1
+ {"version":3,"file":"WorkspaceSettings.d.ts","sourceRoot":"","sources":["../../src/default/WorkspaceSettings.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAe1C,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB;gEAC4D;IAC5D,SAAS,CAAC,IAAI,IAAI,CAAC;CACpB;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG,YAAY,CAqI7E"}
@@ -9,7 +9,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
9
  */
10
10
  import { useEffect, useState } from "react";
11
11
  import { Badge, Button, Card, Input, Popconfirm, Spin, Typography } from "antd";
12
- import { useErrorDisplay, useT } from "@stapel/core";
12
+ import { actionAvailable, actionBlocked, firstBlock, useActionGate, useErrorDisplay, useT, } from "@stapel/core";
13
13
  import { useWorkspace } from "../model/queries.js";
14
14
  import { useUpdateWorkspace, useDeleteWorkspace } from "../model/mutations.js";
15
15
  import { WORKSPACES_I18N_KEYS } from "../i18n/keys.js";
@@ -25,6 +25,11 @@ export function WorkspaceSettings(props) {
25
25
  const deleteMutation = useDeleteWorkspace();
26
26
  const workspace = query.data;
27
27
  const [name, setName] = useState("");
28
+ // Two unrelated situations used to share one `disabled={!isOwner ||
29
+ // !name.trim()}` bit, and a person facing either of them saw the same dead
30
+ // button. `firstBlock` keeps them two, in the order you would explain them.
31
+ const isOwner = workspace?.my_role === "owner";
32
+ const saveGate = useActionGate(firstBlock(isOwner ? actionAvailable() : actionBlocked(WORKSPACES_I18N_KEYS.blockedNotOwner), name.trim() ? actionAvailable() : actionBlocked(WORKSPACES_I18N_KEYS.blockedNameRequired)));
28
33
  useEffect(() => {
29
34
  if (workspace)
30
35
  setName(workspace.name);
@@ -46,7 +51,6 @@ export function WorkspaceSettings(props) {
46
51
  detail: undefined,
47
52
  } }));
48
53
  }
49
- const isOwner = workspace.my_role === "owner";
50
- return (_jsxs("div", { "data-testid": "workspace-settings", style: { display: "grid", gap: 16 }, children: [_jsxs(Card, { children: [_jsx(Typography.Title, { level: 4, style: { marginTop: 0 }, children: t(WORKSPACES_I18N_KEYS.settingsTitle) }), _jsx(Typography.Text, { type: "secondary", children: t(WORKSPACES_I18N_KEYS.settingsSubtitle) }), _jsxs("div", { style: { display: "grid", gap: 12, maxWidth: 480, marginTop: 16 }, children: [_jsxs("div", { children: [_jsx(Typography.Text, { children: t(WORKSPACES_I18N_KEYS.fieldName) }), _jsx(Input, { value: name, onChange: (e) => setName(e.target.value), disabled: !isOwner })] }), _jsxs("div", { children: [_jsx(Typography.Text, { children: t(WORKSPACES_I18N_KEYS.fieldSlug) }), _jsx("div", { children: _jsx(Typography.Text, { code: true, children: workspace.slug }) })] }), _jsxs("div", { children: [_jsx(Typography.Text, { children: t(WORKSPACES_I18N_KEYS.fieldType) }), _jsx("div", { children: _jsx(Badge, { status: "default", text: workspace.type }) })] })] }), _jsx(ErrorAlert, { error: errorDisplay(updateMutation.error), style: { marginTop: 12 } }), _jsx(Button, { type: "primary", style: { marginTop: 16 }, loading: updateMutation.isPending, disabled: !isOwner || !name.trim(), onClick: handleSave, "data-analytics": "none", "data-analytics-reason": "business action \u2014 host app wraps with its own tracked(); pairs carry no @stapel/analytics runtime dependency by architecture", children: updateMutation.isPending ? t(WORKSPACES_I18N_KEYS.saving) : t(WORKSPACES_I18N_KEYS.save) })] }), isOwner && (_jsxs(Card, { children: [_jsx(Typography.Title, { level: 5, type: "danger", style: { marginTop: 0 }, children: t(WORKSPACES_I18N_KEYS.dangerZoneTitle) }), _jsx(Popconfirm, { title: t(WORKSPACES_I18N_KEYS.deleteWorkspaceConfirm), onConfirm: handleDelete, okButtonProps: { loading: deleteMutation.isPending, danger: true }, children: _jsx(Button, { danger: true, children: t(WORKSPACES_I18N_KEYS.deleteWorkspace) }) })] }))] }));
54
+ return (_jsxs("div", { "data-testid": "workspace-settings", style: { display: "grid", gap: 16 }, children: [_jsxs(Card, { children: [_jsx(Typography.Title, { level: 4, style: { marginTop: 0 }, children: t(WORKSPACES_I18N_KEYS.settingsTitle) }), _jsx(Typography.Text, { type: "secondary", children: t(WORKSPACES_I18N_KEYS.settingsSubtitle) }), _jsxs("div", { style: { display: "grid", gap: 12, maxWidth: 480, marginTop: 16 }, children: [_jsxs("div", { children: [_jsx(Typography.Text, { children: t(WORKSPACES_I18N_KEYS.fieldName) }), _jsx(Input, { value: name, onChange: (e) => setName(e.target.value), disabled: !isOwner }), !isOwner && (_jsx("div", { children: _jsx(Typography.Text, { type: "secondary", "data-testid": "workspace-name-blocked", children: t(WORKSPACES_I18N_KEYS.blockedNotOwner) }) }))] }), _jsxs("div", { children: [_jsx(Typography.Text, { children: t(WORKSPACES_I18N_KEYS.fieldSlug) }), _jsx("div", { children: _jsx(Typography.Text, { code: true, children: workspace.slug }) })] }), _jsxs("div", { children: [_jsx(Typography.Text, { children: t(WORKSPACES_I18N_KEYS.fieldType) }), _jsx("div", { children: _jsx(Badge, { status: "default", text: workspace.type }) })] })] }), _jsx(ErrorAlert, { error: errorDisplay(updateMutation.error), style: { marginTop: 12 } }), _jsx(Button, { type: "primary", style: { marginTop: 16 }, loading: updateMutation.isPending, disabled: saveGate.disabled, onClick: handleSave, "data-analytics": "none", "data-analytics-reason": "business action \u2014 host app wraps with its own tracked(); pairs carry no @stapel/analytics runtime dependency by architecture", children: updateMutation.isPending ? t(WORKSPACES_I18N_KEYS.saving) : t(WORKSPACES_I18N_KEYS.save) }), saveGate.reason !== undefined && (_jsx("div", { style: { marginTop: 8 }, children: _jsx(Typography.Text, { type: "secondary", "data-testid": "workspace-save-blocked", children: saveGate.reason }) }))] }), isOwner && (_jsxs(Card, { children: [_jsx(Typography.Title, { level: 5, type: "danger", style: { marginTop: 0 }, children: t(WORKSPACES_I18N_KEYS.dangerZoneTitle) }), _jsx(Popconfirm, { title: t(WORKSPACES_I18N_KEYS.deleteWorkspaceConfirm), onConfirm: handleDelete, okButtonProps: { loading: deleteMutation.isPending, danger: true }, children: _jsx(Button, { danger: true, children: t(WORKSPACES_I18N_KEYS.deleteWorkspace) }) })] }))] }));
51
55
  }
52
56
  //# sourceMappingURL=WorkspaceSettings.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"WorkspaceSettings.js","sourceRoot":"","sources":["../../src/default/WorkspaceSettings.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"WorkspaceSettings.js","sourceRoot":"","sources":["../../src/default/WorkspaceSettings.tsx"],"names":[],"mappings":"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"}
@@ -6,6 +6,14 @@ export interface CanBag {
6
6
  /** The underlying read is still in flight (verdict is `false` meanwhile —
7
7
  * deny-by-default; render a skeleton off this if flicker matters). */
8
8
  readonly isLoading: boolean;
9
+ /**
10
+ * The read FAILED, so `allowed: false` means "we could not find out",
11
+ * not "you may not". Deny-by-default still holds — a capability check that
12
+ * opened up on an outage would be a security hole — but a skin with
13
+ * somewhere to put a sentence should say which of the two it is rather than
14
+ * silently withholding the control.
15
+ */
16
+ readonly isUnknown: boolean;
9
17
  }
10
18
  /**
11
19
  * Capability gate (org-program §A2) — the mandate model's UI affordance:
@@ -1 +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"}
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;IAC5B;;;;;;OAMG;IACH,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,CAWZ"}
@@ -23,7 +23,11 @@ export function Can(props) {
23
23
  const caps = useCapabilities(props.workspaceId);
24
24
  const allowed = caps.can(props.capability);
25
25
  if (typeof props.children === "function") {
26
- return props.children({ allowed, isLoading: caps.isLoading });
26
+ return props.children({
27
+ allowed,
28
+ isLoading: caps.state.status === "loading",
29
+ isUnknown: caps.state.status === "failed",
30
+ });
27
31
  }
28
32
  return allowed ? props.children : (props.fallback ?? null);
29
33
  }
@@ -1 +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"}
1
+ {"version":3,"file":"Can.js","sourceRoot":"","sources":["../../src/headless/Can.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAmBtD;;;;;;;;;;;;;;;;;;;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;YACpB,OAAO;YACP,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS;YAC1C,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,QAAQ;SAC1C,CAAC,CAAC;IACL,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC,CAAC;AAC7D,CAAC"}
@@ -1,22 +1,23 @@
1
1
  import type { ReactNode } from "react";
2
- import type { StapelApiError } from "@stapel/core";
2
+ import type { LoadState, StapelApiError } from "@stapel/core";
3
3
  import type { Member, MemberInvite } from "../api/types.js";
4
4
  import type { MemberRoleChange } from "../model/mutations.js";
5
5
  /** Render-prop bag for {@link Members}. */
6
6
  export interface MembersBag {
7
7
  /**
8
- * The workspace's members once loaded (empty before load). This is one page
9
- * (default 100) — the roster's own {@link Member.email}/`search` narrows
10
- * further; a full pager is out of scope for this renderless view (bring
11
- * your own via `useMembers`' `params` for anchor pagination).
8
+ * The roster read, as a state a skin cannot flatten — render it with core's
9
+ * `matchList`. This is one page (default 100); a full pager is out of scope
10
+ * for this renderless view (bring your own via `useMembers`' `params` for
11
+ * anchor pagination).
12
+ *
13
+ * Separate from {@link writeError} on purpose: "the roster could not be
14
+ * loaded" and "your invite was rejected" are different sentences, and the
15
+ * old merged `isError` could only produce one of them.
12
16
  */
13
- readonly members: readonly Member[];
14
- /** The initial member-list load is in flight. */
15
- readonly isLoading: boolean;
16
- /** The read, an invite, a role change, or a removal failed. */
17
- readonly isError: boolean;
18
- /** The error, when `isError` (a localizable `StapelApiError`), else null. */
19
- readonly error: StapelApiError | null;
17
+ readonly state: LoadState<readonly Member[]>;
18
+ /** An invite, a role change, or a removal failed. Never the read — that is
19
+ * {@link state}. */
20
+ readonly writeError: StapelApiError | null;
20
21
  /** Invite one or more emails at a role. */
21
22
  invite(body: MemberInvite): void;
22
23
  /** An invite call is in flight. */
@@ -41,7 +42,7 @@ export interface MembersBag {
41
42
  *
42
43
  * ```tsx
43
44
  * <Members workspaceId={id}>
44
- * {({ members, invite, updateRole, remove }) => ( ... )}
45
+ * {({ state, invite, updateRole, remove }) => ( ... )}
45
46
  * </Members>
46
47
  * ```
47
48
  */
@@ -1 +1 @@
1
- {"version":3,"file":"Members.d.ts","sourceRoot":"","sources":["../../src/headless/Members.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAO5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,2CAA2C;AAC3C,MAAM,WAAW,UAAU;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,iDAAiD;IACjD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,2CAA2C;IAC3C,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;IACjC,mCAAmC;IACnC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,8BAA8B;IAC9B,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC3C,uCAAuC;IACvC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,mCAAmC;IACnC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mCAAmC;IACnC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,+CAA+C;IAC/C,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,SAAS,CAAC;CAC1C,GAAG,SAAS,CAmCZ"}
1
+ {"version":3,"file":"Members.d.ts","sourceRoot":"","sources":["../../src/headless/Members.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAO5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,2CAA2C;AAC3C,MAAM,WAAW,UAAU;IACzB;;;;;;;;;OASG;IACH,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;IAC7C;wBACoB;IACpB,QAAQ,CAAC,UAAU,EAAE,cAAc,GAAG,IAAI,CAAC;IAC3C,2CAA2C;IAC3C,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;IACjC,mCAAmC;IACnC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,8BAA8B;IAC9B,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC3C,uCAAuC;IACvC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,mCAAmC;IACnC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mCAAmC;IACnC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,+CAA+C;IAC/C,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,SAAS,CAAC;CAC1C,GAAG,SAAS,CAyBZ"}
@@ -1,3 +1,4 @@
1
+ import { loadStateFromQuery, mapLoad } from "@stapel/core";
1
2
  import { useMembers } from "../model/queries.js";
2
3
  import { useInviteMembers, useRemoveMember, useUpdateMemberRole, } from "../model/mutations.js";
3
4
  /**
@@ -9,7 +10,7 @@ import { useInviteMembers, useRemoveMember, useUpdateMemberRole, } from "../mode
9
10
  *
10
11
  * ```tsx
11
12
  * <Members workspaceId={id}>
12
- * {({ members, invite, updateRole, remove }) => ( ... )}
13
+ * {({ state, invite, updateRole, remove }) => ( ... )}
13
14
  * </Members>
14
15
  * ```
15
16
  */
@@ -19,17 +20,8 @@ export function Members(props) {
19
20
  const roleMutation = useUpdateMemberRole(props.workspaceId);
20
21
  const removeMutation = useRemoveMember(props.workspaceId);
21
22
  return props.children({
22
- members: query.data?.items ?? [],
23
- isLoading: query.isLoading,
24
- isError: query.isError ||
25
- inviteMutation.isError ||
26
- roleMutation.isError ||
27
- removeMutation.isError,
28
- error: query.error ??
29
- inviteMutation.error ??
30
- roleMutation.error ??
31
- removeMutation.error ??
32
- null,
23
+ state: mapLoad(loadStateFromQuery(query), (page) => page.items),
24
+ writeError: inviteMutation.error ?? roleMutation.error ?? removeMutation.error ?? null,
33
25
  invite: (body) => {
34
26
  inviteMutation.mutate(body);
35
27
  },
@@ -1 +1 @@
1
- {"version":3,"file":"Members.js","sourceRoot":"","sources":["../../src/headless/Members.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAkC/B;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,OAAO,CAAC,KAGvB;IACC,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC5C,MAAM,cAAc,GAAG,gBAAgB,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC3D,MAAM,YAAY,GAAG,mBAAmB,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC5D,MAAM,cAAc,GAAG,eAAe,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC1D,OAAO,KAAK,CAAC,QAAQ,CAAC;QACpB,OAAO,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;QAChC,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,OAAO,EACL,KAAK,CAAC,OAAO;YACb,cAAc,CAAC,OAAO;YACtB,YAAY,CAAC,OAAO;YACpB,cAAc,CAAC,OAAO;QACxB,KAAK,EACH,KAAK,CAAC,KAAK;YACX,cAAc,CAAC,KAAK;YACpB,YAAY,CAAC,KAAK;YAClB,cAAc,CAAC,KAAK;YACpB,IAAI;QACN,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACf,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC;QACD,UAAU,EAAE,cAAc,CAAC,SAAS;QACpC,UAAU,EAAE,CAAC,MAAM,EAAE,EAAE;YACrB,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QACD,cAAc,EAAE,YAAY,CAAC,SAAS;QACtC,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE;YACjB,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC;QACD,UAAU,EAAE,cAAc,CAAC,SAAS;QACpC,OAAO,EAAE,GAAG,EAAE;YACZ,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;QACvB,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"Members.js","sourceRoot":"","sources":["../../src/headless/Members.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAG3D,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAmC/B;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,OAAO,CAAC,KAGvB;IACC,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC5C,MAAM,cAAc,GAAG,gBAAgB,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC3D,MAAM,YAAY,GAAG,mBAAmB,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC5D,MAAM,cAAc,GAAG,eAAe,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC1D,OAAO,KAAK,CAAC,QAAQ,CAAC;QACpB,KAAK,EAAE,OAAO,CAAC,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;QAC/D,UAAU,EACR,cAAc,CAAC,KAAK,IAAI,YAAY,CAAC,KAAK,IAAI,cAAc,CAAC,KAAK,IAAI,IAAI;QAC5E,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACf,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC;QACD,UAAU,EAAE,cAAc,CAAC,SAAS;QACpC,UAAU,EAAE,CAAC,MAAM,EAAE,EAAE;YACrB,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QACD,cAAc,EAAE,YAAY,CAAC,SAAS;QACtC,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE;YACjB,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC;QACD,UAAU,EAAE,cAAc,CAAC,SAAS;QACpC,OAAO,EAAE,GAAG,EAAE;YACZ,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;QACvB,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -1,11 +1,13 @@
1
1
  import type { ReactNode } from "react";
2
- import type { StapelApiError } from "@stapel/core";
2
+ import type { LoadState } from "@stapel/core";
3
3
  import type { RoleInfo } from "../api/types.js";
4
4
  /** Render-prop bag for {@link RoleSelect}. */
5
5
  export interface RoleSelectBag {
6
6
  /** The effective registry (GET /roles): builtin four + the deployment's
7
- * overlay, rank-descending. Empty while loading. */
8
- readonly roles: readonly RoleInfo[];
7
+ * overlay, rank-descending. A state rather than an array: a role picker
8
+ * built from `[]` because the registry read FAILED offers no roles at all
9
+ * and looks exactly like a deployment that defined none. */
10
+ readonly state: LoadState<readonly RoleInfo[]>;
9
11
  /**
10
12
  * Display label for a role key: the i18n key `workspaces.role.<key>` when
11
13
  * the current locale's merged bundle carries it (the pair ships the builtin
@@ -15,9 +17,6 @@ export interface RoleSelectBag {
15
17
  * never render as a dotted i18n key.
16
18
  */
17
19
  labelFor(role: string): string;
18
- readonly isLoading: boolean;
19
- readonly isError: boolean;
20
- readonly error: StapelApiError | null;
21
20
  }
22
21
  /**
23
22
  * Headless role picker over the effective role registry (org-program §A2) —
@@ -26,9 +25,16 @@ export interface RoleSelectBag {
26
25
  *
27
26
  * ```tsx
28
27
  * <RoleSelect>
29
- * {({ roles, labelFor }) => (
30
- * <Select options={roles.map((r) => ({ value: r.role, label: labelFor(r.role) }))} />
31
- * )}
28
+ * {({ state, labelFor }) =>
29
+ * matchList(state, {
30
+ * loading: () => <Select loading options={[]} />,
31
+ * failed: (error) => <RoleLoadFailed error={error} />,
32
+ * empty: () => <Select disabled options={[]} />,
33
+ * ready: (roles) => (
34
+ * <Select options={roles.map((r) => ({ value: r.role, label: labelFor(r.role) }))} />
35
+ * ),
36
+ * })
37
+ * }
32
38
  * </RoleSelect>
33
39
  * ```
34
40
  *
@@ -1 +1 @@
1
- {"version":3,"file":"RoleSelect.d.ts","sourceRoot":"","sources":["../../src/headless/RoleSelect.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAGhD,8CAA8C;AAC9C,MAAM,WAAW,aAAa;IAC5B;wDACoD;IACpD,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;IACpC;;;;;;;OAOG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;CACvC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE;IAChC,QAAQ,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,SAAS,CAAC;CAC7C,GAAG,SAAS,CAcZ"}
1
+ {"version":3,"file":"RoleSelect.d.ts","sourceRoot":"","sources":["../../src/headless/RoleSelect.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAGhD,8CAA8C;AAC9C,MAAM,WAAW,aAAa;IAC5B;;;gEAG4D;IAC5D,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,SAAS,QAAQ,EAAE,CAAC,CAAC;IAC/C;;;;;;;OAOG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE;IAChC,QAAQ,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,SAAS,CAAC;CAC7C,GAAG,SAAS,CAWZ"}
@@ -1,4 +1,4 @@
1
- import { useI18n } from "@stapel/core";
1
+ import { loadStateFromQuery, useI18n } from "@stapel/core";
2
2
  import { useRoles } from "../model/queries.js";
3
3
  /**
4
4
  * Headless role picker over the effective role registry (org-program §A2) —
@@ -7,9 +7,16 @@ import { useRoles } from "../model/queries.js";
7
7
  *
8
8
  * ```tsx
9
9
  * <RoleSelect>
10
- * {({ roles, labelFor }) => (
11
- * <Select options={roles.map((r) => ({ value: r.role, label: labelFor(r.role) }))} />
12
- * )}
10
+ * {({ state, labelFor }) =>
11
+ * matchList(state, {
12
+ * loading: () => <Select loading options={[]} />,
13
+ * failed: (error) => <RoleLoadFailed error={error} />,
14
+ * empty: () => <Select disabled options={[]} />,
15
+ * ready: (roles) => (
16
+ * <Select options={roles.map((r) => ({ value: r.role, label: labelFor(r.role) }))} />
17
+ * ),
18
+ * })
19
+ * }
13
20
  * </RoleSelect>
14
21
  * ```
15
22
  *
@@ -24,11 +31,8 @@ export function RoleSelect(props) {
24
31
  // must show its raw name, not `workspaces.role.secretary`).
25
32
  const bundle = i18n.getBundle();
26
33
  return props.children({
27
- roles: query.data ?? [],
34
+ state: loadStateFromQuery(query),
28
35
  labelFor: (role) => bundle[`workspaces.role.${role}`] ?? role,
29
- isLoading: query.isLoading,
30
- isError: query.isError,
31
- error: query.error ?? null,
32
36
  });
33
37
  }
34
38
  //# sourceMappingURL=RoleSelect.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RoleSelect.js","sourceRoot":"","sources":["../../src/headless/RoleSelect.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAqB/C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,UAAU,CAAC,KAE1B;IACC,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;IACzB,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,2EAA2E;IAC3E,2EAA2E;IAC3E,4DAA4D;IAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;IAChC,OAAO,KAAK,CAAC,QAAQ,CAAC;QACpB,KAAK,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE;QACvB,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,mBAAmB,IAAI,EAAE,CAAC,IAAI,IAAI;QAC7D,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,IAAI;KAC3B,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"RoleSelect.js","sourceRoot":"","sources":["../../src/headless/RoleSelect.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAG3D,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAoB/C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,UAAU,CAAC,KAE1B;IACC,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;IACzB,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,2EAA2E;IAC3E,2EAA2E;IAC3E,4DAA4D;IAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;IAChC,OAAO,KAAK,CAAC,QAAQ,CAAC;QACpB,KAAK,EAAE,kBAAkB,CAAC,KAAK,CAAC;QAChC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,mBAAmB,IAAI,EAAE,CAAC,IAAI,IAAI;KAC9D,CAAC,CAAC;AACL,CAAC"}
@@ -1,22 +1,30 @@
1
1
  import type { ReactNode } from "react";
2
- import type { StapelApiError } from "@stapel/core";
2
+ import type { LoadState, StapelApiError } from "@stapel/core";
3
3
  import type { Workspace, WorkspaceCreate } from "../api/types.js";
4
4
  /** Render-prop bag for {@link WorkspaceList}. */
5
5
  export interface WorkspaceListBag {
6
- /** The caller's workspaces once loaded (empty before load). */
7
- readonly workspaces: readonly Workspace[];
8
- /** The initial list load is in flight (no data yet). */
9
- readonly isLoading: boolean;
10
- /** Either the read or the create failed. */
11
- readonly isError: boolean;
12
- /** The error, when `isError` (a localizable `StapelApiError`), else null. */
13
- readonly error: StapelApiError | null;
6
+ /**
7
+ * The read, as a state a skin cannot flatten: `loading` / `ready` with the
8
+ * rows / `failed` with the error. Render it with core's `matchList`, whose
9
+ * four arms are all required, so "you have no workspaces" can only be said
10
+ * about a load that actually succeeded.
11
+ *
12
+ * This replaced a `workspaces: readonly Workspace[]` field that was `[]` in
13
+ * all three cases. On 2026-08-09 the list endpoint answered 404 for hours
14
+ * and every screen built on that field said the person had no workspaces.
15
+ * The bag carried `isError` beside it the whole time; nothing forced a skin
16
+ * to look, so nothing did.
17
+ */
18
+ readonly state: LoadState<readonly Workspace[]>;
14
19
  /** Create a workspace (owner-seeded). */
15
20
  create(body: WorkspaceCreate): void;
16
21
  /** A create call is in flight. */
17
22
  readonly isCreating: boolean;
18
23
  /** The workspace created by the last `create`, else null. */
19
24
  readonly created: Workspace | null;
25
+ /** The CREATE's failure — a write, and therefore a different question from
26
+ * {@link state}, which is about the read. */
27
+ readonly createError: StapelApiError | null;
20
28
  /** Refetch the list from the server. */
21
29
  refetch(): void;
22
30
  }
@@ -28,7 +36,14 @@ export interface WorkspaceListBag {
28
36
  *
29
37
  * ```tsx
30
38
  * <WorkspaceList>
31
- * {({ workspaces, create }) => ( ... )}
39
+ * {({ state, create }) =>
40
+ * matchList(state, {
41
+ * loading: () => <Spinner />,
42
+ * failed: (error) => <ErrorPanel error={error} onRetry={refetch} />,
43
+ * empty: () => <FirstWorkspacePrompt onCreate={create} />,
44
+ * ready: (workspaces) => <List items={workspaces} />,
45
+ * })
46
+ * }
32
47
  * </WorkspaceList>
33
48
  * ```
34
49
  */
@@ -1 +1 @@
1
- {"version":3,"file":"WorkspaceList.d.ts","sourceRoot":"","sources":["../../src/headless/WorkspaceList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAIlE,iDAAiD;AACjD,MAAM,WAAW,gBAAgB;IAC/B,+DAA+D;IAC/D,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C,wDAAwD;IACxD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,4CAA4C;IAC5C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,yCAAyC;IACzC,MAAM,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAAC;IACpC,kCAAkC;IAClC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,EAAE,SAAS,GAAG,IAAI,CAAC;IACnC,wCAAwC;IACxC,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACnC,QAAQ,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,SAAS,CAAC;CAChD,GAAG,SAAS,CAiBZ"}
1
+ {"version":3,"file":"WorkspaceList.d.ts","sourceRoot":"","sources":["../../src/headless/WorkspaceList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAIlE,iDAAiD;AACjD,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,SAAS,SAAS,EAAE,CAAC,CAAC;IAChD,yCAAyC;IACzC,MAAM,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAAC;IACpC,kCAAkC;IAClC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,EAAE,SAAS,GAAG,IAAI,CAAC;IACnC;iDAC6C;IAC7C,QAAQ,CAAC,WAAW,EAAE,cAAc,GAAG,IAAI,CAAC;IAC5C,wCAAwC;IACxC,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACnC,QAAQ,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,SAAS,CAAC;CAChD,GAAG,SAAS,CAkBZ"}
@@ -1,3 +1,4 @@
1
+ import { loadStateFromQuery, mapLoad } from "@stapel/core";
1
2
  import { useWorkspaces } from "../model/queries.js";
2
3
  import { useCreateWorkspace } from "../model/mutations.js";
3
4
  /**
@@ -8,7 +9,14 @@ import { useCreateWorkspace } from "../model/mutations.js";
8
9
  *
9
10
  * ```tsx
10
11
  * <WorkspaceList>
11
- * {({ workspaces, create }) => ( ... )}
12
+ * {({ state, create }) =>
13
+ * matchList(state, {
14
+ * loading: () => <Spinner />,
15
+ * failed: (error) => <ErrorPanel error={error} onRetry={refetch} />,
16
+ * empty: () => <FirstWorkspacePrompt onCreate={create} />,
17
+ * ready: (workspaces) => <List items={workspaces} />,
18
+ * })
19
+ * }
12
20
  * </WorkspaceList>
13
21
  * ```
14
22
  */
@@ -16,15 +24,16 @@ export function WorkspaceList(props) {
16
24
  const query = useWorkspaces();
17
25
  const mutation = useCreateWorkspace();
18
26
  return props.children({
19
- workspaces: query.data?.workspaces ?? [],
20
- isLoading: query.isLoading,
21
- isError: query.isError || mutation.isError,
22
- error: query.error ?? mutation.error ?? null,
27
+ // `workspaces` is optional in the wire schema, so a SUCCESSFUL response
28
+ // may legitimately carry none. That is an empty list, not a failed load —
29
+ // the distinction this whole field exists for is already made above it.
30
+ state: mapLoad(loadStateFromQuery(query), (list) => list.workspaces ?? []),
23
31
  create: (body) => {
24
32
  mutation.mutate(body);
25
33
  },
26
34
  isCreating: mutation.isPending,
27
35
  created: mutation.data ?? null,
36
+ createError: mutation.error ?? null,
28
37
  refetch: () => {
29
38
  void query.refetch();
30
39
  },
@@ -1 +1 @@
1
- {"version":3,"file":"WorkspaceList.js","sourceRoot":"","sources":["../../src/headless/WorkspaceList.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAsB3D;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAAC,KAE7B;IACC,MAAM,KAAK,GAAG,aAAa,EAAE,CAAC;IAC9B,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAC;IACtC,OAAO,KAAK,CAAC,QAAQ,CAAC;QACpB,UAAU,EAAE,KAAK,CAAC,IAAI,EAAE,UAAU,IAAI,EAAE;QACxC,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO;QAC1C,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,QAAQ,CAAC,KAAK,IAAI,IAAI;QAC5C,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACf,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACxB,CAAC;QACD,UAAU,EAAE,QAAQ,CAAC,SAAS;QAC9B,OAAO,EAAE,QAAQ,CAAC,IAAI,IAAI,IAAI;QAC9B,OAAO,EAAE,GAAG,EAAE;YACZ,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;QACvB,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"WorkspaceList.js","sourceRoot":"","sources":["../../src/headless/WorkspaceList.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAG3D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AA8B3D;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,aAAa,CAAC,KAE7B;IACC,MAAM,KAAK,GAAG,aAAa,EAAE,CAAC;IAC9B,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAC;IACtC,OAAO,KAAK,CAAC,QAAQ,CAAC;QACpB,wEAAwE;QACxE,0EAA0E;QAC1E,wEAAwE;QACxE,KAAK,EAAE,OAAO,CAAC,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC;QAC1E,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACf,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACxB,CAAC;QACD,UAAU,EAAE,QAAQ,CAAC,SAAS;QAC9B,OAAO,EAAE,QAAQ,CAAC,IAAI,IAAI,IAAI;QAC9B,WAAW,EAAE,QAAQ,CAAC,KAAK,IAAI,IAAI;QACnC,OAAO,EAAE,GAAG,EAAE;YACZ,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;QACvB,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -10,11 +10,22 @@ import type { I18nDictionary, I18nEngine } from "@stapel/core";
10
10
  */
11
11
  export declare const WORKSPACES_I18N_KEYS: {
12
12
  readonly unknownError: "workspaces.error.unknown";
13
+ readonly retry: "workspaces.retry";
13
14
  readonly listLoading: "workspaces.list.loading";
14
15
  readonly listEmpty: "workspaces.list.empty";
16
+ /**
17
+ * "We could not load your workspaces" — the sentence whose ABSENCE was the
18
+ * 2026-08-09 incident. Deliberately about US failing to load, never about
19
+ * the person having none, and never a 404's "no longer available" (which
20
+ * asserts the thing does not exist — tracker #211).
21
+ */
22
+ readonly listLoadFailed: "workspaces.list.load_failed";
15
23
  readonly listCreate: "workspaces.list.create";
16
24
  readonly listCreating: "workspaces.list.creating";
17
25
  readonly membersLoading: "workspaces.members.loading";
26
+ readonly membersEmpty: "workspaces.members.empty";
27
+ readonly membersLoadFailed: "workspaces.members.load_failed";
28
+ readonly rolesLoadFailed: "workspaces.roles.load_failed";
18
29
  readonly membersInvite: "workspaces.members.invite";
19
30
  readonly membersInviting: "workspaces.members.inviting";
20
31
  readonly membersUpdateRole: "workspaces.members.update_role";
@@ -28,6 +39,8 @@ export declare const WORKSPACES_I18N_KEYS: {
28
39
  readonly fieldSlug: "workspaces.settings.field.slug";
29
40
  readonly fieldType: "workspaces.settings.field.type";
30
41
  readonly save: "workspaces.settings.save";
42
+ readonly blockedNotOwner: "workspaces.settings.blocked.not_owner";
43
+ readonly blockedNameRequired: "workspaces.settings.blocked.name_required";
31
44
  readonly saving: "workspaces.settings.saving";
32
45
  readonly dangerZoneTitle: "workspaces.settings.danger_zone.title";
33
46
  readonly deleteWorkspace: "workspaces.settings.danger_zone.delete";
@@ -1 +1 @@
1
- {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/i18n/keys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/D;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0EvB,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAC3B,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,OAAO,oBAAoB,CAAC,CAAC;AAEnE;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,EAAE,cAyEpC,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,SAAO,GAAG,IAAI,CAE9E"}
1
+ {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/i18n/keys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/D;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB;;;;;IAQ/B;;;;;OAKG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6EK,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAC3B,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,OAAO,oBAAoB,CAAC,CAAC;AAEnE;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,EAAE,cAmFpC,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,SAAO,GAAG,IAAI,CAE9E"}