ملف آخر
Signed-off-by: Mohamed-Abdelhalim2 <codepen.io.dropper985@passinbox.com>
This commit is contained in:
+102
@@ -0,0 +1,102 @@
|
||||
import { AppProvider, useApp } from './store';
|
||||
import Sidebar from './components/Sidebar';
|
||||
import Dashboard from './components/Dashboard';
|
||||
import ChannelView from './components/ChannelView';
|
||||
import PlaylistView from './components/PlaylistView';
|
||||
import ChannelBrowser from './components/ChannelBrowser';
|
||||
import FolderView from './components/FolderView';
|
||||
import { Search, Bell, Settings } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
function TopBar() {
|
||||
const { currentView, searchQuery, setSearchQuery } = useApp();
|
||||
const [focused, setFocused] = useState(false);
|
||||
|
||||
const getTitle = () => {
|
||||
switch (currentView.type) {
|
||||
case 'dashboard': return 'الرئيسية';
|
||||
case 'browser': return 'استكشف القنوات';
|
||||
case 'channel': return 'القناة';
|
||||
case 'playlist': return 'قائمة التشغيل';
|
||||
case 'folder': return 'المجلد';
|
||||
default: return 'مساري';
|
||||
}
|
||||
};
|
||||
|
||||
if (currentView.type === 'playlist') return null;
|
||||
|
||||
return (
|
||||
<div className="h-14 bg-white border-b border-gray-100 flex items-center justify-between px-5 shrink-0">
|
||||
<div className="flex items-center gap-3">
|
||||
<h2 className="text-lg font-bold text-gray-800">{getTitle()}</h2>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 max-w-md mx-8">
|
||||
<div className={`relative transition-all ${focused ? 'scale-105' : ''}`}>
|
||||
<Search className="absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" />
|
||||
<input
|
||||
value={searchQuery}
|
||||
onChange={e => setSearchQuery(e.target.value)}
|
||||
onFocus={() => setFocused(true)}
|
||||
onBlur={() => setFocused(false)}
|
||||
placeholder="ابحث في قنواتك..."
|
||||
className={`w-full bg-gray-50 rounded-xl pr-10 pl-4 py-2 text-sm border transition-all outline-none ${
|
||||
focused ? 'border-orange-300 ring-2 ring-orange-100 bg-white' : 'border-transparent'
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<button className="relative p-2.5 rounded-xl hover:bg-gray-100 transition-colors text-gray-400 hover:text-gray-600">
|
||||
<Bell className="w-4.5 h-4.5" />
|
||||
<span className="absolute top-1.5 right-1.5 w-2 h-2 bg-orange-500 rounded-full" />
|
||||
</button>
|
||||
<button className="p-2.5 rounded-xl hover:bg-gray-100 transition-colors text-gray-400 hover:text-gray-600">
|
||||
<Settings className="w-4.5 h-4.5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function MainContent() {
|
||||
const { currentView } = useApp();
|
||||
|
||||
switch (currentView.type) {
|
||||
case 'dashboard':
|
||||
return <Dashboard />;
|
||||
case 'channel':
|
||||
return <ChannelView channelId={currentView.channelId} />;
|
||||
case 'playlist':
|
||||
return <PlaylistView playlistId={currentView.playlistId} />;
|
||||
case 'browser':
|
||||
return <ChannelBrowser />;
|
||||
case 'folder':
|
||||
return <FolderView folderId={currentView.folderId} />;
|
||||
default:
|
||||
return <Dashboard />;
|
||||
}
|
||||
}
|
||||
|
||||
function AppContent() {
|
||||
return (
|
||||
<div className="flex h-screen w-screen overflow-hidden bg-[#FFF8F0]" dir="rtl">
|
||||
<Sidebar />
|
||||
<div className="flex-1 flex flex-col overflow-hidden">
|
||||
<TopBar />
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<MainContent />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<AppProvider>
|
||||
<AppContent />
|
||||
</AppProvider>
|
||||
);
|
||||
}
|
||||
+679
@@ -0,0 +1,679 @@
|
||||
import { Channel, Playlist, Video, Folder } from './types';
|
||||
|
||||
// ── Channels ──────────────────────────────────────────────
|
||||
export const channels: Channel[] = [
|
||||
{
|
||||
id: 'ch-tech',
|
||||
name: 'أبدع تقنيًا',
|
||||
description: 'قناة تعليمية متخصصة في البرمجة والتقنية وتطوير البرمجيات. نقدم دورات تعليمية شاملة في مختلف لغات البرمجة والتقنيات الحديثة.',
|
||||
gradient: 'from-blue-500 to-cyan-400',
|
||||
emoji: '💻',
|
||||
categories: ['تعليم', 'تقنية', 'برمجة', 'ذكاء اصطناعي'],
|
||||
subscriberCount: 1250000,
|
||||
videoCount: 342,
|
||||
},
|
||||
{
|
||||
id: 'ch-history',
|
||||
name: 'تاريخنا المجيد',
|
||||
description: 'قناة مخصصة لتاريخ الإسلام والأمة العربية. نستعرض أحداث التاريخ الإسلامي والعربي بأسلوب شيق ومبسط.',
|
||||
gradient: 'from-amber-500 to-orange-400',
|
||||
emoji: '🏰',
|
||||
categories: ['تاريخ', 'إسلام', 'حروب', 'حضارة', 'تراث'],
|
||||
subscriberCount: 2100000,
|
||||
videoCount: 256,
|
||||
},
|
||||
{
|
||||
id: 'ch-invent',
|
||||
name: 'المخترع العربي',
|
||||
description: 'قناة تهتم بالاختراعات والابتكارات العربية والعالمية. نصنع ونشرح ونبني مشاريع تقنية وعلمية متنوعة.',
|
||||
gradient: 'from-emerald-500 to-teal-400',
|
||||
emoji: '🔧',
|
||||
categories: ['اختراعات', 'تقنية', 'ابتكار', 'روبوتات', 'إلكترونيات'],
|
||||
subscriberCount: 890000,
|
||||
videoCount: 178,
|
||||
},
|
||||
{
|
||||
id: 'ch-sharia',
|
||||
name: 'علوم الشريعة',
|
||||
description: 'قناة علمية شرعية متخصصة في العلوم الشرعية والفقه والعقيدة والتفسير والحديث. نهج أهل السنة والجماعة.',
|
||||
gradient: 'from-purple-500 to-indigo-400',
|
||||
emoji: '📖',
|
||||
categories: ['دين', 'شريعة', 'فقه', 'عقيدة', 'تفسير', 'حديث'],
|
||||
subscriberCount: 3200000,
|
||||
videoCount: 520,
|
||||
},
|
||||
{
|
||||
id: 'ch-lang',
|
||||
name: 'لغتنا الجميلة',
|
||||
description: 'قناة مخصصة للغة العربية الفصحى والنحو والصرف والبلاغة والأدب العربي والشعر والنثر.',
|
||||
gradient: 'from-rose-500 to-pink-400',
|
||||
emoji: '✨',
|
||||
categories: ['لغة عربية', 'أدب', 'شعر', 'نثر', 'بلاغة', 'نحو'],
|
||||
subscriberCount: 760000,
|
||||
videoCount: 195,
|
||||
},
|
||||
{
|
||||
id: 'ch-biz',
|
||||
name: 'ريادة المستقبل',
|
||||
description: 'قناة متخصصة في ريادة الأعمال والمشاريع الناشئة وقصص النجاح العربية. نقدم نصائح عملية لرواد الأعمال.',
|
||||
gradient: 'from-orange-500 to-yellow-400',
|
||||
emoji: '🚀',
|
||||
categories: ['ريادة', 'أعمال', 'إدارة', 'تسويق', 'اقتصاد'],
|
||||
subscriberCount: 540000,
|
||||
videoCount: 134,
|
||||
},
|
||||
{
|
||||
id: 'ch-design',
|
||||
name: 'عالم التصميم',
|
||||
description: 'قناة متخصصة في التصميم الهندسي والمعماري. نستعرض أعظم التصاميم في العالم والمشاريع المعمارية الفريدة.',
|
||||
gradient: 'from-violet-500 to-fuchsia-400',
|
||||
emoji: '🎨',
|
||||
categories: ['تصميم', 'هندسة', 'عمارة', 'فن إسلامي'],
|
||||
subscriberCount: 430000,
|
||||
videoCount: 98,
|
||||
},
|
||||
{
|
||||
id: 'ch-projects',
|
||||
name: 'مشاريع عربية',
|
||||
description: 'قناة تعرض المشاريع العربية والإسلامية الكبرى في مختلف المجالات التقنية والإنشائية والاقتصادية.',
|
||||
gradient: 'from-sky-500 to-blue-400',
|
||||
emoji: '🏗️',
|
||||
categories: ['مشاريع', 'تقنية', 'ابتكار', 'اقتصاد'],
|
||||
subscriberCount: 380000,
|
||||
videoCount: 87,
|
||||
},
|
||||
{
|
||||
id: 'ch-culture',
|
||||
name: 'ثقافتنا هويتنا',
|
||||
description: 'قناة تهتم بالثقافة العربية والإسلامية والعادات والتقاليد واللباس الإسلامي والتراث العربي الأصيل.',
|
||||
gradient: 'from-red-500 to-rose-400',
|
||||
emoji: '🕌',
|
||||
categories: ['ثقافة', 'تراث', 'حضارة', 'إسلام'],
|
||||
subscriberCount: 920000,
|
||||
videoCount: 167,
|
||||
},
|
||||
{
|
||||
id: 'ch-innovation',
|
||||
name: 'عالم الابتكار',
|
||||
description: 'قناة عن الابتكار والتصميم الحديث والاختراعات الثورية التي تغير العالم. نسلط الضوء على القمة في التصميم.',
|
||||
gradient: 'from-teal-500 to-emerald-400',
|
||||
emoji: '💡',
|
||||
categories: ['ابتكار', 'تصميم', 'اختراعات', 'تقنية'],
|
||||
subscriberCount: 670000,
|
||||
videoCount: 143,
|
||||
},
|
||||
{
|
||||
id: 'ch-politics',
|
||||
name: 'ساحة السياسة',
|
||||
description: 'قناة تحليلية سياسية تتناول القضايا السياسية العربية والإسلامية والصراعات في المنطقة بأسلوب موضوعي.',
|
||||
gradient: 'from-slate-600 to-gray-500',
|
||||
emoji: '⚖️',
|
||||
categories: ['سياسة', 'تاريخ', 'حروب', 'قانون'],
|
||||
subscriberCount: 1800000,
|
||||
videoCount: 312,
|
||||
},
|
||||
{
|
||||
id: 'ch-wars',
|
||||
name: 'الملاحم والفتوحات',
|
||||
description: 'قناة مخصصة لتاريخ الحروب العربية والإسلامية والفتوحات والمعارك الكبرى والقادة العسكريين المسلمين.',
|
||||
gradient: 'from-zinc-700 to-stone-600',
|
||||
emoji: '⚔️',
|
||||
categories: ['حروب', 'تاريخ', 'عسكرية', 'استراتيجية', 'إسلام'],
|
||||
subscriberCount: 1500000,
|
||||
videoCount: 245,
|
||||
},
|
||||
];
|
||||
|
||||
// ── Playlists ─────────────────────────────────────────────
|
||||
function makeVideos(channelId: string, playlistId: string, titles: string[], durations: number[]): Video[] {
|
||||
return titles.map((title, i) => ({
|
||||
id: `v-${channelId}-${playlistId}-${i}`,
|
||||
playlistId,
|
||||
channelId,
|
||||
title,
|
||||
description: `${title} - شرح مفصل مع أمثلة وتطبيقات عملية.`,
|
||||
duration: `${Math.floor(durations[i] / 60)}:${String(durations[i] % 60).padStart(2, '0')}`,
|
||||
durationSeconds: durations[i],
|
||||
viewCount: Math.floor(Math.random() * 500000) + 10000,
|
||||
publishedAt: `2024-${String(Math.floor(Math.random() * 12) + 1).padStart(2, '0')}-${String(Math.floor(Math.random() * 28) + 1).padStart(2, '0')}`,
|
||||
position: i,
|
||||
}));
|
||||
}
|
||||
|
||||
export const playlists: Playlist[] = [
|
||||
// Tech Channel
|
||||
{
|
||||
id: 'pl-tech-1',
|
||||
channelId: 'ch-tech',
|
||||
title: 'أساسيات البرمجة بلغة Python',
|
||||
description: 'دورة شاملة لتعلم أساسيات البرمجة بلغة بايثون من الصفر حتى الاحتراف',
|
||||
videoCount: 12,
|
||||
gradient: 'from-blue-600 to-cyan-500',
|
||||
emoji: '🐍',
|
||||
categories: ['برمجة', 'تقنية', 'تعليم'],
|
||||
},
|
||||
{
|
||||
id: 'pl-tech-2',
|
||||
channelId: 'ch-tech',
|
||||
title: 'تطوير تطبيقات الويب الكاملة',
|
||||
description: 'دورة تطوير تطبيقات الويب من الواجهة إلى الخادم باستخدام React و Node.js',
|
||||
videoCount: 8,
|
||||
gradient: 'from-indigo-500 to-blue-400',
|
||||
emoji: '🌐',
|
||||
categories: ['برمجة', 'تقنية', 'تعليم'],
|
||||
},
|
||||
{
|
||||
id: 'pl-tech-3',
|
||||
channelId: 'ch-tech',
|
||||
title: 'الذكاء الاصطناعي للمبتدئين',
|
||||
description: 'مقدمة شاملة في الذكاء الاصطناعي وتعلم الآلة والشبكات العصبية',
|
||||
videoCount: 10,
|
||||
gradient: 'from-purple-500 to-pink-400',
|
||||
emoji: '🤖',
|
||||
categories: ['ذكاء اصطناعي', 'تقنية', 'تعليم'],
|
||||
},
|
||||
// History Channel
|
||||
{
|
||||
id: 'pl-hist-1',
|
||||
channelId: 'ch-history',
|
||||
title: 'فتوحات الإسلام الكبرى',
|
||||
description: 'سلسلة عن الفتوحات الإسلامية الكبرى من عهد النبوة إلى الدولة الأموية',
|
||||
videoCount: 15,
|
||||
gradient: 'from-amber-600 to-orange-500',
|
||||
emoji: '⚔️',
|
||||
categories: ['تاريخ', 'إسلام', 'حروب'],
|
||||
},
|
||||
{
|
||||
id: 'pl-hist-2',
|
||||
channelId: 'ch-history',
|
||||
title: 'حضارة الأندلس الإسلامية',
|
||||
description: 'رحلة في تاريخ الأندلس الإسلامية depuis الفتح حتى السقوط',
|
||||
videoCount: 8,
|
||||
gradient: 'from-yellow-500 to-amber-400',
|
||||
emoji: '🏛️',
|
||||
categories: ['تاريخ', 'حضارة', 'إسلام'],
|
||||
},
|
||||
{
|
||||
id: 'pl-hist-3',
|
||||
channelId: 'ch-history',
|
||||
title: 'عظماء المسلمين',
|
||||
description: 'سير وحكايات عظماء المسلمين الذين غيروا مجرى التاريخ',
|
||||
videoCount: 12,
|
||||
gradient: 'from-orange-600 to-red-400',
|
||||
emoji: '🌟',
|
||||
categories: ['تاريخ', 'إسلام', 'سيرة'],
|
||||
},
|
||||
// Inventor Channel
|
||||
{
|
||||
id: 'pl-inv-1',
|
||||
channelId: 'ch-invent',
|
||||
title: 'اختراعات غيرت العالم',
|
||||
description: 'سلسلة عن أعظم الاختراعات التي غيرت مسار البشرية',
|
||||
videoCount: 10,
|
||||
gradient: 'from-emerald-600 to-green-400',
|
||||
emoji: '🔧',
|
||||
categories: ['اختراعات', 'ابتكار', 'علوم'],
|
||||
},
|
||||
{
|
||||
id: 'pl-inv-2',
|
||||
channelId: 'ch-invent',
|
||||
title: 'اصنع روبوتك بنفسك',
|
||||
description: 'دورة عملية لبناء روبوت من الصفر باستخدام Arduino',
|
||||
videoCount: 8,
|
||||
gradient: 'from-teal-500 to-emerald-400',
|
||||
emoji: '🤖',
|
||||
categories: ['روبوتات', 'إلكترونيات', 'اختراعات'],
|
||||
},
|
||||
// Sharia Channel
|
||||
{
|
||||
id: 'pl-sharia-1',
|
||||
channelId: 'ch-sharia',
|
||||
title: 'فقه العبادات الميسّر',
|
||||
description: 'شرح ميسر لأحكام العبادات من الطهارة إلى الحج',
|
||||
videoCount: 20,
|
||||
gradient: 'from-purple-600 to-indigo-500',
|
||||
emoji: '🕌',
|
||||
categories: ['فقه', 'شريعة', 'دين'],
|
||||
},
|
||||
{
|
||||
id: 'pl-sharia-2',
|
||||
channelId: 'ch-sharia',
|
||||
title: 'أصول العقيدة الإسلامية',
|
||||
description: 'شرح أصول عقيدة أهل السنة والجماعة',
|
||||
videoCount: 15,
|
||||
gradient: 'from-indigo-600 to-violet-500',
|
||||
emoji: '💫',
|
||||
categories: ['عقيدة', 'شريعة', 'دين'],
|
||||
},
|
||||
// Language Channel
|
||||
{
|
||||
id: 'pl-lang-1',
|
||||
channelId: 'ch-lang',
|
||||
title: 'النحو الواضح المبسّط',
|
||||
description: 'دورة في النحو العربي مبسطة وميسرة للمبتدئين والمتقدمين',
|
||||
videoCount: 12,
|
||||
gradient: 'from-rose-500 to-pink-400',
|
||||
emoji: '📝',
|
||||
categories: ['لغة عربية', 'نحو', 'تعليم'],
|
||||
},
|
||||
{
|
||||
id: 'pl-lang-2',
|
||||
channelId: 'ch-lang',
|
||||
title: 'بلاغة القرآن الكريم',
|
||||
description: 'دراسة بلاغة القرآن والإعجاز اللغوي في آياته الكريمة',
|
||||
videoCount: 10,
|
||||
gradient: 'from-pink-500 to-rose-400',
|
||||
emoji: '📜',
|
||||
categories: ['بلاغة', 'لغة عربية', 'قرآن'],
|
||||
},
|
||||
// Business Channel
|
||||
{
|
||||
id: 'pl-biz-1',
|
||||
channelId: 'ch-biz',
|
||||
title: 'كيف تبني شركة ناشئة ناجحة',
|
||||
description: 'دليلك الشامل لبناء وإدارة شركة ناشئة من الفكرة إلى التنفيذ',
|
||||
videoCount: 8,
|
||||
gradient: 'from-orange-500 to-amber-400',
|
||||
emoji: '🏢',
|
||||
categories: ['ريادة', 'أعمال', 'إدارة'],
|
||||
},
|
||||
{
|
||||
id: 'pl-biz-2',
|
||||
channelId: 'ch-biz',
|
||||
title: 'قصص نجاح عربية ملهمة',
|
||||
description: 'قصص نجاح حقيقية لرواد أعمال ومبتكرين عرب غيروا الواقع',
|
||||
videoCount: 6,
|
||||
gradient: 'from-yellow-500 to-orange-400',
|
||||
emoji: '⭐',
|
||||
categories: ['ريادة', 'أعمال', 'قصص'],
|
||||
},
|
||||
// Design Channel
|
||||
{
|
||||
id: 'pl-design-1',
|
||||
channelId: 'ch-design',
|
||||
title: 'أساسيات التصميم الهندسي',
|
||||
description: 'مدخل إلى عالم التصميم الهندسي والمعماري',
|
||||
videoCount: 10,
|
||||
gradient: 'from-violet-500 to-purple-400',
|
||||
emoji: '✏️',
|
||||
categories: ['تصميم', 'هندسة', 'عمارة'],
|
||||
},
|
||||
{
|
||||
id: 'pl-design-2',
|
||||
channelId: 'ch-design',
|
||||
title: 'أعظم التصاميم المعمارية الإسلامية',
|
||||
description: 'جولة في أعظم المباني والتصاميم المعمارية الإسلامية عبر التاريخ',
|
||||
videoCount: 8,
|
||||
gradient: 'from-fuchsia-500 to-violet-400',
|
||||
emoji: '🕌',
|
||||
categories: ['عمارة', 'فن إسلامي', 'تصميم'],
|
||||
},
|
||||
// Projects Channel
|
||||
{
|
||||
id: 'pl-proj-1',
|
||||
channelId: 'ch-projects',
|
||||
title: 'مشاريع تقنية عربية رائدة',
|
||||
description: 'استعراض أبرز المشاريع التقنية العربية التي تنافس عالميًا',
|
||||
videoCount: 6,
|
||||
gradient: 'from-sky-500 to-blue-400',
|
||||
emoji: '💻',
|
||||
categories: ['مشاريع', 'تقنية', 'ابتكار'],
|
||||
},
|
||||
// Culture Channel
|
||||
{
|
||||
id: 'pl-cult-1',
|
||||
channelId: 'ch-culture',
|
||||
title: 'العادات والتقاليد العربية',
|
||||
description: 'رحلة في العادات والتقاليد العربية الأصيلة عبر البلدان العربية',
|
||||
videoCount: 8,
|
||||
gradient: 'from-red-500 to-rose-400',
|
||||
emoji: '🌍',
|
||||
categories: ['ثقافة', 'تراث', 'حضارة'],
|
||||
},
|
||||
{
|
||||
id: 'pl-cult-2',
|
||||
channelId: 'ch-culture',
|
||||
title: 'اللباس الإسلامي عبر التاريخ',
|
||||
description: 'تطور اللباس الإسلامي والعربي عبر العصور ودلالاته الثقافية',
|
||||
videoCount: 6,
|
||||
gradient: 'from-rose-500 to-red-400',
|
||||
emoji: '👔',
|
||||
categories: ['ثقافة', 'تراث', 'إسلام'],
|
||||
},
|
||||
// Innovation Channel
|
||||
{
|
||||
id: 'pl-innov-1',
|
||||
channelId: 'ch-innovation',
|
||||
title: 'الابتكار في العالم العربي',
|
||||
description: 'قصص الابتكار والإبداع في العالم العربي والمشاريع المتميزة',
|
||||
videoCount: 8,
|
||||
gradient: 'from-teal-500 to-cyan-400',
|
||||
emoji: '💡',
|
||||
categories: ['ابتكار', 'مشاريع', 'تقنية'],
|
||||
},
|
||||
// Politics Channel
|
||||
{
|
||||
id: 'pl-pol-1',
|
||||
channelId: 'ch-politics',
|
||||
title: 'القضية الفلسطينية: تاريخ ومستقبل',
|
||||
description: 'سلسلة تحليلية شاملة عن القضية الفلسطينية depuis بدايتها حتى اليوم',
|
||||
videoCount: 12,
|
||||
gradient: 'from-slate-600 to-gray-500',
|
||||
emoji: '🇵🇸',
|
||||
categories: ['سياسة', 'تاريخ', 'قانون'],
|
||||
},
|
||||
// Wars Channel
|
||||
{
|
||||
id: 'pl-wars-1',
|
||||
channelId: 'ch-wars',
|
||||
title: 'حروب العرب عبر التاريخ',
|
||||
description: 'سلسلة عن أبرز الحروب والمعارك التي خاضها العرب والمسلمون',
|
||||
videoCount: 15,
|
||||
gradient: 'from-zinc-700 to-stone-500',
|
||||
emoji: '⚔️',
|
||||
categories: ['حروب', 'تاريخ', 'عسكرية'],
|
||||
},
|
||||
{
|
||||
id: 'pl-wars-2',
|
||||
channelId: 'ch-wars',
|
||||
title: 'القادة العسكريون المسلمون',
|
||||
description: 'سير أعظم القادة العسكريين في التاريخ الإسلامي',
|
||||
videoCount: 10,
|
||||
gradient: 'from-stone-600 to-zinc-500',
|
||||
emoji: '🗡️',
|
||||
categories: ['حروب', 'تاريخ', 'سيرة', 'عسكرية'],
|
||||
},
|
||||
];
|
||||
|
||||
// ── Video Data ────────────────────────────────────────────
|
||||
const videoTitlesMap: Record<string, string[]> = {
|
||||
'pl-tech-1': [
|
||||
'مقدمة في لغة Python وتثبيت البيئة', 'المتغيرات وأنواع البيانات',
|
||||
'العمليات الحسابية والمنطقية', 'الشروط وجملة if-else',
|
||||
'الحلقات التكرارية for و while', 'القوائم (Lists) والعمليات عليها',
|
||||
'القواميس (Dictionaries) والتعامل معها', 'الدوال (Functions) والنطاق',
|
||||
'البرمجة الكائنية (OOP) - الجزء الأول', 'البرمجة الكائنية (OOP) - الجزء الثاني',
|
||||
'التعامل مع الملفات', 'مشروع تطبيقي: بناء برنامج إدارة مهام',
|
||||
],
|
||||
'pl-tech-2': [
|
||||
'مقدمة في تطوير الويب و HTML', 'تنسيق الصفحات بـ CSS',
|
||||
'أساسيات JavaScript', 'مقدمة في React.js',
|
||||
'إدارة الحالة في React', 'بناء واجهة برمجة التطبيقات (API)',
|
||||
'قواعد البيانات مع MongoDB', 'نشر التطبيق على الإنترنت',
|
||||
],
|
||||
'pl-tech-3': [
|
||||
'ما هو الذكاء الاصطناعي؟', 'أنواع التعلم الآلي',
|
||||
'الانحدار الخطي واللوجستي', 'أشجار القرار والغابات العشوائية',
|
||||
'الشبكات العصبية الاصطناعية', 'التعلم العميق (Deep Learning)',
|
||||
'معالجة اللغات الطبيعية (NLP)', 'الرؤية الحاسوبية (Computer Vision)',
|
||||
'التعلم المعزز', 'أخلاقيات الذكاء الاصطناعي',
|
||||
],
|
||||
'pl-hist-1': [
|
||||
'مقدمة في الفتوحات الإسلامية', 'غزوات النبي ﷺ',
|
||||
'فتح مكة المكرمة', 'الفتوحات في عهد أبي بكر الصديق',
|
||||
'فتح العراق وفارس', 'فتح الشام ومصر',
|
||||
'معركة اليرموك', 'معركة القادسية',
|
||||
'فتح فارس والأهواز', 'الفتوحات في عهد عثمان بن عفان',
|
||||
'الفتوحات في أفريقيا', 'فتح الأندلس',
|
||||
'معركة Tours وبلاط الشهداء', 'الفتوحات في عهد عبد الملك بن مروان',
|
||||
'ميراث الفتوحات الإسلامية',
|
||||
],
|
||||
'pl-hist-2': [
|
||||
'الفتح الإسلامي للأندلس', 'عهد الولاة في الأندلس',
|
||||
'تأسيس إمارة قرطبة', 'عصر الخلافة الأموية في الأندلس',
|
||||
'عصر ملوك الطوائف', 'دولة المرابطين والموحدون',
|
||||
'مملكة غرناطة آخر المعاقل', 'سقوط غرناطة ومأساة التطهير',
|
||||
],
|
||||
'pl-hist-3': [
|
||||
'خالد بن الوليد سيف الله', 'صلاح الدين الأيوبي',
|
||||
'طارق بن زياد فاتح الأندلس', 'عقبة بن نافع فاتح أفريقيا',
|
||||
'موسى بن نصير', 'الإمام البخاري',
|
||||
'ابن سينا شيخ الأطباء', 'ابن خلدون مؤسس علم الاجتماع',
|
||||
'طه حسين عميد الأدب العربي', 'الشافعي إمام الفقهاء',
|
||||
'ابن تيمية شيخ الإسلام', 'محمد الفاتح فاتح القسطنطينة',
|
||||
],
|
||||
'pl-inv-1': [
|
||||
'اختراع الطباعة وتأثيرها', 'المحرك البخاري والثورة الصناعية',
|
||||
'الكهرباء وتغير العالم', 'اختراع الهاتف والاتصالات',
|
||||
'الطائرة: تحقيق حلم الطيران', 'الكمبيوتر والثورة الرقمية',
|
||||
'الإنترنت: الشبكة التي غيرت العالم', 'اختراع الهاتف الذكي',
|
||||
'الطباعة ثلاثية الأبعاد', 'السيارة الكهربائية ومستقبل النقل',
|
||||
],
|
||||
'pl-inv-2': [
|
||||
'مقدمة في الروبوتات و Arduino', 'المكونات الإلكترونية الأساسية',
|
||||
'التحكم بالمحركات والحركة', 'المستشعرات وقراءة البيانات',
|
||||
'التحكم عن بعد بالبلوتوث', 'بناء هيكل الروبوت',
|
||||
'برمجة سلوك الروبوت', 'المشروع النهائي: روبوت متكامل',
|
||||
],
|
||||
'pl-sharia-1': [
|
||||
'مقدمة في علم الفقه وأهميته', 'المياه والطهارة',
|
||||
'الوضوء: أحكامه وسننه', 'التيمم والمسح على الخفين',
|
||||
'الصلاة: أركانها وشروطها', 'صلاة الجماعة وصلاة الجمعة',
|
||||
'صلاة المريض وقضاء الصلاة', 'الصيام: أحكامه وآدابه',
|
||||
'صيام التطوع والنوافل', 'الزكاة: شروطها ومصارفها',
|
||||
'زكاة الفطر والأموال', 'الحج والعمرة: المناسك',
|
||||
'أحكام الجهاد في الإسلام', 'الأيمان والنذور',
|
||||
'الأطعمة والأشربة في الإسلام', 'اللباس والزينة في الإسلام',
|
||||
'آداب الطعام والشراب', 'آداب النوم والاستيقاظ',
|
||||
'صلة الرحم وبر الوالدين', 'الأمر بالمعروف والنهي عن المنكر',
|
||||
],
|
||||
'pl-sharia-2': [
|
||||
'مقدمة في علم العقيدة', 'أركان الإيمان الستة',
|
||||
'الإيمان بالله تعالى', 'الإيمان بالملائكة',
|
||||
'الإيمان بالكتب السماوية', 'الإيمان بالرسل والأنبياء',
|
||||
'الإيمان باليوم الآخر', 'الإيمان بالقدر خيره وشره',
|
||||
'التوحيد وأقسامه', 'الشرك وأنواعه',
|
||||
'البدع والمحدثات في الدين', 'الصحابة ومنزلتهم',
|
||||
'أشراط الساعة الصغرى والكبرى', 'القضاء والقدر',
|
||||
'أسماء الله الحسنى وصفاته',
|
||||
],
|
||||
'pl-lang-1': [
|
||||
'مقدمة في علم النحو وأهميته', 'أقسام الكلام: اسم وفعل وحرف',
|
||||
'المعرب والمبني', 'الجملة الاسمية: المبتدأ والخبر',
|
||||
'الجملة الفعلية: الفاعل والمفعول به', 'المضاف والمضاف إليه',
|
||||
'النعت والتوكيد والعطف', 'الحال والتمييز',
|
||||
'المفعول لأجله والمفعول معه', 'الاستثناء',
|
||||
'النداء وأحكامه', 'إعراب الجمل',
|
||||
],
|
||||
'pl-lang-2': [
|
||||
'مقدمة في علم البلاغة', 'علم البيان والتشبيه',
|
||||
'الاستعارة والمجاز', 'علم المعاني والخبر والإنشاء',
|
||||
'التقديم والتأخير', 'القصر والاختصاص',
|
||||
'علم البديع: المحسنات اللفظية', 'علم البديع: المحسنات المعنوية',
|
||||
'الإعجاز البياني للقرآن', 'تحليل بلاغي لسور مختارة',
|
||||
],
|
||||
'pl-biz-1': [
|
||||
'من أين تبدأ فكرتك؟', 'دراسة الجدوى وتحليل السوق',
|
||||
'كتابة خطة العمل (Business Plan)', 'بناء فريق العمل',
|
||||
'التمويل وجمع رأس المال', 'التسويق الرقمي للمشروعات الناشئة',
|
||||
'إدارة المال والميزانية', 'توسيع المشروع والنمو',
|
||||
],
|
||||
'pl-biz-2': [
|
||||
'قصة نجاح: محمد العريفي', 'قصة نجاح: سوق.كم (Souq.com)',
|
||||
'قصة نجاح: كريم (Careem)', 'قصة نجاح: نون (Noon)',
|
||||
'قصة نجاح: طلبات (Talabat)', 'قصة نجاح: أنجزي (Anghami)',
|
||||
],
|
||||
'pl-design-1': [
|
||||
'مقدمة في التصميم الهندسي', 'أساسيات الرسم الهندسي',
|
||||
'الإسقاط والمساقط', 'التصميم ثلاثي الأبعاد',
|
||||
'استخدام برامج التصميم CAD', 'تصميم الأبنية السكنية',
|
||||
'التصميم المستدام والبيئي', 'تصميم المساجد والمراكز الإسلامية',
|
||||
'التصميم الداخلي وتنسيق المساحات', 'مشروع تطبيقي: تصميم فيلا متكاملة',
|
||||
],
|
||||
'pl-design-2': [
|
||||
'المسجد الحرام والمسجد النبوي', 'مسجد الصخرة في القدس',
|
||||
'جامع قرطبة الكبير', 'مسجد السلطان أحمد في إسطنبول',
|
||||
'تاج محل في الهند', 'قصر الحمراء في غرناطة',
|
||||
'المدرسة المستنصرية في بغداد', 'مسجد الشيخ زايد الكبير',
|
||||
],
|
||||
'pl-proj-1': [
|
||||
'تطبيق كريم: أول يونيكورن عربي', 'منصة بايت: ثورة في التعليم',
|
||||
'شركة أرامكو: عملاق الطاقة', 'مدينة NEOM: رؤية المستقبل',
|
||||
'برج المملكة: أيقونة الرياض', 'وادي السيليكون العربي',
|
||||
],
|
||||
'pl-cult-1': [
|
||||
'الضيافة العربية: كرم لا حدود له', 'القهوة العربية وثقافة تقديمها',
|
||||
'الأعياد والمناسبات العربية', 'الموسيقى والفنون العربية',
|
||||
'الخط العربي وفنونه', 'الأسواق الشعبية العربية',
|
||||
'الحرف اليدوية العربية', 'الطبخ العربي وتنوعه',
|
||||
],
|
||||
'pl-cult-2': [
|
||||
'اللباس في العصر الجاهلي', 'لباس الرسول ﷺ والصحابة',
|
||||
'اللباس في العصر الأموي والعباسي', 'اللباس في الأندلس',
|
||||
'اللباس في العصر العثماني', 'اللباس العربي المعاصر والحجاب',
|
||||
],
|
||||
'pl-innov-1': [
|
||||
'الابتكار في الإمارات', 'التقنية في السعودية: رؤية 2030',
|
||||
'الابتكار في مصر: وادي التكنولوجيا', 'الأردن: وادي السيليكون الجديد',
|
||||
'الابتكار في تونس والمغرب', 'المرأة العربية والابتكار',
|
||||
'الابتكار في التعليم العربي', 'مستقبل الابتكار العربي',
|
||||
],
|
||||
'pl-pol-1': [
|
||||
'جذور القضية الفلسطينية', 'وعد بلفور والانتداب البريطاني',
|
||||
'النكبة عام 1948', 'حرب 1967 وحرب الاستنزاف',
|
||||
'حرب أكتوبر 1973', 'اتفاقيات كامب ديفيد وأوسلو',
|
||||
'الانتفاضة الأولى والثانية', 'حصار غزة',
|
||||
'القدس ومكانتها', 'المقاومة الفلسطينية',
|
||||
'القضية في المحافل الدولية', 'مستقبل القضية الفلسطينية',
|
||||
],
|
||||
'pl-wars-1': [
|
||||
'حروب ما قبل الإسلام', 'غزوات الرسول ﷺ',
|
||||
'حروب الردة', 'الفتوحات الإسلامية الكبرى',
|
||||
'الحروب الصليبية', 'معركة حطين',
|
||||
'فتح القسطنطينية', 'معركة عين جالوت',
|
||||
'حرب الاستقلال الجزائرية', 'حرب 1948',
|
||||
'حرب 1967', 'حرب 1973',
|
||||
'الحرب اللبنانية الأهلية', 'الحرب العراقية الإيرانية',
|
||||
'حرب الخليج الثانية',
|
||||
],
|
||||
'pl-wars-2': [
|
||||
'خالد بن الوليد: سيف الله المسلول', 'صلاح الدين الأيوبي: محرر القدس',
|
||||
'طارق بن زياد: فاتح الأندلس', 'عقبة بن نافع: فاتح أفريقيا',
|
||||
'أسامة بن زيد: أصغر قائد', 'المعتصم بالله: بطل عمورية',
|
||||
'قطز: بطل عين جالوت', 'محمد الفاتح: فاتح القسطنطينية',
|
||||
'صلاح الدين الصفدي', 'أبو عبيدة عامر بن الجراح',
|
||||
],
|
||||
};
|
||||
|
||||
// Generate durations for each playlist
|
||||
function generateDurations(count: number): number[] {
|
||||
return Array.from({ length: count }, () => Math.floor(Math.random() * 30 + 8) * 60 + Math.floor(Math.random() * 60));
|
||||
}
|
||||
|
||||
export const videos: Record<string, Video[]> = {};
|
||||
for (const pl of playlists) {
|
||||
const titles = videoTitlesMap[pl.id];
|
||||
if (titles) {
|
||||
const durations = generateDurations(titles.length);
|
||||
videos[pl.id] = makeVideos(pl.channelId, pl.id, titles, durations);
|
||||
}
|
||||
}
|
||||
|
||||
// Standalone videos (not in playlists)
|
||||
export const standaloneVideos: Video[] = [
|
||||
{ id: 'sv-1', playlistId: '', channelId: 'ch-tech', title: 'لماذا يجب أن تتعلم البرمجة في 2024؟', description: 'في هذا الفيديو نناقش أهمية تعلم البرمجة وأفضل اللغات للبدء', duration: '18:30', durationSeconds: 1110, viewCount: 342000, publishedAt: '2024-03-15', position: 0 },
|
||||
{ id: 'sv-2', playlistId: '', channelId: 'ch-tech', title: 'أفضل 10 محررات أكواد للمبرمجين', description: 'مقارنة بين أفضل محررات الأكواد ومميزات كل منها', duration: '22:15', durationSeconds: 1335, viewCount: 198000, publishedAt: '2024-03-20', position: 0 },
|
||||
{ id: 'sv-3', playlistId: '', channelId: 'ch-history', title: 'كيف بنى المسلمون حضارة عظيمة؟', description: 'نظرة شاملة على عوامل قيام الحضارة الإسلامية', duration: '35:45', durationSeconds: 2145, viewCount: 567000, publishedAt: '2024-02-10', position: 0 },
|
||||
{ id: 'sv-4', playlistId: '', channelId: 'ch-history', title: 'أعظم المعارك في التاريخ الإسلامي', description: 'استعراض لأعظم المعارك التي غيرت مجرى التاريخ', duration: '42:00', durationSeconds: 2520, viewCount: 421000, publishedAt: '2024-01-25', position: 0 },
|
||||
{ id: 'sv-5', playlistId: '', channelId: 'ch-sharia', title: 'أهمية طلب العلم الشرعي', description: 'فضيلة طلب العلم الشرعي وأهميته في حياة المسلم', duration: '25:10', durationSeconds: 1510, viewCount: 890000, publishedAt: '2024-04-01', position: 0 },
|
||||
{ id: 'sv-6', playlistId: '', channelId: 'ch-invent', title: 'اختراع عربي ي revolutionize عالم الطاقة', description: 'تعرف على اختراع عربي جديد في مجال الطاقة المتجددة', duration: '15:45', durationSeconds: 945, viewCount: 234000, publishedAt: '2024-03-28', position: 0 },
|
||||
{ id: 'sv-7', playlistId: '', channelId: 'ch-lang', title: 'جمال اللغة العربية: 10 كلمات لا مثيل لها', description: 'كلمات عربية فريدة لا توجد في أي لغة أخرى', duration: '12:30', durationSeconds: 750, viewCount: 456000, publishedAt: '2024-03-10', position: 0 },
|
||||
{ id: 'sv-8', playlistId: '', channelId: 'ch-biz', title: '5 مشاريع ناشئة عربية حققت نجاحًا عالميًا', description: 'قصص نجاح لمشاريع عربية وصلت للعالمية', duration: '28:20', durationSeconds: 1700, viewCount: 312000, publishedAt: '2024-02-20', position: 0 },
|
||||
{ id: 'sv-9', playlistId: '', channelId: 'ch-design', title: 'التصميم المستدام: مستقبل العمارة', description: 'كيف يغير التصميم المستدام وجه العمارة العالمية', duration: '19:55', durationSeconds: 1195, viewCount: 167000, publishedAt: '2024-03-05', position: 0 },
|
||||
{ id: 'sv-10', playlistId: '', channelId: 'ch-culture', title: 'الخط العربي: فن وتاريخ', description: 'تاريخ فن الخط العربي وأشهر الخطاطين', duration: '32:10', durationSeconds: 1930, viewCount: 278000, publishedAt: '2024-01-30', position: 0 },
|
||||
{ id: 'sv-11', playlistId: '', channelId: 'ch-politics', title: 'النظام السياسي العربي: تحليل شامل', description: 'تحليل معمق للأنظمة السياسية في الدول العربية', duration: '45:30', durationSeconds: 2730, viewCount: 534000, publishedAt: '2024-02-15', position: 0 },
|
||||
{ id: 'sv-12', playlistId: '', channelId: 'ch-wars', title: 'أعظم 10 قادة عسكريين في التاريخ', description: 'ترتيب أعظم القادة العسكريين عبر التاريخ', duration: '38:20', durationSeconds: 2300, viewCount: 678000, publishedAt: '2024-03-22', position: 0 },
|
||||
];
|
||||
|
||||
// ── Default Folders ───────────────────────────────────────
|
||||
export const defaultFolders: Folder[] = [
|
||||
{
|
||||
id: 'folder-1',
|
||||
name: 'البرمجة والتقنية',
|
||||
color: 'blue',
|
||||
emoji: '💻',
|
||||
channelIds: ['ch-tech', 'ch-invent', 'ch-innovation'],
|
||||
},
|
||||
{
|
||||
id: 'folder-2',
|
||||
name: 'العلوم الشرعية',
|
||||
color: 'purple',
|
||||
emoji: '📖',
|
||||
channelIds: ['ch-sharia', 'ch-culture'],
|
||||
},
|
||||
{
|
||||
id: 'folder-3',
|
||||
name: 'التاريخ والحروب',
|
||||
color: 'amber',
|
||||
emoji: '⚔️',
|
||||
channelIds: ['ch-history', 'ch-wars'],
|
||||
},
|
||||
{
|
||||
id: 'folder-4',
|
||||
name: 'اللغة والأدب',
|
||||
color: 'rose',
|
||||
emoji: '✨',
|
||||
channelIds: ['ch-lang'],
|
||||
},
|
||||
{
|
||||
id: 'folder-5',
|
||||
name: 'ريادة الأعمال',
|
||||
color: 'orange',
|
||||
emoji: '🚀',
|
||||
channelIds: ['ch-biz', 'ch-projects'],
|
||||
},
|
||||
];
|
||||
|
||||
// ── Helper functions ──────────────────────────────────────
|
||||
export function getChannel(id: string): Channel | undefined {
|
||||
return channels.find(c => c.id === id);
|
||||
}
|
||||
|
||||
export function getPlaylist(id: string): Playlist | undefined {
|
||||
return playlists.find(p => p.id === id);
|
||||
}
|
||||
|
||||
export function getChannelPlaylists(channelId: string): Playlist[] {
|
||||
return playlists.filter(p => p.channelId === channelId);
|
||||
}
|
||||
|
||||
export function getPlaylistVideos(playlistId: string): Video[] {
|
||||
return videos[playlistId] || [];
|
||||
}
|
||||
|
||||
export function getChannelVideos(channelId: string): Video[] {
|
||||
const allVideos: Video[] = [];
|
||||
for (const pl of playlists.filter(p => p.channelId === channelId)) {
|
||||
allVideos.push(...(videos[pl.id] || []));
|
||||
}
|
||||
allVideos.push(...standaloneVideos.filter(v => v.channelId === channelId));
|
||||
return allVideos;
|
||||
}
|
||||
|
||||
export function formatNumber(num: number): string {
|
||||
if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M';
|
||||
if (num >= 1000) return (num / 1000).toFixed(0) + 'K';
|
||||
return num.toString();
|
||||
}
|
||||
|
||||
export function getCategoryColor(cat: string): string {
|
||||
const map: Record<string, string> = {
|
||||
'تعليم': 'bg-blue-100 text-blue-700',
|
||||
'تقنية': 'bg-cyan-100 text-cyan-700',
|
||||
'برمجة': 'bg-indigo-100 text-indigo-700',
|
||||
'تاريخ': 'bg-amber-100 text-amber-700',
|
||||
'إسلام': 'bg-green-100 text-green-700',
|
||||
'حروب': 'bg-red-100 text-red-700',
|
||||
'اختراعات': 'bg-emerald-100 text-emerald-700',
|
||||
'دين': 'bg-purple-100 text-purple-700',
|
||||
'شريعة': 'bg-violet-100 text-violet-700',
|
||||
'لغة عربية': 'bg-pink-100 text-pink-700',
|
||||
'ريادة': 'bg-orange-100 text-orange-700',
|
||||
'تصميم': 'bg-fuchsia-100 text-fuchsia-700',
|
||||
'ثقافة': 'bg-rose-100 text-rose-700',
|
||||
'سياسة': 'bg-slate-100 text-slate-700',
|
||||
'ابتكار': 'bg-teal-100 text-teal-700',
|
||||
'مشاريع': 'bg-sky-100 text-sky-700',
|
||||
};
|
||||
return map[cat] || 'bg-gray-100 text-gray-700';
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
@theme {
|
||||
--font-family-tajawal: 'Tajawal', sans-serif;
|
||||
--color-primary: #FF6B35;
|
||||
--color-primary-dark: #E55A2B;
|
||||
--color-secondary: #7C3AED;
|
||||
--color-accent: #10B981;
|
||||
--color-accent-gold: #F59E0B;
|
||||
--color-surface: #FFF8F0;
|
||||
--color-surface-card: #FFFFFF;
|
||||
--color-surface-dark: #1E1B2E;
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: 'Tajawal', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #FFF8F0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% { background-position: -200% 0; }
|
||||
100% { background-position: 200% 0; }
|
||||
}
|
||||
|
||||
@keyframes pulse-glow {
|
||||
0%, 100% { box-shadow: 0 0 5px rgba(255, 107, 53, 0.3); }
|
||||
50% { box-shadow: 0 0 20px rgba(255, 107, 53, 0.6); }
|
||||
}
|
||||
|
||||
@keyframes slide-up {
|
||||
from { opacity: 0; transform: translateY(20px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes slide-in-right {
|
||||
from { opacity: 0; transform: translateX(-30px); }
|
||||
to { opacity: 1; transform: translateX(0); }
|
||||
}
|
||||
|
||||
@keyframes progress-fill {
|
||||
from { width: 0%; }
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-5px); }
|
||||
}
|
||||
|
||||
@keyframes gradient-shift {
|
||||
0% { background-position: 0% 50%; }
|
||||
50% { background-position: 100% 50%; }
|
||||
100% { background-position: 0% 50%; }
|
||||
}
|
||||
|
||||
.animate-shimmer {
|
||||
animation: shimmer 2s infinite;
|
||||
background-size: 200% 100%;
|
||||
}
|
||||
|
||||
.animate-pulse-glow {
|
||||
animation: pulse-glow 2s infinite;
|
||||
}
|
||||
|
||||
.animate-slide-up {
|
||||
animation: slide-up 0.4s ease-out;
|
||||
}
|
||||
|
||||
.animate-slide-in {
|
||||
animation: slide-in-right 0.3s ease-out;
|
||||
}
|
||||
|
||||
.animate-progress {
|
||||
animation: progress-fill 1s ease-out;
|
||||
}
|
||||
|
||||
.animate-float {
|
||||
animation: float 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.gradient-animated {
|
||||
background-size: 200% 200%;
|
||||
animation: gradient-shift 3s ease infinite;
|
||||
}
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar-thumb {
|
||||
background: rgba(124, 58, 237, 0.2);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(124, 58, 237, 0.4);
|
||||
}
|
||||
|
||||
.video-card:hover .video-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.progress-ring {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.playlist-gradient {
|
||||
background: linear-gradient(135deg, #FF6B35 0%, #7C3AED 50%, #10B981 100%);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { StrictMode } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
|
||||
createRoot(document.getElementById("root")!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>
|
||||
);
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
import React, { createContext, useContext, useState, useCallback, useEffect } from 'react';
|
||||
import { ViewType, Folder, UserProgress } from './types';
|
||||
import { defaultFolders } from './data';
|
||||
|
||||
interface AppState {
|
||||
currentView: ViewType;
|
||||
followedChannelIds: string[];
|
||||
folders: Folder[];
|
||||
progress: UserProgress;
|
||||
sidebarOpen: boolean;
|
||||
searchQuery: string;
|
||||
}
|
||||
|
||||
interface AppContextType extends AppState {
|
||||
setView: (view: ViewType) => void;
|
||||
followChannel: (id: string) => void;
|
||||
unfollowChannel: (id: string) => void;
|
||||
toggleSidebar: () => void;
|
||||
setSearchQuery: (q: string) => void;
|
||||
markVideoWatched: (videoId: string) => void;
|
||||
updatePlaylistProgress: (playlistId: string, videoIndex: number) => void;
|
||||
completePlaylist: (playlistId: string) => void;
|
||||
createFolder: (name: string, color: string, emoji: string) => void;
|
||||
deleteFolder: (id: string) => void;
|
||||
addChannelToFolder: (folderId: string, channelId: string) => void;
|
||||
removeChannelFromFolder: (folderId: string, channelId: string) => void;
|
||||
updateFolder: (folderId: string, updates: Partial<Folder>) => void;
|
||||
}
|
||||
|
||||
const AppContext = createContext<AppContextType | null>(null);
|
||||
|
||||
export function useApp() {
|
||||
const ctx = useContext(AppContext);
|
||||
if (!ctx) throw new Error('useApp must be used within AppProvider');
|
||||
return ctx;
|
||||
}
|
||||
|
||||
const STORAGE_KEY = 'masari-state';
|
||||
|
||||
function loadState(): Partial<AppState> {
|
||||
try {
|
||||
const saved = localStorage.getItem(STORAGE_KEY);
|
||||
if (saved) return JSON.parse(saved);
|
||||
} catch {}
|
||||
return {};
|
||||
}
|
||||
|
||||
function saveState(state: AppState) {
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify({
|
||||
followedChannelIds: state.followedChannelIds,
|
||||
folders: state.folders,
|
||||
progress: state.progress,
|
||||
}));
|
||||
} catch {}
|
||||
}
|
||||
|
||||
export function AppProvider({ children }: { children: React.ReactNode }) {
|
||||
const saved = loadState();
|
||||
|
||||
const [currentView, setCurrentView] = useState<ViewType>({ type: 'dashboard' });
|
||||
const [followedChannelIds, setFollowedChannelIds] = useState<string[]>(
|
||||
saved.followedChannelIds || ['ch-tech', 'ch-history', 'ch-sharia', 'ch-lang', 'ch-biz']
|
||||
);
|
||||
const [folders, setFolders] = useState<Folder[]>(
|
||||
saved.folders || defaultFolders
|
||||
);
|
||||
const [progress, setProgress] = useState<UserProgress>(
|
||||
saved.progress || {
|
||||
watchedVideos: [],
|
||||
playlistProgress: {},
|
||||
completedPlaylists: [],
|
||||
streak: 3,
|
||||
lastWatchDate: new Date().toISOString().split('T')[0],
|
||||
totalWatched: 0,
|
||||
}
|
||||
);
|
||||
const [sidebarOpen, setSidebarOpen] = useState(true);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
// Save to localStorage on changes
|
||||
useEffect(() => {
|
||||
saveState({ followedChannelIds, folders, progress } as AppState);
|
||||
}, [followedChannelIds, folders, progress]);
|
||||
|
||||
const setView = useCallback((view: ViewType) => {
|
||||
setCurrentView(view);
|
||||
}, []);
|
||||
|
||||
const followChannel = useCallback((id: string) => {
|
||||
setFollowedChannelIds(prev => prev.includes(id) ? prev : [...prev, id]);
|
||||
}, []);
|
||||
|
||||
const unfollowChannel = useCallback((id: string) => {
|
||||
setFollowedChannelIds(prev => prev.filter(c => c !== id));
|
||||
}, []);
|
||||
|
||||
const toggleSidebar = useCallback(() => {
|
||||
setSidebarOpen(prev => !prev);
|
||||
}, []);
|
||||
|
||||
const markVideoWatched = useCallback((videoId: string) => {
|
||||
setProgress(prev => ({
|
||||
...prev,
|
||||
watchedVideos: prev.watchedVideos.includes(videoId)
|
||||
? prev.watchedVideos
|
||||
: [...prev.watchedVideos, videoId],
|
||||
totalWatched: prev.totalWatched + 1,
|
||||
lastWatchDate: new Date().toISOString().split('T')[0],
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const updatePlaylistProgress = useCallback((playlistId: string, videoIndex: number) => {
|
||||
setProgress(prev => ({
|
||||
...prev,
|
||||
playlistProgress: {
|
||||
...prev.playlistProgress,
|
||||
[playlistId]: Math.max(prev.playlistProgress[playlistId] || 0, videoIndex),
|
||||
},
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const completePlaylist = useCallback((playlistId: string) => {
|
||||
setProgress(prev => ({
|
||||
...prev,
|
||||
completedPlaylists: prev.completedPlaylists.includes(playlistId)
|
||||
? prev.completedPlaylists
|
||||
: [...prev.completedPlaylists, playlistId],
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const createFolder = useCallback((name: string, color: string, emoji: string) => {
|
||||
const newFolder: Folder = {
|
||||
id: `folder-${Date.now()}`,
|
||||
name,
|
||||
color,
|
||||
emoji,
|
||||
channelIds: [],
|
||||
};
|
||||
setFolders(prev => [...prev, newFolder]);
|
||||
}, []);
|
||||
|
||||
const deleteFolder = useCallback((id: string) => {
|
||||
setFolders(prev => prev.filter(f => f.id !== id));
|
||||
}, []);
|
||||
|
||||
const addChannelToFolder = useCallback((folderId: string, channelId: string) => {
|
||||
setFolders(prev => prev.map(f =>
|
||||
f.id === folderId && !f.channelIds.includes(channelId)
|
||||
? { ...f, channelIds: [...f.channelIds, channelId] }
|
||||
: f
|
||||
));
|
||||
}, []);
|
||||
|
||||
const removeChannelFromFolder = useCallback((folderId: string, channelId: string) => {
|
||||
setFolders(prev => prev.map(f =>
|
||||
f.id === folderId
|
||||
? { ...f, channelIds: f.channelIds.filter(c => c !== channelId) }
|
||||
: f
|
||||
));
|
||||
}, []);
|
||||
|
||||
const updateFolder = useCallback((folderId: string, updates: Partial<Folder>) => {
|
||||
setFolders(prev => prev.map(f =>
|
||||
f.id === folderId ? { ...f, ...updates } : f
|
||||
));
|
||||
}, []);
|
||||
|
||||
const value: AppContextType = {
|
||||
currentView,
|
||||
followedChannelIds,
|
||||
folders,
|
||||
progress,
|
||||
sidebarOpen,
|
||||
searchQuery,
|
||||
setView,
|
||||
followChannel,
|
||||
unfollowChannel,
|
||||
toggleSidebar,
|
||||
setSearchQuery,
|
||||
markVideoWatched,
|
||||
updatePlaylistProgress,
|
||||
completePlaylist,
|
||||
createFolder,
|
||||
deleteFolder,
|
||||
addChannelToFolder,
|
||||
removeChannelFromFolder,
|
||||
updateFolder,
|
||||
};
|
||||
|
||||
return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
|
||||
}
|
||||
Reference in New Issue
Block a user