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