-
Notifications
You must be signed in to change notification settings - Fork 11
Description
// Full-stack example: Top AI Application (Next.js + React)
// This single-file contains examples of frontend React component and backend API route snippets.
// Use these snippets to create actual files in a Next.js project.
/* =====================
QUICK MANUAL TEST CASES (how to verify)
- Open the app, go to Signup, create an account -> expect success message.
- Login with that account -> you should see the main app and your name in the header.
- Click "Translate Demo" -> an alert should show translated text (requires /api/ai).
- Click "Summarize Demo" -> an alert should show summarized text (requires /api/ai).
- Send a chat message -> it should append your message and then AI response (requires /api/ai).
These are manual checks for the demo UI. If you want automated tests I can add simple Jest/React Testing Library tests.
*/
/* =====================
FRONTEND: components/TopAIApp.jsx
===================== */
import React, { useState, useEffect } from "react";
export default function TopAIApp() {
const [view, setView] = useState("auth"); // auth | app
const [token, setToken] = useState(null);
const [user, setUser] = useState(null);
// Auth fields
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [mobile, setMobile] = useState("");
// Chat and editing
const [message, setMessage] = useState("");
const [messages, setMessages] = useState([]);
const [loading, setLoading] = useState(false);
const [history, setHistory] = useState([]);
useEffect(() => {
// Try to load user from localStorage (simple demo)
const t = typeof window !== "undefined" ? localStorage.getItem("t") : null;
const u = typeof window !== "undefined" ? localStorage.getItem("u") : null;
if (t && u) {
setToken(t);
try { setUser(JSON.parse(u)); } catch (e) { setUser(null); }
setView("app");
fetchHistory(t);
}
}, []);
const signup = async () => {
const res = await fetch("/api/auth/signup", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name, email, password, mobile }),
});
const data = await res.json();
if (res.ok) {
alert("Signup successful. Please login.");
setView("auth");
} else alert(data.error || "Signup failed");
};
const login = async () => {
const res = await fetch("/api/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
});
const data = await res.json();
if (res.ok) {
setToken(data.token);
setUser(data.user);
localStorage.setItem("t", data.token);
localStorage.setItem("u", JSON.stringify(data.user));
setView("app");
fetchHistory(data.token);
} else alert(data.error || "Login failed");
};
const logout = async () => {
await fetch("/api/auth/logout", { method: "POST" });
setToken(null);
setUser(null);
localStorage.removeItem("t");
localStorage.removeItem("u");
setView("auth");
};
const fetchHistory = async (t) => {
try {
const res = await fetch("/api/history", {
headers: { Authorization: Bearer ${t} },
});
if (res.ok) {
const d = await res.json();
setHistory(d.history || []);
}
} catch (e) {
console.error(e);
}
};
const sendMessage = async () => {
if (!message.trim()) return;
setLoading(true);
const userMsg = { role: "user", text: message };
setMessages((m) => [...m, userMsg]);
try {
const res = await fetch("/api/ai", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: token ? `Bearer ${token}` : undefined,
},
body: JSON.stringify({ query: message, language: "auto" }),
});
const d = await res.json();
const aiMsg = { role: "ai", text: d.response };
setMessages((m) => [...m, aiMsg]);
// optionally refresh history
fetchHistory(token);
} catch (err) {
console.error(err);
setMessages((m) => [...m, { role: "ai", text: "Error: could not reach AI" }]);
}
setMessage("");
setLoading(false);
};
// Helper functions moved outside JSX to avoid inline string/newline issues
const translateDemo = async () => {
try {
const res = await fetch('/api/ai', {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: token ? Bearer ${token} : undefined },
body: JSON.stringify({ query: 'Please translate to Hindi: Hello world', mode: 'translate', language: 'hi' }),
});
const d = await res.json();
alert('Translate result: ' + (d.response || JSON.stringify(d)));
} catch (e) {
console.error(e);
alert('Translate failed');
}
};
const summarizeDemo = async () => {
try {
const textToSummarize = messages.map((x) => x.text).join('\n');
const res = await fetch('/api/ai', {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: token ? Bearer ${token} : undefined },
body: JSON.stringify({ query: 'Summarize:\n' + textToSummarize, mode: 'summarize' }),
});
const d = await res.json();
alert('Summary: ' + (d.response || JSON.stringify(d)));
} catch (e) {
console.error(e);
alert('Summarize failed');
}
};
// Simple UI in plain JSX (you can replace with Tailwind/Design system)
if (view === "auth") {
return (
<div style={{ maxWidth: 800, margin: "0 auto", padding: 20 }}>
Top AI Application - Signup / Login (Demo)
<div style={{ display: "flex", gap: 12 }}>
<div style={{ flex: 1 }}>
<h3>Signup</h3>
<input placeholder="Name" value={name} onChange={(e) => setName(e.target.value)} />
<input placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} />
<input placeholder="Password" value={password} onChange={(e) => setPassword(e.target.value)} type="password" />
<input placeholder="Mobile" value={mobile} onChange={(e) => setMobile(e.target.value)} />
<button onClick={signup}>Signup</button>
</div>
<div style={{ flex: 1 }}>
<h3>Login</h3>
<input placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} />
<input placeholder="Password" value={password} onChange={(e) => setPassword(e.target.value)} type="password" />
<button onClick={login}>Login</button>
</div>
</div>
<p style={{ marginTop: 12 }}>
Demo: after login you will see chat + editing tools + history. This is a frontend example — create the API routes
shown below to make it fully functional.
</p>
</div>
);
}
return (
<div style={{ maxWidth: 900, margin: "0 auto", padding: 20 }}>
<header style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
Top AI Application
<span style={{ marginRight: 8 }}>{user?.name || user?.email}
Logout
<main style={{ display: "grid", gridTemplateColumns: "2fr 1fr", gap: 16, marginTop: 16 }}>
<section>
<div style={{ border: "1px solid #ddd", padding: 12, borderRadius: 8, minHeight: 400 }}>
<div style={{ minHeight: 300, overflowY: "auto", display: "flex", flexDirection: "column", gap: 8 }}>
{messages.map((m, i) => (
<div key={i} style={{ alignSelf: m.role === "user" ? "flex-end" : "flex-start", background: m.role === "user" ? "#007bff" : "#eee", color: m.role === "user" ? "white" : "black", padding: 8, borderRadius: 8, maxWidth: "75%" }}>
{m.text}
</div>
))}
</div>
<div style={{ display: "flex", gap: 8, marginTop: 8 }}>
<input value={message} onChange={(e) => setMessage(e.target.value)} placeholder="Type your message..." style={{ flex: 1 }} />
<button onClick={sendMessage} disabled={loading}>{loading ? "Thinking..." : "Send"}</button>
</div>
</div>
{/* Editing tools */}
<div style={{ marginTop: 12, border: "1px solid #ddd", padding: 12, borderRadius: 8 }}>
<h3>Editing Tools</h3>
<p>Translate / Summarize / Grammar / Text-to-Speech / Speech-to-Text — call /api/ai with different `mode` values.</p>
<div style={{ display: "flex", gap: 8 }}>
<button onClick={translateDemo}>Translate Demo</button>
<button onClick={summarizeDemo}>Summarize Demo</button>
</div>
</div>
</section>
<aside>
<div style={{ border: '1px solid #ddd', padding: 12, borderRadius: 8 }}>
<h3>History</h3>
<ul>
{history.map((h, i) => (
<li key={i}><strong>{new Date(h.createdAt).toLocaleString()}</strong>: {h.query} → {h.response?.slice(0,80)}...</li>
))}
</ul>
</div>
<div style={{ marginTop: 12, border: '1px solid #ddd', padding: 12, borderRadius: 8 }}>
<h3>Account</h3>
<p>{user?.name}</p>
<p>{user?.email}</p>
<p>{user?.mobile}</p>
</div>
</aside>
</main>
</div>
);
}
/* =====================
BACKEND: /pages/api/auth/signup.js
===================== */
// Example Next.js API route (create file at pages/api/auth/signup.js)
/*
import clientPromise from '../../../lib/mongodb';
import bcrypt from 'bcryptjs';
export default async function handler(req, res) {
if (req.method !== 'POST') return res.status(405).end();
const { name, email, password, mobile } = req.body;
if (!email || !password) return res.status(400).json({ error: 'Missing fields' });
const client = await clientPromise;
const db = client.db('topaiapp');
const existing = await db.collection('users').findOne({ email });
if (existing) return res.status(400).json({ error: 'User exists' });
const hash = await bcrypt.hash(password, 10);
const user = { name, email, password: hash, mobile, createdAt: new Date() };
await db.collection('users').insertOne(user);
res.status(200).json({ ok: true });
}
*/
/* =====================
BACKEND: /pages/api/auth/login.js
===================== /
/
import clientPromise from '../../../lib/mongodb';
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
export default async function handler(req, res) {
if (req.method !== 'POST') return res.status(405).end();
const { email, password } = req.body;
const client = await clientPromise;
const db = client.db('topaiapp');
const user = await db.collection('users').findOne({ email });
if (!user) return res.status(400).json({ error: 'Invalid credentials' });
const ok = await bcrypt.compare(password, user.password);
if (!ok) return res.status(400).json({ error: 'Invalid credentials' });
const token = jwt.sign({ uid: user._id, email: user.email }, process.env.JWT_SECRET, { expiresIn: '7d' });
res.status(200).json({ token, user: { name: user.name, email: user.email, mobile: user.mobile } });
}
*/
/* =====================
BACKEND: /pages/api/auth/logout.js
===================== /
/
export default async function handler(req, res) {
// For stateless JWT-based auth you can just let client remove token.
res.status(200).json({ ok: true });
}
*/
/* =====================
BACKEND: /pages/api/ai.js
===================== /
/
import OpenAI from 'openai';
import clientPromise from '../../lib/mongodb';
import jwt from 'jsonwebtoken';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
export default async function handler(req, res) {
if (req.method !== 'POST') return res.status(405).end();
const { query, mode = 'chat', language = 'auto' } = req.body;
// Optional: verify JWT and get user id
let userId = null;
const auth = req.headers.authorization;
if (auth && auth.startsWith('Bearer ')) {
try {
const payload = jwt.verify(auth.split(' ')[1], process.env.JWT_SECRET);
userId = payload.uid;
} catch (e) {
// ignore invalid token for demo
}
}
try {
// Build prompt or API call depending on mode
let prompt = query;
if (mode === 'summarize') prompt = Summarize the following text:\n\n${query};
if (mode === 'translate' && language) prompt = Translate the following text to ${language}:\n\n${query};
// Call OpenAI Chat Completions (example using chat completions)
const response = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
{ role: 'system', content: 'You are a multilingual helpful assistant.' },
{ role: 'user', content: prompt },
],
max_tokens: 800,
});
const aiText = response.choices[0].message.content;
// Save to history if userId present
if (userId) {
const client = await clientPromise;
const db = client.db('topaiapp');
await db.collection('history').insertOne({ userId, query, response: aiText, mode, createdAt: new Date() });
}
res.status(200).json({ response: aiText });
} catch (e) {
console.error(e);
res.status(500).json({ error: 'AI error' });
}
}
*/
/* =====================
BACKEND: /pages/api/history.js
===================== /
/
import clientPromise from '../../lib/mongodb';
import jwt from 'jsonwebtoken';
export default async function handler(req, res) {
const auth = req.headers.authorization;
if (!auth || !auth.startsWith('Bearer ')) return res.status(401).json({ error: 'Unauthorized' });
try {
const payload = jwt.verify(auth.split(' ')[1], process.env.JWT_SECRET);
const uid = payload.uid;
const client = await clientPromise;
const db = client.db('topaiapp');
const history = await db.collection('history').find({ userId: uid }).sort({ createdAt: -1 }).limit(50).toArray();
res.status(200).json({ history });
} catch (e) {
res.status(401).json({ error: 'Unauthorized' });
}
}
*/
/* =====================
LIB: lib/mongodb.js
===================== /
/
import { MongoClient } from 'mongodb';
const uri = process.env.MONGODB_URI;
let client;
let clientPromise;
if (!process.env.MONGODB_URI) {
throw new Error('Please add MONGODB_URI to .env.local');
}
if (process.env.NODE_ENV === 'development') {
// In dev mode, use a global variable to preserve value across module reloads
if (!global._mongoClientPromise) {
client = new MongoClient(uri);
global._mongoClientPromise = client.connect();
}
clientPromise = global._mongoClientPromise;
} else {
client = new MongoClient(uri);
clientPromise = client.connect();
}
export default clientPromise;
*/
/* =====================
ENV (.env.local)
OPENAI_API_KEY=sk-...
MONGODB_URI=mongodb+srv://USER:PASS@cluster.mongodb.net/topaiapp?retryWrites=true&w=majority
JWT_SECRET=your_long_secret_here
*/
/* =====================
SETUP STEPS (short)
- Create a Next.js app: npx create-next-app@latest my-app
- Add the files above in their respective paths (components, pages/api/*, lib)
- npm install openai mongodb bcryptjs jsonwebtoken
- Add .env.local with keys
- Run: npm run dev
This example is intentionally minimal and educational. For production you must:
- Use HTTPS, secure cookies or refresh token flow
- Validate & sanitize inputs
- Handle errors & rate limits
- Add quotas, subscription & billing if needed
- Follow OpenAI usage policies and content filters
*/
// End of combined example file. Copy each backend snippet into its own file when implementing.