@agent-native/core 0.152.0 → 0.153.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/adding-a-feature/SKILL.md +208 -0
- package/corpus/templates/analytics/.agents/skills/context-awareness/SKILL.md +206 -0
- package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +6 -4
- package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +73 -80
- package/corpus/templates/analytics/.agents/skills/portability/SKILL.md +107 -0
- package/corpus/templates/analytics/.agents/skills/secrets/SKILL.md +339 -0
- package/corpus/templates/analytics/.agents/skills/self-modifying-code/SKILL.md +3 -3
- package/corpus/templates/analytics/.agents/skills/shadcn-ui/SKILL.md +1 -1
- package/corpus/templates/analytics/.agents/skills/sharing/SKILL.md +1 -0
- package/corpus/templates/analytics/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/corpus/templates/analytics/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/corpus/templates/analytics/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/corpus/templates/analytics/.agents/skills/workspace-conventions/SKILL.md +93 -0
- package/corpus/templates/analytics/actions/data-source-status.ts +4 -1
- package/corpus/templates/analytics/actions/migrate-first-party-analytics-to-bigquery.ts +2 -2
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +7 -2
- package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +7 -1
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +30 -32
- package/corpus/templates/analytics/app/components/layout/layout-route-policy.ts +10 -0
- package/corpus/templates/analytics/app/global.css +5 -1
- package/corpus/templates/analytics/app/lib/data-source-status.ts +3 -3
- package/corpus/templates/analytics/app/lib/resource-access.ts +6 -1
- package/corpus/templates/analytics/app/pages/DataSources.tsx +79 -51
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +1 -0
- package/corpus/templates/analytics/app/pages/analyses/AnalysisDetail.tsx +1 -0
- package/corpus/templates/analytics/changelog/2026-08-12-analytics-sidebar-ask-toggle-and-compact-rail.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-12-data-source-statuses-now-distinguish-workspace-access-from-c.md +6 -0
- package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +559 -147
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +79 -5
- package/corpus/templates/analytics/server/plugins/db.ts +84 -0
- package/corpus/templates/assets/.agents/skills/adding-a-feature/SKILL.md +208 -0
- package/corpus/templates/assets/.agents/skills/context-awareness/SKILL.md +206 -0
- package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +73 -80
- package/corpus/templates/assets/.agents/skills/portability/SKILL.md +107 -0
- package/corpus/templates/assets/.agents/skills/secrets/SKILL.md +339 -0
- package/corpus/templates/assets/.agents/skills/self-modifying-code/SKILL.md +3 -3
- package/corpus/templates/assets/.agents/skills/shadcn-ui/SKILL.md +1 -1
- package/corpus/templates/assets/.agents/skills/sharing/SKILL.md +1 -0
- package/corpus/templates/assets/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/corpus/templates/assets/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/corpus/templates/assets/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/corpus/templates/assets/.agents/skills/workspace-conventions/SKILL.md +93 -0
- package/corpus/templates/assets/app/components/asset/AssetPreviewDialog.tsx +13 -5
- package/corpus/templates/assets/app/lib/asset-urls.ts +13 -0
- package/corpus/templates/assets/app/routes/asset.$id.tsx +16 -6
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +1 -0
- package/corpus/templates/assets/app/routes/library.tsx +336 -78
- package/corpus/templates/assets/shared/api.ts +1 -1
- package/corpus/templates/brain/.agents/skills/adding-a-feature/SKILL.md +3 -2
- package/corpus/templates/brain/.agents/skills/brain/SKILL.md +8 -6
- package/corpus/templates/brain/.agents/skills/context-awareness/SKILL.md +206 -0
- package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +73 -80
- package/corpus/templates/brain/.agents/skills/ingestion-and-connectors/SKILL.md +7 -0
- package/corpus/templates/brain/.agents/skills/portability/SKILL.md +107 -0
- package/corpus/templates/brain/.agents/skills/secrets/SKILL.md +339 -0
- package/corpus/templates/brain/.agents/skills/self-modifying-code/SKILL.md +3 -3
- package/corpus/templates/brain/.agents/skills/shadcn-ui/SKILL.md +1 -1
- package/corpus/templates/brain/.agents/skills/sharing/SKILL.md +1 -0
- package/corpus/templates/brain/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/corpus/templates/brain/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/corpus/templates/brain/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/corpus/templates/brain/.agents/skills/workspace-conventions/SKILL.md +93 -0
- package/corpus/templates/brain/AGENTS.md +2 -1
- package/corpus/templates/brain/actions/ask-brain.ts +19 -16
- package/corpus/templates/brain/actions/create-source.ts +11 -2
- package/corpus/templates/brain/actions/import-markdown-files.ts +237 -0
- package/corpus/templates/brain/actions/resanitize-captures.ts +3 -1
- package/corpus/templates/brain/actions/test-slack-connection.ts +12 -3
- package/corpus/templates/brain/actions/update-source.ts +10 -1
- package/corpus/templates/brain/app/components/brain/Surface.tsx +1 -11
- package/corpus/templates/brain/app/i18n-data.ts +111 -0
- package/corpus/templates/brain/app/lib/brain.ts +23 -1
- package/corpus/templates/brain/app/routes/knowledge.tsx +0 -2
- package/corpus/templates/brain/app/routes/ops.tsx +0 -2
- package/corpus/templates/brain/app/routes/review.tsx +0 -2
- package/corpus/templates/brain/app/routes/search.tsx +0 -2
- package/corpus/templates/brain/app/routes/sources.tsx +354 -6
- package/corpus/templates/brain/server/lib/brain-response-guard.ts +63 -1
- package/corpus/templates/brain/server/lib/brain.ts +9 -6
- package/corpus/templates/brain/server/lib/connectors.ts +20 -6
- package/corpus/templates/brain/server/lib/search.ts +9 -1
- package/corpus/templates/brain/server/lib/source-credentials.ts +95 -57
- package/corpus/templates/brain/server/plugins/agent-chat.ts +5 -2
- package/corpus/templates/brain/server/plugins/auth.ts +2 -0
- package/corpus/templates/calendar/.agents/skills/adding-a-feature/SKILL.md +208 -0
- package/corpus/templates/calendar/.agents/skills/context-awareness/SKILL.md +206 -0
- package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +73 -80
- package/corpus/templates/calendar/.agents/skills/portability/SKILL.md +107 -0
- package/corpus/templates/calendar/.agents/skills/secrets/SKILL.md +339 -0
- package/corpus/templates/calendar/.agents/skills/self-modifying-code/SKILL.md +3 -3
- package/corpus/templates/calendar/.agents/skills/shadcn-ui/SKILL.md +1 -1
- package/corpus/templates/calendar/.agents/skills/sharing/SKILL.md +1 -0
- package/corpus/templates/calendar/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/corpus/templates/calendar/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/corpus/templates/calendar/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/corpus/templates/calendar/.agents/skills/workspace-conventions/SKILL.md +93 -0
- package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +1 -0
- package/corpus/templates/chat/.agents/skills/adding-a-feature/SKILL.md +3 -2
- package/corpus/templates/chat/.agents/skills/context-awareness/SKILL.md +206 -0
- package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +73 -80
- package/corpus/templates/chat/.agents/skills/portability/SKILL.md +107 -0
- package/corpus/templates/chat/.agents/skills/secrets/SKILL.md +339 -0
- package/corpus/templates/chat/.agents/skills/self-modifying-code/SKILL.md +3 -3
- package/corpus/templates/chat/.agents/skills/shadcn-ui/SKILL.md +1 -1
- package/corpus/templates/chat/.agents/skills/sharing/SKILL.md +1 -0
- package/corpus/templates/chat/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/corpus/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/corpus/templates/chat/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/corpus/templates/chat/.agents/skills/workspace-conventions/SKILL.md +93 -0
- package/corpus/templates/chat/AGENTS.md +4 -5
- package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +73 -80
- package/corpus/templates/clips/.agents/skills/meetings/SKILL.md +1 -1
- package/corpus/templates/clips/.agents/skills/secrets/SKILL.md +339 -0
- package/corpus/templates/clips/.agents/skills/self-modifying-code/SKILL.md +3 -3
- package/corpus/templates/clips/.agents/skills/shadcn-ui/SKILL.md +1 -1
- package/corpus/templates/clips/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/corpus/templates/clips/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/corpus/templates/clips/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/corpus/templates/clips/.agents/skills/workspace-conventions/SKILL.md +93 -0
- package/corpus/templates/clips/actions/add-comment.ts +1 -2
- package/corpus/templates/clips/actions/delete-comment.ts +2 -0
- package/corpus/templates/clips/actions/get-recording-insights.ts +9 -8
- package/corpus/templates/clips/actions/get-recording-player-data.ts +4 -1
- package/corpus/templates/clips/actions/react-to-comment.ts +1 -1
- package/corpus/templates/clips/actions/react-to-recording.ts +1 -1
- package/corpus/templates/clips/actions/reply-to-comment.ts +1 -1
- package/corpus/templates/clips/actions/resolve-comment.ts +1 -2
- package/corpus/templates/clips/actions/update-comment.ts +1 -1
- package/corpus/templates/clips/app/components/editor/editor-layout.tsx +1 -0
- package/corpus/templates/clips/app/components/editor/thumbnail-picker.tsx +59 -16
- package/corpus/templates/clips/app/components/player/comments-panel.tsx +88 -39
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +12 -8
- package/corpus/templates/clips/app/components/player/timestamped-comment-button.tsx +3 -1
- package/corpus/templates/clips/app/components/player/video-player.tsx +1 -1
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +2 -1
- package/corpus/templates/clips/app/i18n/en-US.ts +2 -1
- package/corpus/templates/clips/app/lib/public-meeting.ts +1 -1
- package/corpus/templates/clips/app/lib/thumbnail-capture.ts +71 -0
- package/corpus/templates/clips/app/lib/thumbnail-picker-state.ts +93 -0
- package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +1 -1
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +25 -19
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +8 -0
- package/corpus/templates/clips/changelog/2026-07-23-meeting-recording-meters-and-dismissed-prompts.md +1 -1
- package/corpus/templates/clips/changelog/2026-08-11-clips-asks-to-take-notes-again-when-you-join-an-adhoc-zoom-o.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-12-viewers-are-read-only-and-commenters-can-comment.md +6 -0
- package/corpus/templates/clips/desktop/src-tauri/src/adhoc_meetings_watcher.rs +166 -14
- package/corpus/templates/clips/desktop/src-tauri/src/call_activity.rs +146 -0
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +1 -0
- package/corpus/templates/clips/desktop/src-tauri/src/silence_detector.rs +2 -122
- package/corpus/templates/clips/server/lib/recording-page-access.ts +6 -1
- package/corpus/templates/clips/server/routes/api/public-recording.get.ts +5 -0
- package/corpus/templates/clips/shared/share-meta.ts +19 -3
- package/corpus/templates/content/.agents/skills/adding-a-feature/SKILL.md +208 -0
- package/corpus/templates/content/.agents/skills/context-awareness/SKILL.md +206 -0
- package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +73 -80
- package/corpus/templates/content/.agents/skills/portability/SKILL.md +107 -0
- package/corpus/templates/content/.agents/skills/secrets/SKILL.md +339 -0
- package/corpus/templates/content/.agents/skills/self-modifying-code/SKILL.md +3 -3
- package/corpus/templates/content/.agents/skills/shadcn-ui/SKILL.md +1 -1
- package/corpus/templates/content/.agents/skills/sharing/SKILL.md +1 -0
- package/corpus/templates/content/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/corpus/templates/content/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/corpus/templates/content/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/corpus/templates/content/.agents/skills/workspace-conventions/SKILL.md +93 -0
- package/corpus/templates/content/actions/_database-utils.ts +10 -0
- package/corpus/templates/content/actions/add-comment.ts +1 -1
- package/corpus/templates/content/actions/attach-content-database-source.ts +158 -61
- package/corpus/templates/content/actions/create-document.ts +1 -0
- package/corpus/templates/content/actions/delete-comment.ts +1 -1
- package/corpus/templates/content/actions/get-document.ts +9 -1
- package/corpus/templates/content/actions/import-content-source.ts +8 -0
- package/corpus/templates/content/actions/list-documents.ts +10 -1
- package/corpus/templates/content/actions/update-comment.ts +1 -1
- package/corpus/templates/content/actions/update-document.ts +11 -0
- package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +196 -30
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +52 -29
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +13 -8
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +62 -2
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +163 -53
- package/corpus/templates/content/app/components/editor/database/TimelineView.tsx +16 -0
- package/corpus/templates/content/app/components/editor/database/settings.tsx +22 -13
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +7 -7
- package/corpus/templates/content/app/hooks/use-documents.ts +2 -0
- package/corpus/templates/content/app/i18n-data.ts +10 -0
- package/corpus/templates/content/app/root.tsx +1 -21
- package/corpus/templates/content/changelog/2026-08-03-choose-text-or-a-heading-from-one-compact-formatting-control.md +6 -0
- package/corpus/templates/content/changelog/2026-08-05-connect-sources-from-add-column.md +6 -0
- package/corpus/templates/content/changelog/2026-08-12-viewers-are-read-only-and-commenters-can-comment.md +6 -0
- package/corpus/templates/content/docs/solutions/evidence/content-text-selector/README.md +26 -0
- package/corpus/templates/content/shared/api.ts +7 -1
- package/corpus/templates/crm/.agents/skills/actions/SKILL.md +146 -0
- package/corpus/templates/crm/.agents/skills/actions/references/action-fields.md +136 -0
- package/corpus/templates/crm/.agents/skills/actions/references/examples.md +94 -0
- package/corpus/templates/crm/.agents/skills/actions/references/provider-apis.md +95 -0
- package/corpus/templates/crm/.agents/skills/adding-a-feature/SKILL.md +208 -0
- package/corpus/templates/crm/.agents/skills/context-awareness/SKILL.md +206 -0
- package/corpus/templates/crm/.agents/skills/delegate-to-agent/SKILL.md +289 -0
- package/corpus/templates/crm/.agents/skills/frontend-design/SKILL.md +296 -0
- package/corpus/templates/crm/.agents/skills/frontend-design/references/visual-direction.md +99 -0
- package/corpus/templates/crm/.agents/skills/portability/SKILL.md +107 -0
- package/corpus/templates/crm/.agents/skills/real-time-sync/SKILL.md +232 -0
- package/corpus/templates/crm/.agents/skills/secrets/SKILL.md +339 -0
- package/corpus/templates/crm/.agents/skills/security/SKILL.md +324 -0
- package/corpus/templates/crm/.agents/skills/self-modifying-code/SKILL.md +123 -0
- package/corpus/templates/crm/.agents/skills/shadcn-ui/SKILL.md +123 -0
- package/corpus/templates/crm/.agents/skills/sharing/SKILL.md +1 -0
- package/corpus/templates/crm/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/corpus/templates/crm/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/corpus/templates/crm/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/corpus/templates/crm/.agents/skills/workspace-conventions/SKILL.md +93 -0
- package/corpus/templates/design/.agents/skills/adding-a-feature/SKILL.md +208 -0
- package/corpus/templates/design/.agents/skills/context-awareness/SKILL.md +206 -0
- package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +73 -80
- package/corpus/templates/design/.agents/skills/portability/SKILL.md +107 -0
- package/corpus/templates/design/.agents/skills/real-time-sync/SKILL.md +232 -0
- package/corpus/templates/design/.agents/skills/secrets/SKILL.md +339 -0
- package/corpus/templates/design/.agents/skills/self-modifying-code/SKILL.md +3 -3
- package/corpus/templates/design/.agents/skills/shadcn-ui/SKILL.md +1 -1
- package/corpus/templates/design/.agents/skills/sharing/SKILL.md +1 -0
- package/corpus/templates/design/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/corpus/templates/design/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/corpus/templates/design/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/corpus/templates/design/.agents/skills/workspace-conventions/SKILL.md +93 -0
- package/corpus/templates/design/actions/index-design-system-with-builder.ts +16 -2
- package/corpus/templates/design/app/components/design-system/ProductionDesignSystemShowcase.tsx +6 -18
- package/corpus/templates/design/app/i18n/en-US.ts +1 -1
- package/corpus/templates/design/app/i18n-data.ts +40 -0
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +153 -131
- package/corpus/templates/design/app/pages/DesignSystems.tsx +7 -61
- package/corpus/templates/design/app/pages/Present.tsx +8 -2
- package/corpus/templates/design/app/pages/Templates.tsx +1 -0
- package/corpus/templates/design/app/pages/design-editor/tool-state.ts +4 -1
- package/corpus/templates/design/app/pages/design-editor/types.ts +6 -1
- package/corpus/templates/design/changelog/2026-08-12-viewers-are-read-only-and-commenters-can-comment.md +6 -0
- package/corpus/templates/dispatch/.agents/skills/adding-a-feature/SKILL.md +208 -0
- package/corpus/templates/dispatch/.agents/skills/context-awareness/SKILL.md +206 -0
- package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +73 -80
- package/corpus/templates/dispatch/.agents/skills/portability/SKILL.md +107 -0
- package/corpus/templates/dispatch/.agents/skills/real-time-sync/SKILL.md +232 -0
- package/corpus/templates/dispatch/.agents/skills/secrets/SKILL.md +339 -0
- package/corpus/templates/dispatch/.agents/skills/self-modifying-code/SKILL.md +3 -3
- package/corpus/templates/dispatch/.agents/skills/shadcn-ui/SKILL.md +1 -1
- package/corpus/templates/dispatch/.agents/skills/sharing/SKILL.md +1 -0
- package/corpus/templates/dispatch/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/corpus/templates/dispatch/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/corpus/templates/dispatch/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/corpus/templates/dispatch/.agents/skills/workspace-conventions/SKILL.md +93 -0
- package/corpus/templates/dispatch/actions/apply-workspace-connection-setup.ts +97 -15
- package/corpus/templates/dispatch/actions/connection-permissions.ts +77 -0
- package/corpus/templates/dispatch/actions/delete-workspace-connection.ts +12 -2
- package/corpus/templates/dispatch/actions/list-workspace-user-groups.ts +19 -0
- package/corpus/templates/dispatch/actions/plan-workspace-connection-setup.ts +9 -2
- package/corpus/templates/dispatch/actions/set-workspace-connection-grant.ts +21 -2
- package/corpus/templates/dispatch/actions/upsert-workspace-connection.ts +66 -1
- package/corpus/templates/dispatch/actions/upsert-workspace-user-group.ts +27 -0
- package/corpus/templates/dispatch/app/chat-first-pages.ts +84 -0
- package/corpus/templates/dispatch/app/i18n-data.ts +3989 -3527
- package/corpus/templates/dispatch/app/routes/integrations.tsx +1568 -685
- package/corpus/templates/dispatch/app/routes/settings.tsx +14 -9
- package/corpus/templates/dispatch/changelog/2026-08-12-search-and-pin-workspace-apps-from-the-app-catalog.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-12-workspace-integration-access-now-shows-exactly-which-apps-ca.md +6 -0
- package/corpus/templates/dispatch/server/lib/app-roles.ts +5 -0
- package/corpus/templates/dispatch/shared/app-roles.ts +1 -0
- package/corpus/templates/factory/.agents/skills/adding-a-feature/SKILL.md +3 -2
- package/corpus/templates/factory/.agents/skills/context-awareness/SKILL.md +206 -0
- package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +73 -80
- package/corpus/templates/factory/.agents/skills/portability/SKILL.md +107 -0
- package/corpus/templates/factory/.agents/skills/secrets/SKILL.md +339 -0
- package/corpus/templates/factory/.agents/skills/self-modifying-code/SKILL.md +3 -3
- package/corpus/templates/factory/.agents/skills/shadcn-ui/SKILL.md +1 -1
- package/corpus/templates/factory/.agents/skills/sharing/SKILL.md +1 -0
- package/corpus/templates/factory/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/corpus/templates/factory/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/corpus/templates/factory/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/corpus/templates/factory/.agents/skills/workspace-conventions/SKILL.md +93 -0
- package/corpus/templates/factory/AGENTS.md +10 -1
- package/corpus/templates/factory/README.md +18 -7
- package/corpus/templates/factory/actions/list-triage-items.ts +10 -4
- package/corpus/templates/factory/actions/poll-github-sources.ts +53 -14
- package/corpus/templates/factory/actions/poll-sentry-errors.ts +31 -13
- package/corpus/templates/factory/actions/poll-slack-channel.ts +41 -5
- package/corpus/templates/factory/actions/provider-api-catalog.ts +13 -0
- package/corpus/templates/factory/actions/provider-api-docs.ts +11 -0
- package/corpus/templates/factory/actions/provider-api-request.ts +17 -0
- package/corpus/templates/factory/actions/save-triage-config.ts +87 -38
- package/corpus/templates/factory/app/components/factory/FactoryAuditView.tsx +14 -12
- package/corpus/templates/factory/app/components/factory/FactoryCanvas.tsx +60 -77
- package/corpus/templates/factory/app/components/factory/FactoryInspector.tsx +66 -125
- package/corpus/templates/factory/app/components/layout/Header.tsx +1 -0
- package/corpus/templates/factory/app/components/layout/Layout.tsx +8 -3
- package/corpus/templates/factory/app/components/layout/Sidebar.tsx +9 -5
- package/corpus/templates/factory/app/components/ui/input.tsx +15 -1
- package/corpus/templates/factory/app/components/ui/popover.tsx +1 -0
- package/corpus/templates/factory/app/components/ui/switch.tsx +1 -0
- package/corpus/templates/factory/app/components/ui/textarea.tsx +20 -1
- package/corpus/templates/factory/app/hooks/use-navigation-state.ts +5 -3
- package/corpus/templates/factory/app/i18n/en-US.ts +17 -0
- package/corpus/templates/factory/app/routes/_index.tsx +5 -87
- package/corpus/templates/factory/app/routes/chat.$threadId.tsx +1 -1
- package/corpus/templates/factory/app/routes/chat.tsx +91 -0
- package/corpus/templates/factory/app/routes/factory.tsx +554 -427
- package/corpus/templates/factory/app/routes/settings.tsx +1 -1
- package/corpus/templates/factory/server/connectors/credentials.ts +25 -0
- package/corpus/templates/factory/server/lib/provider-api.ts +34 -0
- package/corpus/templates/factory/server/plugins/agent-chat.ts +15 -3
- package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +96 -24
- package/corpus/templates/factory/server/triage/review-state.ts +33 -0
- package/corpus/templates/factory/server/triage/slack-poller.ts +1 -0
- package/corpus/templates/forms/.agents/skills/adding-a-feature/SKILL.md +208 -0
- package/corpus/templates/forms/.agents/skills/context-awareness/SKILL.md +206 -0
- package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +73 -80
- package/corpus/templates/forms/.agents/skills/portability/SKILL.md +107 -0
- package/corpus/templates/forms/.agents/skills/secrets/SKILL.md +339 -0
- package/corpus/templates/forms/.agents/skills/self-modifying-code/SKILL.md +3 -3
- package/corpus/templates/forms/.agents/skills/shadcn-ui/SKILL.md +1 -1
- package/corpus/templates/forms/.agents/skills/sharing/SKILL.md +1 -0
- package/corpus/templates/forms/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/corpus/templates/forms/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/corpus/templates/forms/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/corpus/templates/forms/.agents/skills/workspace-conventions/SKILL.md +93 -0
- package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +1 -0
- package/corpus/templates/forms/shared/types.ts +1 -1
- package/corpus/templates/macros/.agents/skills/adding-a-feature/SKILL.md +208 -0
- package/corpus/templates/macros/.agents/skills/context-awareness/SKILL.md +206 -0
- package/corpus/templates/macros/.agents/skills/delegate-to-agent/SKILL.md +289 -0
- package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +73 -80
- package/corpus/templates/macros/.agents/skills/portability/SKILL.md +107 -0
- package/corpus/templates/macros/.agents/skills/real-time-sync/SKILL.md +232 -0
- package/corpus/templates/macros/.agents/skills/secrets/SKILL.md +339 -0
- package/corpus/templates/macros/.agents/skills/self-modifying-code/SKILL.md +123 -0
- package/corpus/templates/macros/.agents/skills/shadcn-ui/SKILL.md +1 -1
- package/corpus/templates/macros/.agents/skills/sharing/SKILL.md +1 -0
- package/corpus/templates/macros/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/corpus/templates/macros/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/corpus/templates/macros/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/corpus/templates/macros/.agents/skills/workspace-conventions/SKILL.md +93 -0
- package/corpus/templates/mail/.agents/skills/adding-a-feature/SKILL.md +208 -0
- package/corpus/templates/mail/.agents/skills/context-awareness/SKILL.md +206 -0
- package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +73 -80
- package/corpus/templates/mail/.agents/skills/portability/SKILL.md +107 -0
- package/corpus/templates/mail/.agents/skills/secrets/SKILL.md +339 -0
- package/corpus/templates/mail/.agents/skills/self-modifying-code/SKILL.md +3 -3
- package/corpus/templates/mail/.agents/skills/shadcn-ui/SKILL.md +1 -1
- package/corpus/templates/mail/.agents/skills/sharing/SKILL.md +1 -0
- package/corpus/templates/mail/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/corpus/templates/mail/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/corpus/templates/mail/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/corpus/templates/mail/.agents/skills/workspace-conventions/SKILL.md +93 -0
- package/corpus/templates/mail/actions/send-email.ts +9 -0
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +4 -4
- package/corpus/templates/mail/changelog/2026-08-12-agent-sent-messages-appear-immediately.md +6 -0
- package/corpus/templates/plan/.agents/skills/adding-a-feature/SKILL.md +3 -2
- package/corpus/templates/plan/.agents/skills/context-awareness/SKILL.md +206 -0
- package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +73 -80
- package/corpus/templates/plan/.agents/skills/portability/SKILL.md +107 -0
- package/corpus/templates/plan/.agents/skills/secrets/SKILL.md +339 -0
- package/corpus/templates/plan/.agents/skills/self-modifying-code/SKILL.md +3 -3
- package/corpus/templates/plan/.agents/skills/shadcn-ui/SKILL.md +1 -1
- package/corpus/templates/plan/.agents/skills/sharing/SKILL.md +1 -0
- package/corpus/templates/plan/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/corpus/templates/plan/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/corpus/templates/plan/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/corpus/templates/plan/.agents/skills/workspace-conventions/SKILL.md +93 -0
- package/corpus/templates/plan/actions/delete-plan-comment.ts +6 -0
- package/corpus/templates/plan/actions/reply-to-plan-comment.ts +7 -1
- package/corpus/templates/plan/actions/resolve-plan-comment.ts +7 -1
- package/corpus/templates/plan/actions/update-visual-plan.ts +6 -0
- package/corpus/templates/plan/app/hooks/use-plans.ts +1 -1
- package/corpus/templates/plan/app/pages/PlansPage.tsx +51 -23
- package/corpus/templates/plan/changelog/2026-08-12-viewers-are-read-only-and-commenters-can-comment.md +6 -0
- package/corpus/templates/plan/server/plans.ts +2 -2
- package/corpus/templates/plan/shared/types.ts +1 -1
- package/corpus/templates/slides/.agents/skills/adding-a-feature/SKILL.md +208 -0
- package/corpus/templates/slides/.agents/skills/context-awareness/SKILL.md +206 -0
- package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +6 -0
- package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +73 -80
- package/corpus/templates/slides/.agents/skills/portability/SKILL.md +107 -0
- package/corpus/templates/slides/.agents/skills/real-time-sync/SKILL.md +232 -0
- package/corpus/templates/slides/.agents/skills/secrets/SKILL.md +339 -0
- package/corpus/templates/slides/.agents/skills/self-modifying-code/SKILL.md +3 -3
- package/corpus/templates/slides/.agents/skills/shadcn-ui/SKILL.md +1 -1
- package/corpus/templates/slides/.agents/skills/sharing/SKILL.md +1 -0
- package/corpus/templates/slides/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/corpus/templates/slides/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/corpus/templates/slides/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/corpus/templates/slides/.agents/skills/workspace-conventions/SKILL.md +93 -0
- package/corpus/templates/slides/AGENTS.md +4 -0
- package/corpus/templates/slides/actions/add-slide-comment.ts +1 -1
- package/corpus/templates/slides/actions/delete-slide-comment.ts +1 -1
- package/corpus/templates/slides/actions/get-layout-overflows.ts +67 -2
- package/corpus/templates/slides/actions/update-slide-comment.ts +1 -1
- package/corpus/templates/slides/actions/view-screen.ts +76 -26
- package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +30 -8
- package/corpus/templates/slides/app/components/design-system/DesignSystemCard.tsx +2 -1
- package/corpus/templates/slides/app/components/editor/AlignmentGuides.tsx +75 -0
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +80 -80
- package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +125 -10
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +244 -9
- package/corpus/templates/slides/app/components/editor/SlideOverflowWarning.tsx +1 -1
- package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +3 -3
- package/corpus/templates/slides/app/components/editor/slide-object-interactions.ts +218 -0
- package/corpus/templates/slides/app/context/DeckContext.tsx +56 -30
- package/corpus/templates/slides/app/hooks/use-deck-role.ts +13 -5
- package/corpus/templates/slides/app/hooks/use-design-systems.ts +1 -1
- package/corpus/templates/slides/app/i18n/en-US.ts +6 -2
- package/corpus/templates/slides/app/lib/create-deck-generation.ts +1 -0
- package/corpus/templates/slides/app/lib/pending-deck-changes.ts +32 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +85 -4
- package/corpus/templates/slides/app/pages/Index.tsx +65 -66
- package/corpus/templates/slides/changelog/2026-08-12-viewers-are-read-only-and-commenters-can-comment.md +6 -0
- package/corpus/templates/tasks/.agents/skills/adding-a-feature/SKILL.md +3 -2
- package/corpus/templates/tasks/.agents/skills/context-awareness/SKILL.md +206 -0
- package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +73 -80
- package/corpus/templates/tasks/.agents/skills/portability/SKILL.md +107 -0
- package/corpus/templates/tasks/.agents/skills/secrets/SKILL.md +339 -0
- package/corpus/templates/tasks/.agents/skills/self-modifying-code/SKILL.md +3 -3
- package/corpus/templates/tasks/.agents/skills/shadcn-ui/SKILL.md +1 -1
- package/corpus/templates/tasks/.agents/skills/sharing/SKILL.md +1 -0
- package/corpus/templates/tasks/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/corpus/templates/tasks/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/corpus/templates/tasks/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/corpus/templates/tasks/.agents/skills/workspace-conventions/SKILL.md +93 -0
- package/corpus/templates/tasks/app/components/custom-fields/FieldsPage.tsx +1 -4
- package/corpus/templates/tasks/app/components/inbox/InboxList.tsx +0 -1
- package/corpus/templates/tasks/app/components/inbox/InboxListPage.tsx +0 -1
- package/corpus/templates/tasks/app/components/shared/ListViewHeader.tsx +0 -3
- package/corpus/templates/tasks/app/components/shared/PageHeader.tsx +1 -4
- package/corpus/templates/tasks/app/components/tasks/TaskList.tsx +0 -1
- package/corpus/templates/tasks/app/components/tasks/TaskListPage.tsx +0 -1
- package/dist/agent/engine/ai-sdk-engine.js +5 -0
- package/dist/agent/engine/anthropic-engine.js +8 -0
- package/dist/agent/engine/registry.js +24 -19
- package/dist/agent/engine/translate-anthropic.js +16 -1
- package/dist/agent/production-agent.d.ts +8 -0
- package/dist/agent/production-agent.js +66 -0
- package/dist/automations/service.d.ts +2 -0
- package/dist/automations/service.js +5 -1
- package/dist/chat-threads/schema.d.ts +2 -2
- package/dist/cli/changelog.js +21 -4
- package/dist/cli/create.d.ts +12 -1
- package/dist/cli/create.js +99 -11
- package/dist/cli/index.js +6 -6
- package/dist/cli/process.d.ts +7 -0
- package/dist/cli/process.js +12 -0
- package/dist/cli/workspace-skill-policy.d.ts +15 -0
- package/dist/cli/workspace-skill-policy.js +62 -0
- package/dist/cli/workspacify.js +36 -0
- package/dist/client/AgentPanel.d.ts +14 -2
- package/dist/client/AgentPanel.js +40 -24
- package/dist/client/AssistantChat.js +93 -183
- package/dist/client/FeedbackButton.js +15 -2
- package/dist/client/RouteTransitionIndicator.d.ts +14 -2
- package/dist/client/RouteTransitionIndicator.js +16 -5
- package/dist/client/agent-page/AgentTabsPage.js +19 -6
- package/dist/client/app-config.d.ts +3 -0
- package/dist/client/app-config.js +6 -0
- package/dist/client/app-providers.js +2 -1
- package/dist/client/changelog/Changelog.d.ts +2 -1
- package/dist/client/changelog/Changelog.js +7 -5
- package/dist/client/chat/markdown-renderer.d.ts +3 -1
- package/dist/client/chat/markdown-renderer.js +24 -24
- package/dist/client/chat/message-components.d.ts +23 -0
- package/dist/client/chat/message-components.js +27 -44
- package/dist/client/chat/tool-call-display.d.ts +2 -2
- package/dist/client/chat/tool-call-display.js +6 -3
- package/dist/client/chat-first/app-open-actions.d.ts +28 -0
- package/dist/client/chat-first/app-open-actions.js +25 -0
- package/dist/client/chat-first/index.d.ts +1 -0
- package/dist/client/chat-first/index.js +1 -0
- package/dist/client/chat-first/primary-nav.d.ts +2 -1
- package/dist/client/chat-first/primary-nav.js +12 -5
- package/dist/client/conversation/use-near-bottom-autoscroll.d.ts +14 -0
- package/dist/client/conversation/use-near-bottom-autoscroll.js +14 -0
- package/dist/client/error-format.js +32 -3
- package/dist/client/extensions/EmbeddedExtension.js +1 -0
- package/dist/client/extensions/ExtensionViewer.js +3 -1
- package/dist/client/extensions/InlineExtensionFrame.js +1 -0
- package/dist/client/extensions/iframe-bridge.d.ts +3 -2
- package/dist/client/extensions/iframe-bridge.js +4 -2
- package/dist/client/guided-questions.d.ts +15 -1
- package/dist/client/guided-questions.js +33 -15
- package/dist/client/i18n.js +28 -5
- package/dist/client/integrations/IntegrationConnectionChoice.d.ts +11 -0
- package/dist/client/integrations/IntegrationConnectionChoice.js +6 -0
- package/dist/client/integrations/IntegrationGrid.d.ts +23 -0
- package/dist/client/integrations/IntegrationGrid.js +12 -0
- package/dist/client/integrations/IntegrationsPanel.js +92 -26
- package/dist/client/integrations/index.d.ts +2 -0
- package/dist/client/integrations/index.js +2 -0
- package/dist/client/onboarding/FirstRunOnboarding.d.ts +3 -1
- package/dist/client/onboarding/FirstRunOnboarding.js +26 -16
- package/dist/client/onboarding/first-run-enabled.js +1 -5
- package/dist/client/onboarding/first-run-startup-gate.d.ts +5 -0
- package/dist/client/onboarding/first-run-startup-gate.js +51 -0
- package/dist/client/onboarding/first-run-status.d.ts +7 -0
- package/dist/client/onboarding/first-run-status.js +26 -0
- package/dist/client/onboarding/index.d.ts +3 -0
- package/dist/client/onboarding/index.js +3 -0
- package/dist/client/onboarding/use-onboarding.d.ts +1 -0
- package/dist/client/onboarding/use-onboarding.js +13 -11
- package/dist/client/org/TeamPage.js +174 -28
- package/dist/client/resources/McpConnectionSuggestion.js +29 -72
- package/dist/client/resources/McpIntegrationDialog.d.ts +3 -1
- package/dist/client/resources/McpIntegrationDialog.js +176 -51
- package/dist/client/resources/index.d.ts +1 -0
- package/dist/client/resources/index.js +1 -0
- package/dist/client/resources/mcp-integration-catalog.d.ts +8 -0
- package/dist/client/resources/mcp-integration-catalog.js +28 -0
- package/dist/client/settings/ExtensionsSettingsContent.js +2 -2
- package/dist/client/settings/SettingsPanel.d.ts +6 -0
- package/dist/client/settings/SettingsPanel.js +21 -8
- package/dist/client/settings/SettingsRow.d.ts +3 -1
- package/dist/client/settings/SettingsRow.js +4 -2
- package/dist/client/settings/SettingsSkeleton.js +5 -3
- package/dist/client/settings/SettingsTabsPage.js +2 -2
- package/dist/client/settings/UsageSection.d.ts +4 -1
- package/dist/client/settings/UsageSection.js +187 -93
- package/dist/client/settings/agent-settings-search.js +6 -2
- package/dist/client/setup-connections/useBuilderConnectCardController.d.ts +1 -1
- package/dist/client/setup-connections/useBuilderConnectCardController.js +2 -2
- package/dist/client/sharing/ShareButton.d.ts +2 -0
- package/dist/client/sharing/ShareButton.js +9 -2
- package/dist/client/sharing/ShareDialog.js +4 -1
- package/dist/client/sharing/useShareButtonController.d.ts +2 -1
- package/dist/client/sharing/useShareButtonController.js +8 -0
- package/dist/client/sharing/useShareDialogController.d.ts +1 -1
- package/dist/client/sharing/useShareDialogController.js +2 -1
- package/dist/config.d.ts +13 -0
- package/dist/config.js +53 -0
- package/dist/dashboard-storage/schema.d.ts +2 -2
- package/dist/data-programs/schema.d.ts +2 -2
- package/dist/extensions/actions.js +2 -1
- package/dist/extensions/html-shell.d.ts +2 -2
- package/dist/extensions/routes.js +4 -1
- package/dist/extensions/schema.d.ts +2 -2
- package/dist/file-upload/pre-upload-attachments.js +1 -1
- package/dist/framework-tools.js +3 -0
- package/dist/history/types.d.ts +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/integrations/webhook-handler.js +11 -3
- package/dist/jobs/background-automation-runner.d.ts +1 -0
- package/dist/jobs/background-automation-runner.js +4 -2
- package/dist/jobs/frontmatter.d.ts +4 -0
- package/dist/jobs/frontmatter.js +16 -0
- package/dist/jobs/run-history.js +1 -1
- package/dist/jobs/tools.js +9 -5
- package/dist/localization/default-messages.d.ts +27 -0
- package/dist/localization/default-messages.js +28 -1
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +3 -3
- package/dist/onboarding/plugin.js +17 -2
- package/dist/org/context.js +16 -0
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
- package/dist/provider-api/actions/provider-api.d.ts +8 -8
- package/dist/review/actions/create-review-comment.js +1 -1
- package/dist/review/actions/delete-review-comment.js +4 -1
- package/dist/review/actions/list-review-comments.js +4 -2
- package/dist/review/actions/reply-review-comment.js +1 -1
- package/dist/review/types.d.ts +1 -1
- package/dist/server/action-discovery.js +29 -0
- package/dist/server/agent-chat/context-tools.js +27 -6
- package/dist/server/agent-chat/plugin-options.d.ts +7 -0
- package/dist/server/agent-chat-plugin.d.ts +2 -0
- package/dist/server/agent-chat-plugin.js +12 -4
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/builder-browser.js +31 -0
- package/dist/server/onboarding-html.js +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/workspace-provider-oauth.js +38 -26
- package/dist/setup-connections/index.d.ts +1 -1
- package/dist/setup-connections/index.js +1 -1
- package/dist/shared/first-run-onboarding.d.ts +6 -0
- package/dist/shared/first-run-onboarding.js +6 -0
- package/dist/sharing/access.d.ts +1 -1
- package/dist/sharing/access.js +4 -1
- package/dist/sharing/actions/share-resource.d.ts +1 -1
- package/dist/sharing/actions/share-resource.js +2 -2
- package/dist/sharing/registry.d.ts +2 -2
- package/dist/sharing/schema.d.ts +5 -5
- package/dist/sharing/schema.js +9 -6
- package/dist/styles/agent-native.css +21 -0
- package/dist/styles/chat-history-list.css +1 -1
- package/dist/templates/chat/.agents/skills/adding-a-feature/SKILL.md +3 -2
- package/dist/templates/chat/.agents/skills/context-awareness/SKILL.md +206 -0
- package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +73 -80
- package/dist/templates/chat/.agents/skills/portability/SKILL.md +107 -0
- package/dist/templates/chat/.agents/skills/secrets/SKILL.md +339 -0
- package/dist/templates/chat/.agents/skills/self-modifying-code/SKILL.md +3 -3
- package/dist/templates/chat/.agents/skills/shadcn-ui/SKILL.md +1 -1
- package/dist/templates/chat/.agents/skills/sharing/SKILL.md +1 -0
- package/dist/templates/chat/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/dist/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/dist/templates/chat/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/dist/templates/chat/.agents/skills/workspace-conventions/SKILL.md +93 -0
- package/dist/templates/chat/AGENTS.md +4 -5
- package/dist/templates/default/.agents/skills/adding-a-feature/SKILL.md +3 -2
- package/dist/templates/default/.agents/skills/context-awareness/SKILL.md +206 -0
- package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +73 -80
- package/dist/templates/default/.agents/skills/portability/SKILL.md +107 -0
- package/dist/templates/default/.agents/skills/self-modifying-code/SKILL.md +3 -3
- package/dist/templates/default/.agents/skills/shadcn-ui/SKILL.md +1 -1
- package/dist/templates/default/.agents/skills/sharing/SKILL.md +233 -0
- package/dist/templates/default/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/dist/templates/default/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/dist/templates/default/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/dist/templates/default/.agents/skills/workspace-conventions/SKILL.md +93 -0
- package/dist/templates/default/AGENTS.md +17 -2
- package/dist/templates/default/agent-native.config.ts +6 -0
- package/dist/templates/default/app/i18n/ar-SA.ts +2 -0
- package/dist/templates/default/app/i18n/de-DE.ts +2 -0
- package/dist/templates/default/app/i18n/en-US.ts +3 -1
- package/dist/templates/default/app/i18n/es-ES.ts +2 -0
- package/dist/templates/default/app/i18n/fr-FR.ts +2 -0
- package/dist/templates/default/app/i18n/hi-IN.ts +2 -0
- package/dist/templates/default/app/i18n/ja-JP.ts +2 -0
- package/dist/templates/default/app/i18n/ko-KR.ts +2 -0
- package/dist/templates/default/app/i18n/pt-BR.ts +2 -0
- package/dist/templates/default/app/i18n/zh-CN.ts +2 -0
- package/dist/templates/default/app/i18n/zh-TW.ts +2 -0
- package/dist/templates/headless/.agents/skills/delegate-to-agent/SKILL.md +289 -0
- package/dist/templates/headless/.agents/skills/security/SKILL.md +324 -0
- package/dist/templates/headless/.agents/skills/storing-data/SKILL.md +178 -0
- package/dist/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +3 -2
- package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +73 -80
- package/dist/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +3 -3
- package/dist/templates/workspace-core/.agents/skills/shadcn-ui/SKILL.md +1 -1
- package/dist/templates/workspace-core/.agents/skills/sharing/SKILL.md +1 -0
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/dist/templates/workspace-core/.agents/skills/workspace-conventions/SKILL.md +3 -2
- package/dist/templates/workspace-core/AGENTS.md +9 -3
- package/dist/templates/workspace-root/AGENTS.md +26 -1
- package/dist/templates/workspace-root/agent-native.config.ts +8 -0
- package/dist/triggers/actions.js +2 -2
- package/dist/usage/actions/get-usage-alerts.d.ts +5 -0
- package/dist/usage/actions/get-usage-alerts.js +16 -0
- package/dist/usage/actions/get-usage-metrics.d.ts +7 -0
- package/dist/usage/actions/get-usage-metrics.js +26 -0
- package/dist/usage/actions/manage-usage-alert.d.ts +14 -0
- package/dist/usage/actions/manage-usage-alert.js +48 -0
- package/dist/usage/alerts-store.d.ts +78 -0
- package/dist/usage/alerts-store.js +592 -0
- package/dist/usage/metrics-store.d.ts +95 -0
- package/dist/usage/metrics-store.js +386 -0
- package/dist/usage/store.js +10 -0
- package/dist/vite/action-types-plugin.js +28 -0
- package/dist/vite/agent-native-config-loader.d.ts +6 -0
- package/dist/vite/agent-native-config-loader.js +54 -2
- package/dist/vite/client.js +11 -5
- package/dist/workspace-connections/actions/bulk-update-workspace-user-groups.d.ts +6 -0
- package/dist/workspace-connections/actions/bulk-update-workspace-user-groups.js +24 -0
- package/dist/workspace-connections/actions/delete-workspace-user-group.d.ts +7 -0
- package/dist/workspace-connections/actions/delete-workspace-user-group.js +29 -0
- package/dist/workspace-connections/actions/list-workspace-user-groups.d.ts +2 -0
- package/dist/workspace-connections/actions/list-workspace-user-groups.js +13 -0
- package/dist/workspace-connections/actions/upsert-workspace-user-group.d.ts +6 -0
- package/dist/workspace-connections/actions/upsert-workspace-user-group.js +22 -0
- package/dist/workspace-connections/credential-key-aliases.d.ts +3 -0
- package/dist/workspace-connections/credential-key-aliases.js +31 -0
- package/dist/workspace-connections/credentials.js +1 -25
- package/dist/workspace-connections/groups.d.ts +32 -0
- package/dist/workspace-connections/groups.js +288 -0
- package/dist/workspace-connections/index.d.ts +3 -1
- package/dist/workspace-connections/index.js +3 -1
- package/dist/workspace-connections/store.d.ts +9 -0
- package/dist/workspace-connections/store.js +68 -8
- package/docs/content/components.mdx +2 -2
- package/docs/content/sharing.mdx +2 -1
- package/package.json +10 -2
- package/src/templates/chat/.agents/skills/adding-a-feature/SKILL.md +3 -2
- package/src/templates/chat/.agents/skills/context-awareness/SKILL.md +206 -0
- package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +73 -80
- package/src/templates/chat/.agents/skills/portability/SKILL.md +107 -0
- package/src/templates/chat/.agents/skills/secrets/SKILL.md +339 -0
- package/src/templates/chat/.agents/skills/self-modifying-code/SKILL.md +3 -3
- package/src/templates/chat/.agents/skills/shadcn-ui/SKILL.md +1 -1
- package/src/templates/chat/.agents/skills/sharing/SKILL.md +1 -0
- package/src/templates/chat/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/src/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/src/templates/chat/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/src/templates/chat/.agents/skills/workspace-conventions/SKILL.md +93 -0
- package/src/templates/chat/AGENTS.md +4 -5
- package/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +3 -2
- package/src/templates/default/.agents/skills/context-awareness/SKILL.md +206 -0
- package/src/templates/default/.agents/skills/frontend-design/SKILL.md +73 -80
- package/src/templates/default/.agents/skills/portability/SKILL.md +107 -0
- package/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +3 -3
- package/src/templates/default/.agents/skills/shadcn-ui/SKILL.md +1 -1
- package/src/templates/default/.agents/skills/sharing/SKILL.md +233 -0
- package/src/templates/default/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/src/templates/default/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/src/templates/default/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/src/templates/default/.agents/skills/workspace-conventions/SKILL.md +93 -0
- package/src/templates/default/AGENTS.md +17 -2
- package/src/templates/default/agent-native.config.ts +6 -0
- package/src/templates/default/app/i18n/ar-SA.ts +2 -0
- package/src/templates/default/app/i18n/de-DE.ts +2 -0
- package/src/templates/default/app/i18n/en-US.ts +3 -1
- package/src/templates/default/app/i18n/es-ES.ts +2 -0
- package/src/templates/default/app/i18n/fr-FR.ts +2 -0
- package/src/templates/default/app/i18n/hi-IN.ts +2 -0
- package/src/templates/default/app/i18n/ja-JP.ts +2 -0
- package/src/templates/default/app/i18n/ko-KR.ts +2 -0
- package/src/templates/default/app/i18n/pt-BR.ts +2 -0
- package/src/templates/default/app/i18n/zh-CN.ts +2 -0
- package/src/templates/default/app/i18n/zh-TW.ts +2 -0
- package/src/templates/headless/.agents/skills/delegate-to-agent/SKILL.md +289 -0
- package/src/templates/headless/.agents/skills/security/SKILL.md +324 -0
- package/src/templates/headless/.agents/skills/storing-data/SKILL.md +178 -0
- package/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +3 -2
- package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +73 -80
- package/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +3 -3
- package/src/templates/workspace-core/.agents/skills/shadcn-ui/SKILL.md +1 -1
- package/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +1 -0
- package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +116 -44
- package/src/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +9 -3
- package/src/templates/workspace-core/.agents/skills/turn-into-app/references/spreadsheet-source.md +148 -0
- package/src/templates/workspace-core/.agents/skills/workspace-conventions/SKILL.md +3 -2
- package/src/templates/workspace-core/AGENTS.md +9 -3
- package/src/templates/workspace-root/AGENTS.md +26 -1
- package/src/templates/workspace-root/agent-native.config.ts +8 -0
- package/corpus/templates/analytics/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/corpus/templates/analytics/.agents/skills/create-skill/SKILL.md +0 -195
- package/corpus/templates/analytics/.agents/skills/feature-flags/SKILL.md +0 -169
- package/corpus/templates/analytics/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/corpus/templates/assets/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/corpus/templates/assets/.agents/skills/create-skill/SKILL.md +0 -195
- package/corpus/templates/assets/.agents/skills/feature-flags/SKILL.md +0 -169
- package/corpus/templates/assets/.agents/skills/real-time-collab/SKILL.md +0 -392
- package/corpus/templates/assets/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/corpus/templates/brain/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/corpus/templates/brain/.agents/skills/create-skill/SKILL.md +0 -195
- package/corpus/templates/brain/.agents/skills/feature-flags/SKILL.md +0 -169
- package/corpus/templates/brain/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/corpus/templates/calendar/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/corpus/templates/calendar/.agents/skills/create-skill/SKILL.md +0 -195
- package/corpus/templates/calendar/.agents/skills/feature-flags/SKILL.md +0 -169
- package/corpus/templates/calendar/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/corpus/templates/chat/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/corpus/templates/chat/.agents/skills/create-skill/SKILL.md +0 -195
- package/corpus/templates/chat/.agents/skills/feature-flags/SKILL.md +0 -169
- package/corpus/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/corpus/templates/clips/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/corpus/templates/clips/.agents/skills/create-skill/SKILL.md +0 -195
- package/corpus/templates/clips/.agents/skills/feature-flags/SKILL.md +0 -169
- package/corpus/templates/clips/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/corpus/templates/content/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/corpus/templates/content/.agents/skills/create-skill/SKILL.md +0 -195
- package/corpus/templates/content/.agents/skills/feature-flags/SKILL.md +0 -169
- package/corpus/templates/content/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/corpus/templates/crm/.agents/skills/feature-flags/SKILL.md +0 -169
- package/corpus/templates/crm/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/corpus/templates/design/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/corpus/templates/design/.agents/skills/create-skill/SKILL.md +0 -195
- package/corpus/templates/design/.agents/skills/feature-flags/SKILL.md +0 -169
- package/corpus/templates/design/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/corpus/templates/dispatch/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/corpus/templates/dispatch/.agents/skills/create-skill/SKILL.md +0 -195
- package/corpus/templates/dispatch/.agents/skills/feature-flags/SKILL.md +0 -169
- package/corpus/templates/dispatch/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/corpus/templates/factory/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/corpus/templates/factory/.agents/skills/create-skill/SKILL.md +0 -195
- package/corpus/templates/factory/.agents/skills/feature-flags/SKILL.md +0 -169
- package/corpus/templates/factory/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/corpus/templates/forms/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/corpus/templates/forms/.agents/skills/create-skill/SKILL.md +0 -195
- package/corpus/templates/forms/.agents/skills/feature-flags/SKILL.md +0 -169
- package/corpus/templates/forms/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/corpus/templates/macros/.agents/skills/feature-flags/SKILL.md +0 -169
- package/corpus/templates/macros/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/corpus/templates/mail/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/corpus/templates/mail/.agents/skills/create-skill/SKILL.md +0 -195
- package/corpus/templates/mail/.agents/skills/feature-flags/SKILL.md +0 -169
- package/corpus/templates/mail/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/corpus/templates/plan/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/corpus/templates/plan/.agents/skills/create-skill/SKILL.md +0 -195
- package/corpus/templates/plan/.agents/skills/feature-flags/SKILL.md +0 -169
- package/corpus/templates/plan/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/corpus/templates/slides/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/corpus/templates/slides/.agents/skills/create-skill/SKILL.md +0 -195
- package/corpus/templates/slides/.agents/skills/feature-flags/SKILL.md +0 -169
- package/corpus/templates/slides/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/corpus/templates/tasks/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/corpus/templates/tasks/.agents/skills/create-skill/SKILL.md +0 -195
- package/corpus/templates/tasks/.agents/skills/feature-flags/SKILL.md +0 -169
- package/corpus/templates/tasks/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/dist/templates/chat/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/dist/templates/chat/.agents/skills/create-skill/SKILL.md +0 -195
- package/dist/templates/chat/.agents/skills/feature-flags/SKILL.md +0 -169
- package/dist/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/dist/templates/default/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/dist/templates/default/.agents/skills/create-skill/SKILL.md +0 -195
- package/dist/templates/default/.agents/skills/feature-flags/SKILL.md +0 -169
- package/dist/templates/default/.agents/skills/integration-webhooks/SKILL.md +0 -364
- package/dist/templates/default/.agents/skills/internationalization/SKILL.md +0 -82
- package/dist/templates/default/.agents/skills/onboarding/SKILL.md +0 -56
- package/dist/templates/default/.agents/skills/real-time-collab/SKILL.md +0 -392
- package/dist/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/dist/templates/headless/.agents/skills/feature-flags/SKILL.md +0 -169
- package/dist/templates/headless/.agents/skills/integration-webhooks/SKILL.md +0 -364
- package/dist/templates/headless/.agents/skills/turn-into-app/SKILL.md +0 -402
- package/dist/templates/headless/.agents/skills/turn-into-app/agents/openai.yaml +0 -4
- package/dist/templates/headless/.agents/skills/turn-into-app/references/fresh-project.md +0 -90
- package/dist/templates/headless/.agents/skills/turn-into-skill/SKILL.md +0 -112
- package/dist/templates/headless/.agents/skills/turn-into-skill/agents/openai.yaml +0 -4
- package/dist/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +0 -411
- package/dist/templates/workspace-core/.agents/skills/adding-workspace-apps/SKILL.md +0 -107
- package/dist/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +0 -176
- package/dist/templates/workspace-core/.agents/skills/agent-page/SKILL.md +0 -63
- package/dist/templates/workspace-core/.agents/skills/audit-log/SKILL.md +0 -96
- package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +0 -295
- package/dist/templates/workspace-core/.agents/skills/automations/SKILL.md +0 -242
- package/dist/templates/workspace-core/.agents/skills/browser-sessions/SKILL.md +0 -131
- package/dist/templates/workspace-core/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/dist/templates/workspace-core/.agents/skills/changelog/SKILL.md +0 -114
- package/dist/templates/workspace-core/.agents/skills/client-methods/SKILL.md +0 -115
- package/dist/templates/workspace-core/.agents/skills/client-methods/references/legacy-client-fetch-audit-2026-06-03.md +0 -62
- package/dist/templates/workspace-core/.agents/skills/client-side-routing/SKILL.md +0 -87
- package/dist/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +0 -139
- package/dist/templates/workspace-core/.agents/skills/context-xray/SKILL.md +0 -72
- package/dist/templates/workspace-core/.agents/skills/create-skill/SKILL.md +0 -195
- package/dist/templates/workspace-core/.agents/skills/data-programs/SKILL.md +0 -128
- package/dist/templates/workspace-core/.agents/skills/extension-points/SKILL.md +0 -219
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +0 -533
- package/dist/templates/workspace-core/.agents/skills/extensions/references/api.md +0 -340
- package/dist/templates/workspace-core/.agents/skills/extensions/references/examples.md +0 -259
- package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +0 -547
- package/dist/templates/workspace-core/.agents/skills/external-agents/references/mcp-apps-embedding.md +0 -157
- package/dist/templates/workspace-core/.agents/skills/feature-flags/SKILL.md +0 -169
- package/dist/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +0 -117
- package/dist/templates/workspace-core/.agents/skills/harness-agents/SKILL.md +0 -170
- package/dist/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +0 -364
- package/dist/templates/workspace-core/.agents/skills/internationalization/SKILL.md +0 -82
- package/dist/templates/workspace-core/.agents/skills/mvp-followup/SKILL.md +0 -52
- package/dist/templates/workspace-core/.agents/skills/native-navigation/SKILL.md +0 -67
- package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +0 -329
- package/dist/templates/workspace-core/.agents/skills/onboarding/SKILL.md +0 -56
- package/dist/templates/workspace-core/.agents/skills/performance/SKILL.md +0 -299
- package/dist/templates/workspace-core/.agents/skills/qa/SKILL.md +0 -333
- package/dist/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +0 -392
- package/dist/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +0 -74
- package/dist/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +0 -70
- package/dist/templates/workspace-core/.agents/skills/server-plugins/SKILL.md +0 -84
- package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +0 -278
- package/dist/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/dist/templates/workspace-core/.agents/skills/visual-answer/SKILL.md +0 -101
- package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +0 -222
- package/dist/templates/workspace-core/.agents/skills/writing-agent-instructions/SKILL.md +0 -363
- package/src/templates/chat/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/src/templates/chat/.agents/skills/create-skill/SKILL.md +0 -195
- package/src/templates/chat/.agents/skills/feature-flags/SKILL.md +0 -169
- package/src/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/src/templates/default/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/src/templates/default/.agents/skills/create-skill/SKILL.md +0 -195
- package/src/templates/default/.agents/skills/feature-flags/SKILL.md +0 -169
- package/src/templates/default/.agents/skills/integration-webhooks/SKILL.md +0 -364
- package/src/templates/default/.agents/skills/internationalization/SKILL.md +0 -82
- package/src/templates/default/.agents/skills/onboarding/SKILL.md +0 -56
- package/src/templates/default/.agents/skills/real-time-collab/SKILL.md +0 -392
- package/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/src/templates/headless/.agents/skills/feature-flags/SKILL.md +0 -169
- package/src/templates/headless/.agents/skills/integration-webhooks/SKILL.md +0 -364
- package/src/templates/headless/.agents/skills/turn-into-app/SKILL.md +0 -402
- package/src/templates/headless/.agents/skills/turn-into-app/agents/openai.yaml +0 -4
- package/src/templates/headless/.agents/skills/turn-into-app/references/fresh-project.md +0 -90
- package/src/templates/headless/.agents/skills/turn-into-skill/SKILL.md +0 -112
- package/src/templates/headless/.agents/skills/turn-into-skill/agents/openai.yaml +0 -4
- package/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +0 -411
- package/src/templates/workspace-core/.agents/skills/adding-workspace-apps/SKILL.md +0 -107
- package/src/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +0 -176
- package/src/templates/workspace-core/.agents/skills/agent-page/SKILL.md +0 -63
- package/src/templates/workspace-core/.agents/skills/audit-log/SKILL.md +0 -96
- package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +0 -295
- package/src/templates/workspace-core/.agents/skills/automations/SKILL.md +0 -242
- package/src/templates/workspace-core/.agents/skills/browser-sessions/SKILL.md +0 -131
- package/src/templates/workspace-core/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/src/templates/workspace-core/.agents/skills/changelog/SKILL.md +0 -114
- package/src/templates/workspace-core/.agents/skills/client-methods/SKILL.md +0 -115
- package/src/templates/workspace-core/.agents/skills/client-methods/references/legacy-client-fetch-audit-2026-06-03.md +0 -62
- package/src/templates/workspace-core/.agents/skills/client-side-routing/SKILL.md +0 -87
- package/src/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +0 -139
- package/src/templates/workspace-core/.agents/skills/context-xray/SKILL.md +0 -72
- package/src/templates/workspace-core/.agents/skills/create-skill/SKILL.md +0 -195
- package/src/templates/workspace-core/.agents/skills/data-programs/SKILL.md +0 -128
- package/src/templates/workspace-core/.agents/skills/extension-points/SKILL.md +0 -219
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +0 -533
- package/src/templates/workspace-core/.agents/skills/extensions/references/api.md +0 -340
- package/src/templates/workspace-core/.agents/skills/extensions/references/examples.md +0 -259
- package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +0 -547
- package/src/templates/workspace-core/.agents/skills/external-agents/references/mcp-apps-embedding.md +0 -157
- package/src/templates/workspace-core/.agents/skills/feature-flags/SKILL.md +0 -169
- package/src/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +0 -117
- package/src/templates/workspace-core/.agents/skills/harness-agents/SKILL.md +0 -170
- package/src/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +0 -364
- package/src/templates/workspace-core/.agents/skills/internationalization/SKILL.md +0 -82
- package/src/templates/workspace-core/.agents/skills/mvp-followup/SKILL.md +0 -52
- package/src/templates/workspace-core/.agents/skills/native-navigation/SKILL.md +0 -67
- package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +0 -329
- package/src/templates/workspace-core/.agents/skills/onboarding/SKILL.md +0 -56
- package/src/templates/workspace-core/.agents/skills/performance/SKILL.md +0 -299
- package/src/templates/workspace-core/.agents/skills/qa/SKILL.md +0 -333
- package/src/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +0 -392
- package/src/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +0 -74
- package/src/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +0 -70
- package/src/templates/workspace-core/.agents/skills/server-plugins/SKILL.md +0 -84
- package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +0 -278
- package/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +0 -127
- package/src/templates/workspace-core/.agents/skills/visual-answer/SKILL.md +0 -101
- package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +0 -222
- package/src/templates/workspace-core/.agents/skills/writing-agent-instructions/SKILL.md +0 -363
|
@@ -4,6 +4,23 @@ import {
|
|
|
4
4
|
useActionQuery,
|
|
5
5
|
} from "@agent-native/core/client/hooks";
|
|
6
6
|
import { useT } from "@agent-native/core/client/i18n";
|
|
7
|
+
import {
|
|
8
|
+
IntegrationConnectionChoice,
|
|
9
|
+
IntegrationGrid,
|
|
10
|
+
} from "@agent-native/core/client/integrations";
|
|
11
|
+
import { useAppRoles, useOrgRole } from "@agent-native/core/client/org";
|
|
12
|
+
import {
|
|
13
|
+
McpIntegrationDialog,
|
|
14
|
+
McpIntegrationLogo,
|
|
15
|
+
getDefaultMcpIntegrations,
|
|
16
|
+
useCreateMcpServer,
|
|
17
|
+
type DefaultMcpIntegration,
|
|
18
|
+
} from "@agent-native/core/client/resources";
|
|
19
|
+
import {
|
|
20
|
+
useShareOrgMemberSearch,
|
|
21
|
+
type ShareOrgMember,
|
|
22
|
+
} from "@agent-native/core/client/sharing";
|
|
23
|
+
import { credentialKeyMatches } from "@agent-native/core/workspace-connections/credential-key-aliases";
|
|
7
24
|
import {
|
|
8
25
|
ActionQueryError,
|
|
9
26
|
DispatchShell,
|
|
@@ -31,10 +48,6 @@ import {
|
|
|
31
48
|
} from "@agent-native/dispatch/components/ui/dialog";
|
|
32
49
|
import { Input } from "@agent-native/dispatch/components/ui/input";
|
|
33
50
|
import { Label } from "@agent-native/dispatch/components/ui/label";
|
|
34
|
-
import {
|
|
35
|
-
RadioGroup,
|
|
36
|
-
RadioGroupItem,
|
|
37
|
-
} from "@agent-native/dispatch/components/ui/radio-group";
|
|
38
51
|
import {
|
|
39
52
|
Select,
|
|
40
53
|
SelectContent,
|
|
@@ -54,6 +67,7 @@ import {
|
|
|
54
67
|
IconBuilding,
|
|
55
68
|
IconChartBar,
|
|
56
69
|
IconCheck,
|
|
70
|
+
IconChevronDown,
|
|
57
71
|
IconCircleDashed,
|
|
58
72
|
IconClock,
|
|
59
73
|
IconDatabase,
|
|
@@ -63,6 +77,7 @@ import {
|
|
|
63
77
|
IconPlugConnected,
|
|
64
78
|
IconPlus,
|
|
65
79
|
IconRefresh,
|
|
80
|
+
IconSearch,
|
|
66
81
|
IconShieldCheck,
|
|
67
82
|
IconTrash,
|
|
68
83
|
IconUsersGroup,
|
|
@@ -91,6 +106,7 @@ const CONNECTION_QUERY_KEY = [
|
|
|
91
106
|
"list-workspace-connections",
|
|
92
107
|
CONNECTION_QUERY_PARAMS,
|
|
93
108
|
] as const;
|
|
109
|
+
const GROUP_QUERY_KEY = ["action", "list-workspace-user-groups", {}] as const;
|
|
94
110
|
|
|
95
111
|
type IconComponent = ComponentType<{
|
|
96
112
|
size?: number | string;
|
|
@@ -162,6 +178,8 @@ interface WorkspaceConnection {
|
|
|
162
178
|
scopes: string[];
|
|
163
179
|
config: Record<string, unknown>;
|
|
164
180
|
allowedApps: string[];
|
|
181
|
+
allowedUsers?: string[];
|
|
182
|
+
allowedUserGroups?: string[];
|
|
165
183
|
credentialRefs: WorkspaceConnectionCredentialRef[];
|
|
166
184
|
createdAt: string;
|
|
167
185
|
updatedAt: string;
|
|
@@ -259,6 +277,18 @@ interface ConnectionFormState {
|
|
|
259
277
|
credentialRefs: WorkspaceConnectionCredentialRef[];
|
|
260
278
|
allApps: boolean;
|
|
261
279
|
selectedApps: string[];
|
|
280
|
+
allUsers: boolean;
|
|
281
|
+
selectedUsers: string[];
|
|
282
|
+
selectedUserGroups: string[];
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
interface WorkspaceUserGroup {
|
|
286
|
+
id: string;
|
|
287
|
+
orgId: string;
|
|
288
|
+
name: string;
|
|
289
|
+
memberEmails: string[];
|
|
290
|
+
createdAt: string;
|
|
291
|
+
updatedAt: string;
|
|
262
292
|
}
|
|
263
293
|
|
|
264
294
|
interface WorkspaceConnectionSetupPlanCredential {
|
|
@@ -300,6 +330,8 @@ interface WorkspaceConnectionSetupPlan {
|
|
|
300
330
|
| "status"
|
|
301
331
|
| "scopes"
|
|
302
332
|
| "allowedApps"
|
|
333
|
+
| "allowedUsers"
|
|
334
|
+
| "allowedUserGroups"
|
|
303
335
|
| "credentialRefs"
|
|
304
336
|
| "lastError"
|
|
305
337
|
> | null;
|
|
@@ -323,6 +355,14 @@ interface SetupWizardFormState {
|
|
|
323
355
|
credentialRefs: WorkspaceConnectionCredentialRef[];
|
|
324
356
|
grantMode: "all-apps" | "selected-apps";
|
|
325
357
|
selectedApps: string[];
|
|
358
|
+
userGrantMode: "all-users" | "selected-users";
|
|
359
|
+
selectedUsers: string[];
|
|
360
|
+
selectedUserGroups: string[];
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
interface ProviderChoice {
|
|
364
|
+
provider: WorkspaceConnectionProvider;
|
|
365
|
+
personalIntegration: DefaultMcpIntegration;
|
|
326
366
|
}
|
|
327
367
|
|
|
328
368
|
const EMPTY_RESPONSE: WorkspaceConnectionsResponse = {
|
|
@@ -387,6 +427,17 @@ const PROVIDER_ICONS: Record<string, IconComponent> = {
|
|
|
387
427
|
generic: IconWorld,
|
|
388
428
|
};
|
|
389
429
|
|
|
430
|
+
const MCP_INTEGRATIONS_BY_ID = new Map(
|
|
431
|
+
getDefaultMcpIntegrations().map((integration) => [
|
|
432
|
+
integration.id,
|
|
433
|
+
integration,
|
|
434
|
+
]),
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
const PROVIDER_LOGO_IDS: Record<string, string> = {
|
|
438
|
+
google_drive: "google-workspace",
|
|
439
|
+
};
|
|
440
|
+
|
|
390
441
|
function cx(...classes: Array<string | false | null | undefined>) {
|
|
391
442
|
return classes.filter(Boolean).join(" ");
|
|
392
443
|
}
|
|
@@ -395,6 +446,25 @@ function iconForProvider(providerId: string): IconComponent {
|
|
|
395
446
|
return PROVIDER_ICONS[providerId] ?? IconPlugConnected;
|
|
396
447
|
}
|
|
397
448
|
|
|
449
|
+
function logoForProvider(providerId: string, providerName: string): ReactNode {
|
|
450
|
+
const integration = MCP_INTEGRATIONS_BY_ID.get(
|
|
451
|
+
PROVIDER_LOGO_IDS[providerId] ?? providerId,
|
|
452
|
+
);
|
|
453
|
+
if (!integration?.logoUrl) {
|
|
454
|
+
const Icon = iconForProvider(providerId);
|
|
455
|
+
return <Icon size={18} />;
|
|
456
|
+
}
|
|
457
|
+
return (
|
|
458
|
+
<McpIntegrationLogo
|
|
459
|
+
name={providerName}
|
|
460
|
+
logoUrl={integration.logoUrl}
|
|
461
|
+
integrationId={integration.id}
|
|
462
|
+
className="size-7 rounded-md border-0 bg-transparent"
|
|
463
|
+
imageClassName="size-full p-0.5"
|
|
464
|
+
/>
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
398
468
|
function iconForApp(appId: string): IconComponent {
|
|
399
469
|
return APP_ICONS[appId] ?? IconUsersGroup;
|
|
400
470
|
}
|
|
@@ -556,11 +626,13 @@ function missingRequiredCredentialKeys(
|
|
|
556
626
|
refs: WorkspaceConnectionCredentialRef[],
|
|
557
627
|
): string[] {
|
|
558
628
|
if (!provider) return [];
|
|
559
|
-
const available = new Set(refs.map((ref) => ref.key.trim()).filter(Boolean));
|
|
560
629
|
return provider.credentialKeys
|
|
561
630
|
.filter((credential) => credential.required)
|
|
562
631
|
.map((credential) => credential.key)
|
|
563
|
-
.filter(
|
|
632
|
+
.filter(
|
|
633
|
+
(key) =>
|
|
634
|
+
!refs.some((ref) => credentialKeyMatches(provider.id, key, ref.key)),
|
|
635
|
+
);
|
|
564
636
|
}
|
|
565
637
|
|
|
566
638
|
function formFromConnection(
|
|
@@ -583,6 +655,9 @@ function formFromConnection(
|
|
|
583
655
|
: [],
|
|
584
656
|
allApps: connection.allowedApps.length === 0,
|
|
585
657
|
selectedApps: connection.allowedApps,
|
|
658
|
+
allUsers: (connection.allowedUsers?.length ?? 0) === 0,
|
|
659
|
+
selectedUsers: connection.allowedUsers ?? [],
|
|
660
|
+
selectedUserGroups: connection.allowedUserGroups ?? [],
|
|
586
661
|
};
|
|
587
662
|
}
|
|
588
663
|
|
|
@@ -610,6 +685,14 @@ function setupFormFromPlan(
|
|
|
610
685
|
: normalizeCredentialRefs(plan.suggestedCredentialRefs, plan.provider),
|
|
611
686
|
grantMode: plan.grantRecommendation.accessMode,
|
|
612
687
|
selectedApps,
|
|
688
|
+
userGrantMode:
|
|
689
|
+
connection &&
|
|
690
|
+
((connection.allowedUsers?.length ?? 0) > 0 ||
|
|
691
|
+
(connection.allowedUserGroups?.length ?? 0) > 0)
|
|
692
|
+
? "selected-users"
|
|
693
|
+
: "all-users",
|
|
694
|
+
selectedUsers: connection?.allowedUsers ?? [],
|
|
695
|
+
selectedUserGroups: connection?.allowedUserGroups ?? [],
|
|
613
696
|
};
|
|
614
697
|
}
|
|
615
698
|
|
|
@@ -629,32 +712,6 @@ function appIsGranted(
|
|
|
629
712
|
);
|
|
630
713
|
}
|
|
631
714
|
|
|
632
|
-
function grantModeForApp(
|
|
633
|
-
connection: WorkspaceConnection,
|
|
634
|
-
appId: string,
|
|
635
|
-
grants: WorkspaceConnectionsResponse["grants"],
|
|
636
|
-
): "all-apps" | "selected" | "explicit" | "off" {
|
|
637
|
-
if (connection.allowedApps.length === 0) return "all-apps";
|
|
638
|
-
if (connection.allowedApps.includes(appId)) return "selected";
|
|
639
|
-
if (
|
|
640
|
-
grants.some(
|
|
641
|
-
(grant) =>
|
|
642
|
-
grant.connectionId === connection.id &&
|
|
643
|
-
(grant.appId === appId || grant.appId === "*"),
|
|
644
|
-
)
|
|
645
|
-
) {
|
|
646
|
-
return "explicit";
|
|
647
|
-
}
|
|
648
|
-
return "off";
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
function grantModeLabel(
|
|
652
|
-
t: Translate,
|
|
653
|
-
mode: ReturnType<typeof grantModeForApp>,
|
|
654
|
-
): string {
|
|
655
|
-
return t(`integrations.grantMode.${mode}`);
|
|
656
|
-
}
|
|
657
|
-
|
|
658
715
|
function usageForAppGrant(
|
|
659
716
|
connection: WorkspaceConnection,
|
|
660
717
|
appId: string,
|
|
@@ -711,6 +768,38 @@ function summarizeGrant(
|
|
|
711
768
|
return `${labels.join(", ")}${suffix}`;
|
|
712
769
|
}
|
|
713
770
|
|
|
771
|
+
function summarizeUserAccess(
|
|
772
|
+
connection: WorkspaceConnection,
|
|
773
|
+
t: Translate,
|
|
774
|
+
groups: WorkspaceUserGroup[] = [],
|
|
775
|
+
): string {
|
|
776
|
+
const allowedUsers = connection.allowedUsers ?? [];
|
|
777
|
+
const allowedGroupIds = connection.allowedUserGroups ?? [];
|
|
778
|
+
if (allowedUsers.length === 0 && allowedGroupIds.length === 0) {
|
|
779
|
+
return t(
|
|
780
|
+
/* i18n-key-ignore */
|
|
781
|
+
"integrations.allWorkspaceMembers",
|
|
782
|
+
{ defaultValue: "All workspace members" },
|
|
783
|
+
);
|
|
784
|
+
}
|
|
785
|
+
const groupLabels = allowedGroupIds
|
|
786
|
+
.map((id) => groups.find((group) => group.id === id)?.name ?? id)
|
|
787
|
+
.slice(0, 2);
|
|
788
|
+
const labels = [
|
|
789
|
+
...groupLabels,
|
|
790
|
+
...(allowedUsers.length > 0 ? [`${allowedUsers.length} people`] : []),
|
|
791
|
+
];
|
|
792
|
+
const suffix =
|
|
793
|
+
allowedGroupIds.length + (allowedUsers.length > 0 ? 1 : 0) > labels.length
|
|
794
|
+
? ` +${
|
|
795
|
+
allowedGroupIds.length +
|
|
796
|
+
(allowedUsers.length > 0 ? 1 : 0) -
|
|
797
|
+
labels.length
|
|
798
|
+
}`
|
|
799
|
+
: "";
|
|
800
|
+
return `${labels.join(", ")}${suffix}`;
|
|
801
|
+
}
|
|
802
|
+
|
|
714
803
|
function summarizeAppList(
|
|
715
804
|
appIds: string[],
|
|
716
805
|
grantApps: GrantApp[],
|
|
@@ -752,124 +841,32 @@ function providerHasOAuth(provider: WorkspaceConnectionProvider): boolean {
|
|
|
752
841
|
return Boolean(provider.oauth) || provider.id === "slack";
|
|
753
842
|
}
|
|
754
843
|
|
|
755
|
-
function ProviderCard({
|
|
756
|
-
provider,
|
|
757
|
-
connections,
|
|
758
|
-
onCreate,
|
|
759
|
-
}: {
|
|
760
|
-
provider: WorkspaceConnectionProvider;
|
|
761
|
-
connections: WorkspaceConnection[];
|
|
762
|
-
onCreate: () => void;
|
|
763
|
-
}) {
|
|
764
|
-
const t = useT();
|
|
765
|
-
const Icon = iconForProvider(provider.id);
|
|
766
|
-
const active = connections.filter((item) => item.status !== "disabled");
|
|
767
|
-
const readiness = provider.readiness;
|
|
768
|
-
const readinessStatus =
|
|
769
|
-
readiness?.status ?? (active.length > 0 ? "ready" : "not_configured");
|
|
770
|
-
return (
|
|
771
|
-
<article className="flex min-h-[232px] flex-col justify-between rounded-lg border bg-card p-4 shadow-sm transition hover:border-foreground/20">
|
|
772
|
-
<div className="space-y-3">
|
|
773
|
-
<div className="flex items-start justify-between gap-3">
|
|
774
|
-
<div className="flex h-10 w-10 items-center justify-center rounded-md border bg-muted">
|
|
775
|
-
<Icon size={18} className="text-muted-foreground" />
|
|
776
|
-
</div>
|
|
777
|
-
<Pill className={READINESS_CLASSES[readinessStatus]}>
|
|
778
|
-
{readinessStatus === "ready" ? (
|
|
779
|
-
<IconCheck size={12} />
|
|
780
|
-
) : (
|
|
781
|
-
<IconCircleDashed size={12} />
|
|
782
|
-
)}
|
|
783
|
-
{readinessLabel(t, readinessStatus)}
|
|
784
|
-
</Pill>
|
|
785
|
-
</div>
|
|
786
|
-
<div>
|
|
787
|
-
<h2 className="text-sm font-semibold text-foreground">
|
|
788
|
-
{provider.label}
|
|
789
|
-
</h2>
|
|
790
|
-
<p className="mt-1 line-clamp-3 text-sm leading-5 text-muted-foreground">
|
|
791
|
-
{provider.description}
|
|
792
|
-
</p>
|
|
793
|
-
</div>
|
|
794
|
-
<div className="flex flex-wrap gap-1.5">
|
|
795
|
-
{provider.capabilities.map((capability) => (
|
|
796
|
-
<Pill key={capability} className="border-border bg-background">
|
|
797
|
-
{capability}
|
|
798
|
-
</Pill>
|
|
799
|
-
))}
|
|
800
|
-
</div>
|
|
801
|
-
{readiness?.missingRequiredCredentialKeys.length ? (
|
|
802
|
-
<div className="rounded-md border border-amber-500/20 bg-amber-500/10 px-3 py-2 text-xs text-amber-700 dark:text-amber-400">
|
|
803
|
-
{t("integrations.missingKeys", {
|
|
804
|
-
keys: readiness.missingRequiredCredentialKeys
|
|
805
|
-
.slice(0, 2)
|
|
806
|
-
.join(", "),
|
|
807
|
-
suffix:
|
|
808
|
-
readiness.missingRequiredCredentialKeys.length > 2
|
|
809
|
-
? ` +${readiness.missingRequiredCredentialKeys.length - 2}`
|
|
810
|
-
: "",
|
|
811
|
-
})}
|
|
812
|
-
</div>
|
|
813
|
-
) : null}
|
|
814
|
-
</div>
|
|
815
|
-
<div className="mt-4 flex items-center justify-between gap-3 border-t pt-3">
|
|
816
|
-
<span className="text-xs text-muted-foreground">
|
|
817
|
-
{active.length > 0
|
|
818
|
-
? t("integrations.activeConnection", { count: active.length })
|
|
819
|
-
: provider.credentialKeys.length === 0
|
|
820
|
-
? t("integrations.noCredentialRefs")
|
|
821
|
-
: t("integrations.credentialRefCount", {
|
|
822
|
-
count: provider.credentialKeys.length,
|
|
823
|
-
})}
|
|
824
|
-
</span>
|
|
825
|
-
<div className="flex items-center gap-2">
|
|
826
|
-
{providerHasOAuth(provider) ? (
|
|
827
|
-
<Button
|
|
828
|
-
type="button"
|
|
829
|
-
size="sm"
|
|
830
|
-
onClick={() => startWorkspaceProviderOAuth(provider)}
|
|
831
|
-
>
|
|
832
|
-
<IconShieldCheck size={14} />
|
|
833
|
-
{t("integrations.connect")}
|
|
834
|
-
</Button>
|
|
835
|
-
) : null}
|
|
836
|
-
<Button
|
|
837
|
-
type="button"
|
|
838
|
-
variant={providerHasOAuth(provider) ? "ghost" : "outline"}
|
|
839
|
-
size="sm"
|
|
840
|
-
onClick={onCreate}
|
|
841
|
-
>
|
|
842
|
-
<IconKey size={14} />
|
|
843
|
-
{providerHasOAuth(provider)
|
|
844
|
-
? t("integrations.connectionFallback")
|
|
845
|
-
: t("integrations.connect")}
|
|
846
|
-
</Button>
|
|
847
|
-
</div>
|
|
848
|
-
</div>
|
|
849
|
-
</article>
|
|
850
|
-
);
|
|
851
|
-
}
|
|
852
|
-
|
|
853
844
|
function ConnectionRow({
|
|
854
845
|
connection,
|
|
855
846
|
provider,
|
|
856
847
|
grantApps,
|
|
857
848
|
grants,
|
|
849
|
+
groups,
|
|
858
850
|
onEdit,
|
|
859
851
|
onRepair,
|
|
860
852
|
onDelete,
|
|
861
853
|
onToggleGrant,
|
|
862
854
|
grantPending,
|
|
855
|
+
canManage,
|
|
856
|
+
canManageGrant,
|
|
863
857
|
}: {
|
|
864
858
|
connection: WorkspaceConnection;
|
|
865
859
|
provider?: WorkspaceConnectionProvider;
|
|
866
860
|
grantApps: GrantApp[];
|
|
867
861
|
grants: WorkspaceConnectionsResponse["grants"];
|
|
862
|
+
groups: WorkspaceUserGroup[];
|
|
868
863
|
onEdit: () => void;
|
|
869
864
|
onRepair: () => void;
|
|
870
865
|
onDelete: () => void;
|
|
871
866
|
onToggleGrant: (appId: string, granted: boolean) => void;
|
|
872
867
|
grantPending: boolean;
|
|
868
|
+
canManage: boolean;
|
|
869
|
+
canManageGrant: (appId: string) => boolean;
|
|
873
870
|
}) {
|
|
874
871
|
const t = useT();
|
|
875
872
|
const Icon = iconForProvider(connection.provider);
|
|
@@ -883,12 +880,12 @@ function ConnectionRow({
|
|
|
883
880
|
connection.status === "needs_reauth" ||
|
|
884
881
|
connection.status === "disabled";
|
|
885
882
|
return (
|
|
886
|
-
<article className="rounded-lg
|
|
883
|
+
<article className="rounded-lg bg-muted/35">
|
|
887
884
|
<div className="dispatch-connection-card-grid grid gap-4 p-4">
|
|
888
885
|
<div className="min-w-0 space-y-3">
|
|
889
886
|
<div className="flex flex-wrap items-start justify-between gap-3">
|
|
890
887
|
<div className="flex min-w-0 items-start gap-3">
|
|
891
|
-
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-md
|
|
888
|
+
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-md bg-background/80">
|
|
892
889
|
<Icon size={18} className="text-muted-foreground" />
|
|
893
890
|
</div>
|
|
894
891
|
<div className="min-w-0">
|
|
@@ -914,59 +911,61 @@ function ConnectionRow({
|
|
|
914
911
|
</p>
|
|
915
912
|
</div>
|
|
916
913
|
</div>
|
|
917
|
-
|
|
918
|
-
|
|
914
|
+
{canManage ? (
|
|
915
|
+
<div className="flex items-center gap-1.5">
|
|
916
|
+
{needsRepair ? (
|
|
917
|
+
<Button
|
|
918
|
+
type="button"
|
|
919
|
+
variant="outline"
|
|
920
|
+
size="sm"
|
|
921
|
+
onClick={onRepair}
|
|
922
|
+
>
|
|
923
|
+
<IconRefresh size={14} />
|
|
924
|
+
{t("integrations.repair")}
|
|
925
|
+
</Button>
|
|
926
|
+
) : null}
|
|
919
927
|
<Button
|
|
920
928
|
type="button"
|
|
921
|
-
variant="
|
|
929
|
+
variant="ghost"
|
|
922
930
|
size="sm"
|
|
923
|
-
onClick={
|
|
931
|
+
onClick={onEdit}
|
|
924
932
|
>
|
|
925
|
-
<
|
|
926
|
-
{t("integrations.
|
|
933
|
+
<IconEdit size={14} />
|
|
934
|
+
{t("integrations.edit")}
|
|
927
935
|
</Button>
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
</Button>
|
|
942
|
-
</div>
|
|
936
|
+
<Button
|
|
937
|
+
type="button"
|
|
938
|
+
variant="ghost"
|
|
939
|
+
size="icon"
|
|
940
|
+
onClick={onDelete}
|
|
941
|
+
aria-label={`${t("integrations.delete")} ${connection.label}`}
|
|
942
|
+
title={`${t("integrations.delete")} ${connection.label}`}
|
|
943
|
+
className="text-muted-foreground hover:bg-destructive/10 hover:text-destructive"
|
|
944
|
+
>
|
|
945
|
+
<IconTrash size={14} />
|
|
946
|
+
</Button>
|
|
947
|
+
</div>
|
|
948
|
+
) : null}
|
|
943
949
|
</div>
|
|
944
950
|
|
|
945
|
-
<div className="dispatch-connection-meta-grid grid gap-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
connection.
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
}
|
|
954
|
-
/>
|
|
955
|
-
<ConnectionMeta
|
|
956
|
-
icon={IconShieldCheck}
|
|
957
|
-
label={t("integrations.scopes")}
|
|
958
|
-
value={
|
|
959
|
-
connection.scopes.length
|
|
960
|
-
? connection.scopes.join(", ")
|
|
961
|
-
: t("integrations.none")
|
|
962
|
-
}
|
|
963
|
-
/>
|
|
951
|
+
<div className="dispatch-connection-meta-grid grid gap-x-6 gap-y-3">
|
|
952
|
+
{connection.scopes.length > 0 ? (
|
|
953
|
+
<ConnectionMeta
|
|
954
|
+
icon={IconShieldCheck}
|
|
955
|
+
label={t("integrations.scopes")}
|
|
956
|
+
value={connection.scopes.join(", ")}
|
|
957
|
+
/>
|
|
958
|
+
) : null}
|
|
964
959
|
<ConnectionMeta
|
|
965
960
|
icon={IconUsersGroup}
|
|
966
|
-
label={t(
|
|
967
|
-
|
|
961
|
+
label={t(
|
|
962
|
+
/* i18n-key-ignore */
|
|
963
|
+
"integrations.peopleWithAccess",
|
|
964
|
+
{ defaultValue: "People with access" },
|
|
965
|
+
)}
|
|
966
|
+
value={summarizeUserAccess(connection, t, groups)}
|
|
968
967
|
/>
|
|
969
|
-
{
|
|
968
|
+
{connection.lastUsedAt ? (
|
|
970
969
|
<ConnectionMeta
|
|
971
970
|
icon={IconClock}
|
|
972
971
|
label={t("integrations.usage")}
|
|
@@ -1000,20 +999,11 @@ function ConnectionRow({
|
|
|
1000
999
|
) : null}
|
|
1001
1000
|
</div>
|
|
1002
1001
|
|
|
1003
|
-
<div className="rounded-md
|
|
1002
|
+
<div className="rounded-md bg-muted/20 p-3">
|
|
1004
1003
|
<div className="mb-2 flex items-center justify-between gap-2">
|
|
1005
|
-
<
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
</h3>
|
|
1009
|
-
<p className="mt-0.5 text-xs text-muted-foreground">
|
|
1010
|
-
{connection.allowedApps.length === 0
|
|
1011
|
-
? t("integrations.everyWorkspaceAppCanReuse")
|
|
1012
|
-
: t("integrations.selectedAppsCanReuse", {
|
|
1013
|
-
apps: summarizeGrant(connection, grantApps, grants, t),
|
|
1014
|
-
})}
|
|
1015
|
-
</p>
|
|
1016
|
-
</div>
|
|
1004
|
+
<h3 className="text-sm font-medium text-foreground">
|
|
1005
|
+
{t("integrations.appGrants")}
|
|
1006
|
+
</h3>
|
|
1017
1007
|
<Pill className="border-border bg-muted text-muted-foreground">
|
|
1018
1008
|
{connection.allowedApps.length === 0
|
|
1019
1009
|
? t("integrations.allApps")
|
|
@@ -1024,12 +1014,11 @@ function ConnectionRow({
|
|
|
1024
1014
|
{grantApps.map((app) => {
|
|
1025
1015
|
const AppIcon = app.icon;
|
|
1026
1016
|
const granted = appIsGranted(connection, app.id, grants);
|
|
1027
|
-
const mode = grantModeForApp(connection, app.id, grants);
|
|
1028
1017
|
const usage = usageForAppGrant(connection, app.id, grants);
|
|
1029
1018
|
return (
|
|
1030
1019
|
<div
|
|
1031
1020
|
key={app.id}
|
|
1032
|
-
className="flex min-h-9 items-center justify-between gap-3 rounded-md
|
|
1021
|
+
className="flex min-h-9 items-center justify-between gap-3 rounded-md bg-muted/40 px-2.5 py-2"
|
|
1033
1022
|
>
|
|
1034
1023
|
<div className="flex min-w-0 items-center gap-2">
|
|
1035
1024
|
<AppIcon size={14} className="text-muted-foreground" />
|
|
@@ -1037,26 +1026,16 @@ function ConnectionRow({
|
|
|
1037
1026
|
<span className="truncate text-sm font-medium">
|
|
1038
1027
|
{app.label}
|
|
1039
1028
|
</span>
|
|
1040
|
-
{usage
|
|
1029
|
+
{usage != null ? (
|
|
1041
1030
|
<span className="truncate text-xs text-muted-foreground">
|
|
1042
1031
|
{formatTimestamp(t, usage)}
|
|
1043
1032
|
</span>
|
|
1044
1033
|
) : null}
|
|
1045
1034
|
</span>
|
|
1046
|
-
<Pill
|
|
1047
|
-
className={cx(
|
|
1048
|
-
"h-5 px-1.5 text-[11px]",
|
|
1049
|
-
granted
|
|
1050
|
-
? "border-green-500/20 bg-green-500/10 text-green-700 dark:text-green-400"
|
|
1051
|
-
: "border-border bg-muted text-muted-foreground",
|
|
1052
|
-
)}
|
|
1053
|
-
>
|
|
1054
|
-
{grantModeLabel(t, mode)}
|
|
1055
|
-
</Pill>
|
|
1056
1035
|
</div>
|
|
1057
1036
|
<Switch
|
|
1058
1037
|
checked={granted}
|
|
1059
|
-
disabled={grantPending}
|
|
1038
|
+
disabled={grantPending || !canManageGrant(app.id)}
|
|
1060
1039
|
onCheckedChange={(checked) =>
|
|
1061
1040
|
onToggleGrant(app.id, checked)
|
|
1062
1041
|
}
|
|
@@ -1088,12 +1067,12 @@ function ConnectionMeta({
|
|
|
1088
1067
|
value: string;
|
|
1089
1068
|
}) {
|
|
1090
1069
|
return (
|
|
1091
|
-
<div className="min-w-0
|
|
1070
|
+
<div className="min-w-0">
|
|
1092
1071
|
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
|
1093
1072
|
<Icon size={13} />
|
|
1094
1073
|
{label}
|
|
1095
1074
|
</div>
|
|
1096
|
-
<div className="mt-
|
|
1075
|
+
<div className="mt-0.5 truncate text-sm font-medium text-foreground">
|
|
1097
1076
|
{value}
|
|
1098
1077
|
</div>
|
|
1099
1078
|
</div>
|
|
@@ -1152,17 +1131,27 @@ function Modal({
|
|
|
1152
1131
|
open,
|
|
1153
1132
|
onClose,
|
|
1154
1133
|
children,
|
|
1134
|
+
fullscreen = false,
|
|
1155
1135
|
}: {
|
|
1156
1136
|
title: string;
|
|
1157
1137
|
description?: string;
|
|
1158
1138
|
open: boolean;
|
|
1159
1139
|
onClose: () => void;
|
|
1160
1140
|
children: ReactNode;
|
|
1141
|
+
fullscreen?: boolean;
|
|
1161
1142
|
}) {
|
|
1162
1143
|
return (
|
|
1163
1144
|
<Dialog open={open} onOpenChange={(next) => !next && onClose()}>
|
|
1164
|
-
<DialogContent
|
|
1165
|
-
|
|
1145
|
+
<DialogContent
|
|
1146
|
+
aria-describedby={undefined}
|
|
1147
|
+
className={cx(
|
|
1148
|
+
"p-0",
|
|
1149
|
+
fullscreen
|
|
1150
|
+
? "inset-0 flex h-[100dvh] max-h-none w-full max-w-none translate-x-0 translate-y-0 flex-col overflow-hidden rounded-none"
|
|
1151
|
+
: "max-h-[92vh] max-w-2xl overflow-y-auto",
|
|
1152
|
+
)}
|
|
1153
|
+
>
|
|
1154
|
+
<DialogHeader className="shrink-0 border-b p-4 pe-10 sm:px-8">
|
|
1166
1155
|
<DialogTitle className="text-base">{title}</DialogTitle>
|
|
1167
1156
|
{description ? (
|
|
1168
1157
|
<DialogDescription>{description}</DialogDescription>
|
|
@@ -1174,64 +1163,556 @@ function Modal({
|
|
|
1174
1163
|
);
|
|
1175
1164
|
}
|
|
1176
1165
|
|
|
1177
|
-
function
|
|
1166
|
+
function UserAccessControl({
|
|
1167
|
+
allUsers,
|
|
1168
|
+
selectedUsers,
|
|
1169
|
+
selectedUserGroups,
|
|
1170
|
+
groups,
|
|
1171
|
+
canManageGroups,
|
|
1172
|
+
onChange,
|
|
1173
|
+
onManageGroups,
|
|
1174
|
+
onEditGroup,
|
|
1175
|
+
}: {
|
|
1176
|
+
allUsers: boolean;
|
|
1177
|
+
selectedUsers: string[];
|
|
1178
|
+
selectedUserGroups: string[];
|
|
1179
|
+
groups: WorkspaceUserGroup[];
|
|
1180
|
+
canManageGroups: boolean;
|
|
1181
|
+
onChange: (next: {
|
|
1182
|
+
allUsers: boolean;
|
|
1183
|
+
selectedUsers: string[];
|
|
1184
|
+
selectedUserGroups: string[];
|
|
1185
|
+
}) => void;
|
|
1186
|
+
onManageGroups: () => void;
|
|
1187
|
+
onEditGroup: (group: WorkspaceUserGroup) => void;
|
|
1188
|
+
}) {
|
|
1189
|
+
const t = useT();
|
|
1190
|
+
const [open, setOpen] = useState(false);
|
|
1191
|
+
const [query, setQuery] = useState("");
|
|
1192
|
+
const memberSearch = useShareOrgMemberSearch(query, open && !allUsers, {
|
|
1193
|
+
limit: 100,
|
|
1194
|
+
});
|
|
1195
|
+
const selectedSet = useMemo(
|
|
1196
|
+
() => new Set(selectedUsers.map((email) => email.toLowerCase())),
|
|
1197
|
+
[selectedUsers],
|
|
1198
|
+
);
|
|
1199
|
+
const selectedGroupSet = useMemo(
|
|
1200
|
+
() => new Set(selectedUserGroups),
|
|
1201
|
+
[selectedUserGroups],
|
|
1202
|
+
);
|
|
1203
|
+
|
|
1204
|
+
function toggleMember(member: ShareOrgMember, checked: boolean) {
|
|
1205
|
+
const email = member.email.toLowerCase();
|
|
1206
|
+
const next = checked
|
|
1207
|
+
? Array.from(new Set([...selectedUsers, email]))
|
|
1208
|
+
: selectedUsers.filter((value) => value.toLowerCase() !== email);
|
|
1209
|
+
onChange({
|
|
1210
|
+
allUsers: false,
|
|
1211
|
+
selectedUsers: next,
|
|
1212
|
+
selectedUserGroups,
|
|
1213
|
+
});
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
function toggleGroup(groupId: string, checked: boolean) {
|
|
1217
|
+
const next = checked
|
|
1218
|
+
? Array.from(new Set([...selectedUserGroups, groupId]))
|
|
1219
|
+
: selectedUserGroups.filter((value) => value !== groupId);
|
|
1220
|
+
onChange({ allUsers: false, selectedUsers, selectedUserGroups: next });
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
const selectedGroupNames = groups
|
|
1224
|
+
.filter((group) => selectedGroupSet.has(group.id))
|
|
1225
|
+
.map((group) => group.name);
|
|
1226
|
+
|
|
1227
|
+
const summary = allUsers
|
|
1228
|
+
? t(
|
|
1229
|
+
/* i18n-key-ignore */
|
|
1230
|
+
"integrations.allWorkspaceMembers",
|
|
1231
|
+
{ defaultValue: "All workspace members" },
|
|
1232
|
+
)
|
|
1233
|
+
: [
|
|
1234
|
+
...selectedGroupNames,
|
|
1235
|
+
...(selectedUsers.length > 0
|
|
1236
|
+
? [
|
|
1237
|
+
t("integrations.selectedPeople", {
|
|
1238
|
+
count: selectedUsers.length,
|
|
1239
|
+
}),
|
|
1240
|
+
]
|
|
1241
|
+
: []),
|
|
1242
|
+
].join(", ") || t("integrations.choosePeopleOrGroups");
|
|
1243
|
+
|
|
1244
|
+
return (
|
|
1245
|
+
<details
|
|
1246
|
+
open={open}
|
|
1247
|
+
onToggle={(event) => setOpen(event.currentTarget.open)}
|
|
1248
|
+
className="group rounded-lg bg-muted/30"
|
|
1249
|
+
>
|
|
1250
|
+
<summary className="flex cursor-pointer list-none items-center justify-between gap-3 px-4 py-3 text-sm font-medium text-foreground marker:hidden">
|
|
1251
|
+
<span className="flex min-w-0 items-center gap-2">
|
|
1252
|
+
<IconUsersGroup className="size-4 shrink-0 text-muted-foreground" />
|
|
1253
|
+
<span className="truncate">
|
|
1254
|
+
{t(
|
|
1255
|
+
/* i18n-key-ignore */
|
|
1256
|
+
"integrations.peopleWithAccess",
|
|
1257
|
+
{ defaultValue: "People with access" },
|
|
1258
|
+
)}
|
|
1259
|
+
</span>
|
|
1260
|
+
<span className="truncate text-xs font-normal text-muted-foreground">
|
|
1261
|
+
{summary}
|
|
1262
|
+
</span>
|
|
1263
|
+
</span>
|
|
1264
|
+
<IconChevronDown className="size-4 shrink-0 text-muted-foreground transition-transform group-open:rotate-180" />
|
|
1265
|
+
</summary>
|
|
1266
|
+
<div className="grid gap-3 px-4 pb-4">
|
|
1267
|
+
<div className="flex items-center justify-between gap-4 rounded-md bg-muted/40 px-3 py-3">
|
|
1268
|
+
<div className="min-w-0">
|
|
1269
|
+
<p className="text-sm font-medium text-foreground">
|
|
1270
|
+
{t(
|
|
1271
|
+
/* i18n-key-ignore */
|
|
1272
|
+
"integrations.allWorkspaceMembers",
|
|
1273
|
+
{ defaultValue: "All workspace members" },
|
|
1274
|
+
)}
|
|
1275
|
+
</p>
|
|
1276
|
+
<p className="mt-0.5 text-xs leading-5 text-muted-foreground">
|
|
1277
|
+
{t(
|
|
1278
|
+
/* i18n-key-ignore */
|
|
1279
|
+
"integrations.allWorkspaceMembersDescription",
|
|
1280
|
+
{
|
|
1281
|
+
defaultValue: "Give access to everyone in this workspace.",
|
|
1282
|
+
},
|
|
1283
|
+
)}
|
|
1284
|
+
</p>
|
|
1285
|
+
</div>
|
|
1286
|
+
<Switch
|
|
1287
|
+
checked={allUsers}
|
|
1288
|
+
onCheckedChange={(checked) =>
|
|
1289
|
+
onChange({
|
|
1290
|
+
allUsers: checked,
|
|
1291
|
+
selectedUsers: checked ? [] : selectedUsers,
|
|
1292
|
+
selectedUserGroups: checked ? [] : selectedUserGroups,
|
|
1293
|
+
})
|
|
1294
|
+
}
|
|
1295
|
+
aria-label={t(
|
|
1296
|
+
/* i18n-key-ignore */
|
|
1297
|
+
"integrations.allWorkspaceMembersAria",
|
|
1298
|
+
{ defaultValue: "Allow all workspace members" },
|
|
1299
|
+
)}
|
|
1300
|
+
/>
|
|
1301
|
+
</div>
|
|
1302
|
+
|
|
1303
|
+
{!allUsers ? (
|
|
1304
|
+
<div className="grid gap-4">
|
|
1305
|
+
{canManageGroups ? (
|
|
1306
|
+
<div className="grid gap-2">
|
|
1307
|
+
<div className="flex items-center justify-between gap-3">
|
|
1308
|
+
<div>
|
|
1309
|
+
<p className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
1310
|
+
{t("integrations.groups")}
|
|
1311
|
+
</p>
|
|
1312
|
+
<p className="mt-0.5 text-xs text-muted-foreground">
|
|
1313
|
+
{t("integrations.groupsDescription")}
|
|
1314
|
+
</p>
|
|
1315
|
+
</div>
|
|
1316
|
+
<Button
|
|
1317
|
+
type="button"
|
|
1318
|
+
variant="ghost"
|
|
1319
|
+
size="sm"
|
|
1320
|
+
onClick={onManageGroups}
|
|
1321
|
+
className="h-7 px-2 text-xs"
|
|
1322
|
+
>
|
|
1323
|
+
<IconPlus size={13} />
|
|
1324
|
+
{t("integrations.newGroup")}
|
|
1325
|
+
</Button>
|
|
1326
|
+
</div>
|
|
1327
|
+
{groups.length > 0 ? (
|
|
1328
|
+
<div className="grid gap-1">
|
|
1329
|
+
{groups.map((group) => {
|
|
1330
|
+
const checked = selectedGroupSet.has(group.id);
|
|
1331
|
+
return (
|
|
1332
|
+
<div
|
|
1333
|
+
key={group.id}
|
|
1334
|
+
className="flex items-center gap-1 rounded-md bg-background/60 px-1 py-1 transition-colors hover:bg-accent/30"
|
|
1335
|
+
>
|
|
1336
|
+
<Label
|
|
1337
|
+
htmlFor={`connection-group-${group.id}`}
|
|
1338
|
+
className="flex min-w-0 flex-1 cursor-pointer items-center justify-between gap-3 px-2 py-1"
|
|
1339
|
+
>
|
|
1340
|
+
<span className="flex min-w-0 items-center gap-2">
|
|
1341
|
+
<IconUsersGroup
|
|
1342
|
+
size={14}
|
|
1343
|
+
className="shrink-0 text-muted-foreground"
|
|
1344
|
+
/>
|
|
1345
|
+
<span className="truncate text-sm font-medium">
|
|
1346
|
+
{group.name}
|
|
1347
|
+
</span>
|
|
1348
|
+
<Pill className="border-0 bg-muted px-1.5 text-[11px] text-muted-foreground">
|
|
1349
|
+
{group.memberEmails.length}
|
|
1350
|
+
</Pill>
|
|
1351
|
+
</span>
|
|
1352
|
+
<Checkbox
|
|
1353
|
+
id={`connection-group-${group.id}`}
|
|
1354
|
+
checked={checked}
|
|
1355
|
+
onCheckedChange={(value) =>
|
|
1356
|
+
toggleGroup(group.id, value === true)
|
|
1357
|
+
}
|
|
1358
|
+
aria-label={group.name}
|
|
1359
|
+
/>
|
|
1360
|
+
</Label>
|
|
1361
|
+
<Button
|
|
1362
|
+
type="button"
|
|
1363
|
+
variant="ghost"
|
|
1364
|
+
size="icon"
|
|
1365
|
+
className="size-7 shrink-0"
|
|
1366
|
+
onClick={() => onEditGroup(group)}
|
|
1367
|
+
aria-label={t("integrations.editGroupAria", {
|
|
1368
|
+
name: group.name,
|
|
1369
|
+
})}
|
|
1370
|
+
>
|
|
1371
|
+
<IconEdit size={13} />
|
|
1372
|
+
</Button>
|
|
1373
|
+
</div>
|
|
1374
|
+
);
|
|
1375
|
+
})}
|
|
1376
|
+
</div>
|
|
1377
|
+
) : (
|
|
1378
|
+
<Button
|
|
1379
|
+
type="button"
|
|
1380
|
+
variant="ghost"
|
|
1381
|
+
onClick={onManageGroups}
|
|
1382
|
+
className="h-auto justify-start rounded-md bg-background/60 px-3 py-2 text-xs text-muted-foreground"
|
|
1383
|
+
>
|
|
1384
|
+
<IconPlus size={13} />
|
|
1385
|
+
{t("integrations.createGroupCta")}
|
|
1386
|
+
</Button>
|
|
1387
|
+
)}
|
|
1388
|
+
</div>
|
|
1389
|
+
) : null}
|
|
1390
|
+
|
|
1391
|
+
<div className="grid gap-2">
|
|
1392
|
+
<p className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
1393
|
+
{t("integrations.people")}
|
|
1394
|
+
</p>
|
|
1395
|
+
<div className="relative">
|
|
1396
|
+
<IconSearch className="pointer-events-none absolute start-3 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" />
|
|
1397
|
+
<Input
|
|
1398
|
+
value={query}
|
|
1399
|
+
onChange={(event) => setQuery(event.target.value)}
|
|
1400
|
+
placeholder={t(
|
|
1401
|
+
/* i18n-key-ignore */
|
|
1402
|
+
"integrations.searchPeoplePlaceholder",
|
|
1403
|
+
{ defaultValue: "Search people" },
|
|
1404
|
+
)}
|
|
1405
|
+
aria-label={t(
|
|
1406
|
+
/* i18n-key-ignore */
|
|
1407
|
+
"integrations.searchPeopleLabel",
|
|
1408
|
+
{ defaultValue: "Search workspace members" },
|
|
1409
|
+
)}
|
|
1410
|
+
className="h-9 ps-9"
|
|
1411
|
+
/>
|
|
1412
|
+
</div>
|
|
1413
|
+
{memberSearch.isLoading ? (
|
|
1414
|
+
<p className="px-1 text-xs text-muted-foreground">
|
|
1415
|
+
{t(
|
|
1416
|
+
/* i18n-key-ignore */
|
|
1417
|
+
"integrations.loadingPeople",
|
|
1418
|
+
{ defaultValue: "Loading people..." },
|
|
1419
|
+
)}
|
|
1420
|
+
</p>
|
|
1421
|
+
) : memberSearch.error ? (
|
|
1422
|
+
<p className="px-1 text-xs text-destructive">
|
|
1423
|
+
{t(
|
|
1424
|
+
/* i18n-key-ignore */
|
|
1425
|
+
"integrations.peopleLoadError",
|
|
1426
|
+
{ defaultValue: "Could not load workspace members." },
|
|
1427
|
+
)}
|
|
1428
|
+
</p>
|
|
1429
|
+
) : memberSearch.members.length > 0 ? (
|
|
1430
|
+
<div className="grid gap-1">
|
|
1431
|
+
{memberSearch.members.map((member) => {
|
|
1432
|
+
const checked = selectedSet.has(member.email.toLowerCase());
|
|
1433
|
+
return (
|
|
1434
|
+
<Label
|
|
1435
|
+
key={member.email}
|
|
1436
|
+
htmlFor={`connection-user-${member.email}`}
|
|
1437
|
+
className="flex cursor-pointer items-center justify-between gap-3 rounded-md bg-background/60 px-3 py-2 transition-colors hover:bg-accent/30"
|
|
1438
|
+
>
|
|
1439
|
+
<span className="min-w-0">
|
|
1440
|
+
<span className="block truncate text-sm font-medium">
|
|
1441
|
+
{member.name || member.email}
|
|
1442
|
+
</span>
|
|
1443
|
+
{member.name ? (
|
|
1444
|
+
<span className="block truncate text-xs text-muted-foreground">
|
|
1445
|
+
{member.email}
|
|
1446
|
+
</span>
|
|
1447
|
+
) : null}
|
|
1448
|
+
</span>
|
|
1449
|
+
<Checkbox
|
|
1450
|
+
id={`connection-user-${member.email}`}
|
|
1451
|
+
checked={checked}
|
|
1452
|
+
onCheckedChange={(value) =>
|
|
1453
|
+
toggleMember(member, value === true)
|
|
1454
|
+
}
|
|
1455
|
+
aria-label={member.email}
|
|
1456
|
+
/>
|
|
1457
|
+
</Label>
|
|
1458
|
+
);
|
|
1459
|
+
})}
|
|
1460
|
+
</div>
|
|
1461
|
+
) : (
|
|
1462
|
+
<p className="px-1 text-xs text-muted-foreground">
|
|
1463
|
+
{t(
|
|
1464
|
+
/* i18n-key-ignore */
|
|
1465
|
+
"integrations.noPeopleFound",
|
|
1466
|
+
{ defaultValue: "No workspace members found." },
|
|
1467
|
+
)}
|
|
1468
|
+
</p>
|
|
1469
|
+
)}
|
|
1470
|
+
{memberSearch.hasMore ? (
|
|
1471
|
+
<Button
|
|
1472
|
+
type="button"
|
|
1473
|
+
variant="ghost"
|
|
1474
|
+
size="sm"
|
|
1475
|
+
onClick={memberSearch.loadMore}
|
|
1476
|
+
disabled={memberSearch.isLoadingMore}
|
|
1477
|
+
className="w-fit text-xs"
|
|
1478
|
+
>
|
|
1479
|
+
{t(
|
|
1480
|
+
/* i18n-key-ignore */
|
|
1481
|
+
"integrations.loadMorePeople",
|
|
1482
|
+
{ defaultValue: "Load more" },
|
|
1483
|
+
)}
|
|
1484
|
+
</Button>
|
|
1485
|
+
) : null}
|
|
1486
|
+
</div>
|
|
1487
|
+
</div>
|
|
1488
|
+
) : null}
|
|
1489
|
+
</div>
|
|
1490
|
+
</details>
|
|
1491
|
+
);
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
function GroupEditor({
|
|
1178
1495
|
open,
|
|
1179
|
-
|
|
1180
|
-
providers,
|
|
1181
|
-
grantApps,
|
|
1496
|
+
group,
|
|
1182
1497
|
saving,
|
|
1183
|
-
onChange,
|
|
1184
1498
|
onClose,
|
|
1185
|
-
|
|
1499
|
+
onSave,
|
|
1186
1500
|
}: {
|
|
1187
1501
|
open: boolean;
|
|
1188
|
-
|
|
1189
|
-
providers: WorkspaceConnectionProvider[];
|
|
1190
|
-
grantApps: GrantApp[];
|
|
1502
|
+
group: WorkspaceUserGroup | null;
|
|
1191
1503
|
saving: boolean;
|
|
1192
|
-
onChange: (form: ConnectionFormState) => void;
|
|
1193
1504
|
onClose: () => void;
|
|
1194
|
-
|
|
1505
|
+
onSave: (name: string, memberEmails: string[]) => void;
|
|
1195
1506
|
}) {
|
|
1196
1507
|
const t = useT();
|
|
1197
|
-
|
|
1198
|
-
const
|
|
1199
|
-
const
|
|
1200
|
-
const
|
|
1201
|
-
|
|
1202
|
-
|
|
1508
|
+
const [name, setName] = useState("");
|
|
1509
|
+
const [members, setMembers] = useState<string[]>([]);
|
|
1510
|
+
const [query, setQuery] = useState("");
|
|
1511
|
+
const memberSearch = useShareOrgMemberSearch(query, open, { limit: 100 });
|
|
1512
|
+
const selectedSet = useMemo(
|
|
1513
|
+
() => new Set(members.map((email) => email.toLowerCase())),
|
|
1514
|
+
[members],
|
|
1203
1515
|
);
|
|
1516
|
+
|
|
1517
|
+
useEffect(() => {
|
|
1518
|
+
if (!open) return;
|
|
1519
|
+
setName(group?.name ?? "");
|
|
1520
|
+
setMembers(group?.memberEmails ?? []);
|
|
1521
|
+
setQuery("");
|
|
1522
|
+
}, [group, open]);
|
|
1523
|
+
|
|
1524
|
+
function toggleMember(email: string, checked: boolean) {
|
|
1525
|
+
const normalized = email.toLowerCase();
|
|
1526
|
+
setMembers((current) =>
|
|
1527
|
+
checked
|
|
1528
|
+
? Array.from(new Set([...current, normalized]))
|
|
1529
|
+
: current.filter((value) => value !== normalized),
|
|
1530
|
+
);
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1204
1533
|
return (
|
|
1205
1534
|
<Modal
|
|
1206
1535
|
open={open}
|
|
1207
1536
|
onClose={onClose}
|
|
1208
|
-
title={
|
|
1209
|
-
|
|
1210
|
-
? t("integrations.editConnection")
|
|
1211
|
-
: t("integrations.newConnection")
|
|
1212
|
-
}
|
|
1213
|
-
description={provider?.description}
|
|
1537
|
+
title={t(group ? "integrations.editGroup" : "integrations.createGroup")}
|
|
1538
|
+
description={t("integrations.groupDescription")}
|
|
1214
1539
|
>
|
|
1215
|
-
<form
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
}
|
|
1540
|
+
<form
|
|
1541
|
+
className="flex min-h-0 flex-1 flex-col"
|
|
1542
|
+
onSubmit={(event) => {
|
|
1543
|
+
event.preventDefault();
|
|
1544
|
+
onSave(name.trim(), members);
|
|
1545
|
+
}}
|
|
1546
|
+
>
|
|
1547
|
+
<div className="min-h-0 flex-1 space-y-5 overflow-y-auto p-5">
|
|
1548
|
+
<TextField
|
|
1549
|
+
label={t("integrations.groupName")}
|
|
1550
|
+
value={name}
|
|
1551
|
+
onChange={setName}
|
|
1552
|
+
placeholder={t("integrations.groupNamePlaceholder")}
|
|
1553
|
+
required
|
|
1554
|
+
/>
|
|
1555
|
+
|
|
1556
|
+
<div className="grid gap-2">
|
|
1557
|
+
<div className="flex items-end justify-between gap-3">
|
|
1558
|
+
<div>
|
|
1559
|
+
<Label>{t("integrations.members")}</Label>
|
|
1560
|
+
<p className="mt-1 text-xs text-muted-foreground">
|
|
1561
|
+
{t("integrations.groupMembersDescription")}
|
|
1562
|
+
</p>
|
|
1563
|
+
</div>
|
|
1564
|
+
<Pill className="border-0 bg-muted text-muted-foreground">
|
|
1565
|
+
{t("integrations.selectedMembers", { count: members.length })}
|
|
1566
|
+
</Pill>
|
|
1567
|
+
</div>
|
|
1568
|
+
<div className="relative">
|
|
1569
|
+
<IconSearch className="pointer-events-none absolute start-3 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" />
|
|
1570
|
+
<Input
|
|
1571
|
+
value={query}
|
|
1572
|
+
onChange={(event) => setQuery(event.target.value)}
|
|
1573
|
+
placeholder={t("integrations.searchPeoplePlaceholder")}
|
|
1574
|
+
aria-label={t("integrations.searchPeopleLabel")}
|
|
1575
|
+
className="h-9 ps-9"
|
|
1576
|
+
/>
|
|
1577
|
+
</div>
|
|
1578
|
+
{memberSearch.isLoading ? (
|
|
1579
|
+
<p className="px-1 text-xs text-muted-foreground">
|
|
1580
|
+
{t("integrations.loadingPeople")}
|
|
1581
|
+
</p>
|
|
1582
|
+
) : memberSearch.error ? (
|
|
1583
|
+
<p className="px-1 text-xs text-destructive">
|
|
1584
|
+
{t("integrations.peopleLoadError")}
|
|
1585
|
+
</p>
|
|
1586
|
+
) : (
|
|
1587
|
+
<div className="grid gap-1">
|
|
1588
|
+
{memberSearch.members.map((member) => {
|
|
1589
|
+
const checked = selectedSet.has(member.email.toLowerCase());
|
|
1590
|
+
return (
|
|
1591
|
+
<Label
|
|
1592
|
+
key={member.email}
|
|
1593
|
+
htmlFor={`group-member-${member.email}`}
|
|
1594
|
+
className="flex cursor-pointer items-center justify-between gap-3 rounded-md bg-muted/35 px-3 py-2 transition-colors hover:bg-muted/55"
|
|
1595
|
+
>
|
|
1596
|
+
<span className="min-w-0">
|
|
1597
|
+
<span className="block truncate text-sm font-medium">
|
|
1598
|
+
{member.name || member.email}
|
|
1599
|
+
</span>
|
|
1600
|
+
{member.name ? (
|
|
1601
|
+
<span className="block truncate text-xs text-muted-foreground">
|
|
1602
|
+
{member.email}
|
|
1603
|
+
</span>
|
|
1604
|
+
) : null}
|
|
1605
|
+
</span>
|
|
1606
|
+
<Checkbox
|
|
1607
|
+
id={`group-member-${member.email}`}
|
|
1608
|
+
checked={checked}
|
|
1609
|
+
onCheckedChange={(value) =>
|
|
1610
|
+
toggleMember(member.email, value === true)
|
|
1611
|
+
}
|
|
1612
|
+
aria-label={member.email}
|
|
1613
|
+
/>
|
|
1614
|
+
</Label>
|
|
1615
|
+
);
|
|
1616
|
+
})}
|
|
1617
|
+
</div>
|
|
1618
|
+
)}
|
|
1619
|
+
{memberSearch.hasMore ? (
|
|
1620
|
+
<Button
|
|
1621
|
+
type="button"
|
|
1622
|
+
variant="ghost"
|
|
1623
|
+
size="sm"
|
|
1624
|
+
onClick={memberSearch.loadMore}
|
|
1625
|
+
disabled={memberSearch.isLoadingMore}
|
|
1626
|
+
className="w-fit text-xs"
|
|
1627
|
+
>
|
|
1628
|
+
{t("integrations.loadMorePeople")}
|
|
1629
|
+
</Button>
|
|
1630
|
+
) : null}
|
|
1631
|
+
</div>
|
|
1632
|
+
</div>
|
|
1633
|
+
<DialogFooter className="shrink-0 border-t p-4">
|
|
1634
|
+
<Button type="button" variant="ghost" onClick={onClose}>
|
|
1635
|
+
{t("integrations.cancel")}
|
|
1636
|
+
</Button>
|
|
1637
|
+
<Button type="submit" disabled={saving || !name.trim()}>
|
|
1638
|
+
{saving ? <IconRefresh size={14} className="animate-spin" /> : null}
|
|
1639
|
+
{t(group ? "integrations.saveGroup" : "integrations.createGroup")}
|
|
1640
|
+
</Button>
|
|
1641
|
+
</DialogFooter>
|
|
1642
|
+
</form>
|
|
1643
|
+
</Modal>
|
|
1644
|
+
);
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
function ConnectionForm({
|
|
1648
|
+
open,
|
|
1649
|
+
form,
|
|
1650
|
+
providers,
|
|
1651
|
+
grantApps,
|
|
1652
|
+
groups,
|
|
1653
|
+
canManageGroups,
|
|
1654
|
+
canManageAllApps,
|
|
1655
|
+
saving,
|
|
1656
|
+
onChange,
|
|
1657
|
+
onClose,
|
|
1658
|
+
onSubmit,
|
|
1659
|
+
onManageGroups,
|
|
1660
|
+
onEditGroup,
|
|
1661
|
+
}: {
|
|
1662
|
+
open: boolean;
|
|
1663
|
+
form: ConnectionFormState | null;
|
|
1664
|
+
providers: WorkspaceConnectionProvider[];
|
|
1665
|
+
grantApps: GrantApp[];
|
|
1666
|
+
groups: WorkspaceUserGroup[];
|
|
1667
|
+
canManageGroups: boolean;
|
|
1668
|
+
canManageAllApps: boolean;
|
|
1669
|
+
saving: boolean;
|
|
1670
|
+
onChange: (form: ConnectionFormState) => void;
|
|
1671
|
+
onClose: () => void;
|
|
1672
|
+
onSubmit: (event: FormEvent<HTMLFormElement>) => void;
|
|
1673
|
+
onManageGroups: () => void;
|
|
1674
|
+
onEditGroup: (group: WorkspaceUserGroup) => void;
|
|
1675
|
+
}) {
|
|
1676
|
+
const t = useT();
|
|
1677
|
+
if (!form) return null;
|
|
1678
|
+
const provider = providers.find((item) => item.id === form.provider);
|
|
1679
|
+
const credentialRefs = normalizeCredentialRefs(form.credentialRefs, provider);
|
|
1680
|
+
const missingCredentialRefs = missingRequiredCredentialKeys(
|
|
1681
|
+
provider,
|
|
1682
|
+
credentialRefs,
|
|
1683
|
+
);
|
|
1684
|
+
return (
|
|
1685
|
+
<Modal
|
|
1686
|
+
open={open}
|
|
1687
|
+
onClose={onClose}
|
|
1688
|
+
fullscreen
|
|
1689
|
+
title={
|
|
1690
|
+
form.id
|
|
1691
|
+
? t("integrations.editConnection")
|
|
1692
|
+
: t("integrations.newConnection")
|
|
1693
|
+
}
|
|
1694
|
+
description={provider?.description}
|
|
1695
|
+
>
|
|
1696
|
+
<form onSubmit={onSubmit}>
|
|
1697
|
+
<div className="grid gap-4 p-4">
|
|
1698
|
+
<div className="grid gap-3 sm:grid-cols-2">
|
|
1699
|
+
<div className="grid gap-1.5 text-sm">
|
|
1700
|
+
<Label>{t("integrations.provider")}</Label>
|
|
1701
|
+
<Select
|
|
1702
|
+
value={form.provider}
|
|
1703
|
+
onValueChange={(value) => {
|
|
1704
|
+
const nextProvider = providers.find(
|
|
1705
|
+
(item) => item.id === value,
|
|
1706
|
+
);
|
|
1707
|
+
onChange({
|
|
1708
|
+
...form,
|
|
1709
|
+
provider: value,
|
|
1710
|
+
label: form.label || nextProvider?.label || value,
|
|
1711
|
+
credentialRefs: nextProvider
|
|
1712
|
+
? credentialRefsForProvider(nextProvider)
|
|
1713
|
+
: form.credentialRefs,
|
|
1714
|
+
});
|
|
1715
|
+
}}
|
|
1235
1716
|
>
|
|
1236
1717
|
<SelectTrigger>
|
|
1237
1718
|
<SelectValue placeholder={t("integrations.chooseProvider")} />
|
|
@@ -1300,27 +1781,43 @@ function ConnectionForm({
|
|
|
1300
1781
|
/>
|
|
1301
1782
|
</div>
|
|
1302
1783
|
|
|
1303
|
-
<div className="rounded-md
|
|
1304
|
-
|
|
1784
|
+
<div className="rounded-md bg-muted/30 p-3">
|
|
1785
|
+
{canManageAllApps ? (
|
|
1786
|
+
<div className="flex items-center justify-between gap-3">
|
|
1787
|
+
<div>
|
|
1788
|
+
<div className="text-sm font-medium">
|
|
1789
|
+
{t("integrations.accessMode")}
|
|
1790
|
+
</div>
|
|
1791
|
+
<div className="text-xs text-muted-foreground">
|
|
1792
|
+
{form.allApps
|
|
1793
|
+
? t("integrations.allAppsCanReuseConnection")
|
|
1794
|
+
: t("integrations.onlySelectedAppsCanReuseConnection")}
|
|
1795
|
+
</div>
|
|
1796
|
+
</div>
|
|
1797
|
+
<Switch
|
|
1798
|
+
checked={form.allApps}
|
|
1799
|
+
onCheckedChange={(checked) =>
|
|
1800
|
+
onChange({ ...form, allApps: checked })
|
|
1801
|
+
}
|
|
1802
|
+
aria-label={t("integrations.grantAllWorkspaceAppsAria")}
|
|
1803
|
+
/>
|
|
1804
|
+
</div>
|
|
1805
|
+
) : (
|
|
1305
1806
|
<div>
|
|
1306
1807
|
<div className="text-sm font-medium">
|
|
1307
|
-
{t("integrations.
|
|
1808
|
+
{t("integrations.dispatchOnlyAccess", {
|
|
1809
|
+
defaultValue: "Dispatch only",
|
|
1810
|
+
})}
|
|
1308
1811
|
</div>
|
|
1309
1812
|
<div className="text-xs text-muted-foreground">
|
|
1310
|
-
{
|
|
1311
|
-
|
|
1312
|
-
|
|
1813
|
+
{t("integrations.dispatchOnlyAccessDescription", {
|
|
1814
|
+
defaultValue:
|
|
1815
|
+
"You can manage connections for Dispatch. Organization admins can share them with other apps.",
|
|
1816
|
+
})}
|
|
1313
1817
|
</div>
|
|
1314
1818
|
</div>
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
onCheckedChange={(checked) =>
|
|
1318
|
-
onChange({ ...form, allApps: checked })
|
|
1319
|
-
}
|
|
1320
|
-
aria-label={t("integrations.grantAllWorkspaceAppsAria")}
|
|
1321
|
-
/>
|
|
1322
|
-
</div>
|
|
1323
|
-
{!form.allApps ? (
|
|
1819
|
+
)}
|
|
1820
|
+
{!form.allApps && canManageAllApps ? (
|
|
1324
1821
|
<div className="mt-3 flex flex-wrap gap-2">
|
|
1325
1822
|
{grantApps.map((app) => {
|
|
1326
1823
|
const AppIcon = app.icon;
|
|
@@ -1354,6 +1851,24 @@ function ConnectionForm({
|
|
|
1354
1851
|
) : null}
|
|
1355
1852
|
</div>
|
|
1356
1853
|
|
|
1854
|
+
<UserAccessControl
|
|
1855
|
+
allUsers={form.allUsers}
|
|
1856
|
+
selectedUsers={form.selectedUsers}
|
|
1857
|
+
selectedUserGroups={form.selectedUserGroups}
|
|
1858
|
+
groups={groups}
|
|
1859
|
+
canManageGroups={canManageGroups}
|
|
1860
|
+
onManageGroups={onManageGroups}
|
|
1861
|
+
onEditGroup={onEditGroup}
|
|
1862
|
+
onChange={({ allUsers, selectedUsers, selectedUserGroups }) =>
|
|
1863
|
+
onChange({
|
|
1864
|
+
...form,
|
|
1865
|
+
allUsers,
|
|
1866
|
+
selectedUsers,
|
|
1867
|
+
selectedUserGroups,
|
|
1868
|
+
})
|
|
1869
|
+
}
|
|
1870
|
+
/>
|
|
1871
|
+
|
|
1357
1872
|
<CredentialRefsEditor
|
|
1358
1873
|
provider={provider}
|
|
1359
1874
|
refs={form.credentialRefs}
|
|
@@ -1410,14 +1925,47 @@ function CredentialRefsEditor({
|
|
|
1410
1925
|
provider,
|
|
1411
1926
|
refs,
|
|
1412
1927
|
missingRefs,
|
|
1928
|
+
progressive = false,
|
|
1413
1929
|
onChange,
|
|
1414
1930
|
}: {
|
|
1415
1931
|
provider?: WorkspaceConnectionProvider;
|
|
1416
1932
|
refs: WorkspaceConnectionCredentialRef[];
|
|
1417
1933
|
missingRefs: string[];
|
|
1934
|
+
progressive?: boolean;
|
|
1418
1935
|
onChange: (refs: WorkspaceConnectionCredentialRef[]) => void;
|
|
1419
1936
|
}) {
|
|
1420
1937
|
const t = useT();
|
|
1938
|
+
if (
|
|
1939
|
+
progressive &&
|
|
1940
|
+
refs.length === 0 &&
|
|
1941
|
+
provider?.credentialKeys.length === 0
|
|
1942
|
+
) {
|
|
1943
|
+
return (
|
|
1944
|
+
<div className="grid gap-5">
|
|
1945
|
+
<div>
|
|
1946
|
+
<h2 className="text-lg font-semibold tracking-[-0.02em] text-foreground">
|
|
1947
|
+
{t("integrations.credentialRefs")}
|
|
1948
|
+
</h2>
|
|
1949
|
+
<p className="mt-1 text-sm leading-6 text-muted-foreground">
|
|
1950
|
+
{t(
|
|
1951
|
+
/* i18n-key-ignore */
|
|
1952
|
+
"integrations.noCredentialRefs",
|
|
1953
|
+
{ defaultValue: "No keys are needed for this connection." },
|
|
1954
|
+
)}
|
|
1955
|
+
</p>
|
|
1956
|
+
</div>
|
|
1957
|
+
<Button
|
|
1958
|
+
type="button"
|
|
1959
|
+
variant="outline"
|
|
1960
|
+
className="w-fit"
|
|
1961
|
+
onClick={() => onChange(appendCredentialRef([], provider))}
|
|
1962
|
+
>
|
|
1963
|
+
<IconPlus size={14} />
|
|
1964
|
+
{t("integrations.addRef")}
|
|
1965
|
+
</Button>
|
|
1966
|
+
</div>
|
|
1967
|
+
);
|
|
1968
|
+
}
|
|
1421
1969
|
const providerKeys = new Map(
|
|
1422
1970
|
provider?.credentialKeys.map((credential) => [
|
|
1423
1971
|
credential.key,
|
|
@@ -1427,7 +1975,7 @@ function CredentialRefsEditor({
|
|
|
1427
1975
|
const rows = refs.length > 0 ? refs : appendCredentialRef([], provider);
|
|
1428
1976
|
|
|
1429
1977
|
return (
|
|
1430
|
-
<div className="rounded-md
|
|
1978
|
+
<div className="rounded-md bg-muted/30 p-3">
|
|
1431
1979
|
<div className="flex flex-wrap items-start justify-between gap-3">
|
|
1432
1980
|
<div>
|
|
1433
1981
|
<div className="text-sm font-medium">
|
|
@@ -1454,7 +2002,7 @@ function CredentialRefsEditor({
|
|
|
1454
2002
|
return (
|
|
1455
2003
|
<div
|
|
1456
2004
|
key={`${index}:${ref.provider ?? provider?.id ?? "provider"}`}
|
|
1457
|
-
className="grid gap-2 rounded-md
|
|
2005
|
+
className="grid gap-2 rounded-md bg-muted/40 px-3 py-3 sm:grid-cols-[minmax(0,1fr)_8rem_auto]"
|
|
1458
2006
|
>
|
|
1459
2007
|
<div className="grid min-w-0 gap-1.5 text-sm">
|
|
1460
2008
|
<Label className="flex items-center gap-1.5">
|
|
@@ -1561,13 +2109,17 @@ function SetupWizard({
|
|
|
1561
2109
|
form,
|
|
1562
2110
|
providers,
|
|
1563
2111
|
grantApps,
|
|
2112
|
+
groups,
|
|
2113
|
+
canManageGroups,
|
|
2114
|
+
canManageAllApps,
|
|
1564
2115
|
loading,
|
|
1565
2116
|
saving,
|
|
1566
2117
|
onStepChange,
|
|
1567
|
-
onProviderChange,
|
|
1568
2118
|
onChange,
|
|
1569
2119
|
onClose,
|
|
1570
2120
|
onSubmit,
|
|
2121
|
+
onManageGroups,
|
|
2122
|
+
onEditGroup,
|
|
1571
2123
|
}: {
|
|
1572
2124
|
open: boolean;
|
|
1573
2125
|
state: SetupWizardState | null;
|
|
@@ -1576,13 +2128,17 @@ function SetupWizard({
|
|
|
1576
2128
|
form: SetupWizardFormState | null;
|
|
1577
2129
|
providers: WorkspaceConnectionProvider[];
|
|
1578
2130
|
grantApps: GrantApp[];
|
|
2131
|
+
groups: WorkspaceUserGroup[];
|
|
2132
|
+
canManageGroups: boolean;
|
|
2133
|
+
canManageAllApps: boolean;
|
|
1579
2134
|
loading: boolean;
|
|
1580
2135
|
saving: boolean;
|
|
1581
2136
|
onStepChange: (step: number) => void;
|
|
1582
|
-
onProviderChange: (providerId: string) => void;
|
|
1583
2137
|
onChange: (form: SetupWizardFormState) => void;
|
|
1584
2138
|
onClose: () => void;
|
|
1585
2139
|
onSubmit: () => void;
|
|
2140
|
+
onManageGroups: () => void;
|
|
2141
|
+
onEditGroup: (group: WorkspaceUserGroup) => void;
|
|
1586
2142
|
}) {
|
|
1587
2143
|
const t = useT();
|
|
1588
2144
|
const provider =
|
|
@@ -1594,18 +2150,34 @@ function SetupWizard({
|
|
|
1594
2150
|
provider,
|
|
1595
2151
|
credentialRefs,
|
|
1596
2152
|
);
|
|
2153
|
+
const visiblePlanWarnings = (plan?.warnings ?? []).filter(
|
|
2154
|
+
(warning) =>
|
|
2155
|
+
!warning.startsWith("Missing required credential refs:") ||
|
|
2156
|
+
missingCredentialRefs.length > 0,
|
|
2157
|
+
);
|
|
2158
|
+
const hasCredentialStep = Boolean(
|
|
2159
|
+
provider &&
|
|
2160
|
+
(provider.credentialKeys.length > 0 || credentialRefs.length > 0),
|
|
2161
|
+
);
|
|
1597
2162
|
const selectedApps = form?.selectedApps ?? [];
|
|
2163
|
+
const selectedUsers = form?.selectedUsers ?? [];
|
|
2164
|
+
const selectedUserGroups = form?.selectedUserGroups ?? [];
|
|
1598
2165
|
const stepItems = [
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
2166
|
+
t("integrations.provider"),
|
|
2167
|
+
...(hasCredentialStep ? [t("integrations.refs")] : []),
|
|
2168
|
+
t("integrations.access"),
|
|
1602
2169
|
];
|
|
1603
|
-
const
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
2170
|
+
const isProviderStep = step === 0;
|
|
2171
|
+
const isCredentialStep = hasCredentialStep && step === 1;
|
|
2172
|
+
const isAccessStep = step === stepItems.length - 1;
|
|
2173
|
+
const canAdvance = isProviderStep
|
|
2174
|
+
? Boolean(form?.label.trim())
|
|
2175
|
+
: isCredentialStep
|
|
2176
|
+
? missingCredentialRefs.length === 0
|
|
2177
|
+
: (form?.grantMode === "all-apps" || selectedApps.length > 0) &&
|
|
2178
|
+
(form?.userGrantMode === "all-users" ||
|
|
2179
|
+
selectedUsers.length > 0 ||
|
|
2180
|
+
selectedUserGroups.length > 0);
|
|
1609
2181
|
const suggestedGrantApps = useMemo(() => {
|
|
1610
2182
|
const map = new Map<
|
|
1611
2183
|
string,
|
|
@@ -1630,11 +2202,30 @@ function SetupWizard({
|
|
|
1630
2202
|
}
|
|
1631
2203
|
return Array.from(map.values());
|
|
1632
2204
|
}, [grantApps, plan?.suggestedApps]);
|
|
2205
|
+
const availableGrantApps = canManageAllApps
|
|
2206
|
+
? suggestedGrantApps
|
|
2207
|
+
: suggestedGrantApps.filter((app) => app.id.toLowerCase() === "dispatch");
|
|
2208
|
+
const selectedAppLabels = availableGrantApps
|
|
2209
|
+
.filter((app) => selectedApps.includes(app.id))
|
|
2210
|
+
.map((app) => app.label);
|
|
2211
|
+
const accessSummary =
|
|
2212
|
+
form?.grantMode === "all-apps"
|
|
2213
|
+
? t("integrations.allApps")
|
|
2214
|
+
: t("integrations.selectedApps");
|
|
2215
|
+
const accessSummaryDescription =
|
|
2216
|
+
form?.grantMode === "all-apps"
|
|
2217
|
+
? t("integrations.everyWorkspaceAppCanReuse")
|
|
2218
|
+
: selectedAppLabels.length > 0
|
|
2219
|
+
? t("integrations.selectedAppsCanReuse", {
|
|
2220
|
+
apps: selectedAppLabels.join(", "),
|
|
2221
|
+
})
|
|
2222
|
+
: t("integrations.chooseOneApp");
|
|
1633
2223
|
|
|
1634
2224
|
return (
|
|
1635
2225
|
<Modal
|
|
1636
2226
|
open={open}
|
|
1637
2227
|
onClose={onClose}
|
|
2228
|
+
fullscreen
|
|
1638
2229
|
title={
|
|
1639
2230
|
state?.mode === "repair"
|
|
1640
2231
|
? t("integrations.repairConnectionTitle", {
|
|
@@ -1647,342 +2238,346 @@ function SetupWizard({
|
|
|
1647
2238
|
provider: provider?.label || t("integrations.providerFallback"),
|
|
1648
2239
|
})
|
|
1649
2240
|
}
|
|
1650
|
-
description={
|
|
2241
|
+
description={undefined}
|
|
1651
2242
|
>
|
|
1652
|
-
<div className="
|
|
1653
|
-
<div className="grid
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
<
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
: "border-border bg-background text-muted-foreground",
|
|
1670
|
-
)}
|
|
1671
|
-
>
|
|
1672
|
-
{complete ? <IconCheck size={14} /> : <Icon size={14} />}
|
|
1673
|
-
<span className="truncate">{item.label}</span>
|
|
1674
|
-
</button>
|
|
1675
|
-
);
|
|
1676
|
-
})}
|
|
1677
|
-
</div>
|
|
1678
|
-
|
|
1679
|
-
{loading ? (
|
|
1680
|
-
<div className="rounded-lg border border-dashed px-6 py-10 text-center text-sm text-muted-foreground">
|
|
1681
|
-
{t("integrations.loadingSetupPlan")}
|
|
2243
|
+
<div className="min-h-0 flex-1 overflow-y-auto">
|
|
2244
|
+
<div className="mx-auto grid w-full max-w-2xl gap-8 px-6 py-8 sm:px-10 sm:py-12">
|
|
2245
|
+
<div className="grid gap-2">
|
|
2246
|
+
<div className="flex items-center justify-between gap-4 text-xs text-muted-foreground">
|
|
2247
|
+
<span>
|
|
2248
|
+
Step {step + 1} of {stepItems.length}
|
|
2249
|
+
</span>
|
|
2250
|
+
<span className="font-medium text-foreground">
|
|
2251
|
+
{stepItems[step]}
|
|
2252
|
+
</span>
|
|
2253
|
+
</div>
|
|
2254
|
+
<div className="h-1 overflow-hidden rounded-full bg-muted">
|
|
2255
|
+
<div
|
|
2256
|
+
className="h-full rounded-full bg-primary transition-[width] duration-200 ease-[var(--ease-out-strong)]"
|
|
2257
|
+
style={{ width: `${((step + 1) / stepItems.length) * 100}%` }}
|
|
2258
|
+
/>
|
|
2259
|
+
</div>
|
|
1682
2260
|
</div>
|
|
1683
|
-
) : null}
|
|
1684
2261
|
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
<div
|
|
1691
|
-
key={warning}
|
|
1692
|
-
className="flex items-start gap-2 rounded-md border border-amber-500/20 bg-amber-500/10 px-3 py-2 text-sm text-amber-700 dark:text-amber-400"
|
|
1693
|
-
>
|
|
1694
|
-
<IconAlertTriangle size={15} className="mt-0.5 shrink-0" />
|
|
1695
|
-
<span className="min-w-0 break-words">{warning}</span>
|
|
1696
|
-
</div>
|
|
1697
|
-
))}
|
|
1698
|
-
</div>
|
|
1699
|
-
) : null}
|
|
2262
|
+
{loading ? (
|
|
2263
|
+
<div className="rounded-lg bg-muted/30 px-6 py-10 text-center text-sm text-muted-foreground">
|
|
2264
|
+
{t("integrations.loadingSetupPlan")}
|
|
2265
|
+
</div>
|
|
2266
|
+
) : null}
|
|
1700
2267
|
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
<
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
onValueChange={onProviderChange}
|
|
2268
|
+
{!loading && form && provider ? (
|
|
2269
|
+
<>
|
|
2270
|
+
{visiblePlanWarnings.length ? (
|
|
2271
|
+
<div className="grid gap-2">
|
|
2272
|
+
{visiblePlanWarnings.map((warning) => (
|
|
2273
|
+
<div
|
|
2274
|
+
key={warning}
|
|
2275
|
+
className="flex items-start gap-2 rounded-md border border-amber-500/20 bg-amber-500/10 px-3 py-2 text-sm text-amber-700 dark:text-amber-400"
|
|
1710
2276
|
>
|
|
1711
|
-
<
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
</
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
2277
|
+
<IconAlertTriangle
|
|
2278
|
+
size={15}
|
|
2279
|
+
className="mt-0.5 shrink-0"
|
|
2280
|
+
/>
|
|
2281
|
+
<span className="min-w-0 break-words">{warning}</span>
|
|
2282
|
+
</div>
|
|
2283
|
+
))}
|
|
2284
|
+
</div>
|
|
2285
|
+
) : null}
|
|
2286
|
+
|
|
2287
|
+
{isProviderStep ? (
|
|
2288
|
+
<div className="grid gap-6">
|
|
2289
|
+
<div className="flex items-center gap-3">
|
|
2290
|
+
<span className="flex size-10 shrink-0 items-center justify-center rounded-lg bg-muted/40">
|
|
2291
|
+
{logoForProvider(provider.id, provider.label)}
|
|
2292
|
+
</span>
|
|
2293
|
+
<div className="min-w-0">
|
|
2294
|
+
<h2 className="text-lg font-semibold tracking-[-0.02em] text-foreground">
|
|
2295
|
+
{provider.label}
|
|
2296
|
+
</h2>
|
|
2297
|
+
<p className="mt-0.5 text-sm text-muted-foreground">
|
|
2298
|
+
{t("integrations.scopeWorkspace")}
|
|
2299
|
+
</p>
|
|
2300
|
+
</div>
|
|
1724
2301
|
</div>
|
|
2302
|
+
|
|
1725
2303
|
<TextField
|
|
1726
2304
|
label={t("integrations.connectionLabel")}
|
|
1727
2305
|
value={form.label}
|
|
1728
2306
|
onChange={(value) => onChange({ ...form, label: value })}
|
|
1729
2307
|
required
|
|
1730
2308
|
/>
|
|
1731
|
-
</div>
|
|
1732
|
-
<div className="grid gap-3 sm:grid-cols-2">
|
|
1733
|
-
<TextField
|
|
1734
|
-
label={t("integrations.accountLabel")}
|
|
1735
|
-
value={form.accountLabel}
|
|
1736
|
-
onChange={(value) =>
|
|
1737
|
-
onChange({ ...form, accountLabel: value })
|
|
1738
|
-
}
|
|
1739
|
-
placeholder={t("integrations.accountLabelPlaceholder")}
|
|
1740
|
-
/>
|
|
1741
|
-
<TextField
|
|
1742
|
-
label={t("integrations.accountId")}
|
|
1743
|
-
value={form.accountId}
|
|
1744
|
-
onChange={(value) =>
|
|
1745
|
-
onChange({ ...form, accountId: value })
|
|
1746
|
-
}
|
|
1747
|
-
placeholder={t("integrations.accountIdPlaceholder")}
|
|
1748
|
-
/>
|
|
1749
|
-
</div>
|
|
1750
|
-
<div className="rounded-md border bg-background p-3">
|
|
1751
|
-
<div className="text-sm font-medium">
|
|
1752
|
-
{t("integrations.providerPlan")}
|
|
1753
|
-
</div>
|
|
1754
|
-
<div className="mt-2 flex flex-wrap gap-1.5">
|
|
1755
|
-
{provider.capabilities.map((capability) => (
|
|
1756
|
-
<Pill key={capability} className="border-border bg-muted">
|
|
1757
|
-
{capability}
|
|
1758
|
-
</Pill>
|
|
1759
|
-
))}
|
|
1760
|
-
</div>
|
|
1761
|
-
<p className="mt-2 text-xs leading-5 text-muted-foreground">
|
|
1762
|
-
{plan?.grantRecommendation.reason}
|
|
1763
|
-
</p>
|
|
1764
|
-
</div>
|
|
1765
|
-
</div>
|
|
1766
|
-
) : null}
|
|
1767
2309
|
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
2310
|
+
<details className="group rounded-lg bg-muted/30">
|
|
2311
|
+
<summary className="flex cursor-pointer list-none items-center justify-between gap-3 px-3.5 py-3 text-sm font-medium text-foreground marker:hidden">
|
|
2312
|
+
<span>
|
|
2313
|
+
{t(
|
|
2314
|
+
/* i18n-key-ignore */
|
|
2315
|
+
"integrations.advancedDetails",
|
|
2316
|
+
{ defaultValue: "Advanced details" },
|
|
2317
|
+
)}
|
|
2318
|
+
</span>
|
|
2319
|
+
<IconChevronDown className="size-4 shrink-0 text-muted-foreground transition-transform group-open:rotate-180" />
|
|
2320
|
+
</summary>
|
|
2321
|
+
<div className="grid gap-3 px-3.5 pb-3.5 sm:grid-cols-2">
|
|
2322
|
+
<TextField
|
|
2323
|
+
label={t("integrations.accountLabel")}
|
|
2324
|
+
value={form.accountLabel}
|
|
2325
|
+
onChange={(value) =>
|
|
2326
|
+
onChange({ ...form, accountLabel: value })
|
|
2327
|
+
}
|
|
2328
|
+
placeholder={t("integrations.accountLabelPlaceholder")}
|
|
2329
|
+
/>
|
|
2330
|
+
<TextField
|
|
2331
|
+
label={t("integrations.accountId")}
|
|
2332
|
+
value={form.accountId}
|
|
2333
|
+
onChange={(value) =>
|
|
2334
|
+
onChange({ ...form, accountId: value })
|
|
2335
|
+
}
|
|
2336
|
+
placeholder={t("integrations.accountIdPlaceholder")}
|
|
2337
|
+
/>
|
|
2338
|
+
<div className="sm:col-span-2">
|
|
2339
|
+
<p className="text-xs font-medium text-foreground">
|
|
2340
|
+
{t("integrations.providerPlan")}
|
|
2341
|
+
</p>
|
|
2342
|
+
<div className="mt-2 flex flex-wrap gap-1.5">
|
|
2343
|
+
{provider.capabilities.map((capability) => (
|
|
2344
|
+
<Pill
|
|
2345
|
+
key={capability}
|
|
2346
|
+
className="border-border bg-muted"
|
|
2347
|
+
>
|
|
2348
|
+
{capability}
|
|
2349
|
+
</Pill>
|
|
2350
|
+
))}
|
|
2351
|
+
</div>
|
|
2352
|
+
</div>
|
|
2353
|
+
</div>
|
|
2354
|
+
</details>
|
|
2355
|
+
</div>
|
|
2356
|
+
) : null}
|
|
1778
2357
|
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
<RadioGroup
|
|
1788
|
-
value={form.grantMode}
|
|
1789
|
-
onValueChange={(value) =>
|
|
1790
|
-
onChange({
|
|
1791
|
-
...form,
|
|
1792
|
-
grantMode: value as SetupWizardFormState["grantMode"],
|
|
1793
|
-
})
|
|
2358
|
+
{isCredentialStep ? (
|
|
2359
|
+
<CredentialRefsEditor
|
|
2360
|
+
provider={provider}
|
|
2361
|
+
refs={form.credentialRefs}
|
|
2362
|
+
missingRefs={missingCredentialRefs}
|
|
2363
|
+
progressive
|
|
2364
|
+
onChange={(credentialRefs) =>
|
|
2365
|
+
onChange({ ...form, credentialRefs })
|
|
1794
2366
|
}
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
<Label
|
|
1798
|
-
htmlFor="setup-grant-selected"
|
|
1799
|
-
className="flex cursor-pointer items-start gap-3 rounded-md border bg-background p-3"
|
|
1800
|
-
>
|
|
1801
|
-
<RadioGroupItem
|
|
1802
|
-
id="setup-grant-selected"
|
|
1803
|
-
value="selected-apps"
|
|
1804
|
-
className="mt-0.5"
|
|
1805
|
-
/>
|
|
1806
|
-
<span className="grid gap-1">
|
|
1807
|
-
<span className="text-sm font-medium">
|
|
1808
|
-
{t("integrations.selectedApps")}
|
|
1809
|
-
</span>
|
|
1810
|
-
<span className="text-xs leading-5 text-muted-foreground">
|
|
1811
|
-
{t("integrations.selectedAppsDescription")}
|
|
1812
|
-
</span>
|
|
1813
|
-
</span>
|
|
1814
|
-
</Label>
|
|
1815
|
-
<Label
|
|
1816
|
-
htmlFor="setup-grant-all"
|
|
1817
|
-
className="flex cursor-pointer items-start gap-3 rounded-md border bg-background p-3"
|
|
1818
|
-
>
|
|
1819
|
-
<RadioGroupItem
|
|
1820
|
-
id="setup-grant-all"
|
|
1821
|
-
value="all-apps"
|
|
1822
|
-
className="mt-0.5"
|
|
1823
|
-
/>
|
|
1824
|
-
<span className="grid gap-1">
|
|
1825
|
-
<span className="text-sm font-medium">
|
|
1826
|
-
{t("integrations.allApps")}
|
|
1827
|
-
</span>
|
|
1828
|
-
<span className="text-xs leading-5 text-muted-foreground">
|
|
1829
|
-
{t("integrations.allAppsDescription")}
|
|
1830
|
-
</span>
|
|
1831
|
-
</span>
|
|
1832
|
-
</Label>
|
|
1833
|
-
</RadioGroup>
|
|
2367
|
+
/>
|
|
2368
|
+
) : null}
|
|
1834
2369
|
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
2370
|
+
{isAccessStep ? (
|
|
2371
|
+
<div className="grid gap-5">
|
|
2372
|
+
<div>
|
|
2373
|
+
<h2 className="text-lg font-semibold tracking-[-0.02em] text-foreground">
|
|
2374
|
+
{t("integrations.access")}
|
|
2375
|
+
</h2>
|
|
2376
|
+
</div>
|
|
2377
|
+
|
|
2378
|
+
<div className="grid gap-2">
|
|
2379
|
+
<div className="flex items-center gap-3 rounded-lg bg-muted/30 px-4 py-3.5">
|
|
2380
|
+
<div className="min-w-0 flex-1">
|
|
2381
|
+
<p className="text-sm font-medium text-foreground">
|
|
2382
|
+
{accessSummary}
|
|
2383
|
+
</p>
|
|
2384
|
+
<p className="mt-0.5 text-xs leading-5 text-muted-foreground">
|
|
2385
|
+
{accessSummaryDescription}
|
|
2386
|
+
</p>
|
|
2387
|
+
</div>
|
|
1839
2388
|
</div>
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
<
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
{
|
|
1859
|
-
<Pill className="h-5 border-border bg-muted px-1.5 text-[11px]">
|
|
1860
|
-
{t("integrations.suggested")}
|
|
1861
|
-
</Pill>
|
|
1862
|
-
) : null}
|
|
1863
|
-
</span>
|
|
1864
|
-
<Checkbox
|
|
1865
|
-
id={`setup-app-${app.id}`}
|
|
1866
|
-
checked={selected}
|
|
2389
|
+
|
|
2390
|
+
{canManageAllApps ? (
|
|
2391
|
+
<details className="group rounded-lg bg-muted/30">
|
|
2392
|
+
<summary className="flex cursor-pointer list-none items-center justify-between gap-3 px-4 py-3 text-sm font-medium text-foreground marker:hidden">
|
|
2393
|
+
<span>{t("integrations.appGrants")}</span>
|
|
2394
|
+
<IconChevronDown className="size-4 shrink-0 text-muted-foreground transition-transform group-open:rotate-180" />
|
|
2395
|
+
</summary>
|
|
2396
|
+
<div className="grid gap-3 px-4 pb-4">
|
|
2397
|
+
<div className="flex items-center justify-between gap-4 rounded-md bg-muted/40 px-3 py-3">
|
|
2398
|
+
<div className="min-w-0">
|
|
2399
|
+
<p className="text-sm font-medium text-foreground">
|
|
2400
|
+
{t("integrations.allApps")}
|
|
2401
|
+
</p>
|
|
2402
|
+
<p className="mt-0.5 text-xs leading-5 text-muted-foreground">
|
|
2403
|
+
{t("integrations.allAppsDescription")}
|
|
2404
|
+
</p>
|
|
2405
|
+
</div>
|
|
2406
|
+
<Switch
|
|
2407
|
+
checked={form.grantMode === "all-apps"}
|
|
1867
2408
|
onCheckedChange={(checked) =>
|
|
1868
2409
|
onChange({
|
|
1869
2410
|
...form,
|
|
2411
|
+
grantMode: checked
|
|
2412
|
+
? "all-apps"
|
|
2413
|
+
: "selected-apps",
|
|
1870
2414
|
selectedApps: checked
|
|
1871
|
-
?
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
2415
|
+
? []
|
|
2416
|
+
: selectedApps.length > 0
|
|
2417
|
+
? selectedApps
|
|
2418
|
+
: suggestedGrantApps
|
|
2419
|
+
.filter((app) => app.recommended)
|
|
2420
|
+
.slice(0, 1)
|
|
2421
|
+
.map((app) => app.id),
|
|
1877
2422
|
})
|
|
1878
2423
|
}
|
|
1879
|
-
aria-label={t(
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
: t("integrations.grant"),
|
|
1883
|
-
app: app.label,
|
|
1884
|
-
})}
|
|
2424
|
+
aria-label={t(
|
|
2425
|
+
"integrations.grantAllWorkspaceAppsAria",
|
|
2426
|
+
)}
|
|
1885
2427
|
/>
|
|
1886
|
-
</
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
2428
|
+
</div>
|
|
2429
|
+
|
|
2430
|
+
{form.grantMode === "selected-apps" ? (
|
|
2431
|
+
<div className="grid gap-3">
|
|
2432
|
+
<p className="text-xs text-muted-foreground">
|
|
2433
|
+
{t("integrations.chooseOneApp")}
|
|
2434
|
+
</p>
|
|
2435
|
+
<div className="grid gap-2 sm:grid-cols-2">
|
|
2436
|
+
{availableGrantApps.map((app) => {
|
|
2437
|
+
const AppIcon = app.icon;
|
|
2438
|
+
const selected = selectedApps.includes(
|
|
2439
|
+
app.id,
|
|
2440
|
+
);
|
|
2441
|
+
return (
|
|
2442
|
+
<Label
|
|
2443
|
+
key={app.id}
|
|
2444
|
+
htmlFor={`setup-app-${app.id}`}
|
|
2445
|
+
className="flex min-h-11 cursor-pointer items-center justify-between gap-3 rounded-lg bg-muted/40 px-3 py-2 transition-colors hover:bg-muted/60"
|
|
2446
|
+
>
|
|
2447
|
+
<span className="flex min-w-0 items-center gap-2">
|
|
2448
|
+
<AppIcon
|
|
2449
|
+
size={14}
|
|
2450
|
+
className="text-muted-foreground"
|
|
2451
|
+
/>
|
|
2452
|
+
<span className="truncate text-sm font-medium">
|
|
2453
|
+
{app.label}
|
|
2454
|
+
</span>
|
|
2455
|
+
{app.recommended ? (
|
|
2456
|
+
<Pill className="h-5 border-border bg-muted px-1.5 text-[11px]">
|
|
2457
|
+
{t("integrations.suggested")}
|
|
2458
|
+
</Pill>
|
|
2459
|
+
) : null}
|
|
2460
|
+
</span>
|
|
2461
|
+
<Checkbox
|
|
2462
|
+
id={`setup-app-${app.id}`}
|
|
2463
|
+
checked={selected}
|
|
2464
|
+
onCheckedChange={(checked) =>
|
|
2465
|
+
onChange({
|
|
2466
|
+
...form,
|
|
2467
|
+
selectedApps: checked
|
|
2468
|
+
? Array.from(
|
|
2469
|
+
new Set([
|
|
2470
|
+
...selectedApps,
|
|
2471
|
+
app.id,
|
|
2472
|
+
]),
|
|
2473
|
+
)
|
|
2474
|
+
: selectedApps.filter(
|
|
2475
|
+
(appId) => appId !== app.id,
|
|
2476
|
+
),
|
|
2477
|
+
})
|
|
2478
|
+
}
|
|
2479
|
+
aria-label={t(
|
|
2480
|
+
"integrations.appGrantAria",
|
|
2481
|
+
{
|
|
2482
|
+
action: selected
|
|
2483
|
+
? t("integrations.remove")
|
|
2484
|
+
: t("integrations.grant"),
|
|
2485
|
+
app: app.label,
|
|
2486
|
+
},
|
|
2487
|
+
)}
|
|
2488
|
+
/>
|
|
2489
|
+
</Label>
|
|
2490
|
+
);
|
|
2491
|
+
})}
|
|
2492
|
+
</div>
|
|
2493
|
+
</div>
|
|
2494
|
+
) : null}
|
|
2495
|
+
</div>
|
|
2496
|
+
</details>
|
|
1894
2497
|
) : null}
|
|
1895
2498
|
</div>
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
2499
|
+
<UserAccessControl
|
|
2500
|
+
allUsers={form.userGrantMode === "all-users"}
|
|
2501
|
+
selectedUsers={selectedUsers}
|
|
2502
|
+
selectedUserGroups={selectedUserGroups}
|
|
2503
|
+
groups={groups}
|
|
2504
|
+
canManageGroups={canManageGroups}
|
|
2505
|
+
onManageGroups={onManageGroups}
|
|
2506
|
+
onEditGroup={onEditGroup}
|
|
2507
|
+
onChange={({
|
|
2508
|
+
allUsers,
|
|
2509
|
+
selectedUsers: nextUsers,
|
|
2510
|
+
selectedUserGroups: nextGroups,
|
|
2511
|
+
}) =>
|
|
2512
|
+
onChange({
|
|
2513
|
+
...form,
|
|
2514
|
+
userGrantMode: allUsers
|
|
2515
|
+
? "all-users"
|
|
2516
|
+
: "selected-users",
|
|
2517
|
+
selectedUsers: nextUsers,
|
|
2518
|
+
selectedUserGroups: nextGroups,
|
|
2519
|
+
})
|
|
2520
|
+
}
|
|
2521
|
+
/>
|
|
2522
|
+
</div>
|
|
2523
|
+
) : null}
|
|
2524
|
+
</>
|
|
2525
|
+
) : null}
|
|
2526
|
+
</div>
|
|
1901
2527
|
</div>
|
|
1902
2528
|
|
|
1903
|
-
<DialogFooter className="border-t
|
|
1904
|
-
<
|
|
1905
|
-
type="button"
|
|
1906
|
-
variant="outline"
|
|
1907
|
-
onClick={onClose}
|
|
1908
|
-
disabled={saving}
|
|
1909
|
-
>
|
|
1910
|
-
{t("integrations.cancel")}
|
|
1911
|
-
</Button>
|
|
1912
|
-
{step > 0 ? (
|
|
2529
|
+
<DialogFooter className="shrink-0 border-t px-6 py-4 sm:px-10">
|
|
2530
|
+
<div className="mx-auto flex w-full max-w-2xl items-center justify-between gap-2">
|
|
1913
2531
|
<Button
|
|
1914
2532
|
type="button"
|
|
1915
|
-
variant="
|
|
1916
|
-
onClick={
|
|
2533
|
+
variant="ghost"
|
|
2534
|
+
onClick={onClose}
|
|
1917
2535
|
disabled={saving}
|
|
1918
2536
|
>
|
|
1919
|
-
|
|
1920
|
-
{t("integrations.back")}
|
|
1921
|
-
</Button>
|
|
1922
|
-
) : null}
|
|
1923
|
-
{step < 2 ? (
|
|
1924
|
-
<Button
|
|
1925
|
-
type="button"
|
|
1926
|
-
onClick={() => onStepChange(step + 1)}
|
|
1927
|
-
disabled={!canAdvance || loading || saving}
|
|
1928
|
-
>
|
|
1929
|
-
{t("integrations.next")}
|
|
1930
|
-
<IconArrowRight size={14} className="rtl:-scale-x-100" />
|
|
1931
|
-
</Button>
|
|
1932
|
-
) : (
|
|
1933
|
-
<Button
|
|
1934
|
-
type="button"
|
|
1935
|
-
onClick={onSubmit}
|
|
1936
|
-
disabled={!canAdvance || loading || saving}
|
|
1937
|
-
>
|
|
1938
|
-
{saving ? <IconRefresh size={14} className="animate-spin" /> : null}
|
|
1939
|
-
{state?.mode === "repair"
|
|
1940
|
-
? t("integrations.applyRepair")
|
|
1941
|
-
: t("integrations.createConnection")}
|
|
2537
|
+
{t("integrations.cancel")}
|
|
1942
2538
|
</Button>
|
|
1943
|
-
|
|
2539
|
+
<div className="flex items-center gap-2">
|
|
2540
|
+
{step > 0 ? (
|
|
2541
|
+
<Button
|
|
2542
|
+
type="button"
|
|
2543
|
+
variant="outline"
|
|
2544
|
+
onClick={() => onStepChange(step - 1)}
|
|
2545
|
+
disabled={saving}
|
|
2546
|
+
>
|
|
2547
|
+
<IconArrowLeft size={14} className="rtl:-scale-x-100" />
|
|
2548
|
+
{t("integrations.back")}
|
|
2549
|
+
</Button>
|
|
2550
|
+
) : null}
|
|
2551
|
+
{!isAccessStep ? (
|
|
2552
|
+
<Button
|
|
2553
|
+
type="button"
|
|
2554
|
+
onClick={() => onStepChange(step + 1)}
|
|
2555
|
+
disabled={!canAdvance || loading || saving}
|
|
2556
|
+
>
|
|
2557
|
+
{t("integrations.next")}
|
|
2558
|
+
<IconArrowRight size={14} className="rtl:-scale-x-100" />
|
|
2559
|
+
</Button>
|
|
2560
|
+
) : (
|
|
2561
|
+
<Button
|
|
2562
|
+
type="button"
|
|
2563
|
+
onClick={onSubmit}
|
|
2564
|
+
disabled={!canAdvance || loading || saving}
|
|
2565
|
+
>
|
|
2566
|
+
{saving ? (
|
|
2567
|
+
<IconRefresh size={14} className="animate-spin" />
|
|
2568
|
+
) : null}
|
|
2569
|
+
{state?.mode === "repair"
|
|
2570
|
+
? t("integrations.applyRepair")
|
|
2571
|
+
: t("integrations.createConnection")}
|
|
2572
|
+
</Button>
|
|
2573
|
+
)}
|
|
2574
|
+
</div>
|
|
2575
|
+
</div>
|
|
1944
2576
|
</DialogFooter>
|
|
1945
2577
|
</Modal>
|
|
1946
2578
|
);
|
|
1947
2579
|
}
|
|
1948
2580
|
|
|
1949
|
-
function GrantPreview({
|
|
1950
|
-
providerLabel,
|
|
1951
|
-
grantMode,
|
|
1952
|
-
selectedAppIds,
|
|
1953
|
-
grantApps,
|
|
1954
|
-
}: {
|
|
1955
|
-
providerLabel: string;
|
|
1956
|
-
grantMode: SetupWizardFormState["grantMode"];
|
|
1957
|
-
selectedAppIds: string[];
|
|
1958
|
-
grantApps: GrantApp[];
|
|
1959
|
-
}) {
|
|
1960
|
-
const t = useT();
|
|
1961
|
-
const selectedLabel = summarizeAppList(selectedAppIds, grantApps, t);
|
|
1962
|
-
return (
|
|
1963
|
-
<div className="rounded-md border bg-muted/30 px-3 py-2 text-sm">
|
|
1964
|
-
<div className="flex items-center gap-2 font-medium text-foreground">
|
|
1965
|
-
<IconShieldCheck size={14} className="text-muted-foreground" />
|
|
1966
|
-
{t("integrations.grantPreview")}
|
|
1967
|
-
</div>
|
|
1968
|
-
<p className="mt-1 text-xs leading-5 text-muted-foreground">
|
|
1969
|
-
{grantMode === "all-apps"
|
|
1970
|
-
? t("integrations.grantPreviewAllApps", {
|
|
1971
|
-
provider: providerLabel,
|
|
1972
|
-
})
|
|
1973
|
-
: selectedAppIds.length > 0
|
|
1974
|
-
? t("integrations.grantPreviewSelected", {
|
|
1975
|
-
apps: selectedLabel,
|
|
1976
|
-
provider: providerLabel,
|
|
1977
|
-
})
|
|
1978
|
-
: t("integrations.grantPreviewEmpty", {
|
|
1979
|
-
provider: providerLabel,
|
|
1980
|
-
})}
|
|
1981
|
-
</p>
|
|
1982
|
-
</div>
|
|
1983
|
-
);
|
|
1984
|
-
}
|
|
1985
|
-
|
|
1986
2581
|
function DeleteConfirm({
|
|
1987
2582
|
connection,
|
|
1988
2583
|
deleting,
|
|
@@ -2057,7 +2652,7 @@ function ConnectionDeleteImpact({
|
|
|
2057
2652
|
const t = useT();
|
|
2058
2653
|
if (loading) {
|
|
2059
2654
|
return (
|
|
2060
|
-
<div className="rounded-md
|
|
2655
|
+
<div className="rounded-md bg-muted/30 px-3 py-2 text-sm text-muted-foreground">
|
|
2061
2656
|
{t("integrations.checkingAffectedApps")}
|
|
2062
2657
|
</div>
|
|
2063
2658
|
);
|
|
@@ -2073,7 +2668,7 @@ function ConnectionDeleteImpact({
|
|
|
2073
2668
|
);
|
|
2074
2669
|
|
|
2075
2670
|
return (
|
|
2076
|
-
<div className="rounded-md
|
|
2671
|
+
<div className="rounded-md bg-muted/30 px-3 py-2 text-sm">
|
|
2077
2672
|
<div className="flex flex-wrap items-center gap-2">
|
|
2078
2673
|
<Pill className="border-border bg-background text-muted-foreground">
|
|
2079
2674
|
{impact.impactSummary.hasAllAppsAccess
|
|
@@ -2105,14 +2700,28 @@ function ConnectionDeleteImpact({
|
|
|
2105
2700
|
export default function WorkspaceIntegrationsRoute() {
|
|
2106
2701
|
const t = useT();
|
|
2107
2702
|
const queryClient = useQueryClient();
|
|
2703
|
+
const { canManageOrg } = useOrgRole();
|
|
2704
|
+
const { data: dispatchAppRole } = useAppRoles("dispatch");
|
|
2705
|
+
const isDispatchAppAdmin = dispatchAppRole?.myRole === "admin";
|
|
2706
|
+
const canManageConnections = canManageOrg || isDispatchAppAdmin;
|
|
2108
2707
|
const [form, setForm] = useState<ConnectionFormState | null>(null);
|
|
2109
2708
|
const [setupWizard, setSetupWizard] = useState<SetupWizardState | null>(null);
|
|
2110
2709
|
const [setupStep, setSetupStep] = useState(0);
|
|
2111
2710
|
const [setupForm, setSetupForm] = useState<SetupWizardFormState | null>(null);
|
|
2112
2711
|
const [setupFormKey, setSetupFormKey] = useState("");
|
|
2712
|
+
const [providerQuery, setProviderQuery] = useState("");
|
|
2713
|
+
const [providerChoice, setProviderChoice] = useState<ProviderChoice | null>(
|
|
2714
|
+
null,
|
|
2715
|
+
);
|
|
2716
|
+
const [personalIntegrationId, setPersonalIntegrationId] = useState<
|
|
2717
|
+
string | null
|
|
2718
|
+
>(null);
|
|
2113
2719
|
const [deleteTarget, setDeleteTarget] = useState<WorkspaceConnection | null>(
|
|
2114
2720
|
null,
|
|
2115
2721
|
);
|
|
2722
|
+
const [groupEditor, setGroupEditor] = useState<{
|
|
2723
|
+
group: WorkspaceUserGroup | null;
|
|
2724
|
+
} | null>(null);
|
|
2116
2725
|
|
|
2117
2726
|
const connectionsQuery = useActionQuery(
|
|
2118
2727
|
"list-workspace-connections",
|
|
@@ -2122,6 +2731,11 @@ export default function WorkspaceIntegrationsRoute() {
|
|
|
2122
2731
|
includeAgentCards: false,
|
|
2123
2732
|
audience: "all",
|
|
2124
2733
|
});
|
|
2734
|
+
const groupsQuery = useActionQuery(
|
|
2735
|
+
"list-workspace-user-groups",
|
|
2736
|
+
{},
|
|
2737
|
+
{ enabled: canManageOrg },
|
|
2738
|
+
);
|
|
2125
2739
|
const setupPlanQuery = useActionQuery<WorkspaceConnectionSetupPlan>(
|
|
2126
2740
|
"plan-workspace-connection-setup",
|
|
2127
2741
|
{
|
|
@@ -2136,6 +2750,7 @@ export default function WorkspaceIntegrationsRoute() {
|
|
|
2136
2750
|
const providers = data.providers;
|
|
2137
2751
|
const connections = data.connections;
|
|
2138
2752
|
const apps = (appsQuery.data ?? []) as WorkspaceAppSummary[];
|
|
2753
|
+
const groups = (groupsQuery.data ?? []) as WorkspaceUserGroup[];
|
|
2139
2754
|
const providersById = useMemo(
|
|
2140
2755
|
() => new Map(providers.map((provider) => [provider.id, provider])),
|
|
2141
2756
|
[providers],
|
|
@@ -2163,11 +2778,30 @@ export default function WorkspaceIntegrationsRoute() {
|
|
|
2163
2778
|
}
|
|
2164
2779
|
return map;
|
|
2165
2780
|
}, [connections]);
|
|
2781
|
+
const personalIntegrations = useMemo(() => {
|
|
2782
|
+
const map = new Map<string, DefaultMcpIntegration>();
|
|
2783
|
+
for (const integration of getDefaultMcpIntegrations()) {
|
|
2784
|
+
map.set(integration.provider, integration);
|
|
2785
|
+
map.set(integration.id, integration);
|
|
2786
|
+
}
|
|
2787
|
+
return map;
|
|
2788
|
+
}, []);
|
|
2789
|
+
const filteredProviders = useMemo(() => {
|
|
2790
|
+
const query = providerQuery.trim().toLowerCase();
|
|
2791
|
+
if (!query) return providers;
|
|
2792
|
+
return providers.filter((provider) =>
|
|
2793
|
+
`${provider.label} ${provider.description} ${provider.capabilities.join(" ")}`
|
|
2794
|
+
.toLowerCase()
|
|
2795
|
+
.includes(query),
|
|
2796
|
+
);
|
|
2797
|
+
}, [providerQuery, providers]);
|
|
2166
2798
|
|
|
2167
2799
|
const upsertConnection = useActionMutation("upsert-workspace-connection");
|
|
2168
2800
|
const applySetup = useActionMutation("apply-workspace-connection-setup");
|
|
2169
2801
|
const setGrant = useActionMutation("set-workspace-connection-grant");
|
|
2170
2802
|
const deleteConnection = useActionMutation("delete-workspace-connection");
|
|
2803
|
+
const upsertGroup = useActionMutation("upsert-workspace-user-group");
|
|
2804
|
+
const createPersonalMcpServer = useCreateMcpServer();
|
|
2171
2805
|
|
|
2172
2806
|
const currentSetupKey = setupWizard
|
|
2173
2807
|
? `${setupWizard.mode}:${setupWizard.connectionId ?? ""}:${
|
|
@@ -2178,18 +2812,45 @@ export default function WorkspaceIntegrationsRoute() {
|
|
|
2178
2812
|
useEffect(() => {
|
|
2179
2813
|
if (!setupWizard || !setupPlanQuery.data) return;
|
|
2180
2814
|
if (setupFormKey === currentSetupKey) return;
|
|
2181
|
-
|
|
2815
|
+
const nextForm = setupFormFromPlan(setupPlanQuery.data, setupWizard.mode);
|
|
2816
|
+
if (!canManageOrg) {
|
|
2817
|
+
nextForm.grantMode = "selected-apps";
|
|
2818
|
+
nextForm.selectedApps = ["dispatch"];
|
|
2819
|
+
}
|
|
2820
|
+
setSetupForm(nextForm);
|
|
2182
2821
|
setSetupFormKey(currentSetupKey);
|
|
2183
|
-
}, [
|
|
2822
|
+
}, [
|
|
2823
|
+
canManageOrg,
|
|
2824
|
+
currentSetupKey,
|
|
2825
|
+
setupFormKey,
|
|
2826
|
+
setupPlanQuery.data,
|
|
2827
|
+
setupWizard,
|
|
2828
|
+
]);
|
|
2184
2829
|
|
|
2185
2830
|
function openSetup(provider: WorkspaceConnectionProvider) {
|
|
2831
|
+
if (!canManageConnections) return;
|
|
2186
2832
|
setSetupWizard({ mode: "setup", providerId: provider.id });
|
|
2187
2833
|
setSetupStep(0);
|
|
2188
2834
|
setSetupForm(null);
|
|
2189
2835
|
setSetupFormKey("");
|
|
2190
2836
|
}
|
|
2191
2837
|
|
|
2838
|
+
function openProviderConnection(provider: WorkspaceConnectionProvider) {
|
|
2839
|
+
const personalIntegration = personalIntegrations.get(provider.id);
|
|
2840
|
+
if (personalIntegration) {
|
|
2841
|
+
setProviderChoice({ provider, personalIntegration });
|
|
2842
|
+
return;
|
|
2843
|
+
}
|
|
2844
|
+
if (!canManageConnections) return;
|
|
2845
|
+
if (providerHasOAuth(provider)) {
|
|
2846
|
+
startWorkspaceProviderOAuth(provider);
|
|
2847
|
+
return;
|
|
2848
|
+
}
|
|
2849
|
+
openSetup(provider);
|
|
2850
|
+
}
|
|
2851
|
+
|
|
2192
2852
|
function openRepair(connection: WorkspaceConnection) {
|
|
2853
|
+
if (!canManageConnection(connection)) return;
|
|
2193
2854
|
setSetupWizard({
|
|
2194
2855
|
mode: "repair",
|
|
2195
2856
|
providerId: connection.provider,
|
|
@@ -2201,14 +2862,85 @@ export default function WorkspaceIntegrationsRoute() {
|
|
|
2201
2862
|
}
|
|
2202
2863
|
|
|
2203
2864
|
function openEdit(connection: WorkspaceConnection) {
|
|
2865
|
+
if (!canManageConnection(connection)) return;
|
|
2204
2866
|
setForm(
|
|
2205
2867
|
formFromConnection(connection, providersById.get(connection.provider)),
|
|
2206
2868
|
);
|
|
2207
2869
|
}
|
|
2208
2870
|
|
|
2871
|
+
function openGroupEditor(group: WorkspaceUserGroup | null = null) {
|
|
2872
|
+
if (!canManageOrg) return;
|
|
2873
|
+
setGroupEditor({ group });
|
|
2874
|
+
}
|
|
2875
|
+
|
|
2876
|
+
function canManageConnection(connection: WorkspaceConnection): boolean {
|
|
2877
|
+
return (
|
|
2878
|
+
canManageOrg ||
|
|
2879
|
+
(isDispatchAppAdmin &&
|
|
2880
|
+
connection.allowedApps.length === 1 &&
|
|
2881
|
+
connection.allowedApps[0]?.toLowerCase() === "dispatch")
|
|
2882
|
+
);
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2885
|
+
function canManageConnectionGrant(
|
|
2886
|
+
connection: WorkspaceConnection,
|
|
2887
|
+
appId: string,
|
|
2888
|
+
): boolean {
|
|
2889
|
+
return (
|
|
2890
|
+
canManageOrg ||
|
|
2891
|
+
(isDispatchAppAdmin &&
|
|
2892
|
+
appId.toLowerCase() === "dispatch" &&
|
|
2893
|
+
connection.allowedApps.length > 1 &&
|
|
2894
|
+
connection.allowedApps.some(
|
|
2895
|
+
(allowedApp) => allowedApp.toLowerCase() === "dispatch",
|
|
2896
|
+
))
|
|
2897
|
+
);
|
|
2898
|
+
}
|
|
2899
|
+
|
|
2900
|
+
async function handleSaveGroup(name: string, memberEmails: string[]) {
|
|
2901
|
+
if (!canManageOrg) return;
|
|
2902
|
+
try {
|
|
2903
|
+
await upsertGroup.mutateAsync({
|
|
2904
|
+
id: groupEditor?.group?.id,
|
|
2905
|
+
name,
|
|
2906
|
+
memberEmails,
|
|
2907
|
+
});
|
|
2908
|
+
toast.success(
|
|
2909
|
+
t(
|
|
2910
|
+
groupEditor?.group
|
|
2911
|
+
? "integrations.groupUpdated"
|
|
2912
|
+
: "integrations.groupCreated",
|
|
2913
|
+
),
|
|
2914
|
+
);
|
|
2915
|
+
setGroupEditor(null);
|
|
2916
|
+
queryClient.invalidateQueries({ queryKey: GROUP_QUERY_KEY });
|
|
2917
|
+
} catch (error) {
|
|
2918
|
+
toast.error(
|
|
2919
|
+
error instanceof Error
|
|
2920
|
+
? error.message
|
|
2921
|
+
: t("integrations.groupSaveError"),
|
|
2922
|
+
);
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2925
|
+
|
|
2209
2926
|
async function handleSubmit(event: FormEvent<HTMLFormElement>) {
|
|
2210
2927
|
event.preventDefault();
|
|
2211
2928
|
if (!form) return;
|
|
2929
|
+
if (form.id) {
|
|
2930
|
+
const existing = connections.find(
|
|
2931
|
+
(connection) => connection.id === form.id,
|
|
2932
|
+
);
|
|
2933
|
+
if (existing && !canManageConnection(existing)) return;
|
|
2934
|
+
} else if (!canManageConnections) {
|
|
2935
|
+
return;
|
|
2936
|
+
}
|
|
2937
|
+
if (
|
|
2938
|
+
!canManageOrg &&
|
|
2939
|
+
(form.allApps ||
|
|
2940
|
+
form.selectedApps.some((appId) => appId.toLowerCase() !== "dispatch"))
|
|
2941
|
+
) {
|
|
2942
|
+
return;
|
|
2943
|
+
}
|
|
2212
2944
|
try {
|
|
2213
2945
|
const provider = providersById.get(form.provider);
|
|
2214
2946
|
const credentialRefs = normalizeCredentialRefs(
|
|
@@ -2225,6 +2957,8 @@ export default function WorkspaceIntegrationsRoute() {
|
|
|
2225
2957
|
scopes: normalizeList(form.scopes),
|
|
2226
2958
|
credentialRefs,
|
|
2227
2959
|
allowedApps: form.allApps ? [] : form.selectedApps,
|
|
2960
|
+
allowedUsers: form.allUsers ? [] : form.selectedUsers,
|
|
2961
|
+
allowedUserGroups: form.allUsers ? [] : form.selectedUserGroups,
|
|
2228
2962
|
});
|
|
2229
2963
|
toast.success(
|
|
2230
2964
|
form.id
|
|
@@ -2241,6 +2975,16 @@ export default function WorkspaceIntegrationsRoute() {
|
|
|
2241
2975
|
|
|
2242
2976
|
async function applySetupWizard() {
|
|
2243
2977
|
if (!setupForm) return;
|
|
2978
|
+
if (!canManageConnections) return;
|
|
2979
|
+
if (
|
|
2980
|
+
!canManageOrg &&
|
|
2981
|
+
(setupForm.grantMode === "all-apps" ||
|
|
2982
|
+
setupForm.selectedApps.some(
|
|
2983
|
+
(appId) => appId.toLowerCase() !== "dispatch",
|
|
2984
|
+
))
|
|
2985
|
+
) {
|
|
2986
|
+
return;
|
|
2987
|
+
}
|
|
2244
2988
|
try {
|
|
2245
2989
|
await applySetup.mutateAsync({
|
|
2246
2990
|
connectionId: setupForm.connectionId,
|
|
@@ -2256,6 +3000,9 @@ export default function WorkspaceIntegrationsRoute() {
|
|
|
2256
3000
|
),
|
|
2257
3001
|
grantMode: setupForm.grantMode,
|
|
2258
3002
|
selectedApps: setupForm.selectedApps,
|
|
3003
|
+
userGrantMode: setupForm.userGrantMode,
|
|
3004
|
+
selectedUsers: setupForm.selectedUsers,
|
|
3005
|
+
selectedUserGroups: setupForm.selectedUserGroups,
|
|
2259
3006
|
});
|
|
2260
3007
|
toast.success(
|
|
2261
3008
|
setupForm.connectionId
|
|
@@ -2280,6 +3027,7 @@ export default function WorkspaceIntegrationsRoute() {
|
|
|
2280
3027
|
appId: string,
|
|
2281
3028
|
granted: boolean,
|
|
2282
3029
|
) {
|
|
3030
|
+
if (!canManageConnectionGrant(connection, appId)) return;
|
|
2283
3031
|
const previous =
|
|
2284
3032
|
queryClient.getQueryData<WorkspaceConnectionsResponse>(
|
|
2285
3033
|
CONNECTION_QUERY_KEY,
|
|
@@ -2358,6 +3106,7 @@ export default function WorkspaceIntegrationsRoute() {
|
|
|
2358
3106
|
|
|
2359
3107
|
async function confirmDelete() {
|
|
2360
3108
|
if (!deleteTarget) return;
|
|
3109
|
+
if (!canManageConnection(deleteTarget)) return;
|
|
2361
3110
|
const previous =
|
|
2362
3111
|
queryClient.getQueryData<WorkspaceConnectionsResponse>(
|
|
2363
3112
|
CONNECTION_QUERY_KEY,
|
|
@@ -2431,133 +3180,193 @@ export default function WorkspaceIntegrationsRoute() {
|
|
|
2431
3180
|
) : null}
|
|
2432
3181
|
{!connectionsQuery.isError && !appsQuery.isError ? (
|
|
2433
3182
|
<>
|
|
2434
|
-
<section
|
|
2435
|
-
data-usage-tracked={usageTracked ? "true" : undefined}
|
|
2436
|
-
className={cx(
|
|
2437
|
-
"dispatch-integrations-summary-grid grid gap-3",
|
|
2438
|
-
usageTracked && "dispatch-integrations-summary-grid-tracked",
|
|
2439
|
-
)}
|
|
2440
|
-
>
|
|
2441
|
-
<SummaryCard
|
|
2442
|
-
icon={IconCheck}
|
|
2443
|
-
label={t("integrations.readyProviders")}
|
|
2444
|
-
value={`${data.counts.readyProviders ?? 0}/${providers.length}`}
|
|
2445
|
-
detail={t("integrations.readyProvidersDetail")}
|
|
2446
|
-
/>
|
|
2447
|
-
<SummaryCard
|
|
2448
|
-
icon={IconPlugConnected}
|
|
2449
|
-
label={t("integrations.connections")}
|
|
2450
|
-
value={String(connections.length)}
|
|
2451
|
-
detail={t("integrations.connectedCount", {
|
|
2452
|
-
count: connectedCount,
|
|
2453
|
-
})}
|
|
2454
|
-
/>
|
|
2455
|
-
<SummaryCard
|
|
2456
|
-
icon={IconShieldCheck}
|
|
2457
|
-
label={t("integrations.appGrants")}
|
|
2458
|
-
value={String(data.grants.length)}
|
|
2459
|
-
detail={t("integrations.appGrantsDetail", {
|
|
2460
|
-
allAppCount: data.counts.allAppConnections ?? 0,
|
|
2461
|
-
selectedCount: data.counts.selectedAppConnections ?? 0,
|
|
2462
|
-
})}
|
|
2463
|
-
/>
|
|
2464
|
-
<SummaryCard
|
|
2465
|
-
icon={IconKey}
|
|
2466
|
-
label={t("integrations.keyHealth")}
|
|
2467
|
-
value={
|
|
2468
|
-
connectionsMissingKeys === 0
|
|
2469
|
-
? t("integrations.healthy")
|
|
2470
|
-
: t("integrations.missingCount", {
|
|
2471
|
-
count: connectionsMissingKeys,
|
|
2472
|
-
})
|
|
2473
|
-
}
|
|
2474
|
-
detail={
|
|
2475
|
-
attentionCount
|
|
2476
|
-
? t("integrations.connectionNeedsAttention", {
|
|
2477
|
-
count: attentionCount,
|
|
2478
|
-
})
|
|
2479
|
-
: t("integrations.requiredRefsPresent")
|
|
2480
|
-
}
|
|
2481
|
-
/>
|
|
2482
|
-
{usageTracked ? (
|
|
2483
|
-
<SummaryCard
|
|
2484
|
-
icon={IconClock}
|
|
2485
|
-
label={t("integrations.usage")}
|
|
2486
|
-
value={
|
|
2487
|
-
Number.isFinite(mostRecentUsage)
|
|
2488
|
-
? formatTimestamp(
|
|
2489
|
-
t,
|
|
2490
|
-
new Date(mostRecentUsage).toISOString(),
|
|
2491
|
-
)
|
|
2492
|
-
: t("integrations.time.neverUsed")
|
|
2493
|
-
}
|
|
2494
|
-
detail={
|
|
2495
|
-
neverUsedCount
|
|
2496
|
-
? t("integrations.neverUsedCount", {
|
|
2497
|
-
count: neverUsedCount,
|
|
2498
|
-
})
|
|
2499
|
-
: t("integrations.allTrackedAccountsHaveUsage")
|
|
2500
|
-
}
|
|
2501
|
-
/>
|
|
2502
|
-
) : null}
|
|
2503
|
-
</section>
|
|
2504
|
-
|
|
2505
|
-
<IntegrationOnboarding />
|
|
2506
|
-
|
|
2507
3183
|
{connectionsQuery.isLoading ? (
|
|
2508
|
-
<div className="rounded-lg
|
|
3184
|
+
<div className="rounded-lg bg-muted/30 px-6 py-12 text-center text-sm text-muted-foreground">
|
|
2509
3185
|
{t("integrations.loadingWorkspaceIntegrations")}
|
|
2510
3186
|
</div>
|
|
2511
3187
|
) : null}
|
|
2512
3188
|
|
|
2513
3189
|
<section>
|
|
2514
|
-
<div className="mb-3 flex items-
|
|
3190
|
+
<div className="mb-3 flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
|
|
2515
3191
|
<div>
|
|
2516
3192
|
<h2 className="text-sm font-semibold text-foreground">
|
|
2517
|
-
{t(
|
|
3193
|
+
{t(
|
|
3194
|
+
/* i18n-key-ignore */
|
|
3195
|
+
"integrations.workspaceCatalogTitle",
|
|
3196
|
+
{ defaultValue: "Workspace integrations" },
|
|
3197
|
+
)}
|
|
2518
3198
|
</h2>
|
|
2519
3199
|
<p className="text-xs text-muted-foreground">
|
|
2520
|
-
{t(
|
|
2521
|
-
|
|
2522
|
-
|
|
3200
|
+
{t(
|
|
3201
|
+
/* i18n-key-ignore */
|
|
3202
|
+
"integrations.workspaceCatalogDescription",
|
|
3203
|
+
{
|
|
3204
|
+
defaultValue:
|
|
3205
|
+
"Connect once, then choose which apps can use it.",
|
|
3206
|
+
},
|
|
3207
|
+
)}
|
|
2523
3208
|
</p>
|
|
2524
3209
|
</div>
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
3210
|
+
<div className="relative w-full sm:max-w-xs">
|
|
3211
|
+
<IconSearch className="pointer-events-none absolute start-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
|
|
3212
|
+
<Input
|
|
3213
|
+
value={providerQuery}
|
|
3214
|
+
onChange={(event) => setProviderQuery(event.target.value)}
|
|
3215
|
+
placeholder={t(
|
|
3216
|
+
/* i18n-key-ignore */
|
|
3217
|
+
"integrations.searchWorkspacePlaceholder",
|
|
3218
|
+
{ defaultValue: "Search integrations" },
|
|
3219
|
+
)}
|
|
3220
|
+
aria-label={t(
|
|
3221
|
+
/* i18n-key-ignore */
|
|
3222
|
+
"integrations.searchWorkspaceLabel",
|
|
3223
|
+
{ defaultValue: "Search workspace integrations" },
|
|
3224
|
+
)}
|
|
3225
|
+
className="h-9 ps-9"
|
|
2533
3226
|
/>
|
|
2534
|
-
|
|
3227
|
+
</div>
|
|
2535
3228
|
</div>
|
|
3229
|
+
<IntegrationGrid
|
|
3230
|
+
items={filteredProviders.map((provider) => {
|
|
3231
|
+
const active = (
|
|
3232
|
+
providerConnections.get(provider.id) ?? []
|
|
3233
|
+
).filter((connection) => connection.status !== "disabled");
|
|
3234
|
+
const connected = active.length > 0;
|
|
3235
|
+
return {
|
|
3236
|
+
id: provider.id,
|
|
3237
|
+
name: provider.label,
|
|
3238
|
+
description: provider.description,
|
|
3239
|
+
logo: logoForProvider(provider.id, provider.label),
|
|
3240
|
+
status: connected ? "Connected" : undefined,
|
|
3241
|
+
statusClassName: connected
|
|
3242
|
+
? "text-emerald-600 dark:text-emerald-400"
|
|
3243
|
+
: "text-muted-foreground",
|
|
3244
|
+
actionLabel: connected
|
|
3245
|
+
? canManageConnection(active[0])
|
|
3246
|
+
? "Manage"
|
|
3247
|
+
: personalIntegrations.has(provider.id)
|
|
3248
|
+
? "Connect for me"
|
|
3249
|
+
: "Admin only"
|
|
3250
|
+
: "Connect",
|
|
3251
|
+
disabled:
|
|
3252
|
+
!connected &&
|
|
3253
|
+
!canManageConnections &&
|
|
3254
|
+
!personalIntegrations.has(provider.id),
|
|
3255
|
+
onAction: () =>
|
|
3256
|
+
connected
|
|
3257
|
+
? canManageConnection(active[0])
|
|
3258
|
+
? openEdit(active[0])
|
|
3259
|
+
: openProviderConnection(provider)
|
|
3260
|
+
: openProviderConnection(provider),
|
|
3261
|
+
};
|
|
3262
|
+
})}
|
|
3263
|
+
emptyLabel="No integrations match."
|
|
3264
|
+
/>
|
|
2536
3265
|
</section>
|
|
2537
3266
|
|
|
3267
|
+
<details className="rounded-xl bg-muted/30">
|
|
3268
|
+
<summary className="cursor-pointer list-none px-4 py-3 text-sm font-medium text-foreground marker:hidden">
|
|
3269
|
+
{t(/* i18n-key-ignore */ "integrations.workspaceOverview", {
|
|
3270
|
+
defaultValue: "Workspace overview",
|
|
3271
|
+
})}
|
|
3272
|
+
</summary>
|
|
3273
|
+
<div className="p-4">
|
|
3274
|
+
<section
|
|
3275
|
+
data-usage-tracked={usageTracked ? "true" : undefined}
|
|
3276
|
+
className={cx(
|
|
3277
|
+
"dispatch-integrations-summary-grid grid gap-3",
|
|
3278
|
+
usageTracked &&
|
|
3279
|
+
"dispatch-integrations-summary-grid-tracked",
|
|
3280
|
+
)}
|
|
3281
|
+
>
|
|
3282
|
+
<SummaryCard
|
|
3283
|
+
icon={IconCheck}
|
|
3284
|
+
label={t("integrations.readyProviders")}
|
|
3285
|
+
value={`${data.counts.readyProviders ?? 0}/${providers.length}`}
|
|
3286
|
+
detail={t("integrations.readyProvidersDetail")}
|
|
3287
|
+
/>
|
|
3288
|
+
<SummaryCard
|
|
3289
|
+
icon={IconPlugConnected}
|
|
3290
|
+
label={t("integrations.connections")}
|
|
3291
|
+
value={String(connections.length)}
|
|
3292
|
+
detail={t("integrations.connectedCount", {
|
|
3293
|
+
count: connectedCount,
|
|
3294
|
+
})}
|
|
3295
|
+
/>
|
|
3296
|
+
<SummaryCard
|
|
3297
|
+
icon={IconShieldCheck}
|
|
3298
|
+
label={t("integrations.appGrants")}
|
|
3299
|
+
value={String(data.grants.length)}
|
|
3300
|
+
detail={t("integrations.appGrantsDetail", {
|
|
3301
|
+
allAppCount: data.counts.allAppConnections ?? 0,
|
|
3302
|
+
selectedCount: data.counts.selectedAppConnections ?? 0,
|
|
3303
|
+
})}
|
|
3304
|
+
/>
|
|
3305
|
+
<SummaryCard
|
|
3306
|
+
icon={IconKey}
|
|
3307
|
+
label={t("integrations.keyHealth")}
|
|
3308
|
+
value={
|
|
3309
|
+
connectionsMissingKeys === 0
|
|
3310
|
+
? t("integrations.healthy")
|
|
3311
|
+
: t("integrations.missingCount", {
|
|
3312
|
+
count: connectionsMissingKeys,
|
|
3313
|
+
})
|
|
3314
|
+
}
|
|
3315
|
+
detail={
|
|
3316
|
+
attentionCount
|
|
3317
|
+
? t("integrations.connectionNeedsAttention", {
|
|
3318
|
+
count: attentionCount,
|
|
3319
|
+
})
|
|
3320
|
+
: t("integrations.requiredRefsPresent")
|
|
3321
|
+
}
|
|
3322
|
+
/>
|
|
3323
|
+
{usageTracked ? (
|
|
3324
|
+
<SummaryCard
|
|
3325
|
+
icon={IconClock}
|
|
3326
|
+
label={t("integrations.usage")}
|
|
3327
|
+
value={
|
|
3328
|
+
Number.isFinite(mostRecentUsage)
|
|
3329
|
+
? formatTimestamp(
|
|
3330
|
+
t,
|
|
3331
|
+
new Date(mostRecentUsage).toISOString(),
|
|
3332
|
+
)
|
|
3333
|
+
: t("integrations.time.neverUsed")
|
|
3334
|
+
}
|
|
3335
|
+
detail={
|
|
3336
|
+
neverUsedCount
|
|
3337
|
+
? t("integrations.neverUsedCount", {
|
|
3338
|
+
count: neverUsedCount,
|
|
3339
|
+
})
|
|
3340
|
+
: t("integrations.allTrackedAccountsHaveUsage")
|
|
3341
|
+
}
|
|
3342
|
+
/>
|
|
3343
|
+
) : null}
|
|
3344
|
+
</section>
|
|
3345
|
+
</div>
|
|
3346
|
+
</details>
|
|
3347
|
+
|
|
2538
3348
|
<section>
|
|
2539
3349
|
<div className="mb-3 flex items-center justify-between gap-3">
|
|
2540
3350
|
<div>
|
|
2541
3351
|
<h2 className="text-sm font-semibold text-foreground">
|
|
2542
3352
|
{t("integrations.connectedAccounts")}
|
|
2543
3353
|
</h2>
|
|
2544
|
-
<p className="text-xs text-muted-foreground">
|
|
2545
|
-
{
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
})}
|
|
3354
|
+
<p className="mt-0.5 text-xs text-muted-foreground">
|
|
3355
|
+
{t("integrations.sharedConnectionsDescription", {
|
|
3356
|
+
defaultValue:
|
|
3357
|
+
"Saved provider connections that your apps can use.",
|
|
3358
|
+
})}
|
|
2550
3359
|
</p>
|
|
2551
3360
|
</div>
|
|
2552
3361
|
</div>
|
|
2553
3362
|
{connections.length === 0 && !connectionsQuery.isLoading ? (
|
|
2554
|
-
<div className="rounded-lg
|
|
3363
|
+
<div className="rounded-lg bg-muted/30 px-6 py-12 text-center">
|
|
2555
3364
|
<IconPlugConnected
|
|
2556
3365
|
size={24}
|
|
2557
3366
|
className="mx-auto text-muted-foreground"
|
|
2558
3367
|
/>
|
|
2559
3368
|
<p className="mt-3 text-sm font-medium text-foreground">
|
|
2560
|
-
{t("integrations.
|
|
3369
|
+
{t("integrations.noSharedConnectionsYet")}
|
|
2561
3370
|
</p>
|
|
2562
3371
|
<p className="mx-auto mt-1 max-w-md text-sm leading-5 text-muted-foreground">
|
|
2563
3372
|
{t("integrations.emptyConnectedAccountsDescription")}
|
|
@@ -2572,7 +3381,12 @@ export default function WorkspaceIntegrationsRoute() {
|
|
|
2572
3381
|
provider={providersById.get(connection.provider)}
|
|
2573
3382
|
grantApps={grantApps}
|
|
2574
3383
|
grants={data.grants}
|
|
3384
|
+
groups={groups}
|
|
2575
3385
|
grantPending={setGrant.isPending}
|
|
3386
|
+
canManage={canManageConnection(connection)}
|
|
3387
|
+
canManageGrant={(appId) =>
|
|
3388
|
+
canManageConnectionGrant(connection, appId)
|
|
3389
|
+
}
|
|
2576
3390
|
onEdit={() => openEdit(connection)}
|
|
2577
3391
|
onRepair={() => openRepair(connection)}
|
|
2578
3392
|
onDelete={() => setDeleteTarget(connection)}
|
|
@@ -2596,15 +3410,12 @@ export default function WorkspaceIntegrationsRoute() {
|
|
|
2596
3410
|
form={setupForm}
|
|
2597
3411
|
providers={providers}
|
|
2598
3412
|
grantApps={grantApps}
|
|
3413
|
+
groups={groups}
|
|
3414
|
+
canManageGroups={canManageOrg}
|
|
3415
|
+
canManageAllApps={canManageOrg}
|
|
2599
3416
|
loading={setupPlanQuery.isLoading}
|
|
2600
3417
|
saving={applySetup.isPending}
|
|
2601
3418
|
onStepChange={setSetupStep}
|
|
2602
|
-
onProviderChange={(providerId) => {
|
|
2603
|
-
setSetupWizard({ mode: "setup", providerId });
|
|
2604
|
-
setSetupStep(0);
|
|
2605
|
-
setSetupForm(null);
|
|
2606
|
-
setSetupFormKey("");
|
|
2607
|
-
}}
|
|
2608
3419
|
onChange={setSetupForm}
|
|
2609
3420
|
onClose={() => {
|
|
2610
3421
|
setSetupWizard(null);
|
|
@@ -2612,16 +3423,88 @@ export default function WorkspaceIntegrationsRoute() {
|
|
|
2612
3423
|
setSetupFormKey("");
|
|
2613
3424
|
}}
|
|
2614
3425
|
onSubmit={applySetupWizard}
|
|
3426
|
+
onManageGroups={() => openGroupEditor()}
|
|
3427
|
+
onEditGroup={openGroupEditor}
|
|
2615
3428
|
/>
|
|
2616
3429
|
<ConnectionForm
|
|
2617
3430
|
open={!!form}
|
|
2618
3431
|
form={form}
|
|
2619
3432
|
providers={providers}
|
|
2620
3433
|
grantApps={grantApps}
|
|
3434
|
+
groups={groups}
|
|
3435
|
+
canManageGroups={canManageOrg}
|
|
3436
|
+
canManageAllApps={canManageOrg}
|
|
2621
3437
|
saving={upsertConnection.isPending}
|
|
2622
3438
|
onChange={setForm}
|
|
2623
3439
|
onClose={() => setForm(null)}
|
|
2624
3440
|
onSubmit={handleSubmit}
|
|
3441
|
+
onManageGroups={() => openGroupEditor()}
|
|
3442
|
+
onEditGroup={openGroupEditor}
|
|
3443
|
+
/>
|
|
3444
|
+
<Dialog
|
|
3445
|
+
open={!!providerChoice}
|
|
3446
|
+
onOpenChange={(open) => !open && setProviderChoice(null)}
|
|
3447
|
+
>
|
|
3448
|
+
<DialogContent
|
|
3449
|
+
aria-describedby={undefined}
|
|
3450
|
+
className="inset-0 flex h-[100dvh] max-h-none w-full max-w-none translate-x-0 translate-y-0 flex-col overflow-hidden rounded-none p-0"
|
|
3451
|
+
>
|
|
3452
|
+
<DialogHeader className="sr-only">
|
|
3453
|
+
<DialogTitle>
|
|
3454
|
+
{providerChoice
|
|
3455
|
+
? `Connect ${providerChoice.provider.label}`
|
|
3456
|
+
: "Connect integration"}
|
|
3457
|
+
</DialogTitle>
|
|
3458
|
+
</DialogHeader>
|
|
3459
|
+
{providerChoice ? (
|
|
3460
|
+
<IntegrationConnectionChoice
|
|
3461
|
+
name={providerChoice.provider.label}
|
|
3462
|
+
logo={logoForProvider(
|
|
3463
|
+
providerChoice.provider.id,
|
|
3464
|
+
providerChoice.provider.label,
|
|
3465
|
+
)}
|
|
3466
|
+
showWorkspaceOption={canManageConnections}
|
|
3467
|
+
onPersonal={() => {
|
|
3468
|
+
setPersonalIntegrationId(providerChoice.personalIntegration.id);
|
|
3469
|
+
setProviderChoice(null);
|
|
3470
|
+
}}
|
|
3471
|
+
onWorkspace={() => {
|
|
3472
|
+
const provider = providerChoice.provider;
|
|
3473
|
+
setProviderChoice(null);
|
|
3474
|
+
if (providerHasOAuth(provider)) {
|
|
3475
|
+
startWorkspaceProviderOAuth(provider);
|
|
3476
|
+
} else {
|
|
3477
|
+
openSetup(provider);
|
|
3478
|
+
}
|
|
3479
|
+
}}
|
|
3480
|
+
/>
|
|
3481
|
+
) : null}
|
|
3482
|
+
</DialogContent>
|
|
3483
|
+
</Dialog>
|
|
3484
|
+
{personalIntegrationId ? (
|
|
3485
|
+
<McpIntegrationDialog
|
|
3486
|
+
open
|
|
3487
|
+
integrations={[
|
|
3488
|
+
personalIntegrations.get(personalIntegrationId),
|
|
3489
|
+
].filter((integration): integration is DefaultMcpIntegration =>
|
|
3490
|
+
Boolean(integration),
|
|
3491
|
+
)}
|
|
3492
|
+
connectIntegrationId={personalIntegrationId}
|
|
3493
|
+
defaultScope="user"
|
|
3494
|
+
canCreateOrgMcp={false}
|
|
3495
|
+
hasOrg={false}
|
|
3496
|
+
onOpenChange={(open) => {
|
|
3497
|
+
if (!open) setPersonalIntegrationId(null);
|
|
3498
|
+
}}
|
|
3499
|
+
onCreateMcpServer={createPersonalMcpServer.mutateAsync}
|
|
3500
|
+
/>
|
|
3501
|
+
) : null}
|
|
3502
|
+
<GroupEditor
|
|
3503
|
+
open={groupEditor !== null}
|
|
3504
|
+
group={groupEditor?.group ?? null}
|
|
3505
|
+
saving={upsertGroup.isPending}
|
|
3506
|
+
onClose={() => setGroupEditor(null)}
|
|
3507
|
+
onSave={handleSaveGroup}
|
|
2625
3508
|
/>
|
|
2626
3509
|
<DeleteConfirm
|
|
2627
3510
|
connection={deleteTarget}
|
|
@@ -2658,7 +3541,7 @@ function IntegrationOnboarding() {
|
|
|
2658
3541
|
];
|
|
2659
3542
|
|
|
2660
3543
|
return (
|
|
2661
|
-
<section className="rounded-lg
|
|
3544
|
+
<section className="rounded-lg bg-muted/30 px-4 py-3">
|
|
2662
3545
|
<div className="dispatch-onboarding-grid grid gap-3">
|
|
2663
3546
|
<div>
|
|
2664
3547
|
<h2 className="text-sm font-semibold text-foreground">
|
|
@@ -2673,7 +3556,7 @@ function IntegrationOnboarding() {
|
|
|
2673
3556
|
const Icon = step.icon;
|
|
2674
3557
|
return (
|
|
2675
3558
|
<div key={step.title} className="flex min-w-0 gap-2">
|
|
2676
|
-
<div className="mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-md
|
|
3559
|
+
<div className="mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-md bg-muted/50">
|
|
2677
3560
|
<Icon size={14} className="text-muted-foreground" />
|
|
2678
3561
|
</div>
|
|
2679
3562
|
<div className="min-w-0">
|
|
@@ -2705,10 +3588,10 @@ function SummaryCard({
|
|
|
2705
3588
|
detail: string;
|
|
2706
3589
|
}) {
|
|
2707
3590
|
return (
|
|
2708
|
-
<div className="rounded-lg
|
|
3591
|
+
<div className="rounded-lg bg-muted/30 p-4">
|
|
2709
3592
|
<div className="flex items-center justify-between gap-3">
|
|
2710
3593
|
<span className="text-sm text-muted-foreground">{label}</span>
|
|
2711
|
-
<div className="flex h-8 w-8 items-center justify-center rounded-md
|
|
3594
|
+
<div className="flex h-8 w-8 items-center justify-center rounded-md bg-muted/50">
|
|
2712
3595
|
<Icon size={16} className="text-muted-foreground" />
|
|
2713
3596
|
</div>
|
|
2714
3597
|
</div>
|