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;