feat: add AI Prompts section, database model, public pages, and admin CRUD

This commit is contained in:
AyrisAI
2026-07-23 17:38:06 +03:00
parent bd543c3bda
commit bf4e0da112
8 changed files with 762 additions and 7 deletions
+152
View File
@@ -0,0 +1,152 @@
'use client';
import { useState, useEffect, use } from 'react';
import Link from 'next/link';
import { getPromptBySlug } from '@/lib/actions/promptActions';
import { ArrowLeft, Copy, Check, Sparkles, Tag, Share2 } from 'lucide-react';
export default function PromptDetailPage({ params }: { params: Promise<{ locale: string; slug: string }> }) {
const { locale, slug } = use(params);
const [prompt, setPrompt] = useState<any>(null);
const [loading, setLoading] = useState(true);
const [copied, setCopied] = useState(false);
const [copiedLink, setCopiedLink] = useState(false);
useEffect(() => {
async function loadData() {
setLoading(true);
const res = await getPromptBySlug(slug);
setPrompt(res);
setLoading(false);
}
loadData();
}, [slug]);
const handleCopyPrompt = async () => {
if (!prompt) return;
try {
await navigator.clipboard.writeText(prompt.content);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (err) {
console.error('Copy error:', err);
}
};
const handleShare = async () => {
try {
await navigator.clipboard.writeText(window.location.href);
setCopiedLink(true);
setTimeout(() => setCopiedLink(false), 2000);
} catch (err) {
console.error('Share error:', err);
}
};
if (loading) {
return (
<div className="max-w-7xl mx-auto px-4 py-24 text-center text-slate-400 flex flex-col items-center justify-center gap-3">
<p className="text-sm font-semibold">Loading prompt...</p>
</div>
);
}
if (!prompt) {
return (
<div className="max-w-7xl mx-auto px-4 py-24 text-center space-y-4">
<h1 className="text-2xl font-bold text-white">Prompt Not Found</h1>
<Link href={`/${locale}/prompts`} className="text-xs font-bold text-red-400 hover:underline">
Back to Prompts Library
</Link>
</div>
);
}
return (
<div className="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-10 space-y-8 flex-1 w-full">
{/* Navigation & Share Bar */}
<div className="flex items-center justify-between gap-4">
<Link
href={`/${locale}/prompts`}
className="flex items-center gap-1.5 px-3.5 py-2 rounded-xl bg-slate-900 hover:bg-slate-800 text-slate-300 text-xs font-bold border border-slate-800 transition-colors"
>
<ArrowLeft className="w-4 h-4" />
<span>Back to Prompts</span>
</Link>
<button
onClick={handleShare}
className="flex items-center gap-1.5 px-3 py-1.5 rounded-xl bg-slate-900 hover:bg-slate-800 text-slate-300 text-xs font-semibold border border-slate-800 transition-colors"
>
{copiedLink ? (
<>
<Check className="w-3.5 h-3.5 text-emerald-400" />
<span className="text-emerald-400">Link Copied</span>
</>
) : (
<>
<Share2 className="w-3.5 h-3.5 text-slate-400" />
<span>Share Prompt</span>
</>
)}
</button>
</div>
{/* Header Banner */}
<div className="p-8 rounded-3xl bg-gradient-to-br from-slate-900 via-slate-950 to-slate-900 border border-slate-800 space-y-4 shadow-2xl">
<div className="flex flex-wrap items-center gap-2">
<span className="text-xs font-bold text-red-400 bg-red-500/10 px-3 py-1 rounded-full border border-red-500/20">
{prompt.category}
</span>
{prompt.tags?.map((t: string) => (
<span key={t} className="text-[11px] text-slate-400 px-2.5 py-0.5 rounded-full bg-slate-800 border border-slate-700">
#{t}
</span>
))}
</div>
<h1 className="text-3xl sm:text-4xl font-extrabold text-white tracking-tight leading-snug">
{prompt.title}
</h1>
<p className="text-sm text-slate-400 max-w-3xl leading-relaxed">
{prompt.description}
</p>
</div>
{/* Full Prompt Container */}
<div className="p-6 sm:p-8 rounded-3xl bg-slate-900/60 border border-slate-800 space-y-4 shadow-2xl">
<div className="flex items-center justify-between">
<h2 className="text-base font-bold text-white flex items-center gap-2">
<Sparkles className="w-4 h-4 text-red-400" />
<span>AI System Prompt Content</span>
</h2>
<button
onClick={handleCopyPrompt}
className="px-4 py-2 bg-red-600 hover:bg-red-500 text-white text-xs font-bold rounded-xl flex items-center gap-2 shadow-lg shadow-red-600/30 transition-all"
>
{copied ? (
<>
<Check className="w-4 h-4 text-emerald-300" />
<span>Copied to Clipboard!</span>
</>
) : (
<>
<Copy className="w-4 h-4" />
<span>Copy Full Prompt</span>
</>
)}
</button>
</div>
<div className="p-6 bg-slate-950 rounded-2xl border border-slate-800 font-mono text-xs text-slate-200 leading-relaxed overflow-x-auto">
<pre className="whitespace-pre-wrap">{prompt.content}</pre>
</div>
</div>
</div>
);
}