slideshare/src/components/themes/LayoutDetailPage.tsx
Michael Mainguy b4b61ad761 Consolidate button components and eliminate barrel exports
## Button Component System
- Create dedicated components/ui/buttons directory with 6 specialized button components:
  - Button.tsx: Universal button with variants (primary, secondary, danger, link) and sizes
  - ActionButton.tsx: Action buttons matching existing patterns
  - BackButton.tsx: Navigation back buttons
  - CancelLink.tsx: Cancel/link style buttons
  - CloseButton.tsx: Modal/preview close buttons with variants
  - NavigationButton.tsx: Presentation navigation buttons
- Update key components to use new button system (SlideEditor, ContentEditor, Modal, AlertDialog, ConfirmDialog)
- Replace inline styled-jsx with proper CSS files for AlertDialog and ConfirmDialog

## Barrel Export Elimination
- Remove all barrel export files violating IMPORT_STANDARDS.md:
  - src/themes/index.ts
  - src/components/themes/index.ts
  - src/components/presentations/index.ts
  - src/components/slide-editor/index.ts
- Update 15+ files to use direct imports from themeLoader.ts instead of barrel exports
- Fix function naming conflict in ThemeDetailPage.tsx (loadTheme shadowing)
- Follow project standards: direct imports with .tsx extensions for better Vite performance

## Benefits
- Improved Vite tree shaking and module resolution performance
- Consistent, reusable button system across application
- Adherence to project coding standards and import conventions
- Reduced bundle size through elimination of barrel export overhead

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-21 11:42:42 -05:00

193 lines
6.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useEffect } from 'react';
import { useParams, Link } from 'react-router-dom';
import type { Theme, SlideLayout } from '../../types/theme.ts';
import { loadTheme } from '../../utils/themeLoader.ts';
import './LayoutDetailPage.css';
export const LayoutDetailPage: React.FC = () => {
const { themeId, layoutId } = useParams<{ themeId: string; layoutId: string }>();
const [theme, setTheme] = useState<Theme | null>(null);
const [layout, setLayout] = useState<SlideLayout | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [viewMode, setViewMode] = useState<'template' | 'slots'>('slots');
useEffect(() => {
const loadThemeAndLayout = async () => {
if (!themeId || !layoutId) {
setError('Missing theme ID or layout ID');
setLoading(false);
return;
}
try {
setLoading(true);
const themeData = await loadTheme(themeId, false);
if (!themeData) {
setError(`Theme "${themeId}" not found`);
return;
}
const layoutData = themeData.layouts.find((l: SlideLayout) => l.id === layoutId);
if (!layoutData) {
setError(`Layout "${layoutId}" not found in theme "${themeId}"`);
return;
}
setTheme(themeData);
setLayout(layoutData);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to load theme and layout');
} finally {
setLoading(false);
}
};
loadThemeAndLayout();
}, [themeId, layoutId]);
if (loading) {
return (
<div className="layout-detail-page">
<div className="loading-spinner">Loading layout...</div>
</div>
);
}
if (error) {
return (
<div className="layout-detail-page">
<div className="error-message">
<h2>Error</h2>
<p>{error}</p>
<Link to="/themes" className="back-link"> Back to Themes</Link>
</div>
</div>
);
}
if (!theme || !layout) {
return (
<div className="layout-detail-page">
<div className="not-found">
<h2>Layout Not Found</h2>
<p>The requested layout could not be found.</p>
<Link to="/themes" className="back-link"> Back to Themes</Link>
</div>
</div>
);
}
return (
<div className="layout-detail-page">
<header className="layout-detail-header">
<nav className="breadcrumb">
<Link to="/themes">Themes</Link>
<span className="separator"></span>
<Link to={`/themes/${theme.id}`}>{theme.name}</Link>
<span className="separator"></span>
<span className="current">{layout.name}</span>
</nav>
<div className="layout-info">
<h1 className="layout-name">{layout.name}</h1>
<p className="layout-description">{layout.description}</p>
<div className="layout-stats">
<span className="slot-count">{layout.slots.length} slots</span>
<span className="theme-name">from {theme.name}</span>
<Link
to={`/themes/${theme.id}/layouts/${layout.id}/preview`}
className="preview-link"
>
View Full Preview
</Link>
</div>
</div>
</header>
<div className="view-mode-selector">
<button
type="button"
className={`mode-button ${viewMode === 'slots' ? 'active' : ''}`}
onClick={() => setViewMode('slots')}
>
Slots
</button>
<button
type="button"
className={`mode-button ${viewMode === 'template' ? 'active' : ''}`}
onClick={() => setViewMode('template')}
>
Template
</button>
</div>
<main className="layout-content">
{viewMode === 'template' && (
<section className="template-section">
<h2>HTML Template</h2>
<div className="template-code">
<pre><code>{layout.htmlTemplate}</code></pre>
</div>
</section>
)}
{viewMode === 'slots' && (
<section className="slots-section">
<h2>Slot Configuration</h2>
<div className="slots-grid">
{layout.slots.map((slot) => (
<div key={slot.id} className="slot-card">
<div className="slot-header">
<h3 className="slot-id">{slot.id}</h3>
<span className={`slot-type ${slot.type}`}>{slot.type}</span>
{slot.required && <span className="required-badge">Required</span>}
</div>
<div className="slot-details">
{slot.placeholder && (
<div className="slot-field">
<strong>Placeholder:</strong> {slot.placeholder}
</div>
)}
{slot.defaultContent && (
<div className="slot-field">
<strong>Default Content:</strong> {slot.defaultContent}
</div>
)}
{slot.className && (
<div className="slot-field">
<strong>CSS Class:</strong> <code>{slot.className}</code>
</div>
)}
{slot.style && (
<div className="slot-field">
<strong>Inline Style:</strong> <code>{slot.style}</code>
</div>
)}
{slot.attributes && Object.keys(slot.attributes).length > 0 && (
<div className="slot-field">
<strong>Attributes:</strong>
<div className="attributes-list">
{Object.entries(slot.attributes).map(([key, value]) => (
<div key={key} className="attribute-item">
<code>{key}="{value}"</code>
</div>
))}
</div>
</div>
)}
</div>
</div>
))}
</div>
</section>
)}
</main>
</div>
);
};