Files
gitlab-instance-0a899031_cn…/app/[locale]/ai/page.tsx
2026-06-06 04:29:46 -05:00

122 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useState } from "react";
import { useTranslation } from "@/i18n/navigation";
import Footer from "@/app/components/Footer";
import { apiFetch } from "@/app/lib/api-client";
interface AssistantCity {
id: string;
slug?: string;
name: string;
costPerMonth?: number;
internetSpeed?: number;
overallScore?: number;
}
export default function AIPage() {
const { t } = useTranslation("ai");
const [query, setQuery] = useState("");
const [loading, setLoading] = useState(false);
const [answer, setAnswer] = useState("");
const [cities, setCities] = useState<AssistantCity[]>([]);
const exampleQueries = [
"我预算每月800刀想亚洲热带有稳定网适合freelance写作推荐城市+理由",
"帮我写下一站介绍文案",
"生成3月清迈行程计划",
];
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!query.trim()) return;
setLoading(true);
try {
const data = await apiFetch<{ answer: string; cities: AssistantCity[] }>("/api/ai/assistant", {
method: "POST",
body: JSON.stringify({ question: query }),
});
setAnswer(data.answer);
setCities(data.cities || []);
} finally {
setLoading(false);
}
};
return (
<div className="min-h-screen bg-[#fafafa] dark:bg-gray-950">
<div className="max-w-2xl mx-auto px-4 sm:px-6 py-12 sm:py-16">
<div className="text-center mb-10">
<h1 className="text-2xl sm:text-3xl font-bold text-gray-900 dark:text-gray-100">
{t("title")}
</h1>
<p className="mt-2 text-gray-500 dark:text-gray-400">
{t("subtitle")}
</p>
</div>
<div className="rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 p-6">
<form onSubmit={handleSubmit} className="space-y-4">
<textarea
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder={t("placeholder")}
rows={4}
className="w-full rounded-xl border border-gray-200 dark:border-gray-700 px-4 py-3 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-400 resize-none focus:outline-none focus:ring-2 focus:ring-[#ff4d4f]/20 focus:border-[#ff4d4f]"
/>
<button
type="submit"
disabled={loading || !query.trim()}
className="w-full py-3 rounded-xl bg-[#ff4d4f] text-white font-medium hover:bg-[#ff3333] disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
>
{loading ? t("thinking") : t("ask")}
</button>
</form>
{(answer || cities.length > 0) && (
<div className="mt-6 rounded-2xl border border-gray-100 bg-gray-50 p-4 dark:border-gray-800 dark:bg-gray-800">
{answer && <p className="leading-7 text-gray-700 dark:text-gray-300">{answer}</p>}
{cities.length > 0 && (
<div className="mt-4 grid gap-3 sm:grid-cols-3">
{cities.map((city) => (
<div key={city.id} className="rounded-xl bg-white p-3 text-sm shadow-sm dark:bg-gray-900">
<p className="font-semibold text-gray-900 dark:text-gray-100">{city.name}</p>
<p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
¥{Number(city.costPerMonth || 0).toLocaleString()}/ · {city.internetSpeed || 0}Mbps
</p>
<p className="mt-1 text-xs text-[#ff4d4f]"> {city.overallScore || "-"}/5</p>
</div>
))}
</div>
)}
</div>
)}
<div className="mt-6 pt-6 border-t border-gray-100 dark:border-gray-800">
<p className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">
{t("examples")}
</p>
<div className="space-y-2">
{exampleQueries.map((q, i) => (
<button
key={i}
type="button"
onClick={() => setQuery(q)}
className="block w-full text-left px-4 py-3 rounded-xl bg-gray-50 dark:bg-gray-800 text-sm text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-gray-100 transition-colors"
>
{q}
</button>
))}
</div>
</div>
</div>
<p className="mt-6 text-center text-xs text-gray-400 dark:text-gray-500">
AI
</p>
</div>
<Footer />
</div>
);
}