UNPKG

@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
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;