diff --git a/lib/notion.js b/lib/notion.js index a2f19e88..51c02180 100644 --- a/lib/notion.js +++ b/lib/notion.js @@ -1,4 +1,3 @@ export { getAllPosts } from './notion/getAllPosts' export { getAllTags } from './notion/getAllTags' export { getPostBlocks } from './notion/getPostBlocks' -export { getAllCategories } from './notion/getAllCategories' diff --git a/lib/notion/getAllCategories.js b/lib/notion/getAllCategories.js deleted file mode 100644 index b9a543d8..00000000 --- a/lib/notion/getAllCategories.js +++ /dev/null @@ -1,40 +0,0 @@ -import { isIterable } from '../utils' - -/** - * 获取所有文章的分类 - * @param allPosts - * @returns {Promise<{}|*[]>} - */ -export async function getAllCategories({ allPosts, categoryOptions, sliceCount = 0 }) { - if (!allPosts || !categoryOptions) { - return [] - } - // 计数 - let categories = allPosts.map(p => p.category) - categories = [...categories.flat()] - const categoryObj = {} - categories.forEach(category => { - if (category in categoryObj) { - categoryObj[category]++ - } else { - categoryObj[category] = 1 - } - }) - const list = [] - if (isIterable(categoryOptions)) { - for (const c of categoryOptions) { - const count = categoryObj[c.value] - if (count) { - list.push({ id: c.id, name: c.value, color: c.color, count }) - } - } - } - - // 按照数量排序 - // list.sort((a, b) => b.count - a.count) - if (sliceCount && sliceCount > 0) { - return list.slice(0, sliceCount) - } else { - return list - } -} diff --git a/lib/notion/getAllPosts.js b/lib/notion/getAllPosts.js index fbe588da..47198789 100644 --- a/lib/notion/getAllPosts.js +++ b/lib/notion/getAllPosts.js @@ -23,7 +23,7 @@ export async function getAllPosts({ notionPageData, from, pageType }) { const pageBlock = notionPageData.block const schema = notionPageData.schema - const tagOptions = notionPageData.tagOptions + const tagOptions = notionPageData.tags const collectionQuery = notionPageData.collectionQuery const data = [] @@ -73,19 +73,3 @@ function getPostCover(id, block) { if (pageCover.startsWith('http')) return defaultMapImageUrl(pageCover, block[id].value) } } - -/** - * 获取博文总数 - * @param {*} param0 - * @returns - */ -export async function getAllPostCount({ notionPageData, from }) { - if (!notionPageData) { - notionPageData = await getNotionPageData({ from }) - } - if (!notionPageData) { - return [] - } - const allPosts = await getAllPosts({ notionPageData, from, pageType: ['Post'] }) - return allPosts?.length || 0 -} diff --git a/lib/notion/getAllTags.js b/lib/notion/getAllTags.js index 174055d1..33e82f51 100644 --- a/lib/notion/getAllTags.js +++ b/lib/notion/getAllTags.js @@ -7,7 +7,7 @@ import { isIterable } from '../utils' * @param tagOptions tags的下拉选项 * @returns {Promise<{}|*[]>} */ -export async function getAllTags({ allPosts, sliceCount = 0, tagOptions }) { +export function getAllTags({ allPosts, sliceCount = 0, tagOptions }) { if (!allPosts || !tagOptions) { return [] } diff --git a/lib/notion/getNotionData.js b/lib/notion/getNotionData.js index 55a3d031..118ed16b 100644 --- a/lib/notion/getNotionData.js +++ b/lib/notion/getNotionData.js @@ -3,9 +3,11 @@ import { getDataFromCache, setDataToCache } from '@/lib/cache/cache_manager' import { getPostBlocks } from '@/lib/notion/getPostBlocks' import { idToUuid } from 'notion-utils' import { defaultMapImageUrl } from 'react-notion-x' -import { getAllCategories } from './getAllCategories' -import { getAllPosts, getAllPostCount } from './getAllPosts' +import { deepClone, isIterable } from '../utils' +import getAllPageIds from './getAllPageIds' +import { getAllPosts } from './getAllPosts' import { getAllTags } from './getAllTags' +import getPageProperties from './getPageProperties' /** * 获取博客数据 @@ -15,40 +17,29 @@ import { getAllTags } from './getAllTags' * @param categoryCount * @param tagsCount 截取标签数量 * @param pageType 过滤的文章类型,数组格式 ['Page','Post'] - * @returns { - allPosts, 所有博客 - latestPosts, - categories, 所有分类 - postCount, - customNav, 自定义导航菜单 - tags, 所有标签 - siteInfo:{ - title, - description, - pageCover - } - } + * @returns * */ export async function getGlobalNotionData({ pageId = BLOG.NOTION_PAGE_ID, from, - latestPostCount = 5, - categoryCount = BLOG.PREVIEW_CATEGORY_COUNT, - tagsCount = BLOG.PREVIEW_TAG_COUNT, pageType = ['Post'] }) { - const notionPageData = await getNotionPageData({ pageId, from }) - const siteInfo = await getBlogInfo({ notionPageData, from }) - const tagOptions = notionPageData.tagOptions - const categoryOptions = notionPageData.categoryOptions - const customNav = await getCustomNav({ notionPageData }) + // 深拷贝数据 + const notionPageData = deepClone(await getNotionPageData({ pageId, from })) + const allPosts = await getAllPosts({ notionPageData, from, pageType }) - const postCount = await getAllPostCount({ notionPageData, from }) - const categories = await getAllCategories({ allPosts, categoryOptions, sliceCount: categoryCount }) - const tags = await getAllTags({ allPosts, tagOptions, sliceCount: tagsCount }) - const latestPosts = await getLatestPosts({ notionPageData, from, latestPostCount }) - return { allPosts, latestPosts, categories, postCount, customNav, tags, siteInfo } + notionPageData.allPosts = allPosts + // 删除前端不需要的数据 + delete notionPageData.block + delete notionPageData.collection + delete notionPageData.collectionQuery + delete notionPageData.schema + delete notionPageData.rawMetadata + delete notionPageData.pageIds + delete notionPageData.tagOptions + delete notionPageData.categoryOptions + return notionPageData } /** @@ -56,8 +47,7 @@ export async function getGlobalNotionData({ * @param {*}} param0 * @returns */ -async function getLatestPosts({ notionPageData, from, latestPostCount }) { - const allPosts = await getAllPosts({ notionPageData, from, pageType: ['Post'] }) +function getLatestPosts({ allPosts, from, latestPostCount }) { const latestPosts = Object.create(allPosts).sort((a, b) => { const dateA = new Date(a?.lastEditedTime || a?.createdTime || a?.date?.start_date) const dateB = new Date(b?.lastEditedTime || b?.createdTime || b?.date?.start_date) @@ -79,7 +69,7 @@ export async function getNotionPageData({ pageId, from }) { // 尝试从缓存获取 const cacheKey = 'page_block_' + pageId const data = await getDataFromCache(cacheKey) - if (data) { + if (data && data.pageIds?.length > 0) { console.log('[请求缓存]:', `from:${from}`, `root-page-id:${pageId}`) return data } @@ -96,14 +86,7 @@ export async function getNotionPageData({ pageId, from }) { * @param notionPageData * @returns {Promise<[]|*[]>} */ -async function getCustomNav({ notionPageData }) { - if (!notionPageData) { - notionPageData = await getNotionPageData({ from: 'custom-nav' }) - } - if (!notionPageData) { - return [] - } - const allPage = await getAllPosts({ notionPageData, from: 'custom-nav', pageType: ['Page'] }) +function getCustomNav({ allPage }) { const customNav = [] if (allPage && allPage.length > 0) { allPage.forEach(p => { @@ -139,23 +122,55 @@ function getCategoryOptions(schema) { return categorySchema?.options || [] } +/** + * 获取所有文章的分类 + * @param allPosts + * @returns {Promise<{}|*[]>} + */ +function getAllCategories({ allPosts, categoryOptions, sliceCount = 0 }) { + if (!allPosts || !categoryOptions) { + return [] + } + // 计数 + let categories = allPosts.map(p => p.category) + categories = [...categories.flat()] + const categoryObj = {} + categories.forEach(category => { + if (category in categoryObj) { + categoryObj[category]++ + } else { + categoryObj[category] = 1 + } + }) + const list = [] + if (isIterable(categoryOptions)) { + for (const c of categoryOptions) { + const count = categoryObj[c.value] + if (count) { + list.push({ id: c.id, name: c.value, color: c.color, count }) + } + } + } + + // 按照数量排序 + // list.sort((a, b) => b.count - a.count) + if (sliceCount && sliceCount > 0) { + return list.slice(0, sliceCount) + } else { + return list + } +} + /** * 站点信息 * @param notionPageData * @param from * @returns {Promise<{title,description,pageCover}>} */ -async function getBlogInfo({ notionPageData, from }) { - if (!notionPageData) { - notionPageData = await getNotionPageData({ from }) - } - if (!notionPageData) { - return null - } - const collection = notionPageData?.collection +function getBlogInfo(collection, block) { const title = collection?.name?.[0][0] || BLOG.TITLE const description = collection?.description?.[0][0] || BLOG.DESCRIPTION - const pageCover = mapCoverUrl(collection?.cover, notionPageData.block) + const pageCover = mapCoverUrl(collection?.cover, block) return { title, description, pageCover } } @@ -184,23 +199,47 @@ async function getPageRecordMapByNotionAPI({ pageId, from }) { return [] } pageId = idToUuid(pageId) - const collection = Object.values(pageRecordMap.collection)[0]?.value - const collectionQuery = pageRecordMap.collection_query const block = pageRecordMap.block - const schema = collection?.schema - const rawMetadata = block[pageId].value - const tagOptions = getTagOptions(schema) - const categoryOptions = getCategoryOptions(schema) - + const rawMetadata = block[pageId]?.value // Check Type Page-Database和Inline-Database if ( rawMetadata?.type !== 'collection_view_page' && - rawMetadata?.type !== 'collection_view' + rawMetadata?.type !== 'collection_view' ) { console.warn(`pageId "${pageId}" is not a database`) return null } + const collection = Object.values(pageRecordMap.collection)[0]?.value + const collectionQuery = pageRecordMap.collection_query + const schema = collection?.schema + const tagOptions = getTagOptions(schema) + const categoryOptions = getCategoryOptions(schema) + + const data = [] + const pageIds = getAllPageIds(collectionQuery) + for (let i = 0; i < pageIds.length; i++) { + const id = pageIds[i] + const properties = (await getPageProperties(id, block, schema)) || null + properties.slug = properties.slug ?? properties.id + delete properties.content + data.push(properties) + } + + const allPage = data.filter(post => { + return post.title && post?.status?.[0] === 'Published' && ['Page'].indexOf(post?.type?.[0]) > -1 + }) + const allPosts = data.filter(post => { + return post.title && post?.status?.[0] === 'Published' && ['Post'].indexOf(post?.type?.[0]) > -1 + }) + + const customNav = getCustomNav({ allPage }) + const postCount = allPosts?.length || 0 + const categories = getAllCategories({ allPosts, categoryOptions, sliceCount: BLOG.PREVIEW_CATEGORY_COUNT }) + const tags = getAllTags({ allPosts, tagOptions, sliceCount: BLOG.PREVIEW_TAG_COUNT }) + const latestPosts = getLatestPosts({ allPosts, from, latestPostCount: 5 }) + const siteInfo = getBlogInfo({ collection, block }) + return { collection, collectionQuery, @@ -208,6 +247,13 @@ async function getPageRecordMapByNotionAPI({ pageId, from }) { schema, tagOptions, categoryOptions, - rawMetadata + rawMetadata, + siteInfo, + customNav, + postCount, + pageIds, + categories, + tags, + latestPosts } } diff --git a/lib/notion/getPostBlocks.js b/lib/notion/getPostBlocks.js index 38f003b4..1f58d94f 100644 --- a/lib/notion/getPostBlocks.js +++ b/lib/notion/getPostBlocks.js @@ -1,28 +1,19 @@ import BLOG from '@/blog.config' import { NotionAPI } from 'notion-client' import { getDataFromCache, setDataToCache } from '@/lib/cache/cache_manager' +import { deepClone, delay } from '../utils' -export async function getPostBlocks(id, from, slice, retryCount = 3) { +export async function getPostBlocks(id, from, slice) { const cacheKey = 'page_block_' + id let pageBlock = await getDataFromCache(cacheKey) if (pageBlock) { - console.log('[请求缓存]:', `from:${from}`, `id:${id}`, cacheKey) + console.log('[请求缓存]:', `from:${from}`, cacheKey) return filterPostBlocks(id, pageBlock, slice) } - const authToken = BLOG.NOTION_ACCESS_TOKEN || null - const api = new NotionAPI({ authToken }) - try { - console.warn('[请求API]:', `from:${from}`, `id:${id}`) - pageBlock = await api.getPage(id) - console.warn('[请求成功]', `from:${from}`, `id:${id}`) - } catch (error) { - console.error('[请求失败]', `from:${from}`, `id:${id}`, `error:${error}`) - if (retryCount && retryCount > 0) { // 重试 - console.error('[重试请求]', `from:${from}`, `id:${id}`, `剩余次数:${retryCount}`) - return getPostBlocks(id, from, slice, retryCount - 1) - } - return null - } + + console.warn('[请求API]:', `from:${from}`, `id:${id}`) + pageBlock = await getPageWithRetry(id, from) + console.warn('[请求完成]: 结果', `${pageBlock ? '成功' : '失败'}`, `from:${from}`, `id:${id}`) if (pageBlock) { await setDataToCache(cacheKey, pageBlock) @@ -31,6 +22,33 @@ export async function getPostBlocks(id, from, slice, retryCount = 3) { return pageBlock } +/** + * 调用接口,失败会重试 + * @param {*} id + * @param {*} retryAttempts + */ +async function getPageWithRetry(id, from, retryAttempts = 3) { + if (retryAttempts && retryAttempts > 0) { + console.error('[发起请求]', `from:${from}`, `id:${id}`, `剩余重试次数:${retryAttempts}`) + try { + const authToken = BLOG.NOTION_ACCESS_TOKEN || null + const api = new NotionAPI({ authToken }) + return await api.getPage(id) + } catch (e) { + await delay(1000) + const cacheKey = 'page_block_' + id + const pageBlock = await getDataFromCache(cacheKey) + if (pageBlock) { + console.error('[重试获取缓存]', `from:${from}`, `id:${id}`) + return pageBlock + } + return await getPageWithRetry(id, from, retryAttempts - 1) + } + } else { + return null + } +} + /** * 获取到的blockMap删除不需要的字段 * @param {*} id 页面ID @@ -74,15 +92,3 @@ function filterPostBlocks(id, pageBlock, slice) { } return clonePageBlock } - -function deepClone(obj) { - const newObj = Array.isArray(obj) ? [] : {} - if (obj && typeof obj === 'object') { - for (const key in obj) { - if (Object.prototype.hasOwnProperty.call(obj, key)) { - newObj[key] = (obj && typeof obj[key] === 'object') ? deepClone(obj[key]) : obj[key] - } - } - } - return newObj -} diff --git a/lib/utils.js b/lib/utils.js index be9db197..e4b854a7 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -80,3 +80,22 @@ export function isObject(item) { export function isIterable(obj) { return obj != null && typeof obj[Symbol.iterator] === 'function' } + +export function deepClone(obj) { + const newObj = Array.isArray(obj) ? [] : {} + if (obj && typeof obj === 'object') { + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + newObj[key] = (obj && typeof obj[key] === 'object') ? deepClone(obj[key]) : obj[key] + } + } + } + return newObj +} + +/** + * 延时 + * @param {*} ms + * @returns + */ +export const delay = ms => new Promise(resolve => setTimeout(resolve, ms)) diff --git a/pages/article/[slug].js b/pages/article/[slug].js index 66e53de0..40fe2ef2 100644 --- a/pages/article/[slug].js +++ b/pages/article/[slug].js @@ -29,7 +29,7 @@ const Slug = props => { }) } } - }, 5000) + }, 10000) const meta = { title: `${props?.siteInfo?.title || BLOG.TITLE} | loading` } return } diff --git a/pages/feed.js b/pages/feed.js index ccfe5ff4..127f9402 100644 --- a/pages/feed.js +++ b/pages/feed.js @@ -4,7 +4,7 @@ import { getGlobalNotionData } from '@/lib/notion/getNotionData' export async function getServerSideProps ({ res }) { res.setHeader('Content-Type', 'text/xml') // 获取最新文章 - const globalNotionData = await getGlobalNotionData({ from: 'rss', latestPostCount: 5 }) + const globalNotionData = await getGlobalNotionData({ from: 'rss' }) const xmlFeed = await generateRss(globalNotionData?.latestPosts || []) res.write(xmlFeed) res.end() diff --git a/pages/index.js b/pages/index.js index ec18b11c..fe8de2cb 100644 --- a/pages/index.js +++ b/pages/index.js @@ -16,7 +16,7 @@ export async function getStaticProps() { const meta = { title: `${siteInfo?.title} | ${siteInfo?.description}`, description: siteInfo?.description, - image: siteInfo.pageCover, + image: siteInfo?.pageCover, slug: '', type: 'website' } diff --git a/pages/tag/[tag].js b/pages/tag/[tag].js index b5f36c0a..be89d2fa 100644 --- a/pages/tag/[tag].js +++ b/pages/tag/[tag].js @@ -53,7 +53,7 @@ function getTagNames(tags) { export async function getStaticPaths() { const from = 'tag-static-path' - const { tags } = await getGlobalNotionData({ from, tagsCount: 0 }) + const { tags } = await getGlobalNotionData({ from }) const tagNames = getTagNames(tags) return { diff --git a/pages/tag/index.js b/pages/tag/index.js index a4c28dbf..302511dc 100644 --- a/pages/tag/index.js +++ b/pages/tag/index.js @@ -19,7 +19,7 @@ const TagIndex = props => { export async function getStaticProps() { const from = 'tag-index-props' - const props = await getGlobalNotionData({ from, tagsCount: 0 }) + const props = await getGlobalNotionData({ from }) return { props, revalidate: 1 diff --git a/themes/example/LayoutArchive.js b/themes/example/LayoutArchive.js index a7657b12..1b3d1e34 100644 --- a/themes/example/LayoutArchive.js +++ b/themes/example/LayoutArchive.js @@ -24,7 +24,7 @@ export const LayoutArchive = props => { }) return ( -
+
{Object.keys(archivePosts).map(archiveTitle => (
diff --git a/themes/example/LayoutBase.js b/themes/example/LayoutBase.js index eb39ffd1..1f357ad8 100644 --- a/themes/example/LayoutBase.js +++ b/themes/example/LayoutBase.js @@ -28,9 +28,9 @@ const LayoutBase = props => { - <div className="container max-w-4xl mx-auto md:flex items-start py-8 px-2"> + <div className="container mx-auto justify-center md:flex items-start py-8 px-2"> - <div className='w-full flex-grow'>{children}</div> + <div className='w-full max-w-3xl xl:px-14 lg:px-4 '>{children}</div> <SideBar {...props} /> diff --git a/themes/example/LayoutCategoryIndex.js b/themes/example/LayoutCategoryIndex.js index 77a3fcc1..7690b316 100644 --- a/themes/example/LayoutCategoryIndex.js +++ b/themes/example/LayoutCategoryIndex.js @@ -1,15 +1,11 @@ -import { useGlobal } from '@/lib/global' import Link from 'next/link' import LayoutBase from './LayoutBase' export const LayoutCategoryIndex = (props) => { const { categories } = props - const { locale } = useGlobal() + return <LayoutBase {...props}> - <div className=' p-10 w-full'> - <div className='dark:text-gray-200 mb-5'> - <i className='mr-4 fas fa-th' />{locale.COMMON.CATEGORY}: - </div> + <div > <div id='category-list' className='duration-200 flex flex-wrap'> {categories && categories.map(category => { return <Link key={category.name} href={`/category/${category.name}`} passHref> diff --git a/themes/example/LayoutSearch.js b/themes/example/LayoutSearch.js index 528c7976..c79c3ed0 100644 --- a/themes/example/LayoutSearch.js +++ b/themes/example/LayoutSearch.js @@ -1,12 +1,15 @@ import BLOG from '@/blog.config' import { useGlobal } from '@/lib/global' import Link from 'next/link' +import { useRouter } from 'next/router' import { useEffect, useState } from 'react' import SearchInput from './components/SearchInput' import LayoutBase from './LayoutBase' export const LayoutSearch = props => { const { keyword, posts } = props + const router = useRouter() + useEffect(() => { setTimeout(() => { const container = typeof document !== 'undefined' && document.getElementById('container') @@ -15,7 +18,7 @@ export const LayoutSearch = props => { container.innerHTML = container.innerHTML.replace(re, `<span class='text-red-500 border-b border-dashed'>${keyword}</span>`) } }, 100) - }) + }, [router.events]) const { locale } = useGlobal() @@ -51,7 +54,7 @@ export const LayoutSearch = props => { }, []) return <LayoutBase {...props}> - <div className='py-2'> + <div className='pb-12'> <SearchInput {...props} /> </div> diff --git a/themes/example/LayoutTagIndex.js b/themes/example/LayoutTagIndex.js index 249e1639..6a89e168 100644 --- a/themes/example/LayoutTagIndex.js +++ b/themes/example/LayoutTagIndex.js @@ -4,7 +4,7 @@ import LayoutBase from './LayoutBase' export const LayoutTagIndex = (props) => { const { tags } = props return <LayoutBase {...props}> - <div className='p-10'> + <div> <div id='tags-list' className='duration-200 flex flex-wrap'> {tags.map(tag => { return <div key={tag.name} className='p-2'> diff --git a/themes/example/components/ArticleInfo.js b/themes/example/components/ArticleInfo.js index 7e92e254..f7dc5a80 100644 --- a/themes/example/components/ArticleInfo.js +++ b/themes/example/components/ArticleInfo.js @@ -34,14 +34,13 @@ export const ArticleInfo = (props) => { {locale.COMMON.LAST_EDITED_TIME}: {post.lastEditedTime} </span> <span className='mr-2'>|</span> - - </>)} - - <span className="hidden busuanzi_container_page_pv font-light mr-2"> + <span className="hidden busuanzi_container_page_pv font-light mr-2"> <i className='mr-1 fas fa-eye' />   <span className="mr-2 busuanzi_value_page_pv" /> </span> + </>)} + </div> </section> diff --git a/themes/example/components/BlogList.js b/themes/example/components/BlogList.js index bb924fdf..7e2b49f0 100644 --- a/themes/example/components/BlogList.js +++ b/themes/example/components/BlogList.js @@ -39,6 +39,14 @@ export const BlogList = (props) => { <p className="text-gray-700 leading-normal"> {p.summary} </p> + {/* 搜索结果 */} + {p.results && ( + <p className="mt-4 text-gray-700 dark:text-gray-300 text-sm font-light leading-7"> + {p.results.map(r => ( + <span key={r}>{r}</span> + ))} + </p> + )} </article> ))} diff --git a/themes/hexo/components/Header.js b/themes/hexo/components/Header.js index 66770f77..c1e4e71f 100644 --- a/themes/hexo/components/Header.js +++ b/themes/hexo/components/Header.js @@ -103,7 +103,7 @@ const Header = props => { className="duration-500 md:bg-fixed w-full bg-cover bg-center h-screen bg-black text-white" style={{ backgroundImage: - `linear-gradient(rgba(0, 0, 0, 0.8), rgba(0,0,0,0.2), rgba(0, 0, 0, 0.8) ),url("${siteInfo.pageCover}")` + `linear-gradient(rgba(0, 0, 0, 0.8), rgba(0,0,0,0.2), rgba(0, 0, 0, 0.8) ),url("${siteInfo?.pageCover}")` }} > <div className="absolute flex flex-col h-full items-center justify-center w-full font-sans"> diff --git a/themes/hexo/components/HeaderArticle.js b/themes/hexo/components/HeaderArticle.js index 191184cb..8513419d 100644 --- a/themes/hexo/components/HeaderArticle.js +++ b/themes/hexo/components/HeaderArticle.js @@ -41,6 +41,9 @@ export default function HeaderArticle({ post, siteInfo }) { if (!isDarkMode) { const stickyNavElement = document.getElementById('sticky-nav') const header = document.querySelector('#header') + if (!header || !stickyNavElement) { + return + } if (window.scrollY < header.clientHeight) { stickyNavElement?.classList?.add('dark') } else { diff --git a/themes/next/components/Header.js b/themes/next/components/Header.js index 37eb5e50..df6db180 100644 --- a/themes/next/components/Header.js +++ b/themes/next/components/Header.js @@ -98,7 +98,7 @@ export default function Header(props) { className="duration-500 md:bg-fixed w-full bg-cover bg-center h-screen bg-black" style={{ backgroundImage: - `linear-gradient(rgba(0, 0, 0, 0.8), rgba(0,0,0,0.2), rgba(0, 0, 0, 0.8) ),url("${siteInfo.pageCover}")` + `linear-gradient(rgba(0, 0, 0, 0.8), rgba(0,0,0,0.2), rgba(0, 0, 0, 0.8) ),url("${siteInfo?.pageCover}")` }} > <div className="absolute flex h-full items-center lg:-mt-14 justify-center w-full text-4xl md:text-7xl text-white">