Add article detail view to documentation page

- Add state to track selected article (sectionId + articleId)
- Implement renderArticleContent() function to display full article content
- Display intro, steps, tips, and conclusion sections from translations
- Add click handlers to article cards to show detail view
- Add back button to return to article list
- Reset selected article when switching sections in sidebar

Fixes issue where clicking on article cards only showed metadata instead of full content with steps, tips, and detailed information.
This commit is contained in:
Claude
2025-11-18 12:35:22 +00:00
parent 2378a8645f
commit 47bde56d2a

View File

@@ -41,6 +41,7 @@ interface DocArticle {
const DocumentationPage: React.FC = () => {
const { t } = useTranslation('help');
const [activeSection, setActiveSection] = useState<string>('getting-started');
const [selectedArticle, setSelectedArticle] = useState<{ sectionId: string; articleId: string } | null>(null);
const sections: DocSection[] = [
{
@@ -287,6 +288,160 @@ const DocumentationPage: React.FC = () => {
return t(`difficulty.${difficulty}` as any) || difficulty;
};
const getCategoryKey = (sectionId: string): string => {
const categoryMap: Record<string, string> = {
'getting-started': 'gettingStarted',
'features': 'features',
'analytics': 'analytics',
'account': 'account',
'billing': 'billing',
'privacy': 'privacy',
};
return categoryMap[sectionId] || sectionId;
};
const getArticleKey = (articleId: string): string => {
const articleMap: Record<string, string> = {
'quick-start': 'quickStart',
'import-data': 'importData',
'products-catalog': 'productsCatalog',
'first-prediction': 'firstPrediction',
'demand-forecasting': 'demandForecasting',
'production-planning': 'productionPlanning',
'inventory-management': 'inventoryManagement',
'pos-integration': 'posIntegration',
'waste-tracking': 'wasteTracking',
'dashboard-overview': 'dashboardOverview',
'reports': 'reports',
'ai-insights': 'aiInsights',
'performance-metrics': 'performanceMetrics',
};
return articleMap[articleId] || articleId;
};
const handleArticleClick = (sectionId: string, articleId: string) => {
setSelectedArticle({ sectionId, articleId });
window.scrollTo({ top: 0, behavior: 'smooth' });
};
const handleBackToList = () => {
setSelectedArticle(null);
};
const renderArticleContent = () => {
if (!selectedArticle) return null;
const categoryKey = getCategoryKey(selectedArticle.sectionId);
const articleKey = getArticleKey(selectedArticle.articleId);
const contentPath = `articles.${categoryKey}.${articleKey}.content`;
const content: any = t(contentPath, { returnObjects: true });
const article = sections
.find((s) => s.id === selectedArticle.sectionId)
?.articles.find((a) => a.id === selectedArticle.articleId);
if (!article || !content) return null;
return (
<div>
{/* Back Button */}
<button
onClick={handleBackToList}
className="mb-6 flex items-center gap-2 text-[var(--color-primary)] hover:underline"
>
<ChevronRight className="w-5 h-5 transform rotate-180" />
<span>{t('docs.backToHelp')}</span>
</button>
{/* Article Header */}
<div className="mb-8 pb-8 border-b border-[var(--border-primary)]">
<h1 className="text-4xl font-extrabold text-[var(--text-primary)] mb-4">
{article.title}
</h1>
<p className="text-xl text-[var(--text-secondary)] mb-4">{article.description}</p>
<div className="flex items-center gap-4 text-sm">
<span className="flex items-center gap-1 text-[var(--text-tertiary)]">
<PlayCircle className="w-4 h-4" />
{article.readTime}
</span>
<span className={`font-medium ${getDifficultyColor(article.difficulty)}`}>
{getDifficultyLabel(article.difficulty)}
</span>
</div>
</div>
{/* Article Content */}
<div className="prose prose-lg dark:prose-invert max-w-none">
{/* Intro */}
{content.intro && (
<div className="bg-[var(--color-primary)]/5 border-l-4 border-[var(--color-primary)] p-6 rounded-r-xl mb-8">
<p className="text-[var(--text-primary)] text-lg leading-relaxed m-0">
{content.intro}
</p>
</div>
)}
{/* Steps */}
{content.steps && Array.isArray(content.steps) && (
<div className="space-y-6 mb-8">
{content.steps.map((step: any, index: number) => (
<div
key={index}
className="bg-[var(--bg-secondary)] rounded-xl p-6 border border-[var(--border-primary)]"
>
<h3 className="text-xl font-bold text-[var(--text-primary)] mb-3 mt-0">
{step.title}
</h3>
<p className="text-[var(--text-secondary)] leading-relaxed m-0">
{step.description}
</p>
</div>
))}
</div>
)}
{/* Tips */}
{content.tips && Array.isArray(content.tips) && content.tips.length > 0 && (
<div className="bg-amber-50 dark:bg-amber-900/20 border-2 border-amber-200 dark:border-amber-800 rounded-xl p-6 mb-8">
<h3 className="text-xl font-bold text-[var(--text-primary)] mb-4 mt-0 flex items-center gap-2">
<Info className="w-6 h-6 text-amber-600" />
<span>Consejos Útiles</span>
</h3>
<ul className="space-y-2 m-0 pl-0 list-none">
{content.tips.map((tip: string, index: number) => (
<li key={index} className="flex items-start gap-3">
<CheckCircle2 className="w-5 h-5 text-amber-600 flex-shrink-0 mt-0.5" />
<span className="text-[var(--text-secondary)]">{tip}</span>
</li>
))}
</ul>
</div>
)}
{/* Conclusion */}
{content.conclusion && (
<div className="bg-green-50 dark:bg-green-900/20 border-l-4 border-green-600 p-6 rounded-r-xl">
<p className="text-[var(--text-primary)] leading-relaxed m-0">
{content.conclusion}
</p>
</div>
)}
</div>
{/* Back to List Button */}
<div className="mt-12 pt-8 border-t border-[var(--border-primary)]">
<button
onClick={handleBackToList}
className="flex items-center gap-2 px-6 py-3 bg-[var(--color-primary)] text-white rounded-xl font-bold hover:shadow-xl transition-all"
>
<ChevronRight className="w-5 h-5 transform rotate-180" />
<span>Volver a la Lista</span>
</button>
</div>
</div>
);
};
return (
<PublicLayout
variant="default"
@@ -349,7 +504,10 @@ const DocumentationPage: React.FC = () => {
{sections.map((section) => (
<button
key={section.id}
onClick={() => setActiveSection(section.id)}
onClick={() => {
setActiveSection(section.id);
setSelectedArticle(null);
}}
className={`w-full flex items-center gap-3 px-4 py-3 rounded-xl text-left transition-all ${
activeSection === section.id
? 'bg-[var(--color-primary)] text-white'
@@ -405,53 +563,57 @@ const DocumentationPage: React.FC = () => {
{/* Main Content */}
<div className="lg:col-span-3">
{activeContent && (
<div>
{/* Section Header */}
<div className="mb-8">
<div className="flex items-center gap-4 mb-4">
<div className="w-12 h-12 bg-[var(--color-primary)]/10 rounded-xl flex items-center justify-center">
<activeContent.icon className="w-6 h-6 text-[var(--color-primary)]" />
</div>
<div>
<h2 className="text-3xl font-extrabold text-[var(--text-primary)]">
{activeContent.title}
</h2>
<p className="text-[var(--text-secondary)]">{activeContent.description}</p>
{selectedArticle ? (
renderArticleContent()
) : (
activeContent && (
<div>
{/* Section Header */}
<div className="mb-8">
<div className="flex items-center gap-4 mb-4">
<div className="w-12 h-12 bg-[var(--color-primary)]/10 rounded-xl flex items-center justify-center">
<activeContent.icon className="w-6 h-6 text-[var(--color-primary)]" />
</div>
<div>
<h2 className="text-3xl font-extrabold text-[var(--text-primary)]">
{activeContent.title}
</h2>
<p className="text-[var(--text-secondary)]">{activeContent.description}</p>
</div>
</div>
</div>
</div>
{/* Articles Grid */}
<div className="space-y-4">
{activeContent.articles.map((article) => (
<a
key={article.id}
href={`#${article.id}`}
className="block bg-[var(--bg-secondary)] rounded-xl p-6 border border-[var(--border-primary)] hover:border-[var(--color-primary)] hover:shadow-xl transition-all group"
>
<div className="flex items-start justify-between gap-4">
<div className="flex-1">
<h3 className="text-xl font-bold text-[var(--text-primary)] mb-2 group-hover:text-[var(--color-primary)] transition-colors">
{article.title}
</h3>
<p className="text-[var(--text-secondary)] mb-4">{article.description}</p>
<div className="flex items-center gap-4 text-sm">
<span className="flex items-center gap-1 text-[var(--text-tertiary)]">
<PlayCircle className="w-4 h-4" />
{article.readTime}
</span>
<span className={`font-medium ${getDifficultyColor(article.difficulty)}`}>
{getDifficultyLabel(article.difficulty)}
</span>
{/* Articles Grid */}
<div className="space-y-4">
{activeContent.articles.map((article) => (
<button
key={article.id}
onClick={() => handleArticleClick(activeSection, article.id)}
className="w-full block bg-[var(--bg-secondary)] rounded-xl p-6 border border-[var(--border-primary)] hover:border-[var(--color-primary)] hover:shadow-xl transition-all group text-left"
>
<div className="flex items-start justify-between gap-4">
<div className="flex-1">
<h3 className="text-xl font-bold text-[var(--text-primary)] mb-2 group-hover:text-[var(--color-primary)] transition-colors">
{article.title}
</h3>
<p className="text-[var(--text-secondary)] mb-4">{article.description}</p>
<div className="flex items-center gap-4 text-sm">
<span className="flex items-center gap-1 text-[var(--text-tertiary)]">
<PlayCircle className="w-4 h-4" />
{article.readTime}
</span>
<span className={`font-medium ${getDifficultyColor(article.difficulty)}`}>
{getDifficultyLabel(article.difficulty)}
</span>
</div>
</div>
<ChevronRight className="w-6 h-6 text-[var(--text-tertiary)] group-hover:text-[var(--color-primary)] transition-colors flex-shrink-0" />
</div>
<ChevronRight className="w-6 h-6 text-[var(--text-tertiary)] group-hover:text-[var(--color-primary)] transition-colors flex-shrink-0" />
</div>
</a>
))}
</button>
))}
</div>
</div>
</div>
)
)}
</div>
</div>