@stack-spot/ai-chat-widget 1.18.0-beta.8 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/dist/StackspotAIWidget.js +1 -1
- package/dist/app-metadata.json +3 -3
- package/dist/components/RightPanelForm.d.ts.map +1 -1
- package/dist/components/RightPanelForm.js +1 -29
- package/dist/components/RightPanelForm.js.map +1 -1
- package/dist/components/Selector/index.js +5 -5
- package/dist/components/Selector/index.js.map +1 -1
- package/dist/components/Selector/styled.d.ts +1 -3
- package/dist/components/Selector/styled.d.ts.map +1 -1
- package/dist/components/Selector/styled.js +1 -2
- package/dist/components/Selector/styled.js.map +1 -1
- package/dist/components/form/DescribedCheckboxGroup.d.ts.map +1 -1
- package/dist/components/form/DescribedCheckboxGroup.js +2 -23
- package/dist/components/form/DescribedCheckboxGroup.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/state/ChatEntry.d.ts +4 -0
- package/dist/state/ChatEntry.d.ts.map +1 -1
- package/dist/state/ChatEntry.js.map +1 -1
- package/dist/views/Agents/AgentsPanel.d.ts.map +1 -1
- package/dist/views/Agents/AgentsPanel.js +11 -19
- package/dist/views/Agents/AgentsPanel.js.map +1 -1
- package/dist/views/Agents/AgentsTab.d.ts +3 -9
- package/dist/views/Agents/AgentsTab.d.ts.map +1 -1
- package/dist/views/Agents/AgentsTab.js +7 -25
- package/dist/views/Agents/AgentsTab.js.map +1 -1
- package/dist/views/Agents/dictionary.d.ts +1 -1
- package/dist/views/Agents/dictionary.d.ts.map +1 -1
- package/dist/views/Agents/dictionary.js +0 -2
- package/dist/views/Agents/dictionary.js.map +1 -1
- package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
- package/dist/views/Chat/ChatMessage.js +5 -5
- package/dist/views/Chat/ChatMessage.js.map +1 -1
- package/dist/views/ChatHistory/utils.d.ts.map +1 -1
- package/dist/views/ChatHistory/utils.js +3 -12
- package/dist/views/ChatHistory/utils.js.map +1 -1
- package/dist/views/KnowledgeSources.d.ts +0 -12
- package/dist/views/KnowledgeSources.d.ts.map +1 -1
- package/dist/views/KnowledgeSources.js +6 -20
- package/dist/views/KnowledgeSources.js.map +1 -1
- package/dist/views/MessageInput/AgentSelector.d.ts.map +1 -1
- package/dist/views/MessageInput/AgentSelector.js +7 -11
- package/dist/views/MessageInput/AgentSelector.js.map +1 -1
- package/dist/views/MessageInput/ButtonGroup.js +2 -2
- package/dist/views/MessageInput/ButtonGroup.js.map +1 -1
- package/dist/views/MessageInput/QuickCommandSelector.d.ts.map +1 -1
- package/dist/views/MessageInput/QuickCommandSelector.js +4 -11
- package/dist/views/MessageInput/QuickCommandSelector.js.map +1 -1
- package/dist/views/MessageInput/dictionary.d.ts +1 -1
- package/dist/views/MessageInput/dictionary.d.ts.map +1 -1
- package/dist/views/MessageInput/dictionary.js +0 -2
- package/dist/views/MessageInput/dictionary.js.map +1 -1
- package/dist/views/Stacks.d.ts +0 -9
- package/dist/views/Stacks.d.ts.map +1 -1
- package/dist/views/Stacks.js +14 -37
- package/dist/views/Stacks.js.map +1 -1
- package/dist/views/{Workspaces/index.d.ts → Workspaces.d.ts} +1 -1
- package/dist/views/Workspaces.d.ts.map +1 -0
- package/dist/views/Workspaces.js +103 -0
- package/dist/views/Workspaces.js.map +1 -0
- package/package.json +2 -2
- package/src/app-metadata.json +3 -3
- package/src/components/RightPanelForm.tsx +1 -29
- package/src/components/Selector/index.tsx +5 -5
- package/src/components/Selector/styled.ts +2 -3
- package/src/components/form/DescribedCheckboxGroup.tsx +14 -45
- package/src/index.ts +2 -0
- package/src/state/ChatEntry.ts +4 -0
- package/src/views/Agents/AgentsPanel.tsx +11 -21
- package/src/views/Agents/AgentsTab.tsx +9 -42
- package/src/views/Agents/dictionary.ts +0 -3
- package/src/views/Chat/ChatMessage.tsx +6 -5
- package/src/views/ChatHistory/utils.ts +3 -13
- package/src/views/KnowledgeSources.tsx +14 -37
- package/src/views/MessageInput/AgentSelector.tsx +8 -19
- package/src/views/MessageInput/ButtonGroup.tsx +3 -3
- package/src/views/MessageInput/QuickCommandSelector.tsx +4 -15
- package/src/views/MessageInput/dictionary.ts +0 -2
- package/src/views/Stacks.tsx +17 -57
- package/src/views/Workspaces.tsx +137 -0
- package/dist/components/ComponentNavigator.d.ts +0 -21
- package/dist/components/ComponentNavigator.d.ts.map +0 -1
- package/dist/components/ComponentNavigator.js +0 -33
- package/dist/components/ComponentNavigator.js.map +0 -1
- package/dist/components/ListGroup.d.ts +0 -46
- package/dist/components/ListGroup.d.ts.map +0 -1
- package/dist/components/ListGroup.js +0 -16
- package/dist/components/ListGroup.js.map +0 -1
- package/dist/components/WorkspaceTabNavigator.d.ts +0 -17
- package/dist/components/WorkspaceTabNavigator.d.ts.map +0 -1
- package/dist/components/WorkspaceTabNavigator.js +0 -95
- package/dist/components/WorkspaceTabNavigator.js.map +0 -1
- package/dist/views/Workspaces/WorkspacesTab.d.ts +0 -20
- package/dist/views/Workspaces/WorkspacesTab.d.ts.map +0 -1
- package/dist/views/Workspaces/WorkspacesTab.js +0 -64
- package/dist/views/Workspaces/WorkspacesTab.js.map +0 -1
- package/dist/views/Workspaces/index.d.ts.map +0 -1
- package/dist/views/Workspaces/index.js +0 -76
- package/dist/views/Workspaces/index.js.map +0 -1
- package/src/components/ComponentNavigator.tsx +0 -78
- package/src/components/ListGroup.tsx +0 -76
- package/src/components/WorkspaceTabNavigator.tsx +0 -177
- package/src/views/Workspaces/WorkspacesTab.tsx +0 -121
- package/src/views/Workspaces/index.tsx +0 -85
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { Flex } from '@citric/core'
|
|
2
|
-
import { ArrowLeft } from '@citric/icons'
|
|
3
|
-
import { IconButton } from '@citric/ui'
|
|
4
|
-
import { createContext, useCallback, useContext, useMemo, useState } from 'react'
|
|
5
|
-
|
|
6
|
-
export type NavigationMap = Record<string, React.ComponentType<any>>
|
|
7
|
-
|
|
8
|
-
export type NavigationItem<T extends NavigationMap, K extends keyof T = keyof T> = {
|
|
9
|
-
component: K,
|
|
10
|
-
props?: T[K] extends React.ComponentType<infer P> ? P : never,
|
|
11
|
-
fullScreen?: boolean,
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface NavigationContextType<T extends NavigationMap> {
|
|
15
|
-
navigate: <K extends keyof T>(item: NavigationItem<T, K>) => Promise<void>,
|
|
16
|
-
goBack: () => void,
|
|
17
|
-
canGoBack: boolean,
|
|
18
|
-
currentItem: NavigationItem<T>,
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface ComponentNavigatorProps<T extends NavigationMap, K extends keyof T> {
|
|
22
|
-
initialItem: NavigationItem<T, K>,
|
|
23
|
-
components: T,
|
|
24
|
-
renderTitle?: (item: NavigationItem<T, keyof T>) => React.ReactNode,
|
|
25
|
-
className?: string,
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const NavigationContext = createContext<NavigationContextType<NavigationMap> | null>(null)
|
|
29
|
-
|
|
30
|
-
export function ComponentNavigator<T extends NavigationMap, K extends keyof T>({
|
|
31
|
-
initialItem,
|
|
32
|
-
components,
|
|
33
|
-
renderTitle,
|
|
34
|
-
className,
|
|
35
|
-
}: ComponentNavigatorProps<T, K>) {
|
|
36
|
-
const [navigationStack, setNavigationStack] = useState<NavigationItem<T>[]>([initialItem])
|
|
37
|
-
const currentItem = navigationStack[navigationStack.length - 1]
|
|
38
|
-
|
|
39
|
-
const navigate = useCallback((item: NavigationItem<T>) => { setNavigationStack((prev) => [...prev, item]) }, [])
|
|
40
|
-
const canGoBack = navigationStack.length > 1
|
|
41
|
-
const goBack = useCallback(() => {
|
|
42
|
-
if (canGoBack) {
|
|
43
|
-
setNavigationStack((prev) => prev.slice(0, -1))
|
|
44
|
-
}
|
|
45
|
-
}, [canGoBack])
|
|
46
|
-
|
|
47
|
-
const navigationContext = useMemo(() => ({ navigate, goBack, canGoBack, currentItem }), [navigate, goBack, canGoBack, currentItem])
|
|
48
|
-
const Component = components[currentItem.component]
|
|
49
|
-
const isFullScreen = currentItem.fullScreen
|
|
50
|
-
|
|
51
|
-
if (!Component) {
|
|
52
|
-
// eslint-disable-next-line no-console
|
|
53
|
-
console.error(`Componente not found: ${String(currentItem.component)}`)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return (<NavigationContext.Provider value={navigationContext as NavigationContextType<NavigationMap>}>
|
|
57
|
-
<div className={`content-navigator ${className || isFullScreen ? 'full' : ''}`} role="navigation">
|
|
58
|
-
{canGoBack && (
|
|
59
|
-
<Flex alignItems="center" w={12} sx={{ gap: '4px' }}>
|
|
60
|
-
<IconButton onClick={goBack} appearance="square" className="back-button" aria-label="Back">
|
|
61
|
-
<ArrowLeft />
|
|
62
|
-
</IconButton>
|
|
63
|
-
{renderTitle?.(currentItem)}
|
|
64
|
-
</Flex>
|
|
65
|
-
)}
|
|
66
|
-
<Component {...(currentItem.props as any)} />
|
|
67
|
-
</div>
|
|
68
|
-
</NavigationContext.Provider>
|
|
69
|
-
)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function useComponentNavigation<T extends NavigationMap>(): NavigationContextType<T> {
|
|
73
|
-
const context = useContext(NavigationContext)
|
|
74
|
-
if (!context) {
|
|
75
|
-
throw new Error('useComponentNavigation should be used inside ComponentNavigator')
|
|
76
|
-
}
|
|
77
|
-
return context
|
|
78
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { Box, Flex, Text } from '@citric/core'
|
|
2
|
-
import { WithStyle } from '@stack-spot/portal-theme'
|
|
3
|
-
import { useMemo } from 'react'
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
interface ListGroupProps<T> extends WithStyle {
|
|
7
|
-
/**
|
|
8
|
-
* The list available.
|
|
9
|
-
*/
|
|
10
|
-
list: T[],
|
|
11
|
-
/**
|
|
12
|
-
* A function that renders an option as a label. This can either return a string or a React Element.
|
|
13
|
-
*/
|
|
14
|
-
renderLabel: (item: T) => React.ReactNode,
|
|
15
|
-
/**
|
|
16
|
-
* A function that renders an option as a description. This can either return a string or a React Element.
|
|
17
|
-
*/
|
|
18
|
-
renderDescription?: (item: T) => React.ReactNode,
|
|
19
|
-
/**
|
|
20
|
-
* A function that renders a element before the content
|
|
21
|
-
*/
|
|
22
|
-
renderBeforeElement?: (item: T) => React.ReactNode,
|
|
23
|
-
/**
|
|
24
|
-
* A function that renders a element after the content
|
|
25
|
-
*/
|
|
26
|
-
renderAfterElement?: (item: T) => React.ReactNode,
|
|
27
|
-
/**
|
|
28
|
-
* A function that gives a custom className to the rendered option.
|
|
29
|
-
*/
|
|
30
|
-
optionClassName?: (item: T) => string | undefined,
|
|
31
|
-
/**
|
|
32
|
-
* A function that gives a custom style to the rendered option.
|
|
33
|
-
*/
|
|
34
|
-
optionStyle?: (item: T) => React.CSSProperties | undefined,
|
|
35
|
-
/**
|
|
36
|
-
* A function that generates a unique id for the option.
|
|
37
|
-
*/
|
|
38
|
-
keygen: (item: T) => React.Key,
|
|
39
|
-
/**
|
|
40
|
-
* A function to call whenever the item click.
|
|
41
|
-
*/
|
|
42
|
-
onClick: (item: T) => void,
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Renders a radio button group where each option has a label and a description.
|
|
47
|
-
* The description in placed under the label and radio button as an accordion.
|
|
48
|
-
*/
|
|
49
|
-
export function ListGroup<T>(
|
|
50
|
-
{
|
|
51
|
-
list,
|
|
52
|
-
renderLabel,
|
|
53
|
-
renderBeforeElement,
|
|
54
|
-
renderAfterElement,
|
|
55
|
-
className,
|
|
56
|
-
keygen,
|
|
57
|
-
style }: ListGroupProps<T>,
|
|
58
|
-
) {
|
|
59
|
-
const items = useMemo(() => list.map((listItem) => {
|
|
60
|
-
const label = renderLabel(listItem)
|
|
61
|
-
const content = typeof label === 'string' ? <Text>{label}</Text> : label
|
|
62
|
-
|
|
63
|
-
return (
|
|
64
|
-
<li key={keygen(listItem)}>
|
|
65
|
-
<Flex alignItems="center">
|
|
66
|
-
{renderBeforeElement?.(listItem)}
|
|
67
|
-
{content}
|
|
68
|
-
{renderAfterElement?.(listItem)}
|
|
69
|
-
</Flex>
|
|
70
|
-
</li>
|
|
71
|
-
)
|
|
72
|
-
}), [list])
|
|
73
|
-
|
|
74
|
-
return <Box as="ul" m={0} p={0} w={12} style={style} className={className}>{items}</Box>
|
|
75
|
-
}
|
|
76
|
-
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import { Box, Flex, IconBox, Image, Text } from '@citric/core'
|
|
2
|
-
import { ArrowRight, Circle, Search, Times } from '@citric/icons'
|
|
3
|
-
import { Avatar, IconButton } from '@citric/ui'
|
|
4
|
-
import { Placeholder } from '@stack-spot/portal-components/Placeholder'
|
|
5
|
-
import { workspaceAiClient } from '@stack-spot/portal-network'
|
|
6
|
-
import { WorkspaceResponse, WorkspaceVisibilityLevelEnum } from '@stack-spot/portal-network/api/workspace-ai'
|
|
7
|
-
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
8
|
-
import { memo, useMemo, useState } from 'react'
|
|
9
|
-
import { useRightPanel } from '../right-panel/hooks'
|
|
10
|
-
import { ButtonFavorite } from './ButtonFavorite'
|
|
11
|
-
import { ComponentNavigator, ComponentNavigatorProps, NavigationItem, NavigationMap, useComponentNavigation } from './ComponentNavigator'
|
|
12
|
-
import { IconInput } from './IconInput'
|
|
13
|
-
import { ListGroup } from './ListGroup'
|
|
14
|
-
|
|
15
|
-
interface CardSpaceProps {
|
|
16
|
-
onClick: VoidFunction,
|
|
17
|
-
name: string,
|
|
18
|
-
icon: React.ReactElement,
|
|
19
|
-
logoUrl?: string | null,
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export const CardSpace = ({ onClick, name, icon, logoUrl }: CardSpaceProps) =>
|
|
23
|
-
<Flex
|
|
24
|
-
onClick={onClick}
|
|
25
|
-
flex={1}
|
|
26
|
-
alignItems="center"
|
|
27
|
-
justifyContent="space-between"
|
|
28
|
-
mr={2}
|
|
29
|
-
bg="light.400"
|
|
30
|
-
r="sm"
|
|
31
|
-
p={3}
|
|
32
|
-
sx={{ cursor: 'pointer' }}
|
|
33
|
-
>
|
|
34
|
-
<Flex alignContent="center" alignItems="center" sx={{ gap: '8px', m: 1 }} >
|
|
35
|
-
<Avatar size="xxs" appearance="square" sx={{ bg: 'light.600', r: 'xxs' }}>
|
|
36
|
-
{logoUrl ? <Image src={logoUrl} /> : <IconBox> {icon} </IconBox>}
|
|
37
|
-
</Avatar>
|
|
38
|
-
<Text appearance="body2">{name}</Text>
|
|
39
|
-
</Flex>
|
|
40
|
-
<IconButton><ArrowRight /></IconButton>
|
|
41
|
-
</Flex>
|
|
42
|
-
|
|
43
|
-
interface WorkspaceSourcesTabProps {
|
|
44
|
-
visibility: WorkspaceVisibilityLevelEnum,
|
|
45
|
-
onClick: (workspace: WorkspaceResponse) => void,
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const WorkspaceSourcesTab = ({ visibility, onClick }: WorkspaceSourcesTabProps) => {
|
|
49
|
-
const t = useTranslate(dictionary)
|
|
50
|
-
const [filter, setFilter] = useState('')
|
|
51
|
-
const workspaces = workspaceAiClient.workspacesAi.useQuery({ visibility })
|
|
52
|
-
const listFavorites = workspaceAiClient.workspacesAi.useQuery({ visibility: 'favorite' })
|
|
53
|
-
const [addFavorite, pendingAddFav] = workspaceAiClient.addFavoriteWorkspaceAi.useMutation()
|
|
54
|
-
const [removeFavorite, pendingRemoveFav] = workspaceAiClient.removeFavoriteWorkspaceAi.useMutation()
|
|
55
|
-
|
|
56
|
-
// eslint-disable-next-line no-async-promise-executor
|
|
57
|
-
const onAddFavorite = async (idOrSlug: string) => new Promise<boolean>(async (resolve, reject) => {
|
|
58
|
-
try {
|
|
59
|
-
await addFavorite({ workspaceId: idOrSlug })
|
|
60
|
-
await workspaceAiClient.workspacesAi.invalidate()
|
|
61
|
-
if (!pendingAddFav) {
|
|
62
|
-
resolve(true)
|
|
63
|
-
}
|
|
64
|
-
} catch (error) {
|
|
65
|
-
// eslint-disable-next-line no-console
|
|
66
|
-
console.error(error)
|
|
67
|
-
reject(error)
|
|
68
|
-
}
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
// eslint-disable-next-line no-async-promise-executor
|
|
72
|
-
const onRemoveFavorite = (idOrSlug: string) => new Promise<boolean>(async (resolve, reject) => {
|
|
73
|
-
try {
|
|
74
|
-
await removeFavorite({ workspaceId: idOrSlug })
|
|
75
|
-
await workspaceAiClient.workspacesAi.invalidate()
|
|
76
|
-
if (!pendingRemoveFav) {
|
|
77
|
-
resolve(true)
|
|
78
|
-
}
|
|
79
|
-
} catch (error) {
|
|
80
|
-
// eslint-disable-next-line no-console
|
|
81
|
-
console.error(error)
|
|
82
|
-
reject(error)
|
|
83
|
-
}
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
const filtered = useMemo(
|
|
87
|
-
// Recreate the list so that the favorites list is taken into account
|
|
88
|
-
() => filter ? workspaces.filter(w => w.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase())) : [...workspaces],
|
|
89
|
-
[workspaces, filter, listFavorites],
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
return (
|
|
93
|
-
<>
|
|
94
|
-
<Box w={12}>
|
|
95
|
-
<IconInput icon={<Search />} value={filter} onChange={setFilter} className="search" />
|
|
96
|
-
</Box>
|
|
97
|
-
|
|
98
|
-
{!!filtered.length &&
|
|
99
|
-
<ListGroup
|
|
100
|
-
list={filtered}
|
|
101
|
-
keygen={w => w.id}
|
|
102
|
-
onClick={onClick}
|
|
103
|
-
style={{ gap: '6px', display: 'flex', flexDirection: 'column' }}
|
|
104
|
-
renderLabel={w => <CardSpace name={w.name} logoUrl={w.logo} icon={<Circle />} onClick={() => onClick(w)} />}
|
|
105
|
-
renderDescription={w => w.description}
|
|
106
|
-
renderAfterElement={(w) =>
|
|
107
|
-
<ButtonFavorite favorite={{ idOrSlug: w?.id, listFavorites, onAddFavorite, onRemoveFavorite }} />}
|
|
108
|
-
optionClassName={w => (filter && !w.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase()))
|
|
109
|
-
? 'filtered-out'
|
|
110
|
-
: ''
|
|
111
|
-
}
|
|
112
|
-
className="option-list"
|
|
113
|
-
/>
|
|
114
|
-
}
|
|
115
|
-
<Box w={12}>
|
|
116
|
-
{!!workspaces.length && !filtered.length &&
|
|
117
|
-
<Placeholder title={t.noSearchResults} description={t.noSearchResultsDescription} className="no-data-placeholder" />}
|
|
118
|
-
{!workspaces.length && <Placeholder title={t.noData} description={t.noDataDescription} />}
|
|
119
|
-
</Box>
|
|
120
|
-
</>
|
|
121
|
-
)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const WorkspaceHeader = <T extends NavigationMap, K extends keyof T>({ data }: { data: NavigationItem<T, K> }) => {
|
|
126
|
-
const { close: closeRightPanel } = useRightPanel()
|
|
127
|
-
const workspaceId = (data.props as any)['workspaceId']
|
|
128
|
-
if (!workspaceId) return
|
|
129
|
-
|
|
130
|
-
const workspace = workspaceAiClient.workspaceAi.useQuery({ id: workspaceId })
|
|
131
|
-
return <Flex justifyContent="space-between" alignItems="center" flex={1}>
|
|
132
|
-
{data.component === 'workspaceResource' ? 'Spaces' : workspace.name}
|
|
133
|
-
{data.fullScreen && <IconButton title={'t.close'} aria-label={'t.close'} onClick={closeRightPanel}> <Times /> </IconButton>}
|
|
134
|
-
</Flex>
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
interface WorkspaceTabNavigatorProps {
|
|
138
|
-
getNavigateParam: (workspace: WorkspaceResponse) => NavigationItem<NavigationMap, string>,
|
|
139
|
-
visibility?: WorkspaceVisibilityLevelEnum,
|
|
140
|
-
className?: string,
|
|
141
|
-
}
|
|
142
|
-
export function WorkspaceTabNavigator<T extends NavigationMap, K extends keyof T>({ components, getNavigateParam, visibility, className }:
|
|
143
|
-
Omit<ComponentNavigatorProps<T, K>, 'initialItem'> & WorkspaceTabNavigatorProps) {
|
|
144
|
-
|
|
145
|
-
const workspaceTabComponents = useMemo(() => ({
|
|
146
|
-
workspace: memo(function WorkspacesTab() {
|
|
147
|
-
const { navigate } = useComponentNavigation()
|
|
148
|
-
return (<WorkspaceSourcesTab visibility={visibility ?? 'all'} onClick={(w) => navigate(getNavigateParam(w))} />)
|
|
149
|
-
}),
|
|
150
|
-
...components,
|
|
151
|
-
}), [components])
|
|
152
|
-
|
|
153
|
-
return <ComponentNavigator
|
|
154
|
-
initialItem={{ component: 'workspace' }}
|
|
155
|
-
components={workspaceTabComponents}
|
|
156
|
-
className={className}
|
|
157
|
-
renderTitle={(data) => <WorkspaceHeader data={data} />}
|
|
158
|
-
/>
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const dictionary = {
|
|
162
|
-
en: {
|
|
163
|
-
noSearchResults: "Your search didn't yield results.",
|
|
164
|
-
noSearchResultsDescription: 'Please, try another search term.',
|
|
165
|
-
noData: 'There are no spaces yet.',
|
|
166
|
-
noDataDescription: 'Use the AI portal to create new spaces.',
|
|
167
|
-
apply: 'Apply',
|
|
168
|
-
},
|
|
169
|
-
pt: {
|
|
170
|
-
noSearchResults: 'Sua busca não produziu resultados',
|
|
171
|
-
noSearchResultsDescription: 'Por favor, tente outra busca.',
|
|
172
|
-
noData: 'Ainda não há spaces.',
|
|
173
|
-
noDataDescription: 'Use o Portal AI para criar novos spaces.',
|
|
174
|
-
apply: 'Apply',
|
|
175
|
-
},
|
|
176
|
-
} satisfies Dictionary
|
|
177
|
-
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { Button, Flex, IconBox, Image, Text } from '@citric/core'
|
|
2
|
-
import { Agent, Circle, KnowledgeSource, Stack } from '@citric/icons'
|
|
3
|
-
import { Avatar } from '@citric/ui'
|
|
4
|
-
import { workspaceAiClient } from '@stack-spot/portal-network'
|
|
5
|
-
import { WorkspaceResponse, WorkspaceVisibilityLevelEnum } from '@stack-spot/portal-network/api/workspace-ai'
|
|
6
|
-
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
7
|
-
import { useTransition } from 'react'
|
|
8
|
-
import { useComponentNavigation } from '../../components/ComponentNavigator'
|
|
9
|
-
import { Loading } from '../../components/FallbackBoundary/Loading'
|
|
10
|
-
import { ListGroup } from '../../components/ListGroup'
|
|
11
|
-
import { CardSpace, WorkspaceTabNavigator } from '../../components/WorkspaceTabNavigator'
|
|
12
|
-
import { ChatProperties } from '../../state/ChatState'
|
|
13
|
-
import { AgentsTab } from '../Agents/AgentsTab'
|
|
14
|
-
import { KnowledgeSourcesTab } from '../KnowledgeSources'
|
|
15
|
-
import { StacksTab } from '../Stacks'
|
|
16
|
-
|
|
17
|
-
const SpaceCard = ({ workspaceId }: { workspaceId: string }) => {
|
|
18
|
-
const workspace = workspaceAiClient.workspaceAi.useQuery({ id: workspaceId })
|
|
19
|
-
return <Flex flexDirection="column" sx={{ gap: '8px' }}>
|
|
20
|
-
<Flex flexDirection="column" sx={{ gap: '4px' }}>
|
|
21
|
-
<Flex alignContent="center" alignItems="center" sx={{ gap: '8px', m: 1 }} >
|
|
22
|
-
<Avatar size="xxs" appearance="square" sx={{ bg: 'light.600', r: 'xxs' }}>
|
|
23
|
-
{workspace.logo ? <Image src={workspace.logo} /> : <IconBox> <Circle /> </IconBox>}
|
|
24
|
-
</Avatar>
|
|
25
|
-
<Text appearance="body2" weight="medium">{workspace.name} </Text>
|
|
26
|
-
</Flex>
|
|
27
|
-
<Text colorScheme="light.700">{workspace.description}</Text>
|
|
28
|
-
</Flex>
|
|
29
|
-
</Flex>
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface TabProps {
|
|
33
|
-
visibility: WorkspaceVisibilityLevelEnum,
|
|
34
|
-
allKS: React.MutableRefObject<ChatProperties['knowledgeSources']>,
|
|
35
|
-
agent: React.MutableRefObject<ChatProperties['agent']>,
|
|
36
|
-
stack: React.MutableRefObject<ChatProperties['stack']>,
|
|
37
|
-
workspaceId?: string,
|
|
38
|
-
onSubmit: () => void,
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface WorkspaceResource {
|
|
42
|
-
id: string,
|
|
43
|
-
resourceType: 'agent' | 'ks' | 'stack',
|
|
44
|
-
displayName: string,
|
|
45
|
-
icon: React.ReactElement,
|
|
46
|
-
workspaceId?: string,
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const showSubmitButton = false
|
|
50
|
-
|
|
51
|
-
export const WorkspaceResources = ({ workspaceId, allKS, agent, stack }: Omit<TabProps, 'onSubmit'>) => {
|
|
52
|
-
const { navigate } = useComponentNavigation<typeof workspaceTabComponents>()
|
|
53
|
-
const [isPending, startTransition] = useTransition()
|
|
54
|
-
const resourceTypes: WorkspaceResource[] = [
|
|
55
|
-
{ id: 'agents', resourceType: 'agent', displayName: 'Agents', workspaceId, icon: <Agent /> },
|
|
56
|
-
{ id: 'ks', resourceType: 'ks', displayName: 'Knowledge Sources', workspaceId, icon: <KnowledgeSource /> },
|
|
57
|
-
{ id: 'stack', resourceType: 'stack', displayName: 'Stacks', workspaceId, icon: <Stack /> },
|
|
58
|
-
]
|
|
59
|
-
|
|
60
|
-
const handleNavigate = (resource: WorkspaceResource) => {
|
|
61
|
-
startTransition(() => {
|
|
62
|
-
if (resource.resourceType === 'agent')
|
|
63
|
-
navigate({ component: 'agent', props: { visibility: 'WORKSPACE', agent, workspaceId, showSubmitButton }, fullScreen: true })
|
|
64
|
-
|
|
65
|
-
if (resource.resourceType === 'ks')
|
|
66
|
-
navigate({ component: 'ks', props: { visibility: 'workspace', allKS, workspaceId, showSubmitButton }, fullScreen: true })
|
|
67
|
-
|
|
68
|
-
if (resource.resourceType === 'stack')
|
|
69
|
-
navigate({ component: 'stack', props: { visibility: 'workspace', stack, workspaceId, showSubmitButton }, fullScreen: true })
|
|
70
|
-
})
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return (<>
|
|
74
|
-
{workspaceId && <SpaceCard workspaceId={workspaceId} />}
|
|
75
|
-
{isPending ? <Loading /> :
|
|
76
|
-
<ListGroup
|
|
77
|
-
list={resourceTypes}
|
|
78
|
-
keygen={w => w.id}
|
|
79
|
-
onClick={(resource) => { handleNavigate(resource) }}
|
|
80
|
-
renderLabel={r => <CardSpace name={r.displayName} icon={r.icon} onClick={() => handleNavigate(r)} />}
|
|
81
|
-
style={{ gap: '6px', display: 'flex', flexDirection: 'column' }}
|
|
82
|
-
/>
|
|
83
|
-
}
|
|
84
|
-
</>
|
|
85
|
-
)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const workspaceTabComponents = {
|
|
89
|
-
workspaceResource: WorkspaceResources,
|
|
90
|
-
agent: AgentsTab,
|
|
91
|
-
ks: KnowledgeSourcesTab,
|
|
92
|
-
stack: StacksTab,
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export function WorkspacesTab({ visibility, allKS, agent, stack, onSubmit }: TabProps) {
|
|
96
|
-
const t = useTranslate(dictionary)
|
|
97
|
-
const buildNavigateParams = (workspace: WorkspaceResponse) => ({
|
|
98
|
-
component: 'workspaceResource',
|
|
99
|
-
props: { allKS, agent, stack, visibility, workspaceId: workspace.id } satisfies Omit<TabProps, 'onSubmit'>,
|
|
100
|
-
fullScreen: true,
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
return (<>
|
|
104
|
-
<WorkspaceTabNavigator
|
|
105
|
-
components={workspaceTabComponents}
|
|
106
|
-
getNavigateParam={buildNavigateParams}
|
|
107
|
-
visibility={visibility}
|
|
108
|
-
/>
|
|
109
|
-
<Button className="workspace-submit" onClick={onSubmit} >{t.apply}</Button>
|
|
110
|
-
</>
|
|
111
|
-
)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const dictionary = {
|
|
115
|
-
en: {
|
|
116
|
-
apply: 'Apply',
|
|
117
|
-
},
|
|
118
|
-
pt: {
|
|
119
|
-
apply: 'Aplicar',
|
|
120
|
-
},
|
|
121
|
-
} satisfies Dictionary
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
2
|
-
import { useCallback, useEffect, useRef } from 'react'
|
|
3
|
-
import { RightPanelTabs } from '../../components/RightPanelTabs'
|
|
4
|
-
import { useCurrentChat, useWidget, useWidgetState } from '../../context/hooks'
|
|
5
|
-
import { useRightPanel } from '../../right-panel/hooks'
|
|
6
|
-
import { WorkspacesTab } from './WorkspacesTab'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Renders the Workspace selection form in the Right Panel if this is the panel that is currently opened.
|
|
10
|
-
*/
|
|
11
|
-
export const Workspaces = () => {
|
|
12
|
-
const t = useTranslate(dictionary)
|
|
13
|
-
const panel = useWidgetState('panel')
|
|
14
|
-
const { open } = useRightPanel()
|
|
15
|
-
const widget = useWidget()
|
|
16
|
-
const chat = useCurrentChat()
|
|
17
|
-
|
|
18
|
-
useEffect(() => {
|
|
19
|
-
if (panel === 'workspace') open(
|
|
20
|
-
<WorkspacesPanel key={chat.id} />,
|
|
21
|
-
{ title: t.title, description: t.description, onClose: () => widget.set('panel', undefined) },
|
|
22
|
-
)
|
|
23
|
-
}, [panel, t, chat.id])
|
|
24
|
-
return null
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const WorkspacesPanel = () => {
|
|
28
|
-
const t = useTranslate(dictionary)
|
|
29
|
-
const chat = useCurrentChat()
|
|
30
|
-
const allKS = useRef(chat.get('knowledgeSources') ?? [])
|
|
31
|
-
const agent = useRef(chat.get('agent'))
|
|
32
|
-
const stack = useRef(chat.get('stack'))
|
|
33
|
-
const { close } = useRightPanel()
|
|
34
|
-
|
|
35
|
-
const onSubmit = useCallback(() => {
|
|
36
|
-
chat.set('knowledgeSources', allKS.current)
|
|
37
|
-
chat.set('stack', stack.current)
|
|
38
|
-
chat.set('agent', agent.current)
|
|
39
|
-
close()
|
|
40
|
-
}, [chat])
|
|
41
|
-
|
|
42
|
-
useEffect(() => {
|
|
43
|
-
allKS.current = chat.get('knowledgeSources') ?? []
|
|
44
|
-
agent.current = chat.get('agent')
|
|
45
|
-
stack.current = chat.get('stack')
|
|
46
|
-
}, [chat])
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return <RightPanelTabs key={chat.id} tabs={[
|
|
50
|
-
{
|
|
51
|
-
title: t.favorites,
|
|
52
|
-
content: <WorkspacesTab key="favorite" visibility="favorite" allKS={allKS} agent={agent} stack={stack} onSubmit={onSubmit} />,
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
title: t.all,
|
|
56
|
-
content: <WorkspacesTab key="all" visibility="all" allKS={allKS} agent={agent} stack={stack} onSubmit={onSubmit} />,
|
|
57
|
-
},
|
|
58
|
-
]}
|
|
59
|
-
/>
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const dictionary = {
|
|
63
|
-
en: {
|
|
64
|
-
title: 'Spaces',
|
|
65
|
-
description: 'By selecting a space, its Knowledge Sources (KSs), Agents, Quick Commands and Stacks Ai will be consulted to generate the answers.',
|
|
66
|
-
apply: 'Apply',
|
|
67
|
-
noSearchResults: "Your search didn't yield results.",
|
|
68
|
-
noSearchResultsDescription: 'Please, try another search term.',
|
|
69
|
-
noData: 'There are no spaces yet.',
|
|
70
|
-
noDataDescription: 'Use the AI portal to create new spaces.',
|
|
71
|
-
all: 'All',
|
|
72
|
-
favorites: 'Favorites',
|
|
73
|
-
},
|
|
74
|
-
pt: {
|
|
75
|
-
title: 'Spaces',
|
|
76
|
-
description: 'Ao selecionar um space, seus Knowledge Sources (KSs), Agentes, Quick Commands e Stacks Ai serão consultados para gerar as respostas.',
|
|
77
|
-
apply: 'Aplicar',
|
|
78
|
-
noSearchResults: 'Sua busca não produziu resultados',
|
|
79
|
-
noSearchResultsDescription: 'Por favor, tente outra busca.',
|
|
80
|
-
noData: 'Ainda não há spaces.',
|
|
81
|
-
noDataDescription: 'Use o Portal AI para criar novos spaces.',
|
|
82
|
-
all: 'Todos',
|
|
83
|
-
favorites: 'Favoritos',
|
|
84
|
-
},
|
|
85
|
-
} satisfies Dictionary
|