Skip to content

Commit 80af46b

Browse files
committed
feat: complete Ai agent page
1 parent 0913936 commit 80af46b

File tree

9 files changed

+409
-545
lines changed

9 files changed

+409
-545
lines changed

services/webui/public/env-config.js

Lines changed: 1 addition & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import axios from 'axios';
2+
import { useEffect, useState } from 'react';
3+
import { Agent } from './types';
4+
import { useNavigate } from 'react-router-dom';
5+
import LoadingDots from '../Loading';
6+
import Tooltip from '../Tooltip';
7+
import { Button, Modal } from '@cloudscape-design/components'
8+
import Cal, { getCalApi } from '@calcom/embed-react'
9+
import { Flex } from '@tremor/react'
10+
function Agents() {
11+
const [open,setOpen] = useState(false)
12+
const [openCal, setOpenCal] = useState(false)
13+
14+
const [agents, setAgents] = useState<Agent[]>([
15+
{
16+
name: 'Identity & Access',
17+
description:
18+
'Delivers information on user identities, access controls, and activity.',
19+
welcome_message:
20+
'Hi there! This is your Identity & Access Agent. I can help you with anything related to identity management and access tools. What can I assist you with today? For example, you can ask me things like:',
21+
sample_questions: [
22+
'Get me the list of users who have access to Azure Subscriptions.',
23+
'Get me all SPNs with expired passwords.',
24+
'Show me the access activity for user John Doe.',
25+
],
26+
id: 'identity_access',
27+
enabled: true,
28+
is_available: true,
29+
},
30+
{
31+
name: 'DevOps',
32+
description:
33+
'Provides data on secure code, deployment, and automation workflows.',
34+
welcome_message:
35+
'Hello! This is your DevOps Agent. I can provide insights into secure code, deployment, and automation workflows. How can I assist you today? For instance, you could ask me:',
36+
sample_questions: [
37+
'What are the latest secure code scan results?',
38+
'Show me the deployment status for the production environment.',
39+
'Provide a report on automated workflow execution times.',
40+
],
41+
id: 'devops',
42+
enabled: true,
43+
is_available: true,
44+
},
45+
{
46+
name: 'Sales',
47+
description:
48+
'Answers questions about sales activities, including: total activities per rep, activity breakdown by type, activity per deal, deal stage progression activity, and time to first activity. Enables analysis of sales rep performance and deal progression.',
49+
welcome_message:
50+
'Hello! This is your Sales Agent. I can answer questions about sales activities, including total activities per rep, activity breakdown by type, activity per deal, deal stage progression activity, and time to first activity. What can I help you with? For example:',
51+
sample_questions: [
52+
'Show me the total activities (calls, emails, meetings) for each sales rep.',
53+
'Which sales reps had the most/least activities?',
54+
'How much activity was logged for each deal we closed?',
55+
],
56+
id: 'sales',
57+
enabled: true,
58+
is_available: true,
59+
},
60+
])
61+
const selected_agent = {
62+
id: 'identity_access',
63+
}
64+
const navigate = useNavigate()
65+
66+
67+
68+
return (
69+
<>
70+
<div className=" bg-slate-200 dark:bg-gray-950 h-full w-full max-w-sm justify-start items-start max-h-[90vh] flex flex-col gap-2 ">
71+
<div
72+
id="k-agent-bar"
73+
className="flex flex-col gap-2 max-h-[90vh] overflow-y-scroll mt-2 "
74+
>
75+
{agents?.map((agent) => {
76+
return (
77+
<div
78+
key={agent.id}
79+
onClick={() => {
80+
setOpen(true)
81+
}}
82+
className={`rounded-sm flex flex-col justify-start items-start gap-2 hover:dark:bg-gray-700 hover:bg-gray-400 cursor-pointer p-2 ${
83+
selected_agent?.id == agent.id &&
84+
' bg-slate-400 dark:bg-slate-800'
85+
}`}
86+
>
87+
<span className="text-base text-slate-950 dark:text-slate-200">
88+
{agent.name}
89+
</span>
90+
<span className="text-sm text-slate-500 dark:text-slate-400">
91+
{agent.description}
92+
</span>
93+
</div>
94+
)
95+
})}
96+
</div>
97+
</div>
98+
<Modal
99+
size="medium"
100+
visible={open}
101+
onDismiss={() => setOpen(false)}
102+
header="Not available"
103+
>
104+
<Flex className="flex-col gap-2">
105+
<span>
106+
{' '}
107+
This feature is only available on commercial version.
108+
</span>
109+
<Button
110+
onClick={() => {
111+
setOpenCal(true)
112+
}}
113+
>
114+
Contact us
115+
</Button>
116+
</Flex>
117+
</Modal>
118+
<Modal
119+
size="large"
120+
visible={openCal}
121+
onDismiss={() => setOpenCal(false)}
122+
header="Not available"
123+
>
124+
<Cal
125+
namespace="try-enterprise"
126+
calLink="team/clearcompass/try-enterprise"
127+
style={{
128+
width: '100%',
129+
height: '100%',
130+
overflow: 'scroll',
131+
}}
132+
config={{ layout: 'month_view' }}
133+
/>
134+
</Modal>
135+
</>
136+
)
137+
}
138+
139+
export default Agents;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export interface Agent {
2+
id: string
3+
name: string
4+
description: string
5+
is_available: boolean
6+
enabled: boolean
7+
welcome_message: string
8+
sample_questions: string[]
9+
}

services/webui/src/components/AIComponents/Input/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ const KInput: FunctionComponent<any> = ({
8686
disabled={disabled}
8787
className={` absolute right-3 ${is_dark ? 'text-white' : 'text-black'} rounded-full p-2 disabled:text-gray-500 `}
8888
>
89-
<PaperAirplaneIcon />
89+
<PaperAirplaneIcon color="black" className="w-5" />
9090
</button>
9191
</div>
9292
</div>

services/webui/src/components/Layout/Sidebar/index.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
ChevronDoubleUpIcon,
2424
CalendarDateRangeIcon,
2525
CommandLineIcon,
26+
UserIcon,
2627
} from '@heroicons/react/24/outline'
2728
import { useAtom, useAtomValue, useSetAtom } from 'jotai'
2829
import { Popover, Transition } from '@headlessui/react'
@@ -264,6 +265,12 @@ export default function Sidebar({ currentPage }: ISidebar) {
264265
icon: CalendarDateRangeIcon,
265266
isPreview: false,
266267
},
268+
{
269+
name: 'Agent AI',
270+
page: 'ai',
271+
icon: UserIcon,
272+
isPreview: false,
273+
},
267274
{
268275
name: 'Administration',
269276
page: ['administration'],
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
import React, { ChangeEvent, useEffect, useRef, useState } from 'react';
2+
import { Chat, ChatList } from '../types';
3+
import axios from 'axios';
4+
import { dateTimeDisplay } from '../../../utilities/dateDisplay';
5+
import KChatCard from '../../../components/AIComponents/ChatCard';
6+
import KResponseCard from '../../../components/AIComponents/ResponseCard';
7+
import KInput from '../../../components/AIComponents/Input'
8+
import { Button, Modal } from '@cloudscape-design/components';
9+
import Cal, { getCalApi } from '@calcom/embed-react'
10+
import { Flex } from '@tremor/react';
11+
function AIChat() {
12+
const [message, setMessage] = useState('')
13+
const [open,setOpen] = useState(false)
14+
const [openCal, setOpenCal] = useState(false)
15+
16+
17+
const [chats, setChats] = useState<ChatList>({
18+
'0': {
19+
message: 'Hello here is your AI assitant',
20+
text: 'Hello here is your AI assitant',
21+
loading: false,
22+
time: 0,
23+
error: '',
24+
isWelcome: true,
25+
pre_loaded: false,
26+
clarify_needed: false,
27+
messageTime: '',
28+
responseTime: `${
29+
new Date().getHours() > 12
30+
? new Date().getHours() - 12
31+
: new Date().getHours()
32+
}:${new Date().getMinutes()}${
33+
new Date().getHours() > 12 ? 'PM' : 'AM'
34+
}`,
35+
suggestions: [],
36+
response: {},
37+
},
38+
})
39+
40+
const lastMessageRef = useRef(null)
41+
const scroll = () => {
42+
const layout = document.getElementById('layout')
43+
if (layout) {
44+
const start = layout.scrollTop
45+
const end = layout.scrollHeight
46+
const duration = 1500 // Adjust duration in milliseconds
47+
let startTime: any = null
48+
const animateScroll = (timestamp: any) => {
49+
if (!startTime) startTime = timestamp
50+
const progress = Math.min((timestamp - startTime) / duration, 1)
51+
layout.scrollTop = start + (end - start) * progress
52+
if (progress < 1) {
53+
requestAnimationFrame(animateScroll)
54+
}
55+
}
56+
requestAnimationFrame(animateScroll)
57+
// layout.scrollTop = layout?.scrollHeight+400;
58+
}
59+
// if (lastMessageRef.current) {
60+
// // @ts-ignore
61+
// lastMessageRef.current.scrollIntoView({ behavior: "smooth" });
62+
// }
63+
}
64+
65+
useEffect(() => {
66+
scroll()
67+
}, [chats])
68+
69+
return (
70+
<>
71+
<div className=" bg-slate-200 dark:bg-gray-950 flex max-h-[65vh] flex-col justify-start items-start w-full ">
72+
<div
73+
id="layout"
74+
className=" flex justify-start items-start overflow-y-auto w-full bg-slate-200 dark:bg-gray-950 pt-2 "
75+
>
76+
<div className=" w-full relative ">
77+
<section className="chat-section h-full flex flex-col relative gap-8 w-full max-w-[95%] ">
78+
{chats &&
79+
Object.keys(chats).map((key) => {
80+
return (
81+
<>
82+
{!chats[key].isWelcome && (
83+
<KChatCard
84+
date={
85+
chats[key].messageTime
86+
}
87+
key={parseInt(key) + 'chat'}
88+
message={chats[key].message}
89+
/>
90+
)}
91+
<KResponseCard
92+
key={parseInt(key) + 'result'}
93+
ref={
94+
key ===
95+
(
96+
Object.keys(chats)
97+
?.length - 1
98+
).toString()
99+
? lastMessageRef
100+
: null
101+
}
102+
scroll={scroll}
103+
response={chats[key].response}
104+
loading={chats[key].loading}
105+
pre_loaded={
106+
chats[key].pre_loaded
107+
}
108+
chat_id={chats[key].id}
109+
error={chats[key].error}
110+
time={chats[key].time}
111+
text={chats[key].text}
112+
isWelcome={chats[key].isWelcome}
113+
date={chats[key].responseTime}
114+
clarify_needed={
115+
chats[key].clarify_needed
116+
}
117+
clarify_questions={
118+
chats[key].clarify_questions
119+
}
120+
id={''}
121+
suggestions={
122+
chats[key].suggestions
123+
}
124+
onClickSuggestion={(
125+
suggestion: string
126+
) => {}}
127+
/>
128+
</>
129+
)
130+
})}
131+
</section>
132+
</div>
133+
</div>
134+
<KInput
135+
value={message}
136+
chats={chats}
137+
onChange={(e: any) => {
138+
setMessage(e?.target?.value)
139+
}}
140+
onSend={() => {
141+
setOpen(true)
142+
}}
143+
/>
144+
</div>
145+
<Modal
146+
size="medium"
147+
visible={open}
148+
onDismiss={() => setOpen(false)}
149+
header="Not available"
150+
>
151+
<Flex className="flex-col gap-2">
152+
<span>
153+
{' '}
154+
This feature is only available on commercial version.
155+
</span>
156+
<Button
157+
onClick={() => {
158+
setOpenCal(true)
159+
}}
160+
>
161+
Contact us
162+
</Button>
163+
</Flex>
164+
</Modal>
165+
<Modal
166+
size="large"
167+
visible={openCal}
168+
onDismiss={() => setOpenCal(false)}
169+
header="Not available"
170+
>
171+
<Cal
172+
namespace="try-enterprise"
173+
calLink="team/clearcompass/try-enterprise"
174+
style={{
175+
width: '100%',
176+
height: '100%',
177+
overflow: 'scroll',
178+
}}
179+
config={{ layout: 'month_view' }}
180+
/>
181+
</Modal>
182+
</>
183+
)
184+
}
185+
186+
export default AIChat;

0 commit comments

Comments
 (0)