diff --git a/src/ChannelBrowser.tsx b/src/ChannelBrowser.tsx
new file mode 100644
index 0000000..4d4c925
--- /dev/null
+++ b/src/ChannelBrowser.tsx
@@ -0,0 +1,178 @@
+import { useState } from 'react';
+import { useApp } from '../store';
+import { channels, formatNumber, getCategoryColor, getChannelPlaylists } from '../data';
+import { Search, Plus, Check, Filter, ArrowRight, List } from 'lucide-react';
+
+const allCategories = ['الكل', 'تعليم', 'تقنية', 'برمجة', 'تاريخ', 'إسلام', 'حروب', 'اختراعات', 'دين', 'شريعة', 'لغة عربية', 'ريادة', 'أعمال', 'تصميم', 'ثقافة', 'سياسة', 'ابتكار', 'مشاريع'];
+
+export default function ChannelBrowser() {
+ const { followedChannelIds, followChannel, unfollowChannel, setView } = useApp();
+ const [searchQuery, setSearchQuery] = useState('');
+ const [selectedCategory, setSelectedCategory] = useState('الكل');
+ const [showFilters, setShowFilters] = useState(false);
+
+ const filteredChannels = channels.filter(ch => {
+ const matchesSearch = searchQuery === '' ||
+ ch.name.includes(searchQuery) ||
+ ch.description.includes(searchQuery) ||
+ ch.categories.some(c => c.includes(searchQuery));
+
+ const matchesCategory = selectedCategory === 'الكل' || ch.categories.includes(selectedCategory);
+
+ return matchesSearch && matchesCategory;
+ });
+
+ return (
+
+ {/* Header */}
+
+
🔍 استكشف القنوات
+
اكتشف قنوات تعليمية متميزة وابدأ متابعتها
+
+
+ {/* Search */}
+
+
+
+ setSearchQuery(e.target.value)}
+ placeholder="ابحث عن قناة أو موضوع..."
+ className="w-full bg-white rounded-xl pr-11 pl-4 py-3 text-sm border border-gray-200 focus:border-purple-400 focus:ring-2 focus:ring-purple-100 outline-none transition-all"
+ />
+
+
+
+
+ {/* Category Filters */}
+ {showFilters && (
+
+ {allCategories.map(cat => (
+
+ ))}
+
+ )}
+
+ {/* Content Filter Notice */}
+
+
🛡️
+
+
فلترة المحتوى نشطة
+
يتم فلترة المحتوى غير المناسب تلقائيًا
+
+
+
+ {/* Channels Grid */}
+
+ {filteredChannels.map(ch => {
+ const isFollowed = followedChannelIds.includes(ch.id);
+ const chPlaylists = getChannelPlaylists(ch.id);
+
+ return (
+
+ {/* Channel Banner */}
+
+
+
+
+
+
{ch.name}
+
{formatNumber(ch.subscriberCount)} مشترك · {ch.videoCount} فيديو
+
+
+
+
+
{ch.description}
+
+ {/* Categories */}
+
+ {ch.categories.slice(0, 3).map(cat => (
+
+ {cat}
+
+ ))}
+
+
+ {/* Playlists Preview */}
+
+ {chPlaylists.slice(0, 2).map(pl => (
+
+ ))}
+
+
+
+
+
+ );
+ })}
+
+
+ {filteredChannels.length === 0 && (
+
+
🔍
+
لم يتم العثور على نتائج
+
جرّب البحث بكلمات مختلفة
+
+ )}
+
+ );
+}
diff --git a/src/ChannelView.tsx b/src/ChannelView.tsx
new file mode 100644
index 0000000..9f2700c
--- /dev/null
+++ b/src/ChannelView.tsx
@@ -0,0 +1,193 @@
+import { useApp } from '../store';
+import { getChannel, getChannelPlaylists, getPlaylistVideos, standaloneVideos, formatNumber, getCategoryColor } from '../data';
+import { ArrowRight, Play, List, CheckCircle, Users, Video } from 'lucide-react';
+
+export default function ChannelView({ channelId }: { channelId: string }) {
+ const { setView, progress, followedChannelIds, followChannel, unfollowChannel } = useApp();
+ const channel = getChannel(channelId);
+
+ if (!channel) {
+ return القناة غير موجودة
;
+ }
+
+ const isFollowed = followedChannelIds.includes(channelId);
+ const channelPlaylists = getChannelPlaylists(channelId);
+ const channelStandalone = standaloneVideos.filter(v => v.channelId === channelId);
+
+ const totalVideos = channelPlaylists.reduce((sum, pl) => sum + (getPlaylistVideos(pl.id)?.length || 0), 0) + channelStandalone.length;
+ const watchedCount = [
+ ...channelStandalone,
+ ...channelPlaylists.flatMap(pl => getPlaylistVideos(pl.id) || [])
+ ].filter(v => progress.watchedVideos.includes(v.id)).length;
+
+ return (
+
+ {/* Channel Header */}
+
+
+
+
+
+
+
+
+
+ {channel.emoji}
+
+
+
{channel.name}
+
{channel.description}
+
+ {formatNumber(channel.subscriberCount)} مشترك
+ {channel.videoCount} فيديو
+
{channelPlaylists.length} قائمة تشغيل
+
+
+
+
+
+ {/* Categories */}
+
+ {channel.categories.map(cat => (
+
+ {cat}
+
+ ))}
+
+
+
+
+ {/* Stats */}
+
+
+
{watchedCount}/{totalVideos}
+
فيديو مُشاهد
+
+
+
+ {channelPlaylists.filter(pl => progress.completedPlaylists.includes(pl.id)).length}/{channelPlaylists.length}
+
+
قوائم مكتملة
+
+ {totalVideos > 0 && (
+
+
التقدم الكلي
+
+
{Math.round((watchedCount / totalVideos) * 100)}%
+
+ )}
+
+
+ {/* Playlists */}
+
+
+
+ قوائم التشغيل ({channelPlaylists.length})
+
+
+
+ {channelPlaylists.map(pl => {
+ const plVideos = getPlaylistVideos(pl.id);
+ const completedCount = plVideos.filter(v => progress.watchedVideos.includes(v.id)).length;
+ const percentage = plVideos.length > 0 ? Math.round((completedCount / plVideos.length) * 100) : 0;
+ const isComplete = progress.completedPlaylists.includes(pl.id);
+
+ return (
+
+ );
+ })}
+
+
+ {/* Standalone Videos */}
+ {channelStandalone.length > 0 && (
+ <>
+
+
+ فيديوهات حديثة
+
+
+ {channelStandalone.map(video => {
+ const isWatched = progress.watchedVideos.includes(video.id);
+ return (
+
+ );
+ })}
+
+ >
+ )}
+
+
+ );
+}
diff --git a/src/Dashboard.tsx b/src/Dashboard.tsx
new file mode 100644
index 0000000..a4513bc
--- /dev/null
+++ b/src/Dashboard.tsx
@@ -0,0 +1,297 @@
+import { useApp } from '../store';
+import { channels, playlists, videos, standaloneVideos, formatNumber, getChannel, getPlaylistVideos } from '../data';
+import { Play, Clock, CheckCircle, Trophy, Flame, Star, BookOpen } from 'lucide-react';
+
+export default function Dashboard() {
+ const { progress, setView, followedChannelIds, markVideoWatched } = useApp();
+
+ const followedChannels = channels.filter(c => followedChannelIds.includes(c.id));
+ const followedPlaylists = playlists.filter(p => followedChannelIds.includes(p.channelId));
+
+ // Get playlists with progress
+ const playlistsInProgress = followedPlaylists
+ .map(pl => ({
+ playlist: pl,
+ channel: getChannel(pl.channelId),
+ plVideos: getPlaylistVideos(pl.id),
+ currentIndex: progress.playlistProgress[pl.id] || 0,
+ completedCount: getPlaylistVideos(pl.id).filter(v => progress.watchedVideos.includes(v.id)).length,
+ }))
+ .filter(p => p.plVideos.length > 0)
+ .sort((a, b) => {
+ // Prioritize playlists in progress
+ const aProgress = a.completedCount / a.plVideos.length;
+ const bProgress = b.completedCount / b.plVideos.length;
+ if (aProgress > 0 && aProgress < 1 && bProgress > 0 && bProgress < 1) return bProgress - aProgress;
+ if (aProgress > 0 && aProgress < 1) return -1;
+ if (bProgress > 0 && bProgress < 1) return 1;
+ return 0;
+ });
+
+ // Get recent videos from followed channels
+ const recentVideos = standaloneVideos
+ .filter(v => followedChannelIds.includes(v.channelId))
+ .concat(
+ followedPlaylists.flatMap(pl =>
+ (videos[pl.id] || []).slice(-2)
+ )
+ )
+ .sort(() => Math.random() - 0.5)
+ .slice(0, 8);
+
+ const totalVideosAvailable = followedPlaylists.reduce((sum, pl) => sum + (videos[pl.id]?.length || 0), 0) + standaloneVideos.filter(v => followedChannelIds.includes(v.channelId)).length;
+ const overallProgress = totalVideosAvailable > 0 ? Math.round((progress.watchedVideos.length / totalVideosAvailable) * 100) : 0;
+
+ return (
+
+ {/* Hero Stats */}
+
+
+
+
+
+
مرحبًا بك في مساري! 🎯
+
واصل رحلة التعلم من حيث توقفت
+
+
+
+
+
{progress.streak}
+
أيام متتالية
+
+
+
+
+
+
+
{progress.watchedVideos.length}
+
فيديو مُشاهد
+
+
+
{progress.completedPlaylists.length}
+
قائمة مكتملة
+
+
+
{followedChannels.length}
+
قناة متابعة
+
+
+
{overallProgress}%
+
نسبة الإنجاز
+
+
+
+
+
+ {/* Playlists Progress */}
+
+
+
+
+ قوائم التشغيل الخاصة بك
+
+ {playlistsInProgress.length} قائمة
+
+
+
+ {playlistsInProgress.slice(0, 9).map(({ playlist, channel, plVideos, completedCount }) => {
+ const percentage = plVideos.length > 0 ? Math.round((completedCount / plVideos.length) * 100) : 0;
+ const isComplete = progress.completedPlaylists.includes(playlist.id);
+ const nextVideoIndex = completedCount;
+ const nextVideo = plVideos[nextVideoIndex];
+
+ return (
+
+ );
+ })}
+
+
+
+ {/* Channel Grid */}
+
+
+
+
+ قنواتك المتابعة
+
+
+
+
+ {followedChannels.map(ch => {
+ const chPlaylists = playlists.filter(p => p.channelId === ch.id);
+ const chVideosWatched = [...standaloneVideos, ...Object.values(videos).flat()].filter(
+ v => v.channelId === ch.id && progress.watchedVideos.includes(v.id)
+ ).length;
+
+ return (
+
+ );
+ })}
+
+
+
+
+
+ {/* Recent Videos */}
+ {recentVideos.length > 0 && (
+
+
+
+
+ جديد من قنواتك
+
+
+
+
+ {recentVideos.map(video => {
+ const channel = getChannel(video.channelId);
+ const isWatched = progress.watchedVideos.includes(video.id);
+
+ return (
+
+ );
+ })}
+
+
+ )}
+
+ {/* Achievements */}
+
+
+
+ الإنجازات
+
+
+ {[
+ { emoji: '🌟', title: 'أول فيديو', desc: 'شاهد أول فيديو', done: progress.watchedVideos.length >= 1 },
+ { emoji: '📚', title: 'طالب مجتهد', desc: 'شاهد 10 فيديوهات', done: progress.watchedVideos.length >= 10 },
+ { emoji: '🔥', title: 'متسلسل', desc: '3 أيام متتالية', done: progress.streak >= 3 },
+ { emoji: '🏆', title: 'منتهٍ', desc: 'أكمل أول قائمة', done: progress.completedPlaylists.length >= 1 },
+ { emoji: '💎', title: 'محترف', desc: 'أكمل 3 قوائم', done: progress.completedPlaylists.length >= 3 },
+ { emoji: '🎯', title: 'هادف', desc: 'شاهد 50 فيديو', done: progress.watchedVideos.length >= 50 },
+ { emoji: '🚀', title: 'صاروخي', desc: '7 أيام متتالية', done: progress.streak >= 7 },
+ { emoji: '👑', title: 'ملك التعلم', desc: 'أكمل 10 قوائم', done: progress.completedPlaylists.length >= 10 },
+ ].map((ach, i) => (
+
+
{ach.emoji}
+
{ach.title}
+
{ach.desc}
+ {ach.done &&
✅ مُحقق
}
+
+ ))}
+
+
+
+ );
+}
diff --git a/src/FolderView.tsx b/src/FolderView.tsx
new file mode 100644
index 0000000..c75fd8f
--- /dev/null
+++ b/src/FolderView.tsx
@@ -0,0 +1,213 @@
+import { useState } from 'react';
+import { useApp } from '../store';
+import { Channel } from '../types';
+import { channels, getChannel, getChannelPlaylists, getPlaylistVideos } from '../data';
+import { ArrowRight, CheckCircle, Plus, X, FolderOpen, List } from 'lucide-react';
+
+export default function FolderView({ folderId }: { folderId: string }) {
+ const { folders, progress, setView, followedChannelIds, addChannelToFolder, removeChannelFromFolder } = useApp();
+ const [showAddChannel, setShowAddChannel] = useState(false);
+
+ const folder = folders.find(f => f.id === folderId);
+ if (!folder) {
+ return المجلد غير موجود
;
+ }
+
+ const folderChannels = folder.channelIds.map(id => getChannel(id)).filter((c): c is Channel => c !== undefined);
+ const folderPlaylists = folder.channelIds.flatMap(chId => getChannelPlaylists(chId));
+
+ // Available channels not in folder
+ const availableChannels = channels.filter(ch =>
+ followedChannelIds.includes(ch.id) && !folder.channelIds.includes(ch.id)
+ );
+
+ const totalVideos = folderPlaylists.reduce((sum, pl) => sum + (getPlaylistVideos(pl.id)?.length || 0), 0);
+ const watchedVideos = folderPlaylists.reduce((sum, pl) => {
+ return sum + getPlaylistVideos(pl.id).filter(v => progress.watchedVideos.includes(v.id)).length;
+ }, 0);
+ const overallPercentage = totalVideos > 0 ? Math.round((watchedVideos / totalVideos) * 100) : 0;
+
+ const folderColorMap: Record = {
+ blue: 'from-blue-500 to-cyan-400',
+ purple: 'from-purple-500 to-indigo-400',
+ amber: 'from-amber-500 to-orange-400',
+ rose: 'from-rose-500 to-pink-400',
+ orange: 'from-orange-500 to-yellow-400',
+ green: 'from-green-500 to-emerald-400',
+ red: 'from-red-500 to-rose-400',
+ teal: 'from-teal-500 to-cyan-400',
+ };
+
+ const gradient = folderColorMap[folder.color] || 'from-gray-500 to-gray-400';
+
+ return (
+
+ {/* Folder Header */}
+
+
+
+
+
+
+
+ {folder.emoji}
+
+
+
{folder.name}
+
{folderChannels.length} قناة · {folderPlaylists.length} قائمة تشغيل
+
+
+ {watchedVideos} من {totalVideos} فيديو
+
+
+
+
{overallPercentage}%
+
+
+
+
+
+
+
+
+ {/* Add Channel Panel */}
+ {showAddChannel && (
+
+
إضافة قناة للمجلد
+ {availableChannels.length > 0 ? (
+
+ {availableChannels.map(ch => (
+
+ ))}
+
+ ) : (
+
جميع القنوات المتابعة مضافة بالفعل
+ )}
+
+ )}
+
+ {/* Channels in Folder */}
+
+
+
+ القنوات في هذا المجلد
+
+
+
+ {folderChannels.map(ch => {
+ const chPlaylists = getChannelPlaylists(ch.id);
+ return (
+
+
+
+
+ );
+ })}
+
+ {availableChannels.length > 0 && (
+
+ )}
+
+
+ {/* Playlists */}
+
+
+ جميع قوائم التشغيل
+
+
+
+ {folderPlaylists.map(pl => {
+ const plVideos = getPlaylistVideos(pl.id);
+ const ch = getChannel(pl.channelId);
+ if (!ch) return null;
+ const completedCount = plVideos.filter(v => progress.watchedVideos.includes(v.id)).length;
+ const percentage = plVideos.length > 0 ? Math.round((completedCount / plVideos.length) * 100) : 0;
+ const isComplete = progress.completedPlaylists.includes(pl.id);
+
+ return (
+
+ );
+ })}
+
+
+
+ );
+}
diff --git a/src/PlaylistView.tsx b/src/PlaylistView.tsx
new file mode 100644
index 0000000..dbec96c
--- /dev/null
+++ b/src/PlaylistView.tsx
@@ -0,0 +1,320 @@
+import { useState, useEffect, useCallback } from 'react';
+import { useApp } from '../store';
+import { getPlaylist, getPlaylistVideos, getChannel } from '../data';
+import { ArrowRight, Play, Pause, SkipForward, SkipBack, CheckCircle, RotateCcw } from 'lucide-react';
+
+export default function PlaylistView({ playlistId }: { playlistId: string }) {
+ const { setView, progress, markVideoWatched, updatePlaylistProgress, completePlaylist } = useApp();
+ const playlist = getPlaylist(playlistId);
+ const [currentIndex, setCurrentIndex] = useState(0);
+ const [isPlaying, setIsPlaying] = useState(false);
+ const [showCelebration, setShowCelebration] = useState(false);
+ const [progressPercent, setProgressPercent] = useState(0);
+ const [autoPlay, setAutoPlay] = useState(true);
+ const [, setElapsed] = useState(0);
+
+ const plVideos = getPlaylistVideos(playlistId);
+ const channel = playlist ? getChannel(playlist.channelId) : null;
+
+ // Set initial index based on progress
+ useEffect(() => {
+ if (plVideos.length > 0) {
+ const lastWatched = progress.playlistProgress[playlistId];
+ if (lastWatched !== undefined) {
+ // Check if all videos before are watched
+ let startFrom = 0;
+ for (let i = 0; i < plVideos.length; i++) {
+ if (!progress.watchedVideos.includes(plVideos[i].id)) {
+ startFrom = i;
+ break;
+ }
+ startFrom = i + 1;
+ }
+ setCurrentIndex(Math.min(startFrom, plVideos.length - 1));
+ }
+ }
+ }, [playlistId]);
+
+ const currentVideo = plVideos[currentIndex];
+ const completedCount = plVideos.filter(v => progress.watchedVideos.includes(v.id)).length;
+ const totalVideos = plVideos.length;
+ const percentage = totalVideos > 0 ? Math.round((completedCount / totalVideos) * 100) : 0;
+
+ // Simulate video playback progress
+ useEffect(() => {
+ let interval: ReturnType;
+ if (isPlaying && currentVideo) {
+ interval = setInterval(() => {
+ setElapsed(prev => {
+ const newElapsed = prev + 1;
+ const newPercent = Math.min(100, (newElapsed / currentVideo.durationSeconds) * 100);
+ setProgressPercent(newPercent);
+
+ if (newPercent >= 100) {
+ handleVideoComplete();
+ return 0;
+ }
+ return newElapsed;
+ });
+ }, 100); // Speed up for demo
+ }
+ return () => clearInterval(interval);
+ }, [isPlaying, currentIndex]);
+
+ const handleVideoComplete = useCallback(() => {
+ if (!currentVideo) return;
+
+ markVideoWatched(currentVideo.id);
+ updatePlaylistProgress(playlistId, currentIndex);
+ setIsPlaying(false);
+ setProgressPercent(100);
+
+ // Check if playlist is complete
+ const newCompletedCount = plVideos.filter(v =>
+ progress.watchedVideos.includes(v.id) || v.id === currentVideo.id
+ ).length;
+
+ if (newCompletedCount >= totalVideos) {
+ completePlaylist(playlistId);
+ setShowCelebration(true);
+ } else if (autoPlay) {
+ // Auto-play next video
+ setTimeout(() => {
+ if (currentIndex < totalVideos - 1) {
+ setCurrentIndex(prev => prev + 1);
+ setElapsed(0);
+ setProgressPercent(0);
+ setIsPlaying(true);
+ }
+ }, 1500);
+ }
+ }, [currentVideo, currentIndex, plVideos, totalVideos, autoPlay]);
+
+ const goToVideo = (index: number) => {
+ if (index >= 0 && index < totalVideos) {
+ setCurrentIndex(index);
+ setElapsed(0);
+ setProgressPercent(0);
+ setIsPlaying(false);
+ }
+ };
+
+ const nextVideo = () => {
+ if (currentIndex < totalVideos - 1) {
+ goToVideo(currentIndex + 1);
+ }
+ };
+
+ const prevVideo = () => {
+ if (currentIndex > 0) {
+ goToVideo(currentIndex - 1);
+ }
+ };
+
+ if (!playlist || !channel || !currentVideo) {
+ return قائمة التشغيل غير موجودة
;
+ }
+
+ return (
+
+ {/* Celebration Overlay */}
+ {showCelebration && (
+
setShowCelebration(false)}>
+
+
🎉
+
تهانينا!
+
لقد أكملت قائمة التشغيل
+
+
{playlist.title}
+
{channel.name}
+
+
+
+
+
+
+
+ )}
+
+ {/* Top Bar */}
+
+
+
+
+ الفيديو
+ {currentIndex + 1}/{totalVideos}
+
+
+
+
+
+ {/* Main Content */}
+
+ {/* Video Player Area */}
+
+ {/* Video Display */}
+
+
+
+ {isPlaying ? (
+
+
{playlist.emoji}
+
{currentVideo.title}
+
جاري التشغيل...
+
+ ) : (
+
+ )}
+
+ {/* Video Progress Bar */}
+
+
+
+ {/* Controls */}
+
+
+
+
+
+
+
+
+ {currentVideo.duration}
+
+
+
+
+
+
+
+
+ {/* Video Info */}
+
+
{currentVideo.title}
+
+
+ {currentVideo.viewCount.toLocaleString()} مشاهدة
+ {currentVideo.publishedAt}
+
+
+
+
+ {/* Playlist Sidebar */}
+
+
+
{playlist.title}
+
{completedCount} من {totalVideos} مكتمل
+
+
+
+
+ {plVideos.map((video, index) => {
+ const isWatched = progress.watchedVideos.includes(video.id);
+ const isCurrent = index === currentIndex;
+
+ return (
+
+ );
+ })}
+
+
+
+
+ );
+}