'use client';
import { useEffect, useState, useCallback } from 'react';
import {
Plus,
Trash2,
GripVertical,
ExternalLink,
ToggleLeft,
ToggleRight,
} from 'lucide-react';
import {
fetchDiscoverCategories,
createDiscoverCategory,
updateDiscoverCategory,
deleteDiscoverCategory,
reorderDiscoverCategories,
fetchDiscoverSources,
createDiscoverSource,
deleteDiscoverSource,
} from '@/lib/api';
import type { DiscoverCategory, DiscoverSource } from '@/lib/types';
interface CategoryModalProps {
category?: DiscoverCategory;
onClose: () => void;
onSave: (data: { name: string; nameRu: string; icon: string; color: string; keywords: string[]; regions: string[] }) => void;
}
function CategoryModal({ category, onClose, onSave }: CategoryModalProps) {
const [name, setName] = useState(category?.name || '');
const [nameRu, setNameRu] = useState(category?.nameRu || '');
const [icon, setIcon] = useState(category?.icon || 'π°');
const [color, setColor] = useState(category?.color || '#6B7280');
const [keywords, setKeywords] = useState(category?.keywords?.join(', ') || '');
const [regions, setRegions] = useState(category?.regions?.join(', ') || 'world, russia');
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
onSave({
name,
nameRu,
icon,
color,
keywords: keywords.split(',').map(k => k.trim()).filter(Boolean),
regions: regions.split(',').map(r => r.trim()).filter(Boolean),
});
};
return (
{category ? 'Π Π΅Π΄Π°ΠΊΡΠΈΡΠΎΠ²Π°ΡΡ ΠΊΠ°ΡΠ΅Π³ΠΎΡΠΈΡ' : 'ΠΠΎΠ²Π°Ρ ΠΊΠ°ΡΠ΅Π³ΠΎΡΠΈΡ'}
);
}
interface SourceModalProps {
onClose: () => void;
onSave: (data: { name: string; url: string; logoUrl?: string; categories: string[]; trustScore: number; description?: string }) => void;
}
function SourceModal({ onClose, onSave }: SourceModalProps) {
const [name, setName] = useState('');
const [url, setUrl] = useState('');
const [logoUrl, setLogoUrl] = useState('');
const [categories, setCategories] = useState('');
const [trustScore, setTrustScore] = useState(0.5);
const [description, setDescription] = useState('');
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
onSave({
name,
url,
logoUrl: logoUrl || undefined,
categories: categories.split(',').map(c => c.trim()).filter(Boolean),
trustScore,
description: description || undefined,
});
};
return (
ΠΠΎΠ²ΡΠΉ ΠΈΡΡΠΎΡΠ½ΠΈΠΊ
);
}
export default function AdminDiscoverPage() {
const [categories, setCategories] = useState([]);
const [sources, setSources] = useState([]);
const [loading, setLoading] = useState(true);
const [categoryModal, setCategoryModal] = useState(undefined);
const [showSourceModal, setShowSourceModal] = useState(false);
const [activeTab, setActiveTab] = useState<'categories' | 'sources'>('categories');
const loadData = useCallback(async () => {
setLoading(true);
try {
const [catData, srcData] = await Promise.all([
fetchDiscoverCategories(),
fetchDiscoverSources(),
]);
setCategories(catData.categories || []);
setSources(srcData.sources || []);
} catch (err) {
console.error('Failed to load discover config:', err);
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
loadData();
}, [loadData]);
const handleSaveCategory = async (data: { name: string; nameRu: string; icon: string; color: string; keywords: string[]; regions: string[] }) => {
try {
if (categoryModal) {
await updateDiscoverCategory(categoryModal.id, data);
} else {
await createDiscoverCategory(data);
}
setCategoryModal(undefined);
loadData();
} catch (err) {
console.error('Failed to save category:', err);
}
};
const handleDeleteCategory = async (id: string) => {
if (!confirm('Π£Π΄Π°Π»ΠΈΡΡ ΠΊΠ°ΡΠ΅Π³ΠΎΡΠΈΡ?')) return;
try {
await deleteDiscoverCategory(id);
loadData();
} catch (err) {
console.error('Failed to delete category:', err);
}
};
const handleToggleCategory = async (cat: DiscoverCategory) => {
try {
await updateDiscoverCategory(cat.id, { isActive: !cat.isActive });
loadData();
} catch (err) {
console.error('Failed to toggle category:', err);
}
};
const handleSaveSource = async (data: { name: string; url: string; logoUrl?: string; categories: string[]; trustScore: number; description?: string }) => {
try {
await createDiscoverSource(data);
setShowSourceModal(false);
loadData();
} catch (err) {
console.error('Failed to save source:', err);
}
};
const handleDeleteSource = async (id: string) => {
if (!confirm('Π£Π΄Π°Π»ΠΈΡΡ ΠΈΡΡΠΎΡΠ½ΠΈΠΊ?')) return;
try {
await deleteDiscoverSource(id);
loadData();
} catch (err) {
console.error('Failed to delete source:', err);
}
};
if (loading) {
return (
);
}
return (
Discover
Π£ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ ΠΊΠ°ΡΠ΅Π³ΠΎΡΠΈΡΠΌΠΈ ΠΈ ΠΈΡΡΠΎΡΠ½ΠΈΠΊΠ°ΠΌΠΈ Π½ΠΎΠ²ΠΎΡΡΠ΅ΠΉ
{activeTab === 'categories' && (
ΠΠ΅ΡΠ΅ΡΠ°ΡΠΈΡΠ΅ Π΄Π»Ρ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ ΠΏΠΎΡΡΠ΄ΠΊΠ°
{categories.map((cat) => (
{cat.icon}
{cat.nameRu}
{cat.name} β’ {cat.keywords.join(', ')}
))}
)}
{activeTab === 'sources' && (
ΠΠΎΠ²Π΅ΡΠ΅Π½Π½ΡΠ΅ ΠΈΡΡΠΎΡΠ½ΠΈΠΊΠΈ Π½ΠΎΠ²ΠΎΡΡΠ΅ΠΉ
{sources.length === 0 ? (
ΠΡΡΠΎΡΠ½ΠΈΠΊΠΎΠ² ΠΏΠΎΠΊΠ° Π½Π΅Ρ
) : (
{sources.map((source) => (
{source.logoUrl ? (

) : (
)}
{source.name}
{source.url}
ΠΠΎΠ²Π΅ΡΠΈΠ΅:
= 0.7 ? 'text-green-400' :
source.trustScore >= 0.4 ? 'text-yellow-400' : 'text-red-400'
}`}>
{(source.trustScore * 100).toFixed(0)}%
))}
)}
)}
{categoryModal !== undefined && (
setCategoryModal(undefined)}
onSave={handleSaveCategory}
/>
)}
{showSourceModal && (
setShowSourceModal(false)}
onSave={handleSaveSource}
/>
)}
);
}