diff --git a/.env b/.env
new file mode 100644
index 00000000..a366b082
--- /dev/null
+++ b/.env
@@ -0,0 +1 @@
+NEXT_PUBLIC_URL = https://blog.apiki.com/wp-json/wp/v2/posts?_embed&categories=518
\ No newline at end of file
diff --git a/global.d.ts b/global.d.ts
new file mode 100644
index 00000000..3f8e887d
--- /dev/null
+++ b/global.d.ts
@@ -0,0 +1,4 @@
+declare module "*.module.css" {
+ const classes: { [key: string]: string };
+ export default classes;
+}
\ No newline at end of file
diff --git a/next-env.d.ts b/next-env.d.ts
new file mode 100644
index 00000000..1b3be084
--- /dev/null
+++ b/next-env.d.ts
@@ -0,0 +1,5 @@
+///
+///
+
+// NOTE: This file should not be edited
+// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
diff --git a/next.config.ts b/next.config.ts
new file mode 100644
index 00000000..05b99e8f
--- /dev/null
+++ b/next.config.ts
@@ -0,0 +1,21 @@
+import type { NextConfig } from "next";
+
+const nextConfig: NextConfig = {
+ /* config options here */
+ images: {
+ remotePatterns: [
+ {
+ protocol: "https",
+ hostname: "blog.apiki.com",
+ pathname: "/**",
+ },
+ {
+ protocol: "https",
+ hostname: "developers.elementor.com",
+ pathname: "/**",
+ }
+ ],
+ },
+};
+
+export default nextConfig;
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..175b89ee
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,975 @@
+{
+ "name": "front-end-challenge",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "front-end-challenge",
+ "version": "0.1.0",
+ "dependencies": {
+ "next": "15.4.6",
+ "react": "19.1.0",
+ "react-dom": "19.1.0"
+ },
+ "devDependencies": {
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "typescript": "^5"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz",
+ "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz",
+ "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz",
+ "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz",
+ "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz",
+ "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz",
+ "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz",
+ "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz",
+ "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz",
+ "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz",
+ "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz",
+ "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz",
+ "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz",
+ "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz",
+ "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-ppc64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz",
+ "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-ppc64": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz",
+ "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz",
+ "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz",
+ "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz",
+ "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz",
+ "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.4.4"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-arm64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz",
+ "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz",
+ "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz",
+ "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@next/env": {
+ "version": "15.4.6",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-15.4.6.tgz",
+ "integrity": "sha512-yHDKVTcHrZy/8TWhj0B23ylKv5ypocuCwey9ZqPyv4rPdUdRzpGCkSi03t04KBPyU96kxVtUqx6O3nE1kpxASQ==",
+ "license": "MIT"
+ },
+ "node_modules/@next/swc-darwin-arm64": {
+ "version": "15.4.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.4.6.tgz",
+ "integrity": "sha512-667R0RTP4DwxzmrqTs4Lr5dcEda9OxuZsVFsjVtxVMVhzSpo6nLclXejJVfQo2/g7/Z9qF3ETDmN3h65mTjpTQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-darwin-x64": {
+ "version": "15.4.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.4.6.tgz",
+ "integrity": "sha512-KMSFoistFkaiQYVQQnaU9MPWtp/3m0kn2Xed1Ces5ll+ag1+rlac20sxG+MqhH2qYWX1O2GFOATQXEyxKiIscg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-gnu": {
+ "version": "15.4.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.4.6.tgz",
+ "integrity": "sha512-PnOx1YdO0W7m/HWFeYd2A6JtBO8O8Eb9h6nfJia2Dw1sRHoHpNf6lN1U4GKFRzRDBi9Nq2GrHk9PF3Vmwf7XVw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-musl": {
+ "version": "15.4.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.4.6.tgz",
+ "integrity": "sha512-XBbuQddtY1p5FGPc2naMO0kqs4YYtLYK/8aPausI5lyOjr4J77KTG9mtlU4P3NwkLI1+OjsPzKVvSJdMs3cFaw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-gnu": {
+ "version": "15.4.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.4.6.tgz",
+ "integrity": "sha512-+WTeK7Qdw82ez3U9JgD+igBAP75gqZ1vbK6R8PlEEuY0OIe5FuYXA4aTjL811kWPf7hNeslD4hHK2WoM9W0IgA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-musl": {
+ "version": "15.4.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.4.6.tgz",
+ "integrity": "sha512-XP824mCbgQsK20jlXKrUpZoh/iO3vUWhMpxCz8oYeagoiZ4V0TQiKy0ASji1KK6IAe3DYGfj5RfKP6+L2020OQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-arm64-msvc": {
+ "version": "15.4.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.4.6.tgz",
+ "integrity": "sha512-FxrsenhUz0LbgRkNWx6FRRJIPe/MI1JRA4W4EPd5leXO00AZ6YU8v5vfx4MDXTvN77lM/EqsE3+6d2CIeF5NYg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-x64-msvc": {
+ "version": "15.4.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.4.6.tgz",
+ "integrity": "sha512-T4ufqnZ4u88ZheczkBTtOF+eKaM14V8kbjud/XrAakoM5DKQWjW09vD6B9fsdsWS2T7D5EY31hRHdta7QKWOng==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.15",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
+ "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "20.19.9",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.9.tgz",
+ "integrity": "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.1.9",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.9.tgz",
+ "integrity": "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.1.7",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.7.tgz",
+ "integrity": "sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.0.0"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001733",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001733.tgz",
+ "integrity": "sha512-e4QKw/O2Kavj2VQTKZWrwzkt3IxOmIlU6ajRb6LP64LHpBo1J67k2Hi4Vu/TgJWsNtynurfS0uK3MaUTCPfu5Q==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/client-only": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
+ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
+ "license": "MIT"
+ },
+ "node_modules/color": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
+ "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "color-convert": "^2.0.1",
+ "color-string": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=12.5.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/color-string": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
+ "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/detect-libc": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
+ "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/next": {
+ "version": "15.4.6",
+ "resolved": "https://registry.npmjs.org/next/-/next-15.4.6.tgz",
+ "integrity": "sha512-us++E/Q80/8+UekzB3SAGs71AlLDsadpFMXVNM/uQ0BMwsh9m3mr0UNQIfjKed8vpWXsASe+Qifrnu1oLIcKEQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@next/env": "15.4.6",
+ "@swc/helpers": "0.5.15",
+ "caniuse-lite": "^1.0.30001579",
+ "postcss": "8.4.31",
+ "styled-jsx": "5.1.6"
+ },
+ "bin": {
+ "next": "dist/bin/next"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
+ },
+ "optionalDependencies": {
+ "@next/swc-darwin-arm64": "15.4.6",
+ "@next/swc-darwin-x64": "15.4.6",
+ "@next/swc-linux-arm64-gnu": "15.4.6",
+ "@next/swc-linux-arm64-musl": "15.4.6",
+ "@next/swc-linux-x64-gnu": "15.4.6",
+ "@next/swc-linux-x64-musl": "15.4.6",
+ "@next/swc-win32-arm64-msvc": "15.4.6",
+ "@next/swc-win32-x64-msvc": "15.4.6",
+ "sharp": "^0.34.3"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.1.0",
+ "@playwright/test": "^1.51.1",
+ "babel-plugin-react-compiler": "*",
+ "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "sass": "^1.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@playwright/test": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/postcss": {
+ "version": "8.4.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.1.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
+ "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.1.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
+ "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.26.0"
+ },
+ "peerDependencies": {
+ "react": "^19.1.0"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.26.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
+ "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "7.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+ "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "license": "ISC",
+ "optional": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/sharp": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz",
+ "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "color": "^4.2.3",
+ "detect-libc": "^2.0.4",
+ "semver": "^7.7.2"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.34.3",
+ "@img/sharp-darwin-x64": "0.34.3",
+ "@img/sharp-libvips-darwin-arm64": "1.2.0",
+ "@img/sharp-libvips-darwin-x64": "1.2.0",
+ "@img/sharp-libvips-linux-arm": "1.2.0",
+ "@img/sharp-libvips-linux-arm64": "1.2.0",
+ "@img/sharp-libvips-linux-ppc64": "1.2.0",
+ "@img/sharp-libvips-linux-s390x": "1.2.0",
+ "@img/sharp-libvips-linux-x64": "1.2.0",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.0",
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.0",
+ "@img/sharp-linux-arm": "0.34.3",
+ "@img/sharp-linux-arm64": "0.34.3",
+ "@img/sharp-linux-ppc64": "0.34.3",
+ "@img/sharp-linux-s390x": "0.34.3",
+ "@img/sharp-linux-x64": "0.34.3",
+ "@img/sharp-linuxmusl-arm64": "0.34.3",
+ "@img/sharp-linuxmusl-x64": "0.34.3",
+ "@img/sharp-wasm32": "0.34.3",
+ "@img/sharp-win32-arm64": "0.34.3",
+ "@img/sharp-win32-ia32": "0.34.3",
+ "@img/sharp-win32-x64": "0.34.3"
+ }
+ },
+ "node_modules/simple-swizzle": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+ "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "is-arrayish": "^0.3.1"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/styled-jsx": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
+ "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
+ "license": "MIT",
+ "dependencies": {
+ "client-only": "0.0.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/typescript": {
+ "version": "5.9.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
+ "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..a7ca559a
--- /dev/null
+++ b/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "front-end-challenge",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev --turbopack",
+ "build": "next build",
+ "start": "next start",
+ "lint": "next lint"
+ },
+ "dependencies": {
+ "react": "19.1.0",
+ "react-dom": "19.1.0",
+ "next": "15.4.6"
+ },
+ "devDependencies": {
+ "typescript": "^5",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19"
+ }
+}
diff --git a/src/app/[slug]/page.module.css b/src/app/[slug]/page.module.css
new file mode 100644
index 00000000..e52a698f
--- /dev/null
+++ b/src/app/[slug]/page.module.css
@@ -0,0 +1,180 @@
+.container {
+ max-width: 42rem;
+ margin: 0 auto;
+ padding: 2rem 1rem;
+ color: var(--foreground);
+ background: var(--background);
+}
+
+.navigation {
+ margin-bottom: 2rem;
+}
+
+.backButton {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.75rem 1rem;
+ background: var(--gray-alpha-100);
+ border: 1px solid var(--gray-alpha-200);
+ border-radius: 0.5rem;
+ color: var(--foreground);
+ text-decoration: none;
+ font-weight: 500;
+ font-size: 0.875rem;
+ transition: all 0.2s ease;
+}
+
+.backButton:hover {
+ background: var(--gray-alpha-200);
+ transform: translateX(-2px);
+}
+
+.imageContainer {
+ margin-bottom: 1.5rem;
+ width: 100%;
+ aspect-ratio: 16 / 9;
+ position: relative;
+ overflow: hidden;
+ border-radius: 0.75rem;
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+}
+
+.image {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ object-position: center;
+}
+
+.title {
+ font-size: 1.875rem;
+ font-weight: 700;
+ margin-bottom: 0.5rem;
+ line-height: 1.2;
+ color: var(--foreground);
+}
+
+.date {
+ font-size: 0.875rem;
+ color: var(--foreground);
+ opacity: 0.7;
+ margin-bottom: 1.5rem;
+ font-weight: 500;
+ display: block;
+}
+
+.excerpt {
+ font-size: 1.125rem;
+ line-height: 1.6;
+ color: var(--foreground);
+ opacity: 0.8;
+ margin-bottom: 2rem;
+ font-style: italic;
+ border-left: 4px solid var(--gray-alpha-200);
+ padding-left: 1rem;
+}
+
+.content {
+ line-height: 1.6;
+ color: var(--foreground);
+}
+
+.content h1,
+.content h2,
+.content h3,
+.content h4,
+.content h5,
+.content h6 {
+ margin-top: 1.5rem;
+ margin-bottom: 0.5rem;
+ font-weight: 600;
+ color: var(--foreground);
+}
+
+.content p {
+ margin-bottom: 1rem;
+ color: var(--foreground);
+}
+
+.content img {
+ max-width: 100%;
+ height: auto;
+ border-radius: 0.375rem;
+ margin: 1rem 0;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.content a {
+ color: #3b82f6;
+ text-decoration: underline;
+}
+
+.content a:hover {
+ color: #1d4ed8;
+}
+
+@media (prefers-color-scheme: dark) {
+ .content a {
+ color: #60a5fa;
+ }
+
+ .content a:hover {
+ color: #93c5fd;
+ }
+}
+
+.content ul,
+.content ol {
+ margin-bottom: 1rem;
+ padding-left: 1.5rem;
+}
+
+.content li {
+ margin-bottom: 0.25rem;
+ color: var(--foreground);
+}
+
+.content blockquote {
+ border-left: 4px solid var(--gray-alpha-200);
+ padding-left: 1rem;
+ margin: 1rem 0;
+ font-style: italic;
+ color: var(--foreground);
+ opacity: 0.8;
+}
+
+.content code {
+ background-color: var(--gray-alpha-100);
+ padding: 0.125rem 0.25rem;
+ border-radius: 0.25rem;
+ font-family: var(--font-geist-mono);
+ font-size: 0.875rem;
+ color: var(--foreground);
+}
+
+.content pre {
+ background-color: var(--gray-alpha-100);
+ color: var(--foreground);
+ padding: 1rem;
+ border-radius: 0.5rem;
+ overflow-x: auto;
+ margin: 1rem 0;
+ border: 1px solid var(--gray-alpha-200);
+}
+
+.content pre code {
+ background-color: transparent;
+ padding: 0;
+ color: inherit;
+}
+
+@media (prefers-color-scheme: dark) {
+ .imageContainer {
+ box-shadow: 0 4px 6px -1px rgba(255, 255, 255, 0.1), 0 2px 4px -1px rgba(255, 255, 255, 0.06);
+ }
+
+ .content img {
+ box-shadow: 0 2px 8px rgba(255, 255, 255, 0.1);
+ }
+}
\ No newline at end of file
diff --git a/src/app/[slug]/page.tsx b/src/app/[slug]/page.tsx
new file mode 100644
index 00000000..8c0c8cba
--- /dev/null
+++ b/src/app/[slug]/page.tsx
@@ -0,0 +1,79 @@
+import { Card } from "@/types/Card";
+import { CardNamed } from "@/types/CardNamed";
+import { mapPostToBlogPost } from "@/usePosts";
+import Image from "next/image";
+import Link from "next/link";
+import { notFound } from "next/navigation";
+import styles from "./page.module.css";
+
+interface PostProps {
+ params: Promise<{ slug: string }>;
+}
+
+export async function generateStaticParams() {
+ try {
+ const response = await fetch(
+ 'https://blog.apiki.com/wp-json/wp/v2/posts?_embed&categories=518&per_page=100'
+ );
+ const posts: Card[] = await response.json();
+
+ return posts.map((post) => ({
+ slug: post.link.split('com/')[1],
+ }));
+ } catch (error) {
+ console.error('Error generating static params:', error);
+ return [];
+ }
+}
+
+async function getPost(slug: string) {
+ const response = await fetch(
+ `https://blog.apiki.com/wp-json/wp/v2/posts?_embed&slug=${slug}`, {
+ next: { revalidate: 604800 }
+ }
+ );
+ const posts: Card[] = await response.json();
+
+ if (!posts || posts.length === 0) {
+ notFound();
+ }
+
+ const post: Card = posts[0];
+ const cardNamed: CardNamed = mapPostToBlogPost(post, true);
+ return cardNamed;
+}
+
+export default async function PostPage({ params }: PostProps) {
+ const { slug } = await params;
+ const post = await getPost(slug);
+ const markup = { __html: post.content ?? "" }
+
+ return (
+
+
+
+
+
+ {post.titulo}
+ {post.data && }
+ {post.excerpt && (
+
+ )}
+
+
+ );
+}
diff --git a/src/app/favicon.ico b/src/app/favicon.ico
new file mode 100644
index 00000000..3fb07029
Binary files /dev/null and b/src/app/favicon.ico differ
diff --git a/src/app/globals.css b/src/app/globals.css
new file mode 100644
index 00000000..e3734be1
--- /dev/null
+++ b/src/app/globals.css
@@ -0,0 +1,42 @@
+:root {
+ --background: #ffffff;
+ --foreground: #171717;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --background: #0a0a0a;
+ --foreground: #ededed;
+ }
+}
+
+html,
+body {
+ max-width: 100vw;
+ overflow-x: hidden;
+}
+
+body {
+ color: var(--foreground);
+ background: var(--background);
+ font-family: Arial, Helvetica, sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+* {
+ box-sizing: border-box;
+ padding: 0;
+ margin: 0;
+}
+
+a {
+ color: inherit;
+ text-decoration: none;
+}
+
+@media (prefers-color-scheme: dark) {
+ html {
+ color-scheme: dark;
+ }
+}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
new file mode 100644
index 00000000..2498ac23
--- /dev/null
+++ b/src/app/layout.tsx
@@ -0,0 +1,52 @@
+import type { Metadata } from "next";
+import { Geist, Geist_Mono } from "next/font/google";
+import "./globals.css";
+
+const geistSans = Geist({
+ variable: "--font-geist-sans",
+ subsets: ["latin"],
+});
+
+const geistMono = Geist_Mono({
+ variable: "--font-geist-mono",
+ subsets: ["latin"],
+});
+
+export const metadata: Metadata = {
+ title: "Home Blog Apiki - Conteúdo para Desenvolvedores",
+ description: "Blog da Apiki com os melhores conteúdos sobre desenvolvimento web, WordPress, tecnologia e programação para desenvolvedores.",
+ keywords: ["blog", "desenvolvimento", "wordpress", "tecnologia", "programação", "apiki", "web development"],
+ authors: [{ name: "Apiki" }],
+ creator: "Apiki",
+ publisher: "Apiki",
+ robots: "index, follow",
+ openGraph: {
+ title: "Home Blog Apiki - Conteúdo para Desenvolvedores",
+ description: "Blog da Apiki com os melhores conteúdos sobre desenvolvimento web, WordPress, tecnologia e programação.",
+ type: "website",
+ locale: "pt_BR",
+ siteName: "Blog Apiki",
+ },
+ twitter: {
+ card: "summary_large_image",
+ title: "Home Blog Apiki - Conteúdo para Desenvolvedores",
+ description: "Blog da Apiki com os melhores conteúdos sobre desenvolvimento web, WordPress, tecnologia e programação.",
+ },
+ alternates: {
+ canonical: "https://blog.apiki.com",
+ },
+};
+
+export default async function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/src/app/not-found.module.css b/src/app/not-found.module.css
new file mode 100644
index 00000000..c86da80c
--- /dev/null
+++ b/src/app/not-found.module.css
@@ -0,0 +1,113 @@
+.container {
+ min-height: 100vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 2rem;
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
+}
+
+.content {
+ text-align: center;
+ max-width: 600px;
+ z-index: 2;
+}
+
+.title {
+ font-size: clamp(4rem, 8vw, 8rem);
+ font-weight: 900;
+ color: #ff6600;
+ margin: 0;
+ line-height: 1;
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+.subtitle {
+ font-size: clamp(1.5rem, 4vw, 2.5rem);
+ font-weight: 700;
+ color: #333;
+ margin: 1rem 0;
+ line-height: 1.2;
+}
+
+.description {
+ font-size: 1.1rem;
+ color: #666;
+ margin: 2rem 0 3rem 0;
+ line-height: 1.6;
+ max-width: 400px;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.actions {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ align-items: center;
+}
+
+.homeButton {
+ display: inline-block;
+ background: #ff6600;
+ color: white;
+ padding: 1rem 2rem;
+ border-radius: 8px;
+ text-decoration: none;
+ font-weight: 600;
+ font-size: 1.1rem;
+ transition: all 0.3s ease;
+ box-shadow: 0 4px 15px rgba(255, 102, 0, 0.3);
+}
+
+.homeButton:hover {
+ background: #e55a00;
+ transform: translateY(-2px);
+ box-shadow: 0 6px 20px rgba(255, 102, 0, 0.4);
+}
+
+.illustration {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ z-index: 1;
+ opacity: 0.1;
+ pointer-events: none;
+}
+
+.errorCode {
+ font-size: 20rem;
+ font-weight: 900;
+ color: #ff6600;
+ line-height: 1;
+}
+
+@media (max-width: 768px) {
+ .container {
+ padding: 1rem;
+ }
+
+ .homeButton {
+ width: 100%;
+ max-width: 280px;
+ }
+
+ .errorCode {
+ font-size: 12rem;
+ }
+}
+
+@media (prefers-color-scheme: dark) {
+ .container {
+ background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%);
+ }
+
+ .subtitle {
+ color: #fff;
+ }
+
+ .description {
+ color: #ccc;
+ }
+}
diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx
new file mode 100644
index 00000000..bad6fff9
--- /dev/null
+++ b/src/app/not-found.tsx
@@ -0,0 +1,24 @@
+import Link from "next/link";
+import styles from "./not-found.module.css";
+
+export default function NotFound() {
+ return (
+
+
+
404
+
Página não encontrada
+
+ Ops! A página que você está procurando não existe ou foi removida.
+
+
+
+ Voltar para o início
+
+
+
+
+
+ );
+}
diff --git a/src/app/page.module.css b/src/app/page.module.css
new file mode 100644
index 00000000..37b8eea1
--- /dev/null
+++ b/src/app/page.module.css
@@ -0,0 +1,147 @@
+.page {
+ --gray-rgb: 0, 0, 0;
+ --gray-alpha-200: rgba(var(--gray-rgb), 0.08);
+ --gray-alpha-100: rgba(var(--gray-rgb), 0.05);
+
+ --orange: #ff6600;
+
+ display: flex;
+ /* grid-templatee-rows: 20px 1fr 20px; */
+ flex-direction: column;
+ align-items: center;
+ justify-items: center;
+ min-height: 100svh;
+ padding: 80px;
+ gap: 64px;
+ font-family: var(--font-geist-sans);
+}
+
+@media (prefers-color-scheme: dark) {
+ .page {
+ --gray-rgb: 255, 255, 255;
+ --gray-alpha-200: rgba(var(--gray-rgb), 0.145);
+ --gray-alpha-100: rgba(var(--gray-rgb), 0.06);
+ }
+}
+
+.hero {
+ text-align: center;
+ padding: 4rem 0 4rem 0;
+ background: linear-gradient(135deg, var(--gray-alpha-100) 0%, var(--gray-alpha-200) 100%);
+ border-radius: 1rem;
+ margin-bottom: 3rem;
+ border: 1px solid var(--gray-alpha-200);
+ overflow: hidden;
+}
+
+.heroContent {
+ max-width: 600px;
+ margin: 0 auto;
+ padding: 0 1rem;
+}
+
+.heroTitle {
+ font-size: 2.5rem;
+ font-weight: 800;
+ margin-bottom: 1rem;
+ color: var(--orange);
+ text-shadow:
+ 0 1px 2px rgba(255, 102, 0, 0.3),
+ 0 2px 4px rgba(255, 102, 0, 0.2);
+ letter-spacing: -0.02em;
+ line-height: 1.2;
+ padding-top: 0.5rem;
+}
+
+.heroSubtitle {
+ font-size: 1.25rem;
+ line-height: 1.6;
+ color: var(--foreground);
+ opacity: 0.8;
+ margin-bottom: 2rem;
+ font-weight: 400;
+}
+
+.heroStats {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+ font-size: 0.875rem;
+}
+
+.stat {
+ color: var(--foreground);
+ opacity: 0.7;
+ font-weight: 500;
+}
+
+.stat strong {
+ color: var(--orange);
+ font-weight: 600;
+}
+
+@media (min-width: 768px) {
+ .heroTitle {
+ font-size: 3rem;
+ }
+
+ .heroStats {
+ flex-direction: row;
+ justify-content: center;
+ gap: 2rem;
+ }
+}
+
+.card {
+ background: var(--gray-alpha-100);
+ border-radius: 20px;
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.04);
+ padding: 32px;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ width: 100%;
+ max-width: 400px;
+ border: 1px solid var(--gray-alpha-200);
+}
+
+.btn {
+ padding: 10px 20px;
+ background-color: var(--orange);
+ color: #fff;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+ font-size: 16px;
+ font-weight: bold;
+ transition: background 0.2s;
+}
+
+.img {
+ max-width: 100%;
+ height: auto;
+ display: block;
+ border-radius: 12px;
+ box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.06);
+}
+
+@media (prefers-color-scheme: dark) {
+ .card {
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.16);
+ }
+}
+
+.main-home {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+}
+
+.card-wrapper {
+ display: flex;
+ gap: 30px;
+ flex-wrap: wrap;
+ justify-content: center;
+ margin: 20px 0;
+}
\ No newline at end of file
diff --git a/src/app/page.tsx b/src/app/page.tsx
new file mode 100644
index 00000000..3c009435
--- /dev/null
+++ b/src/app/page.tsx
@@ -0,0 +1,63 @@
+
+import { Metadata } from "next";
+import styles from "./page.module.css";
+import { handleBlogPosts } from "@/usePosts";
+import PostsList from "@/components/PostsList";
+
+export const metadata: Metadata = {
+ title: "Home Blog Apiki - Últimas Postagens sobre Desenvolvimento",
+ description: "Confira as últimas postagens do Blog Apiki sobre desenvolvimento web, WordPress, tecnologia e programação. Conteúdo atualizado para desenvolvedores.",
+ openGraph: {
+ title: "Home Blog Apiki - Últimas Postagens sobre Desenvolvimento",
+ description: "Confira as últimas postagens do Blog Apiki sobre desenvolvimento web, WordPress, tecnologia e programação.",
+ type: "website",
+ },
+};
+
+export default async function Home() {
+ const initialData = await handleBlogPosts(1);
+
+ const jsonLd = {
+ '@context': 'https://schema.org',
+ '@type': 'WebSite',
+ name: 'Blog Apiki',
+ description: 'Blog da Apiki com conteúdos sobre desenvolvimento web, WordPress e tecnologia',
+ url: 'https://blog.apiki.com',
+ mainEntity: {
+ '@type': 'Blog',
+ name: 'Blog Apiki',
+ blogPost: initialData.posts.map(post => ({
+ '@type': 'BlogPosting',
+ headline: post.title.rendered,
+ image: post._embedded["wp:featuredmedia"][0].source_url,
+ url: post.link,
+ }))
+ }
+ };
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/src/app/robots.ts b/src/app/robots.ts
new file mode 100644
index 00000000..08beca3a
--- /dev/null
+++ b/src/app/robots.ts
@@ -0,0 +1,11 @@
+import { MetadataRoute } from 'next'
+
+export default function robots(): MetadataRoute.Robots {
+ return {
+ rules: {
+ userAgent: '*',
+ allow: '/',
+ },
+ sitemap: 'https://blog.apiki.com/sitemap.xml',
+ }
+}
diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts
new file mode 100644
index 00000000..a9b71014
--- /dev/null
+++ b/src/app/sitemap.ts
@@ -0,0 +1,12 @@
+import { MetadataRoute } from 'next'
+
+export default function sitemap(): MetadataRoute.Sitemap {
+ return [
+ {
+ url: 'https://blog.apiki.com',
+ lastModified: new Date(),
+ changeFrequency: 'daily',
+ priority: 1,
+ },
+ ]
+}
diff --git a/src/components/Card/card.module.css b/src/components/Card/card.module.css
new file mode 100644
index 00000000..22caa043
--- /dev/null
+++ b/src/components/Card/card.module.css
@@ -0,0 +1,55 @@
+
+.card {
+ background: var(--gray-alpha-100);
+ border-radius: 20px;
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.04);
+ padding: 32px;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ width: 100%;
+ max-width: 400px;
+ border: 1px solid var(--gray-alpha-200);
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
+}
+
+.card:hover {
+ transform: translateY(-4px);
+ box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.1);
+}
+
+.card a {
+ text-decoration: none;
+ color: inherit;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+}
+
+.cardContent {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.card h2 {
+ margin: 0;
+ font-size: 1.25rem;
+ font-weight: 600;
+ line-height: 1.4;
+}
+
+.date {
+ font-size: 0.875rem;
+ color: var(--foreground);
+ opacity: 0.7;
+ font-weight: 500;
+}
+
+.img {
+ max-width: 100%;
+ height: auto;
+ display: block;
+ border-radius: 12px;
+ box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.06);
+}
\ No newline at end of file
diff --git a/src/components/Card/index.tsx b/src/components/Card/index.tsx
new file mode 100644
index 00000000..cc065f29
--- /dev/null
+++ b/src/components/Card/index.tsx
@@ -0,0 +1,28 @@
+import Image from "next/image";
+import React from "react";
+import styles from "./card.module.css";
+import { CardNamed } from "@/types/CardNamed";
+import Link from "next/link";
+
+export default function SingleCard(post : CardNamed) {
+ const slug = post.link.split('com/')[1]
+ return (
+
+
+
+
+
{post.titulo}
+ {post.data && }
+
+
+
+ );
+}
diff --git a/src/components/LoadMoreButton/index.tsx b/src/components/LoadMoreButton/index.tsx
new file mode 100644
index 00000000..107ccaff
--- /dev/null
+++ b/src/components/LoadMoreButton/index.tsx
@@ -0,0 +1,18 @@
+"use client";
+
+import Button from "../ui/button";
+
+interface LoadMoreButtonProps {
+ onLoadMore: () => void;
+ loading?: boolean;
+}
+
+export default function LoadMoreButton({ onLoadMore, loading = false }: LoadMoreButtonProps) {
+ return (
+
+ );
+}
diff --git a/src/components/PostsList/index.tsx b/src/components/PostsList/index.tsx
new file mode 100644
index 00000000..302f9a99
--- /dev/null
+++ b/src/components/PostsList/index.tsx
@@ -0,0 +1,59 @@
+"use client";
+
+import { useState } from "react";
+import styles from "@/app/page.module.css";
+import { handleBlogPosts, mapPostToBlogPost } from "@/usePosts";
+import SingleCard from "@/components/Card";
+import LoadMoreButton from "@/components/LoadMoreButton";
+import { CardNamed } from "@/types/CardNamed";
+import { BlogResponse } from "@/types/BlogResponse";
+
+interface PostsListProps {
+ initialData: BlogResponse;
+}
+
+export default function PostsList({ initialData }: PostsListProps) {
+ const [posts, setPosts] = useState(
+ initialData.posts.map(post => mapPostToBlogPost(post))
+ );
+ const [currentPage, setCurrentPage] = useState(1);
+ const [loading, setLoading] = useState(false);
+ const [totalPages, setTotalPages] = useState(initialData.totalPages);
+
+ const loadMorePosts = async () => {
+ if (loading || currentPage >= totalPages) return;
+
+ setLoading(true);
+ try {
+ const nextPage = currentPage + 1;
+ const data = await handleBlogPosts(nextPage);
+ const newPosts = data.posts.map(post => mapPostToBlogPost(post));
+
+ setPosts(prevPosts => [...prevPosts, ...newPosts]);
+ setCurrentPage(nextPage);
+ setTotalPages(data.totalPages);
+ } catch (error) {
+ console.error("Erro ao carregar mais posts:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const hasMorePosts = currentPage < totalPages;
+
+ return (
+
+
+ {posts.map((post: CardNamed) => (
+
+ ))}
+
+ {hasMorePosts && (
+
+ )}
+
+ );
+}
diff --git a/src/components/ui/button/button.module.css b/src/components/ui/button/button.module.css
new file mode 100644
index 00000000..bc042311
--- /dev/null
+++ b/src/components/ui/button/button.module.css
@@ -0,0 +1,22 @@
+
+.btn {
+ padding: 10px 20px;
+ background-color: var(--orange);
+ color: #fff;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+ font-size: 16px;
+ font-weight: bold;
+ transition: background 0.2s;
+}
+
+.btn:hover:not(:disabled) {
+ background-color: #e55a00;
+}
+
+.btn:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ background-color: #ccc;
+}
\ No newline at end of file
diff --git a/src/components/ui/button/index.tsx b/src/components/ui/button/index.tsx
new file mode 100644
index 00000000..e938f55f
--- /dev/null
+++ b/src/components/ui/button/index.tsx
@@ -0,0 +1,20 @@
+import React, { ButtonHTMLAttributes } from "react";
+import styles from './button.module.css'
+
+interface ButtonProps extends ButtonHTMLAttributes {
+ content: string;
+ onPress: () => void;
+}
+
+export default function Button({ content, onPress, disabled, ...props }: ButtonProps) {
+ return (
+
+ );
+}
diff --git a/src/types/BlogResponse.ts b/src/types/BlogResponse.ts
new file mode 100644
index 00000000..49a766a6
--- /dev/null
+++ b/src/types/BlogResponse.ts
@@ -0,0 +1,7 @@
+import { Card } from "./Card";
+
+export interface BlogResponse {
+ posts: Card[];
+ totalPages: number;
+ totalPosts: number;
+}
\ No newline at end of file
diff --git a/src/types/Card.ts b/src/types/Card.ts
new file mode 100644
index 00000000..d1acc227
--- /dev/null
+++ b/src/types/Card.ts
@@ -0,0 +1,24 @@
+export type Card = {
+ id: number;
+ _embedded: {
+ "wp:featuredmedia": {
+ source_url: string;
+ alt_text: string;
+ media_details: {
+ height: number;
+ width: number;
+ };
+ }[];
+ };
+ title: {
+ rendered: string;
+ };
+ content?: {
+ rendered: string;
+ };
+ excerpt? : {
+ rendered : string;
+ }
+ date? : Date;
+ link: string;
+};
\ No newline at end of file
diff --git a/src/types/CardNamed.ts b/src/types/CardNamed.ts
new file mode 100644
index 00000000..25ce71b6
--- /dev/null
+++ b/src/types/CardNamed.ts
@@ -0,0 +1,12 @@
+export type CardNamed = {
+ id : number;
+ titulo : string;
+ imagem : string;
+ alt : string;
+ height : number;
+ width: number;
+ link : string;
+ content? : string;
+ data : string;
+ excerpt? : string;
+}
diff --git a/src/usePosts.tsx b/src/usePosts.tsx
new file mode 100644
index 00000000..1bda395f
--- /dev/null
+++ b/src/usePosts.tsx
@@ -0,0 +1,52 @@
+import { BlogResponse } from "./types/BlogResponse";
+import { Card } from "./types/Card";
+import { CardNamed } from "./types/CardNamed";
+
+const handleBlogPosts = async (page: number = 1): Promise => {
+ const URL = process.env.NEXT_PUBLIC_URL ?? "";
+ const response = await fetch(`${URL}&page=${page}`, {
+ next: { revalidate: 86400 }
+ });
+ const data: Card[] = await response.json();
+
+ const totalPages = parseInt(response.headers.get("X-WP-TotalPages") || "1");
+ const totalPosts = parseInt(response.headers.get("X-WP-Total") || "0");
+
+ return {
+ posts: data,
+ totalPages,
+ totalPosts,
+ };
+};
+
+const mapPostToBlogPost = (
+ post: Card,
+ isSinglePost: boolean = false
+): CardNamed => {
+ const formatDate = (dateString?: Date) => {
+ if (!dateString) return undefined;
+ const date = new Date(dateString);
+ return date.toLocaleDateString('pt-BR', {
+ day: '2-digit',
+ month: 'long',
+ year: 'numeric'
+ });
+ };
+
+ return {
+ id: post.id,
+ imagem: post._embedded["wp:featuredmedia"][0].source_url,
+ alt: post._embedded["wp:featuredmedia"][0].alt_text,
+ width: post._embedded["wp:featuredmedia"][0].media_details.width,
+ height: post._embedded["wp:featuredmedia"][0].media_details.height,
+ titulo: post.title.rendered,
+ link: post.link,
+ data: formatDate(post.date) ?? "",
+ ...(isSinglePost ? {
+ content: post.content?.rendered,
+ excerpt: post.excerpt?.rendered
+ } : {}),
+ };
+};
+
+export { handleBlogPosts, mapPostToBlogPost };
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 00000000..c1334095
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}