// teknav-pages.jsx — Search, Category, Authors pages (ES module)
import { useState, useMemo, useEffect, useCallback } from 'react';
import { TeknavData } from './teknav-data.js';
function authorProfilePath(author) {
const username = author.username || TeknavData.authors.find(a => a.slug === author.slug)?.username;
return username ? `/profile/@${username}` : `/author/${author.slug}`;
}
import {
useNav, useToast, useAuth,
AuthorAvatar, CategoryBadge, TypeBadge, EmptyState, SkeletonLoader,
inputStyle, pillBtn,
} from './teknav-ui.jsx';
import { DiagramRenderer } from './teknav-diagrams.jsx';
import { ArticleRow } from './teknav-articles.jsx';
import { contentApi } from './src/lib/content-api.js';
import { engagementApi, pushApi } from './src/lib/engagement-api.js';
// ── Search Page ─────────────────────────────────────────────────────────────
function SearchPage({ initQ = '' }) {
const [q, setQ] = useState(initQ);
const [input, setInput] = useState(initQ);
const [filters, setFilters] = useState({ category: '', author: '', type: '', tag: '', dateRange: '' });
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(false);
const { navigate } = useNav();
const load = useCallback(() => {
setLoading(true);
const params = {
q,
category: filters.category,
author: filters.author,
type: filters.type,
tag: filters.tag,
limit: 50,
};
const apiCall = q ? contentApi.search(params) : contentApi.listArticles(params);
apiCall
.then(res => setItems(res.items ?? []))
.finally(() => setLoading(false));
}, [q, filters]);
useEffect(() => { load(); }, [load]);
const doSearch = (e) => { e.preventDefault(); setQ(input); };
const highlightText = (text, query) => {
if (!query) return text;
const parts = text.split(new RegExp(`(${query})`, 'gi'));
return parts.map((part, i) =>
part.toLowerCase() === query.toLowerCase()
? `${part} `
: part
).join('');
};
return (
جستجوی پیشرفته
دستهبندی
setFilters(f => ({ ...f, category: e.target.value }))} style={inputStyle}>
همه دستهها
{TeknavData.categories.map(c => {c.name} )}
نویسنده
setFilters(f => ({ ...f, author: e.target.value }))} style={inputStyle}>
همه نویسندگان
{TeknavData.authors.map(a => {a.name} )}
نوع محتوا
setFilters(f => ({ ...f, type: e.target.value }))} style={inputStyle}>
همه انواع
{['تحلیل عمیق', 'راهنمای فنی', 'دادهنما', 'خبر فوری'].map(t => {t} )}
setFilters({ category: '', author: '', type: '', tag: '', dateRange: '' })} style={{ ...pillBtn, background: '#F4EFE6', color: '#C94C4C', justifyContent: 'center' }}>پاک کردن فیلترها
{loading ? : (
<>
{items.length === 0 ? : (
{items.length.toLocaleString('fa-IR')} نتیجه پیدا شد
{items.map((a, i) =>
)}
)}
>
)}
);
}
function SearchResult({ article, q, highlight, idx }) {
const { navigate } = useNav();
return (
navigate('/article/' + article.slug)} style={{
padding: '24px 0', borderBottom: '1px solid #E4DDD2', cursor: 'pointer',
opacity: 0, animation: `fadeIn 0.3s ${idx * 40}ms forwards`,
}}>
{article.date} · {article.readTime} دقیقه
);
}
// ── Category Page ───────────────────────────────────────────────────────────
function CategoryPage({ slug }) {
const { navigate } = useNav();
const [cat, setCat] = useState(() => TeknavData.categories.find(c => c.slug === slug));
const [articles, setArticles] = useState(() => contentApi.fallbackArticles().filter(a => a.category === slug));
useEffect(() => {
let cancelled = false;
setCat(TeknavData.categories.find(c => c.slug === slug));
setArticles(contentApi.fallbackArticles().filter(a => a.category === slug));
Promise.all([
contentApi.getCategory(slug).catch(() => null),
contentApi.listArticles({ category: slug, limit: 100 }).catch(() => null),
]).then(([category, result]) => {
if (cancelled) return;
if (category) setCat({ ...(TeknavData.categories.find(c => c.slug === slug) ?? {}), ...category, id: category.slug });
if (result?.items) setArticles(result.items);
});
return () => { cancelled = true; };
}, [slug]);
if (!cat) return ;
return (
{/* Category Hero */}
{(() => {
const HERO_IMAGES = {
ai: '/images/category-hero-ai-v2.jpg',
startup: '/images/category-hero-startup.jpg',
hardware: '/images/category-hero-hardware.jpg',
security: '/images/category-hero-security.jpg',
software: '/images/category-hero-software.jpg',
future: '/images/category-hero-future.jpg',
data: '/images/category-hero-data.jpg',
};
const heroImg = HERO_IMAGES[cat.slug];
return (
{/* Background: photo for AI, soft tinted gradient for others */}
{heroImg
?
:
}
{/* Content grid */}
{/* Text card */}
{/* Pill badge */}
دستهبندی
{cat.name}
{cat.description}
{/* Stats */}
{[['تعداد مقاله', cat.articleCount], ['نویسنده فعال', cat.authorCount], ['میانگین زمان مطالعه', `${cat.avgReadTime} دقیقه`]].map(([label, val], i) => (
0 ? '1px solid #E4DDD2' : 'none' }}>
{val}
{label}
))}
{/* Diagram card */}
);
})()}
مقالههای {cat.name} ({articles.length})
{articles.length === 0 ?
: (
articles.map((a, i) =>
)
)}
);
}
// ── Authors List Page ────────────────────────────────────────────────────────
function TagPage({ tag }) {
const decoded = decodeURIComponent(tag);
const [articles, setArticles] = useState(() => contentApi.fallbackArticles().filter((item) => item.tags?.includes(decoded)));
useEffect(() => {
let cancelled = false;
setArticles(contentApi.fallbackArticles().filter((item) => item.tags?.includes(decoded)));
contentApi.listArticles({ tag: decoded, limit: 100 })
.then((res) => { if (!cancelled) setArticles(res.items ?? []); })
.catch(() => {});
return () => { cancelled = true; };
}, [decoded]);
return (
برچسب {decoded}
همه مقالههایی که با این برچسب منتشر شدهاند.
{articles.length === 0 ?
: articles.map((article, index) => (
))}
);
}
function GlossaryPage({ slug }) {
const { navigate } = useNav();
const term = TeknavData.glossary?.find((item) => item.slug === slug);
const articles = contentApi.fallbackArticles().filter((article) => (
article.tags?.some((tag) => tag.toLowerCase() === term?.term?.toLowerCase() || tag.toLowerCase() === term?.english?.toLowerCase())
|| article.summary?.toLowerCase().includes(term?.english?.toLowerCase() || '')
|| article.title?.toLowerCase().includes(term?.english?.toLowerCase() || '')
)).slice(0, 6);
if (!term) return ;
const related = (term.related ?? []).map((item) => TeknavData.glossary?.find((g) => g.slug === item)).filter(Boolean);
const topic = TeknavData.topicHubs?.find((item) => item.slug === term.topic);
return (
navigate('/articles')} style={{ ...pillBtn, background: '#F4EFE6', color: '#0F6B73', border: '1px solid #E4DDD2', marginBottom: 18 }}>بازگشت به مقالهها
فرهنگ واژگان تکنّاو
{term.term}
{term.english}
{term.summary}
پرسشهای رایج
{(term.faqs ?? []).map(([q, a]) => (
))}
مقالههای مرتبط
{articles.length === 0 ?
: articles.map((article, index) =>
)}
{topic && (
navigate(`/topics/${topic.slug}`)} style={{ background: '#fff', border: '1px solid #E4DDD2', borderRadius: 8, padding: 18, textAlign: 'right', cursor: 'pointer', fontFamily: 'Vazirmatn,sans-serif' }}>
خوشه موضوعی
{topic.title}
)}
واژههای مرتبط
{related.map((item) => (
navigate(`/glossary/${item.slug}`)} style={{ ...pillBtn, background: '#F4EFE6', color: '#0F6B73', border: '1px solid #E4DDD2', fontSize: 12 }}>{item.term}
))}
);
}
function toDateKey(article) {
return article?.dateEn || article?.publishedAt || article?.date || '';
}
function AuthorsListPage() {
const { navigate } = useNav();
const [authors, setAuthors] = useState(() => contentApi.fallbackAuthors());
const [articles, setArticles] = useState(() => contentApi.fallbackArticles());
useEffect(() => {
let cancelled = false;
Promise.all([
contentApi.listAuthors().catch(() => null),
contentApi.listArticles({ limit: 100 }).catch(() => null),
]).then(([authorRows, articleRows]) => {
if (cancelled) return;
if (authorRows) setAuthors(authorRows);
if (articleRows?.items) setArticles(articleRows.items);
});
return () => { cancelled = true; };
}, []);
return (
{/* Authors Hero */}
نویسندگان
نویسندگان تکناو
متخصصانی که روایت عمیق فناوری را مینویسند
{authors.map(author => {
const latestArt = articles.find(a => a.authorId === author.id || a.authorSlug === author.slug);
return (
navigate(authorProfilePath(author))} style={{
background: '#fff', border: '1px solid #E4DDD2', borderRadius: 14, padding: 24,
cursor: 'pointer', direction: 'rtl', transition: 'all 0.2s',
}} onMouseEnter={e => { e.currentTarget.style.transform = 'translateY(-3px)'; e.currentTarget.style.boxShadow = '0 10px 32px rgba(0,0,0,0.08)'; }}
onMouseLeave={e => { e.currentTarget.style.transform = 'translateY(0)'; e.currentTarget.style.boxShadow = 'none'; }}>
{author.name}
{author.verifiedExpert && تاییدشده }
{author.specialty}
{author.liveArticleCount ?? author.articleCount} مقاله منتشرشده
{author.bio.slice(0, 120)}…
{latestArt && (
آخرین مقاله:
{latestArt.title.slice(0, 50)}…
)}
);
})}
);
}
// ── Author Profile Page ─────────────────────────────────────────────────────
function SeriesPage({ slug }) {
const { navigate } = useNav();
const [series, setSeries] = useState(null);
const [error, setError] = useState(false);
useEffect(() => {
let cancelled = false;
setError(false);
contentApi.getSeries(slug)
.then((item) => { if (!cancelled) setSeries(item); })
.catch(() => { if (!cancelled) setError(true); });
return () => { cancelled = true; };
}, [slug]);
if (error) {
return (
);
}
if (!series) {
return در حال بارگذاری...
;
}
return (
navigate('/articles')} style={{ ...pillBtn, background: '#F4EFE6', color: '#0F6B73', border: '1px solid #E4DDD2', marginBottom: 18 }}>بازگشت به مقالهها
{series.title}
{series.description &&
{series.description}
}
{(series.articles ?? []).length === 0 ?
: (
{series.articles.map((item, index) => (
{(item.position ?? index + 1).toLocaleString('fa-IR')}
))}
)}
);
}
function TopicHubPage({ slug }) {
const { navigate } = useNav();
const toast = useToast();
const { user } = useAuth();
const topic = TeknavData.topicHubs.find((item) => item.slug === slug);
const category = TeknavData.categories.find((item) => item.slug === topic?.categorySlug);
const [articles, setArticles] = useState(() => contentApi.fallbackArticles().filter((item) => item.category === topic?.categorySlug));
const [series, setSeries] = useState([]);
const [follow, setFollow] = useState({ following: false, count: 0 });
const [guestPushEnabled, setGuestPushEnabled] = useState(false);
const [guestPushBusy, setGuestPushBusy] = useState(false);
useEffect(() => {
if (!topic) return;
let cancelled = false;
setArticles(contentApi.fallbackArticles().filter((item) => item.category === topic.categorySlug));
Promise.all([
contentApi.listArticles({ category: topic.categorySlug, limit: 100 }).catch(() => null),
contentApi.listSeries().catch(() => []),
engagementApi.topicFollowers(topic.slug).catch(() => null),
]).then(([articleRows, seriesRows, followRow]) => {
if (cancelled) return;
if (articleRows?.items) setArticles(articleRows.items);
setSeries((seriesRows ?? []).filter((row) => (row.articles ?? []).some((entry) => entry.article?.category === topic.categorySlug)));
if (followRow) setFollow({ following: !!followRow.following, count: followRow.count ?? 0 });
});
// Check if guest push already subscribed
if ('PushManager' in window && 'serviceWorker' in navigator) {
pushApi.isSubscribed().then(setGuestPushEnabled).catch(() => {});
}
return () => { cancelled = true; };
}, [topic?.slug]);
if (!topic) {
return (
);
}
const featured = articles.filter((item) => item.featured).slice(0, 3);
const latest = articles.slice().sort((a, b) => String(toDateKey(b)).localeCompare(String(toDateKey(a)))).slice(0, 8);
const authors = TeknavData.authors.filter((author) => articles.some((article) => article.authorId === author.id || article.authorSlug === author.slug));
const toggleTopicFollow = async () => {
if (!user) {
toast('برای دنبال کردن موضوع ابتدا وارد شوید', 'warning');
return;
}
try {
const result = follow.following
? await engagementApi.unfollowTopic(topic.slug)
: await engagementApi.followTopic(topic.slug);
setFollow({ following: !!result.following, count: result.count ?? 0 });
toast(result.following ? 'موضوع دنبال شد' : 'دنبال کردن موضوع لغو شد');
} catch {
toast('دنبال کردن موضوع انجام نشد', 'warning');
}
};
const toggleGuestPush = async () => {
if (!('PushManager' in window) || !('serviceWorker' in navigator)) {
toast('مرورگر شما از اعلانهای وب پشتیبانی نمیکند', 'warning');
return;
}
setGuestPushBusy(true);
try {
if (guestPushEnabled) {
await pushApi.unsubscribeGuest();
setGuestPushEnabled(false);
toast('اعلانهای این موضوع غیرفعال شد');
} else {
const perm = typeof Notification !== 'undefined' ? await Notification.requestPermission() : 'denied';
if (perm !== 'granted') {
toast('اجازه اعلان داده نشد', 'warning');
return;
}
await pushApi.subscribeAsGuest([topic.slug]);
setGuestPushEnabled(true);
toast('اعلانهای مقالات جدید این موضوع فعال شد');
}
} catch {
toast('عملیات ناموفق بود', 'error');
} finally {
setGuestPushBusy(false);
}
};
return (
navigate('/articles')} style={{ ...pillBtn, background: '#FFFFFFAA', color: '#0F6B73', border: '1px solid #E4DDD2', marginBottom: 18 }}>همه مقالهها
{topic.title}
{topic.intro}
{follow.following ? 'دنبال میکنید' : 'دنبال کردن موضوع'} · {(follow.count ?? 0).toLocaleString('fa-IR')}
{'PushManager' in window && (
{guestPushBusy ? '…' : guestPushEnabled ? 'اعلان فعال است' : 'دریافت اعلان مقالات'}
)}
مقالههای شاخص
{featured.length === 0 ? : (
{featured.map((article, index) =>
)}
)}
تازهترین تحلیلها
{latest.length === 0 ? : latest.map((article, index) => )}
سریهای مرتبط
{series.length === 0 ?
سری مقاله مرتبط پس از تایید سردبیر اینجا نمایش داده میشود.
: series.map((item) => (
navigate(`/series/${item.slug}`)} style={{ display: 'block', width: '100%', textAlign: 'right', background: '#F4EFE6', border: '1px solid #E4DDD2', borderRadius: 8, padding: 12, marginBottom: 10, color: '#263238', cursor: 'pointer', fontFamily: 'Vazirmatn,sans-serif' }}>{item.title}
))}
نویسندگان مرتبط
{authors.length === 0 ?
نویسنده مرتبطی پیدا نشد.
: authors.map((author) => (
navigate(authorProfilePath(author))} style={{ display: 'flex', alignItems: 'center', gap: 10, width: '100%', background: 'transparent', border: 0, padding: '8px 0', color: '#263238', cursor: 'pointer', fontFamily: 'Vazirmatn,sans-serif', textAlign: 'right' }}>
{author.name}
))}
پرسشهای رایج
{topic.faqs.map(([question, answer]) => (
{question}
{answer}
))}
);
}
function AuthorProfilePage({ slug }) {
const { navigate } = useNav();
const [author, setAuthor] = useState(null);
const [articles, setArticles] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Redirect /author/:slug → /profile/@:username
const staticAuthor = TeknavData.authors.find(a => a.slug === slug);
const username = staticAuthor?.username || slug;
navigate(`/profile/@${username}`);
}, [slug]);
if (loading) return در حال بارگذاری…
;
if (!author) return ;
return (
{/* Profile header */}
{author.name}
{author.verifiedExpert && متخصص تاییدشده }
{author.verifiedExpert && author.verificationNote &&
{author.verificationNote}
}
{author.specialty}
{author.bio}
{author.articleCount} مقاله
{articles.reduce((s, a) => s + (a.views || 0), 0).toLocaleString('fa')} بازدید
مقالههای {author.name}
{articles.length === 0 ?
:
articles.map((a, i) =>
)
}
);
}
function NewsletterArchivePage() {
const { navigate } = useNav();
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
let cancelled = false;
contentApi.listNewsletterArchive()
.then((rows) => { if (!cancelled) setItems(rows); })
.catch(() => { if (!cancelled) setItems([]); })
.finally(() => { if (!cancelled) setLoading(false); });
return () => { cancelled = true; };
}, []);
return (
آرشیو خبرنامه تکنـاو
نسخههای ارسالشده خبرنامه برای دنبال کردن روندهای مهم فناوری، هوش مصنوعی و امنیت.
{loading ?
در حال بارگذاری...
: items.length === 0 ?
: (
{items.map((item) => (
navigate(`/newsletter/${item.slug}`)} style={{ background: '#fff', border: '1px solid #E4DDD2', borderRadius: 10, padding: 18, textAlign: 'right', cursor: 'pointer', fontFamily: 'Vazirmatn,sans-serif' }}>
{item.subject}
{item.sentAt ? new Date(item.sentAt).toLocaleDateString('fa-IR') : 'منتشر نشده'}
))}
)}
);
}
function NewsletterIssuePage({ slug }) {
const { navigate } = useNav();
const [issue, setIssue] = useState(null);
const [error, setError] = useState(false);
useEffect(() => {
let cancelled = false;
setError(false);
contentApi.getNewsletterIssue(slug)
.then((row) => { if (!cancelled) setIssue(row); })
.catch(() => { if (!cancelled) setError(true); });
return () => { cancelled = true; };
}, [slug]);
if (error) return
;
if (!issue) return در حال بارگذاری...
;
return (
navigate('/newsletter')} style={{ ...pillBtn, background: '#F4EFE6', color: '#0F6B73', border: '1px solid #E4DDD2', marginBottom: 18 }}>آرشیو خبرنامه
{issue.subject}
{issue.sentAt ? new Date(issue.sentAt).toLocaleDateString('fa-IR') : ''}
);
}
function JobsPage() {
const toast = useToast();
const [items, setItems] = useState([]);
const [form, setForm] = useState({ company: '', title: '', location: 'Remote', remote: true, url: '', contactEmail: '', description: '' });
const [busy, setBusy] = useState(false);
useEffect(() => {
contentApi.listJobs().then(setItems).catch(() => setItems([]));
}, []);
const set = (key, value) => setForm((current) => ({ ...current, [key]: value }));
const submit = async (event) => {
event.preventDefault();
setBusy(true);
try {
await contentApi.submitJob(form);
setForm({ company: '', title: '', location: 'Remote', remote: true, url: '', contactEmail: '', description: '' });
toast('آگهی شغلی برای بررسی ارسال شد');
} catch {
toast('ارسال آگهی ناموفق بود', 'error');
} finally {
setBusy(false);
}
};
return (
فرصتهای شغلی فناوری
جایگاههای منتخب شرکتهای فنی فارسیزبان. آگهیهای جدید بعد از بررسی تحریریه منتشر میشوند.
{items.length === 0 ? : items.map((job) => (
{job.title}
{job.company}
{job.remote ? 'Remote' : job.location}
{job.description}
{job.url && مشاهده و ارسال رزومه }
))}
);
}
function CoursesPage() {
const [items, setItems] = useState([]);
useEffect(() => { contentApi.listCourses().then(setItems).catch(() => setItems([])); }, []);
return (
دورههای تخصصی تکناو
مسیرهای آموزشی فارسی برای تیمهایی که فناوری را در محصول و عملیات واقعی به کار میگیرند.
{items.map(course => (
{course.level}
{course.title}
{course.summary}
{course.price ? `${course.price.toLocaleString('fa-IR')} تومان` : 'بهزودی'}
))}
);
}
function MembershipPage() {
const { navigate } = useNav();
const { user } = useAuth();
const toast = useToast();
const [loading, setLoading] = useState(false);
const [status, setStatus] = useState(null);
useEffect(() => {
fetch('/api/v1/membership/status', { credentials: 'include' })
.then((r) => r.ok ? r.json() : null)
.then((d) => d && setStatus(d))
.catch(() => {});
}, []);
const subscribe = async () => {
if (!user) { navigate('/login'); return; }
setLoading(true);
try {
const res = await fetch('/api/v1/membership/subscribe', {
method: 'POST', credentials: 'include',
headers: { 'Content-Type': 'application/json' },
});
const data = await res.json();
if (data.redirectUrl) {
window.location.href = data.redirectUrl;
} else {
toast(data.error === 'payment_unavailable' ? 'درگاه پرداخت در دسترس نیست' : 'خطا در اتصال به درگاه پرداخت');
setLoading(false);
}
} catch {
toast('خطا در ارتباط با سرور');
setLoading(false);
}
};
const isPremium = status?.active;
return (
عضویت پریمیوم تکناو
تجربه بدون تبلیغ، مقالههای اختصاصی، دورههای کوتاه و گزارشهای عمیق برای خوانندگان حرفهای.
{isPremium && (
✓ شما در حال حاضر عضو ویژه هستید
{status?.expiresAt && ` — اعتبار تا ${new Date(status.expiresAt).toLocaleDateString('fa-IR')}`}
)}
{[
['👑', 'بدون تبلیغ', 'خواندن آرام و سریعتر بدون جایگاههای تبلیغاتی.'],
['📖', 'محتوای اختصاصی', 'تحلیلهای عمیقتر و پروندههای تخصصی برای اعضا.'],
['🎓', 'تخفیف دورهها', 'دسترسی زودتر و تخفیف روی دورههای فارسی تکناو.'],
].map(([icon, title, body]) => (
))}
{!isPremium && (
سالانه — ۵۰۰,۰۰۰ تومان
{loading ? 'در حال اتصال به درگاه...' : 'شروع عضویت ویژه'}
)}
);
}
function MembershipSuccessPage() {
const { navigate } = useNav();
return (
👑
عضویت ویژه فعال شد!
از حالا به همه مقالات پریمیوم دسترسی دارید. ممنون از حمایت شما از تکناو.
navigate('/')} style={{ ...pillBtn, background: '#0F6B73', color: '#fff', padding: '12px 28px', fontSize: 14 }}>بازگشت به خانه
);
}
// ── Shared legal page shell ──────────────────────────────────────────────────
function LegalPage({ title, subtitle, children }) {
return (
{title}
{subtitle &&
{subtitle}
}
{children}
);
}
function LegalSection({ title, children }) {
return (
);
}
// ── Privacy Policy ───────────────────────────────────────────────────────────
function PrivacyPolicyPage() {
return (
تکناو اطلاعات زیر را از کاربران جمعآوری میکند:
اطلاعات حساب کاربری: نام، آدرس ایمیل و نام کاربری هنگام ثبتنام.
اطلاعات استفاده: صفحات بازدیدشده، مقالات مطالعهشده، مدت زمان حضور در سایت و واکنشهای کاربری — بهصورت ناشناس و جمعی.
اطلاعات فنی: نوع مرورگر، سیستمعامل و آدرس IP بهمنظور امنیت و رفع اشکال.
اطلاعات خبرنامه: آدرس ایمیل در صورت عضویت اختیاری در خبرنامه.
اطلاعات جمعآوریشده صرفاً برای موارد زیر استفاده میشود:
ارائه و بهبود محتوا و خدمات تکناو
ارسال خبرنامه در صورت اشتراک داوطلبانه
تشخیص و پیشگیری از دسترسی غیرمجاز و سوءاستفاده
پاسخ به درخواستها و پشتیبانی کاربران
تکناو اطلاعات شخصی شما را به هیچ شخص ثالثی نمیفروشد.
تکناو از کوکیهای ضروری برای مدیریت نشست (session) و از localStorage برای ذخیره تنظیمات رابط کاربری استفاده میکند. هیچ کوکی تبلیغاتی یا ردیابی شخصثالث استفاده نمیشود.
اطلاعات کاربران با رمزنگاری Argon2id (کلمه عبور) و HTTPS (انتقال داده) محافظت میشوند. دسترسی به دادههای شخصی به حداقل افراد ضروری محدود است.
شما حق دارید هر زمان درخواست دسترسی، اصلاح، یا حذف اطلاعات شخصی خود را از طریق info@teknav.ir ارسال کنید. درخواستها ظرف ۱۴ روز کاری پردازش میشوند.
تکناو ممکن است این سیاست را بهروزرسانی کند. تغییرات مهم از طریق اعلان درونسایتی اطلاعرسانی خواهند شد. ادامه استفاده از سایت پس از انتشار تغییرات بهمنزله پذیرش آنها است.
برای سوالات مرتبط با حریم خصوصی با ما از طریق info@teknav.ir تماس بگیرید.
);
}
// ── Terms of Use ─────────────────────────────────────────────────────────────
function TermsOfUsePage() {
return (
با دسترسی به وبسایت تکناو (teknav.ir) شما موافقت خود را با این شرایط اعلام میکنید. اگر با هر بخشی از این شرایط موافق نیستید، لطفاً از استفاده از سایت خودداری کنید.
تمامی محتوای منتشرشده در تکناو — شامل مقالات، تحلیلها، نمودارها، و طراحیها — متعلق به تکناو یا نویسندگان مربوطه است و تحت قوانین مالکیت معنوی محافظت میشود.
نقلقول کوتاه با ذکر منبع (لینک مستقیم به مقاله) مجاز است. بازنشر کامل یا ترجمه بدون اجازه کتبی ممنوع است.
مسئولیت حفظ امنیت اطلاعات ورود به سیستم با کاربر است.
ایجاد چندین حساب کاربری توسط یک شخص مجاز نیست.
تکناو حق تعلیق یا حذف حسابهای ناقض قوانین را دارد.
کاربران متعهد میشوند که از سایت برای موارد زیر استفاده نکنند:
انتشار محتوای توهینآمیز، خلاف قانون، یا گمراهکننده
ارسال هرزنامه (spam) یا تلاش برای دسترسی غیرمجاز
تداخل در عملکرد سیستم یا سرویسهای تکناو
محتوای تکناو صرفاً جنبه اطلاعرسانی و آموزشی دارد. تکناو در قبال تصمیماتی که بر اساس محتوای منتشرشده اتخاذ میشوند مسئولیتی نمیپذیرد. صحت اطلاعات فنی در حد توان بررسی میشود اما ضمانت کامل داده نمیشود.
تکناو حق دارد هر زمان خدمات، محتوا، یا این شرایط را تغییر دهد. اطلاعرسانی تغییرات مهم از طریق سایت انجام میشود.
این شرایط تابع قوانین جمهوری اسلامی ایران است. هرگونه اختلاف در صلاحیت دادگاههای ایران رسیدگی خواهد شد.
برای سوالات حقوقی با ما از طریق info@teknav.ir در ارتباط باشید.
);
}
// ── About Page ───────────────────────────────────────────────────────────────
function AboutPage() {
const { navigate } = useNav();
return (
تکناو یک نشریه فناوری فارسیزبان است که با هدف تولید محتوای عمیق، علمی، و کاربردی برای متخصصان، پژوهشگران، و علاقهمندان ایرانی فعالیت میکند. ما باور داریم که جامعه فارسیزبان به منابع تخصصیای نیاز دارد که نه صرفاً ترجمه، بلکه تحلیل و تولید اصیل باشند.
{[
['هوش مصنوعی', 'یادگیری ماشین، مدلهای زبانی بزرگ، سیستمهای هوشمند'],
['علم داده', 'مهندسی داده، پایپلاینها، ارزیابی کمی'],
['امنیت سایبری', 'تهدیدات نوظهور، رمزنگاری، تست نفوذ'],
['سختافزار', 'پردازندهها، شتابدهندههای هوش مصنوعی، زیرساخت'],
['نرمافزار', 'معماری سیستم، مهندسی نرمافزار، ابزارهای توسعه'],
['آینده فناوری', 'روندهای بلندمدت، تأثیر اجتماعی فناوری'],
].map(([t, d]) => (
{t}: {d}
))}
هر مقاله تکناو با بررسی منابع اولیه، مقالات علمی، و تجربه عملی تهیه میشود. ما از هوش مصنوعی بهعنوان ابزار کمکی در ویرایش استفاده میکنیم اما تحلیل، نتیجهگیری، و دیدگاهها کاملاً انسانی هستند.
تکناو توسط آرسام صباغ — پژوهشگر ارشد هوش مصنوعی و مهندسی داده — بنیانگذاری شده و با همکاری نویسندگان متخصص در حوزههای مختلف فناوری فعالیت میکند.
navigate('/authors')} style={{ background: '#0F6B73', color: '#fff', border: 'none', borderRadius: 8, padding: '10px 20px', fontSize: 13, fontWeight: 600, cursor: 'pointer', fontFamily: 'Vazirmatn,sans-serif' }}>
مشاهده تیم نویسندگان
navigate('/contact')} style={{ background: 'transparent', color: '#0F6B73', border: '1px solid #0F6B73', borderRadius: 8, padding: '10px 20px', fontSize: 13, fontWeight: 600, cursor: 'pointer', fontFamily: 'Vazirmatn,sans-serif' }}>
تماس با ما
وبسایت: teknav.ir
ایمیل: info@teknav.ir
تهران، ایران
);
}
// ── Contact Page ─────────────────────────────────────────────────────────────
function ContactPage() {
const { navigate } = useNav();
const toast = useToast();
const [form, setForm] = useState({ name: '', email: '', subject: '', message: '' });
const [sent, setSent] = useState(false);
const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
const submit = async (e) => {
e.preventDefault();
if (!form.name.trim() || !form.email.includes('@') || !form.message.trim()) {
toast('لطفاً همه فیلدهای ضروری را پر کنید', 'error');
return;
}
try {
await fetch('/api/contact', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(form),
});
} catch {}
setSent(true);
};
if (sent) return (
✓
پیام شما ارسال شد
ممنون از تماس شما. تیم تکناو بهزودی پاسخ خواهد داد.
navigate('/')} style={{ background: '#0F6B73', color: '#fff', border: 'none', borderRadius: 8, padding: '12px 28px', fontSize: 14, fontWeight: 600, cursor: 'pointer', fontFamily: 'Vazirmatn,sans-serif' }}>
بازگشت به خانه
);
const field = { background: '#FAF7F0', border: '1px solid #D8D0C4', borderRadius: 8, padding: '11px 14px', fontSize: 14, fontFamily: 'Vazirmatn,sans-serif', color: '#263238', width: '100%', boxSizing: 'border-box', outline: 'none', direction: 'rtl' };
return (
{[
['📧', 'ایمیل عمومی', 'info@teknav.ir'],
['✍️', 'همکاری محتوایی', 'admin@teknavglobal.com'],
['📍', 'موقعیت', 'تهران، ایران'],
].map(([icon, label, val]) => (
))}
موضوع
set('subject', e.target.value)} placeholder="موضوع پیام" style={field} />
پیام *
set('message', e.target.value)} placeholder="پیام خود را اینجا بنویسید..." rows={5} style={{ ...field, resize: 'vertical', lineHeight: 1.8 }} />
ارسال پیام
);
}
// ── Tools Page ───────────────────────────────────────────────────────────────
function ToolsPage() {
const { navigate } = useNav();
return (
{[
{
title: 'نرمالساز متن فارسی (NLP)',
desc: 'اصلاح حروف عربی، تنظیم نیمفاصلهها، حذف کدهای HTML و فاصلههای مکرر در متون فارسی برای پیشپردازش سیستمهای هوش مصنوعی.',
path: '/tools/nlp-normalizer',
icon: '📝',
},
{
title: 'محاسبهگر چانک و زمینه RAG',
desc: 'شبیهسازی تکهبندی متون، همپوشانی (Overlap)، محاسبه تعداد چانکها و برآورد تعداد توکنهای فارسی برای پایپلاینهای Retrieval-Augmented Generation.',
path: '/tools/rag-calculator',
icon: '📐',
},
].map((t) => (
{t.icon}
{t.title}
{t.desc}
navigate(t.path)} style={{ background: '#0F6B73', color: '#fff', border: 'none', borderRadius: 8, padding: '10px 18px', fontSize: 13, fontWeight: 600, cursor: 'pointer', fontFamily: 'Vazirmatn,sans-serif', width: '100%' }}>
ورود به ابزار
))}
);
}
// ── NLP Normalizer Page ──────────────────────────────────────────────────────
function NlpNormalizerPage() {
const { navigate } = useNav();
const toast = useToast();
const [text, setText] = useState('');
const [normalized, setNormalized] = useState('');
const [options, setOptions] = useState({
stripHtml: true,
normalizeLetters: true,
normalizeZwnj: true,
cleanWhitespace: true,
});
const toggle = (k) => setOptions((o) => ({ ...o, [k]: !o[k] }));
const handleNormalize = () => {
let t = text || '';
if (options.stripHtml) {
t = t.replace(/<[^>]*>/g, ' ');
}
if (options.normalizeLetters) {
t = t.replace(/ي/g, 'ی').replace(/ك/g, 'ک').replace(/ة/g, 'ه');
}
if (options.normalizeZwnj) {
t = t.replace(/\s+(می|نمی)\s+/g, ' $1');
t = t.replace(/\s+(تر|ترین|ها|های|ام|ات|اش|مان|تان|شان)\b/g, '$1');
t = t.replace(/\b(می|نمی)\b\s+/g, '$1');
}
if (options.cleanWhitespace) {
t = t.replace(/\s+/g, ' ');
t = t.replace(/\n\s*\n+/g, '\n\n');
}
setNormalized(t.trim());
toast('متن با موفقیت نرمالسازی شد', 'success');
};
const copyToClipboard = (val) => {
if (!val) return;
navigator.clipboard.writeText(val);
toast('کپی شد!', 'success');
};
const textareaStyle = {
width: '100%',
minHeight: 180,
background: '#FAF7F0',
border: '1px solid #D8D0C4',
borderRadius: 8,
padding: '12px 16px',
fontSize: 14,
fontFamily: 'Vazirmatn, sans-serif',
color: '#263238',
outline: 'none',
lineHeight: 1.7,
resize: 'vertical',
boxSizing: 'border-box',
};
return (
متن ورودی:
setText(e.target.value)}
placeholder="متن فارسی خود را برای نرمالسازی اینجا وارد کنید..."
style={textareaStyle}
/>
نرمالسازی متن
navigate('/tools')} style={{ background: 'transparent', color: '#5F6B6D', border: '1px solid #D8D0C4', borderRadius: 8, padding: '12px 20px', fontSize: 14, fontWeight: 600, cursor: 'pointer', fontFamily: 'Vazirmatn,sans-serif' }}>
بازگشت
{normalized && (
متن خروجی:
copyToClipboard(normalized)} style={{ background: '#FAF7F0', border: '1px solid #D8D0C4', borderRadius: 6, padding: '6px 12px', fontSize: 12, cursor: 'pointer', fontFamily: 'Vazirmatn,sans-serif', display: 'flex', alignItems: 'center', gap: 6 }}>
📋 کپی خروجی
)}
ارجاع علمی و اشتراکگذاری
برای استفاده از خروجیهای این ابزار در تحقیقات یا کارهای متنباز خود، میتوانید از فرمت ارجاع زیر استفاده کنید:
[Teknav Persian NLP Tool] (https://www.teknav.ir/tools/nlp-normalizer)
copyToClipboard('[Teknav Persian NLP Tool] (https://www.teknav.ir/tools/nlp-normalizer)')} style={{ background: '#fff', border: '1px solid #D8D0C4', borderRadius: 4, padding: '2px 8px', fontSize: 10, cursor: 'pointer', fontFamily: 'sans-serif' }}>Copy
);
}
// ── RAG Calculator Page ──────────────────────────────────────────────────────
function RagCalculatorPage() {
const { navigate } = useNav();
const toast = useToast();
const [text, setText] = useState('');
const [chunkSize, setChunkSize] = useState(500);
const [overlap, setOverlap] = useState(50);
const [chunks, setChunks] = useState([]);
const handleCalculate = () => {
if (!text.trim()) {
toast('لطفاً ابتدا متنی را برای چانکبندی وارد کنید', 'error');
return;
}
if (chunkSize <= 0 || overlap < 0 || overlap >= chunkSize) {
toast('اندازه چانک یا همپوشانی نامعتبر است', 'error');
return;
}
const t = text.trim();
const result = [];
let start = 0;
while (start < t.length) {
let end = start + chunkSize;
if (end > t.length) end = t.length;
const content = t.substring(start, end);
const estimatedTokens = Math.round(content.length / 3.8);
result.push({
id: result.length + 1,
content,
charCount: content.length,
tokenCount: estimatedTokens,
range: [start, end],
});
start += (chunkSize - overlap);
if (start >= t.length || end === t.length) break;
}
setChunks(result);
toast(`تکهبندی با موفقیت انجام شد (${result.length} چانک)`, 'success');
};
const copyToClipboard = (val) => {
if (!val) return;
navigator.clipboard.writeText(val);
toast('کپی شد!', 'success');
};
return (
متن نمونه:
setText(e.target.value)}
placeholder="متن فارسی مورد نظر برای شبیهسازی RAG را در این قسمت بنویسید..."
style={{ width: '100%', minHeight: 140, background: '#FAF7F0', border: '1px solid #D8D0C4', borderRadius: 8, padding: '12px 16px', fontSize: 14, fontFamily: 'Vazirmatn, sans-serif', color: '#263238', outline: 'none', lineHeight: 1.7, resize: 'vertical', boxSizing: 'border-box' }}
/>
پارامترهای شبیهسازی
اندازه هر تکه (به کاراکتر):
setChunkSize(Number(e.target.value))} style={{ width: '100%', padding: '10px 12px', border: '1px solid #D8D0C4', borderRadius: 8, fontSize: 14, fontFamily: 'Vazirmatn, sans-serif' }} />
اندازه همپوشانی (Overlap):
setOverlap(Number(e.target.value))} style={{ width: '100%', padding: '10px 12px', border: '1px solid #D8D0C4', borderRadius: 8, fontSize: 14, fontFamily: 'Vazirmatn, sans-serif' }} />
محاسبه و شبیهسازی
navigate('/tools')} style={{ background: 'transparent', color: '#5F6B6D', border: '1px solid #D8D0C4', borderRadius: 8, padding: '12px 20px', fontSize: 14, fontWeight: 600, cursor: 'pointer', fontFamily: 'Vazirmatn,sans-serif' }}>
بازگشت
{chunks.length > 0 && (
نتایج شبیهسازی تکهبندی:
{[
['طول کل کاراکترها', text.length],
['تعداد تخمینی توکنها', Math.round(text.length / 3.8)],
['تعداد چانکهای خروجی', chunks.length],
].map(([lbl, val]) => (
{lbl}
{val.toLocaleString('fa-IR')}
))}
{chunks.map((c) => (
چانک #{c.id.toLocaleString('fa-IR')} (اندیس {c.range[0].toLocaleString('fa-IR')} تا {c.range[1].toLocaleString('fa-IR')})
{c.charCount.toLocaleString('fa-IR')} کاراکتر · {c.tokenCount.toLocaleString('fa-IR')} توکن تخمینی
copyToClipboard(c.content)} style={{ background: '#fff', border: '1px solid #D8D0C4', borderRadius: 4, padding: '3px 8px', fontSize: 11, cursor: 'pointer', fontFamily: 'Vazirmatn, sans-serif' }}>کپی
{c.content}
))}
)}
);
}
// ── TekRAG Persian Hub Page ──────────────────────────────────────────────────
function TekRagHubPage() {
const toast = useToast();
const copyToClipboard = (val) => {
navigator.clipboard.writeText(val);
toast('کپی شد!', 'success');
};
const citationText = `@article{tekrag_persian_2026,
title={TekRAG-Persian: A Data-Centric Evaluation Framework for Hallucination in Persian Retrieval-Augmented Generation},
author={Sabbagh, Arsam},
journal={Teknav Journal of AI Research},
volume={1},
number={1},
pages={10-24},
year={2026},
publisher={Teknav Media}
}`;
return (
TekRAG-Persian اولین مجموعه داده و چارچوب بنچمارک متمرکز بر زبان فارسی برای سنجش و کاهش میزان توهم در پیادهسازیهای RAG است. این پروژه بر مبنای تحلیل مقالههای تخصصی رسانه تکناو و پاسخدهی دقیق مدلهای زبانی بزرگ توسعه داده شده است.
هدف این بنچمارک ارزیابی سه متغیر کلیدی در بهبود عملکرد سیستمهای بازیابی است: پاکسازی پیشرفته متن، افزودن متادیتاهای ساختاریافته به تکهها، و تأثیر هدایت مدل با تأکید بر استناد به منابع اصلی.
{[
['💾 مجموعه داده در Hugging Face', 'دسترسی به فایلهای خام متون و پرسشها', 'https://huggingface.co/datasets/teknav/tekrag-persian', 'مشاهده دیتاست'],
['🔗 شناسه دیجیتال Zenodo (DOI)', 'نسخه آرشیو شده و معتبر علمی پژوهش', 'https://doi.org/10.5281/zenodo.placeholder', 'مشاهده DOI'],
['⚙️ سورسکد در GitHub', 'کدهای پیادهسازی و ارزیابی بنچمارک', 'https://github.com/teknav/tekrag-persian', 'مشاهده ریپازیتوری'],
].map(([title, desc, link, btnText]) => (
))}
اگر از این بنچمارک یا دیتاست در پژوهشهای دانشگاهی یا پروژههای علمی خود استفاده میکنید، لطفاً با فرمت BibTeX زیر به آن ارجاع دهید:
copyToClipboard(citationText)} style={{ position: 'absolute', top: 12, left: 12, background: '#fff', border: '1px solid #D8D0C4', borderRadius: 6, padding: '5px 10px', fontSize: 11, cursor: 'pointer', fontFamily: 'Vazirmatn, sans-serif' }}>
📋 کپی ارجاع
{citationText}
این بنچمارک شامل بخشهای زیر در فرمت JSON است:
{[
['Corpus (بدنه داده)', 'شامل مقالههای تخصصی با تگها، دستهبندی، پاراگرافها و عناوین مجزا.'],
['Questions (سوالات)', '۲۰۰ سوال واقعی طراحی شده توسط متخصصان در سطوح مختلف دشواری.'],
['Gold Annotations (پاسخهای طلایی)', 'تکههای دقیق پشتیبان برای هر سوال جهت سنجش بازیابی درست اطلاعات.'],
].map(([t, d]) => (
{t}
{d}
))}
);
}
function ComparisonVoteBar({ slug, item1, item2 }) {
const key = `cmp_vote_${slug}`;
const [vote, setVote] = useState(() => {
try { return localStorage.getItem(key) || null; } catch { return null; }
});
const [shared, setShared] = useState(false);
const cast = (choice) => {
try { localStorage.setItem(key, choice); } catch {}
setVote(choice);
};
const shareComparison = () => {
const url = `https://www.teknav.ir/compare/${slug}`;
if (navigator.share) {
navigator.share({ title: `مقایسه ${item1.name} در برابر ${item2.name} — تکناو`, url }).catch(() => {});
} else {
navigator.clipboard?.writeText(url).then(() => { setShared(true); setTimeout(() => setShared(false), 2000); }).catch(() => {});
}
};
return (
شما کدام را ترجیح میدهید؟
cast('item1')}
style={{
flex: 1, padding: '9px 16px', borderRadius: 8, fontSize: 14, fontFamily: 'Vazirmatn,sans-serif', cursor: 'pointer', fontWeight: 700, transition: 'all 0.15s',
background: vote === 'item1' ? '#0F6B73' : '#fff',
color: vote === 'item1' ? '#fff' : '#0F6B73',
border: `2px solid ${vote === 'item1' ? '#0F6B73' : '#E4DDD2'}`,
}}
>
{vote === 'item1' ? '✓ ' : ''}{item1.name}
cast('item2')}
style={{
flex: 1, padding: '9px 16px', borderRadius: 8, fontSize: 14, fontFamily: 'Vazirmatn,sans-serif', cursor: 'pointer', fontWeight: 700, transition: 'all 0.15s',
background: vote === 'item2' ? '#D49A2A' : '#fff',
color: vote === 'item2' ? '#fff' : '#D49A2A',
border: `2px solid ${vote === 'item2' ? '#D49A2A' : '#E4DDD2'}`,
}}
>
{vote === 'item2' ? '✓ ' : ''}{item2.name}
{vote && (
انتخاب شما ثبت شد. برای تغییر دوباره کلیک کنید.
)}
{shared ? '✓ لینک کپی شد' : '🔗 اشتراکگذاری'}
);
}
function ComparisonPage({ slug }) {
const { navigate } = useNav();
const comparison = (TeknavData.comparisons ?? []).find((c) => c.slug === slug);
if (!comparison) {
return (
);
}
const { item1, item2, verdict, useCases = [], relatedArticles = [], relatedGlossary = [] } = comparison;
const articles = TeknavData.articles.filter((a) => relatedArticles.includes(a.slug));
const glossaryTerms = (TeknavData.glossary ?? []).filter((g) => relatedGlossary.includes(g.slug));
const cardStyle = {
background: '#fff', borderRadius: 12, padding: 24,
border: '1px solid #E4DDD2', flex: '1 1 300px',
};
const proStyle = { fontSize: 13, color: '#2E7D32', marginBottom: 6, display: 'flex', alignItems: 'flex-start', gap: 6 };
const conStyle = { fontSize: 13, color: '#C62828', marginBottom: 6, display: 'flex', alignItems: 'flex-start', gap: 6 };
return (
{/* Breadcrumb */}
navigate('/')}>تکناو
›
navigate('/compare')}>مقایسهها
›
{comparison.title}
{/* Header */}
{comparison.title}
{comparison.metaDescription && (
{comparison.metaDescription}
)}
{comparison.factCheckedAt && (
آخرین بروزرسانی: {comparison.factCheckedAt}
)}
{/* Vote + Share bar */}
{/* Two-column cards */}
{[item1, item2].map((item) => (
{item.name}
{item.summary}
مزایا
{(item.pros || []).map((p, i) => (
✓ {p}
))}
معایب
{(item.cons || []).map((c, i) => (
✗ {c}
))}
))}
{/* Use Cases */}
{useCases.length > 0 && (
چه زمانی کدام را انتخاب کنیم؟
{useCases.map(([left, right], i) => (
{item1.name}: {left}
{item2.name}: {right}
))}
)}
{/* Verdict */}
{verdict && (
جمعبندی و نتیجهگیری
{verdict}
)}
{/* Related Glossary */}
{glossaryTerms.length > 0 && (
اصطلاحات مرتبط
{glossaryTerms.map((g) => (
navigate(`/glossary/${g.slug}`)}
style={{ background: '#fff', border: '1px solid #0F6B73', borderRadius: 8, padding: '10px 16px', cursor: 'pointer' }}
>
{g.term}
{g.english}
))}
)}
{/* Related Articles */}
{articles.length > 0 && (
مقالات مرتبط
{articles.map((a) => (
navigate(`/article/${a.slug}`)}
style={{ background: '#fff', border: '1px solid #E4DDD2', borderRadius: 8, padding: '14px 18px', cursor: 'pointer', display: 'flex', gap: 12, alignItems: 'flex-start' }}
>
{a.title}
{a.summary?.slice(0, 100)}…
←
))}
)}
);
}
function DirectoryListPage() {
const { navigate } = useNav();
const [companies] = useState(() => TeknavData.companies ?? []);
const [search, setSearch] = useState('');
const filtered = useMemo(() => {
if (!search.trim()) return companies;
const q = search.trim().toLocaleLowerCase('fa-IR');
return companies.filter((c) =>
c.name.toLocaleLowerCase('fa-IR').includes(q) ||
c.description.toLocaleLowerCase('fa-IR').includes(q) ||
(c.techStack || []).some((t) => t.toLocaleLowerCase('en-US').includes(q)),
);
}, [companies, search]);
return (
navigate('/')}>تکناو
›
دایرکتوری شرکتهای فناوری
دایرکتوری شرکتهای فناوری ایران
پروفایل شرکتهای فناوری ایران همراه با استک فنی، اطلاعات تماس و نشان تأیید.
setSearch(e.target.value)}
style={{ ...inputStyle, marginBottom: 24, width: '100%', maxWidth: 400 }}
/>
{filtered.length === 0 && (
)}
{filtered.map((company) => (
navigate(`/directory/${company.slug}`)}
style={{ background: '#fff', border: '1px solid #E4DDD2', borderRadius: 12, padding: 20, cursor: 'pointer', transition: 'box-shadow 0.2s' }}
onMouseEnter={(e) => { e.currentTarget.style.boxShadow = '0 4px 16px rgba(0,0,0,0.1)'; }}
onMouseLeave={(e) => { e.currentTarget.style.boxShadow = ''; }}
>
{company.name}
{company.verified && ✓ تأیید }
{company.description.slice(0, 100)}…
{(company.techStack || []).slice(0, 4).map((t) => (
{t}
))}
))}
);
}
function CompanyProfilePage({ slug }) {
const { navigate } = useNav();
const company = (TeknavData.companies ?? []).find((c) => c.slug === slug);
if (!company) {
return (
);
}
return (
navigate('/')}>تکناو
›
navigate('/directory')}>دایرکتوری
›
{company.name}
{company.name}
{company.verified && (
✓ تأیید شده
)}
{company.description}
{(company.techStack || []).length > 0 && (
استک فناوری
{company.techStack.map((t) => (
{t}
))}
)}
این صفحه بخشی از دایرکتوری شرکتهای فناوری تکناو است. اگر این شرکت را نمایندگی میکنید، میتوانید پروفایل آن را ادعا و تأیید کنید.
navigate('/login')}
>
ادعای پروفایل
);
}
const CATEGORY_COLORS = { ai: '#0F6B73', data: '#2F8F6B', security: '#C62828', software: '#1565C0', hardware: '#6A1B9A' };
function ComparisonsListPage() {
const { navigate } = useNav();
const comparisons = TeknavData.comparisons ?? [];
const [search, setSearch] = useState('');
const filtered = useMemo(() => {
if (!search.trim()) return comparisons;
const q = search.trim().toLocaleLowerCase('fa-IR');
return comparisons.filter((c) =>
c.title.toLocaleLowerCase('fa-IR').includes(q) ||
c.item1.name.toLocaleLowerCase('en-US').includes(q) ||
c.item2.name.toLocaleLowerCase('en-US').includes(q) ||
(c.tags || []).some((t) => t.toLocaleLowerCase().includes(q)),
);
}, [comparisons, search]);
return (
navigate('/')}>تکناو
› مقایسهها
مقایسههای فنی تکناو
تحلیلهای مقایسهای جامع میان فناوریها، فریمورکها و ابزارهای رایج — برای انتخاب بهتر در پروژههای فارسیزبان.
setSearch(e.target.value)}
style={{ ...inputStyle, marginBottom: 28, width: '100%', maxWidth: 400 }}
/>
{filtered.map((c) => (
navigate(`/compare/${c.slug}`)}
style={{ background: '#fff', border: '1px solid #E4DDD2', borderRadius: 12, padding: '20px 24px', cursor: 'pointer', display: 'flex', gap: 20, alignItems: 'center' }}
onMouseEnter={(e) => { e.currentTarget.style.boxShadow = '0 4px 16px rgba(0,0,0,0.09)'; }}
onMouseLeave={(e) => { e.currentTarget.style.boxShadow = ''; }}
>
{c.category}
{(c.tags || []).slice(0, 3).map((t) => (
{t}
))}
{c.title}
{c.item1.name}
در برابر
{c.item2.name}
←
))}
{filtered.length === 0 &&
}
);
}
function StoriesListPage() {
const { navigate } = useNav();
const today = new Date().toISOString().slice(0, 10);
const stories = (TeknavData.articles ?? []).filter((a) => {
if (a.status !== 'منتشرشده' && a.status) return false;
const d = String(a.dateEn || '').slice(0, 10);
return /^\d{4}-\d{2}-\d{2}$/.test(d) && d <= today;
});
return (
navigate('/')}>تکناو
› استوریها
استوریهای تکناو
مقالات تکناو در قالب استوریهای بصری — سریع، قابل اسوایپ و بهینه برای موبایل.
{stories.map((a) => {
const img = a.ogImage || `/images/og/${a.slug}.jpg`;
return (
{ window.open(`/stories/${a.slug}`, '_blank'); }}
style={{ borderRadius: 14, overflow: 'hidden', cursor: 'pointer', position: 'relative', paddingBottom: '177%', background: '#0C1E22' }}
onMouseEnter={(e) => { e.currentTarget.style.transform = 'scale(1.03)'; e.currentTarget.style.boxShadow = '0 8px 24px rgba(0,0,0,0.25)'; }}
onMouseLeave={(e) => { e.currentTarget.style.transform = ''; e.currentTarget.style.boxShadow = ''; }}
>
{a.categoryName}
{a.title.slice(0, 55)}{a.title.length > 55 ? '…' : ''}
▶ استوری
);
})}
);
}
export { SearchPage, CategoryPage, TagPage, GlossaryPage, AuthorsListPage, AuthorProfilePage, SeriesPage, TopicHubPage, NewsletterArchivePage, NewsletterIssuePage, JobsPage, CoursesPage, MembershipPage, MembershipSuccessPage, PrivacyPolicyPage, TermsOfUsePage, AboutPage, ContactPage, ToolsPage, NlpNormalizerPage, RagCalculatorPage, TekRagHubPage, ComparisonPage, DirectoryListPage, CompanyProfilePage, ComparisonsListPage, StoriesListPage };