diff --git a/messages/az.json b/messages/az.json
index 250e3e2..98e73f0 100644
--- a/messages/az.json
+++ b/messages/az.json
@@ -34,6 +34,7 @@
"resources": "Resurslar",
"questions": "Suallar",
"previous": "Əvvəlki",
- "next": "Növbəti"
+ "next": "Növbəti",
+ "task": "Tapşırıq"
}
-}
+}
\ No newline at end of file
diff --git a/messages/en.json b/messages/en.json
index a200ea3..fb23515 100644
--- a/messages/en.json
+++ b/messages/en.json
@@ -34,6 +34,7 @@
"resources": "Resources",
"questions": "Questions",
"previous": "Previous",
- "next": "Next"
+ "next": "Next",
+ "task": "Task"
}
-}
+}
\ No newline at end of file
diff --git a/next-env.d.ts b/next-env.d.ts
index 9edff1c..c4b7818 100644
--- a/next-env.d.ts
+++ b/next-env.d.ts
@@ -1,6 +1,6 @@
///
///
-import "./.next/types/routes.d.ts";
+import "./.next/dev/types/routes.d.ts";
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
diff --git a/package-lock.json b/package-lock.json
index 992d12a..6b961a7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -24,6 +24,7 @@
"@next-auth/prisma-adapter": "^1.0.7",
"@prisma/client": "6.19",
"@swc/helpers": "^0.5.19",
+ "@tailwindcss/typography": "^0.5.19",
"@tanstack/react-query": "^5.90.21",
"@vercel/analytics": "^2.0.1",
"@vercel/speed-insights": "^2.0.0",
@@ -44,6 +45,7 @@
"react-icons": "^5.5.0",
"react-markdown": "^10.1.0",
"react-select": "^5.10.2",
+ "remark-gfm": "^4.0.1",
"sass": "^1.97.3",
"server-only": "^0.0.1",
"tailwind-merge": "^3.4.0",
@@ -1042,7 +1044,6 @@
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
@@ -1293,7 +1294,6 @@
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@dnd-kit/accessibility": "^3.1.1",
"@dnd-kit/utilities": "^3.2.2",
@@ -2202,7 +2202,6 @@
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-7.1.0.tgz",
"integrity": "sha512-fNxRUk1KhjSbnbuBxlWSnBLKLBNun52ZBTcs22H/xEEzM6Ap81ZFTQ4bZBxVQGQgVY0xugKGoRcCbaKjLQ3XZA==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "7.1.0"
},
@@ -3391,7 +3390,6 @@
"integrity": "sha512-gR2EMvfK/aTxsuooaDA32D8v+us/8AAet+C3J1cc04SW35FPdZYgLF+iN4NDLUgAaUGTKdAB0CYenu1TAgGdMg==",
"hasInstallScript": true,
"license": "Apache-2.0",
- "peer": true,
"engines": {
"node": ">=18.18"
},
@@ -4775,6 +4773,18 @@
"tailwindcss": "4.1.18"
}
},
+ "node_modules/@tailwindcss/typography": {
+ "version": "0.5.19",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.19.tgz",
+ "integrity": "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==",
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "6.0.10"
+ },
+ "peerDependencies": {
+ "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1"
+ }
+ },
"node_modules/@tanstack/query-core": {
"version": "5.90.20",
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.20.tgz",
@@ -4945,7 +4955,6 @@
"integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"undici-types": "~7.16.0"
}
@@ -4967,7 +4976,6 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz",
"integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==",
"license": "MIT",
- "peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -4996,7 +5004,8 @@
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
"license": "MIT",
- "optional": true
+ "optional": true,
+ "peer": true
},
"node_modules/@types/unist": {
"version": "3.0.3",
@@ -5049,7 +5058,6 @@
"integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.54.0",
"@typescript-eslint/types": "8.54.0",
@@ -5648,7 +5656,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -5970,7 +5977,6 @@
"integrity": "sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==",
"devOptional": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/types": "^7.26.0"
}
@@ -6074,7 +6080,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -6356,12 +6361,23 @@
"node": ">= 8"
}
},
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "license": "MIT",
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/csstype": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/damerau-levenshtein": {
"version": "1.0.8",
@@ -6572,6 +6588,7 @@
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz",
"integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==",
"license": "(MPL-2.0 OR Apache-2.0)",
+ "peer": true,
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
}
@@ -6887,7 +6904,6 @@
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -7073,7 +7089,6 @@
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@rtsao/scc": "^1.1.0",
"array-includes": "^3.1.9",
@@ -7954,7 +7969,6 @@
"resolved": "https://registry.npmjs.org/immer/-/immer-11.1.3.tgz",
"integrity": "sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q==",
"license": "MIT",
- "peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
@@ -9029,11 +9043,22 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/markdown-table": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz",
+ "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/marked": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz",
"integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==",
"license": "MIT",
+ "peer": true,
"bin": {
"marked": "bin/marked.js"
},
@@ -9051,6 +9076,34 @@
"node": ">= 0.4"
}
},
+ "node_modules/mdast-util-find-and-replace": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
+ "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "escape-string-regexp": "^5.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/mdast-util-from-markdown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
@@ -9075,6 +9128,107 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/mdast-util-gfm": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz",
+ "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==",
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-gfm-autolink-literal": "^2.0.0",
+ "mdast-util-gfm-footnote": "^2.0.0",
+ "mdast-util-gfm-strikethrough": "^2.0.0",
+ "mdast-util-gfm-table": "^2.0.0",
+ "mdast-util-gfm-task-list-item": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-autolink-literal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz",
+ "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-find-and-replace": "^3.0.0",
+ "micromark-util-character": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-footnote": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz",
+ "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.1.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-strikethrough": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz",
+ "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-table": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz",
+ "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "markdown-table": "^3.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-task-list-item": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz",
+ "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/mdast-util-mdx-expression": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz",
@@ -9289,6 +9443,127 @@
"micromark-util-types": "^2.0.0"
}
},
+ "node_modules/micromark-extension-gfm": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz",
+ "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-extension-gfm-autolink-literal": "^2.0.0",
+ "micromark-extension-gfm-footnote": "^2.0.0",
+ "micromark-extension-gfm-strikethrough": "^2.0.0",
+ "micromark-extension-gfm-table": "^2.0.0",
+ "micromark-extension-gfm-tagfilter": "^2.0.0",
+ "micromark-extension-gfm-task-list-item": "^2.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-autolink-literal": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz",
+ "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-footnote": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz",
+ "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-strikethrough": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz",
+ "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-table": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz",
+ "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-tagfilter": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz",
+ "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-task-list-item": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz",
+ "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/micromark-factory-destination": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
@@ -9786,7 +10061,6 @@
"resolved": "https://registry.npmjs.org/next/-/next-16.2.1.tgz",
"integrity": "sha512-VaChzNL7o9rbfdt60HUj8tev4m6d7iC1igAy157526+cJlXOQu5LzsBXNT+xaJnTP/k+utSX5vMv7m0G+zKH+Q==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@next/env": "16.2.1",
"@swc/helpers": "0.5.15",
@@ -9840,7 +10114,6 @@
"resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.13.tgz",
"integrity": "sha512-sgObCfcfL7BzIK76SS5TnQtc3yo2Oifp/yIpfv6fMfeBOiBJkDWF3A2y9+yqnmJ4JKc2C+nMjSjmgDeTwgN1rQ==",
"license": "ISC",
- "peer": true,
"dependencies": {
"@babel/runtime": "^7.20.13",
"@panva/hkdf": "^1.0.2",
@@ -10421,12 +10694,24 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/postcss-selector-parser": {
+ "version": "6.0.10",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
+ "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/preact": {
"version": "10.28.3",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.28.3.tgz",
"integrity": "sha512-tCmoRkPQLpBeWzpmbhryairGnhW9tKV6c6gr/w+RhoRoKEJwsjzipwp//1oCpGPOchvSLaAPlpcJi9MwMmoPyA==",
"license": "MIT",
- "peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
@@ -10496,7 +10781,6 @@
"devOptional": true,
"hasInstallScript": true,
"license": "Apache-2.0",
- "peer": true,
"dependencies": {
"@prisma/config": "6.12.0",
"@prisma/engines": "6.12.0"
@@ -10573,7 +10857,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -10583,7 +10866,6 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
"license": "MIT",
- "peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -10744,6 +11026,24 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/remark-gfm": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
+ "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-gfm": "^3.0.0",
+ "micromark-extension-gfm": "^3.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-stringify": "^11.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/remark-parse": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
@@ -10777,6 +11077,21 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/remark-stringify": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz",
+ "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/resolve": {
"version": "1.22.11",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
@@ -10911,7 +11226,6 @@
"resolved": "https://registry.npmjs.org/sass/-/sass-1.97.3.tgz",
"integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==",
"license": "MIT",
- "peer": true,
"dependencies": {
"chokidar": "^4.0.0",
"immutable": "^5.0.2",
@@ -11464,7 +11778,6 @@
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
"integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==",
- "dev": true,
"license": "MIT"
},
"node_modules/tapable": {
@@ -11522,7 +11835,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=12"
},
@@ -11769,7 +12081,6 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"devOptional": true,
"license": "Apache-2.0",
- "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -12045,6 +12356,12 @@
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "license": "MIT"
+ },
"node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
@@ -12248,7 +12565,6 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
"license": "MIT",
- "peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
diff --git a/package.json b/package.json
index 23590e3..17acda1 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,7 @@
"@next-auth/prisma-adapter": "^1.0.7",
"@prisma/client": "6.19",
"@swc/helpers": "^0.5.19",
+ "@tailwindcss/typography": "^0.5.19",
"@tanstack/react-query": "^5.90.21",
"@vercel/analytics": "^2.0.1",
"@vercel/speed-insights": "^2.0.0",
@@ -47,6 +48,7 @@
"react-icons": "^5.5.0",
"react-markdown": "^10.1.0",
"react-select": "^5.10.2",
+ "remark-gfm": "^4.0.1",
"sass": "^1.97.3",
"server-only": "^0.0.1",
"tailwind-merge": "^3.4.0",
diff --git a/sample-planet.json b/sample-planet.json
deleted file mode 100644
index b41db43..0000000
--- a/sample-planet.json
+++ /dev/null
@@ -1,287 +0,0 @@
-{
- "id": "planet-js-events-001",
- "category": "javascript",
- "status": "published",
- "step": 8,
- "nextPlanetId": "planet-js-async-001",
- "prevPlanetId": "planet-js-dom-001",
- "image": {
- "url": "",
- "metadata": {
- "width": 1200,
- "height": 630
- },
- "alt": {
- "en": "Illustration showing a web browser reacting to user clicks via JavaScript Event Listeners",
- "az": "Veb brauzerin JavaScript Hadisə İzləyiciləri vasitəsilə istifadəçi kliklərinə reaksiyasını göstərən illüstrasiya"
- }
- },
- "localized": {
- "en": {
- "name": "JavaScript Event Listeners",
- "description": "Master the art of interactive web pages by diving deep into JavaScript Event Listeners. This fundamental concept bridges the gap between static HTML and dynamic, engaging user experiences. In the modern web, almost every user interaction, from a simple button click to complex drag-and-drop mechanics, relies on events. You will learn how the browser dispatches these events and how your code can intercept them. We will explore the differences between older, inline event handlers and the robust addEventListener method used in professional environments. Understanding the event flow, including the capture and bubbling phases, will give you fine-grained control over your application's behavior. By mastering event delegation, you will write cleaner, more efficient code that scales effortlessly as your DOM grows. Ultimately, this knowledge is essential for building intuitive interfaces that react instantly to user input. Throughout this journey, you will practice with real-world scenarios to solidify your understanding. Get ready to bring your web pages to life!",
- "tags": [
- {
- "id": "tag-en-1",
- "tag": "javascript"
- },
- {
- "id": "tag-en-2",
- "tag": "dom-manipulation"
- },
- {
- "id": "tag-en-3",
- "tag": "events"
- },
- {
- "id": "tag-en-4",
- "tag": "interactivity"
- }
- ],
- "researchTopics": [
- {
- "id": "rt-en-1",
- "topic": "Event Bubbling vs Event Capturing Phases"
- },
- {
- "id": "rt-en-2",
- "topic": "The 'event' Object properties and methods (e.g., preventDefault, stopPropagation)"
- },
- {
- "id": "rt-en-3",
- "topic": "Creating and Dispatching Custom Events in JS"
- }
- ],
- "resources": [
- {
- "id": "res-en-1",
- "title": "MDN Web Docs",
- "label": "EventTarget.addEventListener()",
- "url": "https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener"
- },
- {
- "id": "res-en-2",
- "title": "JavaScript.info",
- "label": "Introduction to Browser Events",
- "url": "https://javascript.info/events"
- }
- ],
- "questions": [
- {
- "id": "q-en-1",
- "question": "What is the primary difference in maintainability and functionality between using `addEventListener` and an `onclick` HTML attribute?"
- },
- {
- "id": "q-en-2",
- "question": "How do you effectively prevent an event from bubbling up the DOM tree, and when is this actually necessary?"
- },
- {
- "id": "q-en-3",
- "question": "Why is Event Delegation considered a performance-friendly technique, especially in applications with large, dynamic lists?"
- }
- ],
- "contents": [
- {
- "id": "content-en-1",
- "type": "text",
- "title": {
- "level": "h2",
- "text": "Understanding the Core of Interactivity"
- },
- "text": "Event listeners are the absolute core of interactivity within any modern web application. They serve as vigilant watchdogs, allowing you to 'listen' for highly specific user actions or system events on individual HTML elements. Whether a user is submitting a complex form, moving their mouse across a canvas, pressing a specific keyboard shortcut, or simply waiting for a page to finish loading, an event is fired under the hood. By utilizing JavaScript to hook into these events, you transform a static, lifeless document into a reactive, application-like experience. When the targeted event occurs, the browser immediately executes the callback function you provided, granting you the power to manipulate the DOM, fetch new data, or trigger animations precisely when needed.",
- "variant": "normal"
- },
- {
- "id": "content-en-2",
- "type": "code",
- "label": "Basic Syntax",
- "title": "Adding an Event Listener",
- "description": "The most common, flexible, and strictly recommended way to register an event listener is by leveraging the native `addEventListener` method available on all DOM nodes.",
- "code": {
- "language": "javascript",
- "code": "const submitButton = document.querySelector('.submit-btn');\n\n// We pass the event type 'click' and a callback function\nsubmitButton.addEventListener('click', function(event) {\n console.log('The button was clicked!');\n console.log('Event details:', event);\n});",
- "output": "The button was clicked!\nEvent details: PointerEvent { ... }"
- }
- },
- {
- "id": "content-en-3",
- "type": "text",
- "text": "While it might seem convenient at first to use inline HTML attributes such as `onclick=\"myFunction()\"`, this approach is heavily discouraged in professional development. Inline handlers violate the principle of separation of concerns by tightly coupling your HTML structure directly with your JavaScript behavioral logic, making the codebase significantly harder to read, scale, and maintain over time. Furthermore, the `addEventListener` method provides a crucial advantage: it allows developers to attach multiple, independent listeners of the exact same type to a single element. In stark contrast, attempting to assign multiple functions to an `onclick` property will simply result in the last assignment permanently overwriting all previous handlers.",
- "variant": "warning"
- },
- {
- "id": "content-en-4",
- "type": "html-element",
- "label": "Interactive UI",
- "title": "Live DOM Manipulation Example",
- "description": "Interact with the interactive component below to see exactly how an event listener captures a click and subsequently updates the DOM hierarchy and styling in real-time.",
- "element": {
- "html": "
\n \n
Current Status: Inactive
\n
",
- "css": ".demo-box { padding: 24px; border: 2px solid #e2e8f0; border-radius: 12px; background: #f8fafc; }\nbutton { padding: 12px 20px; cursor: pointer; background: #3b82f6; color: white; border: none; border-radius: 6px; font-weight: bold; transition: background 0.2s; }\nbutton:hover { background: #2563eb; }\n.status { margin-top: 16px; font-size: 1.1rem; }\n.active-color span { color: #10b981; font-weight: 800; }",
- "js": "const btn = document.getElementById('demo-btn');\nconst status = document.getElementById('status-text');\nconst span = status.querySelector('span');\n\nbtn.addEventListener('click', () => {\n status.classList.toggle('active-color');\n span.textContent = status.classList.contains('active-color') ? 'Active!' : 'Inactive';\n});"
- }
- },
- {
- "id": "content-en-5",
- "type": "implementation-task",
- "label": "Practice Task",
- "title": "Task 1: Capture and Validate Keyboard Input",
- "task": "Begin by selecting the input element with the ID 'username-input'. Attach a 'keyup' event listener directly to this element. Inside your callback function, you need to verify if the user pressed the 'Enter' key by checking the `event.key` property. If the condition evaluates to true, retrieve the current string value of the input field, check if it is longer than 3 characters, and log a success message to the console containing the user's input."
- },
- {
- "id": "content-en-7",
- "type": "text",
- "title": {
- "level": "h3",
- "text": "The Power of Event Delegation"
- },
- "text": "Instead of painstakingly attaching individual event listeners to dozens or hundreds of child elements (such as rows in a massive data table or items in a dynamic to-do list), you can employ a highly efficient pattern called Event Delegation. By attaching a single, centralized event listener to a common parent element, you leverage the natural mechanism of event bubbling. The parent container will effortlessly catch any matching events triggered by its descendants as they bubble up the DOM tree. This drastically reduces the memory footprint of your application and automatically handles any new child elements that are injected into the DOM later via JavaScript, eliminating the need to bind new listeners on the fly!",
- "variant": "tip"
- },
- {
- "id": "content-en-8",
- "type": "code",
- "title": "Implementing Event Delegation",
- "description": "Observe how we attach only one listener to the `ul` container. We then inspect the `event.target` property to definitively determine exactly which `li` element originated the click before applying our logic.",
- "code": {
- "language": "javascript",
- "code": "const todoList = document.getElementById('todo-list');\n\n// A single listener handles all clicks inside the list\ntodoList.addEventListener('click', (event) => {\n // Ensure we only react if an actual list item was clicked\n if (event.target.tagName === 'LI') {\n event.target.classList.toggle('task-completed');\n console.log(`Toggled status for: ${event.target.textContent}`);\n }\n});"
- }
- }
- ]
- },
- "az": {
- "name": "JavaScript Hadisə İzləyiciləri",
- "description": "JavaScript Hadisə İzləyicilərini (Event Listeners) dərindən öyrənərək interaktiv veb səhifələr yaratmaq sənətinə yiyələnin. Bu fundamental konsepsiya statik HTML ilə dinamik, cəlbedici istifadəçi təcrübələri arasındakı körpüdür. Müasir vebdə sadə bir düymə klikindən tutmuş mürəkkəb sürüklə-burax (drag-and-drop) mexanizmlərinə qədər demək olar ki, hər bir istifadəçi qarşılıqlı əlaqəsi hadisələrə əsaslanır. Brauzerin bu hadisələri necə göndərdiyini və kodunuzun onları necə tuta biləcəyini öyrənəcəksiniz. Köhnə, sətirdaxili hadisə idarəediciləri ilə peşəkar mühitlərdə istifadə olunan güclü addEventListener metodu arasındakı fərqləri araşdıracağıq. Hadisə axınını, o cümlədən tutulma (capture) və qabarma (bubbling) mərhələlərini anlamaq tətbiqinizin davranışına incə nəzarət etmək imkanı verəcək. Hadisə nümayəndəliyinə (event delegation) yiyələnməklə, DOM böyüdükcə asanlıqla miqyaslanan daha təmiz, daha səmərəli kod yazacaqsınız. Nəhayət, bu biliklər istifadəçi daxiletmələrinə anında reaksiya verən intuitiv interfeyslər qurmaq üçün vacibdir. Bu səyahət boyunca anlayışınızı möhkəmləndirmək üçün real dünya ssenariləri ilə məşq edəcəksiniz. Veb səhifələrinizi canlandırmağa hazır olun!",
- "tags": [
- {
- "id": "tag-az-1",
- "tag": "javascript"
- },
- {
- "id": "tag-az-2",
- "tag": "dom-manipulyasiyası"
- },
- {
- "id": "tag-az-3",
- "tag": "hadisələr"
- },
- {
- "id": "tag-az-4",
- "tag": "interaktivlik"
- }
- ],
- "researchTopics": [
- {
- "id": "rt-az-1",
- "topic": "Hadisələrin qabarması (Bubbling) və tutulması (Capturing) mərhələləri"
- },
- {
- "id": "rt-az-2",
- "topic": "'event' obyektinin xüsusiyyətləri və metodları (məsələn, preventDefault, stopPropagation)"
- },
- {
- "id": "rt-az-3",
- "topic": "JS-də xüsusi (Custom) hadisələrin yaradılması və göndərilməsi"
- }
- ],
- "resources": [
- {
- "id": "res-az-1",
- "title": "MDN Web Docs",
- "label": "EventTarget.addEventListener() (İngiliscə)",
- "url": "https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener"
- },
- {
- "id": "res-az-2",
- "title": "JavaScript.info",
- "label": "Brauzer Hadisələrinə Giriş",
- "url": "https://javascript.info/events"
- }
- ],
- "questions": [
- {
- "id": "q-az-1",
- "question": "`addEventListener` və HTML daxilindəki `onclick` atributunun istifadəsi arasında kodun saxlanması və funksionallıq baxımından əsas fərq nədir?"
- },
- {
- "id": "q-az-2",
- "question": "Bir hadisənin DOM ağacında yuxarıya doğru qabarmasının (bubbling) qarşısını effektiv şəkildə necə ala bilərsiniz və bu, əslində nə vaxt lazımdır?"
- },
- {
- "id": "q-az-3",
- "question": "Xüsusilə böyük və dinamik siyahıları olan tətbiqlərdə Hadisə Nümayəndəliyi (Event Delegation) nə üçün performansa müsbət təsir edən bir texnika hesab olunur?"
- }
- ],
- "contents": [
- {
- "id": "content-az-1",
- "type": "text",
- "title": {
- "level": "h2",
- "text": "İnteraktivliyin Əsasını Anlamaq"
- },
- "text": "Hadisə izləyiciləri istənilən müasir veb tətbiqində interaktivliyin mütləq əsasını təşkil edir. Onlar ayıq gözətçilər kimi fəaliyyət göstərərək, fərdi HTML elementlərində xüsusi istifadəçi hərəkətlərini və ya sistem hadisələrini 'dinləməyə' imkan verir. İstifadəçi mürəkkəb bir formanı göndərdikdə, siçanı kətan üzərində hərəkət etdirdikdə, xüsusi klaviatura qısayoluna basdıqda və ya sadəcə səhifənin yüklənməsini gözlədikdə, arxa planda bir hadisə tetiklənir. Bu hadisələrə qoşulmaq üçün JavaScript-dən istifadə edərək, siz statik, cansız bir sənədi reaktiv, tətbiqə bənzər bir təcrübəyə çevirirsiniz. Hədəflənmiş hadisə baş verdikdə, brauzer təqdim etdiyiniz geri çağırış (callback) funksiyasını dərhal icra edir və sizə DOM-u manipulyasiya etmək, yeni məlumatlar gətirmək və ya tam ehtiyac duyulan anda animasiyaları başlatmaq gücü verir.",
- "variant": "normal"
- },
- {
- "id": "content-az-2",
- "type": "code",
- "label": "Əsas Sintaksis",
- "title": "Hadisə İzləyicisinin Əlavə Edilməsi",
- "description": "Hadisə izləyicisini qeydiyyatdan keçirməyin ən çox yayılan, çevik və qətiyyətlə tövsiyə olunan yolu bütün DOM qovşaqlarında mövcud olan yerli `addEventListener` metodundan istifadə etməkdir.",
- "code": {
- "language": "javascript",
- "code": "const submitButton = document.querySelector('.submit-btn');\n\n// 'click' hadisə növünü və geri çağırış funksiyasını ötürürük\nsubmitButton.addEventListener('click', function(event) {\n console.log('Düyməyə klikləndi!');\n console.log('Hadisə detalları:', event);\n});",
- "output": "Düyməyə klikləndi!\nHadisə detalları: PointerEvent { ... }"
- }
- },
- {
- "id": "content-az-3",
- "type": "text",
- "text": "Əvvəlcə `onclick=\"myFunction()\"` kimi sətirdaxili HTML atributlarından istifadə etmək rahat görünsə də, bu yanaşma peşəkar inkişafda qətiyyən tövsiyə edilmir. Sətirdaxili idarəedicilər HTML strukturunuzu birbaşa JavaScript davranış məntiqi ilə sıx birləşdirərək narahatlıqların ayrılması (separation of concerns) prinsipini pozur və zamanla kod bazasının oxunmasını, miqyaslanmasını və saxlanmasını əhəmiyyətli dərəcədə çətinləşdirir. Bundan əlavə, `addEventListener` metodu mühüm bir üstünlük təmin edir: tərtibatçılara tək bir elementə eyni növ birdən çox, müstəqil izləyici əlavə etməyə imkan verir. Bunun tam əksinə olaraq, bir `onclick` xüsusiyyətinə birdən çox funksiya təyin etməyə cəhd etmək sadəcə olaraq sonuncu təyinatın əvvəlki bütün idarəedicilərin üzərinə daimi olaraq yazılması ilə nəticələnəcək.",
- "variant": "warning"
- },
- {
- "id": "content-az-4",
- "type": "html-element",
- "label": "İnteraktiv UI",
- "title": "Canlı DOM Manipulyasiyası Nümunəsi",
- "description": "Hadisə izləyicisinin kliki necə tutduğunu və sonradan DOM iyerarxiyasını və stillərini real vaxt rejimində necə yenilədiyini dəqiq görmək üçün aşağıdakı interaktiv komponentlə qarşılıqlı əlaqədə olun.",
- "element": {
- "html": "
\n \n
Cari Status: Qeyri-aktiv
\n
",
- "css": ".demo-box { padding: 24px; border: 2px solid #e2e8f0; border-radius: 12px; background: #f8fafc; }\nbutton { padding: 12px 20px; cursor: pointer; background: #3b82f6; color: white; border: none; border-radius: 6px; font-weight: bold; transition: background 0.2s; }\nbutton:hover { background: #2563eb; }\n.status { margin-top: 16px; font-size: 1.1rem; }\n.active-color span { color: #10b981; font-weight: 800; }",
- "js": "const btn = document.getElementById('demo-btn');\nconst status = document.getElementById('status-text');\nconst span = status.querySelector('span');\n\nbtn.addEventListener('click', () => {\n status.classList.toggle('active-color');\n span.textContent = status.classList.contains('active-color') ? 'Aktivdir!' : 'Qeyri-aktiv';\n});"
- }
- },
- {
- "id": "content-az-5",
- "type": "implementation-task",
- "label": "Təcrübə Tapşırığı",
- "title": "Tapşırıq 1: Klaviatura daxiletməsini tutun və yoxlayın",
- "task": "'username-input' ID-si olan input elementini seçərək başlayın. Bu elementə birbaşa 'keyup' hadisə izləyicisi əlavə edin. Geri çağırış funksiyanızın daxilində `event.key` xüsusiyyətini yoxlayaraq istifadəçinin 'Enter' düyməsini basıb-basmadığını yoxlamalısınız. Əgər şərt doğrudursa, input sahəsinin cari sətir dəyərini əldə edin, onun 3 simvoldan uzun olub-olmadığını yoxlayın və istifadəçinin daxiletməsini ehtiva edən uğur mesajını konsola yazdırın."
- },
- {
- "id": "content-az-7",
- "type": "text",
- "title": {
- "level": "h3",
- "text": "Hadisə Nümayəndəliyinin (Event Delegation) Gücü"
- },
- "text": "Onlarla və ya yüzlərlə alt elementə (məsələn, nəhəng bir məlumat cədvəlindəki sətirlər və ya dinamik görüləcək işlər siyahısındakı elementlər) fərdi hadisə izləyicilərini zəhmətlə əlavə etmək əvəzinə, Hadisə Nümayəndəliyi adlı yüksək səmərəli bir naxışdan istifadə edə bilərsiniz. Ümumi bir valideyn elementinə tək, mərkəzləşdirilmiş bir hadisə izləyicisi əlavə etməklə, hadisə qabarmasının təbii mexanizmindən yararlanırsınız. Valideyn konteyner, alt elementlər DOM ağacında yuxarıya doğru qabardıqca onların tetiklədiyi istənilən uyğun hadisələri asanlıqla tutacaq. Bu, tətbiqinizin yaddaş izini kəskin şəkildə azaldır və daha sonra JavaScript vasitəsilə DOM-a daxil edilən istənilən yeni alt elementləri avtomatik olaraq idarə edir, yeni izləyiciləri dərhal bağlamaq ehtiyacını aradan qaldırır!",
- "variant": "tip"
- },
- {
- "id": "content-az-8",
- "type": "code",
- "title": "Hadisə Nümayəndəliyinin Tətbiqi",
- "description": "Yalnız `ul` konteynerinə necə tək bir izləyici əlavə etdiyimizə diqqət yetirin. Sonra məntiqimizi tətbiq etməzdən əvvəl klikin dəqiq hansı `li` elementindən qaynaqlandığını qəti şəkildə müəyyən etmək üçün `event.target` xüsusiyyətini yoxlayırıq.",
- "code": {
- "language": "javascript",
- "code": "const todoList = document.getElementById('todo-list');\n\n// Tək bir izləyici siyahı daxilindəki bütün klikləri idarə edir\ntodoList.addEventListener('click', (event) => {\n // Yalnız həqiqi bir siyahı elementinə klikləndikdə reaksiya verdiyimizə əmin oluruq\n if (event.target.tagName === 'LI') {\n event.target.classList.toggle('task-completed');\n console.log(`Status dəyişdirildi: ${event.target.textContent}`);\n }\n});"
- }
- }
- ]
- }
- }
-}
diff --git a/src/app/(public)/[locale]/error.tsx b/src/app/(public)/[locale]/error.tsx
index 6c4ee0c..5d3d23f 100644
--- a/src/app/(public)/[locale]/error.tsx
+++ b/src/app/(public)/[locale]/error.tsx
@@ -39,9 +39,7 @@ export default function GlobalError({ error, reset }: GlobalErrorProps) {