@ai-growth/nextjs
Version:
Seamlessly integrate Sanity CMS with Next.js applications for automated blog routing and rendering
112 lines (111 loc) • 5.92 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import React from 'react';
import { ContentHeader } from './ContentHeader';
import { ContentBody } from './ContentBody';
import { AuthorInfo } from './AuthorInfo';
import { LoadingSkeleton } from './LoadingSkeleton';
import styles from './DefaultTemplate.module.css';
/**
* DefaultTemplate component for rendering CMS content
*
* This is the main template component that provides a default layout
* for rendering CMS content with header, body, and author sections.
*/
export const DefaultTemplate = ({ content, className = '', showMetadata = true, showAuthor = true, customRenderers, isLoading = false, error, onRetry, }) => {
// Show loading skeleton
if (isLoading) {
return (_jsx(LoadingSkeleton, { showAuthor: showAuthor, showMetadata: showMetadata, className: className }));
}
// Show error state
if (error) {
return (_jsx("div", { className: `${styles.defaultTemplate} ${styles.error} ${className}`, children: _jsxs("div", { className: styles.errorContainer, children: [_jsx("h2", { className: styles.errorTitle, children: "Content Error" }), _jsx("p", { className: styles.errorMessage, children: error }), _jsx("div", { className: styles.errorActions, children: _jsx("button", { className: styles.retryButton, onClick: () => onRetry ? onRetry() : window.location.reload(), type: "button", children: "Retry" }) })] }) }));
}
// Show content not available state
if (!content) {
return (_jsx("div", { className: `${styles.defaultTemplate} ${className}`, children: _jsxs("div", { className: styles.errorContainer, children: [_jsx("h2", { className: styles.errorTitle, children: "Content Not Available" }), _jsx("p", { className: styles.errorMessage, children: "The requested content could not be found or is not available." })] }) }));
}
// Extract content properties
const { title = 'Untitled', _type: contentType = 'page', publishedAt, metadata, author, content: contentBody, } = content;
return (_jsxs("article", { className: `${styles.defaultTemplate} ${className}`, children: [customRenderers?.header ? (customRenderers.header(content)) : (_jsx(ContentHeader, { title: title, contentType: contentType, ...(publishedAt && { publishedAt }), metadata: metadata, showMetadata: showMetadata })), customRenderers?.body ? (customRenderers.body(contentBody)) : (_jsx(ContentBody, { content: contentBody, contentType: contentType, ...(customRenderers?.body && { customRenderer: customRenderers.body }) })), showAuthor && author && (_jsx(_Fragment, { children: customRenderers?.author ? (customRenderers.author(author)) : (_jsx(AuthorInfo, { author: author, showBio: true, showImage: true })) })), !showMetadata && metadata && customRenderers?.metadata && (_jsx("div", { className: styles.metadataSection, children: customRenderers.metadata(metadata) })), metadata && (_jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: {
__html: JSON.stringify(generateStructuredData(content)),
} }))] }));
};
/**
* Generate structured data for SEO
*/
const generateStructuredData = (content) => {
const { title, _type: contentType, publishedAt, metadata, author, slug, _id, } = content;
const baseData = {
'@context': 'https://schema.org',
'@type': contentType === 'post' ? 'BlogPosting' : 'WebPage',
headline: title,
name: title,
identifier: _id,
};
// Add description if available
if (metadata?.description) {
Object.assign(baseData, {
description: metadata.description,
});
}
// Add publication date for posts
if (publishedAt && contentType === 'post') {
Object.assign(baseData, {
datePublished: publishedAt,
dateModified: publishedAt,
});
}
// Add author information
if (author) {
const authorName = typeof author === 'string' ? author : author.name;
if (authorName) {
Object.assign(baseData, {
author: {
'@type': 'Person',
name: authorName,
},
});
}
}
// Add URL if slug is available
if (slug) {
Object.assign(baseData, {
url: `${window.location.origin}/${slug}`,
});
}
// Add image if available in metadata
if (metadata?.image) {
Object.assign(baseData, {
image: metadata.image,
});
}
return baseData;
};
/**
* Error Boundary wrapper for the DefaultTemplate
*/
export class DefaultTemplateErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
console.error('DefaultTemplate Error:', error, errorInfo);
}
render() {
if (this.state.hasError) {
if (this.props.fallback) {
return this.props.fallback;
}
return (_jsx("div", { className: styles.defaultTemplate, children: _jsxs("div", { className: styles.errorContainer, children: [_jsx("h2", { className: styles.errorTitle, children: "Rendering Error" }), _jsx("p", { className: styles.errorMessage, children: "There was an error rendering this content. Please try refreshing the page." }), _jsx("div", { className: styles.errorActions, children: _jsx("button", { className: styles.retryButton, onClick: () => {
this.setState({ hasError: false });
window.location.reload();
}, type: "button", children: "Retry" }) })] }) }));
}
return this.props.children;
}
}
export default DefaultTemplate;