Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Prasanth-S7 committed Dec 12, 2024
2 parents 0786d70 + 7c90234 commit 594b9e7
Show file tree
Hide file tree
Showing 25 changed files with 2,335 additions and 176 deletions.
904 changes: 883 additions & 21 deletions admin/package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
},
"dependencies": {
"@coal-works/ui": "../libs/ui",
"@google-cloud/translate": "^8.5.0",
"@hookform/resolvers": "^3.9.1",
"@mapbox/mapbox-gl-draw": "^1.5.0",
"@radix-ui/react-accordion": "^1.2.1",
Expand All @@ -30,6 +31,7 @@
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-switch": "^1.1.1",
"@radix-ui/react-tabs": "^1.1.1",
"@radix-ui/react-toast": "^1.2.2",
"@radix-ui/react-toggle": "^1.1.0",
"@radix-ui/react-toggle-group": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.4",
Expand Down Expand Up @@ -62,6 +64,7 @@
"react-day-picker": "^8.10.1",
"react-dom": "^18.3.1",
"react-dropzone": "^14.3.5",
"react-google-multi-lang": "^1.0.7",
"react-hook-form": "^7.54.0",
"react-map-gl": "^7.1.7",
"react-phone-number-input": "^3.4.9",
Expand Down
5 changes: 5 additions & 0 deletions admin/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import FormPage from './pages/FormPage'
import ControlPlanTemplateBuilder from './pages/createControlPlan'
import SoftwareIntegration from './pages/SoftwareIntegration'
import { RenderSmp } from './pages/RenderSmp'
import Dgms from './pages/Dgms'


interface ErrorPageProps {
Expand Down Expand Up @@ -317,6 +318,10 @@ function App() {
{
path: '/render-smp',
element: <RenderSmp />
},
{
path: '/dgms',
element: <Dgms />
}
// {
// path: '/form/:uniqueKey',
Expand Down
287 changes: 186 additions & 101 deletions admin/src/components/custom/ChatBot.tsx
Original file line number Diff line number Diff line change
@@ -1,131 +1,216 @@
'use client'

import React, { useState, useRef, ChangeEvent } from 'react'
import { SendHorizontal, X, Paperclip } from 'lucide-react'
import { SendHorizontal, X, Paperclip, Bot } from 'lucide-react'
import { motion, AnimatePresence } from 'framer-motion'
import axios from 'axios'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Card } from '@/components/ui/card'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { LoadingDots } from './LoadingDots'
import { MessageBubble } from './MessageBubble'

interface Message {
id: number;
content: string;
role: 'user' | 'assistant';
id: number
content: string
role: 'user' | 'assistant'
}

interface ChatBotProps {
isOpen: boolean;
onClose: () => void;
isOpen: boolean
onClose: () => void
}

export default function ChatBot({ isOpen, onClose }: ChatBotProps) {
const [messages, setMessages] = useState<Message[]>([])
const [input, setInput] = useState<string>('')
const [isLoading, setIsLoading] = useState(false)
const fileInputRef = useRef<HTMLInputElement>(null);
const [messages, setMessages] = useState<Message[]>([])
const [input, setInput] = useState<string>('')
const [isLoading, setIsLoading] = useState(false)
const fileInputRef = useRef<HTMLInputElement>(null)
const scrollAreaRef = useRef<HTMLDivElement>(null)

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

const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()

if (input.trim() === '') return;
const scrollToBottom = () => {
if (scrollAreaRef.current) {
scrollAreaRef.current.scrollTop = scrollAreaRef.current.scrollHeight
}
}

const userMessage: Message = {
id: Date.now(),
content: input,
role: 'user'
}
setMessages(prevMessages => [...prevMessages, userMessage])

setInput('')
setIsLoading(true)

setTimeout(() => {
const assistantMessage: Message = {
id: Date.now() + 1,
content: `No. of active shifts: 13`,
role: 'assistant'
}
setMessages(prevMessages => [...prevMessages, assistantMessage])
setIsLoading(false)
}, 1000)
const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();

if (input.trim() === '') return;

const userMessage: Message = {
id: Date.now(),
content: input,
role: 'user',
};

setMessages((prevMessages) => [...prevMessages, userMessage]);
setInput('');
setIsLoading(true);
scrollToBottom();

try {
const res = await axios.post('http://192.168.110.223:8000/chatbot-query', {
query: input,
});

const assistantReply = res.data.response;

const assistantMessage: Message = {
id: Date.now() + 1,
content: assistantReply,
role: 'assistant',
};

setMessages((prevMessages) => [...prevMessages, assistantMessage]);
} catch (error) {
console.error('Error communicating with chatbot:', error);

const errorMessage: Message = {
id: Date.now() + 2,
content: 'Something went wrong. Please try again.',
role: 'assistant',
};
setMessages((prevMessages) => [...prevMessages, errorMessage]);
} finally {
setIsLoading(false);
scrollToBottom();
}
};


const handleFileUpload = async (e: ChangeEvent<HTMLInputElement>) => {
const files = e.target.files
if (files) {
const fileNames = Array.from(files).map((file) => file.name).join(', ')
const fileMessage: Message = {
id: Date.now(),
content: `Uploaded files: ${fileNames}`,
role: 'user',
}

setMessages((prevMessages) => [...prevMessages, fileMessage])
scrollToBottom()

const formData = new FormData()
Array.from(files).forEach((file) => {
formData.append('file', file)
})

setIsLoading(true)

try {
await axios.post('http://192.168.110.223:8000/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
})

const handleFileUpload = (e: ChangeEvent<HTMLInputElement>) => {
const files = e.target.files
if (files) {
const fileNames = Array.from(files).map(file => file.name).join(', ')
const fileMessage: Message = {
id: Date.now(),
content: `Uploaded files: ${fileNames}`,
role: 'user'
}
setMessages(prevMessages => [...prevMessages, fileMessage])
const successMessage: Message = {
id: Date.now() + 1,
content: `Files uploaded successfully.`,
role: 'assistant',
}
setMessages((prevMessages) => [...prevMessages, successMessage])
} catch (error) {
const errorMessage: Message = {
id: Date.now() + 2,
content: `Failed to upload files. Please try again.`,
role: 'assistant',
}
setMessages((prevMessages) => [...prevMessages, errorMessage])
console.error('File upload failed:', error)
} finally {
setIsLoading(false)
scrollToBottom()
}
}
}

if (!isOpen) return null
if (!isOpen) return null

return (
<Card className="fixed right-4 bottom-20 w-96 h-[600px] flex flex-col shadow-xl">
return (
<AnimatePresence>
{isOpen && (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 20 }}
transition={{ duration: 0.2 }}
>
<Card className="fixed right-4 bottom-20 w-96 h-[600px] flex flex-col shadow-xl bg-background">
<div className="flex justify-between items-center p-4 border-b">
<div className="flex items-center space-x-2">
<Avatar>
<AvatarImage src="/bot-avatar.png" alt="Chat Assistant" />
<AvatarFallback>
<Bot className="h-6 w-6" />
</AvatarFallback>
</Avatar>
<h2 className="text-lg font-semibold">Chat Assistant</h2>
<Button variant="ghost" size="icon" onClick={onClose}>
<X className="h-4 w-4" />
</Button>
</div>
<Button variant="ghost" size="icon" onClick={onClose}>
<X className="h-4 w-4" />
</Button>
</div>
<ScrollArea className="flex-1 p-4 overflow-y-auto">
<ScrollArea className="flex-1 p-4" ref={scrollAreaRef}>
<div className="space-y-4">
{messages.map((message) => (
<div
key={message.id}
className={`mb-4 ${message.role === 'user' ? 'text-right' : 'text-left'}`}
>
<span
className={`inline-block p-2 rounded-lg max-w-[80%] break-words ${
message.role === 'user'
? 'bg-primary text-primary-foreground'
: 'bg-muted'
}`}
>
{message.content}
</span>
</div>
<MessageBubble key={message.id} message={message} />
))}
{isLoading && (
<div className="flex justify-start">
<div className="bg-muted p-2 rounded-lg">
<LoadingDots />
</div>
</div>
)}
</div>
</ScrollArea>
<form onSubmit={onSubmit} className="p-4 border-t">
<div className="flex space-x-2">
<Input
value={input}
onChange={handleInputChange}
placeholder="Type a message..."
className="flex-1"
/>
<input
type="file"
ref={fileInputRef}
onChange={handleFileUpload}
className="hidden"
multiple
accept=".pdf,.doc,.docx,.xls,.xlsx,image/*"
/>
<Button
type="button"
size="icon"
variant="outline"
onClick={() => fileInputRef.current?.click()}
>
<Paperclip className="h-4 w-4" />
</Button>
<Button
type="submit"
size="icon"
disabled={isLoading || input.trim() === ''}
>
<SendHorizontal className="h-4 w-4" />
</Button>
</div>
<div className="flex space-x-2">
<Input
value={input}
onChange={handleInputChange}
placeholder="Type a message..."
className="flex-1"
/>
<input
type="file"
ref={fileInputRef}
onChange={handleFileUpload}
className="hidden"
multiple
accept=".pdf,.doc,.docx,.xls,.xlsx,image/*"
/>
<Button
type="button"
size="icon"
variant="outline"
onClick={() => fileInputRef.current?.click()}
>
<Paperclip className="h-4 w-4" />
</Button>
<Button
type="submit"
size="icon"
disabled={isLoading || input.trim() === ''}
>
<SendHorizontal className="h-4 w-4" />
</Button>
</div>
</form>
</Card>
)
}
</Card>
</motion.div>
)}
</AnimatePresence>
)
}

32 changes: 32 additions & 0 deletions admin/src/components/custom/LanguageSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { useTranslation } from '@/context/TranslationContext';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"

const languages = [
{ code: 'en', name: 'English' },
{ code: 'es', name: 'Spanish' },
{ code: 'fr', name: 'French' },
{ code: 'de', name: 'German' },
{ code: 'it', name: 'Italian' },
// Add more languages as needed
];

export const LanguageSelector: React.FC = () => {
const { setLanguage, currentLanguage } = useTranslation();

return (
<Select onValueChange={setLanguage} defaultValue={currentLanguage}>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Select Language" />
</SelectTrigger>
<SelectContent>
{languages.map((lang) => (
<SelectItem key={lang.code} value={lang.code}>
{lang.name}
</SelectItem>
))}
</SelectContent>
</Select>
);
};

Loading

0 comments on commit 594b9e7

Please sign in to comment.