diff --git a/bun/index.ts b/bun/index.ts index 07e72d8..6d6af8a 100644 --- a/bun/index.ts +++ b/bun/index.ts @@ -126,23 +126,60 @@ const server = Bun.serve({ return Response.json(data[0]); }, "/api/posts/:slug": async req => { - const res = await connection.run(` - SELECT - * - FROM wp_posts - WHERE post_name = $slug - AND post_status = 'publish' - AND post_type IN ('post', 'page') - LIMIT 1; - `, { slug: req.params.slug.toString() }); + // First query: Get the post + const postRes = await connection.run(` + SELECT + * + FROM wp_posts + WHERE post_name = $slug + AND post_status = 'publish' + AND post_type IN ('post', 'page') + LIMIT 1; + `, { slug: req.params.slug.toString() }); - const data = await res.getRowObjectsJson() + const postData = await postRes.getRowObjectsJson(); - if (data.length === 0) { + if (postData.length === 0) { return Response.json(null, { status: 404 }); } - return Response.json(data[0], { status: 200 }); + const post = postData[0]; + + if (post && post.ID) { + // Second query: Get all postmeta for this post + const metaRes = await connection.run(` + SELECT + meta_id, + meta_key, + meta_value + FROM wp_postmeta + WHERE post_id = $postId; + `, { postId: post.ID.toString() }); + + const metaData = await metaRes.getRowObjectsJson(); + + // Process thumbnail synchronously + for (const meta of metaData) { + if (meta?.meta_key === '_thumbnail_id' && meta.meta_value) { + const thumbRes = await connection.run(` + SELECT + * + FROM wp_posts + WHERE ID = $post_id AND post_type = 'attachment'; + `, { post_id: meta.meta_value.toString() }); + + const thumbData = await thumbRes.getRowObjectsJson(); + post.thumbnail = thumbData[0]; + break; // Once we find the thumbnail, we can stop looking + } + } + + // Add the meta data to the post object as an array + post.wp_postmeta = metaData; + } + + + return Response.json(post, { status: 200 }); }, "/api/pages/:slug": async req => { const res = await connection.run(` @@ -180,15 +217,15 @@ const server = Bun.serve({ }, "/api/home": async req => { const res = await connection.run(` - SELECT p.* - FROM wp_posts p - WHERE p.ID = ( - SELECT option_value - FROM wp_options - WHERE option_name = 'page_on_front' - LIMIT 1 - ) - LIMIT 1;`); + SELECT p.* + FROM wp_posts p + WHERE p.ID = ( + SELECT option_value + FROM wp_options + WHERE option_name = 'page_on_front' + LIMIT 1 + ) + LIMIT 1;`); const data = await res.getRowObjectsJson() diff --git a/public/jkhsakha.duckdb b/jkhsakha.duckdb similarity index 100% rename from public/jkhsakha.duckdb rename to jkhsakha.duckdb diff --git a/next.config.ts b/next.config.ts index ba65465..cfbd3d0 100644 --- a/next.config.ts +++ b/next.config.ts @@ -2,6 +2,13 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { /* config options here */ + serverExternalPackages: [ + "@duckdb/node-api", + "duckdb", + "duckdb-async", + // "@duckdb/node-bindings-darwin-arm64", // Include platform-specific bindings + // "@duckdb/node-bindings-linux-x64" // Include platform-specific bindings + ], reactCompiler: true, reactStrictMode: true, }; diff --git a/package-lock.json b/package-lock.json index 52b8089..4cb6350 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,20 +25,27 @@ "@tiptap/pm": "^3.19.0", "@tiptap/react": "^3.19.0", "@tiptap/starter-kit": "^3.19.0", + "@types/dompurify": "^3.0.5", "@wordpress/block-serialization-default-parser": "^5.39.0", "axios": "^1.13.2", + "clsx": "^2.1.1", "daisyui": "^5.5.14", + "dompurify": "^3.3.1", "embla-carousel-auto-scroll": "^8.6.0", "embla-carousel-autoplay": "^8.6.0", "embla-carousel-react": "^8.6.0", "html-react-parser": "^5.2.17", "interweave": "^13.1.1", + "jsdom": "^28.1.0", "next": "16.1.3", "next-themes": "^0.4.6", + "node-html-parser": "^7.1.0", "react": "19.2.3", "react-dom": "19.2.3", + "react-hook-form": "^7.71.2", "react-resizable-panels": "^4.6.4", - "sass": "^1.97.3" + "sass": "^1.97.3", + "tailwind-merge": "^3.5.0" }, "devDependencies": { "@tailwindcss/postcss": "^4", @@ -57,6 +64,12 @@ "@duckdb/node-api": "^1.4.3-r.3" } }, + "node_modules/@acemir/cssom": { + "version": "0.9.31", + "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.31.tgz", + "integrity": "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==", + "license": "MIT" + }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -70,6 +83,59 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@asamuzakjp/css-color": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.0.1.tgz", + "integrity": "sha512-2SZFvqMyvboVV1d15lMf7XiI3m7SDqXUuKaTymJYLN6dSGadqp+fVojqJlVoMlbZnlTmu3S0TLwLTJpvBMO1Aw==", + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^3.1.1", + "@csstools/css-color-parser": "^4.0.2", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0", + "lru-cache": "^11.2.6" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", + "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.8.1.tgz", + "integrity": "sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==", + "license": "MIT", + "dependencies": { + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.1.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.6" + } + }, + "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", + "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "license": "MIT" + }, "node_modules/@babel/code-frame": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", @@ -310,6 +376,144 @@ "node": ">=6.9.0" } }, + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.0" + }, + "bin": { + "specificity": "bin/cli.js" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", + "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.1.1.tgz", + "integrity": "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.0.2.tgz", + "integrity": "sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.0.2", + "@csstools/css-calc": "^3.1.1" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.0.29", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.29.tgz", + "integrity": "sha512-jx9GjkkP5YHuTmko2eWAvpPnb0mB4mGRr2U7XwVNwevm8nlpobZEVk+GNmiYMk2VuA75v+plfXWyroWKmICZXg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0" + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, "node_modules/@dnd-kit/abstract": { "version": "0.1.21", "resolved": "https://registry.npmjs.org/@dnd-kit/abstract/-/abstract-0.1.21.tgz", @@ -641,6 +845,23 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@exodus/bytes": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.14.1.tgz", + "integrity": "sha512-OhkBFWI6GcRMUroChZiopRiSp2iAMvEBK47NhJooDqz1RERO4QuZIZnjP63TXX8GAiLABkYmX+fuQsdJ1dd2QQ==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, "node_modules/@floating-ui/core": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.4.tgz", @@ -3221,6 +3442,15 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/dompurify": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", + "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", + "license": "MIT", + "dependencies": { + "@types/trusted-types": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -3301,6 +3531,12 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" + }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -3900,6 +4136,15 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -4217,6 +4462,21 @@ "baseline-browser-mapping": "dist/cli.js" } }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -4392,6 +4652,15 @@ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -4459,6 +4728,47 @@ "node": ">= 8" } }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.12.2", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -4472,6 +4782,30 @@ "node": ">=4" } }, + "node_modules/cssstyle": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-6.2.0.tgz", + "integrity": "sha512-Fm5NvhYathRnXNVndkUsCCuR63DCLVVwGOOwQw782coXFi5HhkXdu289l59HlXZBawsyNccXfWRYvLzcDCdDig==", + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^5.0.1", + "@csstools/css-syntax-patches-for-csstree": "^1.0.28", + "css-tree": "^3.1.0", + "lru-cache": "^11.2.6" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cssstyle/node_modules/lru-cache": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", + "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", @@ -4494,6 +4828,28 @@ "dev": true, "license": "BSD-2-Clause" }, + "node_modules/data-urls": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/data-urls/node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -4552,7 +4908,6 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -4566,6 +4921,12 @@ } } }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "license": "MIT" + }, "node_modules/deep-diff": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-1.0.2.tgz", @@ -4731,6 +5092,15 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz", + "integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/domutils": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", @@ -5972,6 +6342,15 @@ "node": ">= 0.4" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, "node_modules/hermes-estree": { "version": "0.25.1", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", @@ -6015,6 +6394,18 @@ "htmlparser2": "10.1.0" } }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, "node_modules/html-react-parser": { "version": "5.2.17", "resolved": "https://registry.npmjs.org/html-react-parser/-/html-react-parser-5.2.17.tgz", @@ -6077,6 +6468,32 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -6446,6 +6863,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "license": "MIT" + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -6653,6 +7076,67 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdom": { + "version": "28.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-28.1.0.tgz", + "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", + "license": "MIT", + "dependencies": { + "@acemir/cssom": "^0.9.31", + "@asamuzakjp/dom-selector": "^6.8.1", + "@bramus/specificity": "^2.4.2", + "@exodus/bytes": "^1.11.0", + "cssstyle": "^6.0.1", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "parse5": "^8.0.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.0", + "undici": "^7.21.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", + "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/jsdom/node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -7146,6 +7630,12 @@ "node": ">= 0.4" } }, + "node_modules/mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "license": "CC0-1.0" + }, "node_modules/mdurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", @@ -7224,7 +7714,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/nanoid": { @@ -7366,6 +7855,16 @@ "license": "MIT", "optional": true }, + "node_modules/node-html-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-7.1.0.tgz", + "integrity": "sha512-iJo8b2uYGT40Y8BTyy5ufL6IVbN8rbm/1QK2xffXU/1a/v3AAa0d1YAoqBNYqaS4R/HajkWIpIfdE6KcyFh1AQ==", + "license": "MIT", + "dependencies": { + "css-select": "^5.1.0", + "he": "1.2.0" + } + }, "node_modules/node-releases": { "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", @@ -7373,6 +7872,18 @@ "dev": true, "license": "MIT" }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -7932,7 +8443,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -7989,6 +8499,22 @@ "react": "^19.2.3" } }, + "node_modules/react-hook-form": { + "version": "7.71.2", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.71.2.tgz", + "integrity": "sha512-1CHvcDYzuRUNOflt4MOq3ZM46AronNJtQ1S7tnX6YN4y72qhgiUItpacZUAQ0TyWYci3yz1X+rXaSxiuEm86PA==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -8138,6 +8664,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", @@ -8295,6 +8830,18 @@ "@parcel/watcher": "^2.4.1" } }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -8760,6 +9307,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "license": "MIT" + }, + "node_modules/tailwind-merge": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz", + "integrity": "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "4.1.18", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", @@ -8829,6 +9392,24 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tldts": { + "version": "7.0.24", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.24.tgz", + "integrity": "sha512-1r6vQTTt1rUiJkI5vX7KG8PR342Ru/5Oh13kEQP2SMbRSZpOey9SrBe27IDxkoWulx8ShWu4K6C0BkctP8Z1bQ==", + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.24" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.24", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.24.tgz", + "integrity": "sha512-pj7yygNMoMRqG7ML2SDQ0xNIOfN3IBDUcPVM2Sg6hP96oFNN2nqnzHreT3z9xLq85IWJyNTvD38O002DdOrPMw==", + "license": "MIT" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -8842,6 +9423,30 @@ "node": ">=8.0" } }, + "node_modules/tough-cookie": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz", + "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==", + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/ts-api-utils": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", @@ -9041,6 +9646,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici": { + "version": "7.22.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz", + "integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", @@ -9213,6 +9827,27 @@ "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", "license": "MIT" }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, "node_modules/whatwg-mimetype": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", @@ -9222,6 +9857,20 @@ "node": ">=12" } }, + "node_modules/whatwg-url": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -9358,6 +10007,21 @@ } } }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "license": "MIT" + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/package.json b/package.json index 0663076..69a27bf 100644 --- a/package.json +++ b/package.json @@ -26,20 +26,27 @@ "@tiptap/pm": "^3.19.0", "@tiptap/react": "^3.19.0", "@tiptap/starter-kit": "^3.19.0", + "@types/dompurify": "^3.0.5", "@wordpress/block-serialization-default-parser": "^5.39.0", "axios": "^1.13.2", + "clsx": "^2.1.1", "daisyui": "^5.5.14", + "dompurify": "^3.3.1", "embla-carousel-auto-scroll": "^8.6.0", "embla-carousel-autoplay": "^8.6.0", "embla-carousel-react": "^8.6.0", "html-react-parser": "^5.2.17", "interweave": "^13.1.1", + "jsdom": "^28.1.0", "next": "16.1.3", "next-themes": "^0.4.6", + "node-html-parser": "^7.1.0", "react": "19.2.3", "react-dom": "19.2.3", + "react-hook-form": "^7.71.2", "react-resizable-panels": "^4.6.4", - "sass": "^1.97.3" + "sass": "^1.97.3", + "tailwind-merge": "^3.5.0" }, "devDependencies": { "@tailwindcss/postcss": "^4", diff --git a/src/app/(client)/[slug]/page.tsx b/src/app/(client)/[slug]/page.tsx index cf8747c..22cb44b 100644 --- a/src/app/(client)/[slug]/page.tsx +++ b/src/app/(client)/[slug]/page.tsx @@ -13,8 +13,8 @@ export interface PageProps { export default async function PostPage({ params }: PageProps) { const { slug } = await params; - const baseUrl = - process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'; + + const baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'; const res = await fetch(`${baseUrl}/api/posts/${slug}`, { next: { revalidate: 60 }, diff --git a/src/app/(client)/page.tsx b/src/app/(client)/page.tsx index 3c7b541..9ae3554 100644 --- a/src/app/(client)/page.tsx +++ b/src/app/(client)/page.tsx @@ -1,14 +1,34 @@ // app/page.tsx +import Flex from '@/components/Blocks/Flex/Flex'; +import Grid from '@/components/Blocks/Grid/Grid'; +import IFrame from '@/components/Blocks/IFrame/IFrame'; import Section from '@/components/Blocks/Section/Section'; +import PostCard from '@/components/Post/PostCard'; import EmblaCarousel from '@/components/UI/EmblaCarousel/EmblaCarousel'; +import Stat from '@/components/UI/Stat/Stat'; import Areas from '@/components/WP/Areas/Areas'; import { SmartSlider } from '@/components/WPRenderer/SmartSlider'; import { renderPostContent } from '@/components/WPRenderer/WPRenderer'; +import { connection } from '@/lib/duckdb'; +import { transformLinks } from '@/lib/utils'; import { CarouselSlide, PostData } from '@/types/entities'; export const revalidate = 10; export default async function HomePage() { + const duckRes = await connection.run(` + SELECT p.* + FROM wp_posts p + WHERE p.ID = ( + SELECT option_value + FROM wp_options + WHERE option_name = 'page_on_front' + LIMIT 1 + ) + LIMIT 1;`); + + const duckData = await duckRes.getRowObjectsJson() + const baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'; const res = await fetch(`${baseUrl}/api/home`, { next: { revalidate: 10 }, }); @@ -18,7 +38,8 @@ export default async function HomePage() { throw new Error('Failed to fetch home posts'); } - const posts: PostData[] = await res.json(); + //const posts: PostData[] = await res.json(); + const posts = duckData const stats: { title: string, value: string }[] = [ { @@ -206,36 +227,67 @@ export default async function HomePage() { ] return ( -
+
{/* {posts.map(post => (
{renderPostContent(post.post_content)}
))} */} - +
-
+ -
+ + + + + + +
-
-
- -
+ + -
+ + {stats.map((stat, index) => ( -
-
{stat.value}
-
{stat.title}
-
+ ))} -
-
+ +
@@ -245,6 +297,12 @@ export default async function HomePage() {
+ + `} />
); } diff --git a/src/app/(editor)/[slug]/edit/page.tsx b/src/app/(editor)/[slug]/edit/page.tsx index b68832c..36ee70f 100644 --- a/src/app/(editor)/[slug]/edit/page.tsx +++ b/src/app/(editor)/[slug]/edit/page.tsx @@ -1,7 +1,4 @@ import { PageProps } from "@/app/(client)/[slug]/page"; -import PostPage from "@/app/(client)/[slug]/page"; -import ClientLayout from "@/app/(client)/layout"; -import Post from "@/components/Post/Post"; import Tiptap from "@/components/Tiptap/Tiptap"; export default async function EditorPage({ params }: PageProps) { diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 47b784b..fade018 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -5,7 +5,7 @@ export const montserratFont = Montserrat({ subsets: ['latin', 'cyrillic'], }) -const mainFont = Roboto({ +export const mainFont = Roboto({ subsets: ['latin', 'cyrillic'], }) diff --git a/src/app/nothing[...puckPath]/client.tsx b/src/app/nothing[...puckPath]/client.tsx deleted file mode 100644 index cebc191..0000000 --- a/src/app/nothing[...puckPath]/client.tsx +++ /dev/null @@ -1,9 +0,0 @@ -"use client"; - -import type { Data } from "@puckeditor/core"; -import { Render } from "@puckeditor/core"; -import puckConfig from "../../../puck.config"; - -export function Client({ data }: { data: Data }) { - return -} diff --git a/src/app/nothing[...puckPath]/page.tsx b/src/app/nothing[...puckPath]/page.tsx deleted file mode 100644 index 2e58684..0000000 --- a/src/app/nothing[...puckPath]/page.tsx +++ /dev/null @@ -1,50 +0,0 @@ -/** - * This file implements a catch-all route that renders the user-facing pages - * generated by Puck. For any route visited (with exception of other hardcoded - * pages in /app), it will check your database (via `getPage`) for a Puck page - * and render it using . - * - * All routes produced by this page are statically rendered using incremental - * static site generation. After the first visit, the page will be cached as - * a static file. Subsequent visits will receive the cache. Publishing a page - * will invalidate the cache as the page is written in /api/puck/route.ts - */ - -import { Client } from "./client"; -import { notFound } from "next/navigation"; -import { Metadata } from "next"; -import { getPage } from "@/lib/get-page"; - -export async function generateMetadata({ - params, -}: { - params: Promise<{ puckPath: string[] }>; -}): Promise { - const { puckPath = [] } = await params; - const path = `/${puckPath.join("/")}`; - - const data = getPage(path) - return { - title: data?.root.props?.title, - }; -} - -export default async function Page({ - params, -}: { - params: Promise<{ puckPath: string[] }>; -}) { - const { puckPath = [] } = await params; - const path = `/${puckPath.join("/")}`; - const data = await getPage(path); - - if (!data) { - return notFound(); - } - - return ; -} - -// Force Next.js to produce static pages: https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic -// Delete this if you need dynamic rendering, such as access to headers or cookies -export const dynamic = "force-static"; diff --git a/src/app/test/page.tsx b/src/app/test/page.tsx index 54bc61b..8af8ac1 100644 --- a/src/app/test/page.tsx +++ b/src/app/test/page.tsx @@ -1,13 +1,15 @@ // app/page.tsx import Areas from "@/components/WP/Areas/Areas"; +import GetLK from "@/components/WP/GetLK/GetLK"; export const revalidate = 10; export default async function TestPage() { return (
- + {/* */} +
); } diff --git a/src/components/Blocks/Banner/Banner.tsx b/src/components/Blocks/Banner/Banner.tsx index 27ec98b..60b2d5f 100644 --- a/src/components/Blocks/Banner/Banner.tsx +++ b/src/components/Blocks/Banner/Banner.tsx @@ -10,7 +10,7 @@ export default async function Banner({ title, subtitle, backgroundImage, logoIma
-
diff --git a/src/components/Blocks/Featured/Featured.tsx b/src/components/Blocks/Featured/Featured.tsx new file mode 100644 index 0000000..0ce1402 --- /dev/null +++ b/src/components/Blocks/Featured/Featured.tsx @@ -0,0 +1,15 @@ +import React from 'react' + +interface FeaturedProps { + +} + +const Featured = () => { + return ( +
+ +
+ ) +} + +export default Featured \ No newline at end of file diff --git a/src/components/Blocks/Flex/Flex.tsx b/src/components/Blocks/Flex/Flex.tsx new file mode 100644 index 0000000..0f76e68 --- /dev/null +++ b/src/components/Blocks/Flex/Flex.tsx @@ -0,0 +1,41 @@ +import { cn } from '@/utils/tailwind' +import { PropsWithChildren } from 'react' + +type Justify = 'center' | 'between' | 'around' | 'start' | 'end' | 'evenly' | 'stretch' +type Align = 'center' | 'end' | 'start' | 'stretch' +type FlexGap = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 +type FlexDirection = 'col' | 'row' + +export interface FlexProps extends PropsWithChildren { + justify?: Justify + align?: Align + gap?: FlexGap + direction?: FlexDirection + className?: string +} + +const Flex = ({ + children, + justify = 'start', + align = 'start', + gap = 2, + direction = 'row', + className +}: FlexProps) => { + const flexClassName = cn( + 'flex', + direction && `flex-${direction}`, + justify && `justify-${justify}`, + align && `items-${align}`, + gap !== undefined && `gap-${gap}`, + className + ) + + return ( +
+ {children} +
+ ) +} + +export default Flex \ No newline at end of file diff --git a/src/components/Blocks/Grid/Grid.tsx b/src/components/Blocks/Grid/Grid.tsx new file mode 100644 index 0000000..883e986 --- /dev/null +++ b/src/components/Blocks/Grid/Grid.tsx @@ -0,0 +1,54 @@ +import { PropsWithChildren } from 'react' +import { cn } from '@/utils/tailwind' + +type GridRows = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 +type GridCols = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 +type GridGap = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 + +export interface GridProps extends PropsWithChildren { + rows_tablet?: GridRows + rows_mobile?: GridRows + rows_desktop?: GridRows + cols_mobile?: GridCols + cols_tablet?: GridCols + cols_desktop?: GridCols + gap?: GridGap + className?: string +} + +const Grid = ({ + children, + gap = 4, + rows_mobile = 1, + rows_tablet = 1, + rows_desktop = 1, + cols_mobile = 1, + cols_tablet = 1, + cols_desktop = 1, + className +}: GridProps) => { + const gridClassName = cn( + 'grid', + // Cols + cols_mobile && `grid-cols-${cols_mobile}`, + cols_tablet && `md:grid-cols-${cols_tablet}`, + cols_desktop && `lg:grid-cols-${cols_desktop}`, + // Rows + rows_mobile && `grid-rows-${rows_mobile}`, + rows_tablet && `md:grid-rows-${rows_tablet}`, + rows_desktop && `lg:grid-rows-${rows_desktop}`, + // Gap + gap !== undefined && `gap-${gap}`, + // Custom classes + className + ) + + + return ( +
+ {children} +
+ ) +} + +export default Grid \ No newline at end of file diff --git a/src/components/Blocks/IFrame/IFrame.tsx b/src/components/Blocks/IFrame/IFrame.tsx new file mode 100644 index 0000000..df75637 --- /dev/null +++ b/src/components/Blocks/IFrame/IFrame.tsx @@ -0,0 +1,26 @@ +import React from 'react' + +export interface IFrameProps { + iframe: string +} + +const IFrame = ({ + iframe +}: IFrameProps) => { + return ( + <> + +
+ + + ) +} + +export default IFrame \ No newline at end of file diff --git a/src/components/Blocks/Section/Section.tsx b/src/components/Blocks/Section/Section.tsx index 0832f35..593b16c 100644 --- a/src/components/Blocks/Section/Section.tsx +++ b/src/components/Blocks/Section/Section.tsx @@ -1,7 +1,7 @@ import { montserratFont } from '@/app/layout' import React, { PropsWithChildren } from 'react' -interface SectionProps extends PropsWithChildren { +export interface SectionProps extends PropsWithChildren { title?: string } diff --git a/src/components/Post/Post.tsx b/src/components/Post/Post.tsx index 8d0de76..c9c6d20 100644 --- a/src/components/Post/Post.tsx +++ b/src/components/Post/Post.tsx @@ -1,10 +1,60 @@ -import { renderPostContent } from '../WPRenderer/WPRenderer' +import { renderElementorContent, renderPostContent } from '../WPRenderer/WPRenderer' import { PostData } from '@/types/entities' const Post = ({ post }: { post: PostData }) => { + const getHtml = (el: { + id: string + elType: string + elements: any[] + settings: any + }) => { + let htmlString = '' + + if (el.elements.length > 0) { + el.elements.map(child => { + htmlString = htmlString + getHtml(child) + }) + } + + if (el.settings.html) { + htmlString = htmlString + el.settings.html + } + + return htmlString + } + + const preProcessElementor = () => { + let htmlString = '' + + post.wp_postmeta && post.wp_postmeta.map(meta => { + if (meta.meta_key === '_elementor_data') { + const elements = JSON.parse(meta.meta_value) + if (Array.isArray(elements)) { + elements.map((el: { + id: string + elType: string + elements: any[] + settings: any + }) => { + htmlString = htmlString + getHtml(el) + }) + } + } + }) + + return htmlString + } + return ( -
+
+ { + post.thumbnail && +
+ +
+ } +

{post.post_title}

{post.post_type === 'post' && ( @@ -15,6 +65,7 @@ const Post = ({ post }: { post: PostData }) => {
)} + {post.wp_postmeta && post.wp_postmeta.find(meta => meta.meta_key === '_elementor_data') && renderElementorContent(preProcessElementor())} {renderPostContent(post.post_content)}
diff --git a/src/components/Post/PostCard.tsx b/src/components/Post/PostCard.tsx new file mode 100644 index 0000000..2703a60 --- /dev/null +++ b/src/components/Post/PostCard.tsx @@ -0,0 +1,74 @@ +import { mainFont } from '@/app/layout' +import { PostCategory } from '@/types/entities' + +interface PostCardProps { + thumbnail_src?: string + show_thumbnail?: boolean + title: string + show_description?: boolean + description?: string + post_href: string + date?: string + show_date?: boolean + categories?: PostCategory[] + show_categories?: boolean +} + +const PostCard = ({ + title, + show_description = false, + description, + show_thumbnail = false, + thumbnail_src, + post_href, + date, + show_date = false, + categories, + show_categories = false +}: PostCardProps) => { + return ( +
+ {show_thumbnail && thumbnail_src && +
+ + Shoes + + +
+ } + +
+ + {title} + + {description && show_description && +

+ {description} +

+ } +
+ {date && show_date && + {date} + } +
+ {categories && show_categories && + categories.map((cat, index) => ( + + {cat.name} + + )) + } +
+ +
+
+ +
+ + ) +} + +export default PostCard \ No newline at end of file diff --git a/src/components/UI/EmblaCarousel/EmblaCarousel.tsx b/src/components/UI/EmblaCarousel/EmblaCarousel.tsx index 32798bf..bb44509 100644 --- a/src/components/UI/EmblaCarousel/EmblaCarousel.tsx +++ b/src/components/UI/EmblaCarousel/EmblaCarousel.tsx @@ -8,7 +8,7 @@ import { CarouselSlide } from '@/types/entities' import { IconChevronLeft, IconChevronRight } from '@tabler/icons-react' import { montserratFont } from '@/app/layout' -interface EmblaCarouselProps { +export interface EmblaCarouselProps { slides: CarouselSlide[] slides_per_view?: number show_dots?: boolean diff --git a/src/components/UI/Stat/Stat.tsx b/src/components/UI/Stat/Stat.tsx new file mode 100644 index 0000000..00ff9e0 --- /dev/null +++ b/src/components/UI/Stat/Stat.tsx @@ -0,0 +1,20 @@ +import React from 'react' + +interface StatProps { + title: string + value: string +} + +const Stat = ({ + title, + value +}: StatProps) => { + return ( +
+
{value}
+
{title}
+
+ ) +} + +export default Stat \ No newline at end of file diff --git a/src/components/WP/Areas/Areas.tsx b/src/components/WP/Areas/Areas.tsx index f18d6a3..36a67f6 100644 --- a/src/components/WP/Areas/Areas.tsx +++ b/src/components/WP/Areas/Areas.tsx @@ -152,8 +152,8 @@ const Areas = () => { const [tooltip, setTooltip] = useState<{ name: string, x: number, y: number } | null>(null) return ( -
- +
+ {tooltip && { {tooltip?.name} } - + { areas.map((area, index) => ( diff --git a/src/components/WP/GetLK/GetLK.tsx b/src/components/WP/GetLK/GetLK.tsx new file mode 100644 index 0000000..cbf6297 --- /dev/null +++ b/src/components/WP/GetLK/GetLK.tsx @@ -0,0 +1,344 @@ +"use client" + +import { useEffect, useState } from 'react' +import { useForm } from 'react-hook-form' +// curl 'https://api.jkhsakha.ru/lk/address/' \ +// -H 'Accept: application/json' \ +// -H 'Accept-Language: ru,en-US;q=0.9,en;q=0.8,de;q=0.7' \ +// -H 'Authorization: bearer' \ +// -H 'Connection: keep-alive' \ +// -H 'Content-Type: application/json' \ +// -H 'Origin: https://new.jkhsakha.ru' \ +// -H 'Referer: https://new.jkhsakha.ru/' \ +// -H 'Sec-Fetch-Dest: empty' \ +// -H 'Sec-Fetch-Mode: cors' \ +// -H 'Sec-Fetch-Site: same-site' \ +// -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36' \ +// -H 'sec-ch-ua: "Not:A-Brand";v="99", "Google Chrome";v="145", "Chromium";v="145"' \ +// -H 'sec-ch-ua-mobile: ?0' \ +// -H 'sec-ch-ua-platform: "macOS"' \ +// --data - raw '{"IDPersonalAccount":0,"IDRegion":0,"IDCity":0,"IDStreet":0,"IDHouse":0,"IDFlat":0}' + +interface AddressData { + IDPersonalAccount: number + IDRegion: number + IDCity: number + IDStreet: number + IDHouse: number + IDFlat: number +} + +interface Region { + IDRegion: number + NameRegion: string +} + +interface City { + IDCity: number + NameCity: string +} + +interface Street { + IDStreet: number + NameStreet: string +} + +interface House { + IDHouse: number + NameHouse: string +} + +interface Flat { + IDPersonalAccount: number + NameFlat: string +} + +const API_BASE_URL = 'https://api.jkhsakha.ru/lk/address/' + +const GetLK = () => { + const [regions, setRegions] = useState([]) + const [cities, setCities] = useState([]) + const [streets, setStreets] = useState([]) + const [houses, setHouses] = useState([]) + const [flats, setFlats] = useState([]) + + const [selectedAccount, setSelectedAccount] = useState(null) + + const { register, watch, setValue } = useForm({ + defaultValues: { + IDPersonalAccount: 0, + IDRegion: 0, + IDCity: 0, + IDStreet: 0, + IDHouse: 0, + IDFlat: 0 + } + }) + + const watchRegion = watch('IDRegion') + const watchCity = watch('IDCity') + const watchStreet = watch('IDStreet') + const watchHouse = watch('IDHouse') + const watchFlat = watch('IDFlat') + + // Fetch regions on component mount + useEffect(() => { + const fetchRegions = async () => { + try { + const response = await fetch(API_BASE_URL, { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': 'bearer', + }, + body: JSON.stringify({ + IDPersonalAccount: 0, + IDRegion: 0, + IDCity: 0, + IDStreet: 0, + IDHouse: 0, + IDFlat: 0 + }) + }) + const data = await response.json() + setRegions(data) + } catch (error) { + console.error('Error fetching regions:', error) + } + } + + fetchRegions() + }, []) + + // Fetch cities when region is selected + useEffect(() => { + if (watchRegion && watchRegion !== 0) { + const fetchCities = async () => { + try { + const response = await fetch(API_BASE_URL, { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': 'bearer', + }, + body: JSON.stringify({ + IDPersonalAccount: 0, + IDRegion: watchRegion, + IDCity: 0, + IDStreet: 0, + IDHouse: 0, + IDFlat: 0 + }) + }) + const data = await response.json() + setCities(data) + setValue('IDCity', 0) // Reset city selection + setStreets([]) // Clear dependent fields + setHouses([]) + setFlats([]) + } catch (error) { + console.error('Error fetching cities:', error) + } + } + + fetchCities() + } + }, [watchRegion, setValue]) + + // Fetch streets when city is selected + useEffect(() => { + if (watchCity && watchCity !== 0) { + const fetchStreets = async () => { + try { + const response = await fetch(API_BASE_URL, { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': 'bearer', + }, + body: JSON.stringify({ + IDPersonalAccount: 0, + IDRegion: watchRegion, + IDCity: watchCity, + IDStreet: 0, + IDHouse: 0, + IDFlat: 0 + }) + }) + const data = await response.json() + setStreets(data) + setValue('IDStreet', 0) // Reset street selection + setHouses([]) // Clear dependent fields + setFlats([]) + } catch (error) { + console.error('Error fetching streets:', error) + } + } + + fetchStreets() + } + }, [watchCity, watchRegion, setValue]) + + // Fetch houses when street is selected + useEffect(() => { + if (watchStreet && watchStreet !== 0) { + const fetchHouses = async () => { + try { + const response = await fetch(API_BASE_URL, { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': 'bearer', + }, + body: JSON.stringify({ + IDPersonalAccount: 0, + IDRegion: watchRegion, + IDCity: watchCity, + IDStreet: watchStreet, + IDHouse: 0, + IDFlat: 0 + }) + }) + const data = await response.json() + setHouses(data) + setValue('IDHouse', 0) // Reset house selection + setFlats([]) // Clear dependent fields + } catch (error) { + console.error('Error fetching houses:', error) + } + } + + fetchHouses() + } + }, [watchStreet, watchCity, watchRegion, setValue]) + + // Fetch flats when house is selected + useEffect(() => { + if (watchHouse && watchHouse !== 0) { + const fetchFlats = async () => { + try { + const response = await fetch(API_BASE_URL, { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': 'bearer', + }, + body: JSON.stringify({ + IDPersonalAccount: 0, + IDRegion: watchRegion, + IDCity: watchCity, + IDStreet: watchStreet, + IDHouse: watchHouse, + IDFlat: 0 + }) + }) + const data = await response.json() + setFlats(data) + setValue('IDFlat', 0) // Reset flat selection + } catch (error) { + console.error('Error fetching flats:', error) + } + } + + fetchFlats() + } + }, [watchHouse, watchStreet, watchCity, watchRegion, setValue]) + + // Update selected account when flat is chosen + useEffect(() => { + if (watchFlat && watchFlat !== 0) { + const selectedFlat = flats.find(flat => flat.IDPersonalAccount === watchFlat) + if (selectedFlat) { + setSelectedAccount(selectedFlat) + } + } + }, [watchFlat, flats]) + + const form = [ + { + label: 'Выберите район / улус', + value: 'IDRegion', + options: regions, + valueKey: 'IDRegion', + labelKey: 'NameRegion' + }, + { + label: 'Выберите нас.пункт', + value: 'IDCity', + options: cities, + valueKey: 'IDCity', + labelKey: 'NameCity', + disabled: !watchRegion || watchRegion === 0 + }, + { + label: 'Выберите улицу', + value: 'IDStreet', + options: streets, + valueKey: 'IDStreet', + labelKey: 'NameStreet', + disabled: !watchCity || watchCity === 0 + }, + { + label: '№ дома', + value: 'IDHouse', + options: houses, + valueKey: 'IDHouse', + labelKey: 'NameHouse', + disabled: !watchStreet || watchStreet === 0 + }, + { + label: 'Квартира', + value: 'IDFlat', + options: flats, + valueKey: 'IDPersonalAccount', + labelKey: 'NameFlat', + disabled: !watchHouse || watchHouse === 0 + } + ] + + return ( +
+
Укажите адрес:
+ +
+ {form.map((select, index) => ( + + ))} +
+ + {selectedAccount && ( +
+
Выбрано:
+
Лицевой счет: {selectedAccount.IDPersonalAccount}
+
Квартира: {selectedAccount.NameFlat}
+
+ )} +
+ ) + +} + +export default GetLK \ No newline at end of file diff --git a/src/components/WPRenderer/HTMLRenderer.tsx b/src/components/WPRenderer/HTMLRenderer.tsx new file mode 100644 index 0000000..0ddc7cf --- /dev/null +++ b/src/components/WPRenderer/HTMLRenderer.tsx @@ -0,0 +1,39 @@ +"use client" + +import { useEffect, useRef } from "react"; + +export function HTMLRenderer({ content }: { + content: string +}) { + const iframeRef = useRef(null); + + useEffect(() => { + if (!iframeRef.current || !content) return; + + const iframe = iframeRef.current; + const iframeDoc = iframe.contentDocument + + if (iframeDoc) { + // Write directly to iframe - scripts will execute! + iframeDoc.open(); + iframeDoc.write(content); + iframeDoc.close(); + + // // Adjust height after content loads + // const adjustHeight = () => { + // iframe.style.height = iframeDoc.body.scrollHeight + 'px'; + // }; + + // iframeDoc.addEventListener('load', adjustHeight); + // return () => iframeDoc.removeEventListener('load', adjustHeight); + } + + }, [content]); + + return ( +