From 0ef8af2525dece4a33b562be700825b664c77fed Mon Sep 17 00:00:00 2001 From: AyrisAI Date: Thu, 23 Jul 2026 17:45:59 +0300 Subject: [PATCH] feat: prompts admin-only, add isOpen field to Prompt model --- app/[locale]/admin/page.tsx | 24 +++++ app/[locale]/prompts/page.tsx | 177 +--------------------------------- components/Navbar.tsx | 3 +- lib/actions/promptActions.ts | 12 +-- prisma/schema.prisma | 1 + 5 files changed, 34 insertions(+), 183 deletions(-) diff --git a/app/[locale]/admin/page.tsx b/app/[locale]/admin/page.tsx index fb8eb9c..3c10c5e 100644 --- a/app/[locale]/admin/page.tsx +++ b/app/[locale]/admin/page.tsx @@ -133,6 +133,7 @@ export default function AdminPage() { const [promptDesc, setPromptDesc] = useState(''); const [promptContent, setPromptContent] = useState(''); const [promptTagsText, setPromptTagsText] = useState('Next.js, System Prompt'); + const [promptIsOpen, setPromptIsOpen] = useState(false); const [savingPrompt, setSavingPrompt] = useState(false); // Lesson Form Modal State (Create or Edit) @@ -386,6 +387,7 @@ export default function AdminPage() { description: promptDesc, content: promptContent, tags, + isOpen: promptIsOpen, }); } else { res = await createPrompt({ @@ -394,6 +396,7 @@ export default function AdminPage() { description: promptDesc, content: promptContent, tags, + isOpen: promptIsOpen, }); } @@ -1295,6 +1298,27 @@ export default function AdminPage() { /> + {/* isOpen Toggle */} +
+
+

Open (Public Access)

+

When enabled, this prompt can be made publicly available

+
+ +
+
- {categories.map((cat) => ( - - ))} -
- - - - {/* Prompts Grid */} - {loading ? ( -
- Loading prompts... -
- ) : prompts.length > 0 ? ( -
- {prompts.map((item) => ( -
-
-
- - {item.category} - -
- - {item.tags?.join(', ')} -
-
- -

- - {item.title} - -

- -

- {item.description} -

- - {/* Prompt Code Preview Box */} -
-
-                    {item.content}
-                  
- - -
-
- -
- - View Full Prompt & Usage - - -
-
- ))} -
- ) : ( -
- No prompts found matching your search. -
- )} - - - ); + // Prompts are admin-only – redirect public visitors to lessons + redirect('/lessons'); } diff --git a/components/Navbar.tsx b/components/Navbar.tsx index bead771..319330f 100644 --- a/components/Navbar.tsx +++ b/components/Navbar.tsx @@ -3,7 +3,7 @@ import Link from 'next/link'; import { usePathname } from 'next/navigation'; import { useTranslations } from 'next-intl'; -import { Code2, BookOpen, Mail, Sparkles, Terminal } from 'lucide-react'; +import { Code2, BookOpen, Mail, Sparkles } from 'lucide-react'; export function Navbar({ locale }: { locale: string }) { const t = useTranslations('nav'); @@ -13,7 +13,6 @@ export function Navbar({ locale }: { locale: string }) { { href: `/${locale}`, label: t('home'), icon: Sparkles }, { href: `/${locale}/lessons`, label: t('lessons'), icon: Code2 }, { href: `/${locale}/cheatsheets`, label: t('cheatsheets'), icon: BookOpen }, - { href: `/${locale}/prompts`, label: 'Prompts', icon: Terminal }, { href: `/${locale}/contact`, label: t('contact'), icon: Mail }, ]; diff --git a/lib/actions/promptActions.ts b/lib/actions/promptActions.ts index 57fcb0c..4ed85f0 100644 --- a/lib/actions/promptActions.ts +++ b/lib/actions/promptActions.ts @@ -53,6 +53,7 @@ export async function createPrompt(data: { description: string; content: string; tags?: string[]; + isOpen?: boolean; }) { try { const slug = data.title @@ -71,11 +72,10 @@ export async function createPrompt(data: { description: data.description, content: data.content, tags, + isOpen: data.isOpen ?? false, }, }); - revalidatePath('/[locale]'); - revalidatePath('/[locale]/prompts'); revalidatePath('/[locale]/admin'); return { success: true, prompt: newPrompt }; } catch (error: any) { @@ -90,6 +90,7 @@ export async function updatePrompt(id: string, data: { description: string; content: string; tags?: string[]; + isOpen?: boolean; }) { try { const tags = data.tags && data.tags.length > 0 ? data.tags : [data.category, 'AI', 'Prompt']; @@ -102,12 +103,11 @@ export async function updatePrompt(id: string, data: { description: data.description, content: data.content, tags, + isOpen: data.isOpen ?? false, updatedAt: new Date(), }, }); - revalidatePath('/[locale]'); - revalidatePath('/[locale]/prompts'); revalidatePath('/[locale]/admin'); return { success: true, prompt: updated }; } catch (error: any) { @@ -122,12 +122,10 @@ export async function deletePrompt(id: string) { where: { id }, }); - revalidatePath('/[locale]'); - revalidatePath('/[locale]/prompts'); revalidatePath('/[locale]/admin'); return { success: true }; } catch (error: any) { console.error('Error deleting prompt:', error); - return { success: false, error: error.message || 'Failed to delete prompt' }; + return { success: false, error: error.message || 'Failed to delete prompt' };; } } diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ee21bc3..b0c41db 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -169,6 +169,7 @@ model Prompt { description String @db.Text content String @db.Text tags String[] + isOpen Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }