diff --git a/app/api/posts/route.ts b/app/api/posts/route.ts new file mode 100644 index 0000000..910bca2 --- /dev/null +++ b/app/api/posts/route.ts @@ -0,0 +1,56 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { createClient } from 'tinacms/dist/client'; + +const POSTS_QUERY = ` + query PostConnection($last: Float) { + postConnection(sort: "date", last: $last) { + totalCount + edges { + cursor + node { + id + title + excerpt + heroImg + category + date + _sys { + breadcrumbs + } + } + } + } + } +`; + +export async function GET(request: NextRequest) { + const searchParams = request.nextUrl.searchParams; + const numPosts = parseInt(searchParams.get('numPosts') || '9', 10); + const branch = searchParams.get('branch') || process.env.NEXT_PUBLIC_TINA_BRANCH || 'main'; + const clientId = process.env.NEXT_PUBLIC_TINA_CLIENT_ID; + const token = process.env.TINA_TOKEN; + + try { + // Create a Tina client configured for the correct branch (following Tina custom queries pattern) + const client = createClient({ + url: `https://content.tinajs.io/content/${clientId}/github/${branch}`, + token: token || '', + queries: () => ({}), + }); + + // Use client.request for inline queries as per Tina documentation + const result = await client.request({ + query: POSTS_QUERY, + variables: { last: numPosts }, + }, {}); + + // console.log('Tina result:', JSON.stringify(result, null, 2)); + + // The client.request returns { data: {...} }, so we need to check the structure + const data = result?.data || result; + return NextResponse.json(data); + } catch (error) { + console.error('Error fetching posts:', error); + return NextResponse.json({ error: 'Failed to fetch posts' }, { status: 500 }); + } +} diff --git a/components/blocks/top-posts.tsx b/components/blocks/top-posts.tsx index 6ba19fd..4589bf4 100644 --- a/components/blocks/top-posts.tsx +++ b/components/blocks/top-posts.tsx @@ -30,28 +30,6 @@ interface PostsData { }; } -const POSTS_QUERY = ` - query PostConnection($last: Float) { - postConnection(sort: "date", last: $last) { - totalCount - edges { - cursor - node { - id - title - excerpt - heroImg - category - date - _sys { - breadcrumbs - } - } - } - } - } -`; - export default function LatestPosts({ numPosts = 9 }: { numPosts?: number }) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); @@ -59,23 +37,10 @@ export default function LatestPosts({ numPosts = 9 }: { numPosts?: number }) { useEffect(() => { async function fetchPosts() { try { - console.log("📢 Fetching latest posts via GraphQL..."); + console.log("📢 Fetching latest posts..."); const branch = process.env.NEXT_PUBLIC_TINA_BRANCH || 'main'; - const clientId = process.env.NEXT_PUBLIC_TINA_CLIENT_ID; - const response = await fetch( - `https://content.tinajs.io/content/${clientId}/github/${branch}`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query: POSTS_QUERY, - variables: { last: numPosts }, - }), - } - ); + const response = await fetch(`/api/posts?numPosts=${numPosts}&branch=${branch}`); if (!response.ok) { throw new Error('Failed to fetch posts'); @@ -83,8 +48,8 @@ export default function LatestPosts({ numPosts = 9 }: { numPosts?: number }) { const result = await response.json(); - if (result.data) { - setData(result.data); + if (result && !result.error) { + setData(result); } } catch (error) { console.error("❌ Error fetching latest posts:", error); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 81913e0..1c4f897 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -846,6 +846,10 @@ packages: resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==} engines: {node: '>=6.9.0'} + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} + '@babel/template@7.25.0': resolution: {integrity: sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==} engines: {node: '>=6.9.0'} @@ -897,8 +901,8 @@ packages: '@codemirror/state@6.5.2': resolution: {integrity: sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==} - '@codemirror/view@6.38.0': - resolution: {integrity: sha512-yvSchUwHOdupXkd7xJ0ob36jdsSR/I+/C+VbY0ffBiL5NiSTEBDfB1ZGWbbIlDd5xgdUkody+lukAdOxYrOBeg==} + '@codemirror/view@6.38.8': + resolution: {integrity: sha512-XcE9fcnkHCbWkjeKyi0lllwXmBLtyYb5dt89dJyx23I9+LSh5vZDIuk7OLG4VM1lgrXZQcY6cxyZyk5WVPRv/A==} '@csstools/selector-resolve-nested@3.0.0': resolution: {integrity: sha512-ZoK24Yku6VJU1gS79a5PFmC8yn3wIapiKmPgun0hZgEI5AOqgH2kiPRsPz1qkGv4HL+wuDLH83yQyk6inMYrJQ==} @@ -1400,6 +1404,12 @@ packages: peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@graphql-tools/utils@10.11.0': + resolution: {integrity: sha512-iBFR9GXIs0gCD+yc3hoNswViL1O5josI33dUqiNStFI/MHLCEPduasceAcazRH77YONKNiviHBV8f7OgcT4o2Q==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@graphql-tools/utils@10.8.6': resolution: {integrity: sha512-Alc9Vyg0oOsGhRapfL3xvqh1zV8nKoFUdtLhXX7Ki4nClaIJXckrA86j+uxEuG3ic6j4jlM1nvcWXRn/71AVLQ==} engines: {node: '>=16.0.0'} @@ -1512,14 +1522,14 @@ packages: '@juggle/resize-observer@3.4.0': resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} - '@lezer/common@1.2.3': - resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} + '@lezer/common@1.4.0': + resolution: {integrity: sha512-DVeMRoGrgn/k45oQNu189BoW4SZwgZFzJ1+1TV5j2NJ/KFC83oa/enRqZSGshyeMk5cPWMhsKs9nx+8o0unwGg==} - '@lezer/highlight@1.2.1': - resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==} + '@lezer/highlight@1.2.3': + resolution: {integrity: sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==} - '@lezer/lr@1.4.2': - resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==} + '@lezer/lr@1.4.4': + resolution: {integrity: sha512-LHL17Mq0OcFXm1pGQssuGTQFPPdxARjKM8f7GA5+sGtHi0K3R84YaSbmche0+RKWHnCsx9asEe5OWOI4FHfe4A==} '@lit-labs/ssr-dom-shim@1.3.0': resolution: {integrity: sha512-nQIWonJ6eFAvUUrSlwyHDm/aE8PBDu5kRpL0vHMg6K8fK3Diq1xdPjTnsJSwxABhaZ+5eBi1btQB5ShUTKo4nQ==} @@ -2486,9 +2496,15 @@ packages: '@types/prop-types@15.7.14': resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==} + '@types/prop-types@15.7.15': + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + '@types/react@18.3.18': resolution: {integrity: sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==} + '@types/react@18.3.27': + resolution: {integrity: sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==} + '@types/styled-components@5.1.34': resolution: {integrity: sha512-mmiVvwpYklFIv9E8qfxuPyIt/OuyIrn6gMOAMOFUO3WJfSrSE+sGUoa4PiZj77Ut7bKZpaa6o1fBKS/4TOEvnA==} @@ -3134,11 +3150,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001700: - resolution: {integrity: sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==} - - caniuse-lite@1.0.30001727: - resolution: {integrity: sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==} + caniuse-lite@1.0.30001757: + resolution: {integrity: sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==} capital-case@1.0.4: resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} @@ -3382,6 +3395,9 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + cytoscape-cose-bilkent@4.1.0: resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==} peerDependencies: @@ -6266,8 +6282,8 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - style-mod@4.1.2: - resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==} + style-mod@4.1.3: + resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==} style-value-types@5.0.0: resolution: {integrity: sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==} @@ -7853,6 +7869,8 @@ snapshots: '@babel/runtime@7.27.6': {} + '@babel/runtime@7.28.4': {} + '@babel/template@7.25.0': dependencies: '@babel/code-frame': 7.26.2 @@ -7924,21 +7942,21 @@ snapshots: '@codemirror/language@6.0.0': dependencies: '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.0 - '@lezer/common': 1.2.3 - '@lezer/highlight': 1.2.1 - '@lezer/lr': 1.4.2 - style-mod: 4.1.2 + '@codemirror/view': 6.38.8 + '@lezer/common': 1.4.0 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.4 + style-mod: 4.1.3 '@codemirror/state@6.5.2': dependencies: '@marijn/find-cluster-break': 1.0.2 - '@codemirror/view@6.38.0': + '@codemirror/view@6.38.8': dependencies: '@codemirror/state': 6.5.2 crelt: 1.0.6 - style-mod: 4.1.2 + style-mod: 4.1.3 w3c-keyname: 2.2.8 '@csstools/selector-resolve-nested@3.0.0(postcss-selector-parser@7.0.0)': @@ -8260,7 +8278,7 @@ snapshots: '@graphql-codegen/plugin-helpers@6.1.0(graphql@15.8.0)': dependencies: - '@graphql-tools/utils': 10.8.6(graphql@15.8.0) + '@graphql-tools/utils': 10.11.0(graphql@15.8.0) change-case-all: 1.0.15 common-tags: 1.8.2 graphql: 15.8.0 @@ -8378,14 +8396,14 @@ snapshots: '@graphql-tools/optimize@2.0.0(graphql@15.8.0)': dependencies: graphql: 15.8.0 - tslib: 2.8.1 + tslib: 2.6.3 '@graphql-tools/relay-operation-optimizer@7.0.19(graphql@15.8.0)': dependencies: '@ardatan/relay-compiler': 12.0.3(graphql@15.8.0) '@graphql-tools/utils': 10.8.6(graphql@15.8.0) graphql: 15.8.0 - tslib: 2.8.1 + tslib: 2.6.3 transitivePeerDependencies: - encoding @@ -8397,6 +8415,14 @@ snapshots: tslib: 2.8.1 value-or-promise: 1.0.12 + '@graphql-tools/utils@10.11.0(graphql@15.8.0)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@15.8.0) + '@whatwg-node/promise-helpers': 1.3.2 + cross-inspect: 1.0.1 + graphql: 15.8.0 + tslib: 2.6.3 + '@graphql-tools/utils@10.8.6(graphql@15.8.0)': dependencies: '@graphql-typed-document-node/core': 3.2.0(graphql@15.8.0) @@ -8404,7 +8430,7 @@ snapshots: cross-inspect: 1.0.1 dset: 3.1.4 graphql: 15.8.0 - tslib: 2.8.1 + tslib: 2.6.3 '@graphql-tools/utils@9.2.1(graphql@15.8.0)': dependencies: @@ -8523,15 +8549,15 @@ snapshots: '@juggle/resize-observer@3.4.0': {} - '@lezer/common@1.2.3': {} + '@lezer/common@1.4.0': {} - '@lezer/highlight@1.2.1': + '@lezer/highlight@1.2.3': dependencies: - '@lezer/common': 1.2.3 + '@lezer/common': 1.4.0 - '@lezer/lr@1.4.2': + '@lezer/lr@1.4.4': dependencies: - '@lezer/common': 1.2.3 + '@lezer/common': 1.4.0 '@lit-labs/ssr-dom-shim@1.3.0': {} @@ -9829,7 +9855,7 @@ snapshots: '@types/hoist-non-react-statics@3.3.6': dependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.27 hoist-non-react-statics: 3.3.2 optional: true @@ -9857,11 +9883,20 @@ snapshots: '@types/prop-types@15.7.14': {} + '@types/prop-types@15.7.15': + optional: true + '@types/react@18.3.18': dependencies: '@types/prop-types': 15.7.14 csstype: 3.1.3 + '@types/react@18.3.27': + dependencies: + '@types/prop-types': 15.7.15 + csstype: 3.2.3 + optional: true + '@types/styled-components@5.1.34': dependencies: '@types/hoist-non-react-statics': 3.3.5 @@ -10285,7 +10320,7 @@ snapshots: '@whatwg-node/promise-helpers@1.3.2': dependencies: - tslib: 2.8.1 + tslib: 2.6.3 '@xobotyi/scrollbar-width@1.9.5': {} @@ -10459,7 +10494,7 @@ snapshots: autoprefixer@10.4.20(postcss@8.5.1): dependencies: browserslist: 4.23.3 - caniuse-lite: 1.0.30001700 + caniuse-lite: 1.0.30001757 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.0.1 @@ -10566,14 +10601,14 @@ snapshots: browserslist@4.23.3: dependencies: - caniuse-lite: 1.0.30001700 + caniuse-lite: 1.0.30001757 electron-to-chromium: 1.5.5 node-releases: 2.0.18 update-browserslist-db: 1.1.0(browserslist@4.23.3) browserslist@4.25.1: dependencies: - caniuse-lite: 1.0.30001727 + caniuse-lite: 1.0.30001757 electron-to-chromium: 1.5.180 node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.25.1) @@ -10628,20 +10663,18 @@ snapshots: camel-case@4.1.2: dependencies: pascal-case: 3.1.2 - tslib: 2.8.1 + tslib: 2.6.3 camelcase-css@2.0.1: {} camelcase@6.3.0: {} - caniuse-lite@1.0.30001700: {} - - caniuse-lite@1.0.30001727: {} + caniuse-lite@1.0.30001757: {} capital-case@1.0.4: dependencies: no-case: 3.0.4 - tslib: 2.8.1 + tslib: 2.6.3 upper-case-first: 2.0.2 catering@2.1.1: {} @@ -10685,7 +10718,7 @@ snapshots: path-case: 3.0.4 sentence-case: 3.0.4 snake-case: 3.0.4 - tslib: 2.8.1 + tslib: 2.6.3 character-entities-html4@2.1.0: {} @@ -10801,7 +10834,7 @@ snapshots: constant-case@3.0.4: dependencies: no-case: 3.0.4 - tslib: 2.8.1 + tslib: 2.6.3 upper-case: 2.0.2 content-disposition@0.5.4: @@ -10867,7 +10900,7 @@ snapshots: cross-inspect@1.0.1: dependencies: - tslib: 2.8.1 + tslib: 2.6.3 cross-spawn@7.0.3: dependencies: @@ -10914,6 +10947,9 @@ snapshots: csstype@3.1.3: {} + csstype@3.2.3: + optional: true + cytoscape-cose-bilkent@4.1.0(cytoscape@3.30.4): dependencies: cose-base: 1.0.3 @@ -12118,7 +12154,7 @@ snapshots: header-case@2.0.4: dependencies: capital-case: 1.0.4 - tslib: 2.8.1 + tslib: 2.6.3 hey-listen@1.0.8: {} @@ -12289,7 +12325,7 @@ snapshots: is-lower-case@2.0.2: dependencies: - tslib: 2.8.1 + tslib: 2.6.3 is-map@2.0.3: {} @@ -12346,7 +12382,7 @@ snapshots: is-upper-case@2.0.2: dependencies: - tslib: 2.8.1 + tslib: 2.6.3 is-weakmap@2.0.2: {} @@ -12623,11 +12659,11 @@ snapshots: lower-case-first@2.0.2: dependencies: - tslib: 2.8.1 + tslib: 2.6.3 lower-case@2.0.2: dependencies: - tslib: 2.8.1 + tslib: 2.6.3 lru-cache@10.0.0: {} @@ -13319,7 +13355,7 @@ snapshots: '@next/env': 14.2.24 '@swc/helpers': 0.5.5 busboy: 1.6.0 - caniuse-lite: 1.0.30001700 + caniuse-lite: 1.0.30001757 graceful-fs: 4.2.11 postcss: 8.4.31 react: 18.3.1 @@ -13344,7 +13380,7 @@ snapshots: no-case@3.0.4: dependencies: lower-case: 2.0.2 - tslib: 2.8.1 + tslib: 2.6.3 node-abi@3.75.0: dependencies: @@ -13474,7 +13510,7 @@ snapshots: param-case@3.0.4: dependencies: dot-case: 3.0.4 - tslib: 2.8.1 + tslib: 2.6.3 parent-module@1.0.1: dependencies: @@ -13509,14 +13545,14 @@ snapshots: pascal-case@3.1.2: dependencies: no-case: 3.0.4 - tslib: 2.8.1 + tslib: 2.6.3 path-browserify@1.0.1: {} path-case@3.0.4: dependencies: dot-case: 3.0.4 - tslib: 2.8.1 + tslib: 2.6.3 path-data-parser@0.1.0: {} @@ -13938,7 +13974,7 @@ snapshots: redux@4.2.1: dependencies: - '@babel/runtime': 7.27.6 + '@babel/runtime': 7.28.4 reflect.getprototypeof@1.0.6: dependencies: @@ -14180,7 +14216,7 @@ snapshots: sentence-case@3.0.4: dependencies: no-case: 3.0.4 - tslib: 2.8.1 + tslib: 2.6.3 upper-case-first: 2.0.2 serve-static@1.16.2: @@ -14345,7 +14381,7 @@ snapshots: sponge-case@1.0.1: dependencies: - tslib: 2.8.1 + tslib: 2.6.3 sprintf-js@1.0.3: {} @@ -14474,7 +14510,7 @@ snapshots: strip-json-comments@3.1.1: {} - style-mod@4.1.2: {} + style-mod@4.1.3: {} style-value-types@5.0.0: dependencies: @@ -14533,7 +14569,7 @@ snapshots: swap-case@2.0.2: dependencies: - tslib: 2.8.1 + tslib: 2.6.3 tabbable@6.2.0: {} @@ -14830,7 +14866,7 @@ snapshots: title-case@3.0.3: dependencies: - tslib: 2.8.1 + tslib: 2.6.3 to-buffer@1.2.1: dependencies: @@ -15083,11 +15119,11 @@ snapshots: upper-case-first@2.0.2: dependencies: - tslib: 2.8.1 + tslib: 2.6.3 upper-case@2.0.2: dependencies: - tslib: 2.8.1 + tslib: 2.6.3 uri-js@4.4.1: dependencies: