Files
ayristech-youtube/app/[locale]/admin/page.tsx
T
2026-07-23 16:55:44 +03:00

1475 lines
61 KiB
TypeScript

'use client';
import { useState, useEffect } from 'react';
import { useTranslations } from 'next-intl';
import {
createLesson,
updateLesson,
getLessons,
deleteLesson,
getDashboardStats
} from '@/lib/actions/lessonActions';
import { getUsers, createAdminUser } from '@/lib/actions/userActions';
import { getSettings, updateSettings } from '@/lib/actions/settingsActions';
import { getContactMessages } from '@/lib/actions/contactActions';
import {
getCategories,
createCategory,
updateCategory,
deleteCategory
} from '@/lib/actions/categoryActions';
import {
getCheatsheets,
createCheatsheet,
updateCheatsheet,
deleteCheatsheet
} from '@/lib/actions/cheatsheetActions';
import {
ShieldAlert,
Plus,
Save,
FileCode,
CheckCircle2,
Trash2,
Edit,
LayoutDashboard,
BookOpen,
Users,
Settings as SettingsIcon,
MessageSquare,
Eye,
Download,
Video,
Loader2,
Search,
X,
Clock,
FileText,
Link as LinkIcon,
FolderTree,
Tag,
Terminal
} from 'lucide-react';
import { YoutubeIcon as Youtube } from '@/components/icons/YoutubeIcon';
function parseChaptersText(text: string) {
if (!text || !text.trim()) return [];
const lines = text.split('\n');
const result: { time: string; seconds: number; title: string }[] = [];
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed) continue;
const match = trimmed.match(/^(\d{1,2}:\d{2}(?::\d{2})?)\s*[-:]?\s*(.+)$/);
if (match) {
const timeStr = match[1];
const titleStr = match[2];
const parts = timeStr.split(':').map(Number);
let seconds = 0;
if (parts.length === 2) {
seconds = parts[0] * 60 + parts[1];
} else if (parts.length === 3) {
seconds = parts[0] * 3600 + parts[1] * 60 + parts[2];
}
result.push({ time: timeStr, seconds, title: titleStr });
}
}
return result;
}
function parseNotesText(text: string) {
if (!text || !text.trim()) return [];
return text
.split('\n')
.map((l) => l.trim())
.filter((l) => l.length > 0);
}
export default function AdminPage() {
const t = useTranslations('admin');
const [activeTab, setActiveTab] = useState<'dashboard' | 'lessons' | 'cheatsheets' | 'categories' | 'users' | 'settings' | 'messages'>('dashboard');
// Stats
const [stats, setStats] = useState({ totalLessons: 0, totalViews: 0, totalDownloads: 0, totalMessages: 0 });
// Lessons list & Search
const [lessons, setLessons] = useState<any[]>([]);
const [lessonSearch, setLessonSearch] = useState('');
const [loadingLessons, setLoadingLessons] = useState(false);
// Dynamic Categories State
const [categoriesList, setCategoriesList] = useState<any[]>([]);
const [newCatName, setNewCatName] = useState('');
const [editingCatId, setEditingCatId] = useState<string | null>(null);
const [editingCatName, setEditingCatName] = useState('');
// Cheatsheets State
const [cheatsheetsList, setCheatsheetsList] = useState<any[]>([]);
const [isCheatFormOpen, setIsCheatFormOpen] = useState(false);
const [editingCheatId, setEditingCheatId] = useState<string | null>(null);
const [cheatTitle, setCheatTitle] = useState('');
const [cheatCategory, setCheatCategory] = useState('Git');
const [cheatDesc, setCheatDesc] = useState('');
const [cheatItems, setCheatItems] = useState<{ command: string; description: string }[]>([
{ command: 'git status', description: 'Check working tree status' },
]);
const [savingCheat, setSavingCheat] = useState(false);
// Lesson Form Modal State (Create or Edit)
const [isFormOpen, setIsFormOpen] = useState(false);
const [editingLessonId, setEditingLessonId] = useState<string | null>(null);
const [formTitle, setFormTitle] = useState('');
const [formYoutubeUrl, setFormYoutubeUrl] = useState('');
const [formCategory, setFormCategory] = useState('Next.js');
const [formDuration, setFormDuration] = useState('15:00');
const [formSummary, setFormSummary] = useState('');
const [formChaptersText, setFormChaptersText] = useState('');
const [formNotesText, setFormNotesText] = useState('');
const [formDownloads, setFormDownloads] = useState<
{ title: string; type: string; url: string; size?: string }[]
>([]);
const [formSnippets, setFormSnippets] = useState<
{ fileName: string; language: string; code: string }[]
>([{ fileName: 'app/page.tsx', language: 'typescript', code: '' }]);
const [savingLesson, setSavingLesson] = useState(false);
// Users State
const [usersList, setUsersList] = useState<any[]>([]);
const [newAdminName, setNewAdminName] = useState('');
const [newAdminEmail, setNewAdminEmail] = useState('');
const [newAdminPass, setNewAdminPass] = useState('');
const [savingUser, setSavingUser] = useState(false);
// Settings State
const [settings, setSettingsData] = useState<Record<string, string>>({
channelName: '',
youtubeUrl: '',
githubUrl: '',
contactEmail: '',
defaultCategory: 'Next.js',
});
const [savingSettings, setSavingSettings] = useState(false);
// Messages State
const [messages, setMessages] = useState<any[]>([]);
// Alert Notifications
const [alert, setAlert] = useState<{ type: 'success' | 'error'; msg: string } | null>(null);
useEffect(() => {
loadTabContent();
}, [activeTab]);
useEffect(() => {
// Pre-fetch categories for lesson modal
getCategories().then((cats) => {
setCategoriesList(cats);
if (cats.length > 0 && !formCategory) {
setFormCategory(cats[0].name);
}
});
}, []);
async function loadTabContent() {
if (activeTab === 'dashboard') {
const [st, les] = await Promise.all([getDashboardStats(), getLessons()]);
setStats(st);
setLessons(les.slice(0, 5));
} else if (activeTab === 'lessons') {
setLoadingLessons(true);
const [les, cats] = await Promise.all([getLessons({ query: lessonSearch }), getCategories()]);
setLessons(les);
setCategoriesList(cats);
setLoadingLessons(false);
} else if (activeTab === 'cheatsheets') {
const sheets = await getCheatsheets();
setCheatsheetsList(sheets);
} else if (activeTab === 'categories') {
const cats = await getCategories();
setCategoriesList(cats);
} else if (activeTab === 'users') {
const u = await getUsers();
setUsersList(u);
} else if (activeTab === 'settings') {
const s = await getSettings();
setSettingsData(s);
} else if (activeTab === 'messages') {
const m = await getContactMessages();
setMessages(m);
}
}
// Filter lessons on search typing
useEffect(() => {
if (activeTab === 'lessons') {
getLessons({ query: lessonSearch }).then(setLessons);
}
}, [lessonSearch]);
const showAlert = (type: 'success' | 'error', msg: string) => {
setAlert({ type, msg });
setTimeout(() => setAlert(null), 4000);
};
// Open Create Form
const handleOpenCreate = () => {
setEditingLessonId(null);
setFormTitle('');
setFormYoutubeUrl('');
setFormCategory(categoriesList[0]?.name || 'Next.js');
setFormDuration('15:00');
setFormSummary('');
setFormChaptersText('');
setFormNotesText('');
setFormDownloads([]);
setFormSnippets([{ fileName: 'app/page.tsx', language: 'typescript', code: '' }]);
setIsFormOpen(true);
};
// Open Edit Form
const handleOpenEdit = (lesson: any) => {
setEditingLessonId(lesson.id);
setFormTitle(lesson.title);
setFormYoutubeUrl(lesson.youtubeUrl);
setFormCategory(lesson.category);
setFormDuration(lesson.duration || '15:00');
setFormSummary(lesson.summary);
setFormNotesText(lesson.notesMarkdown ? lesson.notesMarkdown.join('\n') : '');
setFormChaptersText(
lesson.chapters ? lesson.chapters.map((c: any) => `${c.time} - ${c.title}`).join('\n') : ''
);
setFormDownloads(
lesson.downloads
? lesson.downloads.map((d: any) => ({
title: d.title,
type: d.type,
url: d.url,
size: d.size || '1.0 MB',
}))
: []
);
if (lesson.codeSnippets && lesson.codeSnippets.length > 0) {
setFormSnippets(
lesson.codeSnippets.map((s: any) => ({
fileName: s.fileName,
language: s.language,
code: s.code,
}))
);
} else {
setFormSnippets([{ fileName: 'app/page.tsx', language: 'typescript', code: '' }]);
}
setIsFormOpen(true);
};
const handleAddSnippet = () => {
setFormSnippets([...formSnippets, { fileName: 'app/actions.ts', language: 'typescript', code: '' }]);
};
const handleRemoveSnippet = (idx: number) => {
setFormSnippets(formSnippets.filter((_, i) => i !== idx));
};
const handleAddDownload = () => {
setFormDownloads([
...formDownloads,
{ title: 'Completed Project (.zip)', type: 'zip', url: '#', size: '2.5 MB' },
]);
};
const handleRemoveDownload = (idx: number) => {
setFormDownloads(formDownloads.filter((_, i) => i !== idx));
};
// Save (Create or Update) Lesson
const handleSaveLesson = async (e: React.FormEvent) => {
e.preventDefault();
setSavingLesson(true);
const chapters = parseChaptersText(formChaptersText);
const notesMarkdown = parseNotesText(formNotesText);
let res;
if (editingLessonId) {
res = await updateLesson(editingLessonId, {
title: formTitle,
youtubeUrl: formYoutubeUrl,
category: formCategory,
duration: formDuration,
summary: formSummary,
chapters,
notesMarkdown,
downloads: formDownloads,
codeSnippets: formSnippets,
});
} else {
res = await createLesson({
title: formTitle,
youtubeUrl: formYoutubeUrl,
category: formCategory,
duration: formDuration,
summary: formSummary,
chapters,
notesMarkdown,
downloads: formDownloads,
codeSnippets: formSnippets,
});
}
setSavingLesson(false);
if (res.success) {
showAlert('success', editingLessonId ? 'Lesson updated successfully!' : 'New lesson created successfully!');
setIsFormOpen(false);
loadTabContent();
} else {
showAlert('error', res.error || 'Failed to save lesson');
}
};
// Cheatsheet Handlers
const handleOpenCheatCreate = () => {
setEditingCheatId(null);
setCheatTitle('');
setCheatCategory('Git');
setCheatDesc('');
setCheatItems([{ command: 'git status', description: 'Check working tree status' }]);
setIsCheatFormOpen(true);
};
const handleOpenCheatEdit = (sheet: any) => {
setEditingCheatId(sheet.id);
setCheatTitle(sheet.title);
setCheatCategory(sheet.category);
setCheatDesc(sheet.description);
setCheatItems(
sheet.items && sheet.items.length > 0
? sheet.items.map((i: any) => ({ command: i.command, description: i.description }))
: [{ command: '', description: '' }]
);
setIsCheatFormOpen(true);
};
const handleSaveCheatsheet = async (e: React.FormEvent) => {
e.preventDefault();
setSavingCheat(true);
let res;
if (editingCheatId) {
res = await updateCheatsheet(editingCheatId, {
title: cheatTitle,
category: cheatCategory,
description: cheatDesc,
items: cheatItems,
});
} else {
res = await createCheatsheet({
title: cheatTitle,
category: cheatCategory,
description: cheatDesc,
items: cheatItems,
});
}
setSavingCheat(false);
if (res.success) {
showAlert('success', editingCheatId ? 'Cheatsheet updated successfully!' : 'New cheatsheet created successfully!');
setIsCheatFormOpen(false);
getCheatsheets().then(setCheatsheetsList);
} else {
showAlert('error', res.error || 'Failed to save cheatsheet');
}
};
const handleDeleteCheatsheet = async (id: string, title: string) => {
if (!confirm(`Are you sure you want to delete cheatsheet "${title}"?`)) return;
const res = await deleteCheatsheet(id);
if (res.success) {
showAlert('success', `Cheatsheet "${title}" deleted`);
getCheatsheets().then(setCheatsheetsList);
} else {
showAlert('error', res.error || 'Failed to delete cheatsheet');
}
};
// Category CRUD Handlers
const handleCreateCategory = async (e: React.FormEvent) => {
e.preventDefault();
if (!newCatName.trim()) return;
const res = await createCategory(newCatName);
if (res.success) {
showAlert('success', `Category "${newCatName}" created successfully!`);
setNewCatName('');
getCategories().then(setCategoriesList);
} else {
showAlert('error', res.error || 'Failed to create category');
}
};
const handleUpdateCategory = async (id: string) => {
if (!editingCatName.trim()) return;
const res = await updateCategory(id, editingCatName);
if (res.success) {
showAlert('success', 'Category updated successfully!');
setEditingCatId(null);
setEditingCatName('');
getCategories().then(setCategoriesList);
} else {
showAlert('error', res.error || 'Failed to update category');
}
};
const handleDeleteCategory = async (id: string, name: string) => {
if (!confirm(`Are you sure you want to delete category "${name}"?`)) return;
const res = await deleteCategory(id);
if (res.success) {
showAlert('success', `Category "${name}" deleted`);
getCategories().then(setCategoriesList);
} else {
showAlert('error', res.error || 'Failed to delete category');
}
};
// Delete Lesson
const handleDeleteLesson = async (id: string) => {
if (!confirm('Are you sure you want to delete this tutorial?')) return;
const res = await deleteLesson(id);
if (res.success) {
showAlert('success', 'Lesson deleted');
setLessons(lessons.filter((l) => l.id !== id));
}
};
// Create Admin User
const handleCreateUser = async (e: React.FormEvent) => {
e.preventDefault();
setSavingUser(true);
const res = await createAdminUser({ name: newAdminName, email: newAdminEmail, password: newAdminPass });
setSavingUser(false);
if (res.success) {
showAlert('success', 'New Admin user created successfully!');
setNewAdminName('');
setNewAdminEmail('');
setNewAdminPass('');
getUsers().then(setUsersList);
} else {
showAlert('error', res.error || 'Failed to create user');
}
};
// Save Settings
const handleSaveSettings = async (e: React.FormEvent) => {
e.preventDefault();
setSavingSettings(true);
const res = await updateSettings(settings);
setSavingSettings(false);
if (res.success) {
showAlert('success', 'Settings updated successfully!');
} else {
showAlert('error', res.error || 'Failed to update settings');
}
};
return (
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-10 flex-1 w-full flex flex-col md:flex-row gap-8">
{/* Left Vertical Sidebar Navigation */}
<aside className="w-full md:w-64 flex-shrink-0 space-y-6">
<div className="p-4 rounded-2xl bg-slate-900/60 border border-slate-800 space-y-2">
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-red-500/10 text-red-400 text-xs font-bold border border-red-500/20">
<ShieldAlert className="w-3.5 h-3.5" />
<span>Creator Admin</span>
</div>
<h1 className="text-xl font-black text-white tracking-tight">CMS Portal</h1>
<p className="text-[11px] text-slate-400">ayris.tech Management</p>
</div>
{/* Sidebar Nav Buttons */}
<nav className="flex flex-col gap-1.5 bg-slate-900/80 p-2 rounded-2xl border border-slate-800">
<button
onClick={() => setActiveTab('dashboard')}
className={`flex items-center gap-3 px-4 py-3 rounded-xl text-xs font-bold transition-all text-left ${
activeTab === 'dashboard'
? 'bg-red-600 text-white shadow-lg shadow-red-600/30'
: 'text-slate-400 hover:text-white hover:bg-slate-800/50'
}`}
>
<LayoutDashboard className="w-4 h-4" />
<span>Dashboard</span>
</button>
<button
onClick={() => setActiveTab('lessons')}
className={`flex items-center gap-3 px-4 py-3 rounded-xl text-xs font-bold transition-all text-left ${
activeTab === 'lessons'
? 'bg-red-600 text-white shadow-lg shadow-red-600/30'
: 'text-slate-400 hover:text-white hover:bg-slate-800/50'
}`}
>
<BookOpen className="w-4 h-4" />
<span>Lessons & Code</span>
</button>
<button
onClick={() => setActiveTab('cheatsheets')}
className={`flex items-center gap-3 px-4 py-3 rounded-xl text-xs font-bold transition-all text-left ${
activeTab === 'cheatsheets'
? 'bg-red-600 text-white shadow-lg shadow-red-600/30'
: 'text-slate-400 hover:text-white hover:bg-slate-800/50'
}`}
>
<Terminal className="w-4 h-4" />
<span>Cheatsheets</span>
</button>
<button
onClick={() => setActiveTab('categories')}
className={`flex items-center gap-3 px-4 py-3 rounded-xl text-xs font-bold transition-all text-left ${
activeTab === 'categories'
? 'bg-red-600 text-white shadow-lg shadow-red-600/30'
: 'text-slate-400 hover:text-white hover:bg-slate-800/50'
}`}
>
<FolderTree className="w-4 h-4" />
<span>Categories</span>
</button>
<button
onClick={() => setActiveTab('users')}
className={`flex items-center gap-3 px-4 py-3 rounded-xl text-xs font-bold transition-all text-left ${
activeTab === 'users'
? 'bg-red-600 text-white shadow-lg shadow-red-600/30'
: 'text-slate-400 hover:text-white hover:bg-slate-800/50'
}`}
>
<Users className="w-4 h-4" />
<span>Users</span>
</button>
<button
onClick={() => setActiveTab('settings')}
className={`flex items-center gap-3 px-4 py-3 rounded-xl text-xs font-bold transition-all text-left ${
activeTab === 'settings'
? 'bg-red-600 text-white shadow-lg shadow-red-600/30'
: 'text-slate-400 hover:text-white hover:bg-slate-800/50'
}`}
>
<SettingsIcon className="w-4 h-4" />
<span>Settings</span>
</button>
<button
onClick={() => setActiveTab('messages')}
className={`flex items-center gap-3 px-4 py-3 rounded-xl text-xs font-bold transition-all text-left ${
activeTab === 'messages'
? 'bg-red-600 text-white shadow-lg shadow-red-600/30'
: 'text-slate-400 hover:text-white hover:bg-slate-800/50'
}`}
>
<MessageSquare className="w-4 h-4" />
<span>Messages</span>
</button>
</nav>
</aside>
{/* Main Content View */}
<main className="flex-1 space-y-6 min-w-0">
{/* Alert Banner */}
{alert && (
<div className={`p-4 rounded-2xl border text-xs font-bold flex items-center justify-between shadow-lg ${
alert.type === 'success' ? 'bg-emerald-950/60 border-emerald-800 text-emerald-300' : 'bg-red-950/60 border-red-800 text-red-300'
}`}>
<div className="flex items-center gap-2">
<CheckCircle2 className="w-4 h-4" />
<span>{alert.msg}</span>
</div>
<button onClick={() => setAlert(null)}><X className="w-4 h-4" /></button>
</div>
)}
{/* TAB 1: DASHBOARD STATS */}
{activeTab === 'dashboard' && (
<div className="space-y-8">
{/* Stat Cards Grid */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<div className="p-6 rounded-3xl bg-slate-900/60 border border-slate-800 space-y-2">
<div className="flex items-center justify-between text-slate-400">
<span className="text-xs font-semibold">Total Tutorials</span>
<Video className="w-5 h-5 text-red-500" />
</div>
<div className="text-3xl font-black text-white">{stats.totalLessons}</div>
</div>
<div className="p-6 rounded-3xl bg-slate-900/60 border border-slate-800 space-y-2">
<div className="flex items-center justify-between text-slate-400">
<span className="text-xs font-semibold">Total Views</span>
<Eye className="w-5 h-5 text-sky-400" />
</div>
<div className="text-3xl font-black text-white">{stats.totalViews.toLocaleString()}</div>
</div>
<div className="p-6 rounded-3xl bg-slate-900/60 border border-slate-800 space-y-2">
<div className="flex items-center justify-between text-slate-400">
<span className="text-xs font-semibold">Project Downloads</span>
<Download className="w-5 h-5 text-emerald-400" />
</div>
<div className="text-3xl font-black text-white">{stats.totalDownloads.toLocaleString()}</div>
</div>
<div className="p-6 rounded-3xl bg-slate-900/60 border border-slate-800 space-y-2">
<div className="flex items-center justify-between text-slate-400">
<span className="text-xs font-semibold">Viewer Inquiries</span>
<MessageSquare className="w-5 h-5 text-amber-400" />
</div>
<div className="text-3xl font-black text-white">{stats.totalMessages}</div>
</div>
</div>
{/* Recent Lessons Table Preview */}
<div className="p-6 rounded-3xl bg-slate-900/60 border border-slate-800 space-y-4">
<div className="flex items-center justify-between">
<h2 className="text-lg font-extrabold text-white">Recent YouTube Tutorials</h2>
<button
onClick={() => setActiveTab('lessons')}
className="text-xs font-bold text-red-400 hover:underline"
>
Manage All Lessons
</button>
</div>
<div className="space-y-3">
{lessons.map((lesson) => (
<div key={lesson.id} className="p-4 rounded-2xl bg-slate-950 border border-slate-800 flex items-center justify-between gap-4">
<div className="space-y-1">
<span className="text-[10px] font-bold text-red-400 bg-red-500/10 px-2 py-0.5 rounded border border-red-500/20">
{lesson.category}
</span>
<h3 className="font-bold text-white text-sm">{lesson.title}</h3>
</div>
<div className="flex items-center gap-2">
<button
onClick={() => { setActiveTab('lessons'); handleOpenEdit(lesson); }}
className="px-3 py-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-xs font-bold text-slate-200 flex items-center gap-1"
>
<Edit className="w-3.5 h-3.5 text-sky-400" />
<span>Edit</span>
</button>
</div>
</div>
))}
</div>
</div>
</div>
)}
{/* TAB 2: LESSONS & EDIT MODAL */}
{activeTab === 'lessons' && (
<div className="space-y-6">
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
{/* Search Input */}
<div className="relative w-full sm:w-80">
<Search className="w-4 h-4 text-slate-400 absolute left-3.5 top-1/2 -translate-y-1/2" />
<input
type="text"
placeholder="Filter lessons by title or code..."
value={lessonSearch}
onChange={(e) => setLessonSearch(e.target.value)}
className="w-full bg-slate-900 border border-slate-800 rounded-xl pl-10 pr-4 py-2 text-xs text-white placeholder-slate-500 focus:outline-none"
/>
</div>
<button
onClick={handleOpenCreate}
className="px-5 py-2.5 rounded-xl bg-red-600 hover:bg-red-500 text-white text-xs font-bold flex items-center gap-2 shadow-lg shadow-red-600/30 transition-all"
>
<Plus className="w-4 h-4" />
<span>Create New Tutorial</span>
</button>
</div>
{/* Lessons Table / List */}
{loadingLessons ? (
<div className="p-12 text-center text-slate-400 flex items-center justify-center gap-2">
<Loader2 className="w-5 h-5 animate-spin text-red-500" />
<span>Loading tutorials...</span>
</div>
) : lessons.length > 0 ? (
<div className="space-y-3">
{lessons.map((item) => (
<div key={item.id} className="p-5 rounded-2xl bg-slate-900/60 border border-slate-800 flex items-center justify-between gap-4">
<div className="space-y-1">
<div className="flex items-center gap-2">
<span className="text-[10px] font-bold text-red-400 bg-red-500/10 px-2 py-0.5 rounded border border-red-500/20">
{item.category}
</span>
<span className="text-[10px] text-slate-500 font-mono">
{item.codeSnippets?.length || 0} Files | {item.chapters?.length || 0} Timestamps
</span>
</div>
<h3 className="font-bold text-white text-sm">{item.title}</h3>
<p className="text-xs text-slate-400 line-clamp-1">{item.summary}</p>
</div>
<div className="flex items-center gap-2">
<button
onClick={() => handleOpenEdit(item)}
className="px-3 py-2 rounded-xl bg-slate-800 hover:bg-slate-700 text-white text-xs font-bold flex items-center gap-1.5 transition-colors border border-slate-700/60"
>
<Edit className="w-3.5 h-3.5 text-sky-400" />
<span>Edit</span>
</button>
<button
onClick={() => handleDeleteLesson(item.id)}
className="p-2 rounded-xl bg-slate-800 hover:bg-red-600 text-slate-400 hover:text-white transition-colors"
title="Delete Lesson"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
</div>
))}
</div>
) : (
<div className="p-12 text-center bg-slate-900/40 rounded-2xl border border-slate-800 text-slate-400 text-xs">
No tutorials found.
</div>
)}
</div>
)}
{/* TAB 3: CHEATSHEETS CRUD */}
{activeTab === 'cheatsheets' && (
<div className="space-y-6">
<div className="flex items-center justify-between">
<h2 className="text-lg font-extrabold text-white">Cheatsheets & Commands ({cheatsheetsList.length})</h2>
<button
onClick={handleOpenCheatCreate}
className="px-5 py-2.5 rounded-xl bg-red-600 hover:bg-red-500 text-white text-xs font-bold flex items-center gap-2 shadow-lg shadow-red-600/30 transition-all"
>
<Plus className="w-4 h-4" />
<span>Create New Cheatsheet</span>
</button>
</div>
<div className="space-y-4">
{cheatsheetsList.map((sheet) => (
<div key={sheet.id} className="p-5 rounded-2xl bg-slate-900/60 border border-slate-800 space-y-3">
<div className="flex items-center justify-between gap-4">
<div>
<span className="text-[10px] font-bold text-red-400 bg-red-500/10 px-2 py-0.5 rounded border border-red-500/20">
{sheet.category}
</span>
<h3 className="font-bold text-white text-base mt-1">{sheet.title}</h3>
<p className="text-xs text-slate-400">{sheet.description}</p>
</div>
<div className="flex items-center gap-2">
<button
onClick={() => handleOpenCheatEdit(sheet)}
className="px-3 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-xs font-bold text-sky-400 flex items-center gap-1"
>
<Edit className="w-3.5 h-3.5" />
<span>Edit</span>
</button>
<button
onClick={() => handleDeleteCheatsheet(sheet.id, sheet.title)}
className="p-1.5 rounded-xl bg-slate-800 hover:bg-red-600 text-slate-400 hover:text-white transition-colors"
title="Delete Cheatsheet"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
</div>
<div className="space-y-2 pt-2 border-t border-slate-800">
{sheet.items?.slice(0, 3).map((item: any, idx: number) => (
<div key={idx} className="p-2.5 rounded-lg bg-slate-950 border border-slate-800 flex items-center justify-between text-xs font-mono">
<span className="text-red-300">{item.command}</span>
<span className="text-slate-500 text-[11px]">{item.description}</span>
</div>
))}
</div>
</div>
))}
</div>
</div>
)}
{/* TAB 4: CATEGORIES CRUD */}
{activeTab === 'categories' && (
<div className="space-y-6">
{/* Create Category Form */}
<form onSubmit={handleCreateCategory} className="p-6 rounded-3xl bg-slate-900/60 border border-slate-800 space-y-4">
<h2 className="text-base font-bold text-white flex items-center gap-2">
<FolderTree className="w-4 h-4 text-red-400" />
<span>Create New Category</span>
</h2>
<div className="flex items-center gap-3">
<input
type="text"
required
placeholder="Category Name (e.g. Rust & WASM, Next.js, Go Microservices)"
value={newCatName}
onChange={(e) => setNewCatName(e.target.value)}
className="bg-slate-950 border border-slate-800 rounded-xl px-4 py-2.5 text-xs text-white focus:outline-none flex-1"
/>
<button
type="submit"
className="px-5 py-2.5 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"
>
<Plus className="w-4 h-4" />
<span>Add Category</span>
</button>
</div>
</form>
{/* Categories List */}
<div className="p-6 rounded-3xl bg-slate-900/60 border border-slate-800 space-y-4">
<h2 className="text-base font-bold text-white">Categories ({categoriesList.length})</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
{categoriesList.map((cat) => (
<div key={cat.id} className="p-4 rounded-2xl bg-slate-950 border border-slate-800 flex items-center justify-between gap-3">
{editingCatId === cat.id ? (
<div className="flex items-center gap-2 flex-1">
<input
type="text"
value={editingCatName}
onChange={(e) => setEditingCatName(e.target.value)}
className="bg-slate-900 border border-slate-700 rounded-lg px-3 py-1 text-xs text-white focus:outline-none flex-1"
/>
<button
onClick={() => handleUpdateCategory(cat.id)}
className="p-1.5 rounded-lg bg-emerald-600 text-white text-xs font-bold"
>
Save
</button>
<button
onClick={() => setEditingCatId(null)}
className="p-1.5 rounded-lg bg-slate-800 text-slate-400 hover:text-white"
>
<X className="w-3.5 h-3.5" />
</button>
</div>
) : (
<>
<div className="flex items-center gap-2">
<Tag className="w-4 h-4 text-red-400" />
<span className="font-bold text-white text-xs">{cat.name}</span>
</div>
<div className="flex items-center gap-1.5">
<button
onClick={() => { setEditingCatId(cat.id); setEditingCatName(cat.name); }}
className="p-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-sky-400 transition-colors"
title="Edit Category Name"
>
<Edit className="w-3.5 h-3.5" />
</button>
<button
onClick={() => handleDeleteCategory(cat.id, cat.name)}
className="p-1.5 rounded-lg bg-slate-800 hover:bg-red-600 text-slate-400 hover:text-white transition-colors"
title="Delete Category"
>
<Trash2 className="w-3.5 h-3.5" />
</button>
</div>
</>
)}
</div>
))}
</div>
</div>
</div>
)}
{/* TAB 5: USERS / ADMINS */}
{activeTab === 'users' && (
<div className="space-y-6">
{/* Create User Form */}
<form onSubmit={handleCreateUser} className="p-6 rounded-3xl bg-slate-900/60 border border-slate-800 space-y-4">
<h2 className="text-base font-bold text-white flex items-center gap-2">
<Users className="w-4 h-4 text-red-400" />
<span>Add New Admin User</span>
</h2>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
<input
type="text"
required
placeholder="Full Name"
value={newAdminName}
onChange={(e) => setNewAdminName(e.target.value)}
className="bg-slate-950 border border-slate-800 rounded-xl px-4 py-2 text-xs text-white focus:outline-none"
/>
<input
type="email"
required
placeholder="Email Address"
value={newAdminEmail}
onChange={(e) => setNewAdminEmail(e.target.value)}
className="bg-slate-950 border border-slate-800 rounded-xl px-4 py-2 text-xs text-white focus:outline-none"
/>
<input
type="password"
required
placeholder="Password"
value={newAdminPass}
onChange={(e) => setNewAdminPass(e.target.value)}
className="bg-slate-950 border border-slate-800 rounded-xl px-4 py-2 text-xs text-white focus:outline-none"
/>
</div>
<div className="flex justify-end">
<button
type="submit"
disabled={savingUser}
className="px-5 py-2.5 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"
>
{savingUser ? 'Creating...' : 'Create Admin User'}
</button>
</div>
</form>
{/* Users List Table */}
<div className="p-6 rounded-3xl bg-slate-900/60 border border-slate-800 space-y-4">
<h2 className="text-base font-bold text-white">Admin Users ({usersList.length})</h2>
<div className="space-y-3">
{usersList.map((u) => (
<div key={u.id} className="p-4 rounded-2xl bg-slate-950 border border-slate-800 flex items-center justify-between text-xs">
<div>
<span className="font-bold text-white block">{u.name || 'Admin User'}</span>
<span className="text-slate-400">{u.email}</span>
</div>
<span className="px-3 py-1 rounded-full bg-red-500/10 text-red-400 font-bold border border-red-500/20">
{u.role}
</span>
</div>
))}
</div>
</div>
</div>
)}
{/* TAB 6: SETTINGS */}
{activeTab === 'settings' && (
<form onSubmit={handleSaveSettings} className="p-6 sm:p-8 rounded-3xl bg-slate-900/60 border border-slate-800 space-y-6">
<h2 className="text-lg font-extrabold text-white flex items-center gap-2 border-b border-slate-800 pb-4">
<SettingsIcon className="w-5 h-5 text-red-500" />
<span>Channel & Creator Settings</span>
</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-xs font-bold text-slate-300 mb-1">YouTube Channel Name</label>
<input
type="text"
value={settings.channelName || ''}
onChange={(e) => setSettingsData({ ...settings, channelName: e.target.value })}
className="w-full bg-slate-950 border border-slate-800 rounded-xl px-4 py-2.5 text-xs text-white focus:outline-none"
/>
</div>
<div>
<label className="block text-xs font-bold text-slate-300 mb-1">YouTube Channel URL</label>
<input
type="text"
value={settings.youtubeUrl || ''}
onChange={(e) => setSettingsData({ ...settings, youtubeUrl: e.target.value })}
className="w-full bg-slate-950 border border-slate-800 rounded-xl px-4 py-2.5 text-xs text-white focus:outline-none"
/>
</div>
<div>
<label className="block text-xs font-bold text-slate-300 mb-1">GitHub Repositories URL</label>
<input
type="text"
value={settings.githubUrl || ''}
onChange={(e) => setSettingsData({ ...settings, githubUrl: e.target.value })}
className="w-full bg-slate-950 border border-slate-800 rounded-xl px-4 py-2.5 text-xs text-white focus:outline-none"
/>
</div>
<div>
<label className="block text-xs font-bold text-slate-300 mb-1">Contact Email Address</label>
<input
type="email"
value={settings.contactEmail || ''}
onChange={(e) => setSettingsData({ ...settings, contactEmail: e.target.value })}
className="w-full bg-slate-950 border border-slate-800 rounded-xl px-4 py-2.5 text-xs text-white focus:outline-none"
/>
</div>
</div>
<div className="flex justify-end pt-4">
<button
type="submit"
disabled={savingSettings}
className="px-6 py-3 bg-red-600 hover:bg-red-500 text-white text-xs font-bold rounded-xl shadow-lg shadow-red-600/30 flex items-center gap-2"
>
{savingSettings ? 'Saving...' : 'Save Settings'}
</button>
</div>
</form>
)}
{/* TAB 7: MESSAGES */}
{activeTab === 'messages' && (
<div className="space-y-4">
<h2 className="text-lg font-bold text-white">Viewer Contact Messages ({messages.length})</h2>
{messages.length > 0 ? (
<div className="space-y-3">
{messages.map((msg) => (
<div key={msg.id} className="p-5 rounded-2xl bg-slate-900/60 border border-slate-800 space-y-2">
<div className="flex items-center justify-between text-xs">
<span className="font-bold text-white">{msg.name} ({msg.email})</span>
<span className="text-[10px] text-slate-500">{new Date(msg.createdAt).toLocaleString()}</span>
</div>
<h4 className="text-xs font-semibold text-red-400">{msg.subject}</h4>
<p className="text-xs text-slate-300 leading-relaxed bg-slate-950 p-3 rounded-xl border border-slate-800">
{msg.message}
</p>
</div>
))}
</div>
) : (
<div className="p-8 text-center bg-slate-900/40 rounded-2xl border border-slate-800 text-slate-400 text-xs">
No viewer contact messages received yet.
</div>
)}
</div>
)}
</main>
{/* CREATE / EDIT CHEATSHEET FORM MODAL */}
{isCheatFormOpen && (
<div className="fixed inset-0 z-50 bg-slate-950/80 backdrop-blur-md flex items-center justify-center p-4 overflow-y-auto">
<div className="w-full max-w-2xl bg-slate-900 border border-slate-800 rounded-3xl p-6 sm:p-8 space-y-6 shadow-2xl my-8">
<div className="flex items-center justify-between border-b border-slate-800 pb-4">
<h2 className="text-xl font-bold text-white flex items-center gap-2">
<Terminal className="w-5 h-5 text-red-500" />
<span>{editingCheatId ? 'Edit Cheatsheet' : 'Create New Cheatsheet'}</span>
</h2>
<button onClick={() => setIsCheatFormOpen(false)} className="p-1.5 rounded-lg bg-slate-800 text-slate-400 hover:text-white">
<X className="w-5 h-5" />
</button>
</div>
<form onSubmit={handleSaveCheatsheet} className="space-y-4">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-xs font-bold text-slate-300 mb-1">Title</label>
<input
type="text"
required
value={cheatTitle}
onChange={(e) => setCheatTitle(e.target.value)}
placeholder="Git Essential Commands"
className="w-full bg-slate-950 border border-slate-800 rounded-xl px-4 py-2 text-xs text-white focus:outline-none"
/>
</div>
<div>
<label className="block text-xs font-bold text-slate-300 mb-1">Category</label>
<input
type="text"
required
value={cheatCategory}
onChange={(e) => setCheatCategory(e.target.value)}
placeholder="Git / Next.js / Docker"
className="w-full bg-slate-950 border border-slate-800 rounded-xl px-4 py-2 text-xs text-white focus:outline-none"
/>
</div>
</div>
<div>
<label className="block text-xs font-bold text-slate-300 mb-1">Description</label>
<textarea
rows={2}
required
value={cheatDesc}
onChange={(e) => setCheatDesc(e.target.value)}
placeholder="Daily Git commands for branch management..."
className="w-full bg-slate-950 border border-slate-800 rounded-xl px-4 py-2 text-xs text-white focus:outline-none"
/>
</div>
{/* Command Items */}
<div className="space-y-3 pt-3 border-t border-slate-800">
<div className="flex items-center justify-between">
<h3 className="text-xs font-bold text-white">Commands ({cheatItems.length})</h3>
<button
type="button"
onClick={() => setCheatItems([...cheatItems, { command: '', description: '' }])}
className="px-3 py-1 bg-slate-800 hover:bg-slate-700 text-white text-xs font-semibold rounded-lg flex items-center gap-1"
>
<Plus className="w-3.5 h-3.5" />
<span>Add Command</span>
</button>
</div>
{cheatItems.map((item, idx) => (
<div key={idx} className="p-3 bg-slate-950 rounded-xl border border-slate-800 flex items-center gap-3">
<input
type="text"
required
placeholder="git status"
value={item.command}
onChange={(e) => {
const updated = [...cheatItems];
updated[idx].command = e.target.value;
setCheatItems(updated);
}}
className="bg-slate-900 border border-slate-800 rounded-lg px-3 py-1.5 text-xs font-mono text-white focus:outline-none flex-1"
/>
<input
type="text"
required
placeholder="Check tree status"
value={item.description}
onChange={(e) => {
const updated = [...cheatItems];
updated[idx].description = e.target.value;
setCheatItems(updated);
}}
className="bg-slate-900 border border-slate-800 rounded-lg px-3 py-1.5 text-xs text-white focus:outline-none flex-1"
/>
{cheatItems.length > 1 && (
<button
type="button"
onClick={() => setCheatItems(cheatItems.filter((_, i) => i !== idx))}
className="p-1 text-slate-500 hover:text-red-400"
>
<Trash2 className="w-4 h-4" />
</button>
)}
</div>
))}
</div>
<div className="flex justify-end gap-3 pt-4 border-t border-slate-800">
<button
type="button"
onClick={() => setIsCheatFormOpen(false)}
className="px-4 py-2 rounded-xl bg-slate-800 text-xs font-bold text-slate-300"
>
Cancel
</button>
<button
type="submit"
disabled={savingCheat}
className="px-5 py-2 bg-red-600 hover:bg-red-500 text-white text-xs font-bold rounded-xl flex items-center gap-2"
>
{savingCheat ? 'Saving...' : 'Save Cheatsheet'}
</button>
</div>
</form>
</div>
</div>
)}
{/* CREATE / EDIT LESSON FORM MODAL */}
{isFormOpen && (
<div className="fixed inset-0 z-50 bg-slate-950/80 backdrop-blur-md flex items-center justify-center p-4 overflow-y-auto">
<div className="w-full max-w-3xl bg-slate-900 border border-slate-800 rounded-3xl p-6 sm:p-8 space-y-6 shadow-2xl my-8">
<div className="flex items-center justify-between border-b border-slate-800 pb-4">
<h2 className="text-xl font-bold text-white flex items-center gap-2">
<FileCode className="w-5 h-5 text-red-500" />
<span>{editingLessonId ? 'Edit Tutorial Resources' : 'Create New YouTube Tutorial'}</span>
</h2>
<button onClick={() => setIsFormOpen(false)} className="p-1.5 rounded-lg bg-slate-800 text-slate-400 hover:text-white">
<X className="w-5 h-5" />
</button>
</div>
<form onSubmit={handleSaveLesson} className="space-y-5">
<div>
<label className="block text-xs font-bold text-slate-300 mb-1">{t('videoTitle')}</label>
<input
type="text"
required
value={formTitle}
onChange={(e) => setFormTitle(e.target.value)}
placeholder="Next.js 16 & Server Actions Masterclass"
className="w-full bg-slate-950 border border-slate-800 rounded-xl px-4 py-2.5 text-xs text-white focus:outline-none focus:border-red-500"
/>
</div>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
<div>
<label className="block text-xs font-bold text-slate-300 mb-1">{t('youtubeUrl')}</label>
<input
type="text"
required
value={formYoutubeUrl}
onChange={(e) => setFormYoutubeUrl(e.target.value)}
placeholder="https://www.youtube.com/watch?v=..."
className="w-full bg-slate-950 border border-slate-800 rounded-xl px-4 py-2.5 text-xs text-white focus:outline-none focus:border-red-500"
/>
</div>
<div>
<label className="block text-xs font-bold text-slate-300 mb-1">{t('category')}</label>
<select
value={formCategory}
onChange={(e) => setFormCategory(e.target.value)}
className="w-full bg-slate-950 border border-slate-800 rounded-xl px-4 py-2.5 text-xs text-white focus:outline-none focus:border-red-500"
>
{categoriesList.map((cat) => (
<option key={cat.id} value={cat.name}>
{cat.name}
</option>
))}
</select>
</div>
<div>
<label className="block text-xs font-bold text-slate-300 mb-1">Video Duration</label>
<input
type="text"
required
value={formDuration}
onChange={(e) => setFormDuration(e.target.value)}
placeholder="18:45"
className="w-full bg-slate-950 border border-slate-800 rounded-xl px-4 py-2.5 text-xs text-white focus:outline-none focus:border-red-500 font-mono"
/>
</div>
</div>
<div>
<label className="block text-xs font-bold text-slate-300 mb-1">Lesson Overview & Summary</label>
<textarea
rows={2}
value={formSummary}
onChange={(e) => setFormSummary(e.target.value)}
placeholder="Short overview of topics covered in this video..."
className="w-full bg-slate-950 border border-slate-800 rounded-xl px-4 py-2.5 text-xs text-white focus:outline-none focus:border-red-500"
/>
</div>
{/* Timestamps / Chapters Input */}
<div className="space-y-1.5 pt-3 border-t border-slate-800">
<label className="block text-xs font-bold text-slate-300 flex items-center justify-between">
<span className="flex items-center gap-1.5">
<Clock className="w-4 h-4 text-red-400" />
<span>Video Chapters & Timestamps (Optional)</span>
</span>
<span className="text-[10px] text-slate-500 font-mono">Format: 00:00 - Introduction</span>
</label>
<textarea
rows={3}
value={formChaptersText}
onChange={(e) => setFormChaptersText(e.target.value)}
placeholder={`00:00 - Introduction\n02:30 - Next.js Setup\n08:45 - Server Actions`}
className="w-full bg-slate-950 border border-slate-800 rounded-xl px-4 py-2.5 text-xs font-mono text-slate-200 focus:outline-none focus:border-red-500"
/>
</div>
{/* Lesson Notes Input */}
<div className="space-y-1.5 pt-3 border-t border-slate-800">
<label className="block text-xs font-bold text-slate-300 flex items-center justify-between">
<span className="flex items-center gap-1.5">
<FileText className="w-4 h-4 text-red-400" />
<span>Lesson Notes & Bullet Takeaways (Optional)</span>
</span>
<span className="text-[10px] text-slate-500 font-mono">Optional / Leave empty if none</span>
</label>
<textarea
rows={3}
value={formNotesText}
onChange={(e) => setFormNotesText(e.target.value)}
placeholder={`📌 Optional: Type key notes line by line, or leave empty...`}
className="w-full bg-slate-950 border border-slate-800 rounded-xl px-4 py-2.5 text-xs text-slate-200 focus:outline-none focus:border-red-500"
/>
</div>
{/* Resource Downloads & Links */}
<div className="space-y-3 pt-3 border-t border-slate-800">
<div className="flex items-center justify-between">
<h3 className="text-xs font-bold text-white flex items-center gap-1.5">
<LinkIcon className="w-4 h-4 text-red-400" />
<span>Downloadable Starter & Project Links ({formDownloads.length})</span>
</h3>
<button
type="button"
onClick={handleAddDownload}
className="px-3 py-1 rounded-lg bg-slate-800 hover:bg-slate-700 text-white text-xs font-semibold flex items-center gap-1"
>
<Plus className="w-3.5 h-3.5" />
<span>Add Link</span>
</button>
</div>
{formDownloads.map((dl, idx) => (
<div key={idx} className="p-3 bg-slate-950 rounded-xl border border-slate-800 flex items-center justify-between gap-3 text-xs">
<input
type="text"
value={dl.title}
onChange={(e) => {
const updated = [...formDownloads];
updated[idx].title = e.target.value;
setFormDownloads(updated);
}}
placeholder="Completed Code (.zip)"
className="bg-slate-900 border border-slate-800 rounded-lg px-3 py-1.5 text-xs text-white focus:outline-none flex-1"
/>
<select
value={dl.type}
onChange={(e) => {
const updated = [...formDownloads];
updated[idx].type = e.target.value;
setFormDownloads(updated);
}}
className="bg-slate-900 border border-slate-800 rounded-lg px-2 py-1.5 text-xs text-white focus:outline-none"
>
<option value="zip">ZIP File</option>
<option value="github">GitHub Repo</option>
<option value="pdf">PDF File</option>
<option value="link">External Link</option>
</select>
<input
type="text"
value={dl.url}
onChange={(e) => {
const updated = [...formDownloads];
updated[idx].url = e.target.value;
setFormDownloads(updated);
}}
placeholder="https://..."
className="bg-slate-900 border border-slate-800 rounded-lg px-3 py-1.5 text-xs text-white focus:outline-none flex-1"
/>
<button
type="button"
onClick={() => handleRemoveDownload(idx)}
className="p-1 text-slate-500 hover:text-red-400"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
))}
</div>
{/* Multi-file Code Snippets */}
<div className="space-y-4 pt-3 border-t border-slate-800">
<div className="flex items-center justify-between">
<h3 className="text-xs font-bold text-white flex items-center gap-2">
<FileCode className="w-4 h-4 text-red-400" />
<span>Tabbed Code Files ({formSnippets.length})</span>
</h3>
<button
type="button"
onClick={handleAddSnippet}
className="px-3 py-1 rounded-lg bg-slate-800 hover:bg-slate-700 text-white text-xs font-semibold flex items-center gap-1"
>
<Plus className="w-3.5 h-3.5" />
<span>Add File Tab</span>
</button>
</div>
{formSnippets.map((snip, idx) => (
<div key={idx} className="p-4 bg-slate-950 rounded-2xl border border-slate-800 space-y-3">
<div className="flex items-center justify-between gap-3">
<input
type="text"
value={snip.fileName}
onChange={(e) => {
const updated = [...formSnippets];
updated[idx].fileName = e.target.value;
setFormSnippets(updated);
}}
placeholder="app/page.tsx"
className="bg-slate-900 border border-slate-800 rounded-lg px-3 py-1.5 text-xs font-mono text-white focus:outline-none"
/>
{formSnippets.length > 1 && (
<button
type="button"
onClick={() => handleRemoveSnippet(idx)}
className="p-1 text-slate-500 hover:text-red-400"
>
<Trash2 className="w-4 h-4" />
</button>
)}
</div>
<textarea
rows={5}
value={snip.code}
onChange={(e) => {
const updated = [...formSnippets];
updated[idx].code = e.target.value;
setFormSnippets(updated);
}}
placeholder="// Paste code snippet from the video here..."
className="w-full bg-slate-900 border border-slate-800 rounded-xl p-3 text-xs font-mono text-slate-200 focus:outline-none"
/>
</div>
))}
</div>
{/* Form Action Buttons */}
<div className="pt-4 flex justify-end gap-3 border-t border-slate-800">
<button
type="button"
onClick={() => setIsFormOpen(false)}
className="px-4 py-2.5 rounded-xl bg-slate-800 text-slate-300 text-xs font-bold"
>
Cancel
</button>
<button
type="submit"
disabled={savingLesson}
className="px-6 py-2.5 bg-red-600 hover:bg-red-500 text-white text-xs font-bold rounded-xl shadow-lg shadow-red-600/30 flex items-center gap-2"
>
{savingLesson ? (
<span>Saving...</span>
) : (
<>
<Save className="w-4 h-4" />
<span>{editingLessonId ? 'Save Changes' : 'Publish Tutorial'}</span>
</>
)}
</button>
</div>
</form>
</div>
</div>
)}
</div>
);
}