diff --git a/mobile/package-lock.json b/mobile/package-lock.json
index 6b0701c7..017e1cba 100644
--- a/mobile/package-lock.json
+++ b/mobile/package-lock.json
@@ -12,6 +12,7 @@
"@react-navigation/drawer": "^6.7.2",
"@react-navigation/native": "^6.1.18",
"@react-navigation/stack": "^6.4.1",
+ "jwt-decode": "^4.0.0",
"react": "18.3.1",
"react-native": "0.75.4",
"react-native-chart-kit": "^6.12.0",
@@ -30,6 +31,7 @@
},
"devDependencies": {
"@babel/core": "^7.20.0",
+ "@babel/plugin-proposal-decorators": "^7.25.9",
"@babel/preset-env": "^7.20.0",
"@babel/runtime": "^7.20.0",
"@react-native/babel-preset": "0.75.4",
@@ -62,11 +64,13 @@
}
},
"node_modules/@babel/code-frame": {
- "version": "7.25.7",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz",
- "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==",
+ "version": "7.26.2",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
+ "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/highlight": "^7.25.7",
+ "@babel/helper-validator-identifier": "^7.25.9",
+ "js-tokens": "^4.0.0",
"picocolors": "^1.0.0"
},
"engines": {
@@ -129,11 +133,13 @@
}
},
"node_modules/@babel/generator": {
- "version": "7.25.7",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz",
- "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==",
+ "version": "7.26.2",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz",
+ "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==",
+ "license": "MIT",
"dependencies": {
- "@babel/types": "^7.25.7",
+ "@babel/parser": "^7.26.2",
+ "@babel/types": "^7.26.0",
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25",
"jsesc": "^3.0.2"
@@ -143,11 +149,12 @@
}
},
"node_modules/@babel/helper-annotate-as-pure": {
- "version": "7.25.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.7.tgz",
- "integrity": "sha512-4xwU8StnqnlIhhioZf1tqnVWeQ9pvH/ujS8hRfw/WOza+/a+1qv69BWNy+oY231maTCWgKWhfBU7kDpsds6zAA==",
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz",
+ "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==",
+ "license": "MIT",
"dependencies": {
- "@babel/types": "^7.25.7"
+ "@babel/types": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
@@ -181,16 +188,17 @@
}
},
"node_modules/@babel/helper-create-class-features-plugin": {
- "version": "7.25.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.7.tgz",
- "integrity": "sha512-bD4WQhbkx80mAyj/WCm4ZHcF4rDxkoLFO6ph8/5/mQ3z4vAzltQXAmbc7GvVJx5H+lk5Mi5EmbTeox5nMGCsbw==",
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz",
+ "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-annotate-as-pure": "^7.25.7",
- "@babel/helper-member-expression-to-functions": "^7.25.7",
- "@babel/helper-optimise-call-expression": "^7.25.7",
- "@babel/helper-replace-supers": "^7.25.7",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7",
- "@babel/traverse": "^7.25.7",
+ "@babel/helper-annotate-as-pure": "^7.25.9",
+ "@babel/helper-member-expression-to-functions": "^7.25.9",
+ "@babel/helper-optimise-call-expression": "^7.25.9",
+ "@babel/helper-replace-supers": "^7.25.9",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9",
+ "@babel/traverse": "^7.25.9",
"semver": "^6.3.1"
},
"engines": {
@@ -232,12 +240,13 @@
}
},
"node_modules/@babel/helper-member-expression-to-functions": {
- "version": "7.25.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.7.tgz",
- "integrity": "sha512-O31Ssjd5K6lPbTX9AAYpSKrZmLeagt9uwschJd+Ixo6QiRyfpvgtVQp8qrDR9UNFjZ8+DO34ZkdrN+BnPXemeA==",
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz",
+ "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/traverse": "^7.25.7",
- "@babel/types": "^7.25.7"
+ "@babel/traverse": "^7.25.9",
+ "@babel/types": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
@@ -273,20 +282,22 @@
}
},
"node_modules/@babel/helper-optimise-call-expression": {
- "version": "7.25.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.7.tgz",
- "integrity": "sha512-VAwcwuYhv/AT+Vfr28c9y6SHzTan1ryqrydSTFGjU0uDJHw3uZ+PduI8plCLkRsDnqK2DMEDmwrOQRsK/Ykjng==",
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz",
+ "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/types": "^7.25.7"
+ "@babel/types": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-plugin-utils": {
- "version": "7.25.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz",
- "integrity": "sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==",
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz",
+ "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==",
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
@@ -308,13 +319,14 @@
}
},
"node_modules/@babel/helper-replace-supers": {
- "version": "7.25.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.7.tgz",
- "integrity": "sha512-iy8JhqlUW9PtZkd4pHM96v6BdJ66Ba9yWSE4z0W4TvSZwLBPkyDsiIU3ENe4SmrzRBs76F7rQXTy1lYC49n6Lw==",
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz",
+ "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-member-expression-to-functions": "^7.25.7",
- "@babel/helper-optimise-call-expression": "^7.25.7",
- "@babel/traverse": "^7.25.7"
+ "@babel/helper-member-expression-to-functions": "^7.25.9",
+ "@babel/helper-optimise-call-expression": "^7.25.9",
+ "@babel/traverse": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
@@ -336,29 +348,32 @@
}
},
"node_modules/@babel/helper-skip-transparent-expression-wrappers": {
- "version": "7.25.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.7.tgz",
- "integrity": "sha512-pPbNbchZBkPMD50K0p3JGcFMNLVUCuU/ABybm/PGNj4JiHrpmNyqqCphBk4i19xXtNV0JhldQJJtbSW5aUvbyA==",
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz",
+ "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==",
+ "license": "MIT",
"dependencies": {
- "@babel/traverse": "^7.25.7",
- "@babel/types": "^7.25.7"
+ "@babel/traverse": "^7.25.9",
+ "@babel/types": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-string-parser": {
- "version": "7.25.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz",
- "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==",
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
+ "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.25.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz",
- "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==",
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
+ "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
@@ -396,26 +411,13 @@
"node": ">=6.9.0"
}
},
- "node_modules/@babel/highlight": {
- "version": "7.25.7",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz",
- "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==",
- "dependencies": {
- "@babel/helper-validator-identifier": "^7.25.7",
- "chalk": "^2.4.2",
- "js-tokens": "^4.0.0",
- "picocolors": "^1.0.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/@babel/parser": {
- "version": "7.25.8",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.8.tgz",
- "integrity": "sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==",
+ "version": "7.26.2",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz",
+ "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/types": "^7.25.8"
+ "@babel/types": "^7.26.0"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -514,6 +516,24 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-proposal-decorators": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.25.9.tgz",
+ "integrity": "sha512-smkNLL/O1ezy9Nhy4CNosc4Va+1wo5w4gzSZeLe6y6dM4mmHfYOCPolXQPHQxonZCF+ZyebxN9vqOolkYrSn5g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "@babel/plugin-syntax-decorators": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-proposal-export-default-from": {
"version": "7.25.8",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.25.8.tgz",
@@ -623,6 +643,22 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-syntax-decorators": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.25.9.tgz",
+ "integrity": "sha512-ryzI0McXUPJnRCvMo4lumIKZUzhYUO/ScI+Mz4YVaTLt04DHNSjEUjKVvbzQjZFLuod/cYEc07mJWhzl6v4DPg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-syntax-dynamic-import": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
@@ -1880,28 +1916,30 @@
}
},
"node_modules/@babel/template": {
- "version": "7.25.7",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz",
- "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==",
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
+ "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
+ "license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.25.7",
- "@babel/parser": "^7.25.7",
- "@babel/types": "^7.25.7"
+ "@babel/code-frame": "^7.25.9",
+ "@babel/parser": "^7.25.9",
+ "@babel/types": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
- "version": "7.25.7",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz",
- "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==",
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz",
+ "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==",
+ "license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.25.7",
- "@babel/generator": "^7.25.7",
- "@babel/parser": "^7.25.7",
- "@babel/template": "^7.25.7",
- "@babel/types": "^7.25.7",
+ "@babel/code-frame": "^7.25.9",
+ "@babel/generator": "^7.25.9",
+ "@babel/parser": "^7.25.9",
+ "@babel/template": "^7.25.9",
+ "@babel/types": "^7.25.9",
"debug": "^4.3.1",
"globals": "^11.1.0"
},
@@ -1910,13 +1948,13 @@
}
},
"node_modules/@babel/types": {
- "version": "7.25.8",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz",
- "integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==",
+ "version": "7.26.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz",
+ "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-string-parser": "^7.25.7",
- "@babel/helper-validator-identifier": "^7.25.7",
- "to-fast-properties": "^2.0.0"
+ "@babel/helper-string-parser": "^7.25.9",
+ "@babel/helper-validator-identifier": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
@@ -5595,19 +5633,6 @@
"@webgpu/types": "0.1.21"
}
},
- "node_modules/chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dependencies": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/char-regex": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
@@ -6877,6 +6902,7 @@
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true,
"engines": {
"node": ">=0.8.0"
}
@@ -8247,14 +8273,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/has-property-descriptors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
@@ -10826,6 +10844,15 @@
"node": ">=4.0"
}
},
+ "node_modules/jwt-decode": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
+ "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -12673,6 +12700,7 @@
"version": "5.12.5",
"resolved": "https://registry.npmjs.org/react-native-paper/-/react-native-paper-5.12.5.tgz",
"integrity": "sha512-Qpqd1g9PClmjGj/Dkr1htAwt8cTZ3SCHVmhttxRuG/QML7KzHm5ArLNgR7vz5dW1EwJqTmyl/3gd6gnrtw90mw==",
+ "license": "MIT",
"dependencies": {
"@callstack/react-theme-provider": "^3.0.9",
"color": "^3.1.2",
@@ -12706,6 +12734,7 @@
"version": "3.16.2",
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.16.2.tgz",
"integrity": "sha512-Jk8y+iOLcK3J8YK3Qj/U+zclwfetgM1fFhlYaxFrJ5TPvuwdRG5YY1pvO91FcZ3C1+0meGHR6BZGl9d/Z0xh3Q==",
+ "license": "MIT",
"dependencies": {
"@babel/plugin-transform-arrow-functions": "^7.0.0-0",
"@babel/plugin-transform-class-properties": "^7.0.0-0",
@@ -13976,17 +14005,6 @@
"resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz",
"integrity": "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw=="
},
- "node_modules/supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dependencies": {
- "has-flag": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
@@ -14145,14 +14163,6 @@
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
"integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw=="
},
- "node_modules/to-fast-properties": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
- "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
diff --git a/mobile/package.json b/mobile/package.json
index 21110f62..d916fffa 100644
--- a/mobile/package.json
+++ b/mobile/package.json
@@ -14,6 +14,7 @@
"@react-navigation/drawer": "^6.7.2",
"@react-navigation/native": "^6.1.18",
"@react-navigation/stack": "^6.4.1",
+ "jwt-decode": "^4.0.0",
"react": "18.3.1",
"react-native": "0.75.4",
"react-native-chart-kit": "^6.12.0",
@@ -32,6 +33,7 @@
},
"devDependencies": {
"@babel/core": "^7.20.0",
+ "@babel/plugin-proposal-decorators": "^7.25.9",
"@babel/preset-env": "^7.20.0",
"@babel/runtime": "^7.20.0",
"@react-native/babel-preset": "0.75.4",
diff --git a/mobile/src/pages/App.js b/mobile/src/pages/App.js
index c0328ed8..1dcf309f 100644
--- a/mobile/src/pages/App.js
+++ b/mobile/src/pages/App.js
@@ -18,6 +18,8 @@ import Markets from './Markets';
import Community from './Community';
import Post from './Post';
import CreatePost from './CreatePost';
+import StockDetails from './StockDetails';
+
import { AuthProvider, useAuth } from './context/AuthContext';
@@ -71,6 +73,22 @@ const PostStack = () => {
)
}
+
+const MarketsStack = () => (
+
+ }}
+ />
+
+
+);
+
const DrawerNavigator = () => {
const { username, userId } = useAuth();
@@ -104,7 +122,8 @@ const DrawerNavigator = () => {
)}
{
setLatestPosts(data);
} catch (error) {
console.error('Error fetching latest posts:', error);
- }finally {
- setLoading(false);
}
};
diff --git a/mobile/src/pages/Markets.js b/mobile/src/pages/Markets.js
index 2d55cfb4..36a9deb1 100644
--- a/mobile/src/pages/Markets.js
+++ b/mobile/src/pages/Markets.js
@@ -1,149 +1,187 @@
-import React, { useState } from 'react';
-import { View, Text, FlatList, TouchableOpacity, StyleSheet, ScrollView } from 'react-native';
+import React, { useState, useEffect } from 'react';
+import {
+ View,
+ Text,
+ TextInput,
+ FlatList,
+ TouchableOpacity,
+ StyleSheet,
+ ActivityIndicator,
+ Alert,
+} from 'react-native';
+import Icon from 'react-native-vector-icons/MaterialIcons'; // Using MaterialIcons for the magnifying glass
-const mockStocks = {
- "BIST30": [// BIST 30
- { code: 'AKBNK', name: 'Akbank T.A.Ş.', price: 8.76, about: 'Akbank is one of the largest banks in Turkey, offering a wide range of banking services.' },
- { code: 'ALARK', name: 'Alarko Holding A.Ş.', price: 6.78, about: 'Alarko is a major Turkish conglomerate involved in construction, energy, and tourism.' },
- { code: 'ARCLK', name: 'Arçelik A.Ş.', price: 29.56, about: 'Arçelik is a global home appliances manufacturer, known for brands like Beko and Grundig.' },
- { code: 'ASELS', name: 'Aselsan Elektronik Sanayi ve Ticaret A.Ş.', price: 23.67, about: 'Aselsan is Turkey’s leading defense electronics company, producing advanced technology solutions.' },
- { code: 'BIMAS', name: 'BİM Birleşik Mağazalar A.Ş.', price: 14.89, about: 'BIM is a prominent Turkish retail chain, offering low-cost consumer products.' },
- { code: 'DOHOL', name: 'Doğan Holding A.Ş.', price: 3.12, about: 'Doğan Holding is active in various sectors, including media, energy, and finance.' },
- { code: 'EKGYO', name: 'Emlak Konut Gayrimenkul Yatırım Ortaklığı A.Ş.', price: 3.78, about: 'Emlak Konut is a leading real estate investment trust in Turkey, primarily focused on housing projects.' },
- { code: 'ENJSA', name: 'Enerjisa Enerji A.Ş.', price: 13.22, about: 'Enerjisa operates in Turkey’s energy market, providing electricity distribution and sales.' },
- { code: 'EREGL', name: 'Ereğli Demir ve Çelik Fabrikaları T.A.Ş.', price: 9.47, about: 'Ereğli is one of Turkey’s largest steel producers, supplying a wide range of industries.' },
- { code: 'FROTO', name: 'Ford Otosan A.Ş.', price: 25.45, about: 'Ford Otosan is a joint venture between Ford and Koç Holding, producing commercial vehicles in Turkey.' },
- { code: 'GARAN', name: 'Türkiye Garanti Bankası A.Ş.', price: 11.23, about: 'Garanti is one of Turkey’s leading private banks, known for its innovative banking services.' },
- { code: 'ISCTR', name: 'Türkiye İş Bankası A.Ş.', price: 6.35, about: 'İşbank is Turkey’s largest private bank, providing a broad range of financial services.' },
- { code: 'KCHOL', name: 'Koç Holding A.Ş.', price: 18.90, about: 'Koç Holding is the largest industrial conglomerate in Turkey, with interests in energy, automotive, and finance.' },
- { code: 'KOZAA', name: 'Koza Altın İşletmeleri A.Ş.', price: 10.22, about: 'Koza Altın is Turkey’s largest gold mining company, engaged in exploration and production of gold.' },
- { code: 'KRDMD', name: 'Kardemir Karabük Demir Çelik Sanayi ve Ticaret A.Ş.', price: 4.56, about: 'Kardemir is a major Turkish steel producer, primarily serving the construction and automotive industries.' },
- { code: 'ODAS', name: 'Odaş Elektrik Üretim Sanayi Ticaret A.Ş.', price: 1.82 },
- { code: 'PETKM', name: 'Petkim Petrokimya Holding A.Ş.', price: 6.78 },
- { code: 'PGSUS', name: 'Pegasus Hava Taşımacılığı A.Ş.', price: 20.50 },
- { code: 'SAHOL', name: 'Hacı Ömer Sabancı Holding A.Ş.', price: 12.67 },
- { code: 'SISE', name: 'Şişecam A.Ş.', price: 10.33 },
- { code: 'SODA', name: 'Soda Sanayii A.Ş.', price: 7.44 },
- { code: 'TAVHL', name: 'TAV Havalimanları Holding A.Ş.', price: 19.05 },
- { code: 'TCELL', name: 'Turkcell İletişim Hizmetleri A.Ş.', price: 14.32 },
- { code: 'THYAO', name: 'Türk Hava Yolları A.O.', price: 15.56 },
- { code: 'TOASO', name: 'Tofaş Türk Otomobil Fabrikası A.Ş.', price: 20.87 },
- { code: 'TTKOM', name: 'Türk Telekomünikasyon A.Ş.', price: 7.68 },
- { code: 'TUPRS', name: 'Tüpraş-Türkiye Petrol Rafinerileri A.Ş.', price: 31.23 },
- { code: 'VAKBN', name: 'Türkiye Vakıflar Bankası T.A.O.', price: 7.12 },
- { code: 'YKBNK', name: 'Yapı ve Kredi Bankası A.Ş.', price: 8.23 }],
- "S&P50": [ // S&P top 50
- { code: 'AAPL', name: 'Apple Inc.', price: 175.00, about: 'Apple is a global technology company known for its consumer electronics, including the iPhone, Mac, and Apple Watch.' },
- { code: 'MSFT', name: 'Microsoft Corporation', price: 350.00, about: 'Microsoft is a multinational technology company, best known for its Windows operating system and Office suite.' },
- { code: 'AMZN', name: 'Amazon.com, Inc.', price: 145.00, about: 'Amazon is the world’s largest online retailer, also heavily involved in cloud computing and artificial intelligence.' },
- { code: 'GOOGL', name: 'Alphabet Inc. (Class A)', price: 120.00, about: 'Alphabet is the parent company of Google, specializing in internet-related services and products.' },
- { code: 'FB', name: 'Meta Platforms, Inc.', price: 300.00, about: 'Meta (formerly Facebook) operates the world’s largest social media platforms, including Facebook and Instagram.' },
- { code: 'TSLA', name: 'Tesla, Inc.', price: 720.00, about: 'Tesla is a leading electric vehicle manufacturer, also involved in renewable energy and battery technology.' },
- { code: 'BRK.B', name: 'Berkshire Hathaway Inc. (Class B)', price: 325.00, about: 'Berkshire Hathaway is a multinational conglomerate headed by Warren Buffett, with diverse holdings in various industries.' },
- { code: 'NVDA', name: 'NVIDIA Corporation', price: 480.00, about: 'NVIDIA is a global leader in graphics processing units (GPUs) and AI computing technology.' },
- { code: 'JPM', name: 'JPMorgan Chase & Co.', price: 140.00, about: 'JPMorgan Chase is one of the largest global financial services companies, providing investment banking and financial services.' },
- { code: 'JNJ', name: 'Johnson & Johnson', price: 160.00, about: 'Johnson & Johnson is a multinational healthcare company, known for its pharmaceutical, medical device, and consumer health products.' },
- { code: 'V', name: 'Visa Inc.', price: 250.00, about: 'Visa is a global payments technology company, facilitating electronic funds transfers worldwide.' },
- { code: 'PG', name: 'Procter & Gamble Co.', price: 145.00, about: 'Procter & Gamble is a multinational consumer goods company, known for brands like Tide, Pampers, and Gillette.' },
- { code: 'UNH', name: 'UnitedHealth Group Incorporated', price: 490.00, about: 'UnitedHealth Group is a healthcare company, offering insurance services and healthcare products.' },
- { code: 'HD', name: 'The Home Depot, Inc.', price: 330.00, about: 'Home Depot is the largest home improvement retailer in the US, selling tools, construction products, and services.' },
- { code: 'DIS', name: 'The Walt Disney Company', price: 120.00, about: 'Disney is a global entertainment conglomerate, known for its film studios, theme parks, and media networks.' },
- { code: 'PYPL', name: 'PayPal Holdings, Inc.', price: 80.00 },
- { code: 'MA', name: 'Mastercard Incorporated', price: 380.00 },
- { code: 'CMCSA', name: 'Comcast Corporation', price: 40.00 },
- { code: 'VZ', name: 'Verizon Communications Inc.', price: 35.00 },
- { code: 'NFLX', name: 'Netflix, Inc.', price: 490.00 },
- { code: 'PEP', name: 'PepsiCo, Inc.', price: 190.00 },
- { code: 'T', name: 'AT&T Inc.', price: 15.00 },
- { code: 'CSCO', name: 'Cisco Systems, Inc.', price: 55.00 },
- { code: 'INTC', name: 'Intel Corporation', price: 30.00 },
- { code: 'IBM', name: 'International Business Machines Corporation', price: 135.00 },
- { code: 'TXN', name: 'Texas Instruments Incorporated', price: 185.00 },
- { code: 'LLY', name: 'Eli Lilly and Company', price: 560.00 },
- { code: 'MDT', name: 'Medtronic plc', price: 90.00 },
- { code: 'COST', name: 'Costco Wholesale Corporation', price: 500.00 },
- { code: 'NOW', name: 'ServiceNow, Inc.', price: 550.00 },
- { code: 'QCOM', name: 'QUALCOMM Incorporated', price: 120.00 },
- { code: 'NKE', name: 'Nike, Inc.', price: 150.00 },
- { code: 'MRK', name: 'Merck & Co., Inc.', price: 110.00 },
- { code: 'AMGN', name: 'Amgen Inc.', price: 250.00 },
- { code: 'ISRG', name: 'Intuitive Surgical, Inc.', price: 300.00 },
- { code: 'LMT', name: 'Lockheed Martin Corporation', price: 420.00 },
- { code: 'SPGI', name: 'S&P Global Inc.', price: 400.00 },
- { code: 'MDLZ', name: 'Mondelez International, Inc.', price: 62.00 },
- { code: 'HON', name: 'Honeywell International Inc.', price: 220.00 },
- { code: 'TMO', name: 'Thermo Fisher Scientific Inc.', price: 550.00 },
- { code: 'ADBE', name: 'Adobe Inc.', price: 550.00 },
- { code: 'CAT', name: 'Caterpillar Inc.', price: 260.00 },
- { code: 'SYK', name: 'Stryker Corporation', price: 280.00 },
- { code: 'SYY', name: 'Sysco Corporation', price: 80.00 },
- { code: 'FIS', name: 'Fidelity National Information Services, Inc.', price: 70.00 },
- { code: 'C', name: 'Citigroup Inc.', price: 55.00 },
- { code: 'AXP', name: 'American Express Company', price: 180.00 },
- { code: 'MCO', name: 'Moody\'s Corporation', price: 360.00 },
- { code: 'BKNG', name: 'Booking Holdings Inc.', price: 2200.00 },
- { code: 'SCHW', name: 'The Charles Schwab Corporation', price: 90.00 },
- { code: 'DHR', name: 'Danaher Corporation', price: 310.00 },
- { code: 'ZTS', name: 'Zoetis Inc.', price: 200.00 },
- { code: 'LRCX', name: 'Lam Research Corporation', price: 600.00 },
- { code: 'FISV', name: 'FISV', price: 120.00 },
- { code: 'ADP', name: 'Automatic Data Processing, Inc.', price: 240.00 }],
- };
+const Markets = ({ navigation }) => {
+ const [stocks, setStocks] = useState([]); // Store all loaded stocks
+ const [loading, setLoading] = useState(true);
+ const [loadingMore, setLoadingMore] = useState(false); // Loading state for pagination
+ const [searchLoading, setSearchLoading] = useState(false); // Loading state for search
+ const [searchQuery, setSearchQuery] = useState(''); // Current search input
+ const [searchResults, setSearchResults] = useState([]); // Search results
+ const [page, setPage] = useState(1); // Current page number
+ const [hasMore, setHasMore] = useState(true); // To check if there are more pages to load
-const Markets = () => {
- const [selectedStock, setSelectedStock] = useState(null);
- const [activeCategory, setActiveCategory] = useState('BIST30');
+ const fetchStocks = async (pageNumber = 1) => {
+ try {
+ const response = await fetch(
+ `http://159.223.28.163:30002/stocks/?page=${pageNumber}`,
+ {
+ method: 'GET',
+ headers: {
+ accept: 'application/json',
+ Authorization: 'Basic ZnVya2Fuc2Vua2FsOkxvc29sdmlkYWRvcy41NQ==',
+ 'X-CSRFToken': 'HN4gYGlxSnwtGKK91OG9c6WC6gr8091Pm5Kof3t0WoTHOe0Z2ToubTZUdlOkjR34',
+ },
+ }
+ );
+
+ if (!response.ok) {
+ throw new Error(`HTTP Error: ${response.status}`);
+ }
+
+ const data = await response.json();
+
+ // Filter out stocks with a price of -1
+ const filteredData = data.filter((stock) => stock.price !== -1);
+
+ console.log(`Fetched stocks for page ${pageNumber}:`, filteredData);
+
+ if (!Array.isArray(filteredData) || filteredData.length === 0) {
+ setHasMore(false); // No more data to load
+ return;
+ }
+
+ setStocks((prevStocks) => [
+ ...prevStocks,
+ ...filteredData.filter((stock) => !prevStocks.find((s) => s.id === stock.id)), // Ensure unique stocks
+ ]);
+ } catch (error) {
+ console.error('Error fetching stocks:', error);
+ Alert.alert('Error', 'Unable to fetch stocks. Please try again later.');
+ } finally {
+ setLoading(false);
+ setLoadingMore(false);
+ }
+ };
+
+ const searchStocks = async (query) => {
+ if (!query) {
+ setSearchResults([]); // Clear search results if query is empty
+ return;
+ }
+
+ setSearchLoading(true);
+
+ try {
+ const response = await fetch('http://159.223.28.163:30002/stocks/search/', {
+ method: 'POST',
+ headers: {
+ accept: 'application/json',
+ Authorization: 'Basic ZnVya2Fuc2Vua2FsOkxvc29sdmlkYWRvcy41NQ==',
+ 'Content-Type': 'application/json',
+ 'X-CSRFToken': 'xGhS17H7qedbZRMF0ULpzKQhKe6mG11WcYX0iuPAufAp7l2v1ZtKyxTzRjtyZJ3b',
+ },
+ body: JSON.stringify({
+ pattern: query,
+ limit: 10,
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error(`HTTP Error: ${response.status}`);
+ }
+
+ const data = await response.json();
+
+ // Filter out stocks with a price of -1
+ const filteredData = data.filter((stock) => stock.price !== -1);
+
+ console.log('Search results:', filteredData);
+
+ setSearchResults(filteredData);
+ } catch (error) {
+ console.error('Error searching stocks:', error);
+ Alert.alert('Error', 'Unable to search stocks. Please try again later.');
+ } finally {
+ setSearchLoading(false);
+ }
+ };
+
+ useEffect(() => {
+ fetchStocks(); // Load the first page on component mount
+ }, []);
+
+ const loadMoreStocks = () => {
+ if (loadingMore || !hasMore) return;
+
+ setLoadingMore(true);
+ setPage((prevPage) => {
+ const nextPage = prevPage + 1;
+ fetchStocks(nextPage);
+ return nextPage;
+ });
+ };
const renderStockItem = ({ item }) => (
setSelectedStock(item)}
+ style={styles.stockCard}
+ onPress={() => navigation.navigate('StockDetails', { id: item.id })}
>
- {item.code}
- {item.name}
- ${item.price.toFixed(2)}
+
+ {item.symbol || 'N/A'}
+ {item.name || 'No Name'}
+
+
+ {parseFloat(item.price || 0).toFixed(2)} {item.currency?.code || ''}
+
);
- return (
-
- {/* Header with Categories */}
-
- {Object.keys(mockStocks).map((category) => (
- {
- setActiveCategory(category);
- setSelectedStock(null);
- }}
- >
- {category}
-
- ))}
+ if (loading) {
+ return (
+
+
+ );
+ }
- {/* Stock List */}
-
- item.code}
+ return (
+
+ {/* Search Bar */}
+
+
+ {
+ setSearchQuery(text);
+ searchStocks(text); // Trigger search on input change
+ }}
/>
- {/* Stock Details */}
- {selectedStock && (
-
- {selectedStock.code}
- {selectedStock.name}
- Price: ${selectedStock.price.toFixed(2)}
- {selectedStock.about}
-
+ {/* Loading Indicator for Search */}
+ {searchLoading && (
+
)}
+
+ {/* Stock List */}
+ item.id.toString()} // Ensure unique keys
+ onEndReached={searchQuery ? null : loadMoreStocks} // Disable pagination during search
+ onEndReachedThreshold={0.5} // Adjust how close to the bottom the user needs to be to trigger
+ ListFooterComponent={
+ loadingMore && !searchQuery && (
+
+
+
+ )
+ }
+ />
);
};
@@ -151,75 +189,70 @@ const Markets = () => {
const styles = StyleSheet.create({
container: {
flex: 1,
- backgroundColor: '#F5F5F5',
+ backgroundColor: '#F4F4F4',
+ padding: 10,
},
- header: {
+ searchContainer: {
flexDirection: 'row',
- justifyContent: 'space-around',
- paddingVertical: 10,
+ alignItems: 'center',
backgroundColor: '#FFFFFF',
- borderBottomWidth: 1,
- borderBottomColor: '#DDDDDD',
+ borderRadius: 8,
+ paddingHorizontal: 10,
+ marginBottom: 10,
+ shadowColor: '#000',
+ shadowOffset: { width: 0, height: 2 },
+ shadowOpacity: 0.1,
+ shadowRadius: 4,
+ elevation: 2,
},
- categoryButton: {
- padding: 10,
- borderRadius: 20,
- backgroundColor: '#E0E0E0',
+ searchIcon: {
+ marginRight: 10,
},
- activeCategory: {
- backgroundColor: '#007AFF',
+ searchInput: {
+ fontSize: 16,
+ color: '#333',
+ flex: 1,
},
- categoryText: {
- color: '#FFFFFF',
- fontWeight: 'bold',
+ searchLoader: {
+ marginBottom: 10,
},
- listContainer: {
+ loaderContainer: {
flex: 1,
- backgroundColor: '#FFFFFF',
+ justifyContent: 'center',
+ alignItems: 'center',
},
- stockItem: {
+ stockCard: {
+ backgroundColor: '#FFFFFF',
+ borderRadius: 10,
+ padding: 15,
+ marginVertical: 8,
flexDirection: 'row',
justifyContent: 'space-between',
- padding: 15,
- borderBottomWidth: 1,
- borderBottomColor: '#EEEEEE',
+ alignItems: 'center',
+ shadowColor: '#000',
+ shadowOffset: { width: 0, height: 2 },
+ shadowOpacity: 0.1,
+ shadowRadius: 4,
+ elevation: 3,
},
stockCode: {
- fontWeight: 'bold',
- color: '#333333',
- },
- stockName: {
- color: '#555555',
- },
- stockPrice: {
- color: '#007AFF',
- fontWeight: 'bold',
- },
- detailsContainer: {
- padding: 20,
- backgroundColor: '#FFFFFF',
- borderTopWidth: 1,
- borderTopColor: '#DDDDDD',
- },
- detailsTitle: {
fontSize: 18,
fontWeight: 'bold',
- marginBottom: 10,
color: '#007AFF',
},
- detailsName: {
- fontSize: 16,
+ stockName: {
+ fontSize: 14,
color: '#333333',
+ marginTop: 4,
},
- detailsPrice: {
- fontSize: 14,
- marginTop: 5,
- color: '#555555',
+ stockPrice: {
+ fontSize: 16,
+ fontWeight: '600',
+ color: '#4CAF50',
},
- detailsAbout: {
- fontSize: 14,
- marginTop: 10,
- color: '#666666',
+ footer: {
+ padding: 10,
+ alignItems: 'center',
},
});
diff --git a/mobile/src/pages/StockDetails.js b/mobile/src/pages/StockDetails.js
new file mode 100644
index 00000000..1b3c2bb1
--- /dev/null
+++ b/mobile/src/pages/StockDetails.js
@@ -0,0 +1,238 @@
+import React, { useState, useEffect } from 'react';
+import { View, Text, StyleSheet, ActivityIndicator, ScrollView, Alert } from 'react-native';
+
+const StockDetails = ({ route }) => {
+ const { id } = route.params; // Get the stock ID from navigation params
+ const [stockDetails, setStockDetails] = useState(null);
+ const [loading, setLoading] = useState(true);
+
+ const fetchStockDetails = async () => {
+ const url = `http://159.223.28.163:30002/stocks/${id}/`;
+
+ try {
+ const response = await fetch(url, {
+ method: 'GET',
+ headers: {
+ accept: 'application/json',
+ Authorization: 'Basic ZnVya2Fuc2Vua2FsOkxvc29sdmlkYWRvcy41NQ==',
+ 'X-CSRFToken': 'HN4gYGlxSnwtGKK91OG9c6WC6gr8091Pm5Kof3t0WoTHOe0Z2ToubTZUdlOkjR34',
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! Status: ${response.status}`);
+ }
+
+ const data = await response.json();
+ console.log('Fetched Stock Details:', data); // Debugging log
+ setStockDetails(data);
+ } catch (error) {
+ console.error('Error fetching stock details:', error);
+ Alert.alert('Error', 'Unable to fetch stock details. Please try again later.');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ useEffect(() => {
+ fetchStockDetails();
+ }, [id]);
+
+ if (loading) {
+ return (
+
+
+
+ );
+ }
+
+ if (!stockDetails) {
+ return (
+
+ Unable to load stock details.
+
+ );
+ }
+
+ const {
+ name,
+ symbol,
+ currency,
+ detail,
+ } = stockDetails;
+
+ const {
+ currentPrice,
+ marketCap,
+ fiftyTwoWeekHigh,
+ fiftyTwoWeekLow,
+ volume,
+ averageVolume,
+ open,
+ dayLow,
+ dayHigh,
+ sector,
+ industry,
+ longBusinessSummary,
+ } = detail || {};
+
+ const currencyCode = currency?.code || 'N/A';
+
+ return (
+
+ {/* Stock Overview */}
+
+ {name || 'N/A'}
+ {symbol || 'N/A'}
+
+
+
+ {currencyCode} {currentPrice || 'N/A'}
+
+ Current Price
+
+
+ {/* Stock Highlights */}
+
+ Stock Highlights
+
+ Market Cap: {currencyCode} {marketCap || 'N/A'}
+
+
+ 52-Week High: {currencyCode} {fiftyTwoWeekHigh || 'N/A'}
+
+
+ 52-Week Low: {currencyCode} {fiftyTwoWeekLow || 'N/A'}
+
+
+ Volume: {volume || 'N/A'}
+
+
+ Average Volume: {averageVolume || 'N/A'}
+
+
+
+ {/* Financial Highlights */}
+
+ Financial Highlights
+
+ Open: {currencyCode} {open || 'N/A'}
+
+
+ Day Low: {currencyCode} {dayLow || 'N/A'}
+
+
+ Day High: {currencyCode} {dayHigh || 'N/A'}
+
+
+ Sector: {sector || 'N/A'}
+
+
+ Industry: {industry || 'N/A'}
+
+
+
+ {/* Business Summary */}
+ {longBusinessSummary && (
+
+ Business Summary
+ {longBusinessSummary}
+
+ )}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ loaderContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ errorContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ errorText: {
+ fontSize: 18,
+ color: 'red',
+ textAlign: 'center',
+ },
+ container: {
+ padding: 16,
+ backgroundColor: '#f4f4f4',
+ },
+ scrollContent: {
+ paddingBottom: 40, // Add padding to ensure space for the last element
+ },
+ header: {
+ marginBottom: 20,
+ borderBottomWidth: 2,
+ borderBottomColor: '#007AFF',
+ paddingBottom: 10,
+ },
+ title: {
+ fontSize: 28,
+ fontWeight: 'bold',
+ color: '#000',
+ marginBottom: 5,
+ },
+ symbol: {
+ fontSize: 20,
+ color: '#555',
+ marginTop: 5,
+ },
+ priceContainer: {
+ alignItems: 'center',
+ marginBottom: 20,
+ },
+ currentPrice: {
+ fontSize: 32,
+ fontWeight: 'bold',
+ color: '#007AFF',
+ },
+ subDetail: {
+ fontSize: 16,
+ color: '#777',
+ marginTop: 5,
+ },
+ section: {
+ marginTop: 20,
+ paddingVertical: 15,
+ borderRadius: 10,
+ backgroundColor: '#fff',
+ paddingHorizontal: 20,
+ shadowColor: '#000',
+ shadowOffset: { width: 0, height: 2 },
+ shadowOpacity: 0.1,
+ shadowRadius: 4,
+ elevation: 3,
+ },
+ lastSection: {
+ marginBottom: 40, // Add extra margin for the last section
+ },
+ sectionTitle: {
+ fontSize: 22,
+ fontWeight: 'bold',
+ color: '#007AFF',
+ marginBottom: 10,
+ },
+ detail: {
+ fontSize: 16,
+ color: '#333',
+ marginBottom: 8,
+ },
+ detailLabel: {
+ fontWeight: 'bold',
+ color: '#000',
+ },
+ summary: {
+ fontSize: 16,
+ color: '#555',
+ lineHeight: 24,
+ marginTop: 10,
+ },
+});
+
+export default StockDetails;