@stack-spot/ai-chat-widget 1.14.0-beta.7 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/StackspotAIWidget.d.ts.map +1 -1
  3. package/dist/app-metadata.json +19 -19
  4. package/dist/components/Accordion.d.ts.map +1 -1
  5. package/dist/components/Accordion.js +3 -1
  6. package/dist/components/Accordion.js.map +1 -1
  7. package/dist/components/ButtonFavorite.d.ts +47 -0
  8. package/dist/components/ButtonFavorite.d.ts.map +1 -0
  9. package/dist/components/ButtonFavorite.js +25 -0
  10. package/dist/components/ButtonFavorite.js.map +1 -0
  11. package/dist/components/Code.d.ts.map +1 -1
  12. package/dist/components/FadingOverflow.d.ts.map +1 -1
  13. package/dist/components/FallbackBoundary/index.d.ts.map +1 -1
  14. package/dist/components/IconInput.d.ts.map +1 -1
  15. package/dist/components/Markdown.d.ts.map +1 -1
  16. package/dist/components/Modal.d.ts.map +1 -1
  17. package/dist/components/ProgressBar.d.ts.map +1 -1
  18. package/dist/components/QuickStartButton.d.ts.map +1 -1
  19. package/dist/components/RightPanelForm.d.ts.map +1 -1
  20. package/dist/components/RightPanelTabs.d.ts.map +1 -1
  21. package/dist/components/Selector/index.d.ts +9 -1
  22. package/dist/components/Selector/index.d.ts.map +1 -1
  23. package/dist/components/Selector/index.js +19 -13
  24. package/dist/components/Selector/index.js.map +1 -1
  25. package/dist/components/StackedBadge.d.ts.map +1 -1
  26. package/dist/components/ToolBadge.d.ts.map +1 -1
  27. package/dist/components/Tooltip/context.d.ts.map +1 -1
  28. package/dist/components/form/DescribedCheckboxGroup.d.ts +1 -1
  29. package/dist/components/form/DescribedCheckboxGroup.d.ts.map +1 -1
  30. package/dist/components/form/DescribedCheckboxGroup.js +5 -5
  31. package/dist/components/form/DescribedCheckboxGroup.js.map +1 -1
  32. package/dist/components/form/DescribedRadioGroup.d.ts +1 -1
  33. package/dist/components/form/DescribedRadioGroup.d.ts.map +1 -1
  34. package/dist/components/form/DescribedRadioGroup.js +4 -4
  35. package/dist/components/form/DescribedRadioGroup.js.map +1 -1
  36. package/dist/components/form/dictionary.d.ts +19 -0
  37. package/dist/components/form/dictionary.d.ts.map +1 -0
  38. package/dist/components/form/dictionary.js +19 -0
  39. package/dist/components/form/dictionary.js.map +1 -0
  40. package/dist/components/form/styled.d.ts.map +1 -1
  41. package/dist/components/form/styled.js +1 -2
  42. package/dist/components/form/styled.js.map +1 -1
  43. package/dist/components/form/types.d.ts +8 -0
  44. package/dist/components/form/types.d.ts.map +1 -1
  45. package/dist/right-panel/DefaultPanel.d.ts.map +1 -1
  46. package/dist/right-panel/RightPanelProvider.d.ts.map +1 -1
  47. package/dist/utils/agent.d.ts.map +1 -1
  48. package/dist/views/Agents/AgentDescription.d.ts.map +1 -1
  49. package/dist/views/Agents/AgentsPanel.d.ts.map +1 -1
  50. package/dist/views/Agents/AgentsPanel.js +2 -0
  51. package/dist/views/Agents/AgentsPanel.js.map +1 -1
  52. package/dist/views/Agents/AgentsTab.d.ts.map +1 -1
  53. package/dist/views/Agents/AgentsTab.js +7 -2
  54. package/dist/views/Agents/AgentsTab.js.map +1 -1
  55. package/dist/views/Agents/dictionary.d.ts +1 -1
  56. package/dist/views/Agents/dictionary.d.ts.map +1 -1
  57. package/dist/views/Agents/dictionary.js +2 -0
  58. package/dist/views/Agents/dictionary.js.map +1 -1
  59. package/dist/views/Agents/useAgentFavorites.d.ts +8 -0
  60. package/dist/views/Agents/useAgentFavorites.d.ts.map +1 -0
  61. package/dist/views/Agents/useAgentFavorites.js +31 -0
  62. package/dist/views/Agents/useAgentFavorites.js.map +1 -0
  63. package/dist/views/Chat/AgentInfo.d.ts.map +1 -1
  64. package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
  65. package/dist/views/Chat/ChatMessage.js +2 -2
  66. package/dist/views/Chat/ChatMessages.d.ts.map +1 -1
  67. package/dist/views/Chat/StepsList.d.ts.map +1 -1
  68. package/dist/views/Chat/index.d.ts.map +1 -1
  69. package/dist/views/ChatHistory/HistoryItem.d.ts.map +1 -1
  70. package/dist/views/ChatHistory/utils.d.ts.map +1 -1
  71. package/dist/views/ChatHistory/utils.js +3 -2
  72. package/dist/views/ChatHistory/utils.js.map +1 -1
  73. package/dist/views/Home/BuiltInAgent.d.ts.map +1 -1
  74. package/dist/views/Home/index.d.ts.map +1 -1
  75. package/dist/views/KnowledgeSources.d.ts.map +1 -1
  76. package/dist/views/KnowledgeSources.js +35 -4
  77. package/dist/views/KnowledgeSources.js.map +1 -1
  78. package/dist/views/MessageInput/AgentSelector.d.ts.map +1 -1
  79. package/dist/views/MessageInput/AgentSelector.js +6 -4
  80. package/dist/views/MessageInput/AgentSelector.js.map +1 -1
  81. package/dist/views/MessageInput/ButtonGroup.d.ts.map +1 -1
  82. package/dist/views/MessageInput/QuickCommandSelector.d.ts.map +1 -1
  83. package/dist/views/MessageInput/QuickCommandSelector.js +25 -2
  84. package/dist/views/MessageInput/QuickCommandSelector.js.map +1 -1
  85. package/dist/views/MessageInput/dictionary.d.ts +1 -1
  86. package/dist/views/MinimizedHeader.d.ts.map +1 -1
  87. package/dist/views/Stacks.d.ts.map +1 -1
  88. package/dist/views/Stacks.js +33 -3
  89. package/dist/views/Stacks.js.map +1 -1
  90. package/dist/views/Steps/FlowChart/HandleGroup.d.ts.map +1 -1
  91. package/dist/views/Steps/FlowChart/NodeStep.d.ts.map +1 -1
  92. package/dist/views/Steps/FlowChart/index.d.ts.map +1 -1
  93. package/dist/views/Steps/StepModal.d.ts.map +1 -1
  94. package/dist/views/Steps/StepsPanel.d.ts.map +1 -1
  95. package/dist/views/Workspaces.d.ts.map +1 -1
  96. package/dist/views/Workspaces.js +42 -5
  97. package/dist/views/Workspaces.js.map +1 -1
  98. package/package.json +3 -3
  99. package/src/app-metadata.json +19 -19
  100. package/src/components/Accordion.tsx +3 -2
  101. package/src/components/ButtonFavorite.tsx +100 -0
  102. package/src/components/Selector/index.tsx +45 -16
  103. package/src/components/form/DescribedCheckboxGroup.tsx +27 -11
  104. package/src/components/form/DescribedRadioGroup.tsx +16 -1
  105. package/src/components/form/dictionary.ts +18 -0
  106. package/src/components/form/styled.ts +1 -2
  107. package/src/components/form/types.ts +11 -1
  108. package/src/views/Agents/AgentsPanel.tsx +2 -0
  109. package/src/views/Agents/AgentsTab.tsx +12 -2
  110. package/src/views/Agents/dictionary.ts +2 -0
  111. package/src/views/Agents/useAgentFavorites.ts +32 -0
  112. package/src/views/Chat/ChatMessage.tsx +2 -2
  113. package/src/views/ChatHistory/utils.ts +3 -2
  114. package/src/views/KnowledgeSources.tsx +45 -8
  115. package/src/views/MessageInput/AgentSelector.tsx +8 -3
  116. package/src/views/MessageInput/QuickCommandSelector.tsx +25 -1
  117. package/src/views/Stacks.tsx +39 -4
  118. package/src/views/Workspaces.tsx +49 -8
@@ -1,11 +1,12 @@
1
1
  import { Button } from '@citric/core'
2
2
  import { Search } from '@citric/icons'
3
3
  import { Placeholder } from '@stack-spot/portal-components/Placeholder'
4
- import { aiClient } from '@stack-spot/portal-network'
4
+ import { aiClient, dataIntegrationClient } from '@stack-spot/portal-network'
5
5
  import { KnowledgeSourceItemResponse, VisibilityLevelEnum } from '@stack-spot/portal-network/api/ai'
6
6
  import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
7
7
  import { difference, uniqBy } from 'lodash'
8
8
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
9
+ import { ButtonFavorite } from '../components/ButtonFavorite'
9
10
  import { DescribedCheckboxGroup } from '../components/form/DescribedCheckboxGroup'
10
11
  import { IconInput } from '../components/IconInput'
11
12
  import { RightPanelTabs } from '../components/RightPanelTabs'
@@ -54,10 +55,12 @@ const KnowledgeSourcesPanel = () => {
54
55
  useEffect(() => {
55
56
  allKS.current = chat.get('knowledgeSources') ?? []
56
57
  }, [chat])
57
-
58
+
58
59
  const tabs = isTrial ? [
60
+ { title: t.favorites, content: <KnowledgeSourcesTab key="favorite" visibility="favorite" allKS={allKS} onSubmit={onSubmit} /> },
59
61
  { title: t.personal, content: <KnowledgeSourcesTab key="personal" visibility="personal" allKS={allKS} onSubmit={onSubmit} /> },
60
62
  ]: [
63
+ { title: t.favorites, content: <KnowledgeSourcesTab key="favorite" visibility="favorite" allKS={allKS} onSubmit={onSubmit} /> },
61
64
  { title: t.personal, content: <KnowledgeSourcesTab key="personal" visibility="personal" allKS={allKS} onSubmit={onSubmit} /> },
62
65
  { title: t.shared, content: <KnowledgeSourcesTab key="shared" visibility="shared" allKS={allKS} onSubmit={onSubmit} /> },
63
66
  { title: t.account, content: <KnowledgeSourcesTab key="account" visibility="account" allKS={allKS} onSubmit={onSubmit} /> },
@@ -69,18 +72,46 @@ const KnowledgeSourcesPanel = () => {
69
72
  const KnowledgeSourcesTab = ({ visibility, allKS, onSubmit }: TabProps) => {
70
73
  const t = useTranslate(dictionary)
71
74
  const [filter, setFilter] = useState('')
75
+
72
76
  const knowledgeSources = aiClient.knowledgeSources.useQuery({
73
77
  visibility, order: 'a-to-z', types: ['snippet', 'api', 'event', 'custom'],
74
78
  })
79
+ const listFavorites = dataIntegrationClient.knowledgeSources.useQuery({ visibility: 'favorite' })
80
+ const [addFavorite, pendingAddFav] = dataIntegrationClient.addFavoriteKnowledgeSource.useMutation()
81
+ const [removeFavorite, pendingRemoveFav] = dataIntegrationClient.removeFavoriteKnowledgeSource.useMutation()
82
+
83
+ const onRemoveFavorite = async (idOrSlug: string) => {
84
+ try {
85
+ await removeFavorite({ slug: idOrSlug })
86
+ await aiClient.knowledgeSources.invalidate()
87
+ await dataIntegrationClient.knowledgeSources.invalidate()
88
+ } catch (error) {
89
+ // eslint-disable-next-line no-console
90
+ console.error(error)
91
+ }
92
+ }
93
+
94
+ const onAddFavorite = async (idOrSlug: string) => {
95
+ try {
96
+ await addFavorite({ slug: idOrSlug })
97
+ await aiClient.knowledgeSources.invalidate()
98
+ await dataIntegrationClient.knowledgeSources.invalidate()
99
+ } catch (error) {
100
+ // eslint-disable-next-line no-console
101
+ console.error(error)
102
+ }
103
+ }
104
+
75
105
  const [value, setValue] = useState<KnowledgeSourceItemResponse[]>((() => {
76
106
  const currentlySelected = allKS.current?.map(ks => ks.id)
77
107
  return knowledgeSources.filter(ks => currentlySelected?.includes(ks.slug))
78
108
  })())
79
109
  const filtered = useMemo(
80
- () => filter
81
- ? knowledgeSources.filter(ks => value.includes(ks) || ks.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase()))
82
- : knowledgeSources,
83
- [knowledgeSources, filter, value],
110
+ () => filter
111
+ // Recreate the list so that the favorites list is taken into account
112
+ ? knowledgeSources.filter(ks => value.includes(ks) || ks.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase()))
113
+ : [...knowledgeSources]
114
+ , [knowledgeSources, filter, value, listFavorites],
84
115
  )
85
116
 
86
117
  const onChange = useCallback((newValue: KnowledgeSourceItemResponse[]) => {
@@ -99,6 +130,10 @@ const KnowledgeSourcesTab = ({ visibility, allKS, onSubmit }: TabProps) => {
99
130
  <IconInput icon={<Search />} value={filter} onChange={setFilter} className="search" />
100
131
  {!!filtered.length && <DescribedCheckboxGroup
101
132
  options={filtered}
133
+ renderAfterElement={({ slug }) => (
134
+ <ButtonFavorite
135
+ favorite={{ idOrSlug: slug, listFavorites, onAddFavorite, onRemoveFavorite, pendingAddFav, pendingRemoveFav }} />
136
+ )}
102
137
  keygen={ks => ks.id}
103
138
  value={value}
104
139
  onChange={onChange}
@@ -110,10 +145,10 @@ const KnowledgeSourcesTab = ({ visibility, allKS, onSubmit }: TabProps) => {
110
145
  }
111
146
  className="option-list"
112
147
  />}
113
- {!!knowledgeSources.length && !filtered.length && (
148
+ {!!filtered.length && !filtered.length && (
114
149
  <Placeholder title={t.noSearchResults} description={t.noSearchResultsDescription} />
115
150
  )}
116
- {!knowledgeSources.length && <Placeholder title={t.noData} description={t.noDataDescription} className="no-data-placeholder" />}
151
+ {!filtered.length && <Placeholder title={t.noData} description={t.noDataDescription} className="no-data-placeholder" />}
117
152
  </div>
118
153
  {!!filtered.length && <Button onClick={onSubmit}>{t.apply}</Button>}
119
154
  </>
@@ -124,6 +159,7 @@ const dictionary = {
124
159
  en: {
125
160
  title: 'Knowledge Sources',
126
161
  description: 'By selecting one or more knowledge sources, they will be consulted to generate the responses.',
162
+ favorites: 'Favorites',
127
163
  personal: 'Personal',
128
164
  shared: 'Shared',
129
165
  account: 'Account',
@@ -136,6 +172,7 @@ const dictionary = {
136
172
  pt: {
137
173
  title: 'Knowledge Sources',
138
174
  description: 'Ao selecionar um ou mais knowledge sources, eles serão consultados para gerar as respostas.',
175
+ favorites: 'Favoritos',
139
176
  personal: 'Pessoal',
140
177
  shared: 'Compartilhado',
141
178
  account: 'Conta',
@@ -7,11 +7,15 @@ import { useCallback } from 'react'
7
7
  import { Selector } from '../../components/Selector'
8
8
  import { useCurrentChat, useCurrentChatState } from '../../context/hooks'
9
9
  import { agentRegex } from '../../regex'
10
+ import { useAgentFavorites } from '../Agents/useAgentFavorites'
10
11
 
11
12
  export const AgentSelector = ({ inputRef, isTrial }: { isTrial: boolean,
12
13
  inputRef: React.RefObject<HTMLTextAreaElement | HTMLInputElement>, }) => {
13
14
  const chat = useCurrentChat()
14
15
  const isAgentEnabled = useCurrentChatState('features').agent
16
+
17
+ const { listFavorites, onAddFavorite, onRemoveFavorite, pendingAddFav, pendingRemoveFav } = useAgentFavorites()
18
+
15
19
  const onSelectItem = useCallback((agent: AgentResponse) => {
16
20
  const newValue = `@${agent.slug}`
17
21
  chat.set('nextMessage', undefined)
@@ -36,16 +40,17 @@ export const AgentSelector = ({ inputRef, isTrial }: { isTrial: boolean,
36
40
  }
37
41
 
38
42
  const AgentItem = ({ avatar, name }: AgentResponse) => {
39
- const AvatarComponent = avatar ? <Image width="32" height="32" radius="full" src={avatar} /> : <IconBox size="md"><Agent /></IconBox>
43
+ const avatarComponent = avatar ? <Image width="32" height="32" radius="full" src={avatar} /> : <IconBox size="md"><Agent /></IconBox>
40
44
 
41
45
  return <Flex flexWrap="nowrap" alignItems="center" sx={{ gap: '8px' }}>
42
- {AvatarComponent}
46
+ {avatarComponent}
43
47
  <p className="selector-title">{name}</p>
44
48
  </Flex>
45
49
  }
46
50
 
47
51
  return <Selector
48
52
  inputRef={inputRef}
53
+ favorite={{ listFavorites, onAddFavorite, onRemoveFavorite, pendingAddFav, pendingRemoveFav }}
49
54
  selectorConfig={{
50
55
  resourceName: 'Agent',
51
56
  shortcut: '@',
@@ -53,7 +58,7 @@ export const AgentSelector = ({ inputRef, isTrial }: { isTrial: boolean,
53
58
  regex: agentRegex,
54
59
  urlBuilder: (agent) => `/agents/${agent?.id}`,
55
60
  searchProp: 'name',
56
- sections: isTrial ? ['personal', 'builtIn'] : ['personal', 'account', 'shared', 'builtIn'],
61
+ sections: isTrial ? ['favorite', 'personal', 'builtIn'] : ['favorite', 'personal', 'account', 'shared', 'builtIn'],
57
62
  renderComponentItem: AgentItem,
58
63
  isEnabled: isAgentEnabled,
59
64
  onSelect: onSelectItem,
@@ -11,6 +11,29 @@ export const QuickCommandSelector = ({ inputRef, isTrial }:
11
11
  const chat = useCurrentChat()
12
12
  const isQuickCommandEnabled = useCurrentChatState('features').quickCommands
13
13
 
14
+ const listFavorites = aiClient.quickCommands.useQuery({ visibility: 'favorite' })
15
+ const [addFavorite, pendingAddFav] = aiClient.addFavoriteQuickCommand.useMutation()
16
+ const [removeFavorite, pendingRemoveFav] = aiClient.removeFavoriteQuickCommand.useMutation()
17
+
18
+ const onAddFavorite = async(idOrSlug?: string) => {
19
+ try {
20
+ await addFavorite({ slug: idOrSlug || '' })
21
+ await aiClient.quickCommands.invalidate()
22
+ } catch (error) {
23
+ // eslint-disable-next-line no-console
24
+ console.error(error)
25
+ }
26
+ }
27
+ const onRemoveFavorite = async(idOrSlug?: string) => {
28
+ try {
29
+ await removeFavorite({ slug: idOrSlug || '' })
30
+ await aiClient.quickCommands.invalidate()
31
+ } catch (error) {
32
+ // eslint-disable-next-line no-console
33
+ console.error(error)
34
+ }
35
+ }
36
+
14
37
  const onSelectItem = useCallback((qc: QuickCommandListResponse) => {
15
38
  const newValue = `/${qc.slug}`
16
39
  chat.set('nextMessage', newValue)
@@ -27,6 +50,7 @@ export const QuickCommandSelector = ({ inputRef, isTrial }:
27
50
 
28
51
  return <Selector
29
52
  inputRef={inputRef}
53
+ favorite={{ listFavorites, onAddFavorite, onRemoveFavorite, pendingAddFav, pendingRemoveFav }}
30
54
  selectorConfig={{
31
55
  resourceName: 'Quick Command',
32
56
  shortcut: '/',
@@ -34,7 +58,7 @@ export const QuickCommandSelector = ({ inputRef, isTrial }:
34
58
  searchProp: 'slug',
35
59
  urlBuilder: (qc) => `/quick-command/${qc?.slug}`,
36
60
  regex: quickCommandRegex,
37
- sections: isTrial ? ['personal'] : ['personal', 'workspace', 'account', 'shared'],
61
+ sections: isTrial ? ['favorite', 'personal'] : ['favorite', 'personal', 'workspace', 'account', 'shared'],
38
62
  isEnabled: isQuickCommandEnabled,
39
63
  onSelect: onSelectItem,
40
64
  renderComponentItem: QuickCommandItem,
@@ -5,6 +5,7 @@ import { aiClient } from '@stack-spot/portal-network'
5
5
  import { GetAiStackResponse, VisibilityLevelEnum } from '@stack-spot/portal-network/api/ai'
6
6
  import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
7
7
  import { useEffect, useMemo, useState } from 'react'
8
+ import { ButtonFavorite } from '../components/ButtonFavorite'
8
9
  import { DescribedRadioGroup } from '../components/form/DescribedRadioGroup'
9
10
  import { IconInput } from '../components/IconInput'
10
11
  import { RightPanelTabs } from '../components/RightPanelTabs'
@@ -34,11 +35,14 @@ export const Stacks = () => {
34
35
  const StacksPanel = () => {
35
36
  const t = useTranslate(dictionary)
36
37
  const chat = useCurrentChat()
38
+
37
39
  const isTrial = checkIsTrial()
38
40
 
39
41
  const tabs = useMemo(() => isTrial ? [
42
+ { title: t.favorites, content: <StacksTab key="favorites" visibility="favorite" /> },
40
43
  { title: t.personal, content: <StacksTab key="personal" visibility="personal" /> },
41
44
  ]: [
45
+ { title: t.favorites, content: <StacksTab key="favorites" visibility="favorite" /> },
42
46
  { title: t.personal, content: <StacksTab key="personal" visibility="personal" /> },
43
47
  { title: t.shared, content: <StacksTab key="shared" visibility="shared" /> },
44
48
  { title: t.account, content: <StacksTab key="account" visibility="account" /> },
@@ -52,12 +56,38 @@ const StacksTab = ({ visibility }: { visibility: VisibilityLevelEnum }) => {
52
56
  const { close } = useRightPanel()
53
57
  const chat = useCurrentChat()
54
58
  const [filter, setFilter] = useState('')
55
- // @ts-ignore type in backend (openapi) is incorrect. fixme.
59
+
60
+ const listFavorites = aiClient.aiStacks.useQuery({ visibility: 'favorite' })
61
+ const [addFavorite, pendingAddFav] = aiClient.addFavoriteStackAi.useMutation()
62
+ const [removeFavorite, pendingRemoveFav] = aiClient.removeFavoriteStackAi.useMutation()
63
+
64
+ const onRemoveFavorite = async (idOrSlug: string) => {
65
+ try {
66
+ await removeFavorite({ stackId: idOrSlug })
67
+ await aiClient.aiStacks.invalidate()
68
+ } catch (error) {
69
+ // eslint-disable-next-line no-console
70
+ console.error(error)
71
+ }
72
+ }
73
+
74
+ const onAddFavorite = async (idOrSlug: string) => {
75
+ try {
76
+ await addFavorite({ stackId: idOrSlug })
77
+ await aiClient.aiStacks.invalidate()
78
+ } catch (error) {
79
+ // eslint-disable-next-line no-console
80
+ console.error(error)
81
+ }
82
+ }
83
+
84
+ //@ts-ignore
56
85
  const stacks = aiClient.aiStacks.useQuery({ visibility, order: 'a-to-z' })
57
86
  const [value, setValue] = useState<GetAiStackResponse | undefined>(stacks.find(s => s.id === chat.get('stack')?.id))
58
- const filtered = useMemo(
59
- () => filter ? stacks.filter(s => s === value || s.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase())) : stacks,
60
- [stacks, filter, value],
87
+ const filtered = useMemo(() => filter ?
88
+ // Recreate the list so that the favorites list is taken into account
89
+ stacks.filter(s => s === value || s.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase())) : [...stacks],
90
+ [stacks, listFavorites, filter, value],
61
91
  )
62
92
 
63
93
  function submit() {
@@ -71,6 +101,9 @@ const StacksTab = ({ visibility }: { visibility: VisibilityLevelEnum }) => {
71
101
  <IconInput icon={<Search />} value={filter} onChange={setFilter} className="search" />
72
102
  {!!filtered.length && <DescribedRadioGroup
73
103
  options={filtered}
104
+ renderAfterElement={({ id }) =>
105
+ <ButtonFavorite favorite={{ idOrSlug:id, listFavorites, onAddFavorite, onRemoveFavorite, pendingAddFav, pendingRemoveFav }} />
106
+ }
74
107
  keygen={s => s.id}
75
108
  value={value}
76
109
  onChange={setValue}
@@ -103,6 +136,7 @@ const dictionary = {
103
136
  noSearchResultsDescription: 'Please, try another search term.',
104
137
  noData: 'There are no stacks in this category yet.',
105
138
  noDataDescription: 'Use the tabs above to try other categories or use the AI portal to create new stacks.',
139
+ favorites: 'Favoritos',
106
140
  },
107
141
  pt: {
108
142
  title: 'Stacks AI',
@@ -115,5 +149,6 @@ const dictionary = {
115
149
  noSearchResultsDescription: 'Por favor, tente outra busca.',
116
150
  noData: 'Ainda não há stacks nesta categoria.',
117
151
  noDataDescription: 'Use as abas acima para tentar outras categorias ou use o Portal AI para criar novas stacks.',
152
+ favorites: 'Favorites',
118
153
  },
119
154
  } satisfies Dictionary
@@ -1,13 +1,13 @@
1
1
  import { Button } from '@citric/core'
2
2
  import { Search } from '@citric/icons'
3
3
  import { Placeholder } from '@stack-spot/portal-components/Placeholder'
4
- import { workspaceAiClient } from '@stack-spot/portal-network'
5
- import { WorkspaceResponse } from '@stack-spot/portal-network/api/workspace-ai'
4
+ import { workspaceClient } from '@stack-spot/portal-network'
5
+ import { WorkspaceReadResponse } from '@stack-spot/portal-network/api/workspace'
6
6
  import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
7
7
  import { useEffect, useMemo, useState } from 'react'
8
8
  import { DescribedRadioGroup } from '../components/form/DescribedRadioGroup'
9
9
  import { IconInput } from '../components/IconInput'
10
- import { RightPanelForm } from '../components/RightPanelForm'
10
+ import { RightPanelTabs } from '../components/RightPanelTabs'
11
11
  import { useCurrentChat, useWidget, useWidgetState } from '../context/hooks'
12
12
  import { useRightPanel } from '../right-panel/hooks'
13
13
 
@@ -23,22 +23,60 @@ export const Workspaces = () => {
23
23
 
24
24
  useEffect(() => {
25
25
  if (panel === 'workspace') open(
26
- <RightPanelForm><WorkspacesPanel key={chat.id} /></RightPanelForm>,
26
+ <WorkspacesPanel key={chat.id} />,
27
27
  { title: t.title, description: t.description, onClose: () => widget.set('panel', undefined) },
28
28
  )
29
29
  }, [panel, t, chat.id])
30
-
31
30
  return null
32
31
  }
33
32
 
34
33
  const WorkspacesPanel = () => {
34
+ const t = useTranslate(dictionary)
35
+ const chat = useCurrentChat()
36
+
37
+ return <RightPanelTabs key={chat.id} tabs={[
38
+ // { title: t.favorites, content: <WorkspaceSourcesTab key="favorite" visibility="favorite" /> },
39
+ { title: t.all, content: <WorkspaceSourcesTab key="all" /> },
40
+ ]}
41
+ />
42
+ }
43
+
44
+ const WorkspaceSourcesTab = () => {
35
45
  const t = useTranslate(dictionary)
36
46
  const { close } = useRightPanel()
37
47
  const chat = useCurrentChat()
38
48
  const [filter, setFilter] = useState('')
39
- const workspaces = workspaceAiClient.workspacesAi.useQuery({})
40
- const [value, setValue] = useState<WorkspaceResponse | undefined>(workspaces.find(w => w.id === chat.get('workspace')?.id))
49
+ // const workspaces = workspaceAiClient.workspacesAi.useQuery({ visibility })
50
+ // const [value, setValue] = useState<WorkspaceResponse | undefined>(workspaces.find(w => w.id === chat.get('workspace')?.id))
51
+ const workspaces = workspaceClient.workspaces.useQuery({ aclOnly: false })
52
+
53
+ const [value, setValue] = useState<WorkspaceReadResponse | undefined>(workspaces.find(w => w.id === chat.get('workspace')?.id))
54
+ // const listFavorites = workspaceAiClient.workspacesAi.useQuery({ visibility: 'favorite' })
55
+
56
+ // const [addFavorite, pendingAddFav] = workspaceAiClient.addFavoriteWorkspaceAi.useMutation()
57
+ // const [removeFavorite, pendingRemoveFav] = workspaceAiClient.removeFavoriteWorkspaceAi.useMutation()
58
+
59
+ // const onAddFavorite = async(idOrSlug: string) => {
60
+ // try {
61
+ // await addFavorite({ workspaceId: idOrSlug })
62
+ // await workspaceAiClient.workspacesAi.invalidate()
63
+ // } catch (error) {
64
+ // // eslint-disable-next-line no-console
65
+ // console.error(error)
66
+ // }
67
+ // }
68
+ // const onRemoveFavorite = async(idOrSlug: string) => {
69
+ // try {
70
+ // await removeFavorite({ workspaceId: idOrSlug })
71
+ // await workspaceAiClient.workspacesAi.invalidate()
72
+ // } catch (error) {
73
+ // // eslint-disable-next-line no-console
74
+ // console.error(error)
75
+ // }
76
+ // }
77
+
41
78
  const filtered = useMemo(
79
+ // Recreate the list so that the favorites list is taken into account
42
80
  () => filter ? workspaces.filter(w => w === value || w.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase())) : workspaces,
43
81
  [workspaces, filter, value],
44
82
  )
@@ -47,7 +85,6 @@ const WorkspacesPanel = () => {
47
85
  if (value) chat.set('workspace', { id: value.id, label: value.name })
48
86
  close()
49
87
  }
50
-
51
88
  return (
52
89
  <>
53
90
  <div className="content">
@@ -83,6 +120,8 @@ const dictionary = {
83
120
  noSearchResultsDescription: 'Please, try another search term.',
84
121
  noData: 'There are no workspaces yet.',
85
122
  noDataDescription: 'Use the AI portal to create new workspaces.',
123
+ all: 'All',
124
+ favorites: 'Favorites',
86
125
  },
87
126
  pt: {
88
127
  title: 'Workspaces',
@@ -92,5 +131,7 @@ const dictionary = {
92
131
  noSearchResultsDescription: 'Por favor, tente outra busca.',
93
132
  noData: 'Ainda não há workspace.',
94
133
  noDataDescription: 'Use o Portal AI para criar novos workspaces.',
134
+ all: 'Todos',
135
+ favorites: 'Favoritos',
95
136
  },
96
137
  } satisfies Dictionary