Oviya
commited on
Commit
·
9db5bb0
1
Parent(s):
47111c6
fix
Browse files- package-lock.json +56 -6
- src/app/generate-questions/generate-questions.component.html +3 -3
- src/app/generate-questions/generate-questions.component.ts +360 -566
- src/app/generate-questions/generate-questions.service.ts +16 -20
- src/environments/environment.prod.ts +4 -0
- src/environments/environment.ts +4 -0
package-lock.json
CHANGED
|
@@ -271,6 +271,7 @@
|
|
| 271 |
"url": "https://github.com/sponsors/ai"
|
| 272 |
}
|
| 273 |
],
|
|
|
|
| 274 |
"dependencies": {
|
| 275 |
"nanoid": "^3.3.7",
|
| 276 |
"picocolors": "^1.0.0",
|
|
@@ -354,6 +355,7 @@
|
|
| 354 |
"version": "17.3.12",
|
| 355 |
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-17.3.12.tgz",
|
| 356 |
"integrity": "sha512-9hsdWF4gRRcVJtPcCcYLaX1CIyM9wUu6r+xRl6zU5hq8qhl35hig6ounz7CXFAzLf0WDBdM16bPHouVGaG76lg==",
|
|
|
|
| 357 |
"dependencies": {
|
| 358 |
"tslib": "^2.3.0"
|
| 359 |
},
|
|
@@ -420,6 +422,7 @@
|
|
| 420 |
"version": "17.3.12",
|
| 421 |
"resolved": "https://registry.npmjs.org/@angular/common/-/common-17.3.12.tgz",
|
| 422 |
"integrity": "sha512-vabJzvrx76XXFrm1RJZ6o/CyG32piTB/1sfFfKHdlH1QrmArb8It4gyk9oEjZ1IkAD0HvBWlfWmn+T6Vx3pdUw==",
|
|
|
|
| 423 |
"dependencies": {
|
| 424 |
"tslib": "^2.3.0"
|
| 425 |
},
|
|
@@ -435,6 +438,7 @@
|
|
| 435 |
"version": "17.3.12",
|
| 436 |
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-17.3.12.tgz",
|
| 437 |
"integrity": "sha512-vwI8oOL/gM+wPnptOVeBbMfZYwzRxQsovojZf+Zol9szl0k3SZ3FycWlxxXZGFu3VIEfrP6pXplTmyODS/Lt1w==",
|
|
|
|
| 438 |
"dependencies": {
|
| 439 |
"tslib": "^2.3.0"
|
| 440 |
},
|
|
@@ -455,6 +459,7 @@
|
|
| 455 |
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-17.3.12.tgz",
|
| 456 |
"integrity": "sha512-1F8M7nWfChzurb7obbvuE7mJXlHtY1UG58pcwcomVtpPb+kPavgAO8OEvJHYBMV+bzSxkXt5UIwL9lt9jHUxZA==",
|
| 457 |
"dev": true,
|
|
|
|
| 458 |
"dependencies": {
|
| 459 |
"@babel/core": "7.23.9",
|
| 460 |
"@jridgewell/sourcemap-codec": "^1.4.14",
|
|
@@ -527,6 +532,7 @@
|
|
| 527 |
"version": "17.3.12",
|
| 528 |
"resolved": "https://registry.npmjs.org/@angular/core/-/core-17.3.12.tgz",
|
| 529 |
"integrity": "sha512-MuFt5yKi161JmauUta4Dh0m8ofwoq6Ino+KoOtkYMBGsSx+A7dSm+DUxxNwdj7+DNyg3LjVGCFgBFnq4g8z06A==",
|
|
|
|
| 530 |
"dependencies": {
|
| 531 |
"tslib": "^2.3.0"
|
| 532 |
},
|
|
@@ -542,6 +548,7 @@
|
|
| 542 |
"version": "17.3.12",
|
| 543 |
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-17.3.12.tgz",
|
| 544 |
"integrity": "sha512-tV6r12Q3yEUlXwpVko4E+XscunTIpPkLbaiDn/MTL3Vxi2LZnsLgHyd/i38HaHN+e/H3B0a1ToSOhV5wf3ay4Q==",
|
|
|
|
| 545 |
"dependencies": {
|
| 546 |
"tslib": "^2.3.0"
|
| 547 |
},
|
|
@@ -624,6 +631,7 @@
|
|
| 624 |
"version": "17.3.12",
|
| 625 |
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-17.3.12.tgz",
|
| 626 |
"integrity": "sha512-DYY04ptWh/ulMHzd+y52WCE8QnEYGeIiW3hEIFjCN8z0kbIdFdUtEB0IK5vjNL3ejyhUmphcpeT5PYf3YXtqWQ==",
|
|
|
|
| 627 |
"dependencies": {
|
| 628 |
"tslib": "^2.3.0"
|
| 629 |
},
|
|
@@ -700,6 +708,7 @@
|
|
| 700 |
"version": "7.24.0",
|
| 701 |
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz",
|
| 702 |
"integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==",
|
|
|
|
| 703 |
"dependencies": {
|
| 704 |
"@ampproject/remapping": "^2.2.0",
|
| 705 |
"@babel/code-frame": "^7.23.5",
|
|
@@ -5334,6 +5343,7 @@
|
|
| 5334 |
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
| 5335 |
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
|
| 5336 |
"dev": true,
|
|
|
|
| 5337 |
"bin": {
|
| 5338 |
"acorn": "bin/acorn"
|
| 5339 |
},
|
|
@@ -5404,6 +5414,7 @@
|
|
| 5404 |
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
| 5405 |
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
|
| 5406 |
"dev": true,
|
|
|
|
| 5407 |
"dependencies": {
|
| 5408 |
"fast-deep-equal": "^3.1.1",
|
| 5409 |
"json-schema-traverse": "^1.0.0",
|
|
@@ -5887,6 +5898,7 @@
|
|
| 5887 |
"url": "https://github.com/sponsors/ai"
|
| 5888 |
}
|
| 5889 |
],
|
|
|
|
| 5890 |
"dependencies": {
|
| 5891 |
"caniuse-lite": "^1.0.30001688",
|
| 5892 |
"electron-to-chromium": "^1.5.73",
|
|
@@ -8884,7 +8896,8 @@
|
|
| 8884 |
"version": "5.1.2",
|
| 8885 |
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.1.2.tgz",
|
| 8886 |
"integrity": "sha512-2oIUMGn00FdUiqz6epiiJr7xcFyNYj3rDcfmnzfkBnHyBQ3cBQUs4mmyGsOb7TTLb9kxk7dBcmEmqhDKkBoDyA==",
|
| 8887 |
-
"dev": true
|
|
|
|
| 8888 |
},
|
| 8889 |
"node_modules/jest-diff": {
|
| 8890 |
"version": "30.0.0-alpha.6",
|
|
@@ -9594,6 +9607,7 @@
|
|
| 9594 |
"resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz",
|
| 9595 |
"integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==",
|
| 9596 |
"dev": true,
|
|
|
|
| 9597 |
"dependencies": {
|
| 9598 |
"@colors/colors": "1.5.0",
|
| 9599 |
"body-parser": "^1.19.0",
|
|
@@ -9800,6 +9814,7 @@
|
|
| 9800 |
"resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz",
|
| 9801 |
"integrity": "sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==",
|
| 9802 |
"dev": true,
|
|
|
|
| 9803 |
"dependencies": {
|
| 9804 |
"copy-anything": "^2.0.1",
|
| 9805 |
"parse-node-version": "^1.0.1",
|
|
@@ -11414,6 +11429,7 @@
|
|
| 11414 |
"url": "https://github.com/sponsors/ai"
|
| 11415 |
}
|
| 11416 |
],
|
|
|
|
| 11417 |
"dependencies": {
|
| 11418 |
"nanoid": "^3.3.7",
|
| 11419 |
"picocolors": "^1.1.1",
|
|
@@ -12245,6 +12261,7 @@
|
|
| 12245 |
"version": "7.8.1",
|
| 12246 |
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
| 12247 |
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
|
|
|
| 12248 |
"dependencies": {
|
| 12249 |
"tslib": "^2.1.0"
|
| 12250 |
}
|
|
@@ -12303,6 +12320,7 @@
|
|
| 12303 |
"resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz",
|
| 12304 |
"integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==",
|
| 12305 |
"dev": true,
|
|
|
|
| 12306 |
"dependencies": {
|
| 12307 |
"chokidar": ">=3.0.0 <4.0.0",
|
| 12308 |
"immutable": "^4.0.0",
|
|
@@ -13420,6 +13438,7 @@
|
|
| 13420 |
"resolved": "https://registry.npmjs.org/terser/-/terser-5.29.1.tgz",
|
| 13421 |
"integrity": "sha512-lZQ/fyaIGxsbGxApKmoPTODIzELy3++mXhS5hOqaAWZjQtpq/hFHAc+rm29NND1rYRxRWKcjuARNwULNXa5RtQ==",
|
| 13422 |
"dev": true,
|
|
|
|
| 13423 |
"dependencies": {
|
| 13424 |
"@jridgewell/source-map": "^0.3.3",
|
| 13425 |
"acorn": "^8.8.2",
|
|
@@ -13658,6 +13677,7 @@
|
|
| 13658 |
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
|
| 13659 |
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
|
| 13660 |
"dev": true,
|
|
|
|
| 13661 |
"bin": {
|
| 13662 |
"tsc": "bin/tsc",
|
| 13663 |
"tsserver": "bin/tsserver"
|
|
@@ -13891,6 +13911,7 @@
|
|
| 13891 |
"resolved": "https://registry.npmjs.org/vite/-/vite-5.1.8.tgz",
|
| 13892 |
"integrity": "sha512-mB8ToUuSmzODSpENgvpFk2fTiU/YQ1tmcVJJ4WZbq4fPdGJkFNVcmVL5k7iDug6xzWjjuGDKAuSievIsD6H7Xw==",
|
| 13893 |
"dev": true,
|
|
|
|
| 13894 |
"dependencies": {
|
| 13895 |
"esbuild": "^0.19.3",
|
| 13896 |
"postcss": "^8.4.35",
|
|
@@ -14406,6 +14427,7 @@
|
|
| 14406 |
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz",
|
| 14407 |
"integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==",
|
| 14408 |
"dev": true,
|
|
|
|
| 14409 |
"dependencies": {
|
| 14410 |
"@types/estree": "^1.0.5",
|
| 14411 |
"@webassemblyjs/ast": "^1.12.1",
|
|
@@ -14480,6 +14502,7 @@
|
|
| 14480 |
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz",
|
| 14481 |
"integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==",
|
| 14482 |
"dev": true,
|
|
|
|
| 14483 |
"dependencies": {
|
| 14484 |
"@types/bonjour": "^3.5.9",
|
| 14485 |
"@types/connect-history-api-fallback": "^1.3.5",
|
|
@@ -14606,6 +14629,7 @@
|
|
| 14606 |
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
| 14607 |
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
| 14608 |
"dev": true,
|
|
|
|
| 14609 |
"dependencies": {
|
| 14610 |
"fast-deep-equal": "^3.1.1",
|
| 14611 |
"fast-json-stable-stringify": "^2.0.0",
|
|
@@ -14857,7 +14881,8 @@
|
|
| 14857 |
"node_modules/zone.js": {
|
| 14858 |
"version": "0.14.10",
|
| 14859 |
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.10.tgz",
|
| 14860 |
-
"integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ=="
|
|
|
|
| 14861 |
}
|
| 14862 |
},
|
| 14863 |
"dependencies": {
|
|
@@ -14976,6 +15001,7 @@
|
|
| 14976 |
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
|
| 14977 |
"integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==",
|
| 14978 |
"dev": true,
|
|
|
|
| 14979 |
"requires": {
|
| 14980 |
"nanoid": "^3.3.7",
|
| 14981 |
"picocolors": "^1.0.0",
|
|
@@ -15031,6 +15057,7 @@
|
|
| 15031 |
"version": "17.3.12",
|
| 15032 |
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-17.3.12.tgz",
|
| 15033 |
"integrity": "sha512-9hsdWF4gRRcVJtPcCcYLaX1CIyM9wUu6r+xRl6zU5hq8qhl35hig6ounz7CXFAzLf0WDBdM16bPHouVGaG76lg==",
|
|
|
|
| 15034 |
"requires": {
|
| 15035 |
"tslib": "^2.3.0"
|
| 15036 |
}
|
|
@@ -15075,6 +15102,7 @@
|
|
| 15075 |
"version": "17.3.12",
|
| 15076 |
"resolved": "https://registry.npmjs.org/@angular/common/-/common-17.3.12.tgz",
|
| 15077 |
"integrity": "sha512-vabJzvrx76XXFrm1RJZ6o/CyG32piTB/1sfFfKHdlH1QrmArb8It4gyk9oEjZ1IkAD0HvBWlfWmn+T6Vx3pdUw==",
|
|
|
|
| 15078 |
"requires": {
|
| 15079 |
"tslib": "^2.3.0"
|
| 15080 |
}
|
|
@@ -15083,6 +15111,7 @@
|
|
| 15083 |
"version": "17.3.12",
|
| 15084 |
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-17.3.12.tgz",
|
| 15085 |
"integrity": "sha512-vwI8oOL/gM+wPnptOVeBbMfZYwzRxQsovojZf+Zol9szl0k3SZ3FycWlxxXZGFu3VIEfrP6pXplTmyODS/Lt1w==",
|
|
|
|
| 15086 |
"requires": {
|
| 15087 |
"tslib": "^2.3.0"
|
| 15088 |
}
|
|
@@ -15092,6 +15121,7 @@
|
|
| 15092 |
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-17.3.12.tgz",
|
| 15093 |
"integrity": "sha512-1F8M7nWfChzurb7obbvuE7mJXlHtY1UG58pcwcomVtpPb+kPavgAO8OEvJHYBMV+bzSxkXt5UIwL9lt9jHUxZA==",
|
| 15094 |
"dev": true,
|
|
|
|
| 15095 |
"requires": {
|
| 15096 |
"@babel/core": "7.23.9",
|
| 15097 |
"@jridgewell/sourcemap-codec": "^1.4.14",
|
|
@@ -15146,6 +15176,7 @@
|
|
| 15146 |
"version": "17.3.12",
|
| 15147 |
"resolved": "https://registry.npmjs.org/@angular/core/-/core-17.3.12.tgz",
|
| 15148 |
"integrity": "sha512-MuFt5yKi161JmauUta4Dh0m8ofwoq6Ino+KoOtkYMBGsSx+A7dSm+DUxxNwdj7+DNyg3LjVGCFgBFnq4g8z06A==",
|
|
|
|
| 15149 |
"requires": {
|
| 15150 |
"tslib": "^2.3.0"
|
| 15151 |
}
|
|
@@ -15154,6 +15185,7 @@
|
|
| 15154 |
"version": "17.3.12",
|
| 15155 |
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-17.3.12.tgz",
|
| 15156 |
"integrity": "sha512-tV6r12Q3yEUlXwpVko4E+XscunTIpPkLbaiDn/MTL3Vxi2LZnsLgHyd/i38HaHN+e/H3B0a1ToSOhV5wf3ay4Q==",
|
|
|
|
| 15157 |
"requires": {
|
| 15158 |
"tslib": "^2.3.0"
|
| 15159 |
}
|
|
@@ -15217,6 +15249,7 @@
|
|
| 15217 |
"version": "17.3.12",
|
| 15218 |
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-17.3.12.tgz",
|
| 15219 |
"integrity": "sha512-DYY04ptWh/ulMHzd+y52WCE8QnEYGeIiW3hEIFjCN8z0kbIdFdUtEB0IK5vjNL3ejyhUmphcpeT5PYf3YXtqWQ==",
|
|
|
|
| 15220 |
"requires": {
|
| 15221 |
"tslib": "^2.3.0"
|
| 15222 |
}
|
|
@@ -15256,6 +15289,7 @@
|
|
| 15256 |
"version": "7.24.0",
|
| 15257 |
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz",
|
| 15258 |
"integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==",
|
|
|
|
| 15259 |
"requires": {
|
| 15260 |
"@ampproject/remapping": "^2.2.0",
|
| 15261 |
"@babel/code-frame": "^7.23.5",
|
|
@@ -18715,7 +18749,8 @@
|
|
| 18715 |
"version": "8.14.0",
|
| 18716 |
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
| 18717 |
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
|
| 18718 |
-
"dev": true
|
|
|
|
| 18719 |
},
|
| 18720 |
"acorn-import-attributes": {
|
| 18721 |
"version": "1.9.5",
|
|
@@ -18768,6 +18803,7 @@
|
|
| 18768 |
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
| 18769 |
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
|
| 18770 |
"dev": true,
|
|
|
|
| 18771 |
"requires": {
|
| 18772 |
"fast-deep-equal": "^3.1.1",
|
| 18773 |
"json-schema-traverse": "^1.0.0",
|
|
@@ -19112,6 +19148,7 @@
|
|
| 19112 |
"version": "4.24.3",
|
| 19113 |
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz",
|
| 19114 |
"integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==",
|
|
|
|
| 19115 |
"requires": {
|
| 19116 |
"caniuse-lite": "^1.0.30001688",
|
| 19117 |
"electron-to-chromium": "^1.5.73",
|
|
@@ -21309,7 +21346,8 @@
|
|
| 21309 |
"version": "5.1.2",
|
| 21310 |
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.1.2.tgz",
|
| 21311 |
"integrity": "sha512-2oIUMGn00FdUiqz6epiiJr7xcFyNYj3rDcfmnzfkBnHyBQ3cBQUs4mmyGsOb7TTLb9kxk7dBcmEmqhDKkBoDyA==",
|
| 21312 |
-
"dev": true
|
|
|
|
| 21313 |
},
|
| 21314 |
"jest-diff": {
|
| 21315 |
"version": "30.0.0-alpha.6",
|
|
@@ -21852,6 +21890,7 @@
|
|
| 21852 |
"resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz",
|
| 21853 |
"integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==",
|
| 21854 |
"dev": true,
|
|
|
|
| 21855 |
"requires": {
|
| 21856 |
"@colors/colors": "1.5.0",
|
| 21857 |
"body-parser": "^1.19.0",
|
|
@@ -22019,6 +22058,7 @@
|
|
| 22019 |
"resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz",
|
| 22020 |
"integrity": "sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==",
|
| 22021 |
"dev": true,
|
|
|
|
| 22022 |
"requires": {
|
| 22023 |
"copy-anything": "^2.0.1",
|
| 22024 |
"errno": "^0.1.1",
|
|
@@ -23193,6 +23233,7 @@
|
|
| 23193 |
"version": "8.4.49",
|
| 23194 |
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
|
| 23195 |
"integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
|
|
|
|
| 23196 |
"requires": {
|
| 23197 |
"nanoid": "^3.3.7",
|
| 23198 |
"picocolors": "^1.1.1",
|
|
@@ -23765,6 +23806,7 @@
|
|
| 23765 |
"version": "7.8.1",
|
| 23766 |
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
| 23767 |
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
|
|
|
| 23768 |
"requires": {
|
| 23769 |
"tslib": "^2.1.0"
|
| 23770 |
}
|
|
@@ -23802,6 +23844,7 @@
|
|
| 23802 |
"resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz",
|
| 23803 |
"integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==",
|
| 23804 |
"dev": true,
|
|
|
|
| 23805 |
"requires": {
|
| 23806 |
"chokidar": ">=3.0.0 <4.0.0",
|
| 23807 |
"immutable": "^4.0.0",
|
|
@@ -24643,6 +24686,7 @@
|
|
| 24643 |
"resolved": "https://registry.npmjs.org/terser/-/terser-5.29.1.tgz",
|
| 24644 |
"integrity": "sha512-lZQ/fyaIGxsbGxApKmoPTODIzELy3++mXhS5hOqaAWZjQtpq/hFHAc+rm29NND1rYRxRWKcjuARNwULNXa5RtQ==",
|
| 24645 |
"dev": true,
|
|
|
|
| 24646 |
"requires": {
|
| 24647 |
"@jridgewell/source-map": "^0.3.3",
|
| 24648 |
"acorn": "^8.8.2",
|
|
@@ -24810,7 +24854,8 @@
|
|
| 24810 |
"version": "5.3.3",
|
| 24811 |
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
|
| 24812 |
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
|
| 24813 |
-
"dev": true
|
|
|
|
| 24814 |
},
|
| 24815 |
"ua-parser-js": {
|
| 24816 |
"version": "0.7.40",
|
|
@@ -24957,6 +25002,7 @@
|
|
| 24957 |
"resolved": "https://registry.npmjs.org/vite/-/vite-5.1.8.tgz",
|
| 24958 |
"integrity": "sha512-mB8ToUuSmzODSpENgvpFk2fTiU/YQ1tmcVJJ4WZbq4fPdGJkFNVcmVL5k7iDug6xzWjjuGDKAuSievIsD6H7Xw==",
|
| 24959 |
"dev": true,
|
|
|
|
| 24960 |
"requires": {
|
| 24961 |
"esbuild": "^0.19.3",
|
| 24962 |
"fsevents": "~2.3.3",
|
|
@@ -25210,6 +25256,7 @@
|
|
| 25210 |
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz",
|
| 25211 |
"integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==",
|
| 25212 |
"dev": true,
|
|
|
|
| 25213 |
"requires": {
|
| 25214 |
"@types/estree": "^1.0.5",
|
| 25215 |
"@webassemblyjs/ast": "^1.12.1",
|
|
@@ -25241,6 +25288,7 @@
|
|
| 25241 |
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
| 25242 |
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
| 25243 |
"dev": true,
|
|
|
|
| 25244 |
"requires": {
|
| 25245 |
"fast-deep-equal": "^3.1.1",
|
| 25246 |
"fast-json-stable-stringify": "^2.0.0",
|
|
@@ -25308,6 +25356,7 @@
|
|
| 25308 |
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz",
|
| 25309 |
"integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==",
|
| 25310 |
"dev": true,
|
|
|
|
| 25311 |
"requires": {
|
| 25312 |
"@types/bonjour": "^3.5.9",
|
| 25313 |
"@types/connect-history-api-fallback": "^1.3.5",
|
|
@@ -25509,7 +25558,8 @@
|
|
| 25509 |
"zone.js": {
|
| 25510 |
"version": "0.14.10",
|
| 25511 |
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.10.tgz",
|
| 25512 |
-
"integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ=="
|
|
|
|
| 25513 |
}
|
| 25514 |
}
|
| 25515 |
}
|
|
|
|
| 271 |
"url": "https://github.com/sponsors/ai"
|
| 272 |
}
|
| 273 |
],
|
| 274 |
+
"peer": true,
|
| 275 |
"dependencies": {
|
| 276 |
"nanoid": "^3.3.7",
|
| 277 |
"picocolors": "^1.0.0",
|
|
|
|
| 355 |
"version": "17.3.12",
|
| 356 |
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-17.3.12.tgz",
|
| 357 |
"integrity": "sha512-9hsdWF4gRRcVJtPcCcYLaX1CIyM9wUu6r+xRl6zU5hq8qhl35hig6ounz7CXFAzLf0WDBdM16bPHouVGaG76lg==",
|
| 358 |
+
"peer": true,
|
| 359 |
"dependencies": {
|
| 360 |
"tslib": "^2.3.0"
|
| 361 |
},
|
|
|
|
| 422 |
"version": "17.3.12",
|
| 423 |
"resolved": "https://registry.npmjs.org/@angular/common/-/common-17.3.12.tgz",
|
| 424 |
"integrity": "sha512-vabJzvrx76XXFrm1RJZ6o/CyG32piTB/1sfFfKHdlH1QrmArb8It4gyk9oEjZ1IkAD0HvBWlfWmn+T6Vx3pdUw==",
|
| 425 |
+
"peer": true,
|
| 426 |
"dependencies": {
|
| 427 |
"tslib": "^2.3.0"
|
| 428 |
},
|
|
|
|
| 438 |
"version": "17.3.12",
|
| 439 |
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-17.3.12.tgz",
|
| 440 |
"integrity": "sha512-vwI8oOL/gM+wPnptOVeBbMfZYwzRxQsovojZf+Zol9szl0k3SZ3FycWlxxXZGFu3VIEfrP6pXplTmyODS/Lt1w==",
|
| 441 |
+
"peer": true,
|
| 442 |
"dependencies": {
|
| 443 |
"tslib": "^2.3.0"
|
| 444 |
},
|
|
|
|
| 459 |
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-17.3.12.tgz",
|
| 460 |
"integrity": "sha512-1F8M7nWfChzurb7obbvuE7mJXlHtY1UG58pcwcomVtpPb+kPavgAO8OEvJHYBMV+bzSxkXt5UIwL9lt9jHUxZA==",
|
| 461 |
"dev": true,
|
| 462 |
+
"peer": true,
|
| 463 |
"dependencies": {
|
| 464 |
"@babel/core": "7.23.9",
|
| 465 |
"@jridgewell/sourcemap-codec": "^1.4.14",
|
|
|
|
| 532 |
"version": "17.3.12",
|
| 533 |
"resolved": "https://registry.npmjs.org/@angular/core/-/core-17.3.12.tgz",
|
| 534 |
"integrity": "sha512-MuFt5yKi161JmauUta4Dh0m8ofwoq6Ino+KoOtkYMBGsSx+A7dSm+DUxxNwdj7+DNyg3LjVGCFgBFnq4g8z06A==",
|
| 535 |
+
"peer": true,
|
| 536 |
"dependencies": {
|
| 537 |
"tslib": "^2.3.0"
|
| 538 |
},
|
|
|
|
| 548 |
"version": "17.3.12",
|
| 549 |
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-17.3.12.tgz",
|
| 550 |
"integrity": "sha512-tV6r12Q3yEUlXwpVko4E+XscunTIpPkLbaiDn/MTL3Vxi2LZnsLgHyd/i38HaHN+e/H3B0a1ToSOhV5wf3ay4Q==",
|
| 551 |
+
"peer": true,
|
| 552 |
"dependencies": {
|
| 553 |
"tslib": "^2.3.0"
|
| 554 |
},
|
|
|
|
| 631 |
"version": "17.3.12",
|
| 632 |
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-17.3.12.tgz",
|
| 633 |
"integrity": "sha512-DYY04ptWh/ulMHzd+y52WCE8QnEYGeIiW3hEIFjCN8z0kbIdFdUtEB0IK5vjNL3ejyhUmphcpeT5PYf3YXtqWQ==",
|
| 634 |
+
"peer": true,
|
| 635 |
"dependencies": {
|
| 636 |
"tslib": "^2.3.0"
|
| 637 |
},
|
|
|
|
| 708 |
"version": "7.24.0",
|
| 709 |
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz",
|
| 710 |
"integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==",
|
| 711 |
+
"peer": true,
|
| 712 |
"dependencies": {
|
| 713 |
"@ampproject/remapping": "^2.2.0",
|
| 714 |
"@babel/code-frame": "^7.23.5",
|
|
|
|
| 5343 |
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
| 5344 |
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
|
| 5345 |
"dev": true,
|
| 5346 |
+
"peer": true,
|
| 5347 |
"bin": {
|
| 5348 |
"acorn": "bin/acorn"
|
| 5349 |
},
|
|
|
|
| 5414 |
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
| 5415 |
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
|
| 5416 |
"dev": true,
|
| 5417 |
+
"peer": true,
|
| 5418 |
"dependencies": {
|
| 5419 |
"fast-deep-equal": "^3.1.1",
|
| 5420 |
"json-schema-traverse": "^1.0.0",
|
|
|
|
| 5898 |
"url": "https://github.com/sponsors/ai"
|
| 5899 |
}
|
| 5900 |
],
|
| 5901 |
+
"peer": true,
|
| 5902 |
"dependencies": {
|
| 5903 |
"caniuse-lite": "^1.0.30001688",
|
| 5904 |
"electron-to-chromium": "^1.5.73",
|
|
|
|
| 8896 |
"version": "5.1.2",
|
| 8897 |
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.1.2.tgz",
|
| 8898 |
"integrity": "sha512-2oIUMGn00FdUiqz6epiiJr7xcFyNYj3rDcfmnzfkBnHyBQ3cBQUs4mmyGsOb7TTLb9kxk7dBcmEmqhDKkBoDyA==",
|
| 8899 |
+
"dev": true,
|
| 8900 |
+
"peer": true
|
| 8901 |
},
|
| 8902 |
"node_modules/jest-diff": {
|
| 8903 |
"version": "30.0.0-alpha.6",
|
|
|
|
| 9607 |
"resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz",
|
| 9608 |
"integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==",
|
| 9609 |
"dev": true,
|
| 9610 |
+
"peer": true,
|
| 9611 |
"dependencies": {
|
| 9612 |
"@colors/colors": "1.5.0",
|
| 9613 |
"body-parser": "^1.19.0",
|
|
|
|
| 9814 |
"resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz",
|
| 9815 |
"integrity": "sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==",
|
| 9816 |
"dev": true,
|
| 9817 |
+
"peer": true,
|
| 9818 |
"dependencies": {
|
| 9819 |
"copy-anything": "^2.0.1",
|
| 9820 |
"parse-node-version": "^1.0.1",
|
|
|
|
| 11429 |
"url": "https://github.com/sponsors/ai"
|
| 11430 |
}
|
| 11431 |
],
|
| 11432 |
+
"peer": true,
|
| 11433 |
"dependencies": {
|
| 11434 |
"nanoid": "^3.3.7",
|
| 11435 |
"picocolors": "^1.1.1",
|
|
|
|
| 12261 |
"version": "7.8.1",
|
| 12262 |
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
| 12263 |
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
| 12264 |
+
"peer": true,
|
| 12265 |
"dependencies": {
|
| 12266 |
"tslib": "^2.1.0"
|
| 12267 |
}
|
|
|
|
| 12320 |
"resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz",
|
| 12321 |
"integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==",
|
| 12322 |
"dev": true,
|
| 12323 |
+
"peer": true,
|
| 12324 |
"dependencies": {
|
| 12325 |
"chokidar": ">=3.0.0 <4.0.0",
|
| 12326 |
"immutable": "^4.0.0",
|
|
|
|
| 13438 |
"resolved": "https://registry.npmjs.org/terser/-/terser-5.29.1.tgz",
|
| 13439 |
"integrity": "sha512-lZQ/fyaIGxsbGxApKmoPTODIzELy3++mXhS5hOqaAWZjQtpq/hFHAc+rm29NND1rYRxRWKcjuARNwULNXa5RtQ==",
|
| 13440 |
"dev": true,
|
| 13441 |
+
"peer": true,
|
| 13442 |
"dependencies": {
|
| 13443 |
"@jridgewell/source-map": "^0.3.3",
|
| 13444 |
"acorn": "^8.8.2",
|
|
|
|
| 13677 |
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
|
| 13678 |
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
|
| 13679 |
"dev": true,
|
| 13680 |
+
"peer": true,
|
| 13681 |
"bin": {
|
| 13682 |
"tsc": "bin/tsc",
|
| 13683 |
"tsserver": "bin/tsserver"
|
|
|
|
| 13911 |
"resolved": "https://registry.npmjs.org/vite/-/vite-5.1.8.tgz",
|
| 13912 |
"integrity": "sha512-mB8ToUuSmzODSpENgvpFk2fTiU/YQ1tmcVJJ4WZbq4fPdGJkFNVcmVL5k7iDug6xzWjjuGDKAuSievIsD6H7Xw==",
|
| 13913 |
"dev": true,
|
| 13914 |
+
"peer": true,
|
| 13915 |
"dependencies": {
|
| 13916 |
"esbuild": "^0.19.3",
|
| 13917 |
"postcss": "^8.4.35",
|
|
|
|
| 14427 |
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz",
|
| 14428 |
"integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==",
|
| 14429 |
"dev": true,
|
| 14430 |
+
"peer": true,
|
| 14431 |
"dependencies": {
|
| 14432 |
"@types/estree": "^1.0.5",
|
| 14433 |
"@webassemblyjs/ast": "^1.12.1",
|
|
|
|
| 14502 |
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz",
|
| 14503 |
"integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==",
|
| 14504 |
"dev": true,
|
| 14505 |
+
"peer": true,
|
| 14506 |
"dependencies": {
|
| 14507 |
"@types/bonjour": "^3.5.9",
|
| 14508 |
"@types/connect-history-api-fallback": "^1.3.5",
|
|
|
|
| 14629 |
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
| 14630 |
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
| 14631 |
"dev": true,
|
| 14632 |
+
"peer": true,
|
| 14633 |
"dependencies": {
|
| 14634 |
"fast-deep-equal": "^3.1.1",
|
| 14635 |
"fast-json-stable-stringify": "^2.0.0",
|
|
|
|
| 14881 |
"node_modules/zone.js": {
|
| 14882 |
"version": "0.14.10",
|
| 14883 |
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.10.tgz",
|
| 14884 |
+
"integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ==",
|
| 14885 |
+
"peer": true
|
| 14886 |
}
|
| 14887 |
},
|
| 14888 |
"dependencies": {
|
|
|
|
| 15001 |
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
|
| 15002 |
"integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==",
|
| 15003 |
"dev": true,
|
| 15004 |
+
"peer": true,
|
| 15005 |
"requires": {
|
| 15006 |
"nanoid": "^3.3.7",
|
| 15007 |
"picocolors": "^1.0.0",
|
|
|
|
| 15057 |
"version": "17.3.12",
|
| 15058 |
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-17.3.12.tgz",
|
| 15059 |
"integrity": "sha512-9hsdWF4gRRcVJtPcCcYLaX1CIyM9wUu6r+xRl6zU5hq8qhl35hig6ounz7CXFAzLf0WDBdM16bPHouVGaG76lg==",
|
| 15060 |
+
"peer": true,
|
| 15061 |
"requires": {
|
| 15062 |
"tslib": "^2.3.0"
|
| 15063 |
}
|
|
|
|
| 15102 |
"version": "17.3.12",
|
| 15103 |
"resolved": "https://registry.npmjs.org/@angular/common/-/common-17.3.12.tgz",
|
| 15104 |
"integrity": "sha512-vabJzvrx76XXFrm1RJZ6o/CyG32piTB/1sfFfKHdlH1QrmArb8It4gyk9oEjZ1IkAD0HvBWlfWmn+T6Vx3pdUw==",
|
| 15105 |
+
"peer": true,
|
| 15106 |
"requires": {
|
| 15107 |
"tslib": "^2.3.0"
|
| 15108 |
}
|
|
|
|
| 15111 |
"version": "17.3.12",
|
| 15112 |
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-17.3.12.tgz",
|
| 15113 |
"integrity": "sha512-vwI8oOL/gM+wPnptOVeBbMfZYwzRxQsovojZf+Zol9szl0k3SZ3FycWlxxXZGFu3VIEfrP6pXplTmyODS/Lt1w==",
|
| 15114 |
+
"peer": true,
|
| 15115 |
"requires": {
|
| 15116 |
"tslib": "^2.3.0"
|
| 15117 |
}
|
|
|
|
| 15121 |
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-17.3.12.tgz",
|
| 15122 |
"integrity": "sha512-1F8M7nWfChzurb7obbvuE7mJXlHtY1UG58pcwcomVtpPb+kPavgAO8OEvJHYBMV+bzSxkXt5UIwL9lt9jHUxZA==",
|
| 15123 |
"dev": true,
|
| 15124 |
+
"peer": true,
|
| 15125 |
"requires": {
|
| 15126 |
"@babel/core": "7.23.9",
|
| 15127 |
"@jridgewell/sourcemap-codec": "^1.4.14",
|
|
|
|
| 15176 |
"version": "17.3.12",
|
| 15177 |
"resolved": "https://registry.npmjs.org/@angular/core/-/core-17.3.12.tgz",
|
| 15178 |
"integrity": "sha512-MuFt5yKi161JmauUta4Dh0m8ofwoq6Ino+KoOtkYMBGsSx+A7dSm+DUxxNwdj7+DNyg3LjVGCFgBFnq4g8z06A==",
|
| 15179 |
+
"peer": true,
|
| 15180 |
"requires": {
|
| 15181 |
"tslib": "^2.3.0"
|
| 15182 |
}
|
|
|
|
| 15185 |
"version": "17.3.12",
|
| 15186 |
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-17.3.12.tgz",
|
| 15187 |
"integrity": "sha512-tV6r12Q3yEUlXwpVko4E+XscunTIpPkLbaiDn/MTL3Vxi2LZnsLgHyd/i38HaHN+e/H3B0a1ToSOhV5wf3ay4Q==",
|
| 15188 |
+
"peer": true,
|
| 15189 |
"requires": {
|
| 15190 |
"tslib": "^2.3.0"
|
| 15191 |
}
|
|
|
|
| 15249 |
"version": "17.3.12",
|
| 15250 |
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-17.3.12.tgz",
|
| 15251 |
"integrity": "sha512-DYY04ptWh/ulMHzd+y52WCE8QnEYGeIiW3hEIFjCN8z0kbIdFdUtEB0IK5vjNL3ejyhUmphcpeT5PYf3YXtqWQ==",
|
| 15252 |
+
"peer": true,
|
| 15253 |
"requires": {
|
| 15254 |
"tslib": "^2.3.0"
|
| 15255 |
}
|
|
|
|
| 15289 |
"version": "7.24.0",
|
| 15290 |
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz",
|
| 15291 |
"integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==",
|
| 15292 |
+
"peer": true,
|
| 15293 |
"requires": {
|
| 15294 |
"@ampproject/remapping": "^2.2.0",
|
| 15295 |
"@babel/code-frame": "^7.23.5",
|
|
|
|
| 18749 |
"version": "8.14.0",
|
| 18750 |
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
| 18751 |
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
|
| 18752 |
+
"dev": true,
|
| 18753 |
+
"peer": true
|
| 18754 |
},
|
| 18755 |
"acorn-import-attributes": {
|
| 18756 |
"version": "1.9.5",
|
|
|
|
| 18803 |
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
| 18804 |
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
|
| 18805 |
"dev": true,
|
| 18806 |
+
"peer": true,
|
| 18807 |
"requires": {
|
| 18808 |
"fast-deep-equal": "^3.1.1",
|
| 18809 |
"json-schema-traverse": "^1.0.0",
|
|
|
|
| 19148 |
"version": "4.24.3",
|
| 19149 |
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz",
|
| 19150 |
"integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==",
|
| 19151 |
+
"peer": true,
|
| 19152 |
"requires": {
|
| 19153 |
"caniuse-lite": "^1.0.30001688",
|
| 19154 |
"electron-to-chromium": "^1.5.73",
|
|
|
|
| 21346 |
"version": "5.1.2",
|
| 21347 |
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.1.2.tgz",
|
| 21348 |
"integrity": "sha512-2oIUMGn00FdUiqz6epiiJr7xcFyNYj3rDcfmnzfkBnHyBQ3cBQUs4mmyGsOb7TTLb9kxk7dBcmEmqhDKkBoDyA==",
|
| 21349 |
+
"dev": true,
|
| 21350 |
+
"peer": true
|
| 21351 |
},
|
| 21352 |
"jest-diff": {
|
| 21353 |
"version": "30.0.0-alpha.6",
|
|
|
|
| 21890 |
"resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz",
|
| 21891 |
"integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==",
|
| 21892 |
"dev": true,
|
| 21893 |
+
"peer": true,
|
| 21894 |
"requires": {
|
| 21895 |
"@colors/colors": "1.5.0",
|
| 21896 |
"body-parser": "^1.19.0",
|
|
|
|
| 22058 |
"resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz",
|
| 22059 |
"integrity": "sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==",
|
| 22060 |
"dev": true,
|
| 22061 |
+
"peer": true,
|
| 22062 |
"requires": {
|
| 22063 |
"copy-anything": "^2.0.1",
|
| 22064 |
"errno": "^0.1.1",
|
|
|
|
| 23233 |
"version": "8.4.49",
|
| 23234 |
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
|
| 23235 |
"integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
|
| 23236 |
+
"peer": true,
|
| 23237 |
"requires": {
|
| 23238 |
"nanoid": "^3.3.7",
|
| 23239 |
"picocolors": "^1.1.1",
|
|
|
|
| 23806 |
"version": "7.8.1",
|
| 23807 |
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
| 23808 |
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
| 23809 |
+
"peer": true,
|
| 23810 |
"requires": {
|
| 23811 |
"tslib": "^2.1.0"
|
| 23812 |
}
|
|
|
|
| 23844 |
"resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz",
|
| 23845 |
"integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==",
|
| 23846 |
"dev": true,
|
| 23847 |
+
"peer": true,
|
| 23848 |
"requires": {
|
| 23849 |
"chokidar": ">=3.0.0 <4.0.0",
|
| 23850 |
"immutable": "^4.0.0",
|
|
|
|
| 24686 |
"resolved": "https://registry.npmjs.org/terser/-/terser-5.29.1.tgz",
|
| 24687 |
"integrity": "sha512-lZQ/fyaIGxsbGxApKmoPTODIzELy3++mXhS5hOqaAWZjQtpq/hFHAc+rm29NND1rYRxRWKcjuARNwULNXa5RtQ==",
|
| 24688 |
"dev": true,
|
| 24689 |
+
"peer": true,
|
| 24690 |
"requires": {
|
| 24691 |
"@jridgewell/source-map": "^0.3.3",
|
| 24692 |
"acorn": "^8.8.2",
|
|
|
|
| 24854 |
"version": "5.3.3",
|
| 24855 |
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
|
| 24856 |
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
|
| 24857 |
+
"dev": true,
|
| 24858 |
+
"peer": true
|
| 24859 |
},
|
| 24860 |
"ua-parser-js": {
|
| 24861 |
"version": "0.7.40",
|
|
|
|
| 25002 |
"resolved": "https://registry.npmjs.org/vite/-/vite-5.1.8.tgz",
|
| 25003 |
"integrity": "sha512-mB8ToUuSmzODSpENgvpFk2fTiU/YQ1tmcVJJ4WZbq4fPdGJkFNVcmVL5k7iDug6xzWjjuGDKAuSievIsD6H7Xw==",
|
| 25004 |
"dev": true,
|
| 25005 |
+
"peer": true,
|
| 25006 |
"requires": {
|
| 25007 |
"esbuild": "^0.19.3",
|
| 25008 |
"fsevents": "~2.3.3",
|
|
|
|
| 25256 |
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz",
|
| 25257 |
"integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==",
|
| 25258 |
"dev": true,
|
| 25259 |
+
"peer": true,
|
| 25260 |
"requires": {
|
| 25261 |
"@types/estree": "^1.0.5",
|
| 25262 |
"@webassemblyjs/ast": "^1.12.1",
|
|
|
|
| 25288 |
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
| 25289 |
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
| 25290 |
"dev": true,
|
| 25291 |
+
"peer": true,
|
| 25292 |
"requires": {
|
| 25293 |
"fast-deep-equal": "^3.1.1",
|
| 25294 |
"fast-json-stable-stringify": "^2.0.0",
|
|
|
|
| 25356 |
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz",
|
| 25357 |
"integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==",
|
| 25358 |
"dev": true,
|
| 25359 |
+
"peer": true,
|
| 25360 |
"requires": {
|
| 25361 |
"@types/bonjour": "^3.5.9",
|
| 25362 |
"@types/connect-history-api-fallback": "^1.3.5",
|
|
|
|
| 25558 |
"zone.js": {
|
| 25559 |
"version": "0.14.10",
|
| 25560 |
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.10.tgz",
|
| 25561 |
+
"integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ==",
|
| 25562 |
+
"peer": true
|
| 25563 |
}
|
| 25564 |
}
|
| 25565 |
}
|
src/app/generate-questions/generate-questions.component.html
CHANGED
|
@@ -83,8 +83,8 @@
|
|
| 83 |
pattern="[A-Za-z]*"
|
| 84 |
(input)="onInput($event, question.index); onAnswerChange(question.index)"
|
| 85 |
[ngClass]="{
|
| 86 |
-
'correct-answer':
|
| 87 |
-
'wrong-answer':
|
| 88 |
}" />
|
| 89 |
</span>
|
| 90 |
|
|
@@ -124,7 +124,7 @@
|
|
| 124 |
|
| 125 |
<ul class="hint-list" role="list">
|
| 126 |
<ng-container *ngFor="let hint of hints; let i = index">
|
| 127 |
-
<li *ngIf="hint && hint.trim() !== ''
|
| 128 |
class="hint-item">
|
| 129 |
<strong>Hint for Question {{ i + 1 }}:</strong> {{ hint }}
|
| 130 |
</li>
|
|
|
|
| 83 |
pattern="[A-Za-z]*"
|
| 84 |
(input)="onInput($event, question.index); onAnswerChange(question.index)"
|
| 85 |
[ngClass]="{
|
| 86 |
+
'correct-answer': answerStatuses[question.index] === 'correct',
|
| 87 |
+
'wrong-answer': answerStatuses[question.index] === 'incorrect'
|
| 88 |
}" />
|
| 89 |
</span>
|
| 90 |
|
|
|
|
| 124 |
|
| 125 |
<ul class="hint-list" role="list">
|
| 126 |
<ng-container *ngFor="let hint of hints; let i = index">
|
| 127 |
+
<li *ngIf="hint && hint.trim() !== ''"
|
| 128 |
class="hint-item">
|
| 129 |
<strong>Hint for Question {{ i + 1 }}:</strong> {{ hint }}
|
| 130 |
</li>
|
src/app/generate-questions/generate-questions.component.ts
CHANGED
|
@@ -1,13 +1,11 @@
|
|
| 1 |
-
import { Component } from '@angular/core';
|
| 2 |
import { FormsModule } from '@angular/forms';
|
| 3 |
import { CommonModule } from '@angular/common';
|
| 4 |
import { GenerateQuestionsService } from './generate-questions.service';
|
| 5 |
import { Router } from '@angular/router';
|
| 6 |
import confetti from 'canvas-confetti';
|
| 7 |
-
import { ChangeDetectorRef } from '@angular/core';
|
| 8 |
import { HeaderComponent } from '../shared/header/header.component';
|
| 9 |
-
import { ButtonComponent } from '../shared/button/button.component';
|
| 10 |
-
|
| 11 |
|
| 12 |
interface QuestionWithAnswer {
|
| 13 |
question: string;
|
|
@@ -19,530 +17,450 @@ interface Question {
|
|
| 19 |
index: number;
|
| 20 |
}
|
| 21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
@Component({
|
| 23 |
selector: 'app-generate-questions',
|
| 24 |
standalone: true,
|
| 25 |
-
imports: [FormsModule, CommonModule, HeaderComponent, ButtonComponent],
|
| 26 |
templateUrl: './generate-questions.component.html',
|
| 27 |
styleUrls: ['./generate-questions.component.css']
|
| 28 |
})
|
| 29 |
-
export class GenerateQuestionsComponent {
|
| 30 |
-
topic
|
| 31 |
-
hardcodedTopics
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
"Past Tense",
|
| 35 |
-
"Adjective",
|
| 36 |
-
"Present Continuous",
|
| 37 |
-
];
|
| 38 |
-
|
| 39 |
-
showSuggestions: boolean = false;
|
| 40 |
|
| 41 |
userAnswers: string[] = [];
|
|
|
|
| 42 |
feedback: string[] = [];
|
| 43 |
-
error: string = "";
|
| 44 |
-
isChecked: boolean = false;
|
| 45 |
-
isAnswerModified: boolean = false;
|
| 46 |
-
questions: { parts: string[]; index: number }[] = [];
|
| 47 |
-
attemptCounts: { [key: number]: number } = {};
|
| 48 |
-
correctAnswers: string[] = [];
|
| 49 |
-
questionsWithAnswers: QuestionWithAnswer[] = [];
|
| 50 |
-
currentDifficulty: string = "basic";
|
| 51 |
-
// difficultyLevels: string[] = ['basic', 'elementary', 'pre-intermediate', 'intermediate', 'upper-intermediate', 'advanced', 'hard'];
|
| 52 |
-
difficultyLevels: string[] = ["basic", "intermediate", "expert"];
|
| 53 |
-
isDropdownDisabled: boolean = false;
|
| 54 |
-
isValidationInProgress: boolean = false;
|
| 55 |
-
isTopicLocked: boolean = false;
|
| 56 |
-
isAllLevelsCompleted: boolean = false;
|
| 57 |
-
readonlyAnswers: boolean[] = [];
|
| 58 |
hints: string[] = [];
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
|
| 71 |
-
|
| 72 |
-
|
|
|
|
|
|
|
|
|
|
| 73 |
|
| 74 |
constructor(
|
| 75 |
-
private
|
| 76 |
-
private router: Router
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
if (this.
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
}
|
| 92 |
|
| 93 |
getProgressWidth(): string {
|
| 94 |
const index = this.difficultyLevels.indexOf(this.currentDifficulty);
|
| 95 |
-
if (index === -1 || this.difficultyLevels.length <= 1) return
|
| 96 |
-
|
| 97 |
-
return `${percent}%`;
|
| 98 |
}
|
| 99 |
|
| 100 |
-
generateQuestions() {
|
| 101 |
-
this.hints = [];
|
| 102 |
-
this.isQuestionsGenerated = false;
|
| 103 |
-
this.showLevelTooltip = true;
|
| 104 |
-
this.activateLevelDot = true;
|
| 105 |
-
|
| 106 |
if (this.isAllLevelsCompleted) {
|
| 107 |
-
this.error =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
return;
|
| 109 |
}
|
| 110 |
|
| 111 |
-
this.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
this.showSuggestions = false;
|
| 113 |
this.readonlyAnswers = [];
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
this.generateQuestionsService
|
| 119 |
-
.generateQuestions(this.topic, this.currentDifficulty)
|
| 120 |
-
.subscribe(
|
| 121 |
-
(response) => {
|
| 122 |
-
console.log("Response from backend:", response);
|
| 123 |
-
|
| 124 |
-
// --- Minimal change: accept new shape { text } and fall back to old generations[0].text ---
|
| 125 |
-
const rawQuestions = (response?.text ?? response?.generations?.[0]?.text ?? '').trim();
|
| 126 |
-
|
| 127 |
-
if (rawQuestions) {
|
| 128 |
-
console.log("Raw Questions:", rawQuestions);
|
| 129 |
-
this.isQuestionsGenerated = true;
|
| 130 |
-
|
| 131 |
-
this.isGenerateDisabled = true;
|
| 132 |
-
this.isResetDisabled = false;
|
| 133 |
-
|
| 134 |
-
this.questionsWithAnswers = [];
|
| 135 |
-
|
| 136 |
-
// same regex you already use
|
| 137 |
-
const regex = /(\d+\.\s*.+?_______)\s*(.*?)\s*\(([^)]+)\)\s*$/gm;
|
| 138 |
-
let match;
|
| 139 |
-
|
| 140 |
-
while ((match = regex.exec(rawQuestions)) !== null) {
|
| 141 |
-
console.log("Match:", match);
|
| 142 |
-
|
| 143 |
-
const questionText = (match[1] ?? "").trim(); // up to _______
|
| 144 |
-
const afterBlankRaw = (match[2] ?? ""); // text after the blank
|
| 145 |
-
const correctAnswer = (match[3] ?? "").trim(); // answer in parentheses
|
| 146 |
-
|
| 147 |
-
// clean any stray "_" or "-" right after the blank
|
| 148 |
-
const afterBlankClean = afterBlankRaw
|
| 149 |
-
.replace(/^[_-]+/, "")
|
| 150 |
-
.replace(/^\s+/, " ")
|
| 151 |
-
.replace(/\s{2,}/g, " ");
|
| 152 |
-
|
| 153 |
-
if (questionText && correctAnswer) {
|
| 154 |
-
this.questionsWithAnswers.push({
|
| 155 |
-
question: `${questionText}${afterBlankClean}`,
|
| 156 |
-
correctAnswer,
|
| 157 |
-
});
|
| 158 |
-
}
|
| 159 |
-
}
|
| 160 |
-
|
| 161 |
-
console.log("Extracted Questions with Answers:", this.questionsWithAnswers);
|
| 162 |
-
|
| 163 |
-
// Build renderable questions (parts before/after the blank)
|
| 164 |
-
this.questions = this.questionsWithAnswers.map((q, index) => {
|
| 165 |
-
const normalized = q.question.replace(/\s*_{3,}\s*/g, "_______");
|
| 166 |
-
const parts = normalized.split("_______");
|
| 167 |
-
|
| 168 |
-
if (parts.length === 2) {
|
| 169 |
-
parts[0] = parts[0].replace(/_+$/, "").trim();
|
| 170 |
-
parts[1] = parts[1].replace(/^_+/, "").trim();
|
| 171 |
-
}
|
| 172 |
-
|
| 173 |
-
return { parts, index };
|
| 174 |
-
});
|
| 175 |
-
|
| 176 |
-
this.userAnswers = new Array(this.questions.length).fill("");
|
| 177 |
-
this.feedback = new Array(this.questions.length).fill("");
|
| 178 |
-
this.readonlyAnswers = new Array(this.questions.length).fill(false);
|
| 179 |
-
this.isChecked = false;
|
| 180 |
-
|
| 181 |
-
this.attemptCounts = this.questions.reduce((acc, _q, i) => {
|
| 182 |
-
acc[i] = 0;
|
| 183 |
-
return acc;
|
| 184 |
-
}, {} as { [key: number]: number });
|
| 185 |
-
|
| 186 |
-
console.log("Questions without answers:", this.questions);
|
| 187 |
-
} else {
|
| 188 |
-
this.error = "No questions generated. Please try again.";
|
| 189 |
-
}
|
| 190 |
-
},
|
| 191 |
-
(error) => {
|
| 192 |
-
if (error.status === 400 && error.error.message) {
|
| 193 |
-
this.error = error.error.message;
|
| 194 |
-
} else {
|
| 195 |
-
this.error = "Failed to fetch questions. Please try again later.";
|
| 196 |
-
}
|
| 197 |
-
console.error("Error fetching questions:", error);
|
| 198 |
-
},
|
| 199 |
-
);
|
| 200 |
-
}
|
| 201 |
-
|
| 202 |
-
resetTopic() {
|
| 203 |
-
this.topic = "";
|
| 204 |
this.questions = [];
|
| 205 |
this.questionsWithAnswers = [];
|
| 206 |
this.userAnswers = [];
|
|
|
|
| 207 |
this.readonlyAnswers = [];
|
| 208 |
this.feedback = [];
|
| 209 |
this.hints = [];
|
| 210 |
-
this.attemptCounts =
|
| 211 |
-
|
|
|
|
| 212 |
this.isValidationInProgress = false;
|
| 213 |
this.isFirstAttemptDone = false;
|
| 214 |
this.isAllLevelsCompleted = false;
|
| 215 |
this.hasNewHints = false;
|
| 216 |
-
this.isQuestionsGenerated = false;
|
| 217 |
-
this.isTopicLocked = false;
|
| 218 |
-
this.validated = false;
|
| 219 |
-
this.currentDifficulty = "basic";
|
| 220 |
-
this.error = "";
|
| 221 |
-
this.isGenerateDisabled = true;
|
| 222 |
-
this.isResetDisabled = true;
|
| 223 |
-
this.showSuggestions = false;
|
| 224 |
-
this.showLevelTooltip = false;
|
| 225 |
-
this.activateLevelDot = false;
|
| 226 |
this.showSuggestions = false;
|
| 227 |
-
this.
|
| 228 |
-
this.
|
|
|
|
|
|
|
|
|
|
| 229 |
this.stopConfetti();
|
| 230 |
-
// Stop any active countdowns
|
| 231 |
-
Object.keys(this.countdownTimers).forEach(k => clearInterval(this.countdownTimers[+k]));
|
| 232 |
-
this.countdownTimers = {};
|
| 233 |
-
this.countdowns = [];
|
| 234 |
-
this.showCountdown = [];
|
| 235 |
-
this.stopGlobalCountdown();
|
| 236 |
-
this.stopOverlay()
|
| 237 |
}
|
| 238 |
|
| 239 |
areAllAnswersFilled(): boolean {
|
| 240 |
-
return this.userAnswers.every(
|
| 241 |
}
|
| 242 |
|
| 243 |
-
closeErrorPopup() {
|
| 244 |
-
this.error =
|
| 245 |
-
this.isGenerateDisabled = true;
|
| 246 |
-
this.isResetDisabled = false;
|
| 247 |
}
|
| 248 |
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
|
|
|
|
|
|
|
|
|
| 253 |
|
| 254 |
-
|
| 255 |
-
|
|
|
|
|
|
|
| 256 |
this.isValidationInProgress = true;
|
| 257 |
this.isLoading = true;
|
| 258 |
-
this.isValidationCompleted = true;
|
| 259 |
-
this.validated = true;
|
| 260 |
this.isFirstAttemptDone = true;
|
| 261 |
|
| 262 |
-
const
|
| 263 |
topic: this.topic,
|
| 264 |
-
question:
|
| 265 |
-
user_answer: this.userAnswers[
|
| 266 |
}));
|
| 267 |
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
this.isAllLevelsCompleted = true;
|
| 343 |
-
console.log("🎉 You have completed all difficulty levels.");
|
| 344 |
-
this.triggerConfetti();
|
| 345 |
-
}, this.COUNTDOWN_SECS * 1000);
|
| 346 |
-
}
|
| 347 |
-
|
| 348 |
-
}, this.COUNTDOWN_SECS * 1000);
|
| 349 |
-
}
|
| 350 |
-
|
| 351 |
-
this.hints[index] = hint.length > 0 ? hint : null;
|
| 352 |
-
}
|
| 353 |
-
|
| 354 |
-
this.feedback[index] = validationResponse || "No feedback provided.";
|
| 355 |
-
|
| 356 |
-
// minor 2s reflection (kept as-is)
|
| 357 |
-
setTimeout(() => {
|
| 358 |
-
if (userAnswer === correctAnswer) {
|
| 359 |
-
this.userAnswers[index] = this.userAnswers[index];
|
| 360 |
-
} else {
|
| 361 |
-
this.userAnswers[index] = this.userAnswers[index];
|
| 362 |
-
}
|
| 363 |
-
}, 2000);
|
| 364 |
-
|
| 365 |
-
if (this.attemptCounts[index] < 2) {
|
| 366 |
-
allAttemptsCompleted = false;
|
| 367 |
-
}
|
| 368 |
-
});
|
| 369 |
-
|
| 370 |
-
// Hints auto-hide (unchanged)
|
| 371 |
-
this.hasNewHints = this.hints.some((h) => h && h.trim() !== "");
|
| 372 |
-
if (this.hasNewHints) {
|
| 373 |
-
setTimeout(() => { this.hasNewHints = false; }, 5000);
|
| 374 |
-
}
|
| 375 |
-
|
| 376 |
-
// --- ONE-TIME “i” icon reveal right after the first validation ---
|
| 377 |
-
const hasAnyHint = Array.isArray(this.hints) && this.hints.some(h => !!h && h.trim() !== "");
|
| 378 |
-
const shouldRevealIconOnce = hasAnyHint || hadFirstAttemptWrong || hadSecondAttemptWrong;
|
| 379 |
-
|
| 380 |
-
if (!this.hasShownHintIcon && shouldRevealIconOnce) {
|
| 381 |
-
this.showHintIcon = true; // reveal once
|
| 382 |
-
this.hasShownHintIcon = true; // lock it
|
| 383 |
-
} else {
|
| 384 |
-
this.showHintIcon = false; // never show again in later validations
|
| 385 |
-
}
|
| 386 |
-
// --- END ONE-TIME “i” icon reveal ---
|
| 387 |
-
|
| 388 |
-
if (this.isLastLevel() && this.areAllCorrectAnswersDisplayed()) {
|
| 389 |
-
// Last level, all correct: show finish counter, then congratulations
|
| 390 |
-
this.stopOverlay(); // ensure no old timer remains
|
| 391 |
-
this.startOverlay("Finishing in");
|
| 392 |
-
setTimeout(() => {
|
| 393 |
-
this.stopOverlay();
|
| 394 |
-
this.isAllLevelsCompleted = true;
|
| 395 |
-
this.triggerConfetti();
|
| 396 |
-
}, this.COUNTDOWN_SECS * 1000);
|
| 397 |
-
|
| 398 |
-
} else if (!this.isLastLevel() && (this.areAllCorrectAnswersDisplayed() || allAttemptsCompleted)) {
|
| 399 |
-
// Not last level: even if all answers were correct on first try,
|
| 400 |
-
// show the counter before moving to the next level
|
| 401 |
-
this.stopOverlay(); // ensure fresh HUD
|
| 402 |
-
this.startOverlay("Moving to next level in");
|
| 403 |
-
setTimeout(() => {
|
| 404 |
-
this.stopOverlay();
|
| 405 |
-
this.triggerNextLevelWithDelay();
|
| 406 |
-
}, this.COUNTDOWN_SECS * 1000);
|
| 407 |
-
}
|
| 408 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 409 |
} else {
|
| 410 |
-
this.
|
| 411 |
}
|
|
|
|
|
|
|
|
|
|
| 412 |
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
(
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
this.isLoading = false;
|
| 421 |
-
},
|
| 422 |
-
);
|
| 423 |
}
|
| 424 |
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 430 |
}
|
| 431 |
|
| 432 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 433 |
setTimeout(() => {
|
| 434 |
-
this.
|
| 435 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 436 |
}
|
| 437 |
|
| 438 |
areAllCorrectAnswersDisplayed(): boolean {
|
| 439 |
-
return this.
|
| 440 |
-
(answer, index) =>
|
| 441 |
-
(answer ?? "").trim().toLowerCase() ===
|
| 442 |
-
(this.questionsWithAnswers[index]?.correctAnswer ?? "")
|
| 443 |
-
.trim()
|
| 444 |
-
.toLowerCase(),
|
| 445 |
-
);
|
| 446 |
}
|
| 447 |
|
| 448 |
-
transitionDifficulty() {
|
| 449 |
const currentIndex = this.difficultyLevels.indexOf(this.currentDifficulty);
|
|
|
|
| 450 |
if (currentIndex < this.difficultyLevels.length - 1) {
|
| 451 |
this.currentDifficulty = this.difficultyLevels[currentIndex + 1];
|
| 452 |
-
console.log(`Switching to ${this.currentDifficulty} difficulty level.`);
|
| 453 |
this.generateQuestions();
|
| 454 |
} else {
|
| 455 |
setTimeout(() => {
|
| 456 |
this.questions = [];
|
| 457 |
this.isAllLevelsCompleted = true;
|
| 458 |
-
this.isChecked = false;
|
| 459 |
-
this.isValidationInProgress = false;
|
| 460 |
-
this.isLoading = false;
|
| 461 |
-
console.log("🎉 You have completed all difficulty levels.");
|
| 462 |
this.triggerConfetti();
|
| 463 |
-
},
|
| 464 |
}
|
| 465 |
}
|
| 466 |
|
| 467 |
-
|
| 468 |
-
|
| 469 |
-
}
|
| 470 |
-
|
| 471 |
-
resetAllLevels() {
|
| 472 |
-
this.isAllLevelsCompleted = false;
|
| 473 |
-
this.currentDifficulty = "basic";
|
| 474 |
-
this.questions = [];
|
| 475 |
-
this.userAnswers = [];
|
| 476 |
-
this.feedback = [];
|
| 477 |
-
this.isChecked = false;
|
| 478 |
-
this.isQuestionsGenerated = false;
|
| 479 |
-
this.isValidationInProgress = false;
|
| 480 |
-
this.isTopicLocked = false;
|
| 481 |
-
this.hints = [];
|
| 482 |
-
this.error = "";
|
| 483 |
-
this.isGenerateDisabled = true;
|
| 484 |
-
this.isResetDisabled = false;
|
| 485 |
-
console.log("All levels reset. Starting from basic level.");
|
| 486 |
-
}
|
| 487 |
-
|
| 488 |
-
onAnswerChange(index: number) {
|
| 489 |
-
if (!this.isValidationInProgress) {
|
| 490 |
-
this.isChecked = false;
|
| 491 |
-
this.validated = false;
|
| 492 |
-
}
|
| 493 |
-
const userAnswer = this.userAnswers[index]?.trim();
|
| 494 |
-
console.log("User Answers:", this.userAnswers);
|
| 495 |
-
console.log("Are All Answers Filled:", this.areAllAnswersFilled());
|
| 496 |
-
if (userAnswer) {
|
| 497 |
-
this.userAnswers[index] = userAnswer;
|
| 498 |
-
} else {
|
| 499 |
-
this.userAnswers[index] = "";
|
| 500 |
-
}
|
| 501 |
this.userAnswers = [...this.userAnswers];
|
| 502 |
}
|
| 503 |
|
| 504 |
-
goToHome() {
|
| 505 |
-
this.router.navigate([
|
| 506 |
}
|
| 507 |
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
triggerConfetti() {
|
| 511 |
-
if (this.confettiInterval) {
|
| 512 |
-
clearInterval(this.confettiInterval);
|
| 513 |
-
}
|
| 514 |
|
| 515 |
this.confettiInterval = setInterval(() => {
|
| 516 |
confetti({
|
| 517 |
startVelocity: 30,
|
| 518 |
spread: 360,
|
| 519 |
ticks: 60,
|
| 520 |
-
origin: {
|
| 521 |
-
x: Math.random(),
|
| 522 |
-
y: Math.random() - 0.2,
|
| 523 |
-
},
|
| 524 |
});
|
| 525 |
-
},
|
| 526 |
}
|
| 527 |
|
| 528 |
-
stopConfetti() {
|
| 529 |
this.isAllLevelsCompleted = false;
|
|
|
|
|
|
|
| 530 |
|
|
|
|
| 531 |
if (this.confettiInterval) {
|
| 532 |
clearInterval(this.confettiInterval);
|
| 533 |
this.confettiInterval = null;
|
| 534 |
}
|
| 535 |
-
|
| 536 |
-
|
| 537 |
-
if (canvas) {
|
| 538 |
-
canvas.remove();
|
| 539 |
-
}
|
| 540 |
-
|
| 541 |
-
const confettiDivs = document.querySelectorAll(".confetti, .ts-confetti");
|
| 542 |
-
confettiDivs.forEach((el) => el.remove());
|
| 543 |
}
|
| 544 |
|
| 545 |
-
stopConfettiAndReset() {
|
| 546 |
this.stopConfetti();
|
| 547 |
this.resetTopic();
|
| 548 |
}
|
|
@@ -550,27 +468,10 @@ export class GenerateQuestionsComponent {
|
|
| 550 |
selectTopic(suggestion: string): void {
|
| 551 |
this.topic = suggestion;
|
| 552 |
this.showSuggestions = false;
|
| 553 |
-
this.isGenerateDisabled = false;
|
| 554 |
-
this.isResetDisabled = true;
|
| 555 |
-
this.isDropdownDisabled = true;
|
| 556 |
}
|
| 557 |
|
| 558 |
hideSuggestions(): void {
|
| 559 |
-
setTimeout(() => {
|
| 560 |
-
this.showSuggestions = false;
|
| 561 |
-
}, 150);
|
| 562 |
-
}
|
| 563 |
-
|
| 564 |
-
processHints(hints: string[]) {
|
| 565 |
-
this.hints = hints;
|
| 566 |
-
|
| 567 |
-
this.hasNewHints = hints.length > 0;
|
| 568 |
-
|
| 569 |
-
if (this.hasNewHints) {
|
| 570 |
-
setTimeout(() => {
|
| 571 |
-
this.hasNewHints = false;
|
| 572 |
-
}, 5000);
|
| 573 |
-
}
|
| 574 |
}
|
| 575 |
|
| 576 |
closeHints(): void {
|
|
@@ -578,110 +479,23 @@ export class GenerateQuestionsComponent {
|
|
| 578 |
this.hasNewHints = false;
|
| 579 |
}
|
| 580 |
|
| 581 |
-
|
| 582 |
-
if (this.hints?.length) {
|
| 583 |
-
this.isHintMenuVisible = true;
|
| 584 |
-
this.hasNewHints = false;
|
| 585 |
-
}
|
| 586 |
-
}
|
| 587 |
-
|
| 588 |
-
toggleHintMenu() {
|
| 589 |
this.isHintMenuVisible = !this.isHintMenuVisible;
|
| 590 |
-
if (this.isHintMenuVisible) this.showHintIcon = false;
|
| 591 |
-
}
|
| 592 |
-
|
| 593 |
-
onInput(event: any, index: number) {
|
| 594 |
-
const value = event.target.value;
|
| 595 |
-
const filteredValue = value.replace(/[^a-zA-Z]/g, "");
|
| 596 |
-
this.userAnswers[index] = filteredValue;
|
| 597 |
-
event.target.value = filteredValue;
|
| 598 |
-
}
|
| 599 |
-
|
| 600 |
-
//timer implemetation
|
| 601 |
-
|
| 602 |
-
// --- Countdown state (per question) ---
|
| 603 |
-
countdowns: number[] = []; // current number (5..0) for each question
|
| 604 |
-
showCountdown: boolean[] = []; // whether to show the badge
|
| 605 |
-
private countdownTimers: { [idx: number]: any } = {}; // interval handles
|
| 606 |
-
private readonly COUNTDOWN_SECS = 10;
|
| 607 |
-
// === Global countdown overlay state ===
|
| 608 |
-
showGlobalCountdown = false; // controls the centered overlay
|
| 609 |
-
globalCountdown = 0; // 5..1
|
| 610 |
-
private globalCountdownActive = false;
|
| 611 |
-
private globalTimer: any = null;
|
| 612 |
-
|
| 613 |
-
overlayCaption = '';
|
| 614 |
-
|
| 615 |
-
private beginResetCountdown(index: number, seconds: number = this.COUNTDOWN_SECS): void {
|
| 616 |
-
// avoid duplicate timers for the same question
|
| 617 |
-
if (this.countdownTimers[index]) { return; }
|
| 618 |
-
|
| 619 |
-
this.countdowns[index] = seconds;
|
| 620 |
-
this.showCountdown[index] = true;
|
| 621 |
-
|
| 622 |
-
this.countdownTimers[index] = setInterval(() => {
|
| 623 |
-
this.countdowns[index] = this.countdowns[index] - 1;
|
| 624 |
-
|
| 625 |
-
if (this.countdowns[index] <= 0) {
|
| 626 |
-
this.finishCountdown(index);
|
| 627 |
-
}
|
| 628 |
-
}, 1000);
|
| 629 |
-
}
|
| 630 |
-
|
| 631 |
-
/** Stop timer, hide badge, clear the wrong input, and allow retry. */
|
| 632 |
-
/** Stop timer, hide badge, clear the wrong input, and allow retry. */
|
| 633 |
-
private finishCountdown(index: number): void {
|
| 634 |
-
const t = this.countdownTimers[index]; // FIX: use `this`, not `self`
|
| 635 |
-
if (t) {
|
| 636 |
-
clearInterval(t);
|
| 637 |
-
delete this.countdownTimers[index];
|
| 638 |
-
}
|
| 639 |
-
|
| 640 |
-
this.showCountdown[index] = false;
|
| 641 |
-
this.countdowns[index] = 0;
|
| 642 |
-
|
| 643 |
-
// Clear only the wrong answer and allow the learner to try again.
|
| 644 |
-
this.userAnswers[index] = '';
|
| 645 |
-
|
| 646 |
-
// Turn off the global "checked" flag so inputs are not stuck red.
|
| 647 |
-
this.isChecked = false;
|
| 648 |
-
}
|
| 649 |
-
|
| 650 |
-
private startGlobalCountdown(): void {
|
| 651 |
-
if (this.globalCountdownActive) return; // prevent duplicates
|
| 652 |
-
this.globalCountdownActive = true;
|
| 653 |
-
this.showGlobalCountdown = true;
|
| 654 |
-
this.globalCountdown = this.COUNTDOWN_SECS;
|
| 655 |
-
|
| 656 |
-
this.globalTimer = setInterval(() => {
|
| 657 |
-
this.globalCountdown--;
|
| 658 |
-
if (this.globalCountdown <= 0) {
|
| 659 |
-
this.stopGlobalCountdown();
|
| 660 |
-
}
|
| 661 |
-
}, 1000);
|
| 662 |
}
|
| 663 |
|
| 664 |
-
|
| 665 |
-
|
| 666 |
-
|
| 667 |
-
|
| 668 |
-
}
|
| 669 |
-
this.showGlobalCountdown = false;
|
| 670 |
-
this.globalCountdownActive = false;
|
| 671 |
-
this.globalCountdown = 0;
|
| 672 |
}
|
| 673 |
|
| 674 |
-
|
| 675 |
-
this.
|
| 676 |
-
}
|
| 677 |
-
private startOverlay(baseText: string, seconds: number = this.COUNTDOWN_SECS): void {
|
| 678 |
-
if (this.globalTimer) { clearTimeout(this.globalTimer as any); this.globalTimer = null; }
|
| 679 |
-
if (this.overlayTicker) { clearInterval(this.overlayTicker); this.overlayTicker = null; }
|
| 680 |
|
| 681 |
this.globalCountdownActive = true;
|
| 682 |
this.showGlobalCountdown = true;
|
| 683 |
-
this.overlayCaption =
|
| 684 |
-
|
| 685 |
this.globalCountdown = seconds;
|
| 686 |
this.overlayEndTs = Date.now() + seconds * 1000;
|
| 687 |
this.ringDashoffset = this.ringCircumference;
|
|
@@ -692,31 +506,11 @@ private finishCountdown(index: number): void {
|
|
| 692 |
const elapsedMs = seconds * 1000 - remainingMs;
|
| 693 |
|
| 694 |
this.globalCountdown = Math.ceil(remainingMs / 1000);
|
|
|
|
| 695 |
|
| 696 |
-
|
| 697 |
-
|
| 698 |
-
|
| 699 |
-
if (remainingMs <= 0) this.stopOverlay();
|
| 700 |
-
}, 50) as any;
|
| 701 |
-
|
| 702 |
-
this.globalTimer = setTimeout(() => this.stopOverlay(), seconds * 1000) as any;
|
| 703 |
-
}
|
| 704 |
-
|
| 705 |
-
private stopOverlay(): void {
|
| 706 |
-
if (this.overlayTicker) { clearInterval(this.overlayTicker); this.overlayTicker = null; }
|
| 707 |
-
if (this.globalTimer) { clearTimeout(this.globalTimer as any); this.globalTimer = null; }
|
| 708 |
|
| 709 |
-
this.
|
| 710 |
-
this.globalCountdownActive = false;
|
| 711 |
-
this.overlayCaption = '';
|
| 712 |
-
this.globalCountdown = 0;
|
| 713 |
-
this.ringDashoffset = this.ringCircumference;
|
| 714 |
}
|
| 715 |
-
|
| 716 |
-
ringRadius = 90;
|
| 717 |
-
ringCircumference = 2 * Math.PI * this.ringRadius;
|
| 718 |
-
ringDashoffset = this.ringCircumference;
|
| 719 |
-
|
| 720 |
-
private overlayTicker: any = null;
|
| 721 |
-
private overlayEndTs = 0;
|
| 722 |
}
|
|
|
|
| 1 |
+
import { Component, OnDestroy } from '@angular/core';
|
| 2 |
import { FormsModule } from '@angular/forms';
|
| 3 |
import { CommonModule } from '@angular/common';
|
| 4 |
import { GenerateQuestionsService } from './generate-questions.service';
|
| 5 |
import { Router } from '@angular/router';
|
| 6 |
import confetti from 'canvas-confetti';
|
|
|
|
| 7 |
import { HeaderComponent } from '../shared/header/header.component';
|
| 8 |
+
import { ButtonComponent } from '../shared/button/button.component';
|
|
|
|
| 9 |
|
| 10 |
interface QuestionWithAnswer {
|
| 11 |
question: string;
|
|
|
|
| 17 |
index: number;
|
| 18 |
}
|
| 19 |
|
| 20 |
+
const COUNTDOWN_SECONDS = 10;
|
| 21 |
+
const HINT_AUTO_HIDE_MS = 5000;
|
| 22 |
+
const NEXT_LEVEL_DELAY_MS = 2000;
|
| 23 |
+
const COMPLETION_DELAY_MS = 3000;
|
| 24 |
+
const CONFETTI_INTERVAL_MS = 250;
|
| 25 |
+
const RING_RADIUS = 90;
|
| 26 |
+
|
| 27 |
+
const INCORRECT_INDICATORS = [
|
| 28 |
+
'incorrect', 'not correct', 'wrong', 'not right',
|
| 29 |
+
'is not', 'are not', 'isn\'t correct', 'aren\'t correct'
|
| 30 |
+
];
|
| 31 |
+
|
| 32 |
+
const CORRECT_INDICATORS = [
|
| 33 |
+
'correct', 'right', 'yes', 'accurate',
|
| 34 |
+
'well done', 'good job', 'perfect'
|
| 35 |
+
];
|
| 36 |
+
|
| 37 |
@Component({
|
| 38 |
selector: 'app-generate-questions',
|
| 39 |
standalone: true,
|
| 40 |
+
imports: [FormsModule, CommonModule, HeaderComponent, ButtonComponent],
|
| 41 |
templateUrl: './generate-questions.component.html',
|
| 42 |
styleUrls: ['./generate-questions.component.css']
|
| 43 |
})
|
| 44 |
+
export class GenerateQuestionsComponent implements OnDestroy {
|
| 45 |
+
topic = '';
|
| 46 |
+
readonly hardcodedTopics = ['Noun', 'Verb', 'Past Tense', 'Adjective', 'Present Continuous'];
|
| 47 |
+
questions: Question[] = [];
|
| 48 |
+
questionsWithAnswers: QuestionWithAnswer[] = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
userAnswers: string[] = [];
|
| 51 |
+
answerStatuses: ('correct' | 'incorrect' | 'pending')[] = [];
|
| 52 |
feedback: string[] = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
hints: string[] = [];
|
| 54 |
+
readonlyAnswers: boolean[] = [];
|
| 55 |
+
attemptCounts: Record<number, number> = {};
|
| 56 |
+
|
| 57 |
+
currentDifficulty = 'basic';
|
| 58 |
+
readonly difficultyLevels = ['basic', 'intermediate', 'expert'];
|
| 59 |
+
|
| 60 |
+
showSuggestions = false;
|
| 61 |
+
isQuestionsGenerated = false;
|
| 62 |
+
isValidationInProgress = false;
|
| 63 |
+
isFirstAttemptDone = false;
|
| 64 |
+
isAllLevelsCompleted = false;
|
| 65 |
+
isHintMenuVisible = false;
|
| 66 |
+
isLoading = false;
|
| 67 |
+
isChecked = false;
|
| 68 |
+
error = '';
|
| 69 |
+
|
| 70 |
+
get isGenerateDisabled() { return !this.topic.trim() || this.isQuestionsGenerated; }
|
| 71 |
+
get isResetDisabled() { return !this.topic && !this.isQuestionsGenerated; }
|
| 72 |
+
get isTopicLocked() { return this.isQuestionsGenerated; }
|
| 73 |
+
get isDropdownDisabled() { return this.isQuestionsGenerated; }
|
| 74 |
+
get showLevelTooltip() { return this.isQuestionsGenerated; }
|
| 75 |
+
get activateLevelDot() { return this.isQuestionsGenerated; }
|
| 76 |
+
|
| 77 |
+
private hasShownHintIcon = false;
|
| 78 |
+
showHintIcon = false;
|
| 79 |
+
hasNewHints = false;
|
| 80 |
+
|
| 81 |
+
showGlobalCountdown = false;
|
| 82 |
+
globalCountdown = 0;
|
| 83 |
+
overlayCaption = '';
|
| 84 |
+
ringCircumference = 2 * Math.PI * RING_RADIUS;
|
| 85 |
+
ringDashoffset = this.ringCircumference;
|
| 86 |
|
| 87 |
+
private globalTimer: any;
|
| 88 |
+
private overlayTicker: any;
|
| 89 |
+
private overlayEndTs = 0;
|
| 90 |
+
private globalCountdownActive = false;
|
| 91 |
+
private confettiInterval: any;
|
| 92 |
|
| 93 |
constructor(
|
| 94 |
+
private service: GenerateQuestionsService,
|
| 95 |
+
private router: Router
|
| 96 |
+
) {}
|
| 97 |
+
|
| 98 |
+
ngOnDestroy(): void {
|
| 99 |
+
this.clearTimers();
|
| 100 |
+
this.stopConfettiAnimation();
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
private clearTimers(): void {
|
| 104 |
+
if (this.globalTimer) clearInterval(this.globalTimer);
|
| 105 |
+
if (this.overlayTicker) clearInterval(this.overlayTicker);
|
| 106 |
+
this.globalTimer = null;
|
| 107 |
+
this.overlayTicker = null;
|
| 108 |
+
this.showGlobalCountdown = false;
|
| 109 |
+
this.globalCountdownActive = false;
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
onTopicChange(): void {
|
| 113 |
+
this.showSuggestions = !this.topic.trim();
|
| 114 |
}
|
| 115 |
|
| 116 |
getProgressWidth(): string {
|
| 117 |
const index = this.difficultyLevels.indexOf(this.currentDifficulty);
|
| 118 |
+
if (index === -1 || this.difficultyLevels.length <= 1) return '0%';
|
| 119 |
+
return `${(index / (this.difficultyLevels.length - 1)) * 100}%`;
|
|
|
|
| 120 |
}
|
| 121 |
|
| 122 |
+
generateQuestions(): void {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
if (this.isAllLevelsCompleted) {
|
| 124 |
+
this.error = 'Please reset to start over.';
|
| 125 |
+
return;
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
this.resetQuestionState();
|
| 129 |
+
|
| 130 |
+
this.service.generateQuestions(this.topic, this.currentDifficulty)
|
| 131 |
+
.subscribe({
|
| 132 |
+
next: (response) => this.handleQuestionsResponse(response),
|
| 133 |
+
error: (error) => this.handleQuestionsError(error)
|
| 134 |
+
});
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
private handleQuestionsResponse(response: any): void {
|
| 138 |
+
const rawQuestions = (response?.text ?? response?.generations?.[0]?.text ?? '').trim();
|
| 139 |
+
|
| 140 |
+
if (!rawQuestions) {
|
| 141 |
+
this.error = 'No questions generated. Please try again.';
|
| 142 |
return;
|
| 143 |
}
|
| 144 |
|
| 145 |
+
this.isQuestionsGenerated = true;
|
| 146 |
+
this.parseAndInitializeQuestions(rawQuestions);
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
private handleQuestionsError(error: any): void {
|
| 150 |
+
this.error = error.status === 400 && error.error.message
|
| 151 |
+
? error.error.message
|
| 152 |
+
: 'Failed to fetch questions. Please try again later.';
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
private parseAndInitializeQuestions(rawQuestions: string): void {
|
| 156 |
+
this.questionsWithAnswers = this.parseQuestions(rawQuestions);
|
| 157 |
+
this.questions = this.splitQuestionsIntoParts(this.questionsWithAnswers);
|
| 158 |
+
this.initializeAnswerArrays(this.questions.length);
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
private parseQuestions(text: string): QuestionWithAnswer[] {
|
| 162 |
+
const regex = /(\d+\.\s*.+?_______)\s*(.*?)\s*\(([^)]+)\)\s*$/gm;
|
| 163 |
+
const questions: QuestionWithAnswer[] = [];
|
| 164 |
+
let match;
|
| 165 |
+
|
| 166 |
+
while ((match = regex.exec(text)) !== null) {
|
| 167 |
+
const questionText = match[1]?.trim();
|
| 168 |
+
const afterBlank = match[2]?.replace(/^[_-]+/, '').replace(/^\s+/, ' ').replace(/\s{2,}/g, ' ');
|
| 169 |
+
const correctAnswer = match[3]?.trim();
|
| 170 |
+
|
| 171 |
+
if (questionText && correctAnswer) {
|
| 172 |
+
questions.push({
|
| 173 |
+
question: `${questionText}${afterBlank}`,
|
| 174 |
+
correctAnswer
|
| 175 |
+
});
|
| 176 |
+
}
|
| 177 |
+
}
|
| 178 |
+
return questions;
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
private splitQuestionsIntoParts(questions: QuestionWithAnswer[]): Question[] {
|
| 182 |
+
return questions.map((q, index) => {
|
| 183 |
+
const normalized = q.question.replace(/\s*_{3,}\s*/g, '_______');
|
| 184 |
+
const parts = normalized.split('_______').map(p => p.replace(/[_]+/g, '').trim());
|
| 185 |
+
return { parts, index };
|
| 186 |
+
});
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
private initializeAnswerArrays(length: number): void {
|
| 190 |
+
this.userAnswers = Array(length).fill('');
|
| 191 |
+
this.answerStatuses = Array(length).fill('pending');
|
| 192 |
+
this.feedback = Array(length).fill('');
|
| 193 |
+
this.readonlyAnswers = Array(length).fill(false);
|
| 194 |
+
this.hints = [];
|
| 195 |
+
this.attemptCounts = {};
|
| 196 |
+
for (let i = 0; i < length; i++) {
|
| 197 |
+
this.attemptCounts[i] = 0;
|
| 198 |
+
}
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
private resetQuestionState(): void {
|
| 202 |
+
this.hints = [];
|
| 203 |
this.showSuggestions = false;
|
| 204 |
this.readonlyAnswers = [];
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
resetTopic(): void {
|
| 208 |
+
this.topic = '';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 209 |
this.questions = [];
|
| 210 |
this.questionsWithAnswers = [];
|
| 211 |
this.userAnswers = [];
|
| 212 |
+
this.answerStatuses = [];
|
| 213 |
this.readonlyAnswers = [];
|
| 214 |
this.feedback = [];
|
| 215 |
this.hints = [];
|
| 216 |
+
this.attemptCounts = {};
|
| 217 |
+
|
| 218 |
+
this.isQuestionsGenerated = false;
|
| 219 |
this.isValidationInProgress = false;
|
| 220 |
this.isFirstAttemptDone = false;
|
| 221 |
this.isAllLevelsCompleted = false;
|
| 222 |
this.hasNewHints = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
this.showSuggestions = false;
|
| 224 |
+
this.isChecked = false;
|
| 225 |
+
this.currentDifficulty = 'basic';
|
| 226 |
+
this.error = '';
|
| 227 |
+
|
| 228 |
+
this.clearTimers();
|
| 229 |
this.stopConfetti();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 230 |
}
|
| 231 |
|
| 232 |
areAllAnswersFilled(): boolean {
|
| 233 |
+
return this.userAnswers.every(answer => answer?.trim());
|
| 234 |
}
|
| 235 |
|
| 236 |
+
closeErrorPopup(): void {
|
| 237 |
+
this.error = '';
|
|
|
|
|
|
|
| 238 |
}
|
| 239 |
|
| 240 |
+
private parseAIValidation(response: string): boolean {
|
| 241 |
+
if (!response) return false;
|
| 242 |
+
|
| 243 |
+
const lower = response.toLowerCase();
|
| 244 |
+
|
| 245 |
+
if (INCORRECT_INDICATORS.some(ind => lower.includes(ind))) return false;
|
| 246 |
+
if (CORRECT_INDICATORS.some(ind => lower.includes(ind))) return true;
|
| 247 |
|
| 248 |
+
return false;
|
| 249 |
+
}
|
| 250 |
+
|
| 251 |
+
checkAllAnswers(): void {
|
| 252 |
this.isValidationInProgress = true;
|
| 253 |
this.isLoading = true;
|
|
|
|
|
|
|
| 254 |
this.isFirstAttemptDone = true;
|
| 255 |
|
| 256 |
+
const payload = this.questions.map((q, i) => ({
|
| 257 |
topic: this.topic,
|
| 258 |
+
question: q.parts.join('_______'),
|
| 259 |
+
user_answer: this.userAnswers[i]
|
| 260 |
}));
|
| 261 |
|
| 262 |
+
this.service.validateAllAnswers(payload).subscribe({
|
| 263 |
+
next: (response) => this.handleValidationResponse(response),
|
| 264 |
+
error: (error) => this.handleValidationError(error)
|
| 265 |
+
});
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
private handleValidationResponse(response: any): void {
|
| 269 |
+
if (!response.results?.length) {
|
| 270 |
+
this.error = 'Failed to validate answers. Please try again.';
|
| 271 |
+
this.resetValidationState();
|
| 272 |
+
return;
|
| 273 |
+
}
|
| 274 |
+
|
| 275 |
+
let hadFirstWrong = false;
|
| 276 |
+
let hadSecondWrong = false;
|
| 277 |
+
|
| 278 |
+
response.results.forEach((result: any, index: number) => {
|
| 279 |
+
this.attemptCounts[index]++;
|
| 280 |
+
const isCorrect = this.parseAIValidation(result.validation_response);
|
| 281 |
+
|
| 282 |
+
this.feedback[index] = result.validation_response || 'No feedback provided.';
|
| 283 |
+
|
| 284 |
+
if (isCorrect) {
|
| 285 |
+
this.markAsCorrect(index);
|
| 286 |
+
} else {
|
| 287 |
+
const attemptResult = this.handleIncorrectAttempt(index, result.hint);
|
| 288 |
+
hadFirstWrong = hadFirstWrong || attemptResult.first;
|
| 289 |
+
hadSecondWrong = hadSecondWrong || attemptResult.second;
|
| 290 |
+
}
|
| 291 |
+
});
|
| 292 |
+
|
| 293 |
+
this.handleHintsDisplay(hadFirstWrong || hadSecondWrong);
|
| 294 |
+
|
| 295 |
+
if (!hadSecondWrong) {
|
| 296 |
+
this.checkLevelCompletion();
|
| 297 |
+
}
|
| 298 |
+
|
| 299 |
+
this.resetValidationState();
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
private markAsCorrect(index: number): void {
|
| 303 |
+
this.readonlyAnswers[index] = true;
|
| 304 |
+
this.hints[index] = '';
|
| 305 |
+
this.answerStatuses[index] = 'correct';
|
| 306 |
+
this.userAnswers[index] = this.userAnswers[index].trim();
|
| 307 |
+
}
|
| 308 |
+
|
| 309 |
+
private handleIncorrectAttempt(index: number, hint: string): { first: boolean; second: boolean } {
|
| 310 |
+
this.answerStatuses[index] = 'incorrect';
|
| 311 |
+
this.hints[index] = hint?.trim() || '';
|
| 312 |
+
|
| 313 |
+
if (this.attemptCounts[index] === 1) {
|
| 314 |
+
this.scheduleAnswerClear(index);
|
| 315 |
+
return { first: true, second: false };
|
| 316 |
+
} else if (this.attemptCounts[index] >= 2) {
|
| 317 |
+
this.scheduleAnswerReveal(index);
|
| 318 |
+
return { first: false, second: true };
|
| 319 |
+
}
|
| 320 |
+
|
| 321 |
+
return { first: false, second: false };
|
| 322 |
+
}
|
| 323 |
+
|
| 324 |
+
private scheduleAnswerClear(index: number): void {
|
| 325 |
+
if (!this.globalCountdownActive) this.startOverlay('Try again in');
|
| 326 |
+
|
| 327 |
+
setTimeout(() => {
|
| 328 |
+
this.userAnswers[index] = '';
|
| 329 |
+
this.answerStatuses[index] = 'pending';
|
| 330 |
+
this.clearTimers();
|
| 331 |
+
}, COUNTDOWN_SECONDS * 1000);
|
| 332 |
+
}
|
| 333 |
+
|
| 334 |
+
private scheduleAnswerReveal(index: number): void {
|
| 335 |
+
if (!this.globalCountdownActive) this.startOverlay('Showing correct answer in');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
|
| 337 |
+
setTimeout(() => {
|
| 338 |
+
this.userAnswers[index] = this.questionsWithAnswers[index].correctAnswer;
|
| 339 |
+
this.answerStatuses[index] = 'correct';
|
| 340 |
+
this.readonlyAnswers[index] = true;
|
| 341 |
+
this.clearTimers();
|
| 342 |
+
|
| 343 |
+
if (this.areAllCorrectAnswersDisplayed()) {
|
| 344 |
+
if (this.isLastLevel()) {
|
| 345 |
+
this.scheduleCompletion();
|
| 346 |
} else {
|
| 347 |
+
this.scheduleNextLevel();
|
| 348 |
}
|
| 349 |
+
}
|
| 350 |
+
}, COUNTDOWN_SECONDS * 1000);
|
| 351 |
+
}
|
| 352 |
|
| 353 |
+
private scheduleCompletion(): void {
|
| 354 |
+
this.startOverlay('Finishing in');
|
| 355 |
+
setTimeout(() => {
|
| 356 |
+
this.clearTimers();
|
| 357 |
+
this.isAllLevelsCompleted = true;
|
| 358 |
+
this.triggerConfetti();
|
| 359 |
+
}, COUNTDOWN_SECONDS * 1000);
|
|
|
|
|
|
|
|
|
|
| 360 |
}
|
| 361 |
|
| 362 |
+
private handleHintsDisplay(hasWrongAnswers: boolean): void {
|
| 363 |
+
this.hasNewHints = this.hints.some(h => h?.trim());
|
| 364 |
+
|
| 365 |
+
if (this.hasNewHints) {
|
| 366 |
+
setTimeout(() => { this.hasNewHints = false; }, HINT_AUTO_HIDE_MS);
|
| 367 |
+
}
|
| 368 |
+
|
| 369 |
+
if (!this.hasShownHintIcon && (this.hasNewHints || hasWrongAnswers)) {
|
| 370 |
+
this.showHintIcon = true;
|
| 371 |
+
this.hasShownHintIcon = true;
|
| 372 |
+
}
|
| 373 |
}
|
| 374 |
|
| 375 |
+
private checkLevelCompletion(): void {
|
| 376 |
+
if (this.areAllCorrectAnswersDisplayed()) {
|
| 377 |
+
if (this.isLastLevel()) {
|
| 378 |
+
this.scheduleCompletion();
|
| 379 |
+
} else {
|
| 380 |
+
this.scheduleNextLevel();
|
| 381 |
+
}
|
| 382 |
+
}
|
| 383 |
+
}
|
| 384 |
+
|
| 385 |
+
private scheduleNextLevel(): void {
|
| 386 |
+
this.clearTimers();
|
| 387 |
+
this.startOverlay('Moving to next level in');
|
| 388 |
setTimeout(() => {
|
| 389 |
+
this.clearTimers();
|
| 390 |
+
setTimeout(() => this.transitionDifficulty(), NEXT_LEVEL_DELAY_MS);
|
| 391 |
+
}, COUNTDOWN_SECONDS * 1000);
|
| 392 |
+
}
|
| 393 |
+
|
| 394 |
+
private handleValidationError(error: any): void {
|
| 395 |
+
this.error = 'Error validating answers. Please try again later.';
|
| 396 |
+
this.resetValidationState();
|
| 397 |
+
}
|
| 398 |
+
|
| 399 |
+
private resetValidationState(): void {
|
| 400 |
+
this.isValidationInProgress = false;
|
| 401 |
+
this.isLoading = false;
|
| 402 |
+
}
|
| 403 |
+
|
| 404 |
+
isLastLevel(): boolean {
|
| 405 |
+
return this.currentDifficulty === this.difficultyLevels[this.difficultyLevels.length - 1];
|
| 406 |
}
|
| 407 |
|
| 408 |
areAllCorrectAnswersDisplayed(): boolean {
|
| 409 |
+
return this.readonlyAnswers.every(readonly => readonly);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 410 |
}
|
| 411 |
|
| 412 |
+
transitionDifficulty(): void {
|
| 413 |
const currentIndex = this.difficultyLevels.indexOf(this.currentDifficulty);
|
| 414 |
+
|
| 415 |
if (currentIndex < this.difficultyLevels.length - 1) {
|
| 416 |
this.currentDifficulty = this.difficultyLevels[currentIndex + 1];
|
|
|
|
| 417 |
this.generateQuestions();
|
| 418 |
} else {
|
| 419 |
setTimeout(() => {
|
| 420 |
this.questions = [];
|
| 421 |
this.isAllLevelsCompleted = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 422 |
this.triggerConfetti();
|
| 423 |
+
}, COMPLETION_DELAY_MS);
|
| 424 |
}
|
| 425 |
}
|
| 426 |
|
| 427 |
+
onAnswerChange(index: number): void {
|
| 428 |
+
this.userAnswers[index] = this.userAnswers[index]?.trim() || '';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 429 |
this.userAnswers = [...this.userAnswers];
|
| 430 |
}
|
| 431 |
|
| 432 |
+
goToHome(): void {
|
| 433 |
+
this.router.navigate(['/home']);
|
| 434 |
}
|
| 435 |
|
| 436 |
+
triggerConfetti(): void {
|
| 437 |
+
if (this.confettiInterval) clearInterval(this.confettiInterval);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 438 |
|
| 439 |
this.confettiInterval = setInterval(() => {
|
| 440 |
confetti({
|
| 441 |
startVelocity: 30,
|
| 442 |
spread: 360,
|
| 443 |
ticks: 60,
|
| 444 |
+
origin: { x: Math.random(), y: Math.random() - 0.2 }
|
|
|
|
|
|
|
|
|
|
| 445 |
});
|
| 446 |
+
}, CONFETTI_INTERVAL_MS);
|
| 447 |
}
|
| 448 |
|
| 449 |
+
stopConfetti(): void {
|
| 450 |
this.isAllLevelsCompleted = false;
|
| 451 |
+
this.stopConfettiAnimation();
|
| 452 |
+
}
|
| 453 |
|
| 454 |
+
private stopConfettiAnimation(): void {
|
| 455 |
if (this.confettiInterval) {
|
| 456 |
clearInterval(this.confettiInterval);
|
| 457 |
this.confettiInterval = null;
|
| 458 |
}
|
| 459 |
+
document.querySelector('canvas.confetti-canvas')?.remove();
|
| 460 |
+
document.querySelectorAll('.confetti, .ts-confetti').forEach(el => el.remove());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 461 |
}
|
| 462 |
|
| 463 |
+
stopConfettiAndReset(): void {
|
| 464 |
this.stopConfetti();
|
| 465 |
this.resetTopic();
|
| 466 |
}
|
|
|
|
| 468 |
selectTopic(suggestion: string): void {
|
| 469 |
this.topic = suggestion;
|
| 470 |
this.showSuggestions = false;
|
|
|
|
|
|
|
|
|
|
| 471 |
}
|
| 472 |
|
| 473 |
hideSuggestions(): void {
|
| 474 |
+
setTimeout(() => { this.showSuggestions = false; }, 150);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 475 |
}
|
| 476 |
|
| 477 |
closeHints(): void {
|
|
|
|
| 479 |
this.hasNewHints = false;
|
| 480 |
}
|
| 481 |
|
| 482 |
+
toggleHintMenu(): void {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 483 |
this.isHintMenuVisible = !this.isHintMenuVisible;
|
| 484 |
+
if (this.isHintMenuVisible) this.showHintIcon = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 485 |
}
|
| 486 |
|
| 487 |
+
onInput(event: any, index: number): void {
|
| 488 |
+
const filtered = event.target.value.replace(/[^a-zA-Z]/g, '');
|
| 489 |
+
this.userAnswers[index] = filtered;
|
| 490 |
+
event.target.value = filtered;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 491 |
}
|
| 492 |
|
| 493 |
+
private startOverlay(caption: string, seconds = COUNTDOWN_SECONDS): void {
|
| 494 |
+
this.clearTimers();
|
|
|
|
|
|
|
|
|
|
|
|
|
| 495 |
|
| 496 |
this.globalCountdownActive = true;
|
| 497 |
this.showGlobalCountdown = true;
|
| 498 |
+
this.overlayCaption = caption;
|
|
|
|
| 499 |
this.globalCountdown = seconds;
|
| 500 |
this.overlayEndTs = Date.now() + seconds * 1000;
|
| 501 |
this.ringDashoffset = this.ringCircumference;
|
|
|
|
| 506 |
const elapsedMs = seconds * 1000 - remainingMs;
|
| 507 |
|
| 508 |
this.globalCountdown = Math.ceil(remainingMs / 1000);
|
| 509 |
+
this.ringDashoffset = this.ringCircumference * (1 - Math.min(1, elapsedMs / (seconds * 1000)));
|
| 510 |
|
| 511 |
+
if (remainingMs <= 0) this.clearTimers();
|
| 512 |
+
}, 50);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 513 |
|
| 514 |
+
this.globalTimer = setTimeout(() => this.clearTimers(), seconds * 1000);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 515 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 516 |
}
|
src/app/generate-questions/generate-questions.service.ts
CHANGED
|
@@ -1,38 +1,34 @@
|
|
| 1 |
import { Injectable } from '@angular/core';
|
| 2 |
import { HttpClient } from '@angular/common/http';
|
| 3 |
import { Observable } from 'rxjs';
|
|
|
|
| 4 |
|
| 5 |
@Injectable({
|
| 6 |
providedIn: 'root'
|
| 7 |
})
|
| 8 |
export class GenerateQuestionsService {
|
|
|
|
| 9 |
|
| 10 |
-
|
| 11 |
-
private baseUrl = location.hostname.endsWith('hf.space')
|
| 12 |
-
? 'https://pykara-py-learn-backend.hf.space/media'
|
| 13 |
-
: 'http://localhost:5000/media';
|
| 14 |
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
generateQuestions(topic: string, difficulty: string): Observable<any> {
|
| 19 |
return this.http.post<any>(`${this.baseUrl}/generate-questions`, { topic, difficulty });
|
| 20 |
-
}
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
// Method to validate the user's answer for a specific question
|
| 25 |
-
validateAnswer(question: string, userAnswer: string, topic: string): Observable<any> {
|
| 26 |
-
return this.http.post<any>(`${this.baseUrl}/validate-answer`, {
|
| 27 |
-
question,
|
| 28 |
-
user_answer: userAnswer,
|
| 29 |
-
topic
|
| 30 |
-
});
|
| 31 |
}
|
| 32 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
validateAllAnswers(questions: { topic: string; question: string; user_answer: string }[]): Observable<any> {
|
| 34 |
return this.http.post<any>(`${this.baseUrl}/validate-all-answers`, { questions });
|
| 35 |
}
|
| 36 |
-
|
| 37 |
-
|
| 38 |
}
|
|
|
|
| 1 |
import { Injectable } from '@angular/core';
|
| 2 |
import { HttpClient } from '@angular/common/http';
|
| 3 |
import { Observable } from 'rxjs';
|
| 4 |
+
import { environment } from '../../environments/environment';
|
| 5 |
|
| 6 |
@Injectable({
|
| 7 |
providedIn: 'root'
|
| 8 |
})
|
| 9 |
export class GenerateQuestionsService {
|
| 10 |
+
private readonly baseUrl = environment.apiBaseUrl;
|
| 11 |
|
| 12 |
+
constructor(private http: HttpClient) {}
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
+
/**
|
| 15 |
+
* Generate grammar questions for a given topic and difficulty level.
|
| 16 |
+
*
|
| 17 |
+
* @param topic - The grammar topic
|
| 18 |
+
* @param difficulty - The difficulty level: 'basic', 'intermediate', or 'expert'
|
| 19 |
+
* @returns Observable with generated questions
|
| 20 |
+
*/
|
| 21 |
generateQuestions(topic: string, difficulty: string): Observable<any> {
|
| 22 |
return this.http.post<any>(`${this.baseUrl}/generate-questions`, { topic, difficulty });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
}
|
| 24 |
|
| 25 |
+
/**
|
| 26 |
+
* Validate multiple user answers in a single batch request.
|
| 27 |
+
*
|
| 28 |
+
* @param questions - Array of questions with user answers
|
| 29 |
+
* @returns Observable with validation results and hints
|
| 30 |
+
*/
|
| 31 |
validateAllAnswers(questions: { topic: string; question: string; user_answer: string }[]): Observable<any> {
|
| 32 |
return this.http.post<any>(`${this.baseUrl}/validate-all-answers`, { questions });
|
| 33 |
}
|
|
|
|
|
|
|
| 34 |
}
|
src/environments/environment.prod.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export const environment = {
|
| 2 |
+
production: true,
|
| 3 |
+
apiBaseUrl: 'https://pykara-py-learn-backend.hf.space/media'
|
| 4 |
+
};
|
src/environments/environment.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export const environment = {
|
| 2 |
+
production: false,
|
| 3 |
+
apiBaseUrl: 'http://localhost:5000/media'
|
| 4 |
+
};
|