Skip to content

Commit 1f15f3e

Browse files
authored
feat(demo): Add demo for pinned chat history (#623)
Assisted-by: Cursor
1 parent 38eda58 commit 1f15f3e

File tree

2 files changed

+205
-1
lines changed

2 files changed

+205
-1
lines changed
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
// Assisted by: Cursor
2+
import React, { FunctionComponent, useState } from 'react';
3+
import { ChatbotDisplayMode } from '@patternfly/chatbot/dist/dynamic/Chatbot';
4+
import ChatbotConversationHistoryNav, {
5+
Conversation
6+
} from '@patternfly/chatbot/dist/dynamic/ChatbotConversationHistoryNav';
7+
import { Checkbox, DropdownItem, DropdownList } from '@patternfly/react-core';
8+
import { ThumbtackIcon } from '@patternfly/react-icons';
9+
10+
// Sample conversations
11+
const initialConversations: { [key: string]: Conversation[] } = {
12+
Today: [
13+
{
14+
id: '1',
15+
text: 'Red Hat products and services',
16+
label: 'Conversation options for "Red Hat products and services"'
17+
}
18+
],
19+
'This month': [
20+
{
21+
id: '2',
22+
text: 'Enterprise Linux installation and setup',
23+
label: 'Conversation options for "Enterprise Linux installation and setup"'
24+
},
25+
{
26+
id: '3',
27+
text: 'Troubleshoot system crash',
28+
label: 'Conversation options for "Troubleshoot system crash"'
29+
}
30+
],
31+
March: [
32+
{
33+
id: '4',
34+
text: 'Ansible security and updates',
35+
label: 'Conversation options for "Ansible security and updates"'
36+
},
37+
{
38+
id: '5',
39+
text: 'Red Hat certification',
40+
label: 'Conversation options for "Red Hat certification"'
41+
},
42+
{
43+
id: '6',
44+
text: 'Lightspeed user documentation',
45+
label: 'Conversation options for "Lightspeed user documentation"'
46+
}
47+
],
48+
February: [
49+
{
50+
id: '7',
51+
text: 'Crashing pod assistance',
52+
label: 'Conversation options for "Crashing pod assistance"'
53+
},
54+
{
55+
id: '8',
56+
text: 'OpenShift AI pipelines',
57+
label: 'Conversation options for "OpenShift AI pipelines"'
58+
},
59+
{
60+
id: '9',
61+
text: 'Updating subscription plan',
62+
label: 'Conversation options for "Updating subscription plan"'
63+
},
64+
{
65+
id: '10',
66+
text: 'Red Hat licensing options',
67+
label: 'Conversation options for "Red Hat licensing options"'
68+
}
69+
],
70+
January: [
71+
{
72+
id: '11',
73+
text: 'RHEL system performance',
74+
label: 'Conversation options for "RHEL system performance"'
75+
},
76+
{
77+
id: '12',
78+
text: 'Manage user accounts',
79+
label: 'Conversation options for "Manage user accounts"'
80+
}
81+
]
82+
};
83+
84+
export const ChatbotHeaderPinDemo: FunctionComponent = () => {
85+
const [isDrawerOpen, setIsDrawerOpen] = useState(true);
86+
const [isCompact, setIsCompact] = useState(false);
87+
const [pinnedConversations, setPinnedConversations] = useState<Set<string>>(new Set());
88+
const displayMode = ChatbotDisplayMode.embedded;
89+
90+
const handlePinToggle = (conversationId: string) => {
91+
setPinnedConversations((prev) => {
92+
const newPinned = new Set(prev);
93+
if (newPinned.has(conversationId)) {
94+
newPinned.delete(conversationId);
95+
} else {
96+
newPinned.add(conversationId);
97+
}
98+
return newPinned;
99+
});
100+
};
101+
102+
const createMenuItems = (conversationId: string) => {
103+
const isPinned = pinnedConversations.has(conversationId);
104+
105+
return [
106+
<DropdownList key={`${conversationId}-menu`} aria-label="Conversation options">
107+
<DropdownItem
108+
value={isPinned ? 'Unpin' : 'Pin'}
109+
id={isPinned ? 'Unpin' : 'Pin'}
110+
onClick={() => handlePinToggle(conversationId)}
111+
>
112+
{isPinned ? 'Unpin' : 'Pin'}
113+
</DropdownItem>
114+
<DropdownItem value="Delete" id="Delete">
115+
Delete
116+
</DropdownItem>
117+
<DropdownItem value="Rename" id="Rename">
118+
Rename
119+
</DropdownItem>
120+
<DropdownItem value="Archive" id="Archive">
121+
Archive
122+
</DropdownItem>
123+
</DropdownList>
124+
];
125+
};
126+
127+
// Reorganize conversations to show pinned ones at the top
128+
const organizeConversations = () => {
129+
const organized: { [key: string]: Conversation[] } = {};
130+
const pinnedItems: Conversation[] = [];
131+
132+
// Collect all pinned conversations first
133+
Object.entries(initialConversations).forEach(([_period, conversations]) => {
134+
conversations.forEach((conv) => {
135+
if (pinnedConversations.has(conv.id)) {
136+
pinnedItems.push({
137+
...conv,
138+
menuItems: createMenuItems(conv.id),
139+
icon: <ThumbtackIcon />
140+
});
141+
}
142+
});
143+
});
144+
145+
// Add pinned section if there are pinned items
146+
if (pinnedItems.length > 0) {
147+
organized.Pinned = pinnedItems;
148+
}
149+
150+
// Add unpinned conversations
151+
Object.entries(initialConversations).forEach(([period, conversations]) => {
152+
const unpinnedConversations = conversations
153+
.filter((conv) => !pinnedConversations.has(conv.id))
154+
.map((conv) => ({
155+
...conv,
156+
menuItems: createMenuItems(conv.id)
157+
}));
158+
159+
if (unpinnedConversations.length > 0) {
160+
organized[period] = unpinnedConversations;
161+
}
162+
});
163+
164+
return organized;
165+
};
166+
167+
const conversations = organizeConversations();
168+
169+
return (
170+
<>
171+
<Checkbox
172+
label="Display drawer"
173+
isChecked={isDrawerOpen}
174+
onChange={() => setIsDrawerOpen(!isDrawerOpen)}
175+
id="drawer-pin-visible"
176+
name="drawer-pin-visible"
177+
></Checkbox>
178+
<Checkbox
179+
label="Show compact version"
180+
isChecked={isCompact}
181+
onChange={() => setIsCompact(!isCompact)}
182+
id="drawer-pin-compact"
183+
name="drawer-pin-compact"
184+
></Checkbox>
185+
<ChatbotConversationHistoryNav
186+
displayMode={displayMode}
187+
onDrawerToggle={() => setIsDrawerOpen(!isDrawerOpen)}
188+
isDrawerOpen={isDrawerOpen}
189+
setIsDrawerOpen={setIsDrawerOpen}
190+
conversations={conversations}
191+
drawerContent={<div>Drawer content</div>}
192+
isCompact={isCompact}
193+
/>
194+
</>
195+
);
196+
};

packages/module/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ import { MessageBar } from '@patternfly/chatbot/dist/dynamic/MessageBar';
6868
import SourceDetailsMenuItem from '@patternfly/chatbot/dist/dynamic/SourceDetailsMenuItem';
6969
import { ChatbotModal } from '@patternfly/chatbot/dist/dynamic/ChatbotModal';
7070
import SettingsForm from '@patternfly/chatbot/dist/dynamic/Settings';
71-
import { BellIcon, CalendarAltIcon, ClipboardIcon, CodeIcon, UploadIcon } from '@patternfly/react-icons';
71+
import { BellIcon, CalendarAltIcon, ClipboardIcon, CodeIcon, ThumbtackIcon, UploadIcon } from '@patternfly/react-icons';
7272
import { useDropzone } from 'react-dropzone';
7373

7474
import ChatbotConversationHistoryNav from '@patternfly/chatbot/dist/dynamic/ChatbotConversationHistoryNav';
@@ -361,6 +361,14 @@ Actions can be added to conversations with `menuItems`. Optionally, you can also
361361

362362
```
363363

364+
### Pinning conversations
365+
366+
To help users track important conversations, add a "pin" option to the conversation action menus. This action moves a conversation to a dedicated "pinned" section at the top of the history drawer for quick access. Pinned items should contain an "unpin" option, so that users can remove pinned conversations as needed.
367+
368+
```js file="./ChatbotHeaderDrawerWithPin.tsx"
369+
370+
```
371+
364372
### Drawer with active conversation
365373

366374
If you're showing a conversation that is already active, you can set the `activeItemId` prop on your `<ChatbotConversationHistoryNav>` to apply an active visual state.

0 commit comments

Comments
 (0)