diff --git a/web/src/App.tsx b/web/src/App.tsx index 443ba26e..c9c6ae30 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -72,6 +72,9 @@ const PollsPage = lazy(() => import('@/pages/governance/PollsPage')); const WhatsKURDPage = lazy(() => import('@/pages/social/WhatsKURDPage')); const KurdMediaPage = lazy(() => import('@/pages/social/KurdMediaPage')); const HelpPage = lazy(() => import('@/pages/HelpPage')); +const UniversityPage = lazy(() => import('@/pages/education/UniversityPage')); +const CertificatesPage = lazy(() => import('@/pages/education/CertificatesPage')); +const ResearchPage = lazy(() => import('@/pages/education/ResearchPage')); // Network pages const Mainnet = lazy(() => import('@/pages/networks/Mainnet')); @@ -242,6 +245,9 @@ function App() { } /> } /> } /> + } /> + } /> + } /> } /> } /> } /> diff --git a/web/src/components/MobileHomeLayout.tsx b/web/src/components/MobileHomeLayout.tsx index 944c8d66..ca730dfc 100644 --- a/web/src/components/MobileHomeLayout.tsx +++ b/web/src/components/MobileHomeLayout.tsx @@ -96,10 +96,10 @@ const APP_SECTIONS: AppSection[] = [ emoji: '📚', borderColor: 'border-l-yellow-500', apps: [ - { title: 'mobile.app.university', icon: '🎓', route: '/education', comingSoon: true }, + { title: 'mobile.app.university', icon: '🎓', route: '/education/university' }, { title: 'mobile.app.perwerde', icon: '📖', route: '/education', requiresAuth: true }, - { title: 'mobile.app.certificates', icon: '🏆', route: '/education', comingSoon: true }, - { title: 'mobile.app.research', icon: '🔬', route: '/education', comingSoon: true }, + { title: 'mobile.app.certificates', icon: '🏆', route: '/education/certificates' }, + { title: 'mobile.app.research', icon: '🔬', route: '/education/research' }, ], }, ]; diff --git a/web/src/i18n/locales/en.ts b/web/src/i18n/locales/en.ts index d326dc24..3759f959 100644 --- a/web/src/i18n/locales/en.ts +++ b/web/src/i18n/locales/en.ts @@ -3990,4 +3990,53 @@ export default { 'help.feature.community': 'Community Contact', 'help.whatskurd.title': 'WhatsKURD Messaging', 'help.whatskurd.desc': 'Contact us via the blockchain messaging system', + + // University page + 'university.title': 'Zanîngeha Dijîtal', + 'university.subtitle': 'Kurdistan Digital University', + 'university.breadcrumb': 'Education', + 'university.stats.courses': 'Kurs / Courses', + 'university.stats.students': 'Xwendekar / Students', + 'university.reward': '🎁 Xelat / Reward:', + 'university.enroll': 'Tomar bibe / Enroll', + + // Certificates / Perwerde page + 'certificates.title': 'Perwerde', + 'certificates.subtitle': 'Digital Education Platform', + 'certificates.breadcrumb': 'Education', + 'certificates.tab.courses': 'Kurs', + 'certificates.tab.enrolled': 'Tev li', + 'certificates.tab.completed': 'Qediya', + 'certificates.stats.points': 'Puan / Points', + 'certificates.stats.done': 'Qediyayî / Done', + 'certificates.stats.active': 'Aktîv / Active', + 'certificates.noWallet.title': 'Perwerde', + 'certificates.noWallet.desc': 'Ji kerema xwe wallet ve girêbidin da ku bikaribin kursan bibînin û tev li wan bibin.', + 'certificates.noWallet.en': 'Please connect your wallet to view and enroll in courses.', + 'certificates.loading': 'Tê barkirin... / Loading...', + 'certificates.empty.courses': 'Kursek tune / No courses available', + 'certificates.empty.enrolled': 'Tu tev li kursekê nebûyî / Not enrolled in any course', + 'certificates.empty.completed': 'Kursek neqediyaye / No completed courses', + 'certificates.course.status': 'Rewş / Status', + 'certificates.course.enrollment': 'Têketin / Enrolled', + 'certificates.course.points': 'Puan / Points', + 'certificates.course.openContent': 'Naveroka Kursê Veke / Open Course Content', + 'certificates.enroll': 'Tev li Kursê / Enroll', + 'certificates.completedBanner': 'Te ev kurs qedand! / You completed this course!', + 'certificates.enrolledBanner': 'Tu tev li vê kursê yî / You are enrolled', + + // Research page + 'research.title': 'Navenda Lêkolînê', + 'research.subtitle': 'Research Center', + 'research.breadcrumb': 'Education', + 'research.paperCount': '{{count}} lêkolîn / papers', + 'research.about.title': 'Der barê Navenda Lêkolînê / About', + 'research.about.ku': 'Navenda Lêkolînê gotarên zanistî yên li ser aboriya dijîtal a Kurdistanê, teknolojiya blockchain, û mijarên pêwendîdar berhev dike.', + 'research.about.en': "The Research Center curates academic papers on Kurdistan's digital economy, blockchain technology, and related topics.", + 'research.papers.title': 'Lêkolîn / Papers', + 'research.more': 'Bêtir / More', + 'research.less': 'Kêmtir / Less', + 'research.submit.title': 'Lêkolîna xwe bişînin / Submit your research', + 'research.submit.desc': 'Hûn dikarin gotarên xwe yên zanistî ji bo vekolînê bişînin. Submit your academic papers for peer review.', + 'research.submit.btn': 'Bişîne / Submit', } diff --git a/web/src/pages/education/CertificatesPage.tsx b/web/src/pages/education/CertificatesPage.tsx new file mode 100644 index 00000000..1d290c35 --- /dev/null +++ b/web/src/pages/education/CertificatesPage.tsx @@ -0,0 +1,341 @@ +import { useState, useEffect, useCallback } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; +import { usePezkuwi } from '@/contexts/PezkuwiContext'; + +interface Course { + id: number; + owner: string; + name: string; + description: string; + contentLink: string; + status: 'Active' | 'Archived'; +} + +interface Enrollment { + courseId: number; + enrolledAt: number; + completedAt: number | null; + pointsEarned: number; +} + +type Tab = 'courses' | 'enrolled' | 'completed'; + +interface CourseChainData { + id: number; owner: string; + name: number[] | string; description: number[] | string; + contentLink: number[] | string; + status: 'Active' | 'Archived'; +} +interface EnrollmentChainData { + student: string; courseId: number; enrolledAt: number; + completedAt: number | null; pointsEarned: number; +} + +function decodeText(data: number[] | string): string { + if (typeof data === 'string') return data; + try { return new TextDecoder().decode(new Uint8Array(data)); } catch { return ''; } +} + +export default function CertificatesPage() { + const navigate = useNavigate(); + const { t } = useTranslation(); + const { api, isApiReady, selectedAccount, getKeyPair } = usePezkuwi(); + + const [tab, setTab] = useState('courses'); + const [courses, setCourses] = useState([]); + const [enrollments, setEnrollments] = useState([]); + const [score, setScore] = useState(0); + const [loading, setLoading] = useState(false); + const [enrolling, setEnrolling] = useState(null); + const [expandedCourse, setExpandedCourse] = useState(null); + const [error, setError] = useState(null); + + const fetchCourses = useCallback(async () => { + if (!api || !isApiReady) return; + try { + const entries = await api.query.perwerde.courses.entries(); + const list: Course[] = []; + for (const [, value] of entries) { + if (!value.isEmpty) { + const d = value.toJSON() as unknown as CourseChainData; + list.push({ id: d.id, owner: d.owner, name: decodeText(d.name), description: decodeText(d.description), contentLink: decodeText(d.contentLink), status: d.status }); + } + } + list.sort((a, b) => b.id - a.id); + setCourses(list); + } catch (e) { console.error('fetchCourses', e); } + }, [api, isApiReady]); + + const fetchEnrollments = useCallback(async () => { + if (!api || !isApiReady || !selectedAccount) return; + try { + const ids = (await api.query.perwerde.studentCourses(selectedAccount.address)).toJSON() as number[]; + const list: Enrollment[] = []; + let pts = 0; + for (const cid of ids) { + const e = await api.query.perwerde.enrollments([selectedAccount.address, cid]); + if (!e.isEmpty) { + const d = e.toJSON() as unknown as EnrollmentChainData; + list.push({ courseId: d.courseId, enrolledAt: d.enrolledAt, completedAt: d.completedAt, pointsEarned: d.pointsEarned }); + if (d.completedAt) pts += d.pointsEarned; + } + } + setEnrollments(list); + setScore(pts); + } catch (e) { console.error('fetchEnrollments', e); } + }, [api, isApiReady, selectedAccount]); + + const load = useCallback(async () => { + setLoading(true); + await Promise.all([fetchCourses(), fetchEnrollments()]); + setLoading(false); + }, [fetchCourses, fetchEnrollments]); + + useEffect(() => { + if (isApiReady) load(); + }, [isApiReady, selectedAccount, load]); + + const isEnrolled = (id: number) => enrollments.some(e => e.courseId === id); + const isCompleted = (id: number) => enrollments.find(e => e.courseId === id)?.completedAt != null; + const getEnrollment = (id: number) => enrollments.find(e => e.courseId === id); + + const handleEnroll = async (courseId: number) => { + if (!api || !selectedAccount) return; + setEnrolling(courseId); + setError(null); + try { + const kp = await getKeyPair(selectedAccount.address); + if (!kp) throw new Error('Could not get signer'); + await new Promise((resolve, reject) => { + api.tx.perwerde.enroll(courseId).signAndSend(kp, ({ status, dispatchError }) => { + if (dispatchError) { + const msg = dispatchError.isModule + ? (() => { const d = api.registry.findMetaError(dispatchError.asModule); return `${d.section}.${d.name}`; })() + : dispatchError.toString(); + reject(new Error(msg)); return; + } + if (status.isInBlock || status.isFinalized) resolve(); + }); + }); + setExpandedCourse(null); + await load(); + } catch (e) { + setError(e instanceof Error ? e.message : 'Unknown error'); + } finally { + setEnrolling(null); + } + }; + + const filteredCourses = courses.filter(c => { + if (tab === 'courses') return c.status === 'Active'; + if (tab === 'enrolled') return isEnrolled(c.id) && !isCompleted(c.id); + return isCompleted(c.id); + }); + + const enrolledCount = enrollments.filter(e => !e.completedAt).length; + const completedCount = enrollments.filter(e => e.completedAt != null).length; + + const TABS: { key: Tab; label: string; count: number }[] = [ + { key: 'courses', label: t('certificates.tab.courses', 'Kurs'), count: courses.filter(c => c.status === 'Active').length }, + { key: 'enrolled', label: t('certificates.tab.enrolled', 'Tev li'), count: enrolledCount }, + { key: 'completed', label: t('certificates.tab.completed', 'Qediya'), count: completedCount }, + ]; + + return ( +
+ {/* Header */} +
+
+ + {t('certificates.breadcrumb', 'Education')} +
+
+ 🏆 +

{t('certificates.title', 'Perwerde')}

+

{t('certificates.subtitle', 'Digital Education Platform')}

+
+ {/* Score bar */} + {selectedAccount && ( +
+
+

{score}

+

{t('certificates.stats.points', 'Puan / Points')}

+
+
+
+

{completedCount}

+

{t('certificates.stats.done', 'Qediyayî / Done')}

+
+
+
+

{enrolledCount}

+

{t('certificates.stats.active', 'Aktîv / Active')}

+
+
+ )} +
+ + {/* No wallet */} + {!selectedAccount && ( +
+ 📚 +

{t('certificates.noWallet.title', 'Perwerde')}

+

{t('certificates.noWallet.desc', 'Ji kerema xwe wallet ve girêbidin da ku bikaribin kursan bibînin û tev li wan bibin.')}

+

{t('certificates.noWallet.en', 'Please connect your wallet to view and enroll in courses.')}

+
+ )} + + {selectedAccount && ( + <> + {/* Tabs */} +
+ {TABS.map(tb => ( + + ))} +
+ + {/* Error */} + {error && ( +
+ ⚠️ {error} +
+ )} + + {/* Course list */} +
+ {loading && ( +
+
+

{t('certificates.loading', 'Tê barkirin... / Loading...')}

+
+ )} + + {!loading && filteredCourses.length === 0 && ( +
+

{tab === 'completed' ? '🎓' : tab === 'enrolled' ? '📋' : '📭'}

+

+ {tab === 'courses' ? t('certificates.empty.courses', 'Kursek tune / No courses available') + : tab === 'enrolled' ? t('certificates.empty.enrolled', 'Tu tev li kursekê nebûyî / Not enrolled in any course') + : t('certificates.empty.completed', 'Kursek neqediyaye / No completed courses')} +

+
+ )} + + {!loading && filteredCourses.map(course => { + const enrolled = isEnrolled(course.id); + const completed = isCompleted(course.id); + const enroll = getEnrollment(course.id); + const isExpanded = expandedCourse?.id === course.id; + + return ( +
+
setExpandedCourse(isExpanded ? null : course)} + > +
+ {completed ? '✅' : enrolled ? '📖' : '📚'} +
+
+

{course.name}

+

Kurs #{course.id}

+
+ {completed && enroll && ( + + +{enroll.pointsEarned} + + )} + {isExpanded ? '▲' : '▼'} +
+ + {isExpanded && ( +
+ {course.description && ( +

{course.description}

+ )} + +
+
+ {t('certificates.course.status', 'Rewş / Status')} + + {course.status === 'Active' ? 'Aktîv' : 'Arşîv'} + +
+ {enrolled && ( +
+ {t('certificates.course.enrollment', 'Têketin / Enrolled')} + + {completed ? 'Qediya ✅' : 'Aktîv 📖'} + +
+ )} + {completed && enroll && ( +
+ {t('certificates.course.points', 'Puan / Points')} + +{enroll.pointsEarned} +
+ )} +
+ + {course.contentLink && ( + + 📄 {t('certificates.course.openContent', 'Naveroka Kursê Veke / Open Course Content')} + + )} + + {!enrolled && course.status === 'Active' && ( + + )} + {completed && ( +
+ 🎓 {t('certificates.completedBanner', 'Te ev kurs qedand! / You completed this course!')} +
+ )} + {enrolled && !completed && ( +
+ 📖 {t('certificates.enrolledBanner', 'Tu tev li vê kursê yî / You are enrolled')} +
+ )} +
+ )} +
+ ); + })} +
+ + )} + +
+
+ ); +} diff --git a/web/src/pages/education/ResearchPage.tsx b/web/src/pages/education/ResearchPage.tsx new file mode 100644 index 00000000..999014c6 --- /dev/null +++ b/web/src/pages/education/ResearchPage.tsx @@ -0,0 +1,170 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; + +interface Paper { + id: string; + emoji: string; + titleKu: string; + title: string; + authors: string; + abstract: string; + category: string; + date: string; + citations: number; + status: 'published' | 'peer-review' | 'draft'; +} + +const PAPERS: Paper[] = [ + { + id: '1', emoji: '🔗', + titleKu: 'Blockchain ji bo Aboriya Dijîtal a Kurdistanê', + title: 'Blockchain for Kurdistan Digital Economy', + authors: 'Dr. A. Kurdo, M. Ehmed', + abstract: 'This paper explores the potential of blockchain technology in building a decentralized digital economy for Kurdistan. We propose the Pezkuwi consensus mechanism and analyze its throughput, security, and decentralization trade-offs.', + category: 'Blockchain', date: '2026-02-15', citations: 42, status: 'published', + }, + { + id: '2', emoji: '💱', + titleKu: 'Tokenomiya HEZ: Modela Aborî ya Nenavendî', + title: 'HEZ Tokenomics: A Decentralized Economic Model', + authors: 'Prof. R. Xan, S. Demirtash', + abstract: 'An analysis of the HEZ token economic model including supply dynamics, staking incentives, governance utility, and long-term sustainability. We model inflation, deflation, and equilibrium scenarios.', + category: 'Economics', date: '2026-01-20', citations: 31, status: 'published', + }, + { + id: '3', emoji: '🏘️', + titleKu: 'Bereketli: Aboriya Taxê ya Dijîtal', + title: 'Bereketli: Digital Neighborhood Economy', + authors: 'K. Zana, B. Shêx', + abstract: 'We present Bereketli, a peer-to-peer neighborhood economy platform built on blockchain. The system enables local trade, micro-lending, and community resource sharing with minimal trust assumptions.', + category: 'DeFi / Social', date: '2025-11-30', citations: 18, status: 'published', + }, + { + id: '4', emoji: '🗣️', + titleKu: 'NLP ji bo Zimanê Kurdî: Rewş û Derfet', + title: 'NLP for Kurdish Language: Status and Opportunities', + authors: 'Dr. J. Bakir, D. Ehmed', + abstract: 'A comprehensive survey of Natural Language Processing research for the Kurdish language. We identify key gaps in tokenization, machine translation, and sentiment analysis, and propose a community-driven dataset initiative.', + category: 'AI / Language', date: '2026-03-05', citations: 8, status: 'peer-review', + }, + { + id: '5', emoji: '🔐', + titleKu: 'Nasnameya Dijîtal a Nenavendî li ser Pezkuwi', + title: 'Decentralized Digital Identity on Pezkuwi', + authors: 'M. Baran, A. Kurdo', + abstract: 'We design a self-sovereign identity (SSI) framework for the Pezkuwi network. Citizens control their own credentials using zero-knowledge proofs while maintaining compliance with governance requirements.', + category: 'Identity / Privacy', date: '2026-03-20', citations: 3, status: 'peer-review', + }, +]; + +const STATUS_META = { + 'published': { label: 'Weşandî / Published', color: '#16a34a', bg: '#16a34a22' }, + 'peer-review': { label: 'Peer Review', color: '#b45309', bg: '#f59e0b33' }, + 'draft': { label: 'Draft', color: '#6b7280', bg: '#6b728022' }, +}; + +export default function ResearchPage() { + const navigate = useNavigate(); + const { t } = useTranslation(); + const [expandedId, setExpandedId] = useState(null); + + return ( +
+ {/* Header */} +
+
+ + {t('research.breadcrumb', 'Education')} +
+
+ 🔬 +

{t('research.title', 'Navenda Lêkolînê')}

+

{t('research.subtitle', 'Research Center')}

+

+ {t('research.paperCount', '{{count}} lêkolîn / papers', { count: PAPERS.length })} +

+
+
+ +
+ + {/* About card */} +
+

{t('research.about.title', 'Der barê Navenda Lêkolînê / About')}

+

+ {t('research.about.ku', 'Navenda Lêkolînê gotarên zanistî yên li ser aboriya dijîtal a Kurdistanê, teknolojiya blockchain, û mijarên pêwendîdar berhev dike.')} +

+

+ {t('research.about.en', 'The Research Center curates academic papers on Kurdistan\'s digital economy, blockchain technology, and related topics.')} +

+
+ + {/* Papers */} +

{t('research.papers.title', 'Lêkolîn / Papers')}

+ + {PAPERS.map(paper => { + const meta = STATUS_META[paper.status]; + const isOpen = expandedId === paper.id; + + return ( + + ); + })} + + {/* Submit CTA */} +
+

+ {t('research.submit.title', 'Lêkolîna xwe bişînin / Submit your research')} +

+

+ {t('research.submit.desc', 'Hûn dikarin gotarên xwe yên zanistî ji bo vekolînê bişînin. Submit your academic papers for peer review.')} +

+ +
+ +
+
+
+ ); +} diff --git a/web/src/pages/education/UniversityPage.tsx b/web/src/pages/education/UniversityPage.tsx new file mode 100644 index 00000000..78923bb5 --- /dev/null +++ b/web/src/pages/education/UniversityPage.tsx @@ -0,0 +1,165 @@ +import { useNavigate } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; + +interface Course { + id: string; + emoji: string; + titleKu: string; + title: string; + instructor: string; + duration: string; + level: 'Destpêk' | 'Navîn' | 'Pêşketî'; + levelEn: 'Beginner' | 'Intermediate' | 'Advanced'; + enrolled: number; + lessons: number; + description: string; + reward: string; +} + +const COURSES: Course[] = [ + { + id: '1', emoji: '🗣️', + titleKu: 'Zimanê Kurdî - Kurmancî', title: 'Kurdish Language - Kurmanji', + instructor: 'Prof. Berfîn Shêx', duration: '12 hefte / weeks', + level: 'Destpêk', levelEn: 'Beginner', enrolled: 456, lessons: 48, + description: 'Learn Kurmanji Kurdish from scratch. Covers alphabet, grammar, conversation, reading, and writing.', + reward: 'NFT Certificate + 50 HEZ', + }, + { + id: '2', emoji: '🔗', + titleKu: 'Blockchain 101', title: 'Blockchain 101', + instructor: 'Dr. Azad Kurdo', duration: '8 hefte / weeks', + level: 'Destpêk', levelEn: 'Beginner', enrolled: 312, lessons: 32, + description: 'Introduction to blockchain technology. Learn about consensus, cryptography, smart contracts, and decentralized applications.', + reward: 'NFT Certificate + 30 HEZ', + }, + { + id: '3', emoji: '💻', + titleKu: 'Pêşvebirina Smart Contract', title: 'Smart Contract Development', + instructor: 'Jîn Bakir', duration: '10 hefte / weeks', + level: 'Navîn', levelEn: 'Intermediate', enrolled: 187, lessons: 40, + description: 'Build smart contracts for the Pezkuwi network. Covers Solidity/Ink!, testing, deployment, and security best practices.', + reward: 'NFT Certificate + 100 HEZ', + }, + { + id: '4', emoji: '📊', + titleKu: 'Aboriya Dijîtal û DeFi', title: 'Digital Economics & DeFi', + instructor: 'Prof. Serhat Demirtash', duration: '6 hefte / weeks', + level: 'Navîn', levelEn: 'Intermediate', enrolled: 234, lessons: 24, + description: 'Understand decentralized finance, tokenomics, yield farming, liquidity pools, and the Bereketli neighborhood economy model.', + reward: 'NFT Certificate + 40 HEZ', + }, + { + id: '5', emoji: '🛡️', + titleKu: 'Ewlehiya Sîber û Blockchain', title: 'Cyber Security & Blockchain', + instructor: 'Kawa Zana', duration: '8 hefte / weeks', + level: 'Pêşketî', levelEn: 'Advanced', enrolled: 98, lessons: 32, + description: 'Advanced security topics including audit techniques, common vulnerabilities, formal verification, and incident response.', + reward: 'NFT Certificate + 150 HEZ', + }, + { + id: '6', emoji: '🏛️', + titleKu: 'Dîroka Kurdistanê - Dijîtal', title: 'History of Kurdistan - Digital', + instructor: 'Prof. Dilovan Ehmed', duration: '10 hefte / weeks', + level: 'Destpêk', levelEn: 'Beginner', enrolled: 567, lessons: 40, + description: 'A comprehensive digital course on Kurdish history, culture, and the journey toward digital sovereignty and self-governance.', + reward: 'NFT Certificate + 25 HEZ', + }, +]; + +const LEVEL_COLORS: Record = { + 'Destpêk': '#16a34a', + 'Navîn': '#2563eb', + 'Pêşketî': '#7c3aed', +}; + +export default function UniversityPage() { + const navigate = useNavigate(); + const { t } = useTranslation(); + const totalStudents = COURSES.reduce((s, c) => s + c.enrolled, 0); + + return ( +
+ {/* Header */} +
+
+ + {t('university.breadcrumb', 'Education')} +
+
+ 🎓 +

{t('university.title', 'Zanîngeha Dijîtal')}

+

{t('university.subtitle', 'Kurdistan Digital University')}

+
+
+
+

{COURSES.length}

+

{t('university.stats.courses', 'Kurs / Courses')}

+
+
+
+

{totalStudents.toLocaleString()}

+

{t('university.stats.students', 'Xwendekar / Students')}

+
+
+
+ +
+ {COURSES.map(course => { + const color = LEVEL_COLORS[course.level]; + return ( +
+ {/* Top row */} +
+ {course.emoji} + + {course.level} / {course.levelEn} + +
+ +

{course.titleKu}

+

{course.title}

+

👨‍🏫 {course.instructor}

+

{course.description}

+ + {/* Meta */} +
+
+

📚

+

{course.lessons} ders

+
+
+

⏱️

+

{course.duration}

+
+
+

👥

+

{course.enrolled}

+
+
+ + {/* Reward */} +
+ {t('university.reward', '🎁 Xelat / Reward:')} + {course.reward} +
+ + {/* Enroll button */} + +
+ ); + })} +
+
+
+ ); +}