React Integration
This example demonstrates how to integrate the ZeroinAI Chat Service API into a React application.
Project Setup
First, create a new React application:
npx create-react-app zeroinai-chat-react
cd zeroinai-chat-react
Install the required dependencies:
npm install axios styled-components react-icons
Implementation
API Service
Create a file src/services/chatService.js to handle API communication:
import axios from 'axios';
const API_URL = 'https://api.zeroinai.com/micro-apps/for-api-providers/chat-service';
const API_KEY = process.env.REACT_APP_ZEROINAI_API_KEY;
// Create axios instance with default headers
const apiClient = axios.create({
baseURL: API_URL,
headers: {
'Content-Type': 'application/json',
'X-API-Key': API_KEY
}
});
// Create a new chat session
export const createChat = async (tokenLimit = 1000) => {
try {
const response = await apiClient.post('/chats/', {
token_limit: tokenLimit
});
return response.data;
} catch (error) {
console.error('Error creating chat:', error);
throw error;
}
};
// Send a message and get AI response
export const sendMessage = async (chatId, content) => {
try {
const response = await apiClient.post('/chats/messages/', {
chat_id: chatId,
content: content
});
return response.data;
} catch (error) {
console.error('Error sending message:', error);
throw error;
}
};
// Get chat history
export const getChatHistory = async (chatId, limit = 50, offset = 0) => {
try {
const response = await apiClient.get(`/chats/${chatId}/messages/?limit=${limit}&offset=${offset}`);
return response.data;
} catch (error) {
console.error('Error getting chat history:', error);
throw error;
}
};
// Add more tokens to a chat
export const addTokens = async (chatId, additionalTokens = 500) => {
try {
const response = await apiClient.put(`/chats/${chatId}/tokens/`, {
additional_tokens: additionalTokens
});
return response.data;
} catch (error) {
console.error('Error adding tokens:', error);
throw error;
}
};
// Delete a chat
export const deleteChat = async (chatId) => {
try {
const response = await apiClient.delete(`/chats/${chatId}`);
return response.data;
} catch (error) {
console.error('Error deleting chat:', error);
throw error;
}
};
Chat Components
Create a file src/components/Chat.js for the main chat component:
import React, { useState, useEffect, useRef } from 'react';
import styled from 'styled-components';
import { FiSend, FiPlus, FiTrash2 } from 'react-icons/fi';
import { createChat, sendMessage, getChatHistory, addTokens, deleteChat } from '../services/chatService';
const Chat = () => {
const [chatId, setChatId] = useState(null);
const [messages, setMessages] = useState([]);
const [inputValue, setInputValue] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [tokenInfo, setTokenInfo] = useState({ used: 0, limit: 1000 });
const messagesEndRef = useRef(null);
// Initialize chat on component mount
useEffect(() => {
initializeChat();
}, []);
// Scroll to bottom when messages change
useEffect(() => {
scrollToBottom();
}, [messages]);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};
const initializeChat = async () => {
try {
// Check if we have a chat ID in localStorage
const storedChatId = localStorage.getItem('zeroinai_chat_id');
if (storedChatId) {
setChatId(storedChatId);
// Load chat history
const history = await getChatHistory(storedChatId);
setMessages(history.items);
// Update token info (simplified - in a real app you'd get this from the API)
setTokenInfo({ used: 100, limit: 1000 }); // Placeholder values
} else {
// Create a new chat
setIsLoading(true);
addSystemMessage('Creating new chat session...');
const newChat = await createChat();
setChatId(newChat.chat_id);
// Save chat ID to localStorage
localStorage.setItem('zeroinai_chat_id', newChat.chat_id);
// Update token info
setTokenInfo({
used: newChat.token_used,
limit: newChat.token_limit
});
addSystemMessage('Chat session created. You can start chatting!');
}
} catch (error) {
addSystemMessage('Error initializing chat. Please try again.');
console.error('Error initializing chat:', error);
} finally {
setIsLoading(false);
}
};
const handleSendMessage = async () => {
if (!inputValue.trim() || !chatId || isLoading) return;
try {
setIsLoading(true);
// Add user message to UI immediately
const userMessage = {
chat_id: chatId,
is_user: true,
content: inputValue,
timestamp: new Date().toISOString()
};
setMessages(prevMessages => [...prevMessages, userMessage]);
setInputValue('');
// Send message to API
const aiResponse = await sendMessage(chatId, inputValue);
// Add AI response to UI
setMessages(prevMessages => [...prevMessages, aiResponse]);
// Update token info (simplified)
setTokenInfo(prev => ({
...prev,
used: prev.used + 20 // Simplified token counting
}));
// Check if we need more tokens
if (tokenInfo.used >= tokenInfo.limit * 0.9) {
handleAddTokens();
}
} catch (error) {
// Check if error is due to token limit
if (error.response?.status === 402) {
addSystemMessage('Token limit reached. Adding more tokens...');
await handleAddTokens();
// Retry sending the message
handleSendMessage();
} else {
addSystemMessage('Error sending message. Please try again.');
console.error('Error sending message:', error);
}
} finally {
setIsLoading(false);
}
};
const handleAddTokens = async () => {
try {
setIsLoading(true);
const updatedChat = await addTokens(chatId);
setTokenInfo({
used: updatedChat.token_used,
limit: updatedChat.token_limit
});
addSystemMessage('Added 500 more tokens to the conversation.');
} catch (error) {
addSystemMessage('Error adding tokens. Please try again.');
console.error('Error adding tokens:', error);
} finally {
setIsLoading(false);
}
};
const handleDeleteChat = async () => {
try {
setIsLoading(true);
await deleteChat(chatId);
// Clear localStorage and state
localStorage.removeItem('zeroinai_chat_id');
setChatId(null);
setMessages([]);
// Create a new chat
initializeChat();
} catch (error) {
addSystemMessage('Error deleting chat. Please try again.');
console.error('Error deleting chat:', error);
} finally {
setIsLoading(false);
}
};
const addSystemMessage = (content) => {
const systemMessage = {
is_system: true,
content,
timestamp: new Date().toISOString()
};
setMessages(prevMessages => [...prevMessages, systemMessage]);
};
return (
<ChatContainer>
<ChatHeader>
<h1>ZeroinAI Chat</h1>
<TokenInfo>
Tokens: {tokenInfo.used} / {tokenInfo.limit}
</TokenInfo>
</ChatHeader>
<MessagesContainer>
{messages.map((message, index) => (
<MessageBubble
key={index}
isUser={message.is_user}
isSystem={message.is_system}
>
{message.content}
</MessageBubble>
))}
{isLoading && (
<MessageBubble isSystem>
{chatId ? 'AI is typing...' : 'Initializing chat...'}
</MessageBubble>
)}
<div ref={messagesEndRef} />
</MessagesContainer>
<InputContainer>
<ChatInput
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleSendMessage()}
placeholder="Type your message..."
disabled={isLoading || !chatId}
/>
<SendButton onClick={handleSendMessage} disabled={isLoading || !chatId}>
<FiSend />
</SendButton>
</InputContainer>
<ControlsContainer>
<ActionButton onClick={handleAddTokens} disabled={isLoading}>
<FiPlus /> Add Tokens
</ActionButton>
<ActionButton onClick={handleDeleteChat} disabled={isLoading}>
<FiTrash2 /> New Chat
</ActionButton>
</ControlsContainer>
</ChatContainer>
);
};
// Styled Components
const ChatContainer = styled.div`
display: flex;
flex-direction: column;
width: 100%;
max-width: 600px;
height: 80vh;
margin: 0 auto;
background-color: white;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
overflow: hidden;
`;
const ChatHeader = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 20px;
background-color: #007BFF;
color: white;
h1 {
font-size: 20px;
margin: 0;
}
`;
const TokenInfo = styled.div`
font-size: 14px;
background-color: rgba(255, 255, 255, 0.2);
padding: 5px 10px;
border-radius: 15px;
`;
const MessagesContainer = styled.div`
flex: 1;
padding: 20px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 10px;
background-color: #f5f5f5;
`;
const MessageBubble = styled.div`
padding: 10px 15px;
border-radius: 18px;
max-width: 80%;
word-wrap: break-word;
${props => props.isUser && `
align-self: flex-end;
background-color: #007BFF;
color: white;
`}
${props => !props.isUser && !props.isSystem && `
align-self: flex-start;
background-color: white;
color: #333;
border: 1px solid #e0e0e0;
`}
${props => props.isSystem && `
align-self: center;
background-color: #f8d7da;
color: #721c24;
font-size: 14px;
padding: 8px 12px;
border-radius: 10px;
max-width: 90%;
text-align: center;
`}
`;
const InputContainer = styled.div`
display: flex;
padding: 15px;
background-color: white;
border-top: 1px solid #eee;
`;
const ChatInput = styled.input`
flex: 1;
padding: 12px 15px;
border: 1px solid #ddd;
border-radius: 20px;
outline: none;
font-size: 16px;
&:focus {
border-color: #007BFF;
}
&:disabled {
background-color: #f9f9f9;
cursor: not-allowed;
}
`;
const SendButton = styled.button`
margin-left: 10px;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
background-color: #007BFF;
color: white;
border: none;
border-radius: 50%;
cursor: pointer;
&:hover {
background-color: #0069d9;
}
&:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
`;
const ControlsContainer = styled.div`
display: flex;
justify-content: space-between;
padding: 10px 15px;
background-color: #f8f9fa;
border-top: 1px solid #eee;
`;
const ActionButton = styled.button`
display: flex;
align-items: center;
gap: 5px;
padding: 8px 12px;
background-color: #f8f9fa;
color: #495057;
border: 1px solid #ddd;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
&:hover {
background-color: #e9ecef;
}
&:disabled {
opacity: 0.6;
cursor: not-allowed;
}
`;
export default Chat;
App Component
Update src/App.js to use the Chat component:
import React from 'react';
import styled from 'styled-components';
import Chat from './components/Chat';
function App() {
return (
<AppContainer>
<Header>
<h1>ZeroinAI Chat Demo</h1>
<p>Experience intelligent conversations powered by ZeroinAI</p>
</Header>
<MainContent>
<Chat />
</MainContent>
<Footer>
<p>© {new Date().getFullYear()} ZeroinAI. All rights reserved.</p>
</Footer>
</AppContainer>
);
}
const AppContainer = styled.div`
min-height: 100vh;
display: flex;
flex-direction: column;
background-color: #f0f2f5;
`;
const Header = styled.header`
text-align: center;
padding: 30px 20px;
h1 {
margin: 0;
color: #333;
}
p {
margin: 10px 0 0;
color: #666;
}
`;
const MainContent = styled.main`
flex: 1;
display: flex;
justify-content: center;
align-items: flex-start;
padding: 0 20px 40px;
`;
const Footer = styled.footer`
text-align: center;
padding: 20px;
background-color: #fff;
border-top: 1px solid #eee;
p {
margin: 0;
color: #666;
font-size: 14px;
}
`;
export default App;
Environment Setup
Create a .env file in the root of your project to store your API key:
REACT_APP_ZEROINAI_API_KEY=your_api_key_here
Running the Application
Start the development server:
npm start
Open your browser and navigate to http://localhost:3000 to use the chat application.
Key Features of This Implementation
-
React Hooks: Uses React hooks for state management and side effects.
-
Styled Components: Implements a clean, responsive UI using styled-components.
-
Persistent Chat: Stores the chat ID in localStorage to maintain the conversation across page reloads.
-
Token Management: Tracks token usage and automatically adds more tokens when needed.
-
Error Handling: Comprehensive error handling for various API error scenarios.
-
Real-time UI Updates: The UI updates in real-time to show messages, typing indicators, and system notifications.
Extending the Application
You can extend this basic implementation in several ways:
-
State Management: For larger applications, consider using Redux or Context API for state management.
-
User Authentication: Add user authentication to associate chats with specific users.
-
Chat History: Implement a feature to view and search through past conversations.
-
Theming: Add support for light/dark mode or custom themes.
-
Message Formatting: Add support for markdown or rich text formatting in messages.
-
File Uploads: Implement file uploads and attachments in the chat interface.
-
Typing Indicators: Add real-time typing indicators for a more interactive experience.
-
Voice Input: Integrate speech-to-text for voice input capabilities.
Using with TypeScript
If you're using TypeScript, you can define interfaces for the API responses:
// src/types/chat.ts
export interface ChatConfig {
config_id: string;
agent_name: string;
company_name: string;
primary_color: string;
header_text: string;
logo_url: string | null;
welcome_message: string;
chat_first_page_description: string;
created_at: string;
updated_at: string;
is_full_width: boolean;
start_page_title: string | null;
start_page_slogan: string;
}
export interface ChatSession {
chat_id: string;
created_at: string;
agent_name: string;
creator_company_name: string;
token_limit: number;
token_used: number;
deleted: boolean;
messages: Message[];
}
export interface Message {
chat_id: string;
is_user: boolean;
content: string;
timestamp: string;
}
export interface PaginatedResponse<T> {
items: T[];
total: number;
limit: number;
offset: number;
}
Then update your service and components to use these types for better type safety.