@agent-native/core 0.162.0 → 0.163.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/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/assets/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/brain/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/calendar/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/chat/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/clips/app/lib/camera-composite.ts +15 -1
- package/corpus/templates/clips/changelog/2026-08-18-clips-no-longer-crashes-when-a-camera-bubble-starts-at-a-tin.md +6 -0
- package/corpus/templates/content/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/content/actions/create-content-database.ts +1 -1
- package/corpus/templates/content/actions/create-document.ts +1 -1
- package/corpus/templates/crm/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/design/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +6 -1
- package/corpus/templates/dispatch/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/factory/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/forms/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/macros/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/mail/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/plan/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/slides/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +3 -3
- package/corpus/templates/slides/app/components/editor/GoogleSlidesReferenceImport.tsx +3 -3
- package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +3 -1
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1 -1
- package/corpus/templates/slides/app/components/editor/slide-object-interactions.ts +1 -1
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +1 -1
- package/corpus/templates/slides/server/handlers/request-auth-context.ts +1 -0
- package/corpus/templates/tasks/.agents/skills/sharing/SKILL.md +27 -2
- package/dist/app-config/schema.d.ts +1 -0
- package/dist/app-config/workspace.d.ts +1 -0
- package/dist/app-config/workspace.js +8 -0
- package/dist/chat-threads/schema.d.ts +2 -2
- package/dist/client/org/TeamPage.js +15 -2
- package/dist/client/org/hooks.d.ts +2 -0
- package/dist/client/org/hooks.js +12 -0
- package/dist/client/org/index.d.ts +2 -2
- package/dist/client/org/index.js +1 -1
- package/dist/client/sharing/ShareButton.js +43 -18
- package/dist/client/sharing/useShareButtonController.d.ts +12 -2
- package/dist/client/sharing/useShareButtonController.js +38 -8
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/dashboard-storage/schema.d.ts +2 -2
- package/dist/data-programs/schema.d.ts +2 -2
- package/dist/extensions/schema.d.ts +2 -2
- package/dist/extensions/store.js +3 -0
- package/dist/localization/core-messages/ar-SA.js +1 -0
- package/dist/localization/core-messages/de-DE.js +1 -0
- package/dist/localization/core-messages/en-US.d.ts +1 -0
- package/dist/localization/core-messages/en-US.js +1 -0
- package/dist/localization/core-messages/es-ES.js +1 -0
- package/dist/localization/core-messages/fr-FR.js +1 -0
- package/dist/localization/core-messages/hi-IN.js +1 -0
- package/dist/localization/core-messages/ja-JP.js +1 -0
- package/dist/localization/core-messages/ko-KR.js +1 -0
- package/dist/localization/core-messages/pt-BR.js +1 -0
- package/dist/localization/core-messages/zh-CN.js +1 -0
- package/dist/localization/core-messages/zh-TW.js +1 -0
- package/dist/localization/core-messages.d.ts +1 -0
- package/dist/localization/default-messages.d.ts +4 -0
- package/dist/localization/default-messages.js +4 -0
- package/dist/org/handlers.d.ts +6 -1
- package/dist/org/handlers.js +43 -0
- package/dist/org/index.d.ts +3 -3
- package/dist/org/index.js +2 -2
- package/dist/org/migrations.js +33 -0
- package/dist/org/plugin.js +10 -1
- package/dist/org/schema.d.ts +315 -0
- package/dist/org/schema.js +20 -1
- package/dist/org/types.d.ts +3 -0
- package/dist/org/workspace-app-access.d.ts +10 -0
- package/dist/org/workspace-app-access.js +179 -0
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +8 -8
- package/dist/provider-api/actions/provider-api.d.ts +4 -4
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.js +14 -1
- package/dist/server/poll.js +6 -0
- package/dist/sharing/access.d.ts +2 -2
- package/dist/sharing/access.js +56 -3
- package/dist/sharing/actions/list-resource-shares.d.ts +2 -0
- package/dist/sharing/actions/list-resource-shares.js +26 -1
- package/dist/sharing/actions/share-resource.d.ts +1 -1
- package/dist/sharing/actions/share-resource.js +23 -4
- package/dist/sharing/actions/unshare-resource.d.ts +1 -1
- package/dist/sharing/actions/unshare-resource.js +1 -1
- package/dist/sharing/registry.d.ts +12 -0
- package/dist/sharing/schema.d.ts +4 -4
- package/dist/sharing/schema.js +2 -2
- package/dist/templates/chat/.agents/skills/sharing/SKILL.md +27 -2
- package/dist/templates/default/.agents/skills/sharing/SKILL.md +27 -2
- package/dist/templates/workspace-core/.agents/skills/sharing/SKILL.md +27 -2
- package/dist/workspace-connections/actions/list-workspace-user-groups.d.ts +9 -1
- package/dist/workspace-connections/actions/list-workspace-user-groups.js +9 -4
- package/dist/workspace-connections/groups.d.ts +2 -0
- package/dist/workspace-connections/groups.js +16 -5
- package/package.json +1 -1
- package/src/templates/chat/.agents/skills/sharing/SKILL.md +27 -2
- package/src/templates/default/.agents/skills/sharing/SKILL.md +27 -2
- package/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +27 -2
|
@@ -18,6 +18,11 @@ Any resource a user **creates** (dashboards, documents, forms, decks, compositio
|
|
|
18
18
|
|
|
19
19
|
This is the framework-level primitive. Every ownable resource gets it for free — same API, same UI, same skill.
|
|
20
20
|
|
|
21
|
+
Workspace apps are the intentional organization-scoped exception: their default
|
|
22
|
+
visibility is `org`, configurable by an organization owner/admin. The creator is
|
|
23
|
+
stored as the app owner, and the creator or an organization owner/admin can
|
|
24
|
+
change the app's visibility or manage its share grants.
|
|
25
|
+
|
|
21
26
|
## Concepts
|
|
22
27
|
|
|
23
28
|
### Three visibility levels
|
|
@@ -26,7 +31,8 @@ This is the framework-level primitive. Every ownable resource gets it for free
|
|
|
26
31
|
- **`org`** — owner + explicit grants + anyone in the same org (read-only).
|
|
27
32
|
- **`public`** — owner + explicit grants + **anyone with the link** (read-only). Public docs do NOT appear in other users' list/sidebar/search results — `accessFilter` omits them by default. They're reachable by id (`resolveAccess` admits them) so direct links and SSR routes like `/p/:id` keep working. If a list endpoint legitimately needs cross-user public discovery (a template gallery, etc.), pass `accessFilter(table, shares, ctx, minRole, { includePublic: true })`.
|
|
28
33
|
|
|
29
|
-
Visibility is coarse. Explicit share grants are fine-grained (per user
|
|
34
|
+
Visibility is coarse. Explicit share grants are fine-grained (per user, reusable
|
|
35
|
+
organization group, or per org).
|
|
30
36
|
|
|
31
37
|
### Roles on a share grant
|
|
32
38
|
|
|
@@ -101,6 +107,7 @@ registerShareableResource({
|
|
|
101
107
|
// ...
|
|
102
108
|
allowPublic: false, // hides "Public" in the share dialog and rejects it server-side
|
|
103
109
|
requireOrgMemberForUserShares: true, // user shares must target an org member or pending invitee
|
|
110
|
+
supportsGroupShares: true, // enable reusable organization groups for this resource
|
|
104
111
|
});
|
|
105
112
|
```
|
|
106
113
|
|
|
@@ -111,6 +118,21 @@ Use both for resources that execute code or expose privileged data with the *vie
|
|
|
111
118
|
|
|
112
119
|
Defaults match historical behaviour: `allowPublic: true`, `requireOrgMemberForUserShares: false`. Resources that don't set the flags work as before.
|
|
113
120
|
|
|
121
|
+
### Reusable organization groups
|
|
122
|
+
|
|
123
|
+
The workspace organization model stores reusable groups in
|
|
124
|
+
`workspace_user_groups`. Organization owners/admins manage the groups and their
|
|
125
|
+
members through the existing group actions. Set `supportsGroupShares: true` on a
|
|
126
|
+
resource registration to enable `principalType: "group"` in the generic share
|
|
127
|
+
actions and the shared popover's autocomplete. Group ids are always checked
|
|
128
|
+
against the resource's organization, and membership is evaluated at access time
|
|
129
|
+
so group changes apply to existing shares.
|
|
130
|
+
|
|
131
|
+
Workspace apps opt into this flag and use the organization default visibility
|
|
132
|
+
setting. The app creator is the durable `owner_email`, while organization
|
|
133
|
+
owners/admins receive admin access through the registration's
|
|
134
|
+
`canManageAccess` callback.
|
|
135
|
+
|
|
114
136
|
## Filter list/read queries
|
|
115
137
|
|
|
116
138
|
```ts
|
|
@@ -197,6 +219,9 @@ All app share popovers should use the same compact surface contract:
|
|
|
197
219
|
- Add `ShareAgentsSection` only when the resource has a real agent-readable
|
|
198
220
|
link or prompt. Keep it collapsed by default and supply domain-specific
|
|
199
221
|
content through the shared section shell.
|
|
222
|
+
- When `supportsGroupShares` is enabled, keep the existing people input as the
|
|
223
|
+
only entry point and include organization groups in its autocomplete. Do not
|
|
224
|
+
add a separate group-management surface to each resource.
|
|
200
225
|
- App-specific tabs or controls may remain when they represent a real domain
|
|
201
226
|
action (for example, an embed-code editor), but they should retain the same
|
|
202
227
|
trigger, copy-row, access, and spacing language.
|
|
@@ -211,7 +236,7 @@ The framework auto-mounts these actions in every template — no per-template bo
|
|
|
211
236
|
|
|
212
237
|
| Action | Args | Purpose |
|
|
213
238
|
| -------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------- |
|
|
214
|
-
| `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
|
|
239
|
+
| `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user, group, or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
|
|
215
240
|
| `unshare-resource` | `resourceType, resourceId, principalType, principalId` | Revoke access. |
|
|
216
241
|
| `list-resource-shares` | `resourceType, resourceId` | Current visibility + all share grants. |
|
|
217
242
|
| `set-resource-visibility` | `resourceType, resourceId, visibility` | Change to `private` / `org` / `public`. |
|
|
@@ -43,6 +43,7 @@ export declare const appConfigSchema: z.ZodObject<{
|
|
|
43
43
|
isWorkspace: z.ZodOptional<z.ZodBoolean>;
|
|
44
44
|
appsJson: z.ZodOptional<z.ZodString>;
|
|
45
45
|
gatewayUrl: z.ZodOptional<z.ZodString>;
|
|
46
|
+
orgDirectoryUrl: z.ZodOptional<z.ZodString>;
|
|
46
47
|
oauthOrigin: z.ZodOptional<z.ZodString>;
|
|
47
48
|
}, z.core.$strip>>;
|
|
48
49
|
}, z.core.$strip>;
|
|
@@ -19,5 +19,6 @@ export declare const workspaceConfig: z.ZodObject<{
|
|
|
19
19
|
isWorkspace: z.ZodOptional<z.ZodBoolean>;
|
|
20
20
|
appsJson: z.ZodOptional<z.ZodString>;
|
|
21
21
|
gatewayUrl: z.ZodOptional<z.ZodString>;
|
|
22
|
+
orgDirectoryUrl: z.ZodOptional<z.ZodString>;
|
|
22
23
|
oauthOrigin: z.ZodOptional<z.ZodString>;
|
|
23
24
|
}, z.core.$strip>;
|
|
@@ -42,6 +42,14 @@ export const workspaceConfig = z.object({
|
|
|
42
42
|
env: ["WORKSPACE_GATEWAY_URL", "VITE_WORKSPACE_GATEWAY_URL"],
|
|
43
43
|
doc: "URL of the workspace gateway fronting this app.",
|
|
44
44
|
}),
|
|
45
|
+
orgDirectoryUrl: z
|
|
46
|
+
.string()
|
|
47
|
+
.min(1)
|
|
48
|
+
.optional()
|
|
49
|
+
.meta({
|
|
50
|
+
env: ["AGENT_NATIVE_ORG_DIRECTORY_URL"],
|
|
51
|
+
doc: "URL of the authoritative organization Dispatch directory.",
|
|
52
|
+
}),
|
|
45
53
|
oauthOrigin: z
|
|
46
54
|
.string()
|
|
47
55
|
.min(1)
|
|
@@ -403,14 +403,14 @@ export declare const chatThreadShares: import("drizzle-orm/sqlite-core").SQLiteT
|
|
|
403
403
|
tableName: string;
|
|
404
404
|
dataType: "string";
|
|
405
405
|
columnType: "SQLiteText";
|
|
406
|
-
data: "org" | "user";
|
|
406
|
+
data: "group" | "org" | "user";
|
|
407
407
|
driverParam: string;
|
|
408
408
|
notNull: true;
|
|
409
409
|
hasDefault: false;
|
|
410
410
|
isPrimaryKey: false;
|
|
411
411
|
isAutoincrement: false;
|
|
412
412
|
hasRuntimeDefault: false;
|
|
413
|
-
enumValues: ["user", "org"];
|
|
413
|
+
enumValues: ["user", "group", "org"];
|
|
414
414
|
baseColumn: never;
|
|
415
415
|
identity: undefined;
|
|
416
416
|
generated: undefined;
|
|
@@ -19,7 +19,7 @@ import { SettingsSkeleton } from "../settings/SettingsSkeleton.js";
|
|
|
19
19
|
import { useShareOrgMemberSearch } from "../sharing/share-controller-helpers.js";
|
|
20
20
|
import { useActionMutation, useActionQuery } from "../use-action.js";
|
|
21
21
|
import { cn } from "../utils.js";
|
|
22
|
-
import { useOrg, useOrgMembers, useOrgInvitations, useCreateOrg, useUpdateOrg, useBulkInviteMembers, useChangeMemberRole, useAcceptInvitation, useRemoveMember, useDeleteOrg, useSwitchOrg, useSetOrgDomain, useSetOrgWorkspaceUrl, useSetOrgAuthProvider, useRevealA2ASecret, useSetA2ASecret, useSyncA2ASecret, useJoinByDomain, useAppRoles, useSetAppMemberRole, ORG_MEMBER_PAGE_SIZE, } from "./hooks.js";
|
|
22
|
+
import { useOrg, useOrgMembers, useOrgInvitations, useCreateOrg, useUpdateOrg, useBulkInviteMembers, useChangeMemberRole, useAcceptInvitation, useRemoveMember, useDeleteOrg, useSwitchOrg, useSetOrgDomain, useSetWorkspaceAppDefaultVisibility, useSetOrgWorkspaceUrl, useSetOrgAuthProvider, useRevealA2ASecret, useSetA2ASecret, useSyncA2ASecret, useJoinByDomain, useAppRoles, useSetAppMemberRole, ORG_MEMBER_PAGE_SIZE, } from "./hooks.js";
|
|
23
23
|
const Button = forwardRef(({ className, ...props }, ref) => (_jsx(ToolkitButton, { ref: ref, variant: "ghost", className: cn("h-auto p-0 hover:bg-transparent hover:text-inherit active:scale-100 [&_svg]:!size-auto", className), ...props })));
|
|
24
24
|
Button.displayName = "TeamPrimitiveButton";
|
|
25
25
|
function RoleIcon({ role }) {
|
|
@@ -219,7 +219,20 @@ function MembersCard({ appRoles }) {
|
|
|
219
219
|
const totalMembers = membersData?.totalCount ?? 0;
|
|
220
220
|
const pendingInvites = invitationsData?.invitations ?? [];
|
|
221
221
|
const hasMultipleOrgs = (org.orgs?.length ?? 0) > 1;
|
|
222
|
-
return (_jsxs("div", { className: "space-y-6", children: [_jsxs(SettingsGroup, { title: "Organization", children: [_jsx(SettingsRow, { id: "organization", label: _jsxs("span", { className: "flex items-center gap-2", children: [_jsx(IconUsersGroup, { className: "size-4 text-muted-foreground" }), _jsx(OrgNameDisplay, { name: org.orgName ?? "", canEdit: isOwnerOrAdmin })] }), description: `${t("org.memberCount", { count: totalMembers })} · ${t("org.youAreRole", { role: org.role })}`, control: hasMultipleOrgs ? (_jsxs(Select, { value: org.orgId ?? "", onValueChange: (value) => switchOrg.mutate(value || null), disabled: switchOrg.isPending, children: [_jsx(SelectTrigger, { className: "h-auto w-full rounded-md border border-border bg-background px-2.5 py-1.5 text-xs sm:w-auto", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: org.orgs.map((o) => (_jsx(SelectItem, { value: o.orgId, children: o.orgName }, o.orgId))) })] })) : undefined }), isOwnerOrAdmin && (_jsxs(_Fragment, { children: [_jsx(DomainSettingsSection, { domain: org.allowedDomain, ownerEmail: org.email }), _jsx(WorkspaceUrlSettingsSection, { workspaceUrl: org.workspaceUrl }), _jsx(AuthProviderSettingsSection, { requiredAuthProvider: org.requiredAuthProvider }), isOwner && _jsx(A2ASecretSection, { isSet: Boolean(org.a2aSecretSet) })] })), switchOrg.error && (_jsx("div", { className: "px-5 pb-4", children: _jsx(ErrorText, { error: switchOrg.error }) }))] }), _jsx(MembersTableCard, { members: members, totalMembers: totalMembers, pendingInvites: pendingInvites, isLoadingMembers: isLoadingMembers, isFetchingMembers: isFetchingMembers, currentUserEmail: org.email, currentUserRole: org.role ?? null, appRoles: appRoles, groups: groupsQuery.data ?? [], canManageGroups: isOwnerOrAdmin, memberOffset: memberOffset, hasNextPage: membersData?.hasMore === true, nextMemberOffset: membersData?.nextOffset ?? null, onMemberPageChange: setMemberOffset }), isOwnerOrAdmin && (_jsx(WorkspaceGroupsCard, { groups: groupsQuery.data ?? [] })), isOwner && _jsx(DangerZoneCard, { orgName: org.orgName ?? "" })] }));
|
|
222
|
+
return (_jsxs("div", { className: "space-y-6", children: [_jsxs(SettingsGroup, { title: "Organization", children: [_jsx(SettingsRow, { id: "organization", label: _jsxs("span", { className: "flex items-center gap-2", children: [_jsx(IconUsersGroup, { className: "size-4 text-muted-foreground" }), _jsx(OrgNameDisplay, { name: org.orgName ?? "", canEdit: isOwnerOrAdmin })] }), description: `${t("org.memberCount", { count: totalMembers })} · ${t("org.youAreRole", { role: org.role })}`, control: hasMultipleOrgs ? (_jsxs(Select, { value: org.orgId ?? "", onValueChange: (value) => switchOrg.mutate(value || null), disabled: switchOrg.isPending, children: [_jsx(SelectTrigger, { className: "h-auto w-full rounded-md border border-border bg-background px-2.5 py-1.5 text-xs sm:w-auto", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: org.orgs.map((o) => (_jsx(SelectItem, { value: o.orgId, children: o.orgName }, o.orgId))) })] })) : undefined }), isOwnerOrAdmin && (_jsxs(_Fragment, { children: [_jsx(DomainSettingsSection, { domain: org.allowedDomain, ownerEmail: org.email }), _jsx(WorkspaceAppPrivacySettingsSection, { visibility: org.workspaceAppDefaultVisibility ?? "org" }), _jsx(WorkspaceUrlSettingsSection, { workspaceUrl: org.workspaceUrl }), _jsx(AuthProviderSettingsSection, { requiredAuthProvider: org.requiredAuthProvider }), isOwner && _jsx(A2ASecretSection, { isSet: Boolean(org.a2aSecretSet) })] })), switchOrg.error && (_jsx("div", { className: "px-5 pb-4", children: _jsx(ErrorText, { error: switchOrg.error }) }))] }), _jsx(MembersTableCard, { members: members, totalMembers: totalMembers, pendingInvites: pendingInvites, isLoadingMembers: isLoadingMembers, isFetchingMembers: isFetchingMembers, currentUserEmail: org.email, currentUserRole: org.role ?? null, appRoles: appRoles, groups: groupsQuery.data ?? [], canManageGroups: isOwnerOrAdmin, memberOffset: memberOffset, hasNextPage: membersData?.hasMore === true, nextMemberOffset: membersData?.nextOffset ?? null, onMemberPageChange: setMemberOffset }), isOwnerOrAdmin && (_jsx(WorkspaceGroupsCard, { groups: groupsQuery.data ?? [] })), isOwner && _jsx(DangerZoneCard, { orgName: org.orgName ?? "" })] }));
|
|
223
|
+
}
|
|
224
|
+
function WorkspaceAppPrivacySettingsSection({ visibility, }) {
|
|
225
|
+
const t = useT();
|
|
226
|
+
const setDefault = useSetWorkspaceAppDefaultVisibility();
|
|
227
|
+
return (_jsx(SettingsRow, { id: "workspace-app-default-visibility", label: t("org.workspaceAppsDefaultPrivacy", {
|
|
228
|
+
defaultValue: "New app privacy",
|
|
229
|
+
}), description: t("org.workspaceAppsDefaultPrivacyDescription", {
|
|
230
|
+
defaultValue: "Choose whether new workspace apps start private to their creator or visible to the organization.",
|
|
231
|
+
}), control: _jsxs(Select, { value: visibility, onValueChange: (value) => setDefault.mutate(value === "private" ? "private" : "org"), disabled: setDefault.isPending, children: [_jsx(SelectTrigger, { className: "h-auto w-full rounded-md border border-border bg-background px-2.5 py-1.5 text-xs sm:w-auto", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "org", children: t("org.workspaceAppsOrganization", {
|
|
232
|
+
defaultValue: "Organization",
|
|
233
|
+
}) }), _jsx(SelectItem, { value: "private", children: t("org.workspaceAppsCreatorOnly", {
|
|
234
|
+
defaultValue: "Creator only",
|
|
235
|
+
}) })] })] }) }));
|
|
223
236
|
}
|
|
224
237
|
function MembersTableCard({ members, totalMembers, pendingInvites, isLoadingMembers, isFetchingMembers, currentUserEmail, currentUserRole, appRoles, groups, canManageGroups, memberOffset, hasNextPage, nextMemberOffset, onMemberPageChange, }) {
|
|
225
238
|
const t = useT();
|
|
@@ -59,6 +59,8 @@ export declare function useSwitchOrg(): import("@tanstack/react-query").UseMutat
|
|
|
59
59
|
export declare function useDeleteOrg(): import("@tanstack/react-query").UseMutationResult<any, Error, string, unknown>;
|
|
60
60
|
export declare function useJoinByDomain(): import("@tanstack/react-query").UseMutationResult<any, Error, string, unknown>;
|
|
61
61
|
export declare function useSetOrgDomain(): import("@tanstack/react-query").UseMutationResult<any, Error, string | null, unknown>;
|
|
62
|
+
export type WorkspaceAppDefaultVisibility = "private" | "org";
|
|
63
|
+
export declare function useSetWorkspaceAppDefaultVisibility(): import("@tanstack/react-query").UseMutationResult<any, Error, WorkspaceAppDefaultVisibility, unknown>;
|
|
62
64
|
export declare function useSetOrgWorkspaceUrl(): import("@tanstack/react-query").UseMutationResult<any, Error, string | null, unknown>;
|
|
63
65
|
export declare function useSetOrgAuthProvider(): import("@tanstack/react-query").UseMutationResult<any, Error, "google" | null, unknown>;
|
|
64
66
|
export interface AppRoleAssignment {
|
package/dist/client/org/hooks.js
CHANGED
|
@@ -247,6 +247,18 @@ export function useSetOrgDomain() {
|
|
|
247
247
|
},
|
|
248
248
|
});
|
|
249
249
|
}
|
|
250
|
+
export function useSetWorkspaceAppDefaultVisibility() {
|
|
251
|
+
const qc = useQueryClient();
|
|
252
|
+
return useMutation({
|
|
253
|
+
mutationFn: (visibility) => apiFetch(`${ORG_BASE}/workspace-app-default-visibility`, {
|
|
254
|
+
method: "PUT",
|
|
255
|
+
body: JSON.stringify({ visibility }),
|
|
256
|
+
}),
|
|
257
|
+
onSuccess: async () => {
|
|
258
|
+
await qc.invalidateQueries({ queryKey: ["org-me"] });
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
}
|
|
250
262
|
export function useSetOrgWorkspaceUrl() {
|
|
251
263
|
const qc = useQueryClient();
|
|
252
264
|
return useMutation({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { useOrg, useOrgMembers, useOrgInvitations, useCreateOrg, useUpdateOrg, useInviteMember, useBulkInviteMembers, useChangeMemberRole, useAcceptInvitation, useRemoveMember, useDeleteOrg, useSwitchOrg, useJoinByDomain, useSetOrgDomain, useSetOrgWorkspaceUrl, useRevealA2ASecret, useSetA2ASecret, useSyncA2ASecret, useOrgRole, useAppRoles, useAppRole, useSetAppMemberRole, } from "./hooks.js";
|
|
2
|
-
export type { InviteRole, InviteVars, BulkInviteResult, SyncA2ASecretResult, UseOrgRoleResult, AppRoleAssignment, AppRolesInfo, } from "./hooks.js";
|
|
1
|
+
export { useOrg, useOrgMembers, useOrgInvitations, useCreateOrg, useUpdateOrg, useInviteMember, useBulkInviteMembers, useChangeMemberRole, useAcceptInvitation, useRemoveMember, useDeleteOrg, useSwitchOrg, useJoinByDomain, useSetOrgDomain, useSetWorkspaceAppDefaultVisibility, useSetOrgWorkspaceUrl, useRevealA2ASecret, useSetA2ASecret, useSyncA2ASecret, useOrgRole, useAppRoles, useAppRole, useSetAppMemberRole, } from "./hooks.js";
|
|
2
|
+
export type { InviteRole, InviteVars, BulkInviteResult, SyncA2ASecretResult, UseOrgRoleResult, AppRoleAssignment, AppRolesInfo, WorkspaceAppDefaultVisibility, } from "./hooks.js";
|
|
3
3
|
export type { AppRolesDescriptor } from "../../org/app-roles.js";
|
|
4
4
|
export { OrgSwitcher, type OrgSwitcherProps } from "./OrgSwitcher.js";
|
|
5
5
|
export { InvitationBanner, type InvitationBannerProps, } from "./InvitationBanner.js";
|
package/dist/client/org/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Public client API for the org module.
|
|
2
|
-
export { useOrg, useOrgMembers, useOrgInvitations, useCreateOrg, useUpdateOrg, useInviteMember, useBulkInviteMembers, useChangeMemberRole, useAcceptInvitation, useRemoveMember, useDeleteOrg, useSwitchOrg, useJoinByDomain, useSetOrgDomain, useSetOrgWorkspaceUrl, useRevealA2ASecret, useSetA2ASecret, useSyncA2ASecret, useOrgRole, useAppRoles, useAppRole, useSetAppMemberRole, } from "./hooks.js";
|
|
2
|
+
export { useOrg, useOrgMembers, useOrgInvitations, useCreateOrg, useUpdateOrg, useInviteMember, useBulkInviteMembers, useChangeMemberRole, useAcceptInvitation, useRemoveMember, useDeleteOrg, useSwitchOrg, useJoinByDomain, useSetOrgDomain, useSetWorkspaceAppDefaultVisibility, useSetOrgWorkspaceUrl, useRevealA2ASecret, useSetA2ASecret, useSyncA2ASecret, useOrgRole, useAppRoles, useAppRole, useSetAppMemberRole, } from "./hooks.js";
|
|
3
3
|
export { OrgSwitcher } from "./OrgSwitcher.js";
|
|
4
4
|
export { InvitationBanner, } from "./InvitationBanner.js";
|
|
5
5
|
export { WorkspaceNotice } from "./WorkspaceNotice.js";
|
|
@@ -131,7 +131,7 @@ export function ShareButton(props) {
|
|
|
131
131
|
function SharePanel(props) {
|
|
132
132
|
const t = useT();
|
|
133
133
|
const { controller } = props;
|
|
134
|
-
const { inviteEmail, setInviteEmail: onInviteEmailChange, activeShareTab, handleShareTabChange, data, policy, visibility, canManage, role, setRole, notifyPeople, setNotifyPeople, shareMessage, setShareMessage, messageOpen, setMessageOpen, shareError, setShareError, suggestionsOpen, setSuggestionsOpen, inFlight, memberSearch, memberSuggestions, knownMembers, shares, handleVisibility, handleHideInSearch, handleAdd, handleChangeRole, handleRemove, } = controller;
|
|
134
|
+
const { inviteEmail, setInviteEmail: onInviteEmailChange, activeShareTab, handleShareTabChange, data, policy, visibility, canManage, role, setRole, notifyPeople, setNotifyPeople, shareMessage, setShareMessage, messageOpen, setMessageOpen, shareError, setShareError, suggestionsOpen, setSuggestionsOpen, inFlight, memberSearch, memberSuggestions, groupSuggestions, selectGroup, knownMembers, shares, handleVisibility, handleHideInSearch, handleAdd, handleChangeRole, handleRemove, } = controller;
|
|
135
135
|
const hasInviteEmail = inviteEmail.trim().length > 0;
|
|
136
136
|
const isLoading = data === undefined;
|
|
137
137
|
const meta = visibilityMeta(visibility, t, props.visibilityCopy);
|
|
@@ -155,8 +155,13 @@ function SharePanel(props) {
|
|
|
155
155
|
onInviteEmailChange(next);
|
|
156
156
|
if (shareError)
|
|
157
157
|
setShareError(null);
|
|
158
|
-
},
|
|
159
|
-
|
|
158
|
+
}, onSelectSuggestion: (suggestion) => {
|
|
159
|
+
if (suggestion.principalType === "group") {
|
|
160
|
+
selectGroup(suggestion);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
onInviteEmailChange(suggestion.email);
|
|
164
|
+
}
|
|
160
165
|
setSuggestionsOpen(false);
|
|
161
166
|
if (shareError)
|
|
162
167
|
setShareError(null);
|
|
@@ -165,8 +170,21 @@ function SharePanel(props) {
|
|
|
165
170
|
defaultValue: "Add people from your organization",
|
|
166
171
|
})
|
|
167
172
|
: t("agentChat.share.addPeopleEmail", {
|
|
168
|
-
defaultValue:
|
|
169
|
-
|
|
173
|
+
defaultValue: policy.supportsGroupShares
|
|
174
|
+
? "Add people or groups"
|
|
175
|
+
: "Add people by email",
|
|
176
|
+
}), suggestions: [
|
|
177
|
+
...memberSuggestions.map((member) => ({
|
|
178
|
+
...member,
|
|
179
|
+
principalType: "user",
|
|
180
|
+
})),
|
|
181
|
+
...(policy.supportsGroupShares
|
|
182
|
+
? groupSuggestions.map((group) => ({
|
|
183
|
+
...group,
|
|
184
|
+
principalType: "group",
|
|
185
|
+
}))
|
|
186
|
+
: []),
|
|
187
|
+
], search: memberSearch }), _jsx(RoleSelect, { value: role, onChange: setRole, roleCopy: props.roleCopy, allowedRoles: props.allowedRoles }), _jsx("button", { type: "button", onClick: handleAdd, disabled: !hasInviteEmail, className: BUTTON_PRIMARY_SM, children: t("agentChat.share.add", { defaultValue: "Add" }) })] }), shareError ? (_jsx("div", { role: "alert", className: "rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: shareError })) : null, hasInviteEmail ? (_jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-2 text-xs text-muted-foreground", children: [_jsxs("label", { className: "inline-flex items-center gap-2", children: [_jsx("input", { type: "checkbox", checked: notifyPeople, onChange: (e) => setNotifyPeople(e.target.checked), className: "h-4 w-4 rounded border-input accent-primary" }), t("agentChat.share.notifyPeople", {
|
|
170
188
|
defaultValue: "Notify people",
|
|
171
189
|
})] }), notifyPeople ? (_jsx("button", { type: "button", "aria-expanded": messageOpen, onClick: () => setMessageOpen(!messageOpen), className: "rounded-sm px-1 py-0.5 font-medium text-foreground underline decoration-border underline-offset-2 transition-colors hover:bg-accent hover:text-accent-foreground", children: messageOpen
|
|
172
190
|
? t("agentChat.share.hideMessage", {
|
|
@@ -178,7 +196,7 @@ function SharePanel(props) {
|
|
|
178
196
|
defaultValue: "Message",
|
|
179
197
|
}), placeholder: t("agentChat.share.messagePlaceholder", {
|
|
180
198
|
defaultValue: "Add a short note (optional)",
|
|
181
|
-
}), value: shareMessage, onChange: (event) => setShareMessage(event.target.value), maxLength: 500, rows: 3, className: "w-full resize-y rounded-md border border-input bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background" }) })) : null] })) : null, _jsxs("ul", { className: "flex list-none flex-col gap-1 p-0 m-0", children: [data?.ownerEmail ? (_jsxs("li", { className: "flex items-center gap-3 px-1 py-1.5 text-sm", children: [_jsx(Avatar, { label: displayName(data.ownerEmail, knownMembers, t) }), _jsx("span", { className: "flex-1 min-w-0 truncate", children: displayName(data.ownerEmail, knownMembers, t) }), _jsx("span", { className: "text-xs text-muted-foreground", children: t("agentChat.share.owner", { defaultValue: "Owner" }) })] })) : null, shares.map((s) => (_jsxs("li", { className: cn("flex items-center gap-3 px-1 py-1.5 text-sm", inFlight.has(keyOf(s)) && "opacity-60"), children: [_jsx(Avatar, { label: principalLabel(s, knownMembers, t), org: s.principalType === "org" }), _jsx("span", { className: "flex-1 min-w-0 truncate", children: principalLabel(s, knownMembers, t) }), canManage ? (_jsx(RoleSelect, { value: s.role, onChange: (r) => handleChangeRole(s, r), disabled: inFlight.has(keyOf(s)), plain: true, roleCopy: props.roleCopy, allowedRoles: props.allowedRoles })) : (_jsx("span", { className: "text-xs text-muted-foreground", children: roleMeta(s.role, t, props.roleCopy).label })), canManage ? (_jsx("button", { type: "button", "aria-label": t("agentChat.share.remove", {
|
|
199
|
+
}), value: shareMessage, onChange: (event) => setShareMessage(event.target.value), maxLength: 500, rows: 3, className: "w-full resize-y rounded-md border border-input bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background" }) })) : null] })) : null, _jsxs("ul", { className: "flex list-none flex-col gap-1 p-0 m-0", children: [data?.ownerEmail ? (_jsxs("li", { className: "flex items-center gap-3 px-1 py-1.5 text-sm", children: [_jsx(Avatar, { label: displayName(data.ownerEmail, knownMembers, t) }), _jsx("span", { className: "flex-1 min-w-0 truncate", children: displayName(data.ownerEmail, knownMembers, t) }), _jsx("span", { className: "text-xs text-muted-foreground", children: t("agentChat.share.owner", { defaultValue: "Owner" }) })] })) : null, shares.map((s) => (_jsxs("li", { className: cn("flex items-center gap-3 px-1 py-1.5 text-sm", inFlight.has(keyOf(s)) && "opacity-60"), children: [_jsx(Avatar, { label: principalLabel(s, knownMembers, t), org: s.principalType === "org", group: s.principalType === "group" }), _jsx("span", { className: "flex-1 min-w-0 truncate", children: principalLabel(s, knownMembers, t) }), canManage ? (_jsx(RoleSelect, { value: s.role, onChange: (r) => handleChangeRole(s, r), disabled: inFlight.has(keyOf(s)), plain: true, roleCopy: props.roleCopy, allowedRoles: props.allowedRoles })) : (_jsx("span", { className: "text-xs text-muted-foreground", children: roleMeta(s.role, t, props.roleCopy).label })), canManage ? (_jsx("button", { type: "button", "aria-label": t("agentChat.share.remove", {
|
|
182
200
|
defaultValue: "Remove",
|
|
183
201
|
}), onClick: () => handleRemove(s), disabled: inFlight.has(keyOf(s)), className: BUTTON_GHOST_ICON, children: _jsx(IconTrash, { size: 14 }) })) : null] }, keyOf(s)))), !shares.length && !data?.ownerEmail ? (_jsx("li", { className: "px-1 py-1.5 text-sm text-muted-foreground", children: t("agentChat.share.noAccess", {
|
|
184
202
|
defaultValue: "No one has access yet.",
|
|
@@ -219,13 +237,13 @@ function AdvancedAccessPopover({ control, canManage, onToggle, }) {
|
|
|
219
237
|
defaultValue: "People with the link can still open this.",
|
|
220
238
|
}) })] })] })] }) })] }));
|
|
221
239
|
}
|
|
222
|
-
function MemberAutocomplete({ value, open, onOpenChange, onValueChange,
|
|
240
|
+
function MemberAutocomplete({ value, open, onOpenChange, onValueChange, onSelectSuggestion, onSubmit, placeholder, suggestions, search, }) {
|
|
223
241
|
const t = useT();
|
|
224
242
|
const rawListboxId = useId();
|
|
225
243
|
const listboxId = rawListboxId.replace(/:/g, "");
|
|
226
244
|
const inputRef = useRef(null);
|
|
227
245
|
const [activeIndex, setActiveIndex] = useState(-1);
|
|
228
|
-
const
|
|
246
|
+
const activeSuggestion = activeIndex >= 0 && activeIndex < suggestions.length
|
|
229
247
|
? suggestions[activeIndex]
|
|
230
248
|
: null;
|
|
231
249
|
useEffect(() => {
|
|
@@ -243,8 +261,8 @@ function MemberAutocomplete({ value, open, onOpenChange, onValueChange, onSelect
|
|
|
243
261
|
.getElementById(optionId(listboxId, activeIndex))
|
|
244
262
|
?.scrollIntoView({ block: "nearest" });
|
|
245
263
|
}, [activeIndex, listboxId]);
|
|
246
|
-
const
|
|
247
|
-
|
|
264
|
+
const chooseSuggestion = (suggestion) => {
|
|
265
|
+
onSelectSuggestion(suggestion);
|
|
248
266
|
onOpenChange(false);
|
|
249
267
|
inputRef.current?.focus();
|
|
250
268
|
};
|
|
@@ -273,9 +291,9 @@ function MemberAutocomplete({ value, open, onOpenChange, onValueChange, onSelect
|
|
|
273
291
|
return;
|
|
274
292
|
}
|
|
275
293
|
if (event.key === "Enter") {
|
|
276
|
-
if (open &&
|
|
294
|
+
if (open && activeSuggestion) {
|
|
277
295
|
event.preventDefault();
|
|
278
|
-
|
|
296
|
+
chooseSuggestion(activeSuggestion);
|
|
279
297
|
return;
|
|
280
298
|
}
|
|
281
299
|
if (value.trim()) {
|
|
@@ -299,7 +317,7 @@ function MemberAutocomplete({ value, open, onOpenChange, onValueChange, onSelect
|
|
|
299
317
|
search.loadMore();
|
|
300
318
|
}
|
|
301
319
|
};
|
|
302
|
-
return (_jsxs(Popover, { open: open, onOpenChange: onOpenChange, children: [_jsx(PopoverAnchor, { asChild: true, children: _jsxs("div", { className: "relative flex-1 min-w-0", children: [_jsx(IconSearch, { "aria-hidden": true, size: 15, strokeWidth: 1.8, className: "pointer-events-none absolute start-2.5 top-1/2 -translate-y-1/2 text-muted-foreground" }), _jsx("input", { ref: inputRef, type: "
|
|
320
|
+
return (_jsxs(Popover, { open: open, onOpenChange: onOpenChange, children: [_jsx(PopoverAnchor, { asChild: true, children: _jsxs("div", { className: "relative flex-1 min-w-0", children: [_jsx(IconSearch, { "aria-hidden": true, size: 15, strokeWidth: 1.8, className: "pointer-events-none absolute start-2.5 top-1/2 -translate-y-1/2 text-muted-foreground" }), _jsx("input", { ref: inputRef, type: "text", role: "combobox", "aria-autocomplete": "list", "aria-expanded": open, "aria-controls": open ? listboxId : undefined, "aria-activedescendant": activeIndex >= 0 ? optionId(listboxId, activeIndex) : undefined, placeholder: placeholder, value: value, onChange: (event) => {
|
|
303
321
|
onValueChange(event.target.value);
|
|
304
322
|
onOpenChange(true);
|
|
305
323
|
}, onFocus: () => onOpenChange(true), onBlur: () => {
|
|
@@ -308,11 +326,15 @@ function MemberAutocomplete({ value, open, onOpenChange, onValueChange, onSelect
|
|
|
308
326
|
onOpenChange(false);
|
|
309
327
|
}
|
|
310
328
|
}, 0);
|
|
311
|
-
}, onKeyDown: handleKeyDown, autoComplete: "off", className: "h-9 w-full min-w-0 rounded-md border border-input bg-card ps-8 pe-8 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background" }), search.isLoading ? (_jsx(IconLoader2, { "aria-hidden": true, size: 15, strokeWidth: 1.8, className: "pointer-events-none absolute end-2.5 top-1/2 -translate-y-1/2 animate-spin text-muted-foreground" })) : null] }) }), _jsx(PopoverContent, { align: "start", sideOffset: 4, "data-agent-native-share-overlay": "", onOpenAutoFocus: (event) => event.preventDefault(), className: cn(SHARE_NESTED_OVERLAY_Z, "w-[var(--radix-popper-anchor-width)] min-w-[18rem] rounded-md p-1 shadow-lg", SHARE_POPOVER_SURFACE), children: _jsxs("div", { id: listboxId, role: "listbox", className: "max-h-56 overflow-y-auto overflow-x-hidden", onScroll: handleScroll, children: [suggestions.map((
|
|
329
|
+
}, onKeyDown: handleKeyDown, autoComplete: "off", className: "h-9 w-full min-w-0 rounded-md border border-input bg-card ps-8 pe-8 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background" }), search.isLoading ? (_jsx(IconLoader2, { "aria-hidden": true, size: 15, strokeWidth: 1.8, className: "pointer-events-none absolute end-2.5 top-1/2 -translate-y-1/2 animate-spin text-muted-foreground" })) : null] }) }), _jsx(PopoverContent, { align: "start", sideOffset: 4, "data-agent-native-share-overlay": "", onOpenAutoFocus: (event) => event.preventDefault(), className: cn(SHARE_NESTED_OVERLAY_Z, "w-[var(--radix-popper-anchor-width)] min-w-[18rem] rounded-md p-1 shadow-lg", SHARE_POPOVER_SURFACE), children: _jsxs("div", { id: listboxId, role: "listbox", className: "max-h-56 overflow-y-auto overflow-x-hidden", onScroll: handleScroll, children: [suggestions.map((suggestion, index) => {
|
|
312
330
|
const active = index === activeIndex;
|
|
313
|
-
return (_jsxs("div", { id: optionId(listboxId, index), role: "option", "aria-selected": active, onMouseDown: (event) => event.preventDefault(), onMouseEnter: () => setActiveIndex(index), onClick: () =>
|
|
331
|
+
return (_jsxs("div", { id: optionId(listboxId, index), role: "option", "aria-selected": active, onMouseDown: (event) => event.preventDefault(), onMouseEnter: () => setActiveIndex(index), onClick: () => chooseSuggestion(suggestion), className: cn("flex cursor-pointer select-none flex-col rounded-sm px-3 py-2 text-sm outline-none", active
|
|
314
332
|
? "bg-accent text-accent-foreground"
|
|
315
|
-
: "text-foreground hover:bg-accent hover:text-accent-foreground"), children: [
|
|
333
|
+
: "text-foreground hover:bg-accent hover:text-accent-foreground"), children: [_jsxs("span", { className: "flex items-center gap-2 truncate font-medium", children: [suggestion.principalType === "group" ? (_jsx(IconUsersGroup, { "aria-hidden": true, size: 14, strokeWidth: 1.8, className: "shrink-0 text-muted-foreground" })) : null, _jsx("span", { className: "truncate", children: suggestion.principalType === "group"
|
|
334
|
+
? suggestion.name
|
|
335
|
+
: suggestion.name?.trim() || suggestion.email })] }), suggestion.principalType === "group" ? (_jsx("span", { className: "truncate text-xs text-muted-foreground", children: t("agentChat.share.userGroup", {
|
|
336
|
+
defaultValue: "User group",
|
|
337
|
+
}) })) : suggestion.name?.trim() ? (_jsx("span", { className: "truncate text-xs text-muted-foreground", children: suggestion.email })) : null] }, `${suggestion.principalType}:${suggestion.principalType === "group" ? suggestion.id : suggestion.email}`));
|
|
316
338
|
}), search.isLoading && suggestions.length === 0 ? (_jsx("div", { className: "px-3 py-3 text-sm text-muted-foreground", children: t("agentChat.share.searching", {
|
|
317
339
|
defaultValue: "Searching...",
|
|
318
340
|
}) })) : null, search.error ? (_jsx("div", { className: "px-3 py-3 text-sm text-muted-foreground", children: t("agentChat.share.loadPeopleFailed", {
|
|
@@ -375,8 +397,8 @@ function VisibilitySelect(props) {
|
|
|
375
397
|
.description,
|
|
376
398
|
})) }) }) }) })] }));
|
|
377
399
|
}
|
|
378
|
-
function Avatar({ label, org }) {
|
|
379
|
-
return (_jsx("span", { "aria-hidden": true, className: "inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-muted text-[11px] font-semibold text-muted-foreground", children: org ? _jsx(IconUsersGroup, { size: 14, strokeWidth: 1.75 }) : initials(label) }));
|
|
400
|
+
function Avatar({ label, org, group, }) {
|
|
401
|
+
return (_jsx("span", { "aria-hidden": true, className: "inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-muted text-[11px] font-semibold text-muted-foreground", children: org || group ? (_jsx(IconUsersGroup, { size: 14, strokeWidth: 1.75 })) : (initials(label)) }));
|
|
380
402
|
}
|
|
381
403
|
function keyOf(s) {
|
|
382
404
|
return `${s.principalType}:${s.principalId}`;
|
|
@@ -393,6 +415,9 @@ function principalLabel(share, members, t) {
|
|
|
393
415
|
return t("agentChat.share.organization", {
|
|
394
416
|
defaultValue: "Organization",
|
|
395
417
|
});
|
|
418
|
+
if (share.principalType === "group")
|
|
419
|
+
return (serverLabel ||
|
|
420
|
+
t("agentChat.share.userGroup", { defaultValue: "User group" }));
|
|
396
421
|
return displayName(share.principalId, members, t);
|
|
397
422
|
}
|
|
398
423
|
function displayName(emailOrId, members, t) {
|
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useActionQuery } from "../use-action.js";
|
|
2
2
|
export type ShareButtonVisibility = "private" | "org" | "public";
|
|
3
3
|
export type ShareButtonRole = "viewer" | "commenter" | "editor" | "admin";
|
|
4
4
|
export interface ShareButtonShare {
|
|
5
5
|
id: string;
|
|
6
|
-
principalType: "user" | "org";
|
|
6
|
+
principalType: "user" | "group" | "org";
|
|
7
7
|
principalId: string;
|
|
8
8
|
displayName?: string | null;
|
|
9
9
|
role: ShareButtonRole;
|
|
10
10
|
}
|
|
11
|
+
export interface ShareButtonGroup {
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
memberEmails?: string[];
|
|
15
|
+
}
|
|
11
16
|
export interface ShareButtonSharesResponse {
|
|
12
17
|
ownerEmail: string | null;
|
|
13
18
|
orgId: string | null;
|
|
@@ -18,6 +23,7 @@ export interface ShareButtonSharesResponse {
|
|
|
18
23
|
policy?: {
|
|
19
24
|
allowPublic: boolean;
|
|
20
25
|
requireOrgMemberForUserShares: boolean;
|
|
26
|
+
supportsGroupShares?: boolean;
|
|
21
27
|
};
|
|
22
28
|
}
|
|
23
29
|
export interface ShareButtonOrgMember {
|
|
@@ -65,6 +71,7 @@ export interface ShareButtonController {
|
|
|
65
71
|
policy: {
|
|
66
72
|
allowPublic: boolean;
|
|
67
73
|
requireOrgMemberForUserShares: boolean;
|
|
74
|
+
supportsGroupShares?: boolean;
|
|
68
75
|
};
|
|
69
76
|
visibility: ShareButtonVisibility;
|
|
70
77
|
triggerVisibility: ShareButtonVisibility | null;
|
|
@@ -84,7 +91,10 @@ export interface ShareButtonController {
|
|
|
84
91
|
inFlight: Set<string>;
|
|
85
92
|
memberSearch: ShareButtonOrgMemberSearch;
|
|
86
93
|
memberSuggestions: ShareButtonOrgMember[];
|
|
94
|
+
groupSuggestions: ShareButtonGroup[];
|
|
87
95
|
knownMembers: ShareButtonOrgMember[];
|
|
96
|
+
selectedGroup: ShareButtonGroup | null;
|
|
97
|
+
selectGroup: (group: ShareButtonGroup) => void;
|
|
88
98
|
shares: ShareButtonShare[];
|
|
89
99
|
handleVisibility: (visibility: ShareButtonVisibility) => void;
|
|
90
100
|
handleHideInSearch: () => void;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { useActionQuery } from "../use-action.js";
|
|
2
3
|
import { DEFAULT_MEMBER_SEARCH_DEBOUNCE_MS, DEFAULT_MEMBER_SUGGESTION_LIMIT, extractShareErrorMessage, optimisticallyUpdateShareCache, rollbackShareCache, useShareOrgMemberSearch, useShareMutationGuard, useShareMutations, useShareQuery, } from "./share-controller-helpers.js";
|
|
3
4
|
export function useShareButtonController(options) {
|
|
4
5
|
const [open, setOpen] = useState(false);
|
|
5
6
|
const [inviteEmail, setInviteEmail] = useState("");
|
|
7
|
+
const [selectedGroup, setSelectedGroup] = useState(null);
|
|
6
8
|
const shareTabDefaultValue = options.shareTabs?.defaultValue ?? "share";
|
|
7
9
|
const [activeShareTab, setActiveShareTab] = useState(shareTabDefaultValue);
|
|
8
10
|
const [visibilityOverride, setVisibilityOverride] = useState(null);
|
|
@@ -25,6 +27,7 @@ export function useShareButtonController(options) {
|
|
|
25
27
|
}, [options, shareTabDefaultValue, sharesQuery, visibilityOverride]);
|
|
26
28
|
useEffect(() => {
|
|
27
29
|
setInviteEmail("");
|
|
30
|
+
setSelectedGroup(null);
|
|
28
31
|
setShareMessage("");
|
|
29
32
|
setMessageOpen(false);
|
|
30
33
|
}, [options.resourceId, options.resourceType]);
|
|
@@ -141,6 +144,10 @@ export function useShareButtonController(options) {
|
|
|
141
144
|
limit: DEFAULT_MEMBER_SUGGESTION_LIMIT,
|
|
142
145
|
debounceMs: DEFAULT_MEMBER_SEARCH_DEBOUNCE_MS,
|
|
143
146
|
});
|
|
147
|
+
const groupsQuery = useActionQuery("list-workspace-user-groups", {}, {
|
|
148
|
+
enabled: canManage && policy.supportsGroupShares === true && suggestionsOpen,
|
|
149
|
+
staleTime: 30_000,
|
|
150
|
+
});
|
|
144
151
|
const serverShares = data?.shares ?? [];
|
|
145
152
|
const shares = [
|
|
146
153
|
...serverShares
|
|
@@ -160,7 +167,21 @@ export function useShareButtonController(options) {
|
|
|
160
167
|
}
|
|
161
168
|
}
|
|
162
169
|
const memberSuggestions = memberSearch.members.filter((member) => !excludedMemberEmails.has(member.email.toLowerCase()));
|
|
170
|
+
const excludedGroupIds = new Set(shares
|
|
171
|
+
.filter((share) => share.principalType === "group")
|
|
172
|
+
.map((share) => share.principalId));
|
|
173
|
+
const groupSuggestions = (Array.isArray(groupsQuery.data) ? groupsQuery.data : []).filter((group) => !excludedGroupIds.has(group.id) &&
|
|
174
|
+
(!inviteEmail.trim() ||
|
|
175
|
+
group.name.toLowerCase().includes(inviteEmail.trim().toLowerCase())));
|
|
163
176
|
const knownMembers = memberSearch.members;
|
|
177
|
+
const setInviteEmailValue = useCallback((email) => {
|
|
178
|
+
setSelectedGroup(null);
|
|
179
|
+
setInviteEmail(email);
|
|
180
|
+
}, []);
|
|
181
|
+
const selectGroup = useCallback((group) => {
|
|
182
|
+
setSelectedGroup(group);
|
|
183
|
+
setInviteEmail(group.name);
|
|
184
|
+
}, []);
|
|
164
185
|
const handleVisibility = useCallback((next) => {
|
|
165
186
|
if (next === visibility)
|
|
166
187
|
return;
|
|
@@ -189,11 +210,14 @@ export function useShareButtonController(options) {
|
|
|
189
210
|
const trimmed = inviteEmail.trim();
|
|
190
211
|
if (!trimmed || !canManage)
|
|
191
212
|
return;
|
|
192
|
-
const
|
|
213
|
+
const principalType = selectedGroup ? "group" : "user";
|
|
214
|
+
const principalId = selectedGroup?.id ?? trimmed;
|
|
215
|
+
const message = principalType === "user" && notifyPeople ? shareMessage.trim() : "";
|
|
193
216
|
const optimistic = {
|
|
194
|
-
id: `pending-${
|
|
195
|
-
principalType
|
|
196
|
-
principalId
|
|
217
|
+
id: `pending-${principalType}-${principalId}`,
|
|
218
|
+
principalType,
|
|
219
|
+
principalId,
|
|
220
|
+
...(selectedGroup ? { displayName: selectedGroup.name } : {}),
|
|
197
221
|
role,
|
|
198
222
|
};
|
|
199
223
|
const key = keyOf(optimistic);
|
|
@@ -202,6 +226,7 @@ export function useShareButtonController(options) {
|
|
|
202
226
|
setShareError(null);
|
|
203
227
|
setPendingAdds((previous) => [...previous, optimistic]);
|
|
204
228
|
setInviteEmail("");
|
|
229
|
+
setSelectedGroup(null);
|
|
205
230
|
setShareMessage("");
|
|
206
231
|
setMessageOpen(false);
|
|
207
232
|
setSuggestionsOpen(false);
|
|
@@ -210,10 +235,10 @@ export function useShareButtonController(options) {
|
|
|
210
235
|
share.mutate({
|
|
211
236
|
resourceType: options.resourceType,
|
|
212
237
|
resourceId: options.resourceId,
|
|
213
|
-
principalType
|
|
214
|
-
principalId
|
|
238
|
+
principalType,
|
|
239
|
+
principalId,
|
|
215
240
|
role,
|
|
216
|
-
notify: notifyPeople,
|
|
241
|
+
notify: principalType === "user" && notifyPeople,
|
|
217
242
|
resourceUrl: getNotificationUrl(options.shareUrl),
|
|
218
243
|
...(message ? { message } : {}),
|
|
219
244
|
}, {
|
|
@@ -228,6 +253,7 @@ export function useShareButtonController(options) {
|
|
|
228
253
|
setPendingAdds((previous) => previous.filter((item) => item.id !== optimistic.id));
|
|
229
254
|
clearInFlight(key);
|
|
230
255
|
setInviteEmail(trimmed);
|
|
256
|
+
setSelectedGroup(selectedGroup);
|
|
231
257
|
setShareMessage((current) => current || message);
|
|
232
258
|
setMessageOpen((current) => current || Boolean(message));
|
|
233
259
|
setShareError(extractShareErrorMessage(error));
|
|
@@ -244,6 +270,7 @@ export function useShareButtonController(options) {
|
|
|
244
270
|
options.resourceType,
|
|
245
271
|
options.shareUrl,
|
|
246
272
|
role,
|
|
273
|
+
selectedGroup,
|
|
247
274
|
shareMessage,
|
|
248
275
|
share,
|
|
249
276
|
queryClient,
|
|
@@ -368,7 +395,7 @@ export function useShareButtonController(options) {
|
|
|
368
395
|
activeShareTab,
|
|
369
396
|
handleShareTabChange,
|
|
370
397
|
inviteEmail,
|
|
371
|
-
setInviteEmail,
|
|
398
|
+
setInviteEmail: setInviteEmailValue,
|
|
372
399
|
sharesQuery,
|
|
373
400
|
visibilityOverride,
|
|
374
401
|
handleVisibilityChange,
|
|
@@ -399,6 +426,9 @@ export function useShareButtonController(options) {
|
|
|
399
426
|
handleAdd,
|
|
400
427
|
handleChangeRole,
|
|
401
428
|
handleRemove,
|
|
429
|
+
groupSuggestions,
|
|
430
|
+
selectedGroup,
|
|
431
|
+
selectGroup,
|
|
402
432
|
};
|
|
403
433
|
}
|
|
404
434
|
function keyOf(share) {
|
|
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
|
|
|
62
62
|
error: string;
|
|
63
63
|
states?: undefined;
|
|
64
64
|
} | {
|
|
65
|
-
error?: undefined;
|
|
66
65
|
states: {
|
|
67
66
|
clientId: number;
|
|
68
67
|
state: string;
|
|
69
68
|
}[];
|
|
69
|
+
error?: undefined;
|
|
70
70
|
}>>;
|
|
71
71
|
/**
|
|
72
72
|
* GET /_agent-native/collab/:docId/users
|
|
@@ -77,9 +77,9 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
|
|
|
77
77
|
error: string;
|
|
78
78
|
users?: undefined;
|
|
79
79
|
} | {
|
|
80
|
-
error?: undefined;
|
|
81
80
|
users: {
|
|
82
81
|
clientId: number;
|
|
83
82
|
lastSeen: number;
|
|
84
83
|
}[];
|
|
84
|
+
error?: undefined;
|
|
85
85
|
}>>;
|
|
@@ -464,14 +464,14 @@ export declare function createDashboardStorageSchema(options?: DashboardStorageS
|
|
|
464
464
|
tableName: string;
|
|
465
465
|
dataType: "string";
|
|
466
466
|
columnType: "SQLiteText";
|
|
467
|
-
data: "org" | "user";
|
|
467
|
+
data: "group" | "org" | "user";
|
|
468
468
|
driverParam: string;
|
|
469
469
|
notNull: true;
|
|
470
470
|
hasDefault: false;
|
|
471
471
|
isPrimaryKey: false;
|
|
472
472
|
isAutoincrement: false;
|
|
473
473
|
hasRuntimeDefault: false;
|
|
474
|
-
enumValues: ["user", "org"];
|
|
474
|
+
enumValues: ["user", "group", "org"];
|
|
475
475
|
baseColumn: never;
|
|
476
476
|
identity: undefined;
|
|
477
477
|
generated: undefined;
|
|
@@ -411,14 +411,14 @@ export declare const dataProgramShares: import("drizzle-orm/sqlite-core").SQLite
|
|
|
411
411
|
tableName: string;
|
|
412
412
|
dataType: "string";
|
|
413
413
|
columnType: "SQLiteText";
|
|
414
|
-
data: "org" | "user";
|
|
414
|
+
data: "group" | "org" | "user";
|
|
415
415
|
driverParam: string;
|
|
416
416
|
notNull: true;
|
|
417
417
|
hasDefault: false;
|
|
418
418
|
isPrimaryKey: false;
|
|
419
419
|
isAutoincrement: false;
|
|
420
420
|
hasRuntimeDefault: false;
|
|
421
|
-
enumValues: ["user", "org"];
|
|
421
|
+
enumValues: ["user", "group", "org"];
|
|
422
422
|
baseColumn: never;
|
|
423
423
|
identity: undefined;
|
|
424
424
|
generated: undefined;
|
|
@@ -316,14 +316,14 @@ export declare const extensionShares: import("drizzle-orm/sqlite-core").SQLiteTa
|
|
|
316
316
|
tableName: string;
|
|
317
317
|
dataType: "string";
|
|
318
318
|
columnType: "SQLiteText";
|
|
319
|
-
data: "org" | "user";
|
|
319
|
+
data: "group" | "org" | "user";
|
|
320
320
|
driverParam: string;
|
|
321
321
|
notNull: true;
|
|
322
322
|
hasDefault: false;
|
|
323
323
|
isPrimaryKey: false;
|
|
324
324
|
isAutoincrement: false;
|
|
325
325
|
hasRuntimeDefault: false;
|
|
326
|
-
enumValues: ["user", "org"];
|
|
326
|
+
enumValues: ["user", "group", "org"];
|
|
327
327
|
baseColumn: never;
|
|
328
328
|
identity: undefined;
|
|
329
329
|
generated: undefined;
|