Repository: bbonkr/wordpress-frontend
Pull request #8: feat: add pagination constants and improve layout for post and category templates #7
Author: login: bbonkr
Labels: name: enhancement
description: New feature or request
Created: 2025-03-06T12:00:46Z | Merged: 2025-03-06T12:03:00Z

## ✔️ Related issues
<!-- If this pull request closes an issue, please mention the issue number below -->

- Resolve #7 

## 📑 Description

<!-- Add a brief description of the pr -->

Add routes

- /categories
- /categories/:slug

## ✅ Checks

<!-- Make sure your pr passes the CI checks and do check the following fields as needed - -->

- [x] My pull request adheres to the code style of this project
- [x] My code requires changes to the documentation
- [x] I have updated the documentation as required
- [ ] All the tests have passed

## ℹ Additional Information

<!-- Any additional information like breaking changes, dependencies added, screenshots, comparisons between new and old behavior, etc. -->


Linked issue #7: Add categories route
### Description

- [x] /categories
- [x] /categories/[slug]



Diff:
diff --git a/package-lock.json b/package-lock.json
index 67b533b..6f28582 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,7 +8,7 @@
         "graphql": "^16.9.0",
         "graphql-tag": "^2.12.6",
         "install": "^0.13.0",
-        "next": "^15.2.0",
+        "next": "^15.2.1",
         "next-themes": "^0.4.4",
         "nextjs-toploader": "^3.7.15",
         "npm": "^11.1.0",
@@ -2048,9 +2048,9 @@
       }
     },
     "node_modules/@next/env": {
-      "version": "15.2.0",
-      "resolved": "https://registry.npmjs.org/@next/env/-/env-15.2.0.tgz",
-      "integrity": "sha512-eMgJu1RBXxxqqnuRJQh5RozhskoNUDHBFybvi+Z+yK9qzKeG7dadhv/Vp1YooSZmCnegf7JxWuapV77necLZNA==",
+      "version": "15.2.1",
+      "resolved": "https://registry.npmjs.org/@next/env/-/env-15.2.1.tgz",
+      "integrity": "sha512-JmY0qvnPuS2NCWOz2bbby3Pe0VzdAQ7XpEB6uLIHmtXNfAsAO0KLQLkuAoc42Bxbo3/jMC3dcn9cdf+piCcG2Q==",
       "license": "MIT"
     },
     "node_modules/@next/eslint-plugin-next": {
@@ -2064,9 +2064,9 @@
       }
     },
     "node_modules/@next/swc-darwin-arm64": {
-      "version": "15.2.0",
-      "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.2.0.tgz",
-      "integrity": "sha512-rlp22GZwNJjFCyL7h5wz9vtpBVuCt3ZYjFWpEPBGzG712/uL1bbSkS675rVAUCRZ4hjoTJ26Q7IKhr5DfJrHDA==",
+      "version": "15.2.1",
+      "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.2.1.tgz",
+      "integrity": "sha512-aWXT+5KEREoy3K5AKtiKwioeblmOvFFjd+F3dVleLvvLiQ/mD//jOOuUcx5hzcO9ISSw4lrqtUPntTpK32uXXQ==",
       "cpu": [
         "arm64"
       ],
@@ -2080,9 +2080,9 @@
       }
     },
     "node_modules/@next/swc-darwin-x64": {
-      "version": "15.2.0",
-      "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.2.0.tgz",
-      "integrity": "sha512-DiU85EqSHogCz80+sgsx90/ecygfCSGl5P3b4XDRVZpgujBm5lp4ts7YaHru7eVTyZMjHInzKr+w0/7+qDrvMA==",
+      "version": "15.2.1",
+      "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.2.1.tgz",
+      "integrity": "sha512-E/w8ervu4fcG5SkLhvn1NE/2POuDCDEy5gFbfhmnYXkyONZR68qbUlJlZwuN82o7BrBVAw+tkR8nTIjGiMW1jQ==",
       "cpu": [
         "x64"
       ],
@@ -2096,9 +2096,9 @@
       }
     },
     "node_modules/@next/swc-linux-arm64-gnu": {
-      "version": "15.2.0",
-      "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.2.0.tgz",
-      "integrity": "sha512-VnpoMaGukiNWVxeqKHwi8MN47yKGyki5q+7ql/7p/3ifuU2341i/gDwGK1rivk0pVYbdv5D8z63uu9yMw0QhpQ==",
+      "version": "15.2.1",
+      "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.2.1.tgz",
+      "integrity": "sha512-gXDX5lIboebbjhiMT6kFgu4svQyjoSed6dHyjx5uZsjlvTwOAnZpn13w9XDaIMFFHw7K8CpBK7HfDKw0VZvUXQ==",
       "cpu": [
         "arm64"
       ],
@@ -2112,9 +2112,9 @@
       }
     },
     "node_modules/@next/swc-linux-arm64-musl": {
-      "version": "15.2.0",
-      "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.2.0.tgz",
-      "integrity": "sha512-ka97/ssYE5nPH4Qs+8bd8RlYeNeUVBhcnsNUmFM6VWEob4jfN9FTr0NBhXVi1XEJpj3cMfgSRW+LdE3SUZbPrw==",
+      "version": "15.2.1",
+      "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.2.1.tgz",
+      "integrity": "sha512-3v0pF/adKZkBWfUffmB/ROa+QcNTrnmYG4/SS+r52HPwAK479XcWoES2I+7F7lcbqc7mTeVXrIvb4h6rR/iDKg==",
       "cpu": [
         "arm64"
       ],
@@ -2128,9 +2128,9 @@
       }
     },
     "node_modules/@next/swc-linux-x64-gnu": {
-      "version": "15.2.0",
-      "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.2.0.tgz",
-      "integrity": "sha512-zY1JduE4B3q0k2ZCE+DAF/1efjTXUsKP+VXRtrt/rJCTgDlUyyryx7aOgYXNc1d8gobys/Lof9P9ze8IyRDn7Q==",
+      "version": "15.2.1",
+      "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.2.1.tgz",
+      "integrity": "sha512-RbsVq2iB6KFJRZ2cHrU67jLVLKeuOIhnQB05ygu5fCNgg8oTewxweJE8XlLV+Ii6Y6u4EHwETdUiRNXIAfpBww==",
       "cpu": [
         "x64"
       ],
@@ -2144,9 +2144,9 @@
       }
     },
     "node_modules/@next/swc-linux-x64-musl": {
-      "version": "15.2.0",
-      "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.2.0.tgz",
-      "integrity": "sha512-QqvLZpurBD46RhaVaVBepkVQzh8xtlUN00RlG4Iq1sBheNugamUNPuZEH1r9X1YGQo1KqAe1iiShF0acva3jHQ==",
+      "version": "15.2.1",
+      "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.2.1.tgz",
+      "integrity": "sha512-QHsMLAyAIu6/fWjHmkN/F78EFPKmhQlyX5C8pRIS2RwVA7z+t9cTb0IaYWC3EHLOTjsU7MNQW+n2xGXr11QPpg==",
       "cpu": [
         "x64"
       ],
@@ -2160,9 +2160,9 @@
       }
     },
     "node_modules/@next/swc-win32-arm64-msvc": {
-      "version": "15.2.0",
-      "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.2.0.tgz",
-      "integrity": "sha512-ODZ0r9WMyylTHAN6pLtvUtQlGXBL9voljv6ujSlcsjOxhtXPI1Ag6AhZK0SE8hEpR1374WZZ5w33ChpJd5fsjw==",
+      "version": "15.2.1",
+      "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.2.1.tgz",
+      "integrity": "sha512-Gk42XZXo1cE89i3hPLa/9KZ8OuupTjkDmhLaMKFohjf9brOeZVEa3BQy1J9s9TWUqPhgAEbwv6B2+ciGfe54Vw==",
       "cpu": [
         "arm64"
       ],
@@ -2176,9 +2176,9 @@
       }
     },
     "node_modules/@next/swc-win32-x64-msvc": {
-      "version": "15.2.0",
-      "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.2.0.tgz",
-      "integrity": "sha512-8+4Z3Z7xa13NdUuUAcpVNA6o76lNPniBd9Xbo02bwXQXnZgFvEopwY2at5+z7yHl47X9qbZpvwatZ2BRo3EdZw==",
+      "version": "15.2.1",
+      "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.2.1.tgz",
+      "integrity": "sha512-YjqXCl8QGhVlMR8uBftWk0iTmvtntr41PhG1kvzGp0sUP/5ehTM+cwx25hKE54J0CRnHYjSGjSH3gkHEaHIN9g==",
       "cpu": [
         "x64"
       ],
@@ -2974,6 +2974,7 @@
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
       "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "clean-stack": "^2.0.0",
@@ -3030,6 +3031,7 @@
       "version": "4.3.0",
       "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
       "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "color-convert": "^2.0.1"
@@ -3327,6 +3329,7 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
       "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true,
       "license": "MIT"
     },
     "node_modules/base64-js": {
@@ -3366,6 +3369,7 @@
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "balanced-match": "^1.0.0",
@@ -3571,6 +3575,7 @@
       "version": "4.1.2",
       "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
       "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "ansi-styles": "^4.1.0",
@@ -3634,6 +3639,7 @@
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
       "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=6"
@@ -3759,6 +3765,7 @@
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
       "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "devOptional": true,
       "license": "MIT",
       "dependencies": {
         "color-name": "~1.1.4"
@@ -3771,6 +3778,7 @@
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
       "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "devOptional": true,
       "license": "MIT"
     },
     "node_modules/color-string": {
@@ -3805,6 +3813,7 @@
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
       "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+      "dev": true,
       "license": "MIT"
     },
     "node_modules/constant-case": {
@@ -5303,6 +5312,7 @@
       "version": "4.2.11",
       "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
       "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+      "dev": true,
       "license": "ISC"
     },
     "node_modules/graphemer": {
@@ -5462,6 +5472,7 @@
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
       "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=8"
@@ -5685,6 +5696,7 @@
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
       "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=8"
@@ -6280,6 +6292,7 @@
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
       "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+      "dev": true,
       "license": "ISC"
     },
     "node_modules/isomorphic-ws": {
@@ -6373,6 +6386,7 @@
       "version": "2.3.1",
       "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
       "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+      "dev": true,
       "license": "MIT"
     },
     "node_modules/json-schema-traverse": {
@@ -6990,6 +7004,7 @@
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
       "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
       "license": "ISC",
       "dependencies": {
         "brace-expansion": "^1.1.7"
@@ -7012,6 +7027,7 @@
       "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/mute-stream": {
@@ -7047,12 +7063,12 @@
       "license": "MIT"
     },
     "node_modules/next": {
-      "version": "15.2.0",
-      "resolved": "https://registry.npmjs.org/next/-/next-15.2.0.tgz",
-      "integrity": "sha512-VaiM7sZYX8KIAHBrRGSFytKknkrexNfGb8GlG6e93JqueCspuGte8i4ybn8z4ww1x3f2uzY4YpTaBEW4/hvsoQ==",
+      "version": "15.2.1",
+      "resolved": "https://registry.npmjs.org/next/-/next-15.2.1.tgz",
+      "integrity": "sha512-zxbsdQv3OqWXybK5tMkPCBKyhIz63RstJ+NvlfkaLMc/m5MwXgz2e92k+hSKcyBpyADhMk2C31RIiaDjUZae7g==",
       "license": "MIT",
       "dependencies": {
-        "@next/env": "15.2.0",
+        "@next/env": "15.2.1",
         "@swc/counter": "0.1.3",
         "@swc/helpers": "0.5.15",
         "busboy": "1.6.0",
@@ -7067,14 +7083,14 @@
         "node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
       },
       "optionalDependencies": {
-        "@next/swc-darwin-arm64": "15.2.0",
-        "@next/swc-darwin-x64": "15.2.0",
-        "@next/swc-linux-arm64-gnu": "15.2.0",
-        "@next/swc-linux-arm64-musl": "15.2.0",
-        "@next/swc-linux-x64-gnu": "15.2.0",
-        "@next/swc-linux-x64-musl": "15.2.0",
-        "@next/swc-win32-arm64-msvc": "15.2.0",
-        "@next/swc-win32-x64-msvc": "15.2.0",
+        "@next/swc-darwin-arm64": "15.2.1",
+        "@next/swc-darwin-x64": "15.2.1",
+        "@next/swc-linux-arm64-gnu": "15.2.1",
+        "@next/swc-linux-arm64-musl": "15.2.1",
+        "@next/swc-linux-x64-gnu": "15.2.1",
+        "@next/swc-linux-x64-musl": "15.2.1",
+        "@next/swc-win32-arm64-msvc": "15.2.1",
+        "@next/swc-win32-x64-msvc": "15.2.1",
         "sharp": "^0.33.5"
       },
       "peerDependencies": {
@@ -9947,6 +9963,7 @@
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
       "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "aggregate-error": "^3.0.0"
@@ -10591,6 +10608,7 @@
       "version": "6.3.1",
       "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
       "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
       "license": "ISC",
       "bin": {
         "semver": "bin/semver.js"
@@ -11145,6 +11163,7 @@
       "version": "7.2.0",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
       "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "has-flag": "^4.0.0"
@@ -11739,6 +11758,7 @@
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
       "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "dev": true,
       "license": "ISC",
       "dependencies": {
         "isexe": "^2.0.0"
diff --git a/package.json b/package.json
index 307a18e..8aebf75 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,7 @@
     "graphql": "^16.9.0",
     "graphql-tag": "^2.12.6",
     "install": "^0.13.0",
-    "next": "^15.2.0",
+    "next": "^15.2.1",
     "next-themes": "^0.4.4",
     "nextjs-toploader": "^3.7.15",
     "npm": "^11.1.0",
diff --git a/src/app/[slug]/page.tsx b/src/app/[slug]/page.tsx
index 259c687..f96f56e 100644
--- a/src/app/[slug]/page.tsx
+++ b/src/app/[slug]/page.tsx
@@ -11,7 +11,6 @@ import PageTemplate from "@/components/Templates/Page/PageTemplate";
 import { nextSlugToWpSlug } from "@/utils/nextSlugToWpSlug";
 import PostTemplate from "@/components/Templates/Post/PostTemplate";
 import { SeoQuery } from "@/queries/general/SeoQuery";
-import PostListTemplate from "@/components/Templates/PostList/PostListTemplate";
 
 type Props = {
   params: Promise<{
diff --git a/src/app/categories/[slug]/page.tsx b/src/app/categories/[slug]/page.tsx
new file mode 100644
index 0000000..35d3422
--- /dev/null
+++ b/src/app/categories/[slug]/page.tsx
@@ -0,0 +1,44 @@
+export const dynamic = "force-dynamic";
+import type { Metadata } from "next";
+import CategoryPostsTemplate from "@/components/Templates/CategoryPosts/CategoryPostsTemplate";
+
+type Props = {
+  params: Promise<{
+    slug: string;
+  }>;
+  searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
+};
+
+export async function generateMetadata({ params }: Props): Promise<Metadata> {
+  const { slug } = await params;
+
+  return {
+    alternates: {
+      canonical: `${process.env.NEXT_PUBLIC_BASE_URL}/categories/${slug}`,
+    },
+  };
+}
+
+export function generateStaticParams() {
+  return [];
+}
+
+export default async function CategoryPostsPage({
+  params,
+  searchParams,
+}: Readonly<Props>) {
+  const { slug } = await params;
+  const searchParamsValue = await searchParams;
+  const { after, before, first, last, s } = searchParamsValue ?? {};
+
+  return (
+    <CategoryPostsTemplate
+      categorySlug={slug}
+      after={after?.toString()}
+      before={before?.toString()}
+      first={first?.toString()}
+      last={last?.toString()}
+      s={s?.toString()}
+    />
+  );
+}
diff --git a/src/app/categories/page.tsx b/src/app/categories/page.tsx
new file mode 100644
index 0000000..cbeea03
--- /dev/null
+++ b/src/app/categories/page.tsx
@@ -0,0 +1,35 @@
+import type { Metadata } from "next";
+import CategoriesListTemplate from "@/components/Templates/Categories/CategoryListTemplate";
+
+type Props = {
+  searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
+};
+
+export async function generateMetadata(): Promise<Metadata> {
+  return {
+    alternates: {
+      canonical: `${process.env.NEXT_PUBLIC_BASE_URL}/categories`,
+    },
+  };
+}
+
+export function generateStaticParams() {
+  return [];
+}
+
+export default async function CategoriesPage({
+  searchParams,
+}: Readonly<Props>) {
+  const searchParamsValue = await searchParams;
+  const { after, before, first, last, s } = searchParamsValue ?? {};
+
+  return (
+    <CategoriesListTemplate
+      after={after?.toString()}
+      before={before?.toString()}
+      first={first?.toString()}
+      last={last?.toString()}
+      s={s?.toString()}
+    />
+  );
+}
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 95674ca..9d4fbfe 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -1,17 +1,5 @@
 import type { Metadata } from "next";
-import { notFound } from "next/navigation";
-import { print } from "graphql/language/printer";
-
-import { setSeoData } from "@/utils/seoData";
-
-import { fetchGraphQL } from "@/utils/fetchGraphQL";
-import { ContentInfoQuery } from "@/queries/general/ContentInfoQuery";
-import { ContentNode } from "@/gql/graphql";
-import PageTemplate from "@/components/Templates/Page/PageTemplate";
-import { nextSlugToWpSlug } from "@/utils/nextSlugToWpSlug";
-import PostTemplate from "@/components/Templates/Post/PostTemplate";
-import { SeoQuery } from "@/queries/general/SeoQuery";
-import PostListTemplate from "@/components/Templates/PostList/PostListTemplate";
+import PostListTemplate from "@/components/Templates/Posts/PostListTemplate";
 
 type Props = {
   searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
diff --git a/src/components/Templates/Categories/CategoryListQuery.ts b/src/components/Templates/Categories/CategoryListQuery.ts
new file mode 100644
index 0000000..bf75201
--- /dev/null
+++ b/src/components/Templates/Categories/CategoryListQuery.ts
@@ -0,0 +1,33 @@
+import gql from "graphql-tag";
+
+export const CategoriesListQuery = gql`
+  query categoriesQuery(
+    $after: String = ""
+    $before: String = ""
+    $first: Int = 0
+    $last: Int = 0
+  ) {
+    categories(
+      where: { order: ASC, orderby: TERM_ORDER, childOf: 3 }
+      after: $after
+      before: $before
+      first: $first
+      last: $last
+    ) {
+      nodes {
+        categoryId
+        count
+        slug
+        name
+        uri
+        description
+      }
+      pageInfo {
+        endCursor
+        hasNextPage
+        hasPreviousPage
+        startCursor
+      }
+    }
+  }
+`;
diff --git a/src/components/Templates/Categories/CategoryListTemplate.module.css b/src/components/Templates/Categories/CategoryListTemplate.module.css
new file mode 100644
index 0000000..40d35b1
--- /dev/null
+++ b/src/components/Templates/Categories/CategoryListTemplate.module.css
@@ -0,0 +1,20 @@
+.container {
+  max-width: 1000px;
+  margin: 0 auto;
+  padding: 30px;
+}
+
+.title {
+  text-align: center;
+}
+
+.author {
+  text-align: center;
+  color: #666;
+  margin: 30px 0;
+}
+
+.body > * {
+  word-wrap: break-word;
+  text-wrap: auto;
+}
diff --git a/src/components/Templates/Categories/CategoryListTemplate.tsx b/src/components/Templates/Categories/CategoryListTemplate.tsx
new file mode 100644
index 0000000..0d34296
--- /dev/null
+++ b/src/components/Templates/Categories/CategoryListTemplate.tsx
@@ -0,0 +1,79 @@
+import { CategoryConnection } from "@/gql/graphql";
+import { CategoriesListQuery } from "./CategoryListQuery";
+import { fetchGraphQL } from "@/utils/fetchGraphQL";
+import { print } from "graphql/language/printer";
+
+import Link from "next/link";
+import constants from "@/constants";
+import PaginationTemplate from "../Pagination/PaginationTemplate";
+import styles from "./CategoryListTemplate.module.css";
+interface PostListTemplate {
+  after?: string;
+  before?: string;
+  first?: string;
+  last?: string;
+  s?: string;
+  showPagination?: boolean;
+}
+
+export default async function CategoriesListTemplate({
+  after,
+  before,
+  first,
+  last,
+  s,
+  showPagination,
+}: Readonly<PostListTemplate>) {
+  const firstValue =
+    !after && !before && !last ? `${constants.pagination.first}` : first;
+
+  const { categories } = await fetchGraphQL<{
+    categories: CategoryConnection;
+  }>(print(CategoriesListQuery), {
+    after: after,
+    before: before,
+    first: firstValue ? parseInt(firstValue, 10) : undefined,
+    last: last ? parseInt(last, 10) : undefined,
+    s: s,
+  });
+
+  return (
+    <div
+      className={`w-full px-3 md:px-10 flex flex-col flex-1 justify-between ${styles.container}`}
+    >
+      {categories?.nodes?.length > 0 ? (
+        <ul className="flex-1">
+          {categories?.nodes.map((node) => {
+            return (
+              <li key={node.slug} className="py-1">
+                <Link href={`/categories/${node.slug}`}>
+                  <span>{node?.name}</span>
+                </Link>
+              </li>
+            );
+          })}
+        </ul>
+      ) : (
+        <div className="flex-1 flex flex-col justify-center items-center ">
+          <div className="flex flex-col gap-3 w-full">
+            <p>There is no category.</p>
+            <p>You can find the article you want in the list.</p>
+            <p>
+              <Link href="/">Navigate to post list page</Link>
+            </p>
+          </div>
+        </div>
+      )}
+
+      {showPagination && (
+        <>
+          <hr />
+          <PaginationTemplate
+            route="/categories/"
+            pageInfo={categories.pageInfo}
+          />
+        </>
+      )}
+    </div>
+  );
+}
diff --git a/src/components/Templates/CategoryPosts/CategoryPostsQuery.ts b/src/components/Templates/CategoryPosts/CategoryPostsQuery.ts
new file mode 100644
index 0000000..8174f95
--- /dev/null
+++ b/src/components/Templates/CategoryPosts/CategoryPostsQuery.ts
@@ -0,0 +1,30 @@
+import gql from "graphql-tag";
+
+export const CategoryPostsQuery = gql`
+  query postsOfCategoryQuery(
+    $id: ID = ""
+    $after: String = ""
+    $before: String = ""
+    $first: Int
+    $last: Int
+  ) {
+    category(id: $id, idType: SLUG) {
+      posts(after: $after, before: $before, first: $first, last: $last) {
+        nodes {
+          id
+          excerpt
+          slug
+          title
+          date
+          dateGmt
+        }
+        pageInfo {
+          endCursor
+          hasNextPage
+          hasPreviousPage
+          startCursor
+        }
+      }
+    }
+  }
+`;
diff --git a/src/components/Templates/CategoryPosts/CategoryPostsTemplate.module.css b/src/components/Templates/CategoryPosts/CategoryPostsTemplate.module.css
new file mode 100644
index 0000000..40d35b1
--- /dev/null
+++ b/src/components/Templates/CategoryPosts/CategoryPostsTemplate.module.css
@@ -0,0 +1,20 @@
+.container {
+  max-width: 1000px;
+  margin: 0 auto;
+  padding: 30px;
+}
+
+.title {
+  text-align: center;
+}
+
+.author {
+  text-align: center;
+  color: #666;
+  margin: 30px 0;
+}
+
+.body > * {
+  word-wrap: break-word;
+  text-wrap: auto;
+}
diff --git a/src/components/Templates/CategoryPosts/CategoryPostsTemplate.tsx b/src/components/Templates/CategoryPosts/CategoryPostsTemplate.tsx
new file mode 100644
index 0000000..9e00af8
--- /dev/null
+++ b/src/components/Templates/CategoryPosts/CategoryPostsTemplate.tsx
@@ -0,0 +1,69 @@
+import { print } from "graphql/language/printer";
+
+import { Category } from "@/gql/graphql";
+import { fetchGraphQL } from "@/utils/fetchGraphQL";
+
+import { CategoryPostsQuery } from "./CategoryPostsQuery";
+import Link from "next/link";
+import ListOfPostTemplate from "../ListOfPosts/ListOfPostTemplate";
+import PaginationTemplate from "../Pagination/PaginationTemplate";
+import constants from "@/constants";
+import styles from "./CategoryPostsTemplate.module.css";
+
+interface PostListTemplate {
+  categorySlug: string;
+  after?: string;
+  before?: string;
+  first?: string;
+  last?: string;
+  s?: string;
+}
+
+export default async function CategoryPostsTemplate({
+  categorySlug,
+  after,
+  before,
+  first,
+  last,
+  s,
+}: Readonly<PostListTemplate>) {
+  const firstValue =
+    !after && !before && !last ? `${constants.pagination.first}` : first;
+
+  const { category } = await fetchGraphQL<{
+    category: Category;
+  }>(print(CategoryPostsQuery), {
+    id: categorySlug,
+    after: after,
+    before: before,
+    first: firstValue ? parseInt(firstValue, 10) : undefined,
+    last: last ? parseInt(last, 10) : undefined,
+    s: s,
+  });
+
+  return (
+    <div
+      className={`w-full px-3 md:px-10 flex flex-col flex-1 justify-between ${styles.container}`}
+    >
+      {(category?.posts?.nodes?.length ?? 0) > 0 ? (
+        <ListOfPostTemplate route="/" posts={category?.posts?.nodes} />
+      ) : (
+        <div className="flex-1 flex flex-col justify-center items-center ">
+          <div className="flex flex-col gap-3 w-full">
+            <p>There is no post in this category.</p>
+            <p>You can find the article you want in the list.</p>
+            <p>
+              <Link href="/">Navigate to post list page</Link>
+            </p>
+          </div>
+        </div>
+      )}
+
+      <hr />
+      <PaginationTemplate
+        route={`/categories/${categorySlug}/`}
+        pageInfo={category?.posts?.pageInfo}
+      />
+    </div>
+  );
+}
diff --git a/src/components/Templates/ListOfPosts/ListOfPostTemplate.tsx b/src/components/Templates/ListOfPosts/ListOfPostTemplate.tsx
new file mode 100644
index 0000000..b94b62f
--- /dev/null
+++ b/src/components/Templates/ListOfPosts/ListOfPostTemplate.tsx
@@ -0,0 +1,41 @@
+import { Post } from "@/gql/graphql";
+import Link from "next/link";
+
+interface ListOfPostTemplateProps {
+  posts: Post[] | undefined;
+  /**
+   * route should end with '/'
+   */
+  route: string | undefined;
+}
+export default async function ListOfPostTemplate({
+  posts,
+  route,
+}: Readonly<ListOfPostTemplateProps>) {
+  if (!posts || posts.length === 0) {
+    return (
+      <div className="flex-1 flex flex-col justify-center items-center ">
+        <div className="flex flex-col gap-3 w-full">
+          <p>There is no posts.</p>
+          <p>You can find the posts you want in the list.</p>
+          <p>
+            <Link href="/">Navigate to post list page</Link>
+          </p>
+        </div>
+      </div>
+    );
+  }
+  return (
+    <ul className="flex-1">
+      {posts.map((node) => {
+        return (
+          <li key={node.slug} className="py-1">
+            <Link href={`${route ?? "/"}${node.slug}`}>
+              <span>{node.title}</span>
+            </Link>
+          </li>
+        );
+      })}
+    </ul>
+  );
+}
diff --git a/src/components/Templates/Pagination/PaginationTemplate.tsx b/src/components/Templates/Pagination/PaginationTemplate.tsx
new file mode 100644
index 0000000..b2ddef4
--- /dev/null
+++ b/src/components/Templates/Pagination/PaginationTemplate.tsx
@@ -0,0 +1,66 @@
+import { PageInfo } from "@/gql/graphql";
+import Link from "next/link";
+import Constants from "@/constants";
+
+interface PaginationTemplateProps {
+  pageInfo: PageInfo | undefined;
+  /**
+   * Route should end with '/'
+   */
+  route: string | undefined;
+}
+
+export default function PaginationTemplate({
+  pageInfo,
+  route,
+}: Readonly<PaginationTemplateProps>) {
+  return (
+    <aside className="flex flex-col justify-center items-center py-6">
+      <ul className="flex flex-row gap-3">
+        <li>
+          {pageInfo?.hasPreviousPage ? (
+            <Link href={`${route ?? "/"}`}>First</Link>
+          ) : (
+            <span className="cursor-not-allowed">First</span>
+          )}
+        </li>
+        <li>
+          {pageInfo?.hasPreviousPage ? (
+            <Link
+              href={`${route ?? "/"}?before=${pageInfo.startCursor}&last=${
+                Constants.pagination.last
+              }`}
+            >
+              Previous ({pageInfo?.hasPreviousPage ? "✅" : "❌"})
+            </Link>
+          ) : (
+            <span className="cursor-not-allowed">Previous</span>
+          )}
+        </li>
+
+        <li>
+          {pageInfo?.hasNextPage ? (
+            <Link
+              href={`${route ?? "/"}?after=${pageInfo?.endCursor}&first=${
+                Constants.pagination.first
+              }`}
+            >
+              Next ({pageInfo?.hasNextPage ? "✅" : "❌"})
+            </Link>
+          ) : (
+            <span className="cursor-not-allowed">Next</span>
+          )}
+        </li>
+        <li>
+          {pageInfo?.hasNextPage ? (
+            <Link href={`${route ?? "/"}?last=${Constants.pagination.last}`}>
+              Last
+            </Link>
+          ) : (
+            <span className="cursor-not-allowed">Last</span>
+          )}
+        </li>
+      </ul>
+    </aside>
+  );
+}
diff --git a/src/components/Templates/Post/PostTemplate.module.css b/src/components/Templates/Post/PostTemplate.module.css
index 0f4abf2..e154e7b 100644
--- a/src/components/Templates/Post/PostTemplate.module.css
+++ b/src/components/Templates/Post/PostTemplate.module.css
@@ -1,4 +1,7 @@
 .post {
+  max-width: 1000px;
+  margin: 0 auto;
+  padding: 30px;
 }
 
 .title {
diff --git a/src/components/Templates/PostList/PostListTemplate.tsx b/src/components/Templates/PostList/PostListTemplate.tsx
deleted file mode 100644
index 3fce7ee..0000000
--- a/src/components/Templates/PostList/PostListTemplate.tsx
+++ /dev/null
@@ -1,109 +0,0 @@
-import { print } from "graphql/language/printer";
-
-import { PostConnection } from "@/gql/graphql";
-import { fetchGraphQL } from "@/utils/fetchGraphQL";
-
-import { PostListQuery } from "./PostListQuery";
-import Link from "next/link";
-
-interface PostListTemplate {
-  after?: string;
-  before?: string;
-  first?: string;
-  last?: string;
-  s?: string;
-}
-
-const FIRST = 10;
-const LAST = 10;
-
-export default async function PostListTemplate({
-  after,
-  before,
-  first,
-  last,
-  s,
-}: Readonly<PostListTemplate>) {
-  const firstValue = !after && !before && !last ? `${FIRST}` : first;
-
-  const { posts } = await fetchGraphQL<{
-    posts: PostConnection;
-  }>(print(PostListQuery), {
-    after: after,
-    before: before,
-    first: firstValue ? parseInt(firstValue, 10) : undefined,
-    last: last ? parseInt(last, 10) : undefined,
-    s: s,
-  });
-
-  return (
-    <div className="w-full px-3 md:px-10 flex flex-col flex-1 justify-between">
-      {posts?.nodes?.length > 0 ? (
-        <ul className="flex-1">
-          {posts?.nodes.map((node) => {
-            return (
-              <li key={node.id} className="py-1">
-                <Link href={`/${node.slug}`}>
-                  <span>{node?.title}</span>
-                </Link>
-              </li>
-            );
-          })}
-        </ul>
-      ) : (
-        <div className="flex-1 flex flex-col justify-center items-center ">
-          <div className="flex flex-col gap-3 w-full">
-            <p>
-              There is no posts for the keyword <strong>{s}</strong>.
-            </p>
-            <p>You can find the article you want in the list.</p>
-            <p>
-              <Link href="/">Navigate to post list page</Link>
-            </p>
-          </div>
-        </div>
-      )}
-
-      <hr />
-      <aside className="flex flex-col justify-center items-center py-6">
-        <ul className="flex flex-row gap-3">
-          <li>
-            {posts.pageInfo.hasPreviousPage ? (
-              <Link href="/">First</Link>
-            ) : (
-              <span className="cursor-not-allowed">First</span>
-            )}
-          </li>
-          <li>
-            {posts.pageInfo.hasPreviousPage ? (
-              <Link
-                href={`/?before=${posts.pageInfo.startCursor}&last=${LAST}`}
-              >
-                Previous ({posts.pageInfo.hasPreviousPage ? "✅" : "❌"})
-              </Link>
-            ) : (
-              <span className="cursor-not-allowed">Previous</span>
-            )}
-          </li>
-
-          <li>
-            {posts.pageInfo.hasNextPage ? (
-              <Link href={`/?after=${posts.pageInfo.endCursor}&first=${FIRST}`}>
-                Next ({posts.pageInfo.hasNextPage ? "✅" : "❌"})
-              </Link>
-            ) : (
-              <span className="cursor-not-allowed">Next</span>
-            )}
-          </li>
-          <li>
-            {posts.pageInfo.hasNextPage ? (
-              <Link href={`/?last=${LAST}`}>Last</Link>
-            ) : (
-              <span className="cursor-not-allowed">Last</span>
-            )}
-          </li>
-        </ul>
-      </aside>
-    </div>
-  );
-}
diff --git a/src/components/Templates/PostList/PostListQuery.ts b/src/components/Templates/Posts/PostListQuery.ts
similarity index 100%
rename from src/components/Templates/PostList/PostListQuery.ts
rename to src/components/Templates/Posts/PostListQuery.ts
diff --git a/src/components/Templates/Posts/PostListTemplate.module.css b/src/components/Templates/Posts/PostListTemplate.module.css
new file mode 100644
index 0000000..40d35b1
--- /dev/null
+++ b/src/components/Templates/Posts/PostListTemplate.module.css
@@ -0,0 +1,20 @@
+.container {
+  max-width: 1000px;
+  margin: 0 auto;
+  padding: 30px;
+}
+
+.title {
+  text-align: center;
+}
+
+.author {
+  text-align: center;
+  color: #666;
+  margin: 30px 0;
+}
+
+.body > * {
+  word-wrap: break-word;
+  text-wrap: auto;
+}
diff --git a/src/components/Templates/Posts/PostListTemplate.tsx b/src/components/Templates/Posts/PostListTemplate.tsx
new file mode 100644
index 0000000..7c39072
--- /dev/null
+++ b/src/components/Templates/Posts/PostListTemplate.tsx
@@ -0,0 +1,66 @@
+import { print } from "graphql/language/printer";
+
+import { PostConnection } from "@/gql/graphql";
+import { fetchGraphQL } from "@/utils/fetchGraphQL";
+
+import { PostListQuery } from "./PostListQuery";
+import Link from "next/link";
+import ListOfPostTemplate from "../ListOfPosts/ListOfPostTemplate";
+import PaginationTemplate from "../Pagination/PaginationTemplate";
+import constants from "@/constants";
+
+import styles from "./PostListTemplate.module.css";
+
+interface PostListTemplate {
+  after?: string;
+  before?: string;
+  first?: string;
+  last?: string;
+  s?: string;
+}
+
+export default async function PostListTemplate({
+  after,
+  before,
+  first,
+  last,
+  s,
+}: Readonly<PostListTemplate>) {
+  const firstValue =
+    !after && !before && !last ? `${constants.pagination.first}` : first;
+
+  const { posts } = await fetchGraphQL<{
+    posts: PostConnection;
+  }>(print(PostListQuery), {
+    after: after,
+    before: before,
+    first: firstValue ? parseInt(firstValue, 10) : undefined,
+    last: last ? parseInt(last, 10) : undefined,
+    s: s,
+  });
+
+  return (
+    <div
+      className={`w-full px-3 md:px-10 flex flex-col flex-1 justify-between ${styles.container}`}
+    >
+      {posts?.nodes?.length > 0 ? (
+        <ListOfPostTemplate route="/" posts={posts.nodes} />
+      ) : (
+        <div className="flex-1 flex flex-col justify-center items-center ">
+          <div className="flex flex-col gap-3 w-full">
+            <p>
+              There is no posts for the keyword <strong>{s}</strong>.
+            </p>
+            <p>You can find the article you want in the list.</p>
+            <p>
+              <Link href="/">Navigate to post list page</Link>
+            </p>
+          </div>
+        </div>
+      )}
+
+      <hr />
+      <PaginationTemplate route="/" pageInfo={posts.pageInfo} />
+    </div>
+  );
+}
diff --git a/src/constants.ts b/src/constants.ts
new file mode 100644
index 0000000..8f8211a
--- /dev/null
+++ b/src/constants.ts
@@ -0,0 +1,6 @@
+export default {
+  pagination: {
+    first: 10,
+    last: 10,
+  } as const,
+} as const;


Reviews:
user: login: gemini-code-assist[bot]
body: Hello @bbonkr, I'm Gemini Code Assist[^1]! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

## Summary of Changes
This pull request introduces new routes for categories and category-specific posts, enhancing the site's navigation and content organization. It also updates the `next` dependency to version 15.2.1. The changes include adding new templates for displaying categories and category posts, along with associated queries and styling. Pagination has been implemented for both categories and category-specific posts.

### Highlights
* **New Routes**: Adds `/categories` and `/categories/:slug` routes to display lists of categories and posts within a specific category, respectively.
* **Category Templates**: Introduces `CategoriesListTemplate` and `CategoryPostsTemplate` components for rendering category lists and category-specific posts.
* **Pagination**: Implements pagination for both the categories list and category posts, allowing users to navigate through large sets of content.
* **Dependency Update**: Updates the `next` dependency from version 15.2.0 to 15.2.1.

### Changelog
<details>
<summary>Click here to see the changelog</summary>

* **package-lock.json**
  * Updates the version of `next` and its related dependencies (e.g., `@next/env`, `@next/swc-*`) from 15.2.0 to 15.2.1.
  * Marks several dependencies as `dev` or `devOptional` to clarify their usage.
* **package.json**
  * Updates the `next` dependency from version 15.2.0 to 15.2.1.
* **src/app/[slug]/page.tsx**
  * Removes the import of `PostListTemplate` as it is no longer used in this file. This is line 14.
* **src/app/categories/[slug]/page.tsx**
  * Adds a new route for displaying posts within a specific category, including metadata generation and dynamic route handling.
  * Implements `generateMetadata` to set canonical URLs for category pages (lines 12-19).
  * Uses `CategoryPostsTemplate` to render the category posts with pagination support (lines 35-42).
* **src/app/categories/page.tsx**
  * Adds a new route for displaying a list of categories, including metadata generation.
  * Implements `generateMetadata` to set canonical URLs for the categories page (lines 8-13).
  * Uses `CategoriesListTemplate` to render the list of categories with pagination support (lines 27-33).
* **src/app/page.tsx**
  * Replaces the existing content with a `PostListTemplate` to display a list of posts on the homepage.
  * Removes unused imports related to page and post templates (lines 2, 3, 5, 6, 8, 9, 10, 11, 12, 13, 14).
* **src/components/Templates/Categories/CategoryListQuery.ts**
  * Adds a GraphQL query (`CategoriesListQuery`) to fetch a list of categories with pagination support.
* **src/components/Templates/Categories/CategoryListTemplate.module.css**
  * Adds CSS styles for the `CategoriesListTemplate` component, including container, title, author, and body styles.
* **src/components/Templates/Categories/CategoryListTemplate.tsx**
  * Creates a `CategoriesListTemplate` component to display a list of categories with links to their respective pages.
  * Fetches category data using `CategoriesListQuery` and renders it in a list format (lines 30-38).
  * Includes pagination support using the `PaginationTemplate` component (lines 69-74).
* **src/components/Templates/CategoryPosts/CategoryPostsQuery.ts**
  * Adds a GraphQL query (`CategoryPostsQuery`) to fetch posts for a specific category with pagination support.
* **src/components/Templates/CategoryPosts/CategoryPostsTemplate.module.css**
  * Adds CSS styles for the `CategoryPostsTemplate` component, including container, title, author, and body styles.
* **src/components/Templates/CategoryPosts/CategoryPostsTemplate.tsx**
  * Creates a `CategoryPostsTemplate` component to display posts for a specific category.
  * Fetches category and post data using `CategoryPostsQuery` and renders it using `ListOfPostTemplate` (lines 33-42).
  * Includes pagination support using the `PaginationTemplate` component (lines 62-66).
* **src/components/Templates/ListOfPosts/ListOfPostTemplate.tsx**
  * Creates a `ListOfPostTemplate` component to display a list of posts with links to their respective pages.
  * Handles the case where there are no posts to display (lines 15-26).
  * Renders the list of posts with links to their respective pages (lines 28-41).
* **src/components/Templates/Pagination/PaginationTemplate.tsx**
  * Creates a `PaginationTemplate` component to display pagination links for navigating through pages of content.
  * Displays 'First', 'Previous', 'Next', and 'Last' links based on the `pageInfo` prop (lines 17-64).
* **src/components/Templates/Post/PostTemplate.module.css**
  * Adds basic CSS styles for the `PostTemplate` component, including container, title, and other styles.
* **src/components/Templates/PostList/PostListTemplate.tsx**
  * Removes the `PostListTemplate` component, likely replaced by `src/components/Templates/Posts/PostListTemplate.tsx`.
* **src/components/Templates/Posts/PostListTemplate.module.css**
  * Adds CSS styles for the `PostListTemplate` component, including container, title, author, and body styles.
* **src/components/Templates/Posts/PostListTemplate.tsx**
  * Creates a `PostListTemplate` component to display a list of posts with pagination support.
  * Fetches post data using `PostListQuery` and renders it using `ListOfPostTemplate` (lines 32-40).
  * Includes pagination support using the `PaginationTemplate` component (lines 62-64).
* **src/constants.ts**
  * Adds a `constants` object to define pagination settings, including `first` and `last` values.

</details>


<details>

<summary><b>Using Gemini Code Assist</b></summary>
<br>

The full guide for Gemini Code Assist can be found on our [documentation page](https://developers.google.com/gemini-code-assist/docs/review-github-code), here are some quick tips.

<b>Invoking Gemini</b>

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either `/gemini <command>` or `@gemini-code-assist <command>`. Below is a summary of the supported commands.

Feature | Command | Description
--- | --- | ---
Code Review | `/gemini review` | Performs a code review for the current pull request in its current state.
Pull Request Summary | `/gemini summary` | Provides a summary of the current pull request in its current state.
Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments.
Help | `/gemini help` | Displays a list of available commands.

<b>Customization</b>

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a `.gemini/` folder in the base of the repository. Detailed instructions can be found [here](https://developers.google.com/gemini-code-assist/docs/review-github-code).

<b>Limitations & Feedback</b>

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with :thumbsup: and :thumbsdown: on @gemini-code-assist comments to provide feedback.

</details>

<b>You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the [Gemini Code Assist IDE Extension](https://cloud.google.com/products/gemini/code-assist).</b>


[^1]: Review the [Privacy Notices](https://policies.google.com/privacy), [Generative AI Prohibited Use Policy](https://policies.google.com/terms/generative-ai/use-policy), [Terms of Service](https://policies.google.com/terms), and learn how to configure Gemini Code Assist in GitHub [here](https://developers.google.com/gemini-code-assist/docs/review-github-code). Gemini can make mistakes, so double check it and [use code with caution](https://support.google.com/legal/answer/13505487).

---

**Did you know?**

GraphQL, used for data fetching in this PR, was originally developed internally by Facebook in 2012 before being publicly released in 2015.

state: COMMENTED
author_association: NONE
submitted_at: 2025-03-06T12:01:00Z
user: login: gemini-code-assist[bot]
body: ## Code Review
The pull request introduces new routes for categories and improves layout for post and category templates. The changes include adding new components and templates for categories and category posts, as well as updating dependencies in package.json and package-lock.json. Overall, the code seems well-structured and implements the intended functionality.

### Merge Readiness
The pull request appears to be well-structured and implements the intended functionality. Since there are no critical or high severity issues, the pull request is in good shape to be merged, but I would recommend addressing the medium severity issues before merging. I am unable to approve the pull request, and users should have others review and approve this code before merging.
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-06T12:01:35Z
user: login: gemini-code-assist[bot]
body: ## Code Review
This pull request introduces new routes for categories and improves layout for post and category templates. The changes include adding new components and queries for categories and updating existing components for posts. Overall, the code seems well-structured and addresses the issue of adding pagination and improving layout. However, there are a few areas that could be improved for better readability and maintainability.

### Summary of Findings
* **Missing `showPagination` prop in `CategoriesPage`**: The `CategoriesPage` component does not pass the `showPagination` prop to the `CategoriesListTemplate`, which might lead to inconsistent behavior compared to other paginated pages.
* **Inconsistent use of `constants.pagination`**: The `constants.pagination` values are used in some components but not in others, leading to potential inconsistencies in pagination behavior. It would be better to use the constants consistently across the application.
* **Duplicated 'no posts' message**: The 'no posts' message and the link to the post list page are duplicated in multiple components. Consider creating a reusable component for this message to avoid code duplication.

### Merge Readiness
The pull request is almost ready for merging. However, I recommend addressing the identified issues, especially the missing `showPagination` prop in `CategoriesPage` and the inconsistent use of `constants.pagination`. Addressing these points will improve the consistency and maintainability of the code. I am unable to approve the pull request, and users should have others review and approve this code before merging.
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-06T12:02:12Z

Review comments:
diff_hunk: @@ -11,7 +11,6 @@ import PageTemplate from "@/components/Templates/Page/PageTemplate";
 import { nextSlugToWpSlug } from "@/utils/nextSlugToWpSlug";
 import PostTemplate from "@/components/Templates/Post/PostTemplate";
 import { SeoQuery } from "@/queries/general/SeoQuery";
-import PostListTemplate from "@/components/Templates/PostList/PostListTemplate";
path: src/app/[slug]/page.tsx
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

The `PostListTemplate` component is being removed here. Was this intentional? If so, please confirm that this component is no longer needed and has been replaced by another component.
created_at: 2025-03-06T12:01:35Z
author_association: NONE
diff_hunk: @@ -0,0 +1,79 @@
+import { CategoryConnection } from "@/gql/graphql";
+import { CategoriesListQuery } from "./CategoryListQuery";
+import { fetchGraphQL } from "@/utils/fetchGraphQL";
+import { print } from "graphql/language/printer";
+
+import Link from "next/link";
+import constants from "@/constants";
+import PaginationTemplate from "../Pagination/PaginationTemplate";
+import styles from "./CategoryListTemplate.module.css";
+interface PostListTemplate {
+  after?: string;
+  before?: string;
+  first?: string;
+  last?: string;
+  s?: string;
+  showPagination?: boolean;
+}
+
+export default async function CategoriesListTemplate({
+  after,
+  before,
+  first,
+  last,
+  s,
+  showPagination,
+}: Readonly<PostListTemplate>) {
+  const firstValue =
+    !after && !before && !last ? `${constants.pagination.first}` : first;
+
+  const { categories } = await fetchGraphQL<{
+    categories: CategoryConnection;
+  }>(print(CategoriesListQuery), {
+    after: after,
+    before: before,
+    first: firstValue ? parseInt(firstValue, 10) : undefined,
+    last: last ? parseInt(last, 10) : undefined,
+    s: s,
+  });
+
+  return (
+    <div
+      className={`w-full px-3 md:px-10 flex flex-col flex-1 justify-between ${styles.container}`}
+    >
+      {categories?.nodes?.length > 0 ? (
+        <ul className="flex-1">
+          {categories?.nodes.map((node) => {
+            return (
+              <li key={node.slug} className="py-1">
+                <Link href={`/categories/${node.slug}`}>
+                  <span>{node?.name}</span>
path: src/components/Templates/Categories/CategoryListTemplate.tsx
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

Consider adding a `key` prop to the `Link` component to avoid potential React key issues. This can improve performance and prevent unexpected behavior when the list of categories changes.

```typescript
                <Link key={node.categoryId} href={`/categories/${node.slug}`}>
                  <span>{node?.name}</span>
                </Link>
```
created_at: 2025-03-06T12:01:35Z
author_association: NONE
diff_hunk: @@ -0,0 +1,69 @@
+import { print } from "graphql/language/printer";
+
+import { Category } from "@/gql/graphql";
+import { fetchGraphQL } from "@/utils/fetchGraphQL";
+
+import { CategoryPostsQuery } from "./CategoryPostsQuery";
+import Link from "next/link";
+import ListOfPostTemplate from "../ListOfPosts/ListOfPostTemplate";
+import PaginationTemplate from "../Pagination/PaginationTemplate";
+import constants from "@/constants";
+import styles from "./CategoryPostsTemplate.module.css";
+
+interface PostListTemplate {
+  categorySlug: string;
+  after?: string;
+  before?: string;
+  first?: string;
+  last?: string;
+  s?: string;
+}
+
+export default async function CategoryPostsTemplate({
+  categorySlug,
+  after,
+  before,
+  first,
+  last,
+  s,
+}: Readonly<PostListTemplate>) {
+  const firstValue =
+    !after && !before && !last ? `${constants.pagination.first}` : first;
+
+  const { category } = await fetchGraphQL<{
+    category: Category;
+  }>(print(CategoryPostsQuery), {
+    id: categorySlug,
+    after: after,
+    before: before,
+    first: firstValue ? parseInt(firstValue, 10) : undefined,
+    last: last ? parseInt(last, 10) : undefined,
+    s: s,
+  });
+
+  return (
+    <div
+      className={`w-full px-3 md:px-10 flex flex-col flex-1 justify-between ${styles.container}`}
+    >
+      {(category?.posts?.nodes?.length ?? 0) > 0 ? (
+        <ListOfPostTemplate route="/" posts={category?.posts?.nodes} />
+      ) : (
+        <div className="flex-1 flex flex-col justify-center items-center ">
+          <div className="flex flex-col gap-3 w-full">
+            <p>There is no post in this category.</p>
+            <p>You can find the article you want in the list.</p>
+            <p>
+              <Link href="/">Navigate to post list page</Link>
path: src/components/Templates/CategoryPosts/CategoryPostsTemplate.tsx
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

Consider adding a `key` prop to the `Link` component to avoid potential React key issues. This can improve performance and prevent unexpected behavior when the list of categories changes.

```typescript
            <Link key={node.id} href="/">Navigate to post list page</Link>
```
created_at: 2025-03-06T12:01:35Z
author_association: NONE
diff_hunk: @@ -0,0 +1,41 @@
+import { Post } from "@/gql/graphql";
+import Link from "next/link";
+
+interface ListOfPostTemplateProps {
+  posts: Post[] | undefined;
+  /**
+   * route should end with '/'
+   */
+  route: string | undefined;
+}
+export default async function ListOfPostTemplate({
+  posts,
+  route,
+}: Readonly<ListOfPostTemplateProps>) {
+  if (!posts || posts.length === 0) {
+    return (
+      <div className="flex-1 flex flex-col justify-center items-center ">
+        <div className="flex flex-col gap-3 w-full">
+          <p>There is no posts.</p>
+          <p>You can find the posts you want in the list.</p>
+          <p>
+            <Link href="/">Navigate to post list page</Link>
+          </p>
+        </div>
+      </div>
+    );
+  }
+  return (
+    <ul className="flex-1">
+      {posts.map((node) => {
+        return (
+          <li key={node.slug} className="py-1">
+            <Link href={`${route ?? "/"}${node.slug}`}>
+              <span>{node.title}</span>
path: src/components/Templates/ListOfPosts/ListOfPostTemplate.tsx
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

Consider adding a `key` prop to the `Link` component to avoid potential React key issues. This can improve performance and prevent unexpected behavior when the list of categories changes.

```typescript
            <Link key={node.id} href={`${route ?? "/"}${node.slug}`}>
              <span>{node.title}</span>
            </Link>
```
created_at: 2025-03-06T12:01:35Z
author_association: NONE
diff_hunk: @@ -0,0 +1,66 @@
+import { PageInfo } from "@/gql/graphql";
+import Link from "next/link";
+import Constants from "@/constants";
+
+interface PaginationTemplateProps {
+  pageInfo: PageInfo | undefined;
+  /**
+   * Route should end with '/'
+   */
+  route: string | undefined;
+}
+
+export default function PaginationTemplate({
+  pageInfo,
+  route,
+}: Readonly<PaginationTemplateProps>) {
+  return (
+    <aside className="flex flex-col justify-center items-center py-6">
+      <ul className="flex flex-row gap-3">
+        <li>
+          {pageInfo?.hasPreviousPage ? (
+            <Link href={`${route ?? "/"}`}>First</Link>
path: src/components/Templates/Pagination/PaginationTemplate.tsx
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

It might be useful to add a check to ensure that `route` is defined before using it in the `Link` components. This can prevent potential errors if `route` is undefined.

```typescript
            <Link href={`${route ? route : "/"}`}>First</Link>
```
created_at: 2025-03-06T12:01:35Z
author_association: NONE
diff_hunk: @@ -0,0 +1,35 @@
+import type { Metadata } from "next";
+import CategoriesListTemplate from "@/components/Templates/Categories/CategoryListTemplate";
+
+type Props = {
+  searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
+};
+
+export async function generateMetadata(): Promise<Metadata> {
+  return {
+    alternates: {
+      canonical: `${process.env.NEXT_PUBLIC_BASE_URL}/categories`,
+    },
+  };
+}
+
+export function generateStaticParams() {
+  return [];
+}
+
+export default async function CategoriesPage({
+  searchParams,
+}: Readonly<Props>) {
path: src/app/categories/page.tsx
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

The `CategoriesPage` component doesn't pass the `showPagination` prop to the `CategoriesListTemplate`. This might lead to inconsistent behavior compared to other paginated pages. Should `showPagination` be passed as `true` here?

```typescript
    <CategoriesListTemplate
      after={after?.toString()}
      before={before?.toString()}
      first={first?.toString()}
      last={last?.toString()}
      s={s?.toString()}
      showPagination={true}
    />
```
created_at: 2025-03-06T12:02:11Z
author_association: NONE
diff_hunk: @@ -0,0 +1,41 @@
+import { Post } from "@/gql/graphql";
+import Link from "next/link";
+
+interface ListOfPostTemplateProps {
+  posts: Post[] | undefined;
+  /**
+   * route should end with '/'
+   */
+  route: string | undefined;
+}
+export default async function ListOfPostTemplate({
+  posts,
+  route,
+}: Readonly<ListOfPostTemplateProps>) {
+  if (!posts || posts.length === 0) {
+    return (
+      <div className="flex-1 flex flex-col justify-center items-center ">
+        <div className="flex flex-col gap-3 w-full">
+          <p>There is no posts.</p>
+          <p>You can find the posts you want in the list.</p>
+          <p>
+            <Link href="/">Navigate to post list page</Link>
+          </p>
+        </div>
path: src/components/Templates/ListOfPosts/ListOfPostTemplate.tsx
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

This 'no posts' message and the link to the post list page are duplicated in multiple components. Consider creating a reusable component for this message to avoid code duplication.
created_at: 2025-03-06T12:02:12Z
author_association: NONE
diff_hunk: @@ -0,0 +1,69 @@
+import { print } from "graphql/language/printer";
+
+import { Category } from "@/gql/graphql";
+import { fetchGraphQL } from "@/utils/fetchGraphQL";
+
+import { CategoryPostsQuery } from "./CategoryPostsQuery";
+import Link from "next/link";
+import ListOfPostTemplate from "../ListOfPosts/ListOfPostTemplate";
+import PaginationTemplate from "../Pagination/PaginationTemplate";
+import constants from "@/constants";
+import styles from "./CategoryPostsTemplate.module.css";
+
+interface PostListTemplate {
+  categorySlug: string;
+  after?: string;
+  before?: string;
+  first?: string;
+  last?: string;
+  s?: string;
+}
+
+export default async function CategoryPostsTemplate({
+  categorySlug,
+  after,
+  before,
+  first,
+  last,
+  s,
+}: Readonly<PostListTemplate>) {
+  const firstValue =
+    !after && !before && !last ? `${constants.pagination.first}` : first;
+
+  const { category } = await fetchGraphQL<{
+    category: Category;
+  }>(print(CategoryPostsQuery), {
+    id: categorySlug,
+    after: after,
+    before: before,
+    first: firstValue ? parseInt(firstValue, 10) : undefined,
+    last: last ? parseInt(last, 10) : undefined,
+    s: s,
+  });
+
+  return (
+    <div
+      className={`w-full px-3 md:px-10 flex flex-col flex-1 justify-between ${styles.container}`}
+    >
+      {(category?.posts?.nodes?.length ?? 0) > 0 ? (
+        <ListOfPostTemplate route="/" posts={category?.posts?.nodes} />
+      ) : (
+        <div className="flex-1 flex flex-col justify-center items-center ">
+          <div className="flex flex-col gap-3 w-full">
+            <p>There is no post in this category.</p>
+            <p>You can find the article you want in the list.</p>
+            <p>
+              <Link href="/">Navigate to post list page</Link>
+            </p>
+          </div>
+        </div>
+      )}
path: src/components/Templates/CategoryPosts/CategoryPostsTemplate.tsx
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

This 'no posts' message and the link to the post list page are duplicated in multiple components. Consider creating a reusable component for this message to avoid code duplication.
created_at: 2025-03-06T12:02:12Z
author_association: NONE
diff_hunk: @@ -0,0 +1,66 @@
+import { print } from "graphql/language/printer";
+
+import { PostConnection } from "@/gql/graphql";
+import { fetchGraphQL } from "@/utils/fetchGraphQL";
+
+import { PostListQuery } from "./PostListQuery";
+import Link from "next/link";
+import ListOfPostTemplate from "../ListOfPosts/ListOfPostTemplate";
+import PaginationTemplate from "../Pagination/PaginationTemplate";
+import constants from "@/constants";
+
+import styles from "./PostListTemplate.module.css";
+
+interface PostListTemplate {
+  after?: string;
+  before?: string;
+  first?: string;
+  last?: string;
+  s?: string;
+}
+
+export default async function PostListTemplate({
+  after,
+  before,
+  first,
+  last,
+  s,
+}: Readonly<PostListTemplate>) {
+  const firstValue =
+    !after && !before && !last ? `${constants.pagination.first}` : first;
+
+  const { posts } = await fetchGraphQL<{
+    posts: PostConnection;
+  }>(print(PostListQuery), {
+    after: after,
+    before: before,
+    first: firstValue ? parseInt(firstValue, 10) : undefined,
+    last: last ? parseInt(last, 10) : undefined,
+    s: s,
+  });
+
+  return (
+    <div
+      className={`w-full px-3 md:px-10 flex flex-col flex-1 justify-between ${styles.container}`}
+    >
+      {posts?.nodes?.length > 0 ? (
+        <ListOfPostTemplate route="/" posts={posts.nodes} />
+      ) : (
+        <div className="flex-1 flex flex-col justify-center items-center ">
+          <div className="flex flex-col gap-3 w-full">
+            <p>
+              There is no posts for the keyword <strong>{s}</strong>.
+            </p>
+            <p>You can find the article you want in the list.</p>
+            <p>
+              <Link href="/">Navigate to post list page</Link>
+            </p>
+          </div>
path: src/components/Templates/Posts/PostListTemplate.tsx
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

This 'no posts' message and the link to the post list page are duplicated in multiple components. Consider creating a reusable component for this message to avoid code duplication.
created_at: 2025-03-06T12:02:12Z
author_association: NONE

PR comments:
user: login: vercel[bot]
created_at: 2025-03-06T12:00:50Z
body: [vc]: #+0mRKHH4DCQBDaBUXDp7+E1EDmBdwsRtdvox/roKFlw=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJ3b3JkcHJlc3MtZnJvbnRlbmQiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoid29yZHByZXNzLWZyb250ZW5kLWdpdC1mZWF0dXJlLTctYWRkLWNhdGUtZmM0MjgyLWJib25zLXByb2plY3RzLnZlcmNlbC5hcHAifSwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL2Jib25zLXByb2plY3RzL3dvcmRwcmVzcy1mcm9udGVuZC9DSnJCTDcxa3pUYmlrZm1aa0ZUYjdTaFJmWEJuIiwicHJldmlld1VybCI6IndvcmRwcmVzcy1mcm9udGVuZC1naXQtZmVhdHVyZS03LWFkZC1jYXRlLWZjNDI4Mi1iYm9ucy1wcm9qZWN0cy52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIn1dfQ==
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **wordpress-frontend** | ✅ Ready ([Inspect](https://vercel.com/bbons-projects/wordpress-frontend/CJrBL71kzTbikfmZkFTb7ShRfXBn)) | [Visit Preview](https://wordpress-frontend-git-feature-7-add-cate-fc4282-bbons-projects.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/wordpress-frontend-git-feature-7-add-cate-fc4282-bbons-projects.vercel.app?via=pr-comment-feedback-link) | Mar 6, 2025 0:00am |


author_association: NONE
user: login: bbonkr
created_at: 2025-03-06T12:01:00Z
body: /gemini review
author_association: OWNER
user: login: bbonkr
created_at: 2025-03-06T12:01:35Z
body: /gemini summary
author_association: OWNER
user: login: gemini-code-assist[bot]
created_at: 2025-03-06T12:02:26Z
body: ## Summary of Changes
This pull request introduces new routes for categories and category-specific posts, enhancing the site's navigation and content organization. It also updates the `next` dependency to version 15.2.1. The changes include creating new templates for displaying categories and posts within those categories, along with associated queries and styling. Pagination has been implemented for both categories and category posts, and a few components were refactored to improve code reuse.

### Highlights
* **New Routes**: Adds `/categories` and `/categories/:slug` routes to display lists of categories and posts within a specific category, respectively.
* **Category Templates**: Introduces `CategoriesListTemplate` and `CategoryPostsTemplate` components for rendering category lists and category-specific posts.
* **Pagination**: Implements pagination for both category lists and category posts, allowing users to navigate through large sets of content.
* **Dependency Update**: Updates the `next` dependency from version 15.2.0 to 15.2.1.
* **Code Refactoring**: The `PostListTemplate` component was removed and its functionality was migrated to `Posts/PostListTemplate`.

### Changelog
<details>
<summary>Click here to see the changelog</summary>

* **package-lock.json**
  * Updates the version of `next` and its associated dependencies to 15.2.1.
  * Marks several dependencies as `dev` or `devOptional` to better reflect their usage.
* **package.json**
  * Updates the version of `next` to 15.2.1.
* **src/app/[slug]/page.tsx**
  * Removes import of `PostListTemplate` component, as it is no longer used.
* **src/app/categories/[slug]/page.tsx**
  * Adds a new route for displaying posts within a specific category, including metadata generation and dynamic route handling.
  * Implements pagination using search parameters.
* **src/app/categories/page.tsx**
  * Adds a new route for displaying a list of categories, including metadata generation and pagination support.
* **src/app/page.tsx**
  * Replaces the import and usage of the old `PostListTemplate` with the new `Posts/PostListTemplate`.
* **src/components/Templates/Categories/CategoryListQuery.ts**
  * Adds a GraphQL query for fetching a list of categories with pagination support.
* **src/components/Templates/Categories/CategoryListTemplate.module.css**
  * Adds CSS styles for the `CategoryListTemplate` component.
* **src/components/Templates/Categories/CategoryListTemplate.tsx**
  * Creates a new template component for displaying a list of categories with links to each category page.
  * Implements pagination and handles cases where no categories are available.
* **src/components/Templates/CategoryPosts/CategoryPostsQuery.ts**
  * Adds a GraphQL query for fetching posts within a specific category with pagination support.
* **src/components/Templates/CategoryPosts/CategoryPostsTemplate.module.css**
  * Adds CSS styles for the `CategoryPostsTemplate` component.
* **src/components/Templates/CategoryPosts/CategoryPostsTemplate.tsx**
  * Creates a new template component for displaying posts within a specific category.
  * Implements pagination and handles cases where no posts are available in the category.
* **src/components/Templates/ListOfPosts/ListOfPostTemplate.tsx**
  * Creates a new template component for displaying a list of posts.
  * Handles cases where no posts are available.
* **src/components/Templates/Pagination/PaginationTemplate.tsx**
  * Creates a new template component for displaying pagination links.
* **src/components/Templates/Post/PostTemplate.module.css**
  * Adds CSS styles for the `PostTemplate` component.
* **src/components/Templates/PostList/PostListTemplate.tsx**
  * Removes the `PostListTemplate` component.
* **src/components/Templates/Posts/PostListTemplate.module.css**
  * Adds CSS styles for the `PostListTemplate` component.
* **src/components/Templates/Posts/PostListTemplate.tsx**
  * Creates a new template component for displaying a list of posts with pagination.
* **src/constants.ts**
  * Adds constants for pagination.

</details>

### Activity
* The pull request resolves issue #7, which likely involves adding category routes and improving post/category template layouts.
* Vercel bot provided a preview deployment link.
* bbonkr requested a review from gemini-code-assist.
* gemini-code-assist suggested adding `key` props to `Link` components in `CategoryListTemplate.tsx`, `CategoryPostsTemplate.tsx`, and `ListOfPostTemplate.tsx`.
* gemini-code-assist suggested checking if `route` is defined before using it in `PaginationTemplate.tsx`.
* gemini-code-assist noted that the `CategoriesPage` component doesn't pass the `showPagination` prop to the `CategoriesListTemplate`.
* gemini-code-assist suggested creating a reusable component for the 'no posts' message to avoid code duplication.
* gemini-code-assist inquired about the removal of the `PostListTemplate` component.

author_association: NONE