import React, { useState, useEffect } from 'react';
import { RefreshCw, Save, Plus, Trash2, Edit, CheckCircle2, AlertTriangle, BookOpen, Layers, DollarSign } from 'lucide-react';
import { Course, Plan } from '../types';
interface AdminPricingManagerProps {
courses: Course[];
fetchCourses: () => void;
}
function CoursePricingCard({ course, saveLoading, onSave }: { course: Course, saveLoading: string | null, onSave: (id: string, locked: boolean, price: number) => void }) {
const [localPrice, setLocalPrice] = useState(course.price || 0);
const [localLocked, setLocalLocked] = useState(course.isLocked || false);
useEffect(() => {
setLocalPrice(course.price || 0);
setLocalLocked(course.isLocked || false);
}, [course.price, course.isLocked]);
const isChanged = localPrice !== (course.price || 0) || localLocked !== (course.isLocked || false);
return (
{course.title}
{course.category}
Acesso
{localLocked && (
)}
);
}
export const AdminPricingManager: React.FC = ({ courses, fetchCourses }) => {
const [plans, setPlans] = useState([]);
const [loading, setLoading] = useState(true);
const [saveLoading, setSaveLoading] = useState(null);
// Modal state for Plans
const [showPlanModal, setShowPlanModal] = useState(false);
const [planForm, setPlanForm] = useState>({ cycle: 'LIFETIME', active: true, includedCourses: [] });
const token = localStorage.getItem('devflix_token');
const fetchPlans = async () => {
setLoading(true);
try {
const res = await fetch('/api/admin/plans', {
headers: { 'Authorization': `Bearer ${token}` }
});
if (res.ok) {
const data = await res.json();
setPlans(data);
}
} catch (err) {
console.error(err);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchPlans();
}, []);
const handleUpdateCoursePrice = async (courseId: string, isLocked: boolean, price: number) => {
setSaveLoading(`course_${courseId}`);
try {
await fetch(`/api/admin/courses/${courseId}/price`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({ isLocked, price })
});
fetchCourses();
} catch (err) {
console.error(err);
} finally {
setSaveLoading(null);
}
};
const handleSavePlan = async (e: React.FormEvent) => {
e.preventDefault();
setSaveLoading('plan_save');
try {
const url = planForm.id ? `/api/admin/plans/${planForm.id}` : '/api/admin/plans';
const method = planForm.id ? 'PUT' : 'POST';
await fetch(url, {
method,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(planForm)
});
await fetchPlans();
setShowPlanModal(false);
} catch (err) {
console.error(err);
} finally {
setSaveLoading(null);
}
};
const handleDeletePlan = async (planId: string) => {
if (!window.confirm('Tem certeza que deseja excluir este plano?')) return;
try {
await fetch(`/api/admin/plans/${planId}`, {
method: 'DELETE',
headers: { 'Authorization': `Bearer ${token}` }
});
fetchPlans();
} catch (err) {
console.error(err);
}
};
const toggleCourseInPlan = (courseId: string) => {
const current = planForm.includedCourses || [];
if (current.includes(courseId)) {
setPlanForm({ ...planForm, includedCourses: current.filter(id => id !== courseId) });
} else {
setPlanForm({ ...planForm, includedCourses: [...current, courseId] });
}
};
return (
{/* SEÇÃO 1: Precificação Avulsa */}
Cursos Individuais (Venda Avulsa)
Configure quais cursos são pagos e defina o preço unitário vitalício.
{courses.map(course => (
))}
{/* SEÇÃO 2: Planos e Combos */}
Combos & Planos de Assinatura
Crie pacotes agrupando cursos ou assinaturas globais mensais/anuais.
{loading ? (
) : plans.length === 0 ? (
Nenhum plano ou combo criado.
Crie pacotes para oferecer descontos na compra de vários cursos juntos.
) : (
{plans.map(plan => (
{!plan.active && (
Inativo
)}
{plan.cycle === 'MONTHLY' ? 'Assinatura Mensal' : plan.cycle === 'YEARLY' ? 'Assinatura Anual' : 'Pagamento Único (Combo)'}
{plan.title}
{plan.description}
R$
{plan.price.toFixed(2).replace('.', ',')}
{plan.cycle !== 'LIFETIME' && / {plan.cycle === 'MONTHLY' ? 'mês' : 'ano'}}
Cursos Inclusos:
{plan.includedCourses.length === 0 ? (
Acesso Global (Tudo)
) : (
{plan.includedCourses.slice(0, 3).map(cId => {
const c = courses.find(cx => cx.id === cId);
return c ? - {c.title}
: null;
})}
{plan.includedCourses.length > 3 && (
- + {plan.includedCourses.length - 3} outros cursos
)}
)}
))}
)}
{/* Plan Form Modal */}
{showPlanModal && (
{planForm.id ? 'Editar Plano / Combo' : 'Criar Novo Plano / Combo'}
)}
);
};