mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
fix(resource): prevent permission-gated breadcrumb items from flashing on load (#4732)
* improvement(kb-connectors): align connector UI surfaces and strip redundant description suffix - Fix icon colors to use --text-icon token across connector cards and modal - Switch Reconnect/Update access buttons from active to primary variant - Lift search box surface from --surface-2 to --surface-3 so it's visible against modal bg - Replace raw <button> elements with emcn Button in base.tsx - Shrink Connected Sources modal from lg to md - Strip " to/into/from your knowledge base" from all 27 connector descriptions to prevent overflow * fix(resource): replace raw pagination button with emcn Button * fix(resource): prevent permission-gated breadcrumb items from flashing on load * fix(resource): self-remove keydown listener on Escape and include session loading in isLoading guard
This commit is contained in:
@@ -219,6 +219,36 @@ export const ResourceTable = memo(function ResourceTable({
|
||||
direction: 'desc',
|
||||
})
|
||||
|
||||
const [contextMenuRowId, setContextMenuRowId] = useState<string | null>(null)
|
||||
|
||||
const wrappedOnRowContextMenu = useCallback(
|
||||
(e: React.MouseEvent, rowId: string) => {
|
||||
setContextMenuRowId(rowId)
|
||||
onRowContextMenu?.(e, rowId)
|
||||
},
|
||||
[onRowContextMenu]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!contextMenuRowId) return
|
||||
const clear = () => setContextMenuRowId(null)
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
document.removeEventListener('keydown', handleKeyDown)
|
||||
clear()
|
||||
}
|
||||
}
|
||||
const timeoutId = setTimeout(() => {
|
||||
document.addEventListener('pointerdown', clear, { once: true })
|
||||
document.addEventListener('keydown', handleKeyDown)
|
||||
}, 0)
|
||||
return () => {
|
||||
clearTimeout(timeoutId)
|
||||
document.removeEventListener('pointerdown', clear)
|
||||
document.removeEventListener('keydown', handleKeyDown)
|
||||
}
|
||||
}, [contextMenuRowId])
|
||||
|
||||
const handleSort = useCallback((column: string, direction: 'asc' | 'desc') => {
|
||||
setInternalSort({ column, direction })
|
||||
}, [])
|
||||
@@ -343,7 +373,8 @@ export const ResourceTable = memo(function ResourceTable({
|
||||
rowDragDrop={rowDragDrop}
|
||||
onRowClick={onRowClick}
|
||||
onRowHover={onRowHover}
|
||||
onRowContextMenu={onRowContextMenu}
|
||||
onRowContextMenu={onRowContextMenu ? wrappedOnRowContextMenu : undefined}
|
||||
isContextMenuTarget={contextMenuRowId === row.id}
|
||||
hasCheckbox={hasCheckbox}
|
||||
/>
|
||||
))}
|
||||
@@ -403,17 +434,18 @@ const Pagination = memo(function Pagination({
|
||||
}
|
||||
if (page < 1 || page > totalPages) return null
|
||||
return (
|
||||
<button
|
||||
<Button
|
||||
key={page}
|
||||
type='button'
|
||||
variant='ghost'
|
||||
onClick={() => onPageChange(page)}
|
||||
className={cn(
|
||||
'font-medium text-sm transition-colors hover-hover:text-[var(--text-body)]',
|
||||
'h-auto p-0 font-medium text-sm transition-colors hover-hover:bg-transparent hover-hover:text-[var(--text-body)]',
|
||||
page === currentPage ? 'text-[var(--text-body)]' : 'text-[var(--text-secondary)]'
|
||||
)}
|
||||
>
|
||||
{page}
|
||||
</button>
|
||||
</Button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
@@ -460,6 +492,7 @@ interface DataRowProps {
|
||||
onRowClick?: (rowId: string) => void
|
||||
onRowHover?: (rowId: string) => void
|
||||
onRowContextMenu?: (e: React.MouseEvent, rowId: string) => void
|
||||
isContextMenuTarget?: boolean
|
||||
hasCheckbox: boolean
|
||||
}
|
||||
|
||||
@@ -472,6 +505,7 @@ const DataRow = memo(function DataRow({
|
||||
onRowClick,
|
||||
onRowHover,
|
||||
onRowContextMenu,
|
||||
isContextMenuTarget,
|
||||
hasCheckbox,
|
||||
}: DataRowProps) {
|
||||
const isSelected = selectable?.selectedIds.has(row.id) ?? false
|
||||
@@ -554,7 +588,7 @@ const DataRow = memo(function DataRow({
|
||||
onRowClick && 'cursor-pointer',
|
||||
isDraggable && 'cursor-grab active:cursor-grabbing',
|
||||
isDropTarget && 'data-[drop-target=true]:outline-offset-[-1px]',
|
||||
(selectedRowId === row.id || isSelected) && 'bg-[var(--surface-3)]',
|
||||
(selectedRowId === row.id || isSelected || isContextMenuTarget) && 'bg-[var(--surface-3)]',
|
||||
isActiveDropTarget && 'bg-[var(--surface-4)] outline outline-1 outline-[var(--accent)]',
|
||||
(isDragging || (isAnyDragActive && isSelected && !isActiveDropTarget)) && 'opacity-50'
|
||||
)}
|
||||
|
||||
@@ -1585,10 +1585,11 @@ export function Files() {
|
||||
}
|
||||
: undefined,
|
||||
dropdownItems:
|
||||
isCurrentFolder && canEdit
|
||||
isCurrentFolder && (canEdit || userPermissions.isLoading)
|
||||
? [
|
||||
{
|
||||
label: 'Rename',
|
||||
disabled: !canEdit,
|
||||
onClick: () => breadcrumbRenameRef.current.startRename(folder.id, folder.name),
|
||||
},
|
||||
]
|
||||
@@ -1605,6 +1606,7 @@ export function Files() {
|
||||
router,
|
||||
workspaceId,
|
||||
canEdit,
|
||||
userPermissions.isLoading,
|
||||
breadcrumbRename.editingId,
|
||||
breadcrumbRename.editValue,
|
||||
])
|
||||
|
||||
@@ -814,15 +814,26 @@ export function KnowledgeBase({
|
||||
}
|
||||
: undefined,
|
||||
dropdownItems: [
|
||||
...(userPermissions.canEdit
|
||||
...(userPermissions.canEdit || userPermissions.isLoading
|
||||
? [
|
||||
{
|
||||
label: 'Rename',
|
||||
icon: Pencil,
|
||||
disabled: !userPermissions.canEdit,
|
||||
onClick: () => kbRename.startRename(id, knowledgeBaseName),
|
||||
},
|
||||
{ label: 'Tags', icon: Tag, onClick: () => setShowTagsModal(true) },
|
||||
{ label: 'Delete', icon: Trash, onClick: () => setShowDeleteDialog(true) },
|
||||
{
|
||||
label: 'Tags',
|
||||
icon: Tag,
|
||||
disabled: !userPermissions.canEdit,
|
||||
onClick: () => setShowTagsModal(true),
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
icon: Trash,
|
||||
disabled: !userPermissions.canEdit,
|
||||
onClick: () => setShowDeleteDialog(true),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
@@ -830,8 +841,15 @@ export function KnowledgeBase({
|
||||
]
|
||||
|
||||
const headerActions: HeaderAction[] = [
|
||||
...(userPermissions.canEdit
|
||||
? [{ label: 'New connector', icon: Plus, onClick: () => setShowAddConnectorModal(true) }]
|
||||
...(userPermissions.canEdit || userPermissions.isLoading
|
||||
? [
|
||||
{
|
||||
label: 'New connector',
|
||||
icon: Plus,
|
||||
disabled: !userPermissions.canEdit,
|
||||
onClick: () => setShowAddConnectorModal(true),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]
|
||||
|
||||
@@ -886,18 +904,18 @@ export function KnowledgeBase({
|
||||
/>
|
||||
</div>
|
||||
{enabledFilter.length > 0 && (
|
||||
<button
|
||||
type='button'
|
||||
<Button
|
||||
variant='ghost'
|
||||
onClick={() => {
|
||||
setEnabledFilter([])
|
||||
setCurrentPage(1)
|
||||
setSelectedDocuments(new Set())
|
||||
setIsSelectAllMode(false)
|
||||
}}
|
||||
className='flex h-[32px] w-full items-center justify-center rounded-md text-[var(--text-secondary)] text-caption transition-colors hover-hover:bg-[var(--surface-active)]'
|
||||
className='h-[32px] w-full text-[var(--text-secondary)] text-caption'
|
||||
>
|
||||
Clear status filter
|
||||
</button>
|
||||
</Button>
|
||||
)}
|
||||
<TagFilterSection
|
||||
tagDefinitions={tagDefinitions}
|
||||
@@ -1322,7 +1340,7 @@ export function KnowledgeBase({
|
||||
)}
|
||||
|
||||
<Modal open={showConnectorsModal} onOpenChange={setShowConnectorsModal}>
|
||||
<ModalContent size='lg'>
|
||||
<ModalContent size='md'>
|
||||
<ModalHeader>Connected Sources</ModalHeader>
|
||||
<ModalDescription className='sr-only'>
|
||||
Manage connected data sources for this knowledge base
|
||||
@@ -1535,13 +1553,13 @@ function TagFilterSection({ tagDefinitions, entries, onChange }: TagFilterSectio
|
||||
>
|
||||
<div className='flex items-center justify-between'>
|
||||
<Label className='text-[var(--text-muted)] text-xs'>Tag</Label>
|
||||
<button
|
||||
type='button'
|
||||
<Button
|
||||
variant='ghost'
|
||||
className='size-5 p-0 text-[var(--text-muted)] hover-hover:text-[var(--text-error)]'
|
||||
onClick={() => removeFilter(entry.id)}
|
||||
className='text-[var(--text-muted)] transition-colors hover-hover:text-[var(--text-error)]'
|
||||
>
|
||||
<X className='size-3' />
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
<Combobox
|
||||
options={tagOptions}
|
||||
|
||||
+2
-2
@@ -246,7 +246,7 @@ export function AddConnectorModal({
|
||||
<ModalBody className={step === 'select-type' ? 'pt-2 pb-3' : 'pb-3'}>
|
||||
{step === 'select-type' ? (
|
||||
<div className='flex min-h-0 flex-col gap-2.5'>
|
||||
<div className='flex h-8 items-center gap-2 rounded-md border border-[var(--border)] bg-[var(--surface-2)] px-2 transition-colors duration-100 hover-hover:border-[var(--border-1)] hover-hover:bg-[var(--surface-3)]'>
|
||||
<div className='flex h-8 items-center gap-2 rounded-md border border-[var(--border)] bg-[var(--surface-3)] px-2 transition-colors duration-100 hover-hover:border-[var(--border-1)] hover-hover:bg-[var(--surface-4)]'>
|
||||
<Search
|
||||
className='size-[14px] flex-shrink-0 text-[var(--text-icon)]'
|
||||
strokeWidth={2}
|
||||
@@ -549,7 +549,7 @@ function ConnectorTypeCard({ config, onClick }: ConnectorTypeCardProps) {
|
||||
className='group flex min-h-10 w-full justify-start gap-2 rounded-md px-2 py-1.5 text-left transition-colors hover-hover:bg-[var(--surface-active)]'
|
||||
onClick={onClick}
|
||||
>
|
||||
<Icon className='size-[18px] flex-shrink-0' />
|
||||
<Icon className='size-[18px] flex-shrink-0 text-[var(--text-icon)]' />
|
||||
<div className='flex min-w-0 flex-1 flex-col gap-[1px]'>
|
||||
<span className='truncate font-medium text-[var(--text-body)] text-small'>
|
||||
{config.name}
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ export function ConnectorSelectorField({
|
||||
|
||||
if (isLoading && isEnabled) {
|
||||
return (
|
||||
<div className='flex items-center gap-2 rounded-sm border border-[var(--border-1)] bg-[var(--surface-5)] px-2 py-1.5 font-medium text-[var(--text-muted)] text-sm'>
|
||||
<div className='flex items-center gap-2 rounded-sm border border-[var(--border-1)] bg-[var(--surface-5)] px-2 py-1.5 font-medium text-[var(--text-muted)] text-small'>
|
||||
<Loader className='size-3.5' animate />
|
||||
Loading…
|
||||
</div>
|
||||
|
||||
+3
-3
@@ -372,7 +372,7 @@ function ConnectorCard({
|
||||
<div className='flex items-center justify-between gap-2 px-2 py-2'>
|
||||
<div className='flex min-w-0 items-center gap-2.5'>
|
||||
<div className='relative flex size-9 flex-shrink-0 items-center justify-center rounded-lg bg-[var(--surface-4)]'>
|
||||
{Icon && <Icon className='size-5 text-[var(--text-secondary)]' />}
|
||||
{Icon && <Icon className='size-5 text-[var(--text-icon)]' />}
|
||||
{connector.status === 'disabled' && (
|
||||
<AlertTriangle className='-right-0.5 -top-0.5 absolute size-3 text-[var(--caution)]' />
|
||||
)}
|
||||
@@ -532,7 +532,7 @@ function ConnectorCard({
|
||||
</p>
|
||||
{canEdit && serviceId && providerId && (
|
||||
<Button
|
||||
variant='active'
|
||||
variant='primary'
|
||||
onClick={() => {
|
||||
if (connector.credentialId) {
|
||||
writeOAuthReturnContext({
|
||||
@@ -566,7 +566,7 @@ function ConnectorCard({
|
||||
</div>
|
||||
{canEdit && (
|
||||
<Button
|
||||
variant='active'
|
||||
variant='primary'
|
||||
onClick={() => {
|
||||
if (connector.credentialId) {
|
||||
writeOAuthReturnContext({
|
||||
|
||||
@@ -75,7 +75,7 @@ function parseCursor(cursor?: string): string | undefined {
|
||||
export const airtableConnector: ConnectorConfig = {
|
||||
id: 'airtable',
|
||||
name: 'Airtable',
|
||||
description: 'Sync records from an Airtable table into your knowledge base',
|
||||
description: 'Sync records from an Airtable table',
|
||||
version: '1.0.0',
|
||||
icon: AirtableIcon,
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ async function listWorkspaceProjects(
|
||||
export const asanaConnector: ConnectorConfig = {
|
||||
id: 'asana',
|
||||
name: 'Asana',
|
||||
description: 'Sync tasks from Asana into your knowledge base',
|
||||
description: 'Sync tasks from Asana',
|
||||
version: '1.0.0',
|
||||
icon: AsanaIcon,
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ function cqlResultToStub(item: Record<string, unknown>, domain: string): Externa
|
||||
export const confluenceConnector: ConnectorConfig = {
|
||||
id: 'confluence',
|
||||
name: 'Confluence',
|
||||
description: 'Sync pages from a Confluence space into your knowledge base',
|
||||
description: 'Sync pages from a Confluence space',
|
||||
version: '1.1.0',
|
||||
icon: ConfluenceIcon,
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ function formatMessages(messages: DiscordMessage[]): string {
|
||||
export const discordConnector: ConnectorConfig = {
|
||||
id: 'discord',
|
||||
name: 'Discord',
|
||||
description: 'Sync channel messages from Discord into your knowledge base',
|
||||
description: 'Sync channel messages from Discord',
|
||||
version: '1.0.0',
|
||||
icon: DiscordIcon,
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ function fileToStub(entry: DropboxFileEntry): ExternalDocument {
|
||||
export const dropboxConnector: ConnectorConfig = {
|
||||
id: 'dropbox',
|
||||
name: 'Dropbox',
|
||||
description: 'Sync text files from Dropbox into your knowledge base',
|
||||
description: 'Sync text files from Dropbox',
|
||||
version: '1.0.0',
|
||||
icon: DropboxIcon,
|
||||
|
||||
|
||||
@@ -366,7 +366,7 @@ async function apiGetNote(
|
||||
export const evernoteConnector: ConnectorConfig = {
|
||||
id: 'evernote',
|
||||
name: 'Evernote',
|
||||
description: 'Sync notes from Evernote into your knowledge base',
|
||||
description: 'Sync notes from Evernote',
|
||||
version: '1.0.0',
|
||||
icon: EvernoteIcon,
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ function formatTranscriptContent(transcript: FirefliesTranscript): string {
|
||||
export const firefliesConnector: ConnectorConfig = {
|
||||
id: 'fireflies',
|
||||
name: 'Fireflies',
|
||||
description: 'Sync meeting transcripts from Fireflies.ai into your knowledge base',
|
||||
description: 'Sync meeting transcripts from Fireflies.ai',
|
||||
version: '1.0.0',
|
||||
icon: FirefliesIcon,
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ function treeItemToStub(
|
||||
export const githubConnector: ConnectorConfig = {
|
||||
id: 'github',
|
||||
name: 'GitHub',
|
||||
description: 'Sync files from a GitHub repository into your knowledge base',
|
||||
description: 'Sync files from a GitHub repository',
|
||||
version: '1.0.0',
|
||||
icon: GithubIcon,
|
||||
|
||||
|
||||
@@ -339,7 +339,7 @@ function threadToStub(thread: {
|
||||
export const gmailConnector: ConnectorConfig = {
|
||||
id: 'gmail',
|
||||
name: 'Gmail',
|
||||
description: 'Sync email threads from Gmail into your knowledge base',
|
||||
description: 'Sync email threads from Gmail',
|
||||
version: '1.0.0',
|
||||
icon: GmailIcon,
|
||||
|
||||
|
||||
@@ -246,7 +246,7 @@ function eventToDocument(
|
||||
export const googleCalendarConnector: ConnectorConfig = {
|
||||
id: 'google_calendar',
|
||||
name: 'Google Calendar',
|
||||
description: 'Sync calendar events from Google Calendar into your knowledge base',
|
||||
description: 'Sync calendar events from Google Calendar',
|
||||
version: '1.0.0',
|
||||
icon: GoogleCalendarIcon,
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ function buildQuery(sourceConfig: Record<string, unknown>): string {
|
||||
export const googleDocsConnector: ConnectorConfig = {
|
||||
id: 'google_docs',
|
||||
name: 'Google Docs',
|
||||
description: 'Sync Google Docs documents into your knowledge base',
|
||||
description: 'Sync Google Docs documents',
|
||||
version: '1.0.0',
|
||||
icon: GoogleDocsIcon,
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ function fileToStub(file: DriveFile): ExternalDocument {
|
||||
export const googleDriveConnector: ConnectorConfig = {
|
||||
id: 'google_drive',
|
||||
name: 'Google Drive',
|
||||
description: 'Sync documents from Google Drive into your knowledge base',
|
||||
description: 'Sync documents from Google Drive',
|
||||
version: '1.0.0',
|
||||
icon: GoogleDriveIcon,
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ async function sheetToDocument(
|
||||
export const googleSheetsConnector: ConnectorConfig = {
|
||||
id: 'google_sheets',
|
||||
name: 'Google Sheets',
|
||||
description: 'Sync spreadsheet data from Google Sheets into your knowledge base',
|
||||
description: 'Sync spreadsheet data from Google Sheets',
|
||||
version: '1.0.0',
|
||||
icon: GoogleSheetsIcon,
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ function recordToDocument(
|
||||
export const hubspotConnector: ConnectorConfig = {
|
||||
id: 'hubspot',
|
||||
name: 'HubSpot',
|
||||
description: 'Sync CRM records from HubSpot into your knowledge base',
|
||||
description: 'Sync CRM records from HubSpot',
|
||||
version: '1.0.0',
|
||||
icon: HubspotIcon,
|
||||
|
||||
|
||||
@@ -269,7 +269,7 @@ function formatArticle(article: IntercomArticle): string {
|
||||
export const intercomConnector: ConnectorConfig = {
|
||||
id: 'intercom',
|
||||
name: 'Intercom',
|
||||
description: 'Sync Help Center articles and conversations from Intercom into your knowledge base',
|
||||
description: 'Sync Help Center articles and conversations from Intercom',
|
||||
version: '1.0.0',
|
||||
icon: IntercomIcon,
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ function issueToFullDocument(issue: Record<string, unknown>, domain: string): Ex
|
||||
export const jiraConnector: ConnectorConfig = {
|
||||
id: 'jira',
|
||||
name: 'Jira',
|
||||
description: 'Sync issues from a Jira project into your knowledge base',
|
||||
description: 'Sync issues from a Jira project',
|
||||
version: '1.0.0',
|
||||
icon: JiraIcon,
|
||||
|
||||
|
||||
@@ -203,7 +203,7 @@ function buildIssuesQuery(
|
||||
export const linearConnector: ConnectorConfig = {
|
||||
id: 'linear',
|
||||
name: 'Linear',
|
||||
description: 'Sync issues from Linear into your knowledge base',
|
||||
description: 'Sync issues from Linear',
|
||||
version: '1.0.0',
|
||||
icon: LinearIcon,
|
||||
|
||||
|
||||
@@ -193,7 +193,7 @@ async function resolveChannel(
|
||||
export const microsoftTeamsConnector: ConnectorConfig = {
|
||||
id: 'microsoft_teams',
|
||||
name: 'Microsoft Teams',
|
||||
description: 'Sync channel messages from Microsoft Teams into your knowledge base',
|
||||
description: 'Sync channel messages from Microsoft Teams',
|
||||
version: '1.0.0',
|
||||
icon: MicrosoftTeamsIcon,
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ function pageToStub(page: Record<string, unknown>): ExternalDocument {
|
||||
export const notionConnector: ConnectorConfig = {
|
||||
id: 'notion',
|
||||
name: 'Notion',
|
||||
description: 'Sync pages from a Notion workspace into your knowledge base',
|
||||
description: 'Sync pages from a Notion workspace',
|
||||
version: '1.0.0',
|
||||
icon: NotionIcon,
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ function buildListUrl(folderPath?: string): string {
|
||||
export const onedriveConnector: ConnectorConfig = {
|
||||
id: 'onedrive',
|
||||
name: 'OneDrive',
|
||||
description: 'Sync documents from Microsoft OneDrive into your knowledge base',
|
||||
description: 'Sync documents from Microsoft OneDrive',
|
||||
version: '1.0.0',
|
||||
icon: MicrosoftOneDriveIcon,
|
||||
|
||||
|
||||
@@ -269,7 +269,7 @@ function formatConversation(
|
||||
export const outlookConnector: ConnectorConfig = {
|
||||
id: 'outlook',
|
||||
name: 'Outlook',
|
||||
description: 'Sync email conversations from Outlook into your knowledge base',
|
||||
description: 'Sync email conversations from Outlook',
|
||||
version: '1.0.0',
|
||||
icon: OutlookIcon,
|
||||
|
||||
|
||||
@@ -245,7 +245,7 @@ function resolveSortConfig(sourceConfig: Record<string, unknown>): {
|
||||
export const redditConnector: ConnectorConfig = {
|
||||
id: 'reddit',
|
||||
name: 'Reddit',
|
||||
description: 'Sync subreddit posts and comments from Reddit into your knowledge base',
|
||||
description: 'Sync subreddit posts and comments from Reddit',
|
||||
version: '1.0.0',
|
||||
icon: RedditIcon,
|
||||
|
||||
|
||||
@@ -268,7 +268,7 @@ function recordToDocument(
|
||||
export const salesforceConnector: ConnectorConfig = {
|
||||
id: 'salesforce',
|
||||
name: 'Salesforce',
|
||||
description: 'Sync records from Salesforce into your knowledge base',
|
||||
description: 'Sync records from Salesforce',
|
||||
version: '1.0.0',
|
||||
icon: SalesforceIcon,
|
||||
|
||||
|
||||
@@ -279,7 +279,7 @@ function decodeCursor(cursor: string): PaginationState {
|
||||
export const sharepointConnector: ConnectorConfig = {
|
||||
id: 'sharepoint',
|
||||
name: 'SharePoint',
|
||||
description: 'Sync documents from a SharePoint site into your knowledge base',
|
||||
description: 'Sync documents from a SharePoint site',
|
||||
version: '1.0.0',
|
||||
icon: MicrosoftSharepointIcon,
|
||||
|
||||
|
||||
@@ -486,7 +486,7 @@ async function buildSlackChannelDocument(
|
||||
export const slackConnector: ConnectorConfig = {
|
||||
id: 'slack',
|
||||
name: 'Slack',
|
||||
description: 'Sync channel messages from Slack into your knowledge base',
|
||||
description: 'Sync channel messages from Slack',
|
||||
version: '1.0.0',
|
||||
icon: SlackIcon,
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ export const webflowConnector: ConnectorConfig = {
|
||||
id: 'webflow',
|
||||
name: 'Webflow',
|
||||
description:
|
||||
'Sync CMS collection items from a Webflow site into your knowledge base. Note: Webflow OAuth tokens do not support refresh — you may need to reconnect periodically.',
|
||||
'Sync CMS collection items from a Webflow site. Note: Webflow OAuth tokens do not support refresh — you may need to reconnect periodically.',
|
||||
version: '1.0.0',
|
||||
icon: WebflowIcon,
|
||||
|
||||
|
||||
@@ -314,8 +314,7 @@ function ticketToDocument(
|
||||
export const zendeskConnector: ConnectorConfig = {
|
||||
id: 'zendesk',
|
||||
name: 'Zendesk',
|
||||
description:
|
||||
'Sync Help Center articles and support tickets from Zendesk into your knowledge base',
|
||||
description: 'Sync Help Center articles and support tickets from Zendesk',
|
||||
version: '1.0.0',
|
||||
icon: ZendeskIcon,
|
||||
|
||||
|
||||
@@ -33,17 +33,17 @@ export function useUserPermissions(
|
||||
permissionsLoading = false,
|
||||
permissionsError: string | null = null
|
||||
): WorkspaceUserPermissions {
|
||||
const { data: session } = useSession()
|
||||
const { data: session, isPending: sessionLoading } = useSession()
|
||||
|
||||
const userPermissions = useMemo((): WorkspaceUserPermissions => {
|
||||
const sessionEmail = session?.user?.email
|
||||
if (permissionsLoading || !sessionEmail) {
|
||||
if (permissionsLoading || sessionLoading || !sessionEmail) {
|
||||
return {
|
||||
canRead: false,
|
||||
canEdit: false,
|
||||
canAdmin: false,
|
||||
userPermissions: 'read',
|
||||
isLoading: permissionsLoading,
|
||||
isLoading: permissionsLoading || sessionLoading,
|
||||
error: permissionsError,
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,7 @@ export function useUserPermissions(
|
||||
isLoading: false,
|
||||
error: permissionsError,
|
||||
}
|
||||
}, [session, workspacePermissions, permissionsLoading, permissionsError])
|
||||
}, [session, sessionLoading, workspacePermissions, permissionsLoading, permissionsError])
|
||||
|
||||
return userPermissions
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user