UNPKG

@ai-growth/nextjs

Version:

Seamlessly integrate Sanity CMS with Next.js applications for automated blog routing and rendering

164 lines (163 loc) 6.45 kB
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime"; import { useEffect, useState, useCallback } from 'react'; import { useRouter } from 'next/router'; import { DefaultTemplate } from './DefaultTemplate'; import { validateContentRoute } from '../utils/cms-content'; import { isValidCmsRoute } from '../utils/route-config'; /** * CMS Route Handler for Next.js App Router * * This component handles CMS routing by checking if the current route is a CMS route, * fetching content accordingly, and rendering the appropriate template or children. */ export const CmsRouteHandler = ({ children, customTemplate, loadingComponent, errorComponent, contentOptions = {}, className, showLoading = true, showErrors = true, onRouteChange, onContentLoaded, onContentError, }) => { const router = useRouter(); const [state, setState] = useState({ isCmsRoute: false, isLoading: false, currentSlug: '', content: null, error: null, }); // Fetch content for CMS routes const fetchContent = useCallback(async (path) => { setState(prev => ({ ...prev, isLoading: true, error: null })); try { const result = await validateContentRoute(path, contentOptions); if (result.isValid && result.content) { setState(prev => ({ ...prev, isLoading: false, content: result.content || null, error: null, })); onContentLoaded?.(result.content); } else { const errorMessage = result.error || 'Content not found'; setState(prev => ({ ...prev, isLoading: false, content: null, error: errorMessage, })); onContentError?.(errorMessage); } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; setState(prev => ({ ...prev, isLoading: false, content: null, error: errorMessage, })); onContentError?.(errorMessage); } }, [contentOptions, onContentLoaded, onContentError]); // Handle route changes useEffect(() => { if (!router.isReady) return; const currentPath = router.asPath; const isCms = isValidCmsRoute(currentPath); // Avoid unnecessary updates if (state.currentSlug === currentPath && state.isCmsRoute === isCms) { return; } setState(prev => ({ ...prev, isCmsRoute: isCms, currentSlug: currentPath, })); onRouteChange?.(currentPath, isCms); if (isCms) { fetchContent(currentPath); } }, [router.isReady, router.asPath, state.currentSlug, state.isCmsRoute, onRouteChange]); // Separate effect for fetchContent to avoid dependency issues useEffect(() => { if (state.isCmsRoute && state.currentSlug && !state.isLoading && !state.content && !state.error) { fetchContent(state.currentSlug); } }, [state.isCmsRoute, state.currentSlug, state.isLoading, state.content, state.error, fetchContent]); // Retry function const handleRetry = useCallback(() => { if (state.currentSlug) { fetchContent(state.currentSlug); } }, [state.currentSlug, fetchContent]); // If router is not ready, render children if (!router.isReady) { return _jsx(_Fragment, { children: children }); } // If not a CMS route, render children if (!state.isCmsRoute) { return _jsx(_Fragment, { children: children }); } // CMS route handling const TemplateComponent = customTemplate || DefaultTemplate; // Show loading state if (state.isLoading && showLoading) { if (loadingComponent) { return _jsx("div", { className: className, children: loadingComponent }); } return (_jsx("div", { className: className, children: _jsx(TemplateComponent, { content: null, isLoading: true, error: "", onRetry: handleRetry }) })); } // Show error state if (state.error && showErrors) { if (errorComponent) { const ErrorComponent = errorComponent; return (_jsx("div", { className: className, children: _jsx(ErrorComponent, { error: state.error, onRetry: handleRetry }) })); } return (_jsx("div", { className: className, children: _jsx(TemplateComponent, { content: null, isLoading: false, error: state.error, onRetry: handleRetry }) })); } // Show content if (state.content) { return (_jsx("div", { className: className, children: _jsx(TemplateComponent, { content: state.content, isLoading: false, error: "", onRetry: handleRetry }) })); } // Fallback: render children if no CMS content and not showing errors if (!showErrors) { return _jsx(_Fragment, { children: children }); } // Default fallback return (_jsx("div", { className: className, children: _jsx(TemplateComponent, { content: null, isLoading: false, error: "", onRetry: handleRetry }) })); }; // Hooks para compatibilidade com testes existentes export const useCmsRouteState = () => { const router = useRouter(); const [state, setState] = useState({ isCmsRoute: false, isLoading: false, currentSlug: '', content: null, error: null, }); useEffect(() => { if (!router.isReady) return; const currentPath = router.asPath; const isCms = isValidCmsRoute(currentPath); setState(prev => ({ ...prev, isCmsRoute: isCms, currentSlug: currentPath, })); }, [router.isReady, router.asPath]); return state; }; export const useIsCmsRoute = () => { const router = useRouter(); const [isCmsRoute, setIsCmsRoute] = useState(false); useEffect(() => { if (!router.isReady) { setIsCmsRoute(false); return; } const currentPath = router.asPath; const isCms = isValidCmsRoute(currentPath); setIsCmsRoute(isCms); }, [router.isReady, router.asPath]); return isCmsRoute; }; export default CmsRouteHandler;