From 52d158fb3f9841208cba8bcd62e971b96dee275f Mon Sep 17 00:00:00 2001 From: tylerwelsh Date: Sat, 19 Jul 2025 14:30:38 -0400 Subject: [PATCH 1/4] feat: tylers ai chat bot --- .cursor/rules/frontend-rule.mdc | 3 +- .cursor/rules/general-rule.mdc | 1 - frontend/.gitignore | 35 + frontend/README.md | 155 +- frontend/app/components/ChatInterface.tsx | 295 + frontend/app/globals.css | 34 + frontend/app/layout.tsx | 41 + frontend/app/page.tsx | 12 + frontend/next.config.js | 9 + frontend/package-lock.json | 6055 +++++++++++++++++++++ frontend/package.json | 28 + frontend/postcss.config.js | 6 + frontend/tailwind.config.js | 20 + frontend/tsconfig.json | 27 + frontend/vercel.json | 6 + 15 files changed, 6723 insertions(+), 4 deletions(-) create mode 100644 frontend/.gitignore create mode 100644 frontend/app/components/ChatInterface.tsx create mode 100644 frontend/app/globals.css create mode 100644 frontend/app/layout.tsx create mode 100644 frontend/app/page.tsx create mode 100644 frontend/next.config.js create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/postcss.config.js create mode 100644 frontend/tailwind.config.js create mode 100644 frontend/tsconfig.json create mode 100644 frontend/vercel.json diff --git a/.cursor/rules/frontend-rule.mdc b/.cursor/rules/frontend-rule.mdc index f79bf900b..fd94ca63c 100644 --- a/.cursor/rules/frontend-rule.mdc +++ b/.cursor/rules/frontend-rule.mdc @@ -10,4 +10,5 @@ alwaysApply: false - When asking the user for sensitive information - you must use password style text-entry boxes in the UI. - You should use Next.js as it works best with Vercel. - This frontend will ultimately be deployed on Vercel, but it should be possible to test locally. -- Always provide users with a way to run the created UI once you have created it. \ No newline at end of file +- Always provide users with a way to run the created UI once you have created it. +- I want the theme colors to be #9E72C3, #924DBF, #7338A0, #4A2574, #0F0529 \ No newline at end of file diff --git a/.cursor/rules/general-rule.mdc b/.cursor/rules/general-rule.mdc index 01ef0ac64..9a26afff7 100644 --- a/.cursor/rules/general-rule.mdc +++ b/.cursor/rules/general-rule.mdc @@ -5,7 +5,6 @@ alwaysApply: true --- ## Rules to Follow -- You must always commit your changes whenever you update code. - You must always try and write code that is well documented. (self or commented is fine) - You must only work on a single feature at a time. - You must explain your decisions thouroughly to the user. \ No newline at end of file diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 000000000..5cc1a8814 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,35 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts \ No newline at end of file diff --git a/frontend/README.md b/frontend/README.md index 56347bab6..97752e9e0 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,3 +1,154 @@ -### Front End +# ๐Ÿค– AI Chat Frontend - Your Beautiful Gateway to OpenAI -Please populate this README with instructions on how to run the application! \ No newline at end of file +Welcome to the most stunning chat interface you've ever laid eyes on! This Next.js frontend connects seamlessly to your FastAPI backend and provides a gorgeous, purple-themed chat experience with OpenAI's API. + +## โœจ Features That'll Make You Smile + +- **๐ŸŽจ Gorgeous UI** - Styled with your custom purple theme (#9E72C3, #924DBF, #7338A0, #4A2574, #0F0529) +- **๐Ÿ” Secure API Key Storage** - Password-masked input with local storage +- **๐Ÿ’ฌ Dual Message Types** - Send both developer/system and user messages +- **๐Ÿ“ฑ Responsive Design** - Looks amazing on desktop, tablet, and mobile +- **๐Ÿš€ Real-time Chat** - Instant responses from OpenAI +- **๐ŸŽฏ Smart Environment Detection** - Works locally and in production +- **๐Ÿงน Clean & Simple** - No clutter, just pure chat goodness + +## ๐Ÿš€ Quick Start (Get Running in 2 Minutes!) + +### Prerequisites +- Node.js 18+ (the newer, the better!) +- npm or yarn (we'll use npm in examples) +- Your awesome FastAPI backend running + +### Installation + +1. **Navigate to the frontend directory:** +```bash +cd frontend +``` + +2. **Install the magic dependencies:** +```bash +npm install +``` + +3. **Fire up the development server:** +```bash +npm run dev +``` + +4. **Open your browser and visit:** +``` +http://localhost:3000 +``` + +5. **๐ŸŽ‰ Boom! You're ready to chat!** + +## ๐Ÿ”ง Development Commands + +| Command | What it does | +|---------|-------------| +| `npm run dev` | Starts development server on localhost:3000 | +| `npm run build` | Builds the app for production | +| `npm run start` | Runs the production build | +| `npm run lint` | Checks your code for issues | + +## ๐ŸŒ Backend Configuration + +### Local Development +When running locally (localhost:3000), the frontend automatically connects to `http://localhost:8000` for your backend. + +### Production Deployment +For production, you'll need to update the backend URL in `app/components/ChatInterface.tsx`: + +```typescript +// Find this line around line 66: +return 'https://your-backend-url.vercel.app' // Update this! +``` + +Replace `https://your-backend-url.vercel.app` with your actual deployed backend URL. + +## ๐Ÿš€ Deploying to Vercel + +### Automatic Deployment (Recommended) + +1. **Push your code to GitHub** +2. **Connect to Vercel:** + - Go to [vercel.com](https://vercel.com) + - Import your GitHub repository + - Select the `frontend` folder as your root directory + - Deploy! ๐ŸŽ‰ + +### Manual Deployment + +1. **Install Vercel CLI:** +```bash +npm i -g vercel +``` + +2. **Deploy from the frontend directory:** +```bash +cd frontend +vercel --prod +``` + +## ๐ŸŽจ Theme Customization + +Your beautiful theme colors are defined in `tailwind.config.js`: + +```javascript +colors: { + 'primary': '#9E72C3', // Main purple + 'primary-dark': '#924DBF', // Darker purple + 'primary-darker': '#7338A0', // Even darker + 'primary-darkest': '#4A2574', // Darkest + 'background': '#0F0529', // Deep space background +} +``` + +Want to tweak the colors? Just update these values and watch the magic happen! + +## ๐Ÿ” Security Notes + +- **API Keys**: Stored in localStorage for convenience, but never sent to our servers +- **CORS**: Backend is configured to accept requests from your frontend +- **Input Validation**: All user inputs are properly validated and sanitized + +## ๐Ÿ› Troubleshooting + +### "Cannot connect to backend" +- Make sure your FastAPI backend is running on port 8000 +- Check if CORS is properly configured in your backend +- Verify the backend URL in production deployments + +### "API Key not working" +- Double-check your OpenAI API key +- Ensure your key has sufficient credits +- Try clearing localStorage and re-entering the key + +### "Build errors" +- Run `npm install` to ensure all dependencies are installed +- Check that you're using Node.js 18 or higher +- Clear `.next` folder and rebuild: `rm -rf .next && npm run build` + +## ๐ŸŽฏ What's Next? + +This frontend is designed to be simple but extensible. Some ideas for future enhancements: + +- Message history persistence +- Multiple conversation threads +- File upload support +- Voice input/output +- Custom model selection +- Conversation export + +## ๐Ÿ’ก Pro Tips + +- Use keyboard shortcuts: Enter to save API key, submit messages +- The chat auto-scrolls to show latest messages +- Messages are color-coded: Developer (purple), User (darker purple), Assistant (darkest purple) +- Clear chat anytime with the "Clear Chat" button +- Change API keys easily with "Change API Key" button + +--- + +**Happy chatting! ๐ŸŽ‰** If you run into any issues, the console logs will be your best friend for debugging. \ No newline at end of file diff --git a/frontend/app/components/ChatInterface.tsx b/frontend/app/components/ChatInterface.tsx new file mode 100644 index 000000000..72689a42f --- /dev/null +++ b/frontend/app/components/ChatInterface.tsx @@ -0,0 +1,295 @@ +'use client' + +import { useState, useEffect, useRef } from 'react' +import toast from 'react-hot-toast' + +interface Message { + id: string + type: 'developer' | 'user' | 'assistant' + content: string + timestamp: Date +} + +interface ChatResponse { + response: string +} + +export default function ChatInterface() { + const [apiKey, setApiKey] = useState('') + const [developerMessage, setDeveloperMessage] = useState('') + const [userMessage, setUserMessage] = useState('') + const [messages, setMessages] = useState([]) + const [isLoading, setIsLoading] = useState(false) + const [showApiKeyInput, setShowApiKeyInput] = useState(true) + const messagesEndRef = useRef(null) + + // Load API key from localStorage on component mount + useEffect(() => { + const savedApiKey = localStorage.getItem('openai_api_key') + if (savedApiKey) { + setApiKey(savedApiKey) + setShowApiKeyInput(false) + } + }, []) + + // Auto-scroll to bottom when new messages are added + useEffect(() => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }) + }, [messages]) + + // Save API key to localStorage + const saveApiKey = () => { + if (!apiKey.trim()) { + toast.error('Please enter your OpenAI API key') + return + } + localStorage.setItem('openai_api_key', apiKey) + setShowApiKeyInput(false) + toast.success('API key saved!') + } + + // Clear API key and show input again + const clearApiKey = () => { + localStorage.removeItem('openai_api_key') + setApiKey('') + setShowApiKeyInput(true) + toast.success('API key cleared') + } + + // Determine backend URL based on environment + const getBackendUrl = () => { + if (typeof window !== 'undefined') { + // Check if we're in development + if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') { + return 'http://localhost:8000' + } + // For production, use the same domain (assuming monorepo deployment) + return `${window.location.protocol}//${window.location.host}` + } + // Fallback - this shouldn't happen in browser + return 'https://the-ai-engineer-challenge-ekgs62u6e-tylers-projects-99bfe70b.vercel.app' + } + + // Send chat request to backend + const sendMessage = async () => { + if (!developerMessage.trim() && !userMessage.trim()) { + toast.error('Please enter at least one message') + return + } + + if (!apiKey.trim()) { + toast.error('Please set your OpenAI API key first') + setShowApiKeyInput(true) + return + } + + setIsLoading(true) + + // Add user messages to chat + const newMessages: Message[] = [] + if (developerMessage.trim()) { + newMessages.push({ + id: Date.now().toString() + '_dev', + type: 'developer', + content: developerMessage, + timestamp: new Date() + }) + } + if (userMessage.trim()) { + newMessages.push({ + id: Date.now().toString() + '_user', + type: 'user', + content: userMessage, + timestamp: new Date() + }) + } + + setMessages(prev => [...prev, ...newMessages]) + + try { + const response = await fetch(`${getBackendUrl()}/api/chat`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + developer_message: developerMessage || '', + user_message: userMessage || '', + model: 'gpt-4.1-mini', + api_key: apiKey + }) + }) + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`) + } + + const responseText = await response.text() + + // Add assistant response + const assistantMessage: Message = { + id: Date.now().toString() + '_assistant', + type: 'assistant', + content: responseText, + timestamp: new Date() + } + + setMessages(prev => [...prev, assistantMessage]) + + // Clear input fields + setDeveloperMessage('') + setUserMessage('') + + } catch (error) { + console.error('Error sending message:', error) + toast.error('Failed to send message. Please check your connection and API key.') + } finally { + setIsLoading(false) + } + } + + // Clear all messages + const clearMessages = () => { + setMessages([]) + toast.success('Chat cleared') + } + + if (showApiKeyInput) { + return ( +
+
+

+ Tyler's AI Chat Interface +

+
+
+ + setApiKey(e.target.value)} + placeholder="Enter your OpenAI API key" + className="w-full px-4 py-2 bg-background text-white border border-primary rounded-lg focus:outline-none focus:border-primary-dark" + onKeyPress={(e) => e.key === 'Enter' && saveApiKey()} + /> +
+ +
+
+
+ ) + } + + return ( +
+ {/* Header */} +
+
+

Tyler's AI Chat Interface

+
+ + +
+
+
+ + {/* Messages */} +
+
+ {messages.map((message) => ( +
+
+
+ {message.type} +
+
{message.content}
+
+
+ ))} + {isLoading && ( +
+
+
Assistant
+
Thinking...
+
+
+ )} +
+
+
+ + {/* Input */} +
+
+
+
+ +