Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions community-chatbot/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ next-env.d.ts
__pycache__/
*.sql
*.py
Jira/
4 changes: 2 additions & 2 deletions community-chatbot/app/(agent)/[mode]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import Image from 'next/image';
import { usePathname } from 'next/navigation';

import {
Card, CardContent, CardDescription,
CardHeader, CardTitle,
Card, CardContent, CardDescription,
CardHeader, CardTitle,
} from '@/components/ui/card';
import { integrationModes } from '@/lib/constants/chat';

Expand Down
2 changes: 2 additions & 0 deletions community-chatbot/app/(agent)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import React, { useEffect } from 'react';
import { ChatHeader } from '@/components/agent/Header';
import { ModeSelector } from '@/components/agent/ModeSelector';
import { AppSidebar } from '@/components/agent/Sidebar';
import { AppTour } from '@/components/agent/AppTour';
import { useAgentStore } from '@/lib/store/agent/agentStore';

export default function RootLayout({
Expand All @@ -21,6 +22,7 @@ export default function RootLayout({

return (
<main className="flex w-full h-screen">
<AppTour />
<AppSidebar />
<div className="flex flex-col w-full">
<ChatHeader />
Expand Down
42 changes: 42 additions & 0 deletions community-chatbot/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ body {
--sidebar-border: 220 13% 91%;
--sidebar-ring: 217.2 91.2% 59.8%;
}

.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
Expand Down Expand Up @@ -92,3 +93,44 @@ body {
@apply bg-background text-foreground;
}
}

@layer utilities {

/* Minimal aesthetic scrollbar */
.scrollbar-aesthetic {
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: rgba(0, 0, 0, 0.8) transparent;
}

/* Chrome / Edge / Safari */
.scrollbar-aesthetic::-webkit-scrollbar {
width: 6px;
}

.scrollbar-aesthetic::-webkit-scrollbar-track {
background: transparent;
}

.scrollbar-aesthetic::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.5);
border-radius: 9999px;
}

.scrollbar-aesthetic::-webkit-scrollbar-thumb:hover {
background: rgba(0, 0, 0, 1);
}

/* Dark mode */
.dark .scrollbar-aesthetic {
scrollbar-color: rgba(255, 255, 255, 0.3) transparent;
}

.dark .scrollbar-aesthetic::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
}

.dark .scrollbar-aesthetic::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5);
}
}
42 changes: 42 additions & 0 deletions community-chatbot/components/agent/AppTour.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use client';

import React, { useState, useEffect } from 'react';
import Joyride, { CallBackProps, STATUS } from 'react-joyride';
import { TourTooltip } from '@/components/agent/AppTour/TourTooltip';
import { getJoyrideStyles } from '@/components/agent/AppTour/styles';
import { steps } from '@/components/agent/AppTour/steps';

export function AppTour() {
const [run, setRun] = useState(false);

useEffect(() => {
const hasSeenTour = localStorage.getItem('hasSeenAppTour');
if (!hasSeenTour) {
setRun(true);
}
}, []);

const handleJoyrideCallback = (data: CallBackProps) => {
const { status } = data;
const finishedStatuses: string[] = [STATUS.FINISHED, STATUS.SKIPPED];

if (finishedStatuses.includes(status)) {
setRun(false);
localStorage.setItem('hasSeenAppTour', 'true');
}
};

return (
<Joyride
steps={steps}
run={run}
continuous={true}
showProgress={true}
showSkipButton={true}
callback={handleJoyrideCallback}
scrollOffset={100}
styles={getJoyrideStyles()}
tooltipComponent={TourTooltip}
/>
);
}
73 changes: 73 additions & 0 deletions community-chatbot/components/agent/AppTour/TourTooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
'use client';

import React from 'react';
import { TooltipRenderProps } from 'react-joyride';
import { X, ArrowRight, ArrowLeft, Check } from 'lucide-react';
import { Button } from '@/components/ui/button';

export function TourTooltip({
index,
step,
backProps,
primaryProps,
skipProps,
isLastStep,
}: TooltipRenderProps) {
return (
<div className="bg-white dark:bg-gray-900 border-gray-200 dark:border-gray-800 shadow-2xl p-6 border rounded-2xl w-full max-w-[400px] transition-all overflow-hidden relative">
{/* Header / Step Progress */}
<div className="flex justify-between items-center mb-4">
<span className="bg-blue-100 dark:bg-blue-900/50 px-2 py-0.5 rounded text-blue-600 dark:text-blue-400 text-xs font-bold uppercase tracking-wider">
Step {index + 1}
</span>
<button
{...skipProps}
className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 transition-colors"
title="Skip Tour"
>
<X className="size-5" />
</button>
</div>

{/* Content Area */}
<div className="mb-8">
{step.content}
</div>

{/* Footer / Navigation */}
<div className="flex items-center gap-3">
{index > 0 && (
<Button
{...backProps}
variant="ghost"
size="sm"
className="text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white"
>
<ArrowLeft className="mr-2 size-4" />
Back
</Button>
)}

<div className="flex-1" />

<Button
{...primaryProps}
size="sm"
className="bg-blue-600 hover:bg-blue-700 text-white font-semibold min-w-[100px]"
>
{isLastStep ? (
<>
Get Started
<Check className="ml-2 size-4" />
</>
) : (
<>
Continue
<ArrowRight className="ml-2 size-4" />
</>
)}
</Button>
</div>
</div>
);
}
77 changes: 77 additions & 0 deletions community-chatbot/components/agent/AppTour/steps.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Step } from "react-joyride";

export const steps: Step[] = [
{
target: 'body',
content: (
<div className="text-left">
<h3 className="font-bold text-lg mb-2">Welcome</h3>
<p className="text-sm opacity-80">
Welcome to your workspace. This app uses different assistants for different tasks. Let’s take a quick tour.
</p>
</div>
),
placement: 'center',
disableBeacon: true,
},
{
target: '#mode-selector',
content: (
<div className="text-left">
<h3 className="font-bold text-lg mb-2">Assistants</h3>
<p className="text-sm opacity-80">
Select an assistant based on what you want to do. Each assistant is designed for a specific type of work.
</p>
</div>
),
placement: 'bottom',
},
{
target: '#active-assistant',
content: (
<div className="text-left">
<h3 className="font-bold text-lg mb-2">Current Assistant</h3>
<p className="text-sm opacity-80">
Only one assistant can be active at a time. All actions and responses follow this selection.
</p>
</div>
),
placement: 'bottom',
},
{
target: '#chat-history',
content: (
<div className="text-left">
<h3 className="font-bold text-lg mb-2">Conversation History</h3>
<p className="text-sm opacity-80">
This shows past conversations for the selected assistant. Each assistant has its own history.
</p>
</div>
),
placement: 'right',
},
{
target: '#new-chat-button',
content: (
<div className="text-left">
<h3 className="font-bold text-lg mb-2">New Chat</h3>
<p className="text-sm opacity-80">
Start a new conversation here. The chat will belong to the currently selected assistant.
</p>
</div>
),
placement: 'bottom',
},
{
target: 'body',
content: (
<div className="text-left">
<h3 className="font-bold text-lg mb-2">All Set</h3>
<p className="text-sm opacity-80">
You’re ready to begin. Choose an assistant and start your first conversation.
</p>
</div>
),
placement: 'center',
},
];
14 changes: 14 additions & 0 deletions community-chatbot/components/agent/AppTour/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const getJoyrideStyles = () => ({
options: {
overlayColor: 'rgba(0, 0, 0, 0.6)',
primaryColor: '#2563eb',
zIndex: 10000,
},
spotlight: {
borderRadius: '1rem',
},
tooltip: {
backgroundColor: 'transparent',
padding: 0,
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export function HeaderActions() {
const { isCreatingConversation } = useAgentStore();

return (
<div className="flex items-center gap-2">
<div id="header-actions" className="flex items-center gap-2">
<Button
variant="outline"
size="sm"
Expand Down
13 changes: 8 additions & 5 deletions community-chatbot/components/agent/Mode/Chat/InputBox.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import {
MessageInput,
} from '@/components/agent/Mode/Chat/InputBox/MessageInput';
import {
SubmitButton,
} from '@/components/agent/Mode/Chat/InputBox/SubmitButton';
import { useSendMessage } from '@/hooks/agent/Mode/Chat/useSendMessage';

import { MessageInput } from './InputBox/MessageInput';
import { SubmitButton } from './InputBox/SubmitButton';

export function InputBox() {
const { handleSendMessage } = useSendMessage();
const handleSubmit = async (event?: React.FormEvent) => {
Expand All @@ -13,11 +16,11 @@ export function InputBox() {
return (
<div className="bg-white dark:bg-gray-800 p-4 dark:border-gray-700 border-t">
<div className="mx-auto max-w-4xl">
<form onSubmit={handleSubmit} className="flex gap-2">
<form onSubmit={handleSubmit} className="flex items-end gap-2">
<MessageInput />
<SubmitButton />
</form>
</div>
</div>
);
}
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,42 @@
import { usePathname } from 'next/navigation';
import Textarea from 'react-textarea-autosize';

import { Input } from '@/components/ui/input';
import { useSendMessage } from '@/hooks/agent/Mode/Chat/useSendMessage';
import { integrationModes } from '@/lib/constants/chat';
import { useAgentStore } from '@/lib/store/agent/agentStore';

export function MessageInput() {
const { input, setInput, status } = useAgentStore();
const { handleSendMessage } = useSendMessage();

const pathname = usePathname();
const currentMode = pathname.split('/')[1];
const name = integrationModes.find((m) => m.id === currentMode)?.name;

const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
setInput(e.target.value);
};

const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
if (input.trim()) {
handleSendMessage();
}
}
};

return (
<div className="relative flex-1">
<Input
<div className="flex-1">
<Textarea
value={input}
onChange={handleInputChange}
onKeyDown={handleKeyDown}
placeholder={`Ask ${name || "the assistant"}...`}
disabled={status !== "ready"}
autoFocus
maxRows={5}
className="bg-white bg-opacity-10 disabled:opacity-50 shadow-sm mb-0 p-3 border border-input rounded-md focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring w-full placeholder:text-muted-foreground text-sm resize-none disabled:cursor-not-allowed bg scrollbar-aesthetic"
/>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function SubmitButton() {
type="submit"
size="icon"
disabled={!input.trim() || status !== "ready"}
className="bg-blue-600 hover:bg-blue-700"
className="bg-blue-600 hover:bg-blue-700 mb-2 text-white"
>
<Send className="w-4 h-4" />
</Button>
Expand Down
Loading
Loading