Import source: app/src/pages/ComparisonPage
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
import { useState } from 'react';
|
||||
import { TeknavData } from '../../teknav-data.js';
|
||||
import { useNav, EmptyState } from '../../teknav-ui.jsx';
|
||||
import linkIcon from '../../src/assets/icons/link.svg';
|
||||
|
||||
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 (
|
||||
<div style={{ background: '#F8F5F0', borderRadius: 12, padding: '20px 24px', marginBottom: 32, display: 'flex', gap: 20, flexWrap: 'wrap', alignItems: 'center' }}>
|
||||
<div style={{ flex: 1, minWidth: 260 }}>
|
||||
<p style={{ fontSize: 14, fontWeight: 700, color: '#263238', marginBottom: 12 }}>شما کدام را ترجیح میدهید؟</p>
|
||||
<div style={{ display: 'flex', gap: 10 }}>
|
||||
<button
|
||||
onClick={() => 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}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => 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}
|
||||
</button>
|
||||
</div>
|
||||
{vote && (
|
||||
<p style={{ fontSize: 12, color: '#5F6B6D', marginTop: 10 }}>
|
||||
انتخاب شما ثبت شد. برای تغییر دوباره کلیک کنید.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
onClick={shareComparison}
|
||||
style={{
|
||||
background: shared ? '#2E7D32' : '#0C1E22', color: shared ? '#fff' : '#D49A2A',
|
||||
border: 'none', borderRadius: 8, padding: '10px 20px', fontSize: 13,
|
||||
fontFamily: 'Vazirmatn,sans-serif', cursor: 'pointer', fontWeight: 700, whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{shared ? '✓ لینک کپی شد' : <><span className="tek-icon" style={{ ['--i']: `url("${linkIcon}")` }} aria-hidden="true" /> اشتراکگذاری</>}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function ComparisonPage({ slug }) {
|
||||
const { navigate } = useNav();
|
||||
const comparison = (TeknavData.comparisons ?? []).find((c) => c.slug === slug);
|
||||
|
||||
if (!comparison) {
|
||||
return (
|
||||
<div style={{ padding: '120px 24px', textAlign: 'center', fontFamily: 'Vazirmatn,sans-serif', direction: 'rtl' }}>
|
||||
<EmptyState title="صفحه مقایسه یافت نشد" subtitle="مقایسهای با این آدرس وجود ندارد" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<div style={{ maxWidth: 960, margin: '0 auto', padding: '24px 16px 80px', direction: 'rtl', fontFamily: 'Vazirmatn,sans-serif' }}>
|
||||
<nav style={{ fontSize: 13, color: '#5F6B6D', marginBottom: 24, display: 'flex', gap: 8, alignItems: 'center' }}>
|
||||
<span style={{ cursor: 'pointer', color: '#0F6B73' }} onClick={() => navigate('/')}>تکناو</span>
|
||||
<span>›</span>
|
||||
<span style={{ cursor: 'pointer', color: '#0F6B73' }} onClick={() => navigate('/compare')}>مقایسهها</span>
|
||||
<span>›</span>
|
||||
<span>{comparison.title}</span>
|
||||
</nav>
|
||||
|
||||
<h1 style={{ fontSize: 'clamp(22px,4vw,32px)', fontWeight: 800, color: '#0C1E22', lineHeight: 1.5, marginBottom: 12 }}>
|
||||
{comparison.title}
|
||||
</h1>
|
||||
{comparison.metaDescription && (
|
||||
<p style={{ fontSize: 16, color: '#5F6B6D', lineHeight: 1.8, marginBottom: 16 }}>{comparison.metaDescription}</p>
|
||||
)}
|
||||
{comparison.factCheckedAt && (
|
||||
<p style={{ fontSize: 12, color: '#5F6B6D', marginBottom: 24 }}>
|
||||
آخرین بروزرسانی: {comparison.factCheckedAt}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<ComparisonVoteBar slug={slug} item1={item1} item2={item2} />
|
||||
|
||||
<div style={{ display: 'flex', gap: 20, flexWrap: 'wrap', marginBottom: 40 }}>
|
||||
{[item1, item2].map((item) => (
|
||||
<div key={item.slug} style={cardStyle}>
|
||||
<h2 style={{ fontSize: 20, fontWeight: 700, color: '#0F6B73', marginBottom: 10 }}>{item.name}</h2>
|
||||
<p style={{ fontSize: 14, color: '#263238', lineHeight: 1.8, marginBottom: 16 }}>{item.summary}</p>
|
||||
<div style={{ marginBottom: 8, fontSize: 13, fontWeight: 700, color: '#263238' }}>مزایا</div>
|
||||
{(item.pros || []).map((p, i) => (
|
||||
<div key={i} style={proStyle}><span>✓</span><span>{p}</span></div>
|
||||
))}
|
||||
<div style={{ marginBottom: 8, marginTop: 12, fontSize: 13, fontWeight: 700, color: '#263238' }}>معایب</div>
|
||||
{(item.cons || []).map((c, i) => (
|
||||
<div key={i} style={conStyle}><span>✗</span><span>{c}</span></div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{useCases.length > 0 && (
|
||||
<section style={{ background: '#F8F5F0', borderRadius: 12, padding: 24, marginBottom: 32 }}>
|
||||
<h2 style={{ fontSize: 18, fontWeight: 700, color: '#263238', marginBottom: 16 }}>چه زمانی کدام را انتخاب کنیم؟</h2>
|
||||
{useCases.map(([left, right], i) => (
|
||||
<div key={i} style={{ display: 'flex', gap: 20, flexWrap: 'wrap' }}>
|
||||
<div style={{ flex: '1 1 280px', padding: '12px 16px', background: '#fff', borderRadius: 8, border: '1px solid #E4DDD2', fontSize: 13, lineHeight: 1.7 }}>
|
||||
<span style={{ fontWeight: 700, color: '#0F6B73' }}>{item1.name}:</span> {left}
|
||||
</div>
|
||||
<div style={{ flex: '1 1 280px', padding: '12px 16px', background: '#fff', borderRadius: 8, border: '1px solid #E4DDD2', fontSize: 13, lineHeight: 1.7 }}>
|
||||
<span style={{ fontWeight: 700, color: '#0F6B73' }}>{item2.name}:</span> {right}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</section>
|
||||
)}
|
||||
|
||||
{verdict && (
|
||||
<section style={{ background: '#0F3540', borderRadius: 12, padding: 24, marginBottom: 40, color: '#fff' }}>
|
||||
<h2 style={{ fontSize: 18, fontWeight: 700, color: '#D49A2A', marginBottom: 12 }}>جمعبندی و نتیجهگیری</h2>
|
||||
<p style={{ fontSize: 15, lineHeight: 1.9 }}>{verdict}</p>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{glossaryTerms.length > 0 && (
|
||||
<section style={{ marginBottom: 40 }}>
|
||||
<h2 style={{ fontSize: 18, fontWeight: 700, color: '#263238', marginBottom: 16 }}>اصطلاحات مرتبط</h2>
|
||||
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>
|
||||
{glossaryTerms.map((g) => (
|
||||
<div
|
||||
key={g.slug}
|
||||
onClick={() => navigate(`/glossary/${g.slug}`)}
|
||||
style={{ background: '#fff', border: '1px solid #0F6B73', borderRadius: 8, padding: '10px 16px', cursor: 'pointer' }}
|
||||
>
|
||||
<div style={{ fontSize: 14, fontWeight: 700, color: '#0F6B73' }}>{g.term}</div>
|
||||
<div style={{ fontSize: 12, color: '#5F6B6D' }}>{g.english}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{articles.length > 0 && (
|
||||
<section>
|
||||
<h2 style={{ fontSize: 18, fontWeight: 700, color: '#263238', marginBottom: 16 }}>مقالات مرتبط</h2>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
{articles.map((a) => (
|
||||
<div
|
||||
key={a.id}
|
||||
onClick={() => 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' }}
|
||||
>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ fontSize: 15, fontWeight: 700, color: '#263238', marginBottom: 4 }}>{a.title}</div>
|
||||
<div style={{ fontSize: 13, color: '#5F6B6D' }}>{a.summary?.slice(0, 100)}…</div>
|
||||
</div>
|
||||
<span style={{ fontSize: 20 }}>←</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user