This commit is contained in:
2026-03-24 09:21:38 +09:00
parent e4d2966377
commit 39c7eda9cc
31 changed files with 1655 additions and 135 deletions

View File

@@ -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()

View File

@@ -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,
};

672
package-lock.json generated
View File

@@ -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",

View File

@@ -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",

View File

@@ -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 },

View File

@@ -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 (
<div className="w-full flex flex-col items-center sm:gap-8 mb-8">
<div className="w-full flex flex-col items-center sm:gap-8">
{/* {posts.map(post => (
<div key={post.ID} className="max-w-5xl">
{renderPostContent(post.post_content)}
</div>
))} */}
<EmblaCarousel title_align='left' title_position='top' show_dots={true} show_title={true} slides={slides} />
<EmblaCarousel autoplay title_align='left' title_position='top' show_dots={true} show_title={true} slides={slides} />
<Section title='Новости'>
<div className='grid grid-cols-2 space-y-4 space-x-4'>
<Grid gap={4} cols_mobile={1} cols_tablet={1} cols_desktop={2} rows_mobile={1} rows_tablet={1} rows_desktop={1}>
<EmblaCarousel rounded title_align='left' shape='square' show_title title_position='bottom' autoplay={true} show_dots={true} slides_per_view={1} slides={slides} dots_position='bottom_inside' />
</div>
<Flex justify='between' direction='col' gap={4}>
<PostCard
thumbnail_src='https://new.jkhsakha.ru/wp-content/uploads/2026/02/1-1-300x179.jpeg'
post_href='/#'
description='Post card test'
title='Работники ЖКХ обладатели Гран-при военно-патриотического конкурса'
show_thumbnail={true}
show_categories
categories={[
{
id: 0,
slug: 'news',
name: 'Новости'
},
{
id: 1,
slug: 'news',
name: 'Новости'
}
]}
date='26.02.2026'
show_date
/>
<PostCard
post_href='/#'
title='Осторожно! Мошенники рассылают СМС о возврате за отопление'
description='asd'
/>
<PostCard
post_href='/#'
title='Профсоюз ГУП «ЖКХ РС(Я)» поздравил ветеранов с наступающим Новым годом'
/>
</Flex>
</Grid>
</Section>
<Section title='О предприятии'>
<div className={`grid grid-cols-1 lg:grid-cols-2 justify-center items-center`}>
<div>
<Areas />
</div>
<Grid cols_mobile={1} cols_tablet={1} cols_desktop={2} rows_mobile={1} rows_tablet={1} rows_desktop={1}>
<Areas />
<div className="grid grid-cols-2 space-y-4 space-x-4">
<Grid cols_desktop={2} cols_mobile={2} cols_tablet={2} gap={4}>
{stats.map((stat, index) => (
<div key={index} className="grid grid-rows-[min-content_1fr] space-y-2">
<div className="stat-value text-3xl sm:text-5xl text-[#0063A7]">{stat.value}</div>
<div className="stat-title text-wrap text-lg sm:text-2xl text-neutral">{stat.title}</div>
</div>
<Stat key={index} value={stat.value} title={stat.title} />
))}
</div>
</div>
</Grid>
</Grid>
</Section>
<Section title='Филиалы'>
@@ -245,6 +297,12 @@ export default async function HomePage() {
<Section title="Контакты">
</Section>
<IFrame iframe={`<iframe loading="lazy"
src="https://maps.google.com/maps?q=%D0%B3.%20%D0%AF%D0%BA%D1%83%D1%82%D1%81%D0%BA%2C%20%D1%83%D0%BB.%20%D0%9A%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%2C%20%D0%B4.18%2C%20%D0%B1%D0%BB%D0%BE%D0%BA%20%22%D0%90%22&#038;t=m&#038;z=16&#038;output=embed&#038;iwloc=near"
title="г. Якутск, ул. Кирова, д.18, блок &quot;А&quot;"
aria-label="г. Якутск, ул. Кирова, д.18, блок &quot;А&quot;"
></iframe>`} />
</div>
);
}

View File

@@ -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) {

View File

@@ -5,7 +5,7 @@ export const montserratFont = Montserrat({
subsets: ['latin', 'cyrillic'],
})
const mainFont = Roboto({
export const mainFont = Roboto({
subsets: ['latin', 'cyrillic'],
})

View File

@@ -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 <Render config={puckConfig} data={data} />
}

View File

@@ -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 <Render>.
*
* 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<Metadata> {
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 <Client data={data} />;
}
// 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";

View File

@@ -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 (
<div className="w-full flex flex-col items-center">
<Areas />
{/* <Areas /> */}
<GetLK />
</div>
);
}

View File

@@ -10,7 +10,7 @@ export default async function Banner({ title, subtitle, backgroundImage, logoIma
<section className="relative w-full">
<div className="hero bg-base-200 min-h-8 h-min overflow-hidden" >
<div className="absolute inset-0 overflow-hidden">
<div className="w-full h-full blur-xs scale-[1.1]" style={{
<div className="w-full h-full " style={{
backgroundImage: `url(${backgroundImage})`
}}></div>
</div>

View File

@@ -0,0 +1,15 @@
import React from 'react'
interface FeaturedProps {
}
const Featured = () => {
return (
<div>
</div>
)
}
export default Featured

View File

@@ -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 (
<div className={flexClassName}>
{children}
</div>
)
}
export default Flex

View File

@@ -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 (
<div className={gridClassName}>
{children}
</div>
)
}
export default Grid

View File

@@ -0,0 +1,26 @@
import React from 'react'
export interface IFrameProps {
iframe: string
}
const IFrame = ({
iframe
}: IFrameProps) => {
return (
<>
<style>
{`
iframe {
width: 100%;
min-height: 24rem;
}
`}
</style>
<div className='w-full rounded-sm' dangerouslySetInnerHTML={{ __html: iframe }} />
</>
)
}
export default IFrame

View File

@@ -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
}

View File

@@ -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 (
<div className="container mx-auto py-8 max-w-5xl">
<div className="container mx-auto py-8 max-w-5xl px-2 sm:px-0">
<article className="prose lg:prose-xl max-w-none">
{
post.thumbnail &&
<div>
<img className='' src={post.thumbnail.guid} />
</div>
}
<h1>{post.post_title}</h1>
{post.post_type === 'post' && (
@@ -15,6 +65,7 @@ const Post = ({ post }: { post: PostData }) => {
</div>
)}
{post.wp_postmeta && post.wp_postmeta.find(meta => meta.meta_key === '_elementor_data') && renderElementorContent(preProcessElementor())}
{renderPostContent(post.post_content)}
</article>
</div>

View File

@@ -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 (
<div className={`${mainFont.className} text-[#0063A7] card sm:card-side bg-base-100 h-1/1 w-full shadow-sm`}>
{show_thumbnail && thumbnail_src &&
<figure className='sm:h-full sm:w-full'>
<a href={post_href} className='w-full h-full'>
<img
className='object-cover w-full h-full'
src={thumbnail_src}
alt="Shoes" />
</a>
</figure>
}
<div className="card-body">
<a href={post_href} className={`card-title`}>
{title}
</a>
{description && show_description &&
<p className='font-medium text-base-content'>
{description}
</p>
}
<div className="card-actions items-center justify-between">
{date && show_date &&
<span>{date}</span>
}
<div className='flex gap-2'>
{categories && show_categories &&
categories.map((cat, index) => (
<a key={`category-${cat.id}`} href={cat.slug} className="badge badge-outline">
{cat.name}
</a>
))
}
</div>
</div>
</div>
</div>
)
}
export default PostCard

View File

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

View File

@@ -0,0 +1,20 @@
import React from 'react'
interface StatProps {
title: string
value: string
}
const Stat = ({
title,
value
}: StatProps) => {
return (
<div className="grid grid-rows-[min-content_1fr] space-y-2">
<div className="stat-value text-3xl sm:text-5xl text-[#0063A7]">{value}</div>
<div className="stat-title text-wrap text-lg sm:text-2xl text-neutral">{title}</div>
</div>
)
}
export default Stat

View File

@@ -152,8 +152,8 @@ const Areas = () => {
const [tooltip, setTooltip] = useState<{ name: string, x: number, y: number } | null>(null)
return (
<div className='relative w-full h-96 flex flex-col justify-center items-center'>
<img className='absolute w-full h-auto' src={'/karta-s-logo2.png'} />
<div className='relative w-full min-h-64 md:min-h-96 max-h-96 flex flex-col justify-center items-center'>
<img className='absolute w-auto h-full' src={'/karta-s-logo2.png'} />
{tooltip && <span className="rounded-sm text-nowrap absolute p-2 z-20 pointer-events-none bg-black/90 text-white text-sm text-center" style={{
left: tooltip?.x,
@@ -162,14 +162,14 @@ const Areas = () => {
{tooltip?.name}
</span>}
<svg
className='absolute w-full h-auto'
className='absolute w-auto h-full'
viewBox="15 0 520 520"
aria-labelledby="img-title-1161"
aria-describedby="img-desc-1161"
role="group"
>
<title id="img-title-1161" />
<g role="list" className="relative">
<g role="list" className="">
{
areas.map((area, index) => (
<a key={index} target="_blank" href={area.href} className="relative transition-all duration-300 opacity-0 hover:opacity-100 outline-none" tabIndex={0} aria-label={area.name} role="listitem">

View File

@@ -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<Region[]>([])
const [cities, setCities] = useState<City[]>([])
const [streets, setStreets] = useState<Street[]>([])
const [houses, setHouses] = useState<House[]>([])
const [flats, setFlats] = useState<Flat[]>([])
const [selectedAccount, setSelectedAccount] = useState<Flat | null>(null)
const { register, watch, setValue } = useForm<AddressData>({
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 (
<div className='flex flex-col gap-4'>
<div className='text-lg font-medium'>Укажите адрес:</div>
<div className='flex flex-col gap-3'>
{form.map((select, index) => (
<select
key={`select-${index}`}
className="select appearance-none p-2 border rounded-md"
{...register(select.value as keyof AddressData, {
valueAsNumber: true,
disabled: select.disabled
})}
disabled={select.disabled}
>
<option value={0} disabled={true}>{select.label}</option>
{Array.isArray(select.options) && select.options.map((option) => (
<option
key={option[select.valueKey]}
value={option[select.valueKey]}
>
{option[select.labelKey]}
</option>
))}
</select>
))}
</div>
{selectedAccount && (
<div className='mt-4 p-3 bg-gray-100 rounded-md'>
<div className='font-medium'>Выбрано:</div>
<div>Лицевой счет: {selectedAccount.IDPersonalAccount}</div>
<div>Квартира: {selectedAccount.NameFlat}</div>
</div>
)}
</div>
)
}
export default GetLK

View File

@@ -0,0 +1,39 @@
"use client"
import { useEffect, useRef } from "react";
export function HTMLRenderer({ content }: {
content: string
}) {
const iframeRef = useRef<HTMLIFrameElement>(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 (
<iframe
ref={iframeRef}
sandbox="allow-scripts allow-cross-origin allow-same-origin allow-forms allow-popups"
/>
);
}

View File

@@ -4,6 +4,7 @@ import PostGrid from '../WP/PostGrid/PostGrid.server';
import { CarouselSlide, PagePayload } from '@/types/entities';
import { transformLinks } from '@/lib/utils';
import EmblaCarousel from '../UI/EmblaCarousel/EmblaCarousel';
import { HTMLRenderer } from './HTMLRenderer';
const options: HTMLReactParserOptions = {
replace: (domNode) => {
@@ -47,6 +48,15 @@ const replaceShortcodes = (html: string) => {
// return html.replace(regex, '$3');
// };
export const renderElementorContent = (content: string, payload?: PagePayload) => {
const transformedContent = replaceShortcodes(
transformLinks(content)
)
return (
<div dangerouslySetInnerHTML={{__html: transformedContent}}></div>
// <HTMLRenderer content={transformedContent} />
)
}
export const renderPostContent = (content: string, payload?: PagePayload) => {
@@ -108,6 +118,13 @@ export const renderPostContent = (content: string, payload?: PagePayload) => {
return (
domNode
);
} else if (
domNode &&
domNode.type === 'tag' && domNode.tagName === 'html'
) {
return (
<></>
)
}
},
})}

View File

@@ -1,4 +1,4 @@
import { DuckDBConnection, DuckDBInstance } from '@duckdb/node-api';
const instance = await DuckDBInstance.create('../public/jkhsakha.duckdb');
const instance = await DuckDBInstance.create('./jkhsakha.duckdb');
export const connection = await DuckDBConnection.create(instance)

View File

@@ -1,38 +1,106 @@
import { containsCyrillic, transliterate } from "./transliterate";
// export const transformLinks = (html: string): string => {
// if (!html) return html;
// // Regex to match specific domain links, excluding wp-content
// // Now also captures the full URL to handle different parts
// const regex = /(https?:\/\/)?(www\.)?new\.jkhsakha\.ru\/(?!wp-content)([^\s"']*)/g;
// return html.replace(regex, (match, protocol, www, path) => {
// // Extract the full path including leading slash
// let fullPath = path;
// // Make sure path starts with /
// if (!fullPath.startsWith('/')) {
// fullPath = '/' + fullPath;
// }
// fullPath = decodeURIComponent(fullPath)
// // Check if the path contains Cyrillic characters
// if (containsCyrillic(fullPath)) {
// console.log("contains Cyrillic, transliterating:", fullPath);
// // Split the path into segments
// const pathSegments = fullPath.split('/').filter((segment: any) => segment.length > 0);
// // Transliterate each segment that contains Cyrillic
// const transliteratedSegments = pathSegments.map((segment: any) => {
// return containsCyrillic(segment) ? transliterate(segment) : segment;
// });
// // Reconstruct the path
// const transliteratedPath = '/' + transliteratedSegments.join('/');
// // Ensure the path ends with trailing slash if original had it
// if (fullPath.endsWith('/') && !transliteratedPath.endsWith('/')) {
// return transliteratedPath + '/';
// }
// return transliteratedPath;
// }
// // If no Cyrillic, return the original path
// return fullPath;
// });
// };
export const transformLinks = (html: string): string => {
if (!html) return html;
// Regex to match specific domain links, excluding wp-content
// Now also captures the full URL to handle different parts
const regex = /(https?:\/\/)?(www\.)?new\.jkhsakha\.ru\/(?!wp-content)([^\s"']*)/g;
// First, replace wp-content/uploads links with storage URL
const wpContentRegex = /(https?:\/\/)?(www\.)?new\.jkhsakha\.ru\/wp-content\/uploads\/([^\s"']*)/g;
return html.replace(regex, (match, protocol, www, path) => {
// Extract the full path including leading slash
let fullPath = path;
// Make sure path starts with /
if (!fullPath.startsWith('/')) {
fullPath = '/' + fullPath;
html = html.replace(wpContentRegex, (match, protocol, www, path) => {
const storageUrl = process.env.NEXT_PUBLIC_STORAGE_URL?.replace(/\/$/, ''); // Remove trailing slash if exists
if (!storageUrl) {
console.warn('NEXT_PUBLIC_STORAGE_URL is not defined');
return match; // Return original if env var not set
}
fullPath = decodeURIComponent(fullPath)
// Decode and process the path for Cyrillic characters
let fullPath = '/' + decodeURIComponent(path);
if (containsCyrillic(fullPath)) {
console.log("contains Cyrillic, transliterating:", fullPath);
const pathSegments = fullPath.split('/').filter((segment: any) => segment.length > 0);
const transliteratedSegments = pathSegments.map((segment: any) => {
return containsCyrillic(segment) ? transliterate(segment) : segment;
});
fullPath = '/' + transliteratedSegments.join('/');
if (path.endsWith('/') && !fullPath.endsWith('/')) {
fullPath += '/';
}
}
return `${storageUrl}${fullPath}`;
});
// Then handle other domain links (excluding wp-content)
const otherLinksRegex = /(https?:\/\/)?(www\.)?new\.jkhsakha\.ru\/(?!wp-content)([^\s"']*)/g;
return html.replace(otherLinksRegex, (match, protocol, www, path) => {
let fullPath = path;
if (!fullPath.startsWith('/')) {
fullPath = '/' + fullPath;
}
fullPath = decodeURIComponent(fullPath);
// Check if the path contains Cyrillic characters
if (containsCyrillic(fullPath)) {
console.log("contains Cyrillic, transliterating:", fullPath);
// Split the path into segments
const pathSegments = fullPath.split('/').filter((segment: any) => segment.length > 0);
// Transliterate each segment that contains Cyrillic
const transliteratedSegments = pathSegments.map((segment: any) => {
return containsCyrillic(segment) ? transliterate(segment) : segment;
});
// Reconstruct the path
const transliteratedPath = '/' + transliteratedSegments.join('/');
// Ensure the path ends with trailing slash if original had it
if (fullPath.endsWith('/') && !transliteratedPath.endsWith('/')) {
return transliteratedPath + '/';
}
@@ -40,7 +108,6 @@ export const transformLinks = (html: string): string => {
return transliteratedPath;
}
// If no Cyrillic, return the original path
return fullPath;
});
};

View File

@@ -1,5 +1,11 @@
export interface WPPostMeta {
meta_id: number
meta_key: string
meta_value: string
}
export interface PostData {
ID: string;
guid: string;
post_author: string;
post_name: string;
post_title: string;
@@ -9,6 +15,14 @@ export interface PostData {
post_date_gmt: string;
post_modified: string;
post_content: string;
wp_postmeta?: WPPostMeta[]
thumbnail?: PostData
}
export interface PostCategory {
id: number
name: string
slug: string
}
export interface PagePayload {

6
src/utils/tailwind.ts Normal file
View File

@@ -0,0 +1,6 @@
import clsx from "clsx"
import { twMerge } from "tailwind-merge"
export const cn = (...inputs: Parameters<typeof clsx>) => {
return twMerge(clsx(...inputs))
}

39
tailwind.config.ts Normal file
View File

@@ -0,0 +1,39 @@
module.exports = {
// ... other config
safelist: [
// Grid columns
// 'grid-cols-1', 'grid-cols-2', 'grid-cols-3', 'grid-cols-4',
// 'grid-cols-5', 'grid-cols-6', 'grid-cols-7', 'grid-cols-8',
// 'grid-cols-9', 'grid-cols-10', 'grid-cols-11', 'grid-cols-12',
// 'md:grid-cols-1', 'md:grid-cols-2', 'md:grid-cols-3', 'md:grid-cols-4',
// 'md:grid-cols-5', 'md:grid-cols-6', 'md:grid-cols-7', 'md:grid-cols-8',
// 'md:grid-cols-9', 'md:grid-cols-10', 'md:grid-cols-11', 'md:grid-cols-12',
// 'lg:grid-cols-1', 'lg:grid-cols-2', 'lg:grid-cols-3', 'lg:grid-cols-4',
// 'lg:grid-cols-5', 'lg:grid-cols-6', 'lg:grid-cols-7', 'lg:grid-cols-8',
// 'lg:grid-cols-9', 'lg:grid-cols-10', 'lg:grid-cols-11', 'lg:grid-cols-12',
// // Grid rows
// 'grid-rows-1', 'grid-rows-2', 'grid-rows-3', 'grid-rows-4',
// 'grid-rows-5', 'grid-rows-6', 'grid-rows-7', 'grid-rows-8',
// 'grid-rows-9', 'grid-rows-10', 'grid-rows-11', 'grid-rows-12',
// 'md:grid-rows-1', 'md:grid-rows-2', 'md:grid-rows-3', 'md:grid-rows-4',
// 'md:grid-rows-5', 'md:grid-rows-6', 'md:grid-rows-7', 'md:grid-rows-8',
// 'md:grid-rows-9', 'md:grid-rows-10', 'md:grid-rows-11', 'md:grid-rows-12',
// 'lg:grid-rows-1', 'lg:grid-rows-2', 'lg:grid-rows-3', 'lg:grid-rows-4',
// 'lg:grid-rows-5', 'lg:grid-rows-6', 'lg:grid-rows-7', 'lg:grid-rows-8',
// 'lg:grid-rows-9', 'lg:grid-rows-10', 'lg:grid-rows-11', 'lg:grid-rows-12',
// // Gaps
// 'gap-0', 'gap-1', 'gap-2', 'gap-3', 'gap-4', 'gap-5', 'gap-6',
// 'gap-7', 'gap-8', 'gap-9', 'gap-10', 'gap-11', 'gap-12',
// // Flex
// 'flex', 'flex-row', 'flex-col',
// 'justify-center', 'justify-between', 'justify-around', 'justify-start', 'justify-end', 'justify-evenly', 'justify-stretch',
// 'items-center', 'items-end', 'items-start', 'items-stretch',
{
pattern: /.*/,
},
],
}