diff --git a/.gitignore b/.gitignore index 80704f4378..def2e78c2b 100755 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,6 @@ database.database database.db diagram.png __pycache__/ +migrations +.env + diff --git a/.vscode/settings.json b/.vscode/settings.json index 24da33c3e2..5ae62cd079 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,5 +3,14 @@ "editor.defaultFormatter": "esbenp.prettier-vscode", "workbench.editorAssociations": { "*.md": "vscode.markdown.preview.editor" + }, + "[javascript]": { + "editor.defaultFormatter": "vscode.typescript-language-features" + }, + "[html]": { + "editor.defaultFormatter": "vscode.html-language-features" + }, + "[css]": { + "editor.defaultFormatter": "vscode.css-language-features" } } diff --git a/Pipfile b/Pipfile index b461e2e4ee..00fc91549f 100644 --- a/Pipfile +++ b/Pipfile @@ -20,6 +20,7 @@ flask-admin = "*" typing-extensions = "*" flask-jwt-extended = "==4.6.0" wtforms = "==3.1.2" +stripe = "*" [requires] python_version = "3.10" diff --git a/Pipfile.lock b/Pipfile.lock index a391864e9d..4f681f7cc7 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "74f92d76f687bb774828613a3a513123fe2ffdb429b95b351d29721dddfd3fb8" + "sha256": "a885ef6315310c11f246ceb2430f51076b27c8dcc7042962167ca94b0a9bb03f" }, "pipfile-spec": 6, "requires": { @@ -18,73 +18,189 @@ "default": { "alembic": { "hashes": [ - "sha256:6880dec4f28dd7bd999d2ed13fbe7c9d4337700a44d11a524c0ce0c59aaf0dbd", - "sha256:e8a6ff9f3b1887e1fed68bfb8fb9a000d8f61c21bdcc85b67bb9f87fcbc4fce3" + "sha256:197de710da4b3e91cf66a826a5b31b5d59a127ab41bd0fc42863e2902ce2bbbe", + "sha256:e1a1c738577bca1f27e68728c910cd389b9a92152ff91d902da649c192e30c49" ], - "markers": "python_version >= '3.7'", - "version": "==1.9.2" + "markers": "python_version >= '3.9'", + "version": "==1.15.1" + }, + "blinker": { + "hashes": [ + "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", + "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc" + ], + "markers": "python_version >= '3.9'", + "version": "==1.9.0" }, "certifi": { "hashes": [ - "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3", - "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18" + "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", + "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe" ], "markers": "python_version >= '3.6'", - "version": "==2022.12.7" + "version": "==2025.1.31" + }, + "charset-normalizer": { + "hashes": [ + "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537", + "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa", + "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a", + "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294", + "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b", + "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", + "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", + "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", + "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4", + "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", + "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2", + "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", + "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", + "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", + "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", + "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", + "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", + "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496", + "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", + "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", + "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e", + "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a", + "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4", + "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca", + "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78", + "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", + "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5", + "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", + "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", + "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", + "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765", + "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6", + "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", + "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", + "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", + "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd", + "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c", + "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", + "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", + "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", + "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770", + "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824", + "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f", + "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf", + "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487", + "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d", + "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd", + "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", + "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534", + "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", + "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", + "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", + "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd", + "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", + "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9", + "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", + "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", + "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d", + "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", + "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", + "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", + "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7", + "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", + "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", + "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8", + "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41", + "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", + "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", + "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", + "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", + "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", + "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", + "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", + "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", + "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", + "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", + "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", + "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e", + "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6", + "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", + "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", + "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e", + "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", + "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", + "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c", + "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", + "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", + "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089", + "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", + "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e", + "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", + "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616" + ], + "markers": "python_version >= '3.7'", + "version": "==3.4.1" }, "click": { "hashes": [ - "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", - "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" + "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", + "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a" ], "markers": "python_version >= '3.7'", - "version": "==8.1.3" + "version": "==8.1.8" }, "cloudinary": { "hashes": [ - "sha256:f52a1f5eb2c6820f13aa01c109caa5937ad3fd6caf5967817d0ef6c113403afc" + "sha256:ba223705409b2aaddd5196c2184d65f50a83dffcba3b94f3727658ff6a0172a3", + "sha256:e4191b470c5bae55542b64e0a78659af42971880294456dca480bc974fa9280a" ], "index": "pypi", - "version": "==1.31.0" + "version": "==1.42.2" }, "flask": { "hashes": [ - "sha256:642c450d19c4ad482f96729bd2a8f6d32554aa1e231f4f6b4e7e5264b16cca2b", - "sha256:b9c46cc36662a7949f34b52d8ec7bb59c0d74ba08ba6cb9ce9adc1d8676d9526" + "sha256:5f873c5184c897c8d9d1b05df1e3d01b14910ce69607a117bd3277098a5836ac", + "sha256:d667207822eb83f1c4b50949b1623c8fc8d51f2341d65f72e1a1815397551136" ], "index": "pypi", - "version": "==2.2.2" + "version": "==3.1.0" }, "flask-admin": { "hashes": [ - "sha256:424ffc79b7b0dfff051555686ea12e86e48dffacac14beaa319fb4502ac40988" + "sha256:24cae2af832b6a611a01d7dc35f42d266c1d6c75a426b869d8cb241b78233369", + "sha256:fd8190f1ec3355913a22739c46ed3623f1d82b8112cde324c60a6fc9b21c9406" ], "index": "pypi", - "version": "==1.6.0" + "version": "==1.6.1" }, "flask-cors": { "hashes": [ - "sha256:74efc975af1194fc7891ff5cd85b0f7478be4f7f59fe158102e91abb72bb4438", - "sha256:b60839393f3b84a0f3746f6cdca56c1ad7426aa738b70d6c61375857823181de" + "sha256:6ccb38d16d6b72bbc156c1c3f192bc435bfcc3c2bc864b2df1eb9b2d97b2403c", + "sha256:fa5cb364ead54bbf401a26dbf03030c6b18fb2fcaf70408096a572b409586b0c" + ], + "index": "pypi", + "version": "==5.0.1" + }, + "flask-jwt-extended": { + "hashes": [ + "sha256:63a28fc9731bcc6c4b8815b6f954b5904caa534fc2ae9b93b1d3ef12930dca95", + "sha256:9215d05a9413d3855764bcd67035e75819d23af2fafb6b55197eb5a3313fdfb2" ], "index": "pypi", - "version": "==3.0.10" + "version": "==4.6.0" }, "flask-migrate": { "hashes": [ - "sha256:8662a9dd391ce36deeaf3265987319c20fdb4c8a45306a32ba4f8224459abed4", - "sha256:a0062c8d3f32de02847086b46cfc389412f78c71c89a619ebd7097e89d72ea4b" + "sha256:1a336b06eb2c3ace005f5f2ded8641d534c18798d64061f6ff11f79e1434126d", + "sha256:24d8051af161782e0743af1b04a152d007bad9772b2bca67b7ec1e8ceeb3910d" ], "index": "pypi", - "version": "==4.0.3" + "version": "==4.1.0" }, "flask-sqlalchemy": { "hashes": [ - "sha256:2764335f3c9d7ebdc9ed6044afaf98aae9fa50d7a074cef55dde307ec95903ec", - "sha256:add5750b2f9cd10512995261ee2aa23fab85bd5626061aa3c564b33bb4aa780a" + "sha256:c5765e58ca145401b52106c0f46178569243c5da25556be2c231ecc60867c5b1", + "sha256:cabb6600ddd819a9f859f36515bb1bd8e7dbf30206cc679d2b081dff9e383283" ], "index": "pypi", - "version": "==3.0.3" + "version": "==3.0.5" }, "flask-swagger": { "hashes": [ @@ -96,304 +212,362 @@ }, "greenlet": { "hashes": [ - "sha256:03a8f4f3430c3b3ff8d10a2a86028c660355ab637cee9333d63d66b56f09d52a", - "sha256:0bf60faf0bc2468089bdc5edd10555bab6e85152191df713e2ab1fcc86382b5a", - "sha256:18a7f18b82b52ee85322d7a7874e676f34ab319b9f8cce5de06067384aa8ff43", - "sha256:18e98fb3de7dba1c0a852731c3070cf022d14f0d68b4c87a19cc1016f3bb8b33", - "sha256:1a819eef4b0e0b96bb0d98d797bef17dc1b4a10e8d7446be32d1da33e095dbb8", - "sha256:26fbfce90728d82bc9e6c38ea4d038cba20b7faf8a0ca53a9c07b67318d46088", - "sha256:2780572ec463d44c1d3ae850239508dbeb9fed38e294c68d19a24d925d9223ca", - "sha256:283737e0da3f08bd637b5ad058507e578dd462db259f7f6e4c5c365ba4ee9343", - "sha256:2d4686f195e32d36b4d7cf2d166857dbd0ee9f3d20ae349b6bf8afc8485b3645", - "sha256:2dd11f291565a81d71dab10b7033395b7a3a5456e637cf997a6f33ebdf06f8db", - "sha256:30bcf80dda7f15ac77ba5af2b961bdd9dbc77fd4ac6105cee85b0d0a5fcf74df", - "sha256:32e5b64b148966d9cccc2c8d35a671409e45f195864560829f395a54226408d3", - "sha256:36abbf031e1c0f79dd5d596bfaf8e921c41df2bdf54ee1eed921ce1f52999a86", - "sha256:3a06ad5312349fec0ab944664b01d26f8d1f05009566339ac6f63f56589bc1a2", - "sha256:3a51c9751078733d88e013587b108f1b7a1fb106d402fb390740f002b6f6551a", - "sha256:3c9b12575734155d0c09d6c3e10dbd81665d5c18e1a7c6597df72fd05990c8cf", - "sha256:3f6ea9bd35eb450837a3d80e77b517ea5bc56b4647f5502cd28de13675ee12f7", - "sha256:4b58adb399c4d61d912c4c331984d60eb66565175cdf4a34792cd9600f21b394", - "sha256:4d2e11331fc0c02b6e84b0d28ece3a36e0548ee1a1ce9ddde03752d9b79bba40", - "sha256:5454276c07d27a740c5892f4907c86327b632127dd9abec42ee62e12427ff7e3", - "sha256:561091a7be172ab497a3527602d467e2b3fbe75f9e783d8b8ce403fa414f71a6", - "sha256:6c3acb79b0bfd4fe733dff8bc62695283b57949ebcca05ae5c129eb606ff2d74", - "sha256:703f18f3fda276b9a916f0934d2fb6d989bf0b4fb5a64825260eb9bfd52d78f0", - "sha256:7492e2b7bd7c9b9916388d9df23fa49d9b88ac0640db0a5b4ecc2b653bf451e3", - "sha256:76ae285c8104046b3a7f06b42f29c7b73f77683df18c49ab5af7983994c2dd91", - "sha256:7cafd1208fdbe93b67c7086876f061f660cfddc44f404279c1585bbf3cdc64c5", - "sha256:7efde645ca1cc441d6dc4b48c0f7101e8d86b54c8530141b09fd31cef5149ec9", - "sha256:88d9ab96491d38a5ab7c56dd7a3cc37d83336ecc564e4e8816dbed12e5aaefc8", - "sha256:8eab883b3b2a38cc1e050819ef06a7e6344d4a990d24d45bc6f2cf959045a45b", - "sha256:910841381caba4f744a44bf81bfd573c94e10b3045ee00de0cbf436fe50673a6", - "sha256:9190f09060ea4debddd24665d6804b995a9c122ef5917ab26e1566dcc712ceeb", - "sha256:937e9020b514ceedb9c830c55d5c9872abc90f4b5862f89c0887033ae33c6f73", - "sha256:94c817e84245513926588caf1152e3b559ff794d505555211ca041f032abbb6b", - "sha256:971ce5e14dc5e73715755d0ca2975ac88cfdaefcaab078a284fea6cfabf866df", - "sha256:9d14b83fab60d5e8abe587d51c75b252bcc21683f24699ada8fb275d7712f5a9", - "sha256:9f35ec95538f50292f6d8f2c9c9f8a3c6540bbfec21c9e5b4b751e0a7c20864f", - "sha256:a1846f1b999e78e13837c93c778dcfc3365902cfb8d1bdb7dd73ead37059f0d0", - "sha256:acd2162a36d3de67ee896c43effcd5ee3de247eb00354db411feb025aa319857", - "sha256:b0ef99cdbe2b682b9ccbb964743a6aca37905fda5e0452e5ee239b1654d37f2a", - "sha256:b80f600eddddce72320dbbc8e3784d16bd3fb7b517e82476d8da921f27d4b249", - "sha256:b864ba53912b6c3ab6bcb2beb19f19edd01a6bfcbdfe1f37ddd1778abfe75a30", - "sha256:b9ec052b06a0524f0e35bd8790686a1da006bd911dd1ef7d50b77bfbad74e292", - "sha256:ba2956617f1c42598a308a84c6cf021a90ff3862eddafd20c3333d50f0edb45b", - "sha256:bdfea8c661e80d3c1c99ad7c3ff74e6e87184895bbaca6ee8cc61209f8b9b85d", - "sha256:be4ed120b52ae4d974aa40215fcdfde9194d63541c7ded40ee12eb4dda57b76b", - "sha256:c4302695ad8027363e96311df24ee28978162cdcdd2006476c43970b384a244c", - "sha256:c48f54ef8e05f04d6eff74b8233f6063cb1ed960243eacc474ee73a2ea8573ca", - "sha256:c9c59a2120b55788e800d82dfa99b9e156ff8f2227f07c5e3012a45a399620b7", - "sha256:cd021c754b162c0fb55ad5d6b9d960db667faad0fa2ff25bb6e1301b0b6e6a75", - "sha256:d27ec7509b9c18b6d73f2f5ede2622441de812e7b1a80bbd446cb0633bd3d5ae", - "sha256:d5508f0b173e6aa47273bdc0a0b5ba055b59662ba7c7ee5119528f466585526b", - "sha256:d75209eed723105f9596807495d58d10b3470fa6732dd6756595e89925ce2470", - "sha256:db1a39669102a1d8d12b57de2bb7e2ec9066a6f2b3da35ae511ff93b01b5d564", - "sha256:dbfcfc0218093a19c252ca8eb9aee3d29cfdcb586df21049b9d777fd32c14fd9", - "sha256:e0f72c9ddb8cd28532185f54cc1453f2c16fb417a08b53a855c4e6a418edd099", - "sha256:e7c8dc13af7db097bed64a051d2dd49e9f0af495c26995c00a9ee842690d34c0", - "sha256:ea9872c80c132f4663822dd2a08d404073a5a9b5ba6155bea72fb2a79d1093b5", - "sha256:eff4eb9b7eb3e4d0cae3d28c283dc16d9bed6b193c2e1ace3ed86ce48ea8df19", - "sha256:f82d4d717d8ef19188687aa32b8363e96062911e63ba22a0cff7802a8e58e5f1", - "sha256:fc3a569657468b6f3fb60587e48356fe512c1754ca05a564f11366ac9e306526" + "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e", + "sha256:03a088b9de532cbfe2ba2034b2b85e82df37874681e8c470d6fb2f8c04d7e4b7", + "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", + "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1", + "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159", + "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563", + "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83", + "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9", + "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395", + "sha256:1d3755bcb2e02de341c55b4fca7a745a24a9e7212ac953f6b3a48d117d7257aa", + "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942", + "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1", + "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441", + "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22", + "sha256:346bed03fe47414091be4ad44786d1bd8bef0c3fcad6ed3dee074a032ab408a9", + "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0", + "sha256:37b9de5a96111fc15418819ab4c4432e4f3c2ede61e660b1e33971eba26ef9ba", + "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3", + "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1", + "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", + "sha256:47da355d8687fd65240c364c90a31569a133b7b60de111c255ef5b606f2ae291", + "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39", + "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d", + "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467", + "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475", + "sha256:54558ea205654b50c438029505def3834e80f0869a70fb15b871c29b4575ddef", + "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c", + "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511", + "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c", + "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", + "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a", + "sha256:6ef9ea3f137e5711f0dbe5f9263e8c009b7069d8a1acea822bd5e9dae0ae49c8", + "sha256:7017b2be767b9d43cc31416aba48aab0d2309ee31b4dbf10a1d38fb7972bdf9d", + "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01", + "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145", + "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80", + "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13", + "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e", + "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b", + "sha256:85f3ff71e2e60bd4b4932a043fbbe0f499e263c628390b285cb599154a3b03b1", + "sha256:8b8b36671f10ba80e159378df9c4f15c14098c4fd73a36b9ad715f057272fbef", + "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc", + "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff", + "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120", + "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437", + "sha256:95ffcf719966dd7c453f908e208e14cde192e09fde6c7186c8f1896ef778d8cd", + "sha256:98884ecf2ffb7d7fe6bd517e8eb99d31ff7855a840fa6d0d63cd07c037f6a981", + "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36", + "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a", + "sha256:a0dfc6c143b519113354e780a50381508139b07d2177cb6ad6a08278ec655798", + "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7", + "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761", + "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0", + "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e", + "sha256:b8da394b34370874b4572676f36acabac172602abf054cbc4ac910219f3340af", + "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa", + "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c", + "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42", + "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e", + "sha256:d21e10da6ec19b457b82636209cbe2331ff4306b54d06fa04b7c138ba18c8a81", + "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e", + "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617", + "sha256:db32b5348615a04b82240cc67983cb315309e88d444a288934ee6ceaebcad6cc", + "sha256:dcc62f31eae24de7f8dce72134c8651c58000d3b1868e01392baea7c32c247de", + "sha256:dfc59d69fc48664bc693842bd57acfdd490acafda1ab52c7836e3fc75c90a111", + "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383", + "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70", + "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6", + "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4", + "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011", + "sha256:f1d4aeb8891338e60d1ab6127af1fe45def5259def8094b9c7e34690c8858803", + "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79", + "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f" ], "markers": "python_version >= '3' and platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32')))))", - "version": "==2.0.2" + "version": "==3.1.1" }, "gunicorn": { "hashes": [ - "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e", - "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8" + "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", + "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec" ], "index": "pypi", - "version": "==20.1.0" + "version": "==23.0.0" + }, + "idna": { + "hashes": [ + "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", + "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" + ], + "markers": "python_version >= '3.6'", + "version": "==3.10" }, "itsdangerous": { "hashes": [ - "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", - "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" + "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", + "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173" ], - "markers": "python_version >= '3.7'", - "version": "==2.1.2" + "markers": "python_version >= '3.8'", + "version": "==2.2.0" }, "jinja2": { "hashes": [ - "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", - "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" + "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", + "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67" ], "markers": "python_version >= '3.7'", - "version": "==3.1.2" + "version": "==3.1.6" }, "mako": { "hashes": [ - "sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818", - "sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34" + "sha256:95920acccb578427a9aa38e37a186b1e43156c87260d7ba18ca63aa4c7cbd3a1", + "sha256:b5d65ff3462870feec922dbccf38f6efb44e5714d7b593a656be86663d8600ac" ], - "markers": "python_version >= '3.7'", - "version": "==1.2.4" + "markers": "python_version >= '3.8'", + "version": "==1.3.9" }, "markupsafe": { "hashes": [ - "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed", - "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc", - "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2", - "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460", - "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7", - "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0", - "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1", - "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa", - "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03", - "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323", - "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65", - "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013", - "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036", - "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f", - "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4", - "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419", - "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2", - "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619", - "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a", - "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a", - "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd", - "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7", - "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666", - "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65", - "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859", - "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625", - "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff", - "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156", - "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd", - "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba", - "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f", - "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1", - "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094", - "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a", - "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513", - "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed", - "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d", - "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3", - "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147", - "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c", - "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603", - "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601", - "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a", - "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1", - "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d", - "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3", - "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54", - "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2", - "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6", - "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58" + "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", + "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", + "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", + "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", + "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", + "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", + "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", + "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", + "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", + "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", + "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0", + "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", + "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", + "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", + "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", + "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", + "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", + "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", + "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", + "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", + "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", + "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", + "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", + "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", + "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", + "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", + "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", + "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", + "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", + "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", + "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f", + "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", + "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", + "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", + "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", + "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", + "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", + "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", + "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", + "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", + "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", + "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", + "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", + "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", + "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", + "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", + "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", + "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", + "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29", + "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", + "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", + "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", + "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", + "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", + "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", + "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", + "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", + "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", + "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", + "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", + "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50" ], - "markers": "python_version >= '3.7'", - "version": "==2.1.2" + "markers": "python_version >= '3.9'", + "version": "==3.0.2" + }, + "packaging": { + "hashes": [ + "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", + "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f" + ], + "markers": "python_version >= '3.8'", + "version": "==24.2" }, "psycopg2-binary": { "hashes": [ - "sha256:00475004e5ed3e3bf5e056d66e5dcdf41a0dc62efcd57997acd9135c40a08a50", - "sha256:01ad49d68dd8c5362e4bfb4158f2896dc6e0c02e87b8a3770fc003459f1a4425", - "sha256:024030b13bdcbd53d8a93891a2cf07719715724fc9fee40243f3bd78b4264b8f", - "sha256:02551647542f2bf89073d129c73c05a25c372fc0a49aa50e0de65c3c143d8bd0", - "sha256:043a9fd45a03858ff72364b4b75090679bd875ee44df9c0613dc862ca6b98460", - "sha256:05b3d479425e047c848b9782cd7aac9c6727ce23181eb9647baf64ffdfc3da41", - "sha256:0775d6252ccb22b15da3b5d7adbbf8cfe284916b14b6dc0ff503a23edb01ee85", - "sha256:1764546ffeaed4f9428707be61d68972eb5ede81239b46a45843e0071104d0dd", - "sha256:1e491e6489a6cb1d079df8eaa15957c277fdedb102b6a68cfbf40c4994412fd0", - "sha256:212757ffcecb3e1a5338d4e6761bf9c04f750e7d027117e74aa3cd8a75bb6fbd", - "sha256:215d6bf7e66732a514f47614f828d8c0aaac9a648c46a831955cb103473c7147", - "sha256:25382c7d174c679ce6927c16b6fbb68b10e56ee44b1acb40671e02d29f2fce7c", - "sha256:2abccab84d057723d2ca8f99ff7b619285d40da6814d50366f61f0fc385c3903", - "sha256:2d964eb24c8b021623df1c93c626671420c6efadbdb8655cb2bd5e0c6fa422ba", - "sha256:2ec46ed947801652c9643e0b1dc334cfb2781232e375ba97312c2fc256597632", - "sha256:2ef892cabdccefe577088a79580301f09f2a713eb239f4f9f62b2b29cafb0577", - "sha256:33e632d0885b95a8b97165899006c40e9ecdc634a529dca7b991eb7de4ece41c", - "sha256:3520d7af1ebc838cc6084a3281145d5cd5bdd43fdef139e6db5af01b92596cb7", - "sha256:3d790f84201c3698d1bfb404c917f36e40531577a6dda02e45ba29b64d539867", - "sha256:3fc33295cfccad697a97a76dec3f1e94ad848b7b163c3228c1636977966b51e2", - "sha256:422e3d43b47ac20141bc84b3d342eead8d8099a62881a501e97d15f6addabfe9", - "sha256:426c2ae999135d64e6a18849a7d1ad0e1bd007277e4a8f4752eaa40a96b550ff", - "sha256:46512486be6fbceef51d7660dec017394ba3e170299d1dc30928cbedebbf103a", - "sha256:46850a640df62ae940e34a163f72e26aca1f88e2da79148e1862faaac985c302", - "sha256:484405b883630f3e74ed32041a87456c5e0e63a8e3429aa93e8714c366d62bd1", - "sha256:4e7904d1920c0c89105c0517dc7e3f5c20fb4e56ba9cdef13048db76947f1d79", - "sha256:56b2957a145f816726b109ee3d4e6822c23f919a7d91af5a94593723ed667835", - "sha256:5c6527c8efa5226a9e787507652dd5ba97b62d29b53c371a85cd13f957fe4d42", - "sha256:5cbc554ba47ecca8cd3396ddaca85e1ecfe3e48dd57dc5e415e59551affe568e", - "sha256:5d28ecdf191db558d0c07d0f16524ee9d67896edf2b7990eea800abeb23ebd61", - "sha256:5fc447058d083b8c6ac076fc26b446d44f0145308465d745fba93a28c14c9e32", - "sha256:63e318dbe52709ed10d516a356f22a635e07a2e34c68145484ed96a19b0c4c68", - "sha256:68d81a2fe184030aa0c5c11e518292e15d342a667184d91e30644c9d533e53e1", - "sha256:6e63814ec71db9bdb42905c925639f319c80e7909fb76c3b84edc79dadef8d60", - "sha256:6f8a9bcab7b6db2e3dbf65b214dfc795b4c6b3bb3af922901b6a67f7cb47d5f8", - "sha256:70831e03bd53702c941da1a1ad36c17d825a24fbb26857b40913d58df82ec18b", - "sha256:74eddec4537ab1f701a1647214734bc52cee2794df748f6ae5908e00771f180a", - "sha256:7b3751857da3e224f5629400736a7b11e940b5da5f95fa631d86219a1beaafec", - "sha256:7cf1d44e710ca3a9ce952bda2855830fe9f9017ed6259e01fcd71ea6287565f5", - "sha256:7d07f552d1e412f4b4e64ce386d4c777a41da3b33f7098b6219012ba534fb2c2", - "sha256:7d88db096fa19d94f433420eaaf9f3c45382da2dd014b93e4bf3215639047c16", - "sha256:7ee3095d02d6f38bd7d9a5358fcc9ea78fcdb7176921528dd709cc63f40184f5", - "sha256:902844f9c4fb19b17dfa84d9e2ca053d4a4ba265723d62ea5c9c26b38e0aa1e6", - "sha256:937880290775033a743f4836aa253087b85e62784b63fd099ee725d567a48aa1", - "sha256:95076399ec3b27a8f7fa1cc9a83417b1c920d55cf7a97f718a94efbb96c7f503", - "sha256:9c38d3869238e9d3409239bc05bc27d6b7c99c2a460ea337d2814b35fb4fea1b", - "sha256:9e32cedc389bcb76d9f24ea8a012b3cb8385ee362ea437e1d012ffaed106c17d", - "sha256:9ffdc51001136b699f9563b1c74cc1f8c07f66ef7219beb6417a4c8aaa896c28", - "sha256:a0adef094c49f242122bb145c3c8af442070dc0e4312db17e49058c1702606d4", - "sha256:a36a0e791805aa136e9cbd0ffa040d09adec8610453ee8a753f23481a0057af5", - "sha256:a7e518a0911c50f60313cb9e74a169a65b5d293770db4770ebf004245f24b5c5", - "sha256:af0516e1711995cb08dc19bbd05bec7dbdebf4185f68870595156718d237df3e", - "sha256:b8104f709590fff72af801e916817560dbe1698028cd0afe5a52d75ceb1fce5f", - "sha256:b911dfb727e247340d36ae20c4b9259e4a64013ab9888ccb3cbba69b77fd9636", - "sha256:b9a794cef1d9c1772b94a72eec6da144c18e18041d294a9ab47669bc77a80c1d", - "sha256:b9c33d4aef08dfecbd1736ceab8b7b3c4358bf10a0121483e5cd60d3d308cc64", - "sha256:b9d38a4656e4e715d637abdf7296e98d6267df0cc0a8e9a016f8ba07e4aa3eeb", - "sha256:bcda1c84a1c533c528356da5490d464a139b6e84eb77cc0b432e38c5c6dd7882", - "sha256:bef7e3f9dc6f0c13afdd671008534be5744e0e682fb851584c8c3a025ec09720", - "sha256:c15ba5982c177bc4b23a7940c7e4394197e2d6a424a2d282e7c236b66da6d896", - "sha256:c5254cbd4f4855e11cebf678c1a848a3042d455a22a4ce61349c36aafd4c2267", - "sha256:c5682a45df7d9642eff590abc73157c887a68f016df0a8ad722dcc0f888f56d7", - "sha256:c5e65c6ac0ae4bf5bef1667029f81010b6017795dcb817ba5c7b8a8d61fab76f", - "sha256:d4c7b3a31502184e856df1f7bbb2c3735a05a8ce0ade34c5277e1577738a5c91", - "sha256:d892bfa1d023c3781a3cab8dd5af76b626c483484d782e8bd047c180db590e4c", - "sha256:dbc332beaf8492b5731229a881807cd7b91b50dbbbaf7fe2faf46942eda64a24", - "sha256:dc85b3777068ed30aff8242be2813038a929f2084f69e43ef869daddae50f6ee", - "sha256:e59137cdb970249ae60be2a49774c6dfb015bd0403f05af1fe61862e9626642d", - "sha256:e67b3c26e9b6d37b370c83aa790bbc121775c57bfb096c2e77eacca25fd0233b", - "sha256:e72c91bda9880f097c8aa3601a2c0de6c708763ba8128006151f496ca9065935", - "sha256:f95b8aca2703d6a30249f83f4fe6a9abf2e627aa892a5caaab2267d56be7ab69" + "sha256:04392983d0bb89a8717772a193cfaac58871321e3ec69514e1c4e0d4957b5aff", + "sha256:056470c3dc57904bbf63d6f534988bafc4e970ffd50f6271fc4ee7daad9498a5", + "sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f", + "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5", + "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0", + "sha256:19721ac03892001ee8fdd11507e6a2e01f4e37014def96379411ca99d78aeb2c", + "sha256:1a6784f0ce3fec4edc64e985865c17778514325074adf5ad8f80636cd029ef7c", + "sha256:2286791ececda3a723d1910441c793be44625d86d1a4e79942751197f4d30341", + "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f", + "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7", + "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d", + "sha256:270934a475a0e4b6925b5f804e3809dd5f90f8613621d062848dd82f9cd62007", + "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", + "sha256:2ad26b467a405c798aaa1458ba09d7e2b6e5f96b1ce0ac15d82fd9f95dc38a92", + "sha256:2b3d2491d4d78b6b14f76881905c7a8a8abcf974aad4a8a0b065273a0ed7a2cb", + "sha256:2ce3e21dc3437b1d960521eca599d57408a695a0d3c26797ea0f72e834c7ffe5", + "sha256:30e34c4e97964805f715206c7b789d54a78b70f3ff19fbe590104b71c45600e5", + "sha256:3216ccf953b3f267691c90c6fe742e45d890d8272326b4a8b20850a03d05b7b8", + "sha256:32581b3020c72d7a421009ee1c6bf4a131ef5f0a968fab2e2de0c9d2bb4577f1", + "sha256:35958ec9e46432d9076286dda67942ed6d968b9c3a6a2fd62b48939d1d78bf68", + "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73", + "sha256:3c18f74eb4386bf35e92ab2354a12c17e5eb4d9798e4c0ad3a00783eae7cd9f1", + "sha256:3c4745a90b78e51d9ba06e2088a2fe0c693ae19cc8cb051ccda44e8df8a6eb53", + "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d", + "sha256:3e9c76f0ac6f92ecfc79516a8034a544926430f7b080ec5a0537bca389ee0906", + "sha256:48b338f08d93e7be4ab2b5f1dbe69dc5e9ef07170fe1f86514422076d9c010d0", + "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", + "sha256:512d29bb12608891e349af6a0cccedce51677725a921c07dba6342beaf576f9a", + "sha256:5a507320c58903967ef7384355a4da7ff3f28132d679aeb23572753cbf2ec10b", + "sha256:5c370b1e4975df846b0277b4deba86419ca77dbc25047f535b0bb03d1a544d44", + "sha256:6b269105e59ac96aba877c1707c600ae55711d9dcd3fc4b5012e4af68e30c648", + "sha256:6d4fa1079cab9018f4d0bd2db307beaa612b0d13ba73b5c6304b9fe2fb441ff7", + "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f", + "sha256:73aa0e31fa4bb82578f3a6c74a73c273367727de397a7a0f07bd83cbea696baa", + "sha256:7559bce4b505762d737172556a4e6ea8a9998ecac1e39b5233465093e8cee697", + "sha256:79625966e176dc97ddabc142351e0409e28acf4660b88d1cf6adb876d20c490d", + "sha256:7a813c8bdbaaaab1f078014b9b0b13f5de757e2b5d9be6403639b298a04d218b", + "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526", + "sha256:7f4152f8f76d2023aac16285576a9ecd2b11a9895373a1f10fd9db54b3ff06b4", + "sha256:7f5d859928e635fa3ce3477704acee0f667b3a3d3e4bb109f2b18d4005f38287", + "sha256:851485a42dbb0bdc1edcdabdb8557c09c9655dfa2ca0460ff210522e073e319e", + "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673", + "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0", + "sha256:8aabf1c1a04584c168984ac678a668094d831f152859d06e055288fa515e4d30", + "sha256:8aecc5e80c63f7459a1a2ab2c64df952051df196294d9f739933a9f6687e86b3", + "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e", + "sha256:8de718c0e1c4b982a54b41779667242bc630b2197948405b7bd8ce16bcecac92", + "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a", + "sha256:b5f86c56eeb91dc3135b3fd8a95dc7ae14c538a2f3ad77a19645cf55bab1799c", + "sha256:b73d6d7f0ccdad7bc43e6d34273f70d587ef62f824d7261c4ae9b8b1b6af90e8", + "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", + "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47", + "sha256:ce5ab4bf46a211a8e924d307c1b1fcda82368586a19d0a24f8ae166f5c784864", + "sha256:d00924255d7fc916ef66e4bf22f354a940c67179ad3fd7067d7a0a9c84d2fbfc", + "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00", + "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb", + "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539", + "sha256:e5720a5d25e3b99cd0dc5c8a440570469ff82659bb09431c1439b92caf184d3b", + "sha256:e8b58f0a96e7a1e341fc894f62c1177a7c83febebb5ff9123b579418fdc8a481", + "sha256:e984839e75e0b60cfe75e351db53d6db750b00de45644c5d1f7ee5d1f34a1ce5", + "sha256:eb09aa7f9cecb45027683bb55aebaaf45a0df8bf6de68801a6afdc7947bb09d4", + "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64", + "sha256:ecced182e935529727401b24d76634a357c71c9275b356efafd8a2a91ec07392", + "sha256:ee0e8c683a7ff25d23b55b11161c2663d4b099770f6085ff0a20d4505778d6b4", + "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", + "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1", + "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", + "sha256:ffe8ed017e4ed70f68b7b371d84b7d4a790368db9203dfc2d222febd3a9c8863" ], "index": "pypi", - "version": "==2.9.5" + "version": "==2.9.10" + }, + "pyjwt": { + "hashes": [ + "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", + "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb" + ], + "markers": "python_version >= '3.9'", + "version": "==2.10.1" }, "python-dotenv": { "hashes": [ - "sha256:1c93de8f636cde3ce377292818d0e440b6e45a82f215c3744979151fa8151c49", - "sha256:41e12e0318bebc859fcc4d97d4db8d20ad21721a6aa5047dd59f090391cb549a" + "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", + "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a" ], "index": "pypi", - "version": "==0.21.1" + "version": "==1.0.1" }, "pyyaml": { "hashes": [ - "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf", - "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", - "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", - "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", - "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", - "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", - "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", - "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", - "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", - "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", - "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", - "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", - "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782", - "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", - "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", - "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", - "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", - "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", - "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1", - "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", - "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", - "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", - "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", - "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", - "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", - "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d", - "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", - "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", - "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7", - "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", - "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", - "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", - "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358", - "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", - "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", - "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", - "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", - "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f", - "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", - "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" + "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff", + "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", + "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", + "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", + "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", + "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", + "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", + "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", + "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", + "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", + "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a", + "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", + "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", + "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", + "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", + "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", + "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", + "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a", + "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", + "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", + "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", + "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", + "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", + "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", + "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", + "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", + "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", + "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", + "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", + "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706", + "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", + "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", + "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", + "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083", + "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", + "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", + "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", + "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", + "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", + "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", + "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", + "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", + "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", + "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", + "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5", + "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d", + "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", + "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", + "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", + "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", + "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", + "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", + "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4" ], - "markers": "python_version >= '3.6'", - "version": "==6.0" + "markers": "python_version >= '3.8'", + "version": "==6.0.2" }, - "setuptools": { + "requests": { "hashes": [ - "sha256:a7687c12b444eaac951ea87a9627c4f904ac757e7abdc5aac32833234af90378", - "sha256:e261cdf010c11a41cb5cb5f1bf3338a7433832029f559a6a7614bd42a967c300" + "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", + "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" ], - "markers": "python_version >= '3.7'", - "version": "==67.1.0" + "markers": "python_version >= '3.0'", + "version": "==2.32.3" }, "six": { "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", + "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" + "version": "==1.17.0" }, "sqlalchemy": { "hashes": [ @@ -442,37 +616,45 @@ "index": "pypi", "version": "==1.4.46" }, + "stripe": { + "hashes": [ + "sha256:0ced7cce23a6cb1a393c86a1f7f9435c9d83ae7cbd556362868caf62cb44a92c", + "sha256:6e6cf09ebb6d5fc2d708401cb8868fd7bff987a6d09a0433caaa92c62f97dbc5" + ], + "index": "pypi", + "version": "==11.6.0" + }, "typing-extensions": { "hashes": [ - "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa", - "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e" + "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", + "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" ], "index": "pypi", - "version": "==4.4.0" + "version": "==4.12.2" }, "urllib3": { "hashes": [ - "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72", - "sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1" + "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", + "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.14" + "markers": "python_version >= '3.9'", + "version": "==2.3.0" }, "werkzeug": { "hashes": [ - "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f", - "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5" + "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", + "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746" ], - "markers": "python_version >= '3.7'", - "version": "==2.2.2" + "markers": "python_version >= '3.9'", + "version": "==3.1.3" }, "wtforms": { "hashes": [ - "sha256:6b351bbb12dd58af57ffef05bc78425d08d1914e0fd68ee14143b7ade023c5bc", - "sha256:837f2f0e0ca79481b92884962b914eba4e72b7a2daaf1f939c890ed0124b834b" + "sha256:bf831c042829c8cdbad74c27575098d541d039b1faa74c771545ecac916f2c07", + "sha256:f8d76180d7239c94c6322f7990ae1216dae3659b7aa1cee94b6318bdffb474b9" ], - "markers": "python_version >= '3.7'", - "version": "==3.0.1" + "index": "pypi", + "version": "==3.1.2" } }, "develop": {} diff --git a/docs/assets/novedad-iphone.jpg b/docs/assets/novedad-iphone.jpg new file mode 100644 index 0000000000..2b50d03878 Binary files /dev/null and b/docs/assets/novedad-iphone.jpg differ diff --git a/docs/assets/oferta-samsung.jpg b/docs/assets/oferta-samsung.jpg new file mode 100644 index 0000000000..1df25af3f8 Binary files /dev/null and b/docs/assets/oferta-samsung.jpg differ diff --git a/docs/assets/tendencia-pixelbook.jpg b/docs/assets/tendencia-pixelbook.jpg new file mode 100644 index 0000000000..74a0893c68 Binary files /dev/null and b/docs/assets/tendencia-pixelbook.jpg differ diff --git a/package-lock.json b/package-lock.json index c932d7fc55..8dae9c331c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,16 @@ "version": "1.0.1", "license": "ISC", "dependencies": { + "@stripe/react-stripe-js": "^3.4.0", + "@stripe/stripe-js": "^6.0.0", + "cloudinary": "^2.6.0", "prop-types": "^15.6.1", "react": "^16.8.4", "react-dom": "^16.8.4", "react-polyfills": "0.0.1", - "react-router-dom": "^6.3.0" + "react-router-dom": "^6.3.0", + "styled-components": "^6.1.15", + "sweetalert2": "^11.17.2" }, "devDependencies": { "@babel/cli": "^7.16.0", @@ -1747,6 +1752,27 @@ "node": ">=10.0.0" } }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", + "license": "MIT" + }, "node_modules/@eslint/eslintrc": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", @@ -1850,6 +1876,29 @@ "node": ">= 8" } }, + "node_modules/@stripe/react-stripe-js": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-3.4.0.tgz", + "integrity": "sha512-5m0vProlV2qyB7qXHSn25Ao79BjgJW/oiv2ynJ645dpdjeR7fyeb+KSrA4Esk7jqy+aKmdyn70TAIN0BVgh0MA==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "@stripe/stripe-js": ">=1.44.1 <7.0.0", + "react": ">=16.8.0 <20.0.0", + "react-dom": ">=16.8.0 <20.0.0" + } + }, + "node_modules/@stripe/stripe-js": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-6.0.0.tgz", + "integrity": "sha512-Q8dbgLhXqtSCGflO8KaEQxM6/b3nHyPnX45300jxjUxS1acnBV5x7VRpWw0GDYgxAElIhlarPw0txXdeMzMC7Q==", + "license": "MIT", + "engines": { + "node": ">=12.16" + } + }, "node_modules/@types/body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", @@ -2022,6 +2071,12 @@ "@types/node": "*" } }, + "node_modules/@types/stylis": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", + "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==", + "license": "MIT" + }, "node_modules/@types/ws": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", @@ -3014,6 +3069,15 @@ "tslib": "^2.0.3" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001285", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001285.tgz", @@ -3109,6 +3173,19 @@ "node": ">=6" } }, + "node_modules/cloudinary": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/cloudinary/-/cloudinary-2.6.0.tgz", + "integrity": "sha512-FIlny9RR5LPgkMioG4V7yUpC6ASyIFQMWfx4TgOi/xBeLxJTegbyQc3itiXL0b0lDlSaL0KyT2THEw6osrKqpQ==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21", + "q": "^1.5.1" + }, + "engines": { + "node": ">=9" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -3346,6 +3423,15 @@ "node": ">= 8" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "license": "ISC", + "engines": { + "node": ">=4" + } + }, "node_modules/css-loader": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.5.1.tgz", @@ -3403,6 +3489,17 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "license": "MIT", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/css-what": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", @@ -3427,6 +3524,12 @@ "node": ">=4" } }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", @@ -6236,8 +6339,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash.debounce": { "version": "4.0.8", @@ -6464,10 +6566,16 @@ } }, "node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", - "dev": true, + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -6941,10 +7049,10 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.0", @@ -7059,21 +7167,31 @@ } }, "node_modules/postcss": { - "version": "8.4.4", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.4.tgz", - "integrity": "sha512-joU6fBsN6EIer28Lj6GDFoC/5yOZzLCfn0zHAn/MYXI7aPt4m4hK5KC5ovEZXy+lnCjmYIbQWngvju2ddyEr8Q==", - "dev": true, + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", "dependencies": { - "nanoid": "^3.1.30", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.1" + "nanoid": "^3.3.7", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" } }, "node_modules/postcss-modules-extract-imports": { @@ -7151,8 +7269,7 @@ "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/prelude-ls": { "version": "1.2.1", @@ -7254,6 +7371,17 @@ "node": ">=6" } }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", + "license": "MIT", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, "node_modules/qs": { "version": "6.10.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", @@ -8188,6 +8316,12 @@ "node": ">=8" } }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -8294,10 +8428,10 @@ } }, "node_modules/source-map-js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.1.tgz", - "integrity": "sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==", - "dev": true, + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -8495,6 +8629,40 @@ "webpack": "^5.0.0" } }, + "node_modules/styled-components": { + "version": "6.1.15", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.15.tgz", + "integrity": "sha512-PpOTEztW87Ua2xbmLa7yssjNyUF9vE7wdldRfn1I2E6RTkqknkBYpj771OxM/xrvRGinLy2oysa7GOd7NcZZIA==", + "license": "MIT", + "dependencies": { + "@emotion/is-prop-valid": "1.2.2", + "@emotion/unitless": "0.8.1", + "@types/stylis": "4.2.5", + "css-to-react-native": "3.2.0", + "csstype": "3.1.3", + "postcss": "8.4.49", + "shallowequal": "1.1.0", + "stylis": "4.3.2", + "tslib": "2.6.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + } + }, + "node_modules/stylis": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==", + "license": "MIT" + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -8507,6 +8675,16 @@ "node": ">=4" } }, + "node_modules/sweetalert2": { + "version": "11.17.2", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.17.2.tgz", + "integrity": "sha512-HKxDr1IyV3Lxr3W6sb61qm/p2epFIEdr5EKwteRFHnIg6f8nHFl2kX++DBVz16Mac+fFiU3hMpjq1L6yE2Ge5w==", + "license": "MIT", + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/limonte" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -8672,10 +8850,10 @@ } }, "node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "license": "0BSD" }, "node_modules/type-check": { "version": "0.4.0", @@ -10535,6 +10713,24 @@ "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==", "dev": true }, + "@emotion/is-prop-valid": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", + "requires": { + "@emotion/memoize": "^0.8.1" + } + }, + "@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, "@eslint/eslintrc": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", @@ -10619,6 +10815,19 @@ "fastq": "^1.6.0" } }, + "@stripe/react-stripe-js": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-3.4.0.tgz", + "integrity": "sha512-5m0vProlV2qyB7qXHSn25Ao79BjgJW/oiv2ynJ645dpdjeR7fyeb+KSrA4Esk7jqy+aKmdyn70TAIN0BVgh0MA==", + "requires": { + "prop-types": "^15.7.2" + } + }, + "@stripe/stripe-js": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-6.0.0.tgz", + "integrity": "sha512-Q8dbgLhXqtSCGflO8KaEQxM6/b3nHyPnX45300jxjUxS1acnBV5x7VRpWw0GDYgxAElIhlarPw0txXdeMzMC7Q==" + }, "@types/body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", @@ -10791,6 +11000,11 @@ "@types/node": "*" } }, + "@types/stylis": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", + "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==" + }, "@types/ws": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", @@ -11620,6 +11834,11 @@ "tslib": "^2.0.3" } }, + "camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==" + }, "caniuse-lite": { "version": "1.0.30001285", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001285.tgz", @@ -11687,6 +11906,15 @@ "shallow-clone": "^3.0.0" } }, + "cloudinary": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/cloudinary/-/cloudinary-2.6.0.tgz", + "integrity": "sha512-FIlny9RR5LPgkMioG4V7yUpC6ASyIFQMWfx4TgOi/xBeLxJTegbyQc3itiXL0b0lDlSaL0KyT2THEw6osrKqpQ==", + "requires": { + "lodash": "^4.17.21", + "q": "^1.5.1" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -11877,6 +12105,11 @@ "which": "^2.0.1" } }, + "css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==" + }, "css-loader": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.5.1.tgz", @@ -11917,6 +12150,16 @@ "nth-check": "^2.0.0" } }, + "css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "requires": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "css-what": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", @@ -11929,6 +12172,11 @@ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true }, + "csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, "debug": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", @@ -13987,8 +14235,7 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash.debounce": { "version": "4.0.8", @@ -14166,10 +14413,9 @@ } }, "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", - "dev": true + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==" }, "natural-compare": { "version": "1.4.0", @@ -14520,10 +14766,9 @@ "dev": true }, "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "picomatch": { "version": "2.3.0", @@ -14607,14 +14852,13 @@ } }, "postcss": { - "version": "8.4.4", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.4.tgz", - "integrity": "sha512-joU6fBsN6EIer28Lj6GDFoC/5yOZzLCfn0zHAn/MYXI7aPt4m4hK5KC5ovEZXy+lnCjmYIbQWngvju2ddyEr8Q==", - "dev": true, + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "requires": { - "nanoid": "^3.1.30", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.1" + "nanoid": "^3.3.7", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" } }, "postcss-modules-extract-imports": { @@ -14666,8 +14910,7 @@ "postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "prelude-ls": { "version": "1.2.1", @@ -14747,6 +14990,11 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==" + }, "qs": { "version": "6.10.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", @@ -15461,6 +15709,11 @@ "kind-of": "^6.0.2" } }, + "shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -15554,10 +15807,9 @@ "dev": true }, "source-map-js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.1.tgz", - "integrity": "sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==", - "dev": true + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" }, "source-map-support": { "version": "0.5.21", @@ -15700,6 +15952,27 @@ "dev": true, "requires": {} }, + "styled-components": { + "version": "6.1.15", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.15.tgz", + "integrity": "sha512-PpOTEztW87Ua2xbmLa7yssjNyUF9vE7wdldRfn1I2E6RTkqknkBYpj771OxM/xrvRGinLy2oysa7GOd7NcZZIA==", + "requires": { + "@emotion/is-prop-valid": "1.2.2", + "@emotion/unitless": "0.8.1", + "@types/stylis": "4.2.5", + "css-to-react-native": "3.2.0", + "csstype": "3.1.3", + "postcss": "8.4.49", + "shallowequal": "1.1.0", + "stylis": "4.3.2", + "tslib": "2.6.2" + } + }, + "stylis": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==" + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -15709,6 +15982,11 @@ "has-flag": "^3.0.0" } }, + "sweetalert2": { + "version": "11.17.2", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.17.2.tgz", + "integrity": "sha512-HKxDr1IyV3Lxr3W6sb61qm/p2epFIEdr5EKwteRFHnIg6f8nHFl2kX++DBVz16Mac+fFiU3hMpjq1L6yE2Ge5w==" + }, "tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -15815,10 +16093,9 @@ } }, "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "type-check": { "version": "0.4.0", diff --git a/package.json b/package.json index 3c8d47cba7..a2528f6fae 100755 --- a/package.json +++ b/package.json @@ -73,10 +73,15 @@ ] }, "dependencies": { + "@stripe/react-stripe-js": "^3.4.0", + "@stripe/stripe-js": "^6.0.0", + "cloudinary": "^2.6.0", "prop-types": "^15.6.1", "react": "^16.8.4", "react-dom": "^16.8.4", "react-polyfills": "0.0.1", - "react-router-dom": "^6.3.0" + "react-router-dom": "^6.3.0", + "styled-components": "^6.1.15", + "sweetalert2": "^11.17.2" } } diff --git a/src/api/admin.py b/src/api/admin.py index 3eecb64140..10b6f019e2 100644 --- a/src/api/admin.py +++ b/src/api/admin.py @@ -1,7 +1,7 @@ import os from flask_admin import Admin -from .models import db, User +from .models import db, User, Pedido, Smartphones, TVs, Laptops from flask_admin.contrib.sqla import ModelView def setup_admin(app): @@ -12,6 +12,10 @@ def setup_admin(app): # Add your models here, for example this is how we add a the User model to the admin admin.add_view(ModelView(User, db.session)) + admin.add_view(ModelView(Pedido, db.session)) + admin.add_view(ModelView(Smartphones, db.session)) + admin.add_view(ModelView(TVs, db.session)) + admin.add_view(ModelView(Laptops, db.session)) # You can duplicate that line to add mew models # admin.add_view(ModelView(YourModelName, db.session)) \ No newline at end of file diff --git a/src/api/models.py b/src/api/models.py index dccd8421ee..2465815a0a 100644 --- a/src/api/models.py +++ b/src/api/models.py @@ -1,19 +1,198 @@ from flask_sqlalchemy import SQLAlchemy +from sqlalchemy import PickleType db = SQLAlchemy() + class User(db.Model): - id = db.Column(db.Integer, primary_key=True) - email = db.Column(db.String(120), unique=True, nullable=False) - password = db.Column(db.String(80), unique=False, nullable=False) - is_active = db.Column(db.Boolean(), unique=False, nullable=False) + + __tablename__ = 'user' + + user_id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(50), unique=False, nullable=True) + lastname = db.Column(db.String(75), unique=False, nullable=True) + email = db.Column(db.String(75), unique=True, nullable=False) + password = db.Column(db.String(50), unique=False, nullable=False) + address = db.Column(db.String(50), unique=False, nullable=True) + username = db.Column(db.String(50), unique=True, nullable=False) + birthday_date = db.Column(db.String(15), nullable=True) + user_image = db.Column(db.String(200), nullable=True) + is_active = db.Column(db.Boolean(), nullable=False) + is_admin = db.Column(db.Boolean(), nullable=False) def __repr__(self): - return f'' + return f'' def serialize(self): return { - "id": self.id, + "user_id": self.user_id, + "name": self.name, + "lastname": self.lastname, "email": self.email, - # do not serialize the password, its a security breach + "password": self.password, + "username": self.username, + "address": self.address, + "birthday_date": self.birthday_date, + "image": self.user_image, + "is_active": self.is_active, + "is_admin": self.is_admin, + } + +class Pedido(db.Model): + + __tablename__ = 'pedido' + + pedido_id = db.Column(db.Integer, primary_key=True) + user_id = db.Column(db.Integer, db.ForeignKey('user.user_id'), nullable=False) + user = db.relationship('User', backref= 'pedido') + items = db.Column(PickleType, default=[]) + + def __repr__(self): + return f'' + + def serialize(self): + return { + "pedido_id": self.pedido_id, + "user_id": self.user_id, + "items": self.items + } + +class Smartphones(db.Model): + + __tablename__ = 'smartphones' + + smartphone_id = db.Column(db.Integer, primary_key=True) + modelo = db.Column(db.String(150), unique=False, nullable=False) + pantalla = db.Column(db.String(150), unique=False, nullable=False) + procesador = db.Column(db.String(150), unique=False, nullable=False) + memoria_ram = db.Column(db.String(150), unique=False, nullable=False) + almacenamiento = db.Column(db.String(150), unique=False, nullable=False) + camara = db.Column(db.String(150), unique=False, nullable=False) + bateria = db.Column(db.String(150), unique=False, nullable=False) + precio = db.Column(db.String(50), unique=False, nullable=False) + conectividad = db.Column(db.String(150), unique=False, nullable=False) + colores = db.Column(db.JSON, unique=False, nullable=False) + descripcion = db.Column(db.String(300), unique=False, nullable=False) + imagen = db.Column(db.JSON, unique=False) + tipo = db.Column(db.String(150), unique=False, nullable= False) + + + def __repr__(self): + return f'' + + def serialize(self): + return { + "smartphone_id": self.smartphone_id, + "modelo": self.modelo, + "pantalla" : self.pantalla, + "procesador" : self.procesador, + "memoria_ram" : self.memoria_ram, + "almacenamiento" : self.almacenamiento, + "camara" : self.camara, + "bateria" : self.bateria, + "precio" : self.precio, + "conectividad" : self.conectividad, + "colores" : self.colores, + "descripcion" : self.descripcion, + "imagen" : self.imagen, + "tipo" : self.tipo + + } + +class TVs(db.Model): + + __tablename__ = 'tv' + + tv_id = db.Column(db.Integer, primary_key=True) + marca = db.Column(db.String(150), unique=False, nullable=False) + contenido_de_la_caja = db.Column(db.String(150), unique=False, nullable=False) + modelo = db.Column(db.String(150), unique=False, nullable=False) + usos_recomendados = db.Column(db.String(150), unique=False, nullable=False) + año_modelo = db.Column(db.String(50), unique=False, nullable=False) + fabricante = db.Column(db.String(150), unique=False, nullable=False) + precio = db.Column(db.String(50), unique=False, nullable=False) + descripcion = db.Column(db.String(300), unique=False, nullable=False) + pantalla = db.Column(db.String(150), unique=False, nullable=False) + conectividad = db.Column(db.String(150), unique=False, nullable=False) + medidas = db.Column(db.String(50), unique=False, nullable=False) + imagen = db.Column(db.JSON, unique=False, nullable=False) + tipo = db.Column(db.String(150), unique=False, nullable= False) + + + + + def __repr__(self): + return f'' + + def serialize(self): + return { + "tv_id": self.tv_id, + "marca": self.marca, + "contenido_de_la_caja" : self.contenido_de_la_caja, + "modelo" : self.modelo, + "usos_recomendados" : self.usos_recomendados, + "año_modelo" : self.año_modelo, + "fabricante" : self.fabricante, + "precio" : self.precio, + "descripcion" : self.descripcion, + "pantalla" : self.pantalla, + "conectividad" : self.conectividad, + "medidas" : self.medidas, + "imagen" : self.imagen, + "tipo" : self.tipo + + } + +class Laptops(db.Model): + + __tablename__ = 'laptops' + + laptop_id = db.Column(db.Integer, primary_key=True) + marca = db.Column(db.String(150), unique=False, nullable=False) + modelo = db.Column(db.String(150), unique=False, nullable=False) + pantalla = db.Column(db.String(150), unique=False, nullable=False) + procesador = db.Column(db.String(150), unique=False, nullable=False) + modelo_cpu = db.Column(db.String(150), unique=False, nullable=False) + sistema_operativo = db.Column(db.String(150), unique=False, nullable=False) + memoria_ram = db.Column(db.String(150), unique=False, nullable=False) + almacenamiento = db.Column(db.String(300), unique=False, nullable=False) + camara = db.Column(db.String(150), unique=False, nullable=False) + bateria = db.Column(db.String(150), unique=False, nullable=False) + precio = db.Column(db.String(50), unique=False, nullable=False) + tecnologia = db.Column(db.String(150), unique=False, nullable=False) + colores = db.Column(db.JSON, unique=False, nullable=False) + descripcion = db.Column(db.String(300), unique=False, nullable=False) + imagen = db.Column(db.JSON, unique=False, nullable=False) + funcion_especial = db.Column(db.String(300), unique=False, nullable=False) + descripcion_tarjeta_grafica = db.Column(db.String(300), unique=False, nullable=False) + tipo = db.Column(db.String(150), unique=False, nullable= False) + + + + + def __repr__(self): + return f'' + + def serialize(self): + return { + "laptop_id": self.laptop_id, + "marca": self.marca, + "modelo" : self.modelo, + "pantalla" : self.pantalla, + "procesador" : self.procesador, + "modelo_cpu" : self.modelo_cpu, + "sistema_operativo" : self.sistema_operativo, + "memoria_ram" : self.memoria_ram, + "almacenamiento" : self.almacenamiento, + "camara" : self.camara, + "bateria" : self.bateria, + "precio" : self.precio, + "tecnologia" : self.tecnologia, + "colores" : self.colores, + "descripcion" : self.descripcion, + "imagen" : self.imagen, + "funcion_especial": self.funcion_especial, + "descripcion_tarjeta_grafica": self.descripcion_tarjeta_grafica, + "tipo": self.tipo + } \ No newline at end of file diff --git a/src/api/productos/laptops.py b/src/api/productos/laptops.py new file mode 100644 index 0000000000..2fb4e70525 --- /dev/null +++ b/src/api/productos/laptops.py @@ -0,0 +1,456 @@ +laptops = [ + { + "almacenamiento": "2 TB", + "bateria": "100 Wh, hasta 21 horas", + "camara": "1080p", + "colores": [ + "Gris espacial", + "Plata" + ], + "descripcion": "Potente MacBook Pro con chip M2 Pro y excelente autonomía.", + "imagen": { + "gris_espacial": [ + "https://www.backmarket.es/cdn-cgi/image/format%3Dauto%2Cquality%3D75%2Cwidth%3D1920/https://d2e6ccujb3mkqf.cloudfront.net/036549e6-7731-4e9f-adca-abf791e762e5-1_79a6b1a2-d32c-48ca-ac25-c3547aae3c0e.jpg", + "https://www.backmarket.es/cdn-cgi/image/format%3Dauto%2Cquality%3D75%2Cwidth%3D1920/https://d2e6ccujb3mkqf.cloudfront.net/036549e6-7731-4e9f-adca-abf791e762e5-5_a33f5621-6eba-43b1-a95c-67cffd13b2d0.jpg" + ], + "plata": [ + "https://www.backmarket.es/cdn-cgi/image/format%3Dauto%2Cquality%3D75%2Cwidth%3D640/https://d2e6ccujb3mkqf.cloudfront.net/f2996644-4aae-4312-8d31-b627ee67fc14-2_ec2d9eba-503d-40b2-aa0d-5c0aee04d220.jpg", + "https://www.backmarket.es/cdn-cgi/image/format%3Dauto%2Cquality%3D75%2Cwidth%3D1920/https://d2e6ccujb3mkqf.cloudfront.net/036549e6-7731-4e9f-adca-abf791e762e5-5_a33f5621-6eba-43b1-a95c-67cffd13b2d0.jpg" + ] + }, + "marca": "Apple", + "memoria_ram": "32 GB", + "modelo": "MacBook Pro 16 (2023)", + "modelo_cpu": "Apple M2 Pro", + "pantalla": "Retina de 16 pulgadas, 3024 x 1964", + "precio": "2499 €", + "procesador": "Apple M2 Pro", + "sistema_operativo": "macOS Ventura", + "tecnologia": "Wi-Fi 6, Bluetooth 5.3", + "funcion_especial": "Chip de Apple M2 Pro para un rendimiento excepcional", + "descripcion_tarjeta_grafica": "Tarjeta gráfica integrada Apple GPU con hasta 19 núcleos", + "tipo": "laptop" + + }, + { + "almacenamiento": "512 GB", + "bateria": "86 Wh, hasta 14 horas", + "camara": "720p", + "colores": [ + "Plata", + "Negro" + ], + "descripcion": "Ultraportátil y potente, ideal para profesionales creativos.", + "imagen": { + "negro": [ + "https://m.media-amazon.com/images/S/aplus-media-library-service-media/0b094112-df54-4ec3-be2d-e386ba55a231.__CR85,0,800,600_PT0_SX600_V1___.jpg", + "https://www.notebookcheck.org/uploads/tx_nbc2/DellXPS15-9575__1__01.jpg" + ], + "plata": [ + "https://mercadoactual.es/340110-large_default/dell-xps-15-9510-plata-156-intel-core-i7-16gb-512gb-ssd-nvidia-geforce-rtx-3050-ti-win-0pf3d.jpg", + "https://www.notebookcheck.org/uploads/tx_nbc2/DellXPS15-9575__1__01.jpg" + ] + }, + "marca": "Dell", + "memoria_ram": "32 GB", + "modelo": "XPS 15 (2023)", + "modelo_cpu": "Intel Core i7-13700H", + "pantalla": "OLED de 15.6 pulgadas, 3840 x 2400", + "precio": "1899 €", + "procesador": "Intel Core i7-13700H", + "sistema_operativo": "Windows 11 Pro", + "tecnologia": "Wi-Fi 6E, Bluetooth 5.2", + "funcion_especial": "Pantalla OLED para una experiencia visual excepcional", + "descripcion_tarjeta_grafica": "NVIDIA GeForce RTX 4050 para gráficos de alto rendimiento", + "tipo": "laptop" + + }, + { + "almacenamiento": "1 TB", + "bateria": "66 Wh, hasta 12 horas", + "camara": "1080p", + "colores": [ + "Azul noche", + "Plata" + ], + "descripcion": "Convertible 2 en 1 con una increíble pantalla OLED.", + "imagen": { + "azul_noche": [ + "https://static.fnac-static.com/multimedia/Images/ES/NR/6d/f3/71/7467885/1520-2.jpg", + "https://static.fnac-static.com/multimedia/Images/ES/NR/6d/f3/71/7467885/1541-3.jpg" + ], + "plata": [ + "https://thumb.pccomponentes.com/w-530-530/articles/82/823223/2696-hp-spectre-x360-14-ea1005ns-intel-core-i7-1195g7-16gb-1tb-ssd-135-tactil-comprar.jpg", + "https://static.fnac-static.com/multimedia/Images/ES/NR/6d/f3/71/7467885/1541-3.jpg" + ] + }, + "marca": "HP", + "memoria_ram": "16 GB", + "modelo": "Spectre x360 14 (2023)", + "modelo_cpu": "Intel Core i7-1265U", + "pantalla": "OLED de 13.5 pulgadas, 3000 x 2000", + "precio": "1499 €", + "procesador": "Intel Core i7-1265U", + "sistema_operativo": "Windows 11 Home", + "tecnologia": "Wi-Fi 6, Bluetooth 5.0", + "funcion_especial": "Diseño convertible 2 en 1", + "descripcion_tarjeta_grafica": "Intel Iris Xe Graphics integrada", + "tipo": "laptop" + + }, + { + "almacenamiento": "1 TB", + "bateria": "57 Wh, hasta 15 horas", + "camara": "720p", + "colores": [ + "Negro" + ], + "descripcion": "Líder en portátiles profesionales, ligero y de alto rendimiento.", + "imagen": { + "negro": [ + "https://thumb.pccomponentes.com/w-530-530/articles/1085/10853181/2442-lenovo-thinkpad-e16-gen-2-intel-core-ultra-7-155h-32gb-1tb-ssd-16-comprar.jpg", + "https://thumb.pccomponentes.com/w-530-530/articles/1085/10853181/5444-lenovo-thinkpad-e16-gen-2-intel-core-ultra-7-155h-32gb-1tb-ssd-16-caracteristicas.jpg" + ] + }, + "marca": "Lenovo", + "memoria_ram": "32 GB", + "modelo": "ThinkPad X1 Carbon Gen 11", + "modelo_cpu": "Intel Core i7-1365U", + "pantalla": "IPS de 14 pulgadas, 1920 x 1200", + "precio": "1799 €", + "procesador": "Intel Core i7-1365U", + "sistema_operativo": "Windows 11 Pro", + "tecnologia": "Wi-Fi 6E, Bluetooth 5.2", + "funcion_especial": "Teclado de alta calidad con retroiluminación", + "descripcion_tarjeta_grafica": "Intel Iris Xe Graphics integrada", + "tipo": "laptop" + + }, + { + "almacenamiento": "1 TB", + "bateria": "76 Wh, hasta 12 horas", + "camara": "720p", + "colores": [ + "Blanco", + "Negro" + ], + "descripcion": "Potente portátil gaming con una excelente relación precio/rendimiento.", + "imagen": { + "blanco": [ + "https://m.media-amazon.com/images/I/71XZs3BCOtL.jpg", + "https://gagadget.com/media/post_big/asus-rog-zephyrus-g14-2023-general.jpg" + ], + "negro": [ + "https://thumb.pccomponentes.com/w-530-530/articles/1072/10729033/1338-asus-rog-zephyrus-g14-2023-ga402xv-n2041-amd-ryzen-9-7940hs-32gb-1tb-ssd-rtx-4060-14.jpg", + "https://gagadget.com/media/post_big/asus-rog-zephyrus-g14-2023-general.jpg" + ] + }, + "marca": "Asus", + "memoria_ram": "16 GB", + "modelo": "ROG Zephyrus G14 (2023)", + "modelo_cpu": "AMD Ryzen 9 7940HS", + "pantalla": "IPS de 14 pulgadas, 2560 x 1600, 120 Hz", + "precio": "2199 €", + "procesador": "AMD Ryzen 9 7940HS", + "sistema_operativo": "Windows 11 Home", + "tecnologia": "Wi-Fi 6, Bluetooth 5.2", + "funcion_especial": "Pantalla de alta tasa de refresco y rendimiento en juegos", + "descripcion_tarjeta_grafica": "NVIDIA GeForce RTX 4060", + "tipo": "laptop" + + }, + { + "almacenamiento": "1 TB", + "bateria": "56 Wh, hasta 10 horas", + "camara": "720p", + "colores": [ + "Plata", + "Negro" + ], + "descripcion": "Portátil premium con excelente pantalla y diseño minimalista.", + "imagen": { + "negro": [ + "https://www.backmarket.es/cdn-cgi/image/format%3Dauto%2Cquality%3D75%2Cwidth%3D1920/https://d2e6ccujb3mkqf.cloudfront.net/6b75aee6-c47e-40e1-a720-6cc50db30306-1_4cb097d3-7fb6-4906-b549-30073b198295.jpg", + "https://www.backmarket.es/cdn-cgi/image/format%3Dauto%2Cquality%3D75%2Cwidth%3D1920/https://d2e6ccujb3mkqf.cloudfront.net/a48c3d7e-290b-4790-bcdd-fa92ff990577-3_613568a4-2236-41dd-abc1-ce758d1b4d8a.jpg" + ], + "plata": [ + "https://www.backmarket.es/cdn-cgi/image/format%3Dauto%2Cquality%3D75%2Cwidth%3D1920/https://d2e6ccujb3mkqf.cloudfront.net/a48c3d7e-290b-4790-bcdd-fa92ff990577-1_6becdc7f-5d05-4b60-b111-f5d7aae260db.jpg", + "https://www.backmarket.es/cdn-cgi/image/format%3Dauto%2Cquality%3D75%2Cwidth%3D1920/https://d2e6ccujb3mkqf.cloudfront.net/a48c3d7e-290b-4790-bcdd-fa92ff990577-3_613568a4-2236-41dd-abc1-ce758d1b4d8a.jpg" + ] + }, + "marca": "Microsoft", + "memoria_ram": "16 GB", + "modelo": "Surface Laptop 5", + "modelo_cpu": "Intel Core i7-1265U", + "pantalla": "PixelSense de 13.5 pulgadas, 2256 x 1504", + "precio": "1499 €", + "procesador": "Intel Core i7-1265U", + "sistema_operativo": "Windows 11 Home", + "tecnologia": "Wi-Fi 6, Bluetooth 5.1", + "funcion_especial": "Pantalla táctil con gran precisión y brillo", + "descripcion_tarjeta_grafica": "Intel Iris Xe Graphics integrada", + "tipo": "laptop" + + }, + { + "almacenamiento": "512 GB", + "bateria": "58 Wh, hasta 8 horas", + "camara": "720p", + "colores": [ + "Negro" + ], + "descripcion": "Portátil gaming potente con una pantalla de alta frecuencia de actualización.", + "imagen": { + "negro": [ + "https://img.pccomponentes.com/articles/1083/10831999/1432-acer-gaming-predator-helios-neo-16-phn16-72-intel-core-i7-14650hx-16gb-1tb-ssd-rtx-4060-16-review.jpg", + "https://img.pccomponentes.com/articles/1083/10831999/3806-acer-predator-helios-neo-16-phn16-72-intel-core-i7-14650hx-16gb-1tb-ssd-rtx-4060-16-mejor-precio.jpg" + ] + }, + "marca": "Acer", + "memoria_ram": "32 GB", + "modelo": "Predator Helios 300", + "modelo_cpu": "Intel Core i7-13700H", + "pantalla": "IPS de 15.6 pulgadas, 1920 x 1080, 144 Hz", + "precio": "1699 €", + "procesador": "Intel Core i7-13700H", + "sistema_operativo": "Windows 11 Home", + "tecnologia": "Wi-Fi 6, Bluetooth 5.2", + "funcion_especial": "Rendimiento gráfico para juegos AAA", + "descripcion_tarjeta_grafica": "NVIDIA GeForce RTX 4060", + "tipo": "laptop" + + }, + { + "almacenamiento": "512 GB", + "bateria": "80 Wh, hasta 9 horas", + "camara": "1080p", + "colores": [ + "Negro" + ], + "descripcion": "Un portátil de gaming de alto rendimiento con una pantalla OLED impresionante.", + "imagen": { + "negro": [ + "https://www.backmarket.es/cdn-cgi/image/format%3Dauto%2Cquality%3D75%2Cwidth%3D1920/https://d2e6ccujb3mkqf.cloudfront.net/c1578906-2b57-4337-8e26-82f75cef4a51-1_aa5ec827-9e06-4b53-bccd-794a875965a4.jpg", + "https://www.backmarket.es/cdn-cgi/image/format%3Dauto%2Cquality%3D75%2Cwidth%3D1920/https://d2e6ccujb3mkqf.cloudfront.net/c1578906-2b57-4337-8e26-82f75cef4a51-3_5285f1db-dca5-4ea8-9c5e-d30aed595f92.jpg" + ] + }, + "marca": "Razer", + "memoria_ram": "32 GB", + "modelo": "Blade 15 (2023)", + "modelo_cpu": "Intel Core i9-13900H", + "pantalla": "OLED de 15.6 pulgadas, 3840 x 2160", + "precio": "2499 €", + "procesador": "Intel Core i9-13900H", + "sistema_operativo": "Windows 11 Pro", + "tecnologia": "Wi-Fi 6E, Bluetooth 5.3", + "funcion_especial": "Pantalla OLED con colores vibrantes", + "descripcion_tarjeta_grafica": "NVIDIA GeForce RTX 4080", + "tipo": "laptop" + + }, + { + "almacenamiento": "1 TB", + "bateria": "99 Wh, hasta 8 horas", + "camara": "720p", + "colores": [ + "Negro" + ], + "descripcion": "Portátil gaming de gran tamaño con una frecuencia de actualización extremadamente alta.", + "imagen": { + "negro": [ + "https://www.backmarket.es/cdn-cgi/image/format%3Dauto%2Cquality%3D75%2Cwidth%3D1920/https://d2e6ccujb3mkqf.cloudfront.net/402c9e17-2184-4d8b-8420-ce5e07d4c61d-1_83814f1d-045b-4af5-9cb5-5388a81bf7ca.jpg", + "https://www.backmarket.es/cdn-cgi/image/format%3Dauto%2Cquality%3D75%2Cwidth%3D1920/https://d2e6ccujb3mkqf.cloudfront.net/402c9e17-2184-4d8b-8420-ce5e07d4c61d-2_799ec614-9357-4999-b2fa-ca2c6e8b9025.jpg" + ] + }, + "marca": "MSI", + "memoria_ram": "32 GB", + "modelo": "GE76 Raider", + "modelo_cpu": "Intel Core i9-13900HK", + "pantalla": "IPS de 17.3 pulgadas, 1920 x 1080, 360 Hz", + "precio": "2399 €", + "procesador": "Intel Core i9-13900HK", + "sistema_operativo": "Windows 11 Home", + "tecnologia": "Wi-Fi 6E, Bluetooth 5.2", + "funcion_especial": "Pantalla 360 Hz, ideal para esports", + "descripcion_tarjeta_grafica": "NVIDIA GeForce RTX 4080", + "tipo": "laptop" + + }, + { + "almacenamiento": "1 TB", + "bateria": "94 Wh, hasta 10 horas", + "camara": "720p", + "colores": [ + "Negro" + ], + "descripcion": "Pantalla OLED y gran rendimiento para creadores de contenido.", + "imagen": { + "negro": [ + "https://img.pccomponentes.com/articles/24/246129/1.jpg", + "https://img.pccomponentes.com/articles/24/246129/2.jpg" + ] + }, + "marca": "Gigabyte", + "memoria_ram": "32 GB", + "modelo": "Aero 15 OLED", + "modelo_cpu": "Intel Core i9-13900HK", + "pantalla": "OLED de 15.6 pulgadas, 3840 x 2160", + "precio": "2199 €", + "procesador": "Intel Core i9-13900HK", + "sistema_operativo": "Windows 11 Pro", + "tecnologia": "Wi-Fi 6E, Bluetooth 5.2", + "funcion_especial": "Pantalla OLED perfecta para edición de video y diseño gráfico", + "descripcion_tarjeta_grafica": "NVIDIA GeForce RTX 4070", + "tipo": "laptop" + + }, + { + "almacenamiento": "2 TB", + "bateria": "76 Wh, hasta 14 horas", + "camara": "1080p", + "colores": [ + "Gris", + "Plata" + ], + "descripcion": "Portátil de alto rendimiento con pantalla AMOLED para profesionales exigentes.", + "imagen": { + "gris": [ + "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRgEvkRfewexs6ri2kmuq7QXPaKYErBTipasg&s", + "https://media.gq.com.mx/photos/63dad58e41b2cb1cf9be814b/1:1/w_2017,h_2017,c_limit/Samsung%20Galaxy%20Book3.jpg" + ], + "plata": [ + "https://thumb.pccomponentes.com/w-530-530/articles/1066/10663866/2211-samsung-galaxy-book3-intel-core-i5-1335u-8gb-512gb-ssd-156-comprar.jpg", + "https://media.gq.com.mx/photos/63dad58e41b2cb1cf9be814b/1:1/w_2017,h_2017,c_limit/Samsung%20Galaxy%20Book3.jpg" + ] + }, + "marca": "Samsung", + "memoria_ram": "64 GB", + "modelo": "Galaxy Book3 Ultra", + "modelo_cpu": "Intel Core i9-13900H", + "pantalla": "AMOLED de 16 pulgadas, 2560 x 1600", + "precio": "2999 €", + "procesador": "Intel Core i9-13900H", + "sistema_operativo": "Windows 11 Pro", + "tecnologia": "Wi-Fi 6E, Bluetooth 5.3", + "funcion_especial": "Pantalla AMOLED con colores vivos y excelentes ángulos de visión", + "descripcion_tarjeta_grafica": "NVIDIA GeForce RTX 4070", + "tipo": "laptop" + + }, + { + "almacenamiento": "1 TB", + "bateria": "60 Wh, hasta 10 horas", + "camara": "720p", + "colores": [ + "Plata" + ], + "descripcion": "Portátil ultraligero con gran rendimiento y diseño elegante.", + "imagen": { + "plata": [ + "https://img01.huaweifile.com/eu/es/huawei/pms/uomcdn/ESHW/pms/202407/gbom/6942103129650/800_800_530FC6C11DDCC5B430B1A3C3E977E125mp.png", + "https://img01.huaweifile.com/eu/es/huawei/pms/uomcdn/ESHW/pms/202407/gbom/6942103129650/group/800_800_AACCA3B572833F1357CBC6C3DAF85CDF.png" + ] + }, + "marca": "Huawei", + "memoria_ram": "32 GB", + "modelo": "MateBook X Pro (2023)", + "modelo_cpu": "Intel Core i7-13700H", + "pantalla": "IPS de 14.2 pulgadas, 3120 x 2080", + "precio": "1799 €", + "procesador": "Intel Core i7-13700H", + "sistema_operativo": "Windows 11 Pro", + "tecnologia": "Wi-Fi 6, Bluetooth 5.1", + "funcion_especial": "Cuerpo metálico y pantalla de alta resolución", + "descripcion_tarjeta_grafica": "Intel Iris Xe Graphics integrada", + "tipo": "laptop" + + }, + { + "almacenamiento": "1 TB", + "bateria": "87 Wh, hasta 9 horas", + "camara": "720p", + "colores": [ + "Negro" + ], + "descripcion": "Portátil gaming de alta gama con una impresionante pantalla y rendimiento.", + "imagen": { + "negro": [ + "https://img.pccomponentes.com/articles/1067/10670513/1615-asus-rog-zephyrus-m16-2023-gu604vy-nm001w-intel-core-i9-13900h-32gb-2tb-ssd-rtx-4090-16.jpg", + "https://thumb.pccomponentes.com/w-530-530/articles/1067/10670513/5996-asus-rog-zephyrus-m16-2023-gu604vy-nm001w-intel-core-i9-13900h-32gb-2tb-ssd-rtx-4090-16-caracteristicas.jpg" + ] + }, + "marca": "Alienware", + "memoria_ram": "32 GB", + "modelo": "M16 (2023)", + "modelo_cpu": "Intel Core i9-13900HX", + "pantalla": "IPS de 16 pulgadas, 2560 x 1600, 165 Hz", + "precio": "2399 €", + "procesador": "Intel Core i9-13900HX", + "sistema_operativo": "Windows 11 Home", + "tecnologia": "Wi-Fi 6E, Bluetooth 5.2", + "funcion_especial": "Máximo rendimiento para juegos AAA", + "descripcion_tarjeta_grafica": "NVIDIA GeForce RTX 4080", + "tipo": "laptop" + + }, + { + "almacenamiento": "512 GB", + "bateria": "56 Wh, hasta 12 horas", + "camara": "1080p", + "colores": [ + "Plata" + ], + "descripcion": "Un convertible 2 en 1 de gran calidad, ideal para profesionales creativos.", + "imagen": { + "plata": [ + "https://www.lotespc.es/wp-content/uploads/2024/01/MICROSOFT-SURFACE-PRO-4-.jpg", + "https://cdn.shopify.com/s/files/1/0680/4219/5247/files/image_9b0a6ff4-d184-49d1-aa3d-b4ead43589c9.png?v=1712603761&width=550&height=550&pad_color=ffffff" + ] + }, + "marca": "Microsoft", + "memoria_ram": "32 GB", + "modelo": "Surface Book 4", + "modelo_cpu": "Intel Core i7-1185G7", + "pantalla": "PixelSense de 13.5 pulgadas, 3000 x 2000", + "precio": "1799 €", + "procesador": "Intel Core i7-1185G7", + "sistema_operativo": "Windows 11 Home", + "tecnologia": "Wi-Fi 6, Bluetooth 5.1", + "funcion_especial": "Pantalla táctil con gran resolución y diseño flexible", + "descripcion_tarjeta_grafica": "Intel Iris Xe Graphics integrada", + "tipo": "laptop" + + }, + { + "almacenamiento": "256 GB", + "bateria": "47 Wh, hasta 12 horas", + "camara": "1080p", + "colores": [ + "Just Black", + "Not Pink" + ], + "descripcion": "Portátil ultraportátil con Chrome OS, ideal para tareas diarias y productividad.", + "imagen": { + "just_black": [ + "https://i.ebayimg.com/images/g/K-YAAOSwlwZjm0~3/s-l1200.jpg", + "https://i.ebayimg.com/images/g/UFcAAOSwQlZeODIh/s-l1600.webp" + ], + "not_pink": [ + "https://www.notebookcheck.net/fileadmin/Notebooks/News/_nc3/go_top.PNG", + "https://img.tuttoandroid.net/wp-content/uploads/2019/10/Google-Pixelbook-Go-tastiera-635x392.jpg" + ] + }, + "marca": "Google", + "memoria_ram": "8 GB", + "modelo": "Pixelbook Go", + "modelo_cpu": "Intel Core i5-8200Y", + "pantalla": "Full HD de 13.3 pulgadas, 1920 x 1080", + "precio": "649 €", + "procesador": "Intel Core i5-8200Y", + "sistema_operativo": "Chrome OS", + "tecnologia": "Wi-Fi 5, Bluetooth 4.2", + "funcion_especial": "Ligero y con una batería de larga duración", + "descripcion_tarjeta_grafica": "Intel UHD Graphics 615", + "tipo": "laptop" + } +] \ No newline at end of file diff --git a/src/api/productos/phones.py b/src/api/productos/phones.py new file mode 100644 index 0000000000..ea02db0bbb --- /dev/null +++ b/src/api/productos/phones.py @@ -0,0 +1,462 @@ +phones = [ + { + "almacenamiento": "512 GB", + "bateria": "5000 mAh, carga rápida de 65W", + "camara": "200 MP", + "colores": [ + "Negro", + "Plata", + "Azul" + ], + "conectividad": "5G, Wi-Fi 7, Bluetooth 5.3", + "descripcion": "El buque insignia de Samsung con una cámara de 200 MP y un rendimiento excepcional.", + "imagen": { + "azul": [ + "https://static.fnac-static.com/multimedia/Images/ES/NR/69/b7/92/9615209/1540-1.jpg", + "https://static.fnac-static.com/multimedia/Images/ES/NR/6e/b7/92/9615214/1520-4.jpg" + ], + "negro": [ + "https://static.fnac-static.com/multimedia/Images/ES/NR/6e/b7/92/9615214/1540-1.jpg", + "https://static.fnac-static.com/multimedia/Images/ES/NR/6e/b7/92/9615214/1520-4.jpg" + ], + "plata": [ + "https://static.fnac-static.com/multimedia/Images/ES/NR/6c/b7/92/9615212/1540-1.jpg", + "https://static.fnac-static.com/multimedia/Images/ES/NR/6e/b7/92/9615214/1520-4.jpg" + ] + }, + "memoria_ram": "12 GB", + "modelo": "Samsung Galaxy S25 Ultra", + "pantalla": "Dynamic AMOLED 2X de 6.8 pulgadas, 120 Hz", + "precio": "1299 €", + "procesador": "Snapdragon 8 Elite", + "tipo": "smartphone" + + }, + { + "almacenamiento": " 256 GB", + "bateria": "Hasta 26 horas de video", + "camara": "48 MP", + "colores": [ + "Blanco", + "negro" + ], + "conectividad": "5G, Wi-Fi 6E, Bluetooth 5.3", + "descripcion": "El modelo más accesible de Apple con gran rendimiento y cámara mejorada.", + "imagen": { + "blanco": [ + "https://static.fnac-static.com/multimedia/Images/ES/NR/68/31/95/9777512/1540-1.jpg", + "https://static.fnac-static.com/multimedia/Images/ES/NR/68/31/95/9777512/1541-4.jpg" + ], + "negro": [ + "https://static.fnac-static.com/multimedia/Images/ES/NR/67/31/95/9777511/1540-1.jpg", + "https://static.fnac-static.com/multimedia/Images/ES/NR/68/31/95/9777512/1541-4.jpg" + ], + }, + "memoria_ram": " 8 GB", + "modelo": "Apple iPhone 16e", + "pantalla": "Super Retina XDR OLED de 6.1 pulgadas", + "precio": "709 €", + "procesador": "Chip A18 Bionic", + "tipo": "smartphone" + + }, + { + "almacenamiento": " 512 GB", + "bateria": "5000 mAh, carga rápida de 30W", + "camara": "50 MP", + "colores": [ + "Negro", + "Verde", + "Blanco" + ], + "conectividad": "5G, Wi-Fi 7, Bluetooth 5.2", + "descripcion": "Un Android puro con una cámara excepcional y software impulsado por IA.", + "imagen": { + "blanco": [ + "https://cdn.grupoelcorteingles.es/SGFM/dctm/MEDIA03/202408/08/00157063609564009_24__1200x1200.jpg", + "https://med.greatecno.com/1284222/google-pixel-9-pro-5g-16gb-128gb-dual-sim-negro-obsidiana.jpg" + ], + "negro": [ + "https://www.maxmovil.com/media/catalog/product/cache/2c055c968235f5bf43b443aee4bb62c6/g/o/google_pixel_9_pro_negro_6_.jpg", + "https://med.greatecno.com/1284222/google-pixel-9-pro-5g-16gb-128gb-dual-sim-negro-obsidiana.jpg" + ], + "verde": [ + "https://thumb.pccomponentes.com/w-530-530/articles/1085/10857221/1817-google-pixel-9-5g-12-128gb-verde-pastel-libre.jpg", + "https://med.greatecno.com/1284222/google-pixel-9-pro-5g-16gb-128gb-dual-sim-negro-obsidiana.jpg" + ] + }, + "memoria_ram": "12 GB", + "modelo": "Google Pixel 9 Pro", + "pantalla": "OLED de 6.7 pulgadas", + "precio": "999 €", + "procesador": "Tensor G3", + "tipo": "smartphone" + + }, + { + "almacenamiento": "256 GB", + "bateria": "4500 mAh, carga rápida de 66W", + "camara": "50 MP", + "colores": [ + "Beige", + "Negro", + "Azul" + ], + "conectividad": "5G, Wi-Fi 6, Bluetooth 5.2", + "descripcion": "Un smartphone elegante con una potente cámara y carga rápida.", + "imagen": { + "azul": [ + "https://img4.dhresource.com/webp/m/0x0/f3/albu/km/s/18/b00a7897-1c23-4dbd-ba3b-67f8eeff9193.jpg", + "https://m.media-amazon.com/images/I/617fdVoR+gL.jpg" + ], + "beige": [ + "https://cdn.idealo.com/folder/Product/202749/9/202749936/s11_produktbild_max/huawei-p60-pro-256gb-rococo-pearl.jpg", + "https://m.media-amazon.com/images/I/617fdVoR+gL.jpg" + ], + "negro": [ + "https://cdn.idealo.com/folder/Product/202749/9/202749935/s11_produktbild_max/huawei-p60-pro-256gb-black.jpg", + "https://m.media-amazon.com/images/I/617fdVoR+gL.jpg" + ] + }, + "memoria_ram": " 12 GB", + "modelo": "Huawei P60 Pro", + "pantalla": "OLED de 6.6 pulgadas", + "precio": "1099 €", + "procesador": "Kirin 9900", + "tipo": "smartphone" + + }, + { + "almacenamiento": "512 GB", + "bateria": "5000 mAh, carga rápida de 150W", + "camara": "108 MP", + "colores": [ + "Blanco", + "Negro", + "Gris Titanio" + ], + "conectividad": "5G, Wi-Fi 7, Bluetooth 5.3", + "descripcion": "Un teléfono potente con carga ultrarrápida y una cámara impresionante.", + "imagen": { + "blanco": [ + "https://m.media-amazon.com/images/I/51yCY4G3OkL._AC_UF894,1000_QL80_.jpg", + "https://media1.allzone.es/3598456-large_default/xiaomi-14t-pro-5g-dual-sim-12gb-ram-512gb-gris.jpg" + ], + "negro": [ + "https://m.media-amazon.com/images/I/51N4Aww3+SL.jpg", + "https://media1.allzone.es/3598456-large_default/xiaomi-14t-pro-5g-dual-sim-12gb-ram-512gb-gris.jpg" + ], + "gris_titanio": [ + "https://thumb.pccomponentes.com/w-530-530/articles/1087/10872500/1747-xiaomi-14t-pro-12gb-256gb-gris-titanio-libre-foto.jpg", + "https://media1.allzone.es/3598456-large_default/xiaomi-14t-pro-5g-dual-sim-12gb-ram-512gb-gris.jpg" + ] + }, + "memoria_ram": "12 GB", + "modelo": "Xiaomi Mi 14 Pro", + "pantalla": "AMOLED de 6.7 pulgadas", + "precio": "899 €", + "procesador": "Snapdragon 8 Gen 4", + "tipo": "smartphone" + + }, + { + "almacenamiento": "256 GB, 512 GB", + "bateria": "5000 mAh, carga rápida de 80W", + "camara": "50 MP", + "colores": [ + "Negro", + "Verde", + "Plata" + ], + "conectividad": "5G, Wi-Fi 7, Bluetooth 5.3", + "descripcion": "Dispositivo de alto rendimiento con carga rápida y cámara de alta calidad.", + "imagen": { + "negro": [ + "https://m.media-amazon.com/images/I/71o8VehMHXL.jpg", + "https://s.alicdn.com/@sc04/kf/H2d6848955f9f4d3688ca799e91da94c1b.png_720x720q50.png" + ], + "plata": [ + "https://storage.googleapis.com/catalog-pictures-carrefour-es/catalog/pictures/hd_510x_/6932169340281_1.jpg", + "https://s.alicdn.com/@sc04/kf/H2d6848955f9f4d3688ca799e91da94c1b.png_720x720q50.png" + ], + "verde": [ + "https://ae-pic-a1.aliexpress-media.com/kf/S5e4ec5b5b6464682a2660f61a237e7c72.png_960x960.png_.webp", + "https://s.alicdn.com/@sc04/kf/H2d6848955f9f4d3688ca799e91da94c1b.png_720x720q50.png" + ] + }, + "memoria_ram": "12 GB, 16 GB", + "modelo": "OnePlus 12 Pro", + "pantalla": "6.7\" Fluid AMOLED, 120 Hz", + "precio": "1099 €", + "procesador": "Snapdragon 8 Gen 3", + "tipo": "smartphone" + + }, + { + "almacenamiento": "256 GB, 512 GB", + "bateria": "5000 mAh, carga rápida de 30W", + "camara": "48 MP", + "colores": [ + "Negro", + "Morado" + ], + "conectividad": "5G, Wi-Fi 6, Bluetooth 5.2", + "descripcion": "Un teléfono orientado a medios con una increíble pantalla 4K y sistema de cámara premium.", + "imagen": { + "morado": [ + "https://imagedelivery.net/JAV112JY973Crznn4xb8Sg/19fda8bc-34d7-4f5a-1cdc-4ca45f7f2d00/mobile", + "https://img.youtube.com/vi/BDjAz7E7SFo/sddefault.jpg" + ], + "negro": [ + "https://assets.mmsrg.com/isr/166325/c1/-/ASSET_MP_137324892?x=536&y=402&format=jpg&quality=80&sp=yes&strip=yes&trim&ex=536&ey=402&align=center&resizesource&unsharp=1.5x1+0.7+0.02&cox=0&coy=0&cdx=536&cdy=402", + "https://img.youtube.com/vi/BDjAz7E7SFo/sddefault.jpg" + ] + }, + "memoria_ram": "12 GB, 16 GB", + "modelo": "Sony Xperia 1 V", + "pantalla": "6.5\" 4K OLED, 120 Hz", + "precio": "1299 €", + "procesador": "Snapdragon 8 Gen 2", + "tipo": "smartphone" + + }, + { + "almacenamiento": "256 GB, 512 GB", + "bateria": "5000 mAh, carga rápida de 100W", + "camara": "50 MP", + "colores": [ + "Negro", + "Verde", + "Marron" + ], + "conectividad": "5G, Wi-Fi 7, Bluetooth 5.3", + "descripcion": "El buque insignia de Oppo con carga extremadamente rápida y una cámara robusta.", + "imagen": { + "negro": [ + "https://i.ebayimg.com/images/g/jQ8AAOSwh~BkG8du/s-l1600.webp", + "https://platform.theverge.com/wp-content/uploads/sites/2/chorus/uploads/chorus_asset/file/24522914/13.jpg?quality=90&strip=all&crop=0%2C0%2C100%2C100&w=2400" + ], + "marron": [ + "https://i.ebayimg.com/images/g/S~EAAOSwCxZkG9Om/s-l1600.webp", + "https://platform.theverge.com/wp-content/uploads/sites/2/chorus/uploads/chorus_asset/file/24522914/13.jpg?quality=90&strip=all&crop=0%2C0%2C100%2C100&w=2400" + ], + "verde": [ + "https://storage.googleapis.com/catalog-pictures-carrefour-es/catalog/pictures/hd_510x_/7427285919026_1.jpg", + "https://platform.theverge.com/wp-content/uploads/sites/2/chorus/uploads/chorus_asset/file/24522914/13.jpg?quality=90&strip=all&crop=0%2C0%2C100%2C100&w=2400" + ] + }, + "memoria_ram": "12 GB, 16 GB", + "modelo": "Oppo Find X6 Pro", + "pantalla": "6.7\" AMOLED, 120 Hz", + "precio": "1099 €", + "procesador": "Snapdragon 8 Gen 3", + "tipo": "smartphone" + + }, + { + "almacenamiento": "256 GB, 512 GB", + "bateria": "4600 mAh, carga rápida de 125W", + "camara": "50 MP", + "colores": [ + "Negro", + "Azul" + ], + "conectividad": "5G, Wi-Fi 6E, Bluetooth 5.3", + "descripcion": "Rendimiento sobresaliente con una pantalla de 144 Hz y carga ultrarrápida.", + "imagen": { + "azul": [ + "https://www.maxmovil.com/media/catalog/product/cache/2c055c968235f5bf43b443aee4bb62c6/m/o/motorola-edge40pro_0005_motorola_edge_40_pro_azul-2_1.jpg", + "https://motorolaimgrepo.vtexassets.com/arquivos/motorola-edge-40-pro-pdp-battery-static-kwy7105w.jpg" + ], + "negro": [ + "https://m.media-amazon.com/images/I/71hVkWzI8gL._AC_UF894,1000_QL80_.jpg", + "https://motorolaimgrepo.vtexassets.com/arquivos/motorola-edge-40-pro-pdp-battery-static-kwy7105w.jpg" + ] + }, + "memoria_ram": "8 GB, 12 GB", + "modelo": "Motorola Edge 40 Pro", + "pantalla": "6.6\" OLED, 144 Hz", + "precio": "799 €", + "procesador": "Snapdragon 8 Gen 2", + "tipo": "smartphone" + + }, + { + "almacenamiento": "256 GB, 512 GB", + "bateria": "5000 mAh, carga rápida de 65W", + "camara": "50 MP", + "colores": [ + "Blanco", + "Negro" + ], + "conectividad": "5G, Wi-Fi 6, Bluetooth 5.2", + "descripcion": "Un flagship asequible con gran carga rápida y sólido rendimiento.", + "imagen": { + "blanco": [ + "https://img.pccomponentes.com/articles/1019/10194260/1689-realme-gt2-pro-12-256gb-blanco-libre.jpg", + "https://wwwhatsnew.com/wp-content/uploads/2022/02/realme-gt-2-pro.jpg" + ], + "negro": [ + "https://img.pccomponentes.com/articles/73/738087/179-realme-gt-neo-2-5g-12-256gb-negro-libre-0555f44e-b1e2-4b83-9329-b337e5505938.jpg", + "https://wwwhatsnew.com/wp-content/uploads/2022/02/realme-gt-2-pro.jpg" + ] + }, + "memoria_ram": "12 GB, 16 GB", + "modelo": "Realme GT 2 Pro", + "pantalla": "6.7\" AMOLED, 120 Hz", + "precio": "899 €", + "procesador": "Snapdragon 8 Gen 1", + "tipo": "smartphone" + + }, + { + "almacenamiento": "128 GB, 256 GB", + "bateria": "4300 mAh, carga rápida de 65W", + "camara": "50 MP", + "colores": [ + "Negro", + "Azul", + "Rojo" + ], + "conectividad": "5G, Wi-Fi 6, Bluetooth 5.3", + "descripcion": "Un teléfono compacto y potente con un rendimiento excepcional y cámara sólida.", + "imagen": { + "azul": [ + "https://www.backmarket.es/cdn-cgi/image/format%3Dauto%2Cquality%3D75%2Cwidth%3D260/https://d2e6ccujb3mkqf.cloudfront.net/e91e0429-4ca4-4214-9c20-18e268f8237b-1_ed55fdb2-c400-447a-8e9a-77b75dfe43d6.jpg", + "https://www.worten.es/i/e12f1a12b18cec30b6d624e9244dc5fe814ada11" + ], + "negro": [ + "https://static.fnac-static.com/multimedia/Images/ES/MC/e3/e8/90/9496803/1540-1/tsp20250205131116/Asus-Zenfone-10-5G-8GB-256GB-5-92-Negro.jpg#d832b95b-be9d-4a7c-a54a-bd9c138acfc1", + "https://www.worten.es/i/e12f1a12b18cec30b6d624e9244dc5fe814ada11" + ], + "rojo": [ + "https://m.media-amazon.com/images/I/7159LdMFddL.jpg", + "https://www.worten.es/i/e12f1a12b18cec30b6d624e9244dc5fe814ada11" + ] + }, + "memoria_ram": "8 GB, 12 GB", + "modelo": "Asus Zenfone 10", + "pantalla": "6.3\" AMOLED, 120 Hz", + "precio": "799 €", + "procesador": "Snapdragon 8 Gen 3", + "tipo": "smartphone" + + }, + { + "almacenamiento": "256 GB, 512 GB", + "bateria": "4870 mAh, carga rápida de 120W", + "camara": "50 MP", + "colores": [ + "Negro", + "Rojo" + ], + "conectividad": "5G, Wi-Fi 7, Bluetooth 5.3", + "descripcion": "Un teléfono elegante con enfoque en fotografía y carga rápida.", + "imagen": { + "negro": [ + "https://d2e6ccujb3mkqf.cloudfront.net/341ce9bb-b44e-49e1-82a3-cb578fb22428-1_c8bed3c2-691c-44a7-b3f5-f3218cb46e90.jpg", + "https://i.blogs.es/18d976/fhg23acxoaijtel/840_560.jpeg" + ], + "rojo": [ + "https://img.myipadbox.com/upload/store/product_l/SYA002254203B.jpg", + "https://i.blogs.es/18d976/fhg23acxoaijtel/840_560.jpeg" + ] + }, + "memoria_ram": "12 GB, 16 GB", + "modelo": "Vivo X90 Pro", + "pantalla": "6.78\" AMOLED, 120 Hz", + "precio": "1099 €", + "procesador": "Snapdragon 8 Gen 2", + "tipo": "smartphone" + + }, + { + "almacenamiento": "128 GB, 256 GB", + "bateria": "4700 mAh, carga rápida de 45W", + "camara": "50 MP", + "colores": [ + "Negro", + "Blanco" + ], + "conectividad": "5G, Wi-Fi 6, Bluetooth 5.2", + "descripcion": "Diseño elegante con una gran experiencia de software y buen rendimiento.", + "imagen": { + "blanco": [ + "https://img.pccomponentes.com/articles/1081/10819282/1716-nothing-phone-2a-5g-8-128gb-blanco-libre.jpg", + "https://static.fnac-static.com/multimedia/Images/ES/NR/b1/d5/81/8508849/1520-6.jpg" + ], + "negro": [ + "https://thumb.pccomponentes.com/w-530-530/articles/1081/10819283/1132-nothing-phone-2a-5g-12-256gb-negro-libre.jpg", + "https://static.fnac-static.com/multimedia/Images/ES/NR/ae/d5/81/8508846/1520-6.jpg" + ] + }, + "memoria_ram": "8 GB, 12 GB", + "modelo": "Nothing Phone (2)", + "pantalla": "6.7\" OLED, 120 Hz", + "precio": "799 €", + "procesador": "Snapdragon 8+ Gen 1", + "tipo": "smartphone" + + }, + { + "almacenamiento": "128 GB, 256 GB", + "bateria": "5000 mAh, carga rápida de 65W", + "camara": "50 MP", + "colores": [ + "Blanco", + "Azul" + ], + "conectividad": "5G, Wi-Fi 6, Bluetooth 5.2", + "descripcion": "Un teléfono de gama alta asequible con buen rendimiento y cámara.", + "imagen": { + "azul": [ + "https://m.media-amazon.com/images/I/618zfspRt7L.jpg", + "https://one-tech.es/wp-content/uploads/2022/02/realme-Gt-2-Pro-Lifestyle-12-1000x715.jpg" + ], + "blanco": [ + "https://img.pccomponentes.com/articles/1019/10194269/1194-realme-gt2-pro-12-256gb-negro-libre-foto.jpg", + "https://one-tech.es/wp-content/uploads/2022/02/realme-Gt-2-Pro-Lifestyle-12-1000x715.jpg" + ] + }, + "memoria_ram": "8 GB, 12 GB", + "modelo": "Realme GT 2", + "pantalla": "6.62\" AMOLED, 120 Hz", + "precio": "699 €", + "procesador": "Snapdragon 8 Gen 1", + "tipo": "smartphone" + + }, + { + "almacenamiento": "256 GB, 512 GB", + "bateria": "5000 mAh, carga rápida de 90W", + "camara": "200 MP", + "colores": [ + "Negro", + "Blanco", + "Verde" + ], + "conectividad": "5G, Wi-Fi 7, Bluetooth 5.3", + "descripcion": "Un dispositivo impresionante con una cámara de 200 MP y carga extremadamente rápida.", + "imagen": { + "blanco": [ + "https://cdn.andro4all.com/andro4all/2023/04/xiaomi-13-ultra-blanco-5.jpg?height=600", + "https://resources.claroshop.com/medios-plazavip/mkt/655fabd7bff0e_xiaomi-13-ultra-negro-4jpg.jpg" + ], + "negro": [ + "https://assets.mmsrg.com/isr/166325/c1/-/ASSET_MMS_106806523?x=536&y=402&format=jpg&quality=80&sp=yes&strip=yes&trim&ex=536&ey=402&align=center&resizesource&unsharp=1.5x1+0.7+0.02&cox=0&coy=0&cdx=536&cdy=402", + "https://resources.claroshop.com/medios-plazavip/mkt/655fabd7bff0e_xiaomi-13-ultra-negro-4jpg.jpg" + ], + "verde": [ + "https://i.ebayimg.com/images/g/toMAAOSwoklkP04F/s-l1200.jpg", + "https://resources.claroshop.com/medios-plazavip/mkt/655fabd7bff0e_xiaomi-13-ultra-negro-4jpg.jpg" + ] + }, + "memoria_ram": "12 GB, 16 GB", + "modelo": "Xiaomi 13 Ultra", + "pantalla": "AMOLED de 6.73 pulgadas, 120 Hz", + "precio": "1399 €", + "procesador": "Snapdragon 8 Gen 2", + "tipo": "smartphone" + + } +] \ No newline at end of file diff --git a/src/api/productos/tv.py b/src/api/productos/tv.py new file mode 100644 index 0000000000..8f40b52159 --- /dev/null +++ b/src/api/productos/tv.py @@ -0,0 +1,287 @@ +tvs = [ + { + "año_modelo": "2024", + "conectividad": "HDMI 2.1, Wi-Fi, Bluetooth, USB", + "contenido_de_la_caja": "Televisor, mando a distancia, cable de alimentación, manual de usuario", + "descripcion": "Televisor 4K con tecnología Neo QLED para un contraste mejorado y colores más vivos.", + "fabricante": "Samsung Electronics", + "imagen": [ + "https://www.bhphotovideo.com/images/images2500x2500/samsung_qn65qn90cafxza_neo_qled_qn90c_65_1742740.jpg", + "https://cdn.cs.1worldsync.com/61/68/6168b570-740a-4a17-80e2-c7bf496f49c6.jpg" + ], + "marca": "Samsung", + "medidas": "1230 x 710 x 28 mm", + "modelo": "QN90C Neo QLED", + "pantalla": "55 pulgadas", + "precio": "1499 €", + "usos_recomendados": "Visualización de contenido en 4K, juegos, cine en casa", + "tipo": "tv" + + }, + { + "año_modelo": "2024", + "conectividad": "HDMI 2.1, Wi-Fi, Bluetooth, USB", + "contenido_de_la_caja": "Televisor, mando a distancia, cable de alimentación, manual de usuario", + "descripcion": "OLED con negros profundos, excelente calidad de imagen y sonido envolvente.", + "fabricante": "LG Electronics", + "imagen": [ + "https://pisces.bbystatic.com/image2/BestBuy_US/images/products/6534/6534584cv2d.jpg", + "https://cdn.mos.cms.futurecdn.net/RsAZMsVDJxqSWbdPRP43kh-1200-80.jpg" + ], + "marca": "LG", + "medidas": "1445 x 835 x 25 mm", + "modelo": "OLED C3", + "pantalla": "65 pulgadas", + "precio": "1799 €", + "usos_recomendados": "Cine en casa, visualización de contenido HDR, gaming", + "tipo": "tv" + + }, + { + "año_modelo": "2023", + "conectividad": "HDMI 2.1, Wi-Fi, Bluetooth, USB", + "contenido_de_la_caja": "Televisor, mando a distancia, cable de alimentación, manual de usuario", + "descripcion": "OLED premium con calidad de imagen excepcional y sonido inmersivo.", + "fabricante": "Sony Corporation", + "imagen": [ + "https://cdn.shopify.com/s/files/1/0340/3975/8907/products/XR65A90J_1_257e7966-6983-4f25-8959-8b32c6bd0d57_4000x4000.jpg?v=1617287657", + "https://www.digitaltrends.com/wp-content/uploads/2021/03/sony-a90j-review-ry-13.jpg?p=1" + ], + "marca": "Sony", + "medidas": "1225 x 710 x 35 mm", + "modelo": "Bravia XR A90J", + "pantalla": "55 pulgadas", + "precio": "2499 €", + "usos_recomendados": "Cine en casa, visualización de contenido HDR, gaming", + "tipo": "tv" + + }, + { + "año_modelo": "2024", + "conectividad": "HDMI 2.0, Wi-Fi, Bluetooth, USB", + "contenido_de_la_caja": "Televisor, mando a distancia, cable de alimentación, manual de usuario", + "descripcion": "QLED con alto brillo y contraste, ideal para ver en ambientes iluminados.", + "fabricante": "Samsung Electronics", + "imagen": [ + "https://cdn.idealo.com/folder/Product/202451/3/202451345/s11_produktbild_gross/samsung-tq75q80catxxc.jpg", + "https://thumb.pccomponentes.com/w-530-530/articles/1067/10675443/2606-samsung-tq75q80catxxc-75-qled-ultrahd-4k-quantum-hdr-comprar.jpg" + ], + "marca": "Samsung", + "medidas": "1230 x 720 x 30 mm", + "modelo": "Q80C QLED", + "pantalla": "55 pulgadas", + "precio": "1499 €", + "usos_recomendados": "Visualización de contenido 4K, deportes, cine en casa", + "tipo": "tv" + + }, + { + "año_modelo": "2023", + "conectividad": "HDMI 2.0, Wi-Fi, Bluetooth, USB", + "contenido_de_la_caja": "Televisor, mando a distancia, cable de alimentación, manual de usuario", + "descripcion": "Excelente relación calidad-precio con una gran experiencia HDR y calidad de imagen.", + "fabricante": "TCL Electronics", + "imagen": [ + "https://www.tcl.com/usca/content/dam/tcl/products/home-theater/6-series/6-series-qled.png", + "https://www.tcl.com/usca/content/dam/tcl/product/home-theater/6-series/super-bowl-screen-fill/R635-NFL-rightangle.png" + ], + "marca": "TCL", + "medidas": "1230 x 710 x 25 mm", + "modelo": "6 Series", + "pantalla": "55 pulgadas", + "precio": "799 €", + "usos_recomendados": "Cine en casa, contenido en 4K, entretenimiento", + "tipo": "tv" + + }, + { + "año_modelo": "2023", + "conectividad": "HDMI 2.1, Wi-Fi, Bluetooth, USB", + "contenido_de_la_caja": "Televisor, mando a distancia, cable de alimentación, manual de usuario", + "descripcion": "QLED con gran calidad de imagen, colores brillantes y negros profundos.", + "fabricante": "Vizio Inc.", + "imagen": [ + "https://m.media-amazon.com/images/I/71ELl5K0blL._AC_SL1000_.jpg", + "https://m.media-amazon.com/images/I/71tT3DU-BsL._AC_SL1000_.jpg" + ], + "marca": "Vizio", + "medidas": "1450 x 830 x 35 mm", + "modelo": "P-Series Quantum", + "pantalla": "65 pulgadas", + "precio": "1299 €", + "usos_recomendados": "Cine en casa, contenido 4K, juegos", + "tipo": "tv" + + }, + { + "año_modelo": "2024", + "conectividad": "HDMI 2.1, Wi-Fi, Bluetooth, USB", + "contenido_de_la_caja": "Televisor, mando a distancia, cable de alimentación, manual de usuario", + "descripcion": "OLED con Ambilight para una experiencia de inmersión total y colores brillantes.", + "fabricante": "Philips", + "imagen": [ + "https://www.kamera-express.be/_ipx/b_%23ffffff00,f_webp,fit_contain,s_484x484/https://www.kamera-express.nl/media/9dd5b1e0-2207-44a5-a50f-e7ecbadce877/philips-the-one-43pus8507-ambilight-google-.jpg", + "https://www.kamera-express.be/_ipx/b_%23ffffff00,f_webp,fit_contain,s_484x484/https://www.kamera-express.nl/media/36542099-9256-445a-8c8f-a696cf1c732b/philips-8507-foto4.jpg" + ], + "marca": "Philips", + "medidas": "1450 x 850 x 30 mm", + "modelo": "Ambilight 9300", + "pantalla": "65 pulgadas", + "precio": "1699 €", + "usos_recomendados": "Cine en casa, visualización de contenido HDR, entretenimiento", + "tipo": "tv" + + }, + { + "año_modelo": "2023", + "conectividad": "HDMI 2.1, Wi-Fi, Bluetooth, USB", + "contenido_de_la_caja": "Televisor, mando a distancia, cable de alimentación, manual de usuario", + "descripcion": "Excelente rendimiento en brillo y contraste con tecnología ULED.", + "fabricante": "Hisense", + "imagen": [ + "https://pisces.bbystatic.com/image2/BestBuy_US/images/products/2f3813fb-c093-44f3-b294-787aa28a7dc5.jpg;maxHeight=2000;maxWidth=2000;format=webp", + "https://pisces.bbystatic.com/image2/BestBuy_US/images/products/fc694997-2f59-475c-b080-4808a38dace9.jpg;maxHeight=2000;maxWidth=2000;format=webp" + ], + "marca": "Hisense", + "medidas": "1230 x 710 x 30 mm", + "modelo": "U8H", + "pantalla": "55 pulgadas", + "precio": "999 €", + "usos_recomendados": "Cine en casa, contenido HDR, juegos", + "tipo": "tv" + + }, + { + "año_modelo": "2024", + "conectividad": "HDMI 2.1, Wi-Fi, Bluetooth, USB", + "contenido_de_la_caja": "Televisor, mando a distancia, cable de alimentación, manual de usuario", + "descripcion": "Un televisor que también sirve como una obra de arte con su modo Art.", + "fabricante": "Samsung Electronics", + "imagen": [ + "https://images.samsung.com/is/image/samsung/p6pim/es/feature/165207772/es-feature-bring-world-famous-galleries-into-your-living-room-541088666?$FB_TYPE_A_MO_JPG$", + "https://images.samsung.com/is/image/samsung/assets/uk/lifestyle-tvs/the-frame/highlights/2024/2024-the-frame-why-the-frame-f07-1-the-frame-mo.jpg?$720_N_JPG$" + ], + "marca": "Samsung", + "medidas": "1230 x 720 x 45 mm", + "modelo": "The Frame", + "pantalla": "55 pulgadas", + "precio": "1499 €", + "usos_recomendados": "Visualización de contenido 4K, decoración, arte", + "tipo": "tv" + + }, + { + "año_modelo": "2024", + "conectividad": "HDMI 2.1, Wi-Fi, Bluetooth, USB", + "contenido_de_la_caja": "Televisor, mando a distancia, cable de alimentación, manual de usuario", + "descripcion": "Tecnología NanoCell para colores más reales y vibrantes.", + "fabricante": "LG Electronics", + "imagen": [ + "https://img.pccomponentes.com/articles/1024/10241245/152-lg-50nano826qb-50-led-nanocell-ultrahd-4k-hdr10-pro.jpg", + "https://img.pccomponentes.com/articles/1024/10241245/4823-lg-50nano826qb-50-led-nanocell-ultrahd-4k-hdr10-pro-especificaciones.jpg" + ], + "marca": "LG", + "medidas": "1450 x 835 x 25 mm", + "modelo": "NanoCell NANO90", + "pantalla": "65 pulgadas", + "precio": "1199 €", + "usos_recomendados": "Visualización de contenido 4K, cine en casa, entretenimiento", + "tipo": "tv" + + }, + { + "año_modelo": "2023", + "conectividad": "HDMI 2.1, Wi-Fi, Bluetooth, USB", + "contenido_de_la_caja": "Televisor, mando a distancia, cable de alimentación, manual de usuario", + "descripcion": "Gran calidad de imagen con el procesador XR y un excelente sonido.", + "fabricante": "Sony Corporation", + "imagen": [ + "https://media.ldlc.com/r1600/ld/products/00/05/94/73/LD0005947300.jpg", + "https://assets.mmsrg.com/isr/166325/c1/-/ASSET_MMS_88351334?x=536&y=402&format=jpg&quality=80&sp=yes&strip=yes&trim&ex=536&ey=402&align=center&resizesource&unsharp=1.5x1+0.7+0.02&cox=0&coy=0&cdx=536&cdy=402" + ], + "marca": "Sony", + "medidas": "1225 x 715 x 30 mm", + "modelo": "X90J", + "pantalla": "55 pulgadas", + "precio": "1099 €", + "usos_recomendados": "Cine en casa, contenido 4K, entretenimiento", + "tipo": "tv" + + }, + { + "año_modelo": "2023", + "conectividad": "HDMI 2.0, Wi-Fi, Bluetooth, USB", + "contenido_de_la_caja": "Televisor, mando a distancia, cable de alimentación, manual de usuario", + "descripcion": "Una opción asequible con buenas características y calidad de imagen 4K.", + "fabricante": "TCL Electronics", + "imagen": [ + "https://m.media-amazon.com/images/I/812deNcFfVL._AC_SL1500_.jpg", + "https://m.media-amazon.com/images/I/81DwSazR7gL._AC_SL1500_.jpg" + ], + "marca": "TCL", + "medidas": "1100 x 640 x 30 mm", + "modelo": "4 Series", + "pantalla": "50 pulgadas", + "precio": "499 €", + "usos_recomendados": "Visualización de contenido 4K, entretenimiento", + "tipo": "tv" + + }, + { + "año_modelo": "2023", + "conectividad": "HDMI 2.1, Wi-Fi, Bluetooth, USB", + "contenido_de_la_caja": "Televisor, mando a distancia, cable de alimentación, manual de usuario", + "descripcion": "OLED con la calidad de imagen más precisa y un sonido excepcional.", + "fabricante": "Panasonic", + "imagen": [ + "https://unilet.net/wp-content/uploads/2022/01/unilet_panasoncic_jz1000b_1.jpg", + "https://unilet.net/wp-content/uploads/2022/01/unilet_panasoncic_jz1000b_5.jpg" + ], + "marca": "Panasonic", + "medidas": "1455 x 845 x 35 mm", + "modelo": "TX-65JZ1000B", + "pantalla": "65 pulgadas", + "precio": "2399 €", + "usos_recomendados": "Cine en casa, visualización de contenido HDR", + "tipo": "tv" + + }, + { + "año_modelo": "2023", + "conectividad": "HDMI 2.1, Wi-Fi, Bluetooth, USB", + "contenido_de_la_caja": "Televisor, mando a distancia, cable de alimentación, manual de usuario", + "descripcion": "4K con una calidad de imagen impresionante y buena relación calidad-precio.", + "fabricante": "Sharp Electronics", + "imagen": [ + "https://cdn11.bigcommerce.com/s-8vy557m296/images/stencil/original/products/307/3348/30_4T-C65FS1UR_3QR_PRINT_WEB__19273.1698958571.JPG?c=2", + "https://res.cloudinary.com/sharp-consumer-eu/image/fetch/w_3000,f_auto/https://s3.infra.brandquad.io/accounts-media/SHRP/DAM/origin/aace5afa-6ce9-11ea-9355-e2581aee4843.jpg" + ], + "marca": "Sharp", + "medidas": "1300 x 780 x 30 mm", + "modelo": "AQUOS 4K", + "pantalla": "58 pulgadas", + "precio": "899 €", + "usos_recomendados": "Cine en casa, visualización de contenido 4K", + "tipo": "tv" + + }, + { + "año_modelo": "2023", + "conectividad": "HDMI 2.1, Wi-Fi, Bluetooth, USB", + "contenido_de_la_caja": "Televisor, mando a distancia, cable de alimentación, manual de usuario", + "descripcion": "ULED con una calidad de imagen sobresaliente y tecnología avanzada.", + "fabricante": "Hisense", + "imagen": [ + "https://m.media-amazon.com/images/I/81rcffEFH7L.jpg", + "https://images-na.ssl-images-amazon.com/images/I/51XB6CTlcwS._UL500_.jpg" + ], + "marca": "Hisense", + "medidas": "1450 x 835 x 30 mm", + "modelo": "U7G", + "pantalla": "65 pulgadas", + "precio": "749 €", + "usos_recomendados": "Visualización de contenido 4K, entretenimiento", + "tipo": "tv" + + } +] \ No newline at end of file diff --git a/src/api/routes.py b/src/api/routes.py index 029589a3a1..07df7bab34 100644 --- a/src/api/routes.py +++ b/src/api/routes.py @@ -2,21 +2,371 @@ This module takes care of starting the API Server, Loading the DB and Adding the endpoints """ from flask import Flask, request, jsonify, url_for, Blueprint -from api.models import db, User +from api.models import db, User, Smartphones, TVs, Laptops, Pedido from api.utils import generate_sitemap, APIException from flask_cors import CORS +import json +from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity +from api.productos.phones import phones +from api.productos.tv import tvs +from api.productos.laptops import laptops +import cloudinary +import cloudinary.uploader +import stripe +from sqlalchemy.orm.attributes import flag_modified + + api = Blueprint('api', __name__) +stripe.api_key = "sk_test_51QxWTcF1M5ixil84JujDUVjcNbDMjk8CpG3Akk0Bq1rlK7Ur5mVJkxZGUOJN78FO40hzxIiHJFMbfD4FKVhMrXJq00Q45N3TjR" # Allow CORS requests to this API CORS(api) -@api.route('/hello', methods=['POST', 'GET']) -def handle_hello(): - response_body = { - "message": "Hello! I'm a message that came from the backend, check the network tab on the google inspector and you will see the GET request" - } +#ENDPOINT PASARELA DE PAGO STRIPE +@api.route('/payment', methods=['POST']) +def create_payment(): + try: + data = request.json + intent = stripe.PaymentIntent.create( + amount=data['amount'], + currency= data['currency'], + automatic_payment_methods = {'enabled': True}) + return jsonify({'client_secret': intent['client_secret']}), 200 + except Exception as e: + return jsonify({'success': False, 'error': str(e)}), 400 + +#ENDPOINT IMAGEN CLOUDINARY +@api.route('/upload-image', methods=['POST']) +def upload(): + file_to_upload = request.files['img'] + if file_to_upload: + upload = cloudinary.uploader.upload(file_to_upload) + print(upload['url']) + return jsonify(upload['secure_url']), 201 + return jsonify({'error': 'no se cargo la imagen'}), 400 + + +#ENDPOINTS USERS + +@api.route('/users', methods=['GET']) +def get_users(): + users = User.query.all() + return jsonify([user.serialize() for user in users]), 201 + +@api.route('/user-signup', methods=['POST']) +def post_users(): + data = request.get_json() + exist = User.query.filter_by(username=data['username']).first() + if exist: + return jsonify({"msg": "Este usuario ya existe"}), 400 + email = request.json.get('email') + password = request.json.get('password') + username = request.json.get('username') + + if not email or not password or not username: + return jsonify({"msg" : "Faltan datos"}), 400 + + new_user = User( + name = data['name'], + lastname = data['lastname'], + email = data['email'], + password = data['password'], + username = data['username'], + address = data['address'], + birthday_date = data['birthday_date'], + user_image = data['image'], + is_active = True, + is_admin = False, + ) + db.session.add(new_user) + db.session.commit() + return jsonify({"msg": "Usuario Agregado"}), 201 + +@api.route('/login', methods=["POST"]) +def login_user(): + data = request.get_json() + user = User.query.filter_by(email=data['email'], password=data['password']).first() + if user is None: + return jsonify({'msg': "Contraseña o Email incorrecto"}), 400 + + access_token = create_access_token(identity=str(user.user_id)) + return jsonify({'token': access_token, 'user' : user.serialize()}), 201 + +@api.route('/private', methods=['GET']) +@jwt_required() +def verify(): + current_user_id = get_jwt_identity() + user = User.query.get(current_user_id) + + return jsonify({'msg': 'area privida','id': user.user_id, 'username': user.username}), 201 + +@api.route('/users/', methods=['DELETE']) +def delete_users(id_user): + exist = User.query.filter_by(user_id=id_user).first() + if exist: + db.session.delete(exist) + db.session.commit() + return jsonify({"msg": "User deleted from data base"}), 201 + return jsonify({"msg": "No User id was found"}), 400 + +@api.route('/update-user/', methods=['PUT']) +def update_users(id_user): + user = User.query.get(id_user) + data = request.get_json() + + user.name = data['name'] + user.lastname = data['lastname'] + user.email = data['email'] + user.password = data['password'] + user.username = data['username'] + user.address = data['address'] + user.user_image = data['image'] + user.birthday_date = data['birthday_date'] + + db.session.commit() + return jsonify({'msg': f'actualizado {user.name}'}), 201 + +@api.route('/user/', methods=['GET']) +def get_user_individual(id_user): + user = User.query.get(id_user) + + return jsonify(user.serialize()), 201 + + +#ENDPOINT CREAR CARRITO + +@api.route('/cart/', methods=['POST']) +def add_cart(user_id): + + exist = Pedido.query.filter_by(user_id=user_id).first() + + if exist: + return jsonify({"msg": "Tu carrito ya existe"}), 400 + elif not exist: + new_cart_added = Pedido(user_id=user_id) + db.session.add(new_cart_added) + db.session.commit() + return jsonify({"msg": "Carrito creado"}), 200 + +#ENDPOINT GET INFORMACION CARRITO + +@api.route('cart/', methods=['GET']) +def get_cart(user_id): + + user = User.query.get(user_id) + cart = Pedido.query.filter_by(user_id=user_id) + + data_cart = [cart.serialize() for cart in cart] + return jsonify(data_cart), 200 + +#ENDPOINT DELETE INFORMACION CARRITO + +@api.route('/cart/', methods=['DELETE']) +def delete_cart(user_id): + exist = Pedido.query.filter_by(user_id=user_id).first() + + if exist: + db.session.delete(exist) + db.session.commit() + return jsonify({"msg": "Carrito eliminado"}), 200 + return jsonify({"msg": "Carrito no encontrado"}) + +#ENDPOINT AGREGAR PRODUCTO AL CARRITO + +@api.route('/add-item/', methods=['POST']) +def add_items_pedido (id_user): + data = request.get_json() + pedido = Pedido.query.filter_by(user_id=id_user).first() + + # Buscar si el modelo ya existe en la lista de items + for item in pedido.items: + if item["modelo"] == data["modelo"] and item["color"] == data["color"]: + item["cantidad"] += 1 + break + else: + + pedido.items.append({ + "modelo": data["modelo"], + "precio": data["precio"], + "descripcion": data["descripcion"], + "color": data["color"], + "imagen": data["imagen"], + "cantidad": 1}) + + + flag_modified(pedido, "items") + + + db.session.commit() + + return jsonify({'msg': "agregado", "pedido": pedido.items}), 200 + +#ENDPOINT MODIFICAR CANTIDAD PRODUCTO +@api.route('/cart//edit-item', methods=['PUT']) +def edit_items_pedido(user_id): + data = request.get_json() + pedido = Pedido.query.filter_by(user_id=user_id).first() + + if pedido: + for item in pedido.items: + if item['modelo'] == data['modelo'] and item['color'] == data['color']: + item['cantidad'] = data['cantidad'] + flag_modified(pedido, "items") + db.session.commit() + return jsonify({"msg": "Producto actualizado"}) + + +#ENDPOINT ELIMINAR PRODUCTOS DEL CARRITO + +@api.route('cart//delete-item', methods=['DELETE']) +def delete_item_pedido(user_id): + data = request.get_json() + pedido = Pedido.query.filter_by(user_id=user_id).first() + + if pedido: + for item in pedido.items: + if item['modelo'] == data['modelo'] and item['color'] == data['color']: + pedido.items.remove(item) + flag_modified(pedido, "items") + db.session.commit() + return jsonify({"msg": "Producto eliminado"}) + + +#ENDPOINTS PHONES + +@api.route('/load-phone', methods=['GET']) +def load_phone (): + for phone in phones: + new_phone = Smartphones( + modelo = phone['modelo'], + pantalla = phone['pantalla'], + procesador = phone['procesador'], + memoria_ram = phone['memoria_ram'], + almacenamiento = phone['almacenamiento'], + camara = phone['camara'], + bateria = phone['bateria'], + precio = phone['precio'], + conectividad = phone['conectividad'], + colores = phone['colores'], + descripcion = phone['descripcion'], + imagen = phone['imagen'], + tipo = phone['tipo'] + ) + db.session.add(new_phone) + db.session.commit() + return jsonify({'msg': 'telefonos cargados'}) + +@api.route('/phones', methods=['GET']) +def get_phones(): + phones = Smartphones.query.all() + return jsonify([smartphones.serialize() for smartphones in phones]), 201 + +@api.route('/phone/', methods=['GET']) +def get_phones_individual(id_phone): + phone = Smartphones.query.get(id_phone) + return jsonify(phone.serialize()), 201 + +@api.route('/phones/', methods=['DELETE']) +def delete_phones(id_smartphone): + exist = Smartphones.query.filter_by(smartphone_id=id_smartphone).first() + if exist: + db.session.delete(exist) + db.session.commit() + return jsonify({"msg": "Smartphone deleted from data base"}), 201 + return jsonify({"msg": "No smartphone id was found"}), 400 + +#ENDPOINTS TVS + +@api.route('/load-tvs', methods=['GET']) +def load_tvs(): + for tv in tvs: + new_tv= TVs( + marca = tv['marca'], + contenido_de_la_caja = tv['contenido_de_la_caja'], + modelo = tv['modelo'], + usos_recomendados = tv['usos_recomendados'], + año_modelo = tv['año_modelo'], + fabricante = tv['fabricante'], + precio = tv['precio'], + descripcion = tv['descripcion'], + pantalla = tv['pantalla'], + conectividad = tv['conectividad'], + medidas = tv['medidas'], + imagen = tv['imagen'], + tipo = tv['tipo'] + ) + db.session.add(new_tv) + db.session.commit() + return jsonify({'msg': 'tvs agregados'}) + +@api.route('/tvs', methods=['GET']) +def get_tvs(): + tvs = TVs.query.all() + return jsonify([TVs.serialize() for TVs in tvs]), 201 + +@api.route('/tv/', methods=['GET']) +def get_tv_individual(id_tv): + tv = TVs.query.get(id_tv) + return jsonify(tv.serialize()) + +@api.route('/tvs/', methods=['DELETE']) +def delete_tvs(id_tv): + exist = TVs.query.filter_by(tv_id=id_tv).first() + if exist: + db.session.delete(exist) + db.session.commit() + return jsonify({"msg": "TV deleted from data base"}), 201 + return jsonify({"msg": "No TV id was found"}), 400 + +#ENDPOINTS LAPTOPS + +@api.route('/load-laptops', methods=['GET']) +def load_laptops(): + for laptop in laptops: + new_laptop = Laptops( + marca = laptop['marca'], + modelo = laptop['modelo'], + pantalla = laptop['pantalla'], + procesador = laptop['procesador'], + modelo_cpu = laptop['modelo_cpu'], + sistema_operativo = laptop['sistema_operativo'], + memoria_ram = laptop['memoria_ram'], + almacenamiento = laptop['almacenamiento'], + camara = laptop['camara'], + bateria = laptop['bateria'], + precio = laptop['precio'], + tecnologia = laptop['tecnologia'], + colores = laptop['colores'], + descripcion = laptop['descripcion'], + funcion_especial = laptop['funcion_especial'], + descripcion_tarjeta_grafica = laptop['descripcion_tarjeta_grafica'], + imagen = laptop['imagen'], + tipo = laptop['tipo'] + ) + db.session.add(new_laptop) + db.session.commit() + return jsonify({'msg': 'laptops agregadas'}), 201 + +@api.route('/laptops', methods=['GET']) +def get_laptops(): + laptops = Laptops.query.all() + return jsonify([Laptops.serialize() for Laptops in laptops]), 200 + +@api.route('/laptop/', methods=['GET']) +def get_laptop_individual(id_laptop): + laptop = Laptops.query.get(id_laptop) + return jsonify(laptop.serialize()) + +@api.route('/laptops/', methods=['DELETE']) +def delete_laptops(id_laptop): + exist = Laptops.query.filter_by(laptop_id=id_laptop).first() + if exist: + db.session.delete(exist) + db.session.commit() + return jsonify({"msg": "Laptop deleted from data base"}), 200 + return jsonify({"msg": "No Laptop id was found"}), 400 - return jsonify(response_body), 200 + \ No newline at end of file diff --git a/src/app.py b/src/app.py index 0ea8351d5f..676d9845e3 100644 --- a/src/app.py +++ b/src/app.py @@ -10,6 +10,9 @@ from api.routes import api from api.admin import setup_admin from api.commands import setup_commands +from flask_jwt_extended import JWTManager +import cloudinary +import stripe # from models import Person @@ -17,8 +20,19 @@ static_file_dir = os.path.join(os.path.dirname( os.path.realpath(__file__)), '../public/') app = Flask(__name__) +app.config["JWT_SECRET_KEY"] = os.getenv("FLASK_APP_KEY", "la_clave") +jwt = JWTManager(app) + app.url_map.strict_slashes = False +#cloudinary config +cloudinary.config( + cloud_name = os.getenv('CLOUDINARY_CLOUD_NAME'), + api_key = os.getenv('CLOUDINARY_API_KEY'), + api_secret = os.getenv('CLOUDINARY_API_SECRET'), # Click 'View API Keys' above to copy your API secret + secure=True +) + # database condiguration db_url = os.getenv("DATABASE_URL") if db_url is not None: diff --git a/src/front/js/apiservices/callToApi.js b/src/front/js/apiservices/callToApi.js new file mode 100644 index 0000000000..11039127a6 --- /dev/null +++ b/src/front/js/apiservices/callToApi.js @@ -0,0 +1,189 @@ +import Swal from "sweetalert2"; + +const urlBackend = process.env.BACKEND_URL; + + +export const createUser = async (newUser, navigate) => { + try { + const response = await fetch(urlBackend + 'user-signup', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + 'name': null, + 'lastname': null, + 'email': newUser.email, + 'password': newUser.password, + 'username': newUser.username, + 'address': null, + 'birthday_date': null, + 'image': null + }) + }); + if (response.ok) { + navigate('/login') + } else { + navigate('/signup') + }; + + } catch (error) { + console.log(error.status); + } +}; + +export const getUsers = async () => { + const respose = await fetch(urlBackend + 'users'); + const data = await respose.json(); + return data; +}; + +export const createCart = async (user_id) => { + const response = await fetch(urlBackend + "cart/" + user_id, { + method: "POST", + headers: { "Content-Type": "application/json" } + })} + +export const login = async (user, responseApi, navigate) => { + const response = await fetch(urlBackend + 'login', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + 'email': user.email, + 'password': user.password + }) + }); + const data = await response.json(); + responseApi(data); + sessionStorage.setItem('idUser', data.user.user_id) + sessionStorage.setItem('token', data.token); + if (data.token) { + navigate('/'); + Swal.fire({ + title: "Bienvenido a RAF!", + icon: "success", + html: `Bienvenido`, + draggable: true, + confirmButtonText: ` Great!` + }) + .then((response) => { + if (response.isConfirmed) { + createCart(data.user.user_id) + window.location.reload() + }; + }); + }; +}; + +export const privateUser = async () => { + const token = sessionStorage.getItem('token'); + const response = await fetch(urlBackend + 'private', { + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + } + }); + if (response.ok) { + return true + } else if (!response.ok) { + sessionStorage.removeItem('token') + sessionStorage.removeItem('idUser') + return false + }; +}; + +export const updateUser = async (updateInfo, imageUrl) => { + const idUser = sessionStorage.getItem('idUser') + console.log(imageUrl); + const response = await fetch(`${urlBackend}update-user/${idUser}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + "address": updateInfo.address, + "birthday_date": updateInfo.birthday_date, + "email": updateInfo.email, + "lastname": updateInfo.lastname, + "name": updateInfo.name, + "password": updateInfo.password, + "image": imageUrl || null, + "username": updateInfo.username + }) + }); + const data = await response.json() +}; + +export const sendImage = async (file) => { + try { + const form = new FormData(); + form.append("img", file); + const response = await fetch(urlBackend + 'upload-image', { + method: 'POST', + body: form + }) + const data = await response.json(); + return data; + } catch (error) { + console.log(error); + } +}; + +export const postProduct = async (user_id, data) => { + try { + const response = await fetch(urlBackend + "add-item/" + user_id, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(data) + }) + if (response.ok) { + return { "msg": "Producto agregado al carrito" } + } + } catch (error) { + console.error("Error") + } + +}; + + + +export const deleteProduct = async (user_id, modelo, color) => { + try { + const response = await fetch(urlBackend + "cart/" + user_id + "/delete-item", { method: "DELETE", + body: JSON.stringify({ + "modelo": modelo, + "color": color + }), + headers: { + "Content-Type" : "application/json" + } + }) + } + catch (error) { + console.log(urlBackend + "cart/" + user_id + "/product/" + product_type + "/" + cart_product_id) + return { "msg": "Error al eliminar el producto" } + }; +}; + + + +export const updateQuantityCartProduct = async (user_id, modelo, color, cantidad) => { + const response = await fetch(urlBackend + "cart/" + user_id + "/edit-item", { + method: "PUT", + body: JSON.stringify({ + "modelo": modelo, + "color": color, + "cantidad": cantidad + }), + headers: { + "Content-Type": "application/json" + } + }) + +}; + +export const deleteCart = async (user_id) => { + const response = await fetch(urlBackend + "cart/" + user_id, { + method: "DELETE", + headers: { "Content-Type": "application/json" } + }) +} + diff --git a/src/front/js/component/cart-order-resume.js b/src/front/js/component/cart-order-resume.js new file mode 100644 index 0000000000..65d6901cf2 --- /dev/null +++ b/src/front/js/component/cart-order-resume.js @@ -0,0 +1,84 @@ +import React, { useContext, useEffect } from "react"; +import { Context } from "../store/appContext"; +import { Link, useNavigate } from "react-router-dom"; + +export const OrderResume = () => { + + const navigate = useNavigate() + + const { store } = useContext(Context) + let subtotal = 0; + let totalPrecioEur = "" + let cantidadTotal = 0 + + const paymentView = () => { + navigate("/pasarela-pago", { state: { paymentAmount: subtotal } }) + } + + + return ( + <> +
+
+
+

Resumen del pedido

+
+
+ + + + + + + + + + {store.cart.map((item) => { + cantidadTotal += item.cantidad + subtotal += Number((item.precio).replace(/ €/g, "")) * Number(item.cantidad) + totalPrecioEur = new Intl.NumberFormat("de-DE", { + style: "currency", + currency: "EUR", + }).format(subtotal) + return ( + <> + + + + + + + ) + })} + +
ProductoCantidadPrecio
{item.modelo}{item.cantidad}{new Intl.NumberFormat("de-DE", { + style: "currency", + currency: "EUR", + }).format(Number((item.precio).replace(/ €/g, "")) * Number(item.cantidad))}
+
+
+
+ {cantidadTotal > 1? + <> +

Subtotal( {cantidadTotal} productos )


+ + : + <> +

Subtotal( {cantidadTotal} producto )


+ + } + +

{totalPrecioEur}

+
+
+
+
+ +
+
+
+
+
+ + ) +} \ No newline at end of file diff --git a/src/front/js/component/cart-product-list-product.js b/src/front/js/component/cart-product-list-product.js new file mode 100644 index 0000000000..0ecc1051a2 --- /dev/null +++ b/src/front/js/component/cart-product-list-product.js @@ -0,0 +1,81 @@ +import React, { useContext, useEffect, useState } from "react"; +import { deleteProduct } from "../apiservices/callToApi"; +import { updateQuantityCartProduct } from "../apiservices/callToApi"; +import { Context } from "../store/appContext"; + +export const ListProduct = ({ name, description, quantity, image, color, user_id }) => { + + const [cantidad, setCantidad] = useState(quantity) + const { actions } = useContext(Context) + const aumentarCantidad = () => { + setCantidad(prevCantidad => prevCantidad + 1); + + }; + + const decrementarCantidad = () => { + if (cantidad > 1) { + setCantidad(prevCantidad => prevCantidad - 1); + }; + }; + + const updateProducts = async () => { + await updateQuantityCartProduct(user_id, name, color, cantidad) + actions.getCart(user_id) + } + + + useEffect(() => { + updateProducts() + }, [cantidad]) + + + return ( + <> +
+
+
+
+
+ +
+
+
+
+
+

{name} {color != "no" && `(${color})`}

+

{description}

+
+
+
+
{cantidad > 1 ? + : + + } +

{cantidad}

+ +
+
+
+ +
+ + ) +} \ No newline at end of file diff --git a/src/front/js/component/cart-product-list.js b/src/front/js/component/cart-product-list.js new file mode 100644 index 0000000000..be8acf006c --- /dev/null +++ b/src/front/js/component/cart-product-list.js @@ -0,0 +1,36 @@ +import React, { useContext, useEffect, useState } from 'react'; +import { ListProduct } from './cart-product-list-product'; +import { Context } from '../store/appContext'; + +export const ProductList = ({ user_id }) => { + const { store, actions } = useContext(Context) + + + + return ( + <> +
+
+
+
+
+

Carrito

+

Lista de productos en tu carrito personal

+
+
+ {store.cart.map((item) => { + return ( + <> +
+ +
+ + ) + })} +
+ +
+
+ + ) +} \ No newline at end of file diff --git a/src/front/js/component/catalog-catalog.js b/src/front/js/component/catalog-catalog.js new file mode 100644 index 0000000000..b39308b40a --- /dev/null +++ b/src/front/js/component/catalog-catalog.js @@ -0,0 +1,42 @@ +import React, { useContext } from 'react'; +import { CatalogProductCard } from './catalog-product-card'; +import { Context } from '../store/appContext'; + +export const CatalogCatalog = ({ productList }) => { + + const { store } = useContext(Context) + let titulo = "" + const products = store[productList] + + if (productList == "phones") { + titulo = "de Móviles" + } else if (productList == "tvs") { + titulo = "de TVs"; + } else if (productList == "laptops") { + titulo = "de Portátiles"; + }; + + return ( + <> +
+

Catálogo {titulo}

+
+
+ { + products.map((product, index) => ( +
+
+ +
+
+ )) + } +
+
+
+ + ) +} \ No newline at end of file diff --git a/src/front/js/component/catalog-product-card.js b/src/front/js/component/catalog-product-card.js new file mode 100644 index 0000000000..1b38f9569c --- /dev/null +++ b/src/front/js/component/catalog-product-card.js @@ -0,0 +1,73 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; + +export const CatalogProductCard = ({ product, productName }) => { + const precio = parseInt(product.precio.replace('€', "")); + const totalPrecioEur = new Intl.NumberFormat("de-DE", { + style: "currency", + currency: "EUR", + }).format(precio); + + let tvOn = null; + let images = ""; + + const validacionLista = (producto) => { + if (producto == "laptops" || producto == "phones") { + const color = (product.colores[0].toLowerCase()).replace(/ /g, "_"); + images = product.imagen[color]; + tvOn = false; + } + else { + images = product.imagen; + tvOn = true; + } + }; + + let IndividualProduct = ""; + const validacionProduct = (producto) => { + if (producto == "laptops") { + IndividualProduct = `/laptop-info/${product.laptop_id}` + }; + if (producto == "phones") { + IndividualProduct = `/smartphone-info/${product.smartphone_id}` + }; + if (producto == "tvs") { + IndividualProduct = `/tv-info/${product.tv_id}` + }; + }; + + validacionLista(productName); + validacionProduct(productName); + + return ( +
+
+
+ + + +
+ {product.modelo} +
    + {!tvOn ? + <> +
  • Almacenamiento: {product.almacenamiento}
  • +
  • Memoria Ram: {product.memoria_ram}
  • +
  • Camara: {product.camara}
  • + + : + <> +
  • Marca: {product.marca}
  • +
  • Pantalla: {product.pantalla}
  • +
  • Año Lanzamiento: {product.año_modelo}
  • + + } +
+

{totalPrecioEur}

+ + + +
+
+ ) +} \ No newline at end of file diff --git a/src/front/js/component/empty-cart.js b/src/front/js/component/empty-cart.js new file mode 100644 index 0000000000..f476affa3f --- /dev/null +++ b/src/front/js/component/empty-cart.js @@ -0,0 +1,38 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; + +export const EmptyCart = () => { + return (<> +
+
+

Carro vacío

+ +
+
+
+ +
+
+
+
+

Añade productos para poder verlos en tu carrito personal.

+
+
+ +
+
+
+
+ + + + + + + + + + +
+ ) +} \ No newline at end of file diff --git a/src/front/js/component/footer.js b/src/front/js/component/footer.js index 670323e091..39decf288d 100755 --- a/src/front/js/component/footer.js +++ b/src/front/js/component/footer.js @@ -1,10 +1,51 @@ -import React, { Component } from "react"; +import React from "react"; +import { useContext } from "react"; +import { Context } from "../store/appContext"; +import { Link } from "react-router-dom"; -export const Footer = () => ( - -); +export const Footer = () => { + + const { store } = useContext(Context) + + return ( + <> + + + ) +}; diff --git a/src/front/js/component/formPayment.js b/src/front/js/component/formPayment.js new file mode 100644 index 0000000000..56c882bc57 --- /dev/null +++ b/src/front/js/component/formPayment.js @@ -0,0 +1,68 @@ +import React, { useEffect, useState } from "react"; +import { useStripe, useElements } from "@stripe/react-stripe-js"; +import { PaymentElement } from "@stripe/react-stripe-js"; +import { useNavigate } from "react-router-dom"; +import { deleteCart, createCart } from "../apiservices/callToApi"; + + +export const FormPayment = ({ user_id, amount }) => { + const stripe = useStripe(); + const elements = useElements(); + const [loading, setLoading] = useState(false); + const [message, setMessage] = useState(null); + const navigate = useNavigate(); + const finalAmount = new Intl.NumberFormat("de-DE", { + style: "currency", + currency: "EUR", + }).format(amount) + + const handleSubmit = async (event) => { + event.preventDefault(); + if (!stripe || !elements) return; + + setLoading(true); + + const { error, paymentIntent } = await stripe.confirmPayment({ + elements, + confirmParams: { + return_url: 'https://super-duper-space-adventure-pjpjqx6q46qp27jgx-3000.app.github.dev/cart' + }, + redirect: "if_required" + }, + ); + + setLoading(false); + + if (error) { + setMessage(error.message) + } else if (paymentIntent.status === 'succeeded') { + setMessage("Pago confirmado!!!"); + await deleteCart(user_id) + await createCart(user_id) + navigate('/message-payment'); + + } else { + setMessage("Estado Inesperado") + } + }; + + return ( + <> +
+
+ +
+

Total a pagar:   {finalAmount}

+
+ +
+

{message}

+
+ + + ) +} \ No newline at end of file diff --git a/src/front/js/component/home-carrousel.js b/src/front/js/component/home-carrousel.js new file mode 100644 index 0000000000..2c2c070e50 --- /dev/null +++ b/src/front/js/component/home-carrousel.js @@ -0,0 +1,53 @@ +import React from "react"; +import { Link } from "react-router-dom"; + + +export const Welcome = () => { + return (<> +
+
+
+
+ +
+
+
+ + + +
+
+
+
+ +
+ ... +
+ +
+
+ +
+ ... +
+ +
+
+ +
+ +
+ +
+
+ + +
+ ) +} diff --git a/src/front/js/component/home-catalog.js b/src/front/js/component/home-catalog.js new file mode 100644 index 0000000000..393fdb18de --- /dev/null +++ b/src/front/js/component/home-catalog.js @@ -0,0 +1,60 @@ +import React, { useState, useEffect } from 'react'; +import { ProductSection } from './product-section'; + + +const urlBackend = process.env.BACKEND_URL; + +export const HomeCatalog = () => { + const [phones, setPhones] = useState([]); + const [tvs, setTvs] = useState([]); + const [laptops, setLaptops] = useState([]); + const [random, setRandom] = useState(null); + + useEffect(() => { + const interval = setInterval(() => + setRandom(Math.floor(Math.random() * 15) + 1) + , 10000); + + return () => clearInterval(interval); + }, []); + + const idProduct = [ + Math.max(1, random), + Math.max(2, random - 1), + Math.max(3, random - 2), + ]; + + let uniqueArray = [...new Set(idProduct)] + if (uniqueArray.length < 3) { + uniqueArray.push(random + 2); + }; + + const fetchProducts = async (product, idProduct, setProduct) => { + try { + const productPromises = idProduct.map(async (id) => { + const response = await fetch(`${urlBackend}${product}/${id}`); + return response.json(); // Devuelve el JSON de la respuesta + }); + const data = await Promise.all(productPromises); // Espera todas las promesas + setProduct(data); // Actualiza el estado con un array de datos + } catch (error) { + console.error("Error al obtener los datos:", error); + }; + }; + + useEffect(() => { + fetchProducts('phone', uniqueArray, setPhones); + fetchProducts('tv', uniqueArray, setTvs); + fetchProducts('laptop', uniqueArray, setLaptops); + }, [random]); + + return ( + <> +
+ + + +
+ + ); +}; \ No newline at end of file diff --git a/src/front/js/component/navbar.js b/src/front/js/component/navbar.js index af4b01e334..3b2a208b0b 100755 --- a/src/front/js/component/navbar.js +++ b/src/front/js/component/navbar.js @@ -1,19 +1,146 @@ -import React from "react"; -import { Link } from "react-router-dom"; +import React, { useContext, useEffect, useState } from "react"; +import { Link, useNavigate } from "react-router-dom"; +import { Context } from "../store/appContext"; +import { privateUser } from "../apiservices/callToApi"; + export const Navbar = () => { + const { store, actions } = useContext(Context); + const [isVerified, setIsVerified] = useState(null); + const [search, setSearch] = useState(""); + + const navigate = useNavigate(); + const infoUser = store.infoUser; + + let products = []; + const phones = store.phones; + const tvs = store.tvs; + const laptops = store.laptops; + products = [...phones, ...tvs, ...laptops]; + + const searcher = (e) => { + setSearch(e.target.value); + }; + + let results = search.length > 0 && + products.filter(({ modelo = "", marca = "" }) => + modelo.toLowerCase().includes(search.toLowerCase()) || + marca.toLowerCase().includes(search.toLowerCase())); + + const validacionProduct = (product) => { + let route = ""; + if(product.laptop_id){ + route = `/laptop-info/${product.laptop_id}` + }else if(product.smartphone_id){ + route = `/smartphone-info/${product.smartphone_id}` + }else if(product.tv_id){ + route = `/tv-info/${product.tv_id}` + }; + navigate(route); + setSearch(""); + }; + + const checkout = async () => { + const verified = await privateUser(); + setIsVerified(verified); + }; + + useEffect(() => { + checkout(); + }); + + useEffect(()=>{ + if(isVerified){ + actions.userIndividual(); + }; + },[isVerified]); + + + const logOut = () => { + sessionStorage.removeItem('token'); + sessionStorage.removeItem('idUser'); + checkout(); + }; + return ( - + + ); }; + diff --git a/src/front/js/component/novedadesMail.js b/src/front/js/component/novedadesMail.js new file mode 100644 index 0000000000..d64840f86b --- /dev/null +++ b/src/front/js/component/novedadesMail.js @@ -0,0 +1,24 @@ +import React from 'react'; + +export const NovedadesMail = () => { + return ( + <> +
+
+
+ Recibe novedades en tu correo +
+
+
+

Ingresa tu dirección de contacto para estar al dia de nuestras promociones

+
+ + +
+
+
+
+
+ + ) +} \ No newline at end of file diff --git a/src/front/js/component/onfire.js b/src/front/js/component/onfire.js new file mode 100644 index 0000000000..389555bf97 --- /dev/null +++ b/src/front/js/component/onfire.js @@ -0,0 +1,63 @@ +import React, { useContext } from "react"; +import { OnFireItem } from "./onfireitem" +import { Context } from "../store/appContext"; +import { Link } from "react-router-dom"; + +export const Onfire = ({ type1, id1, type2, id2 }) => { + + const { store } = useContext(Context) + const products1 = store[type1] + const products2 = store[type2] + let productId1 = "" + let productId2 = "" + let fireItem1 = "" + let fireItem2 = "" + + const getId1 = () => { + if (type1 == "phones") { + productId1 = "smartphone_id" + } else if (type1 == "tvs") { + productId1 = "tv_id" + } else if (type1 == "laptops") { + productId1 = "laptop_id" + } + } + + const getId2 = () => { + if (type2 == "phones") { + productId2 = "smartphone_id" + } else if (type2 == "tvs") { + productId2 = "tv_id" + } else if (type2 == "laptops") { + productId2 = "laptop_id" + } + } + + getId1(); + getId2(); + + products1.forEach((element) => { + if (element[productId1] == id1) { + fireItem1 = element + }; + }); + + products2.forEach((element) => { + if (element[productId2] == id2) { + fireItem2 = element + }; + }); + + + + return (<> +
+ + + + + + +
+ ) +} \ No newline at end of file diff --git a/src/front/js/component/onfireitem.js b/src/front/js/component/onfireitem.js new file mode 100644 index 0000000000..3264132beb --- /dev/null +++ b/src/front/js/component/onfireitem.js @@ -0,0 +1,44 @@ +import React, { useEffect, useState } from 'react'; + +export const OnFireItem = ({ item, itemName }) => { + const [imageNumber, setImageNumber] = useState(0); + const [fade, setFade] = useState(true); + + let image = ""; + const validacionLista = () => { + if (itemName == "phones" || itemName == "laptops") { + const color = (item.colores?.[0].toLowerCase())?.replace(/ /g, "_"); + image = item.imagen?.[color]; + } else { + image = item.imagen; + } + } + + validacionLista(); + + useEffect(()=>{ + const interval = setInterval(() => { + setFade(false); + setTimeout(() => { + setImageNumber((prev) => (prev === 0 ? 1 : 0)); + setFade(true); + }, 500); + }, 4000); + + return () => clearInterval(interval); + },[]) + + + return (<> +
+
+ ... +
+ TENDENCIAunread messages +
+
{item.modelo}
+

{item.descripcion}

+
+
+ ) +} \ No newline at end of file diff --git a/src/front/js/component/personalInfo.js b/src/front/js/component/personalInfo.js new file mode 100644 index 0000000000..18f11d988d --- /dev/null +++ b/src/front/js/component/personalInfo.js @@ -0,0 +1,95 @@ +import React, { useContext, useEffect, useState } from 'react'; +import { Context } from '../store/appContext'; +import { sendImage, updateUser } from "../apiservices/callToApi"; + + +export const PersonalInfo = ({ imageUrl, setInfoUsers }) => { + const { store, actions } = useContext(Context); + const [updateInfo, setUpdateInfo] = useState({}); + + const infoUser = store.infoUser + + const handleChange = (e) => { + setUpdateInfo({ ...updateInfo, [e.target.name]: e.target.value }); + }; + + const handleSubmit = async () => { + let imageUpdate = imageUrl || updateInfo.image; + await updateUser(updateInfo, imageUpdate); + actions.userIndividual(); + store.edit = false + }; + + useEffect(() => { + if (updateInfo) { + setInfoUsers(updateInfo); + }; + }, [updateInfo]) + + useEffect(() => { + setUpdateInfo(infoUser); + }, [infoUser]); + + return ( + <> +
+

Zona personal

+
+
+
+
+
+
+ +
+ handleChange(e)} aria-describedby="emailHelp" value={updateInfo.name ?? ""} disabled={!store.edit} /> +
+
+
+ +
+ handleChange(e)} aria-describedby="emailHelp" value={updateInfo.lastname ?? ""} disabled={!store.edit} /> +
+
+
+ +
+ handleChange(e)} aria-describedby="emailHelp" value={updateInfo.username ?? ""} disabled={!store.edit} /> +
+
+
+ +
+ handleChange(e)} aria-describedby="emailHelp" value={updateInfo.email ?? ""} disabled={!store.edit} /> +
+
+
+ +
+ handleChange(e)} aria-describedby="emailHelp" value={updateInfo.password ?? ""} disabled={!store.edit} /> +
+
+
+ +
+ handleChange(e)} aria-describedby="emailHelp" value={updateInfo.address ?? ""} disabled={!store.edit} /> +
+
+
+ +
+ handleChange(e)} aria-describedby="emailHelp" value={updateInfo.birthday_date ?? ""} disabled={!store.edit} /> +
+ +
+
+
+ + +
+
+
+
+ + ) +} \ No newline at end of file diff --git a/src/front/js/component/personalNavbar.js b/src/front/js/component/personalNavbar.js new file mode 100644 index 0000000000..bcfd0b0b27 --- /dev/null +++ b/src/front/js/component/personalNavbar.js @@ -0,0 +1,53 @@ +import React, { useContext, useState } from 'react'; +import { Context } from '../store/appContext'; +import { sendImage, updateUser } from '../apiservices/callToApi' + +export const PersonalNavbar = ({ setImageUrl, infoUsers }) => { + const { store, actions } = useContext(Context); + const infoUser = store.infoUser; + const [file, setFile] = useState(""); + + const changeUploadImage = async (e) => { + setFile(e.target.files[0]); + }; + + const handleClickImage = async () => { + let dataUser = infoUsers || infoUser; + const data = await sendImage(file); + setImageUrl(data); + await updateUser(dataUser, data); + actions.userIndividual(); + setFile(''); + }; + + return ( + <> +
+
+
+
+ +
+ + +
+
+
+
+
+
+

Hola, {infoUser.username}!

+
+
+ +
+ {file && + + } +
+
+ + ) +} \ No newline at end of file diff --git a/src/front/js/component/product-card.js b/src/front/js/component/product-card.js new file mode 100644 index 0000000000..d77615e5dd --- /dev/null +++ b/src/front/js/component/product-card.js @@ -0,0 +1,58 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; + +export const ProductCard = ({ product, name }) => { + + let images = ""; + const validacionLista = (producto) => { + if (producto == "Laptops" || producto == "Moviles") { + const color = (product.colores[0].toLowerCase()).replace(/ /g, "_"); + images = product.imagen[color]; + } + else { + images = product.imagen + }; + }; + + let IndividualProduct = ""; + const validacionProduct = (producto) => { + if (producto == "Laptops") { + IndividualProduct = `/laptop-info/${product.laptop_id}` + } else if (producto == "Moviles") { + IndividualProduct = `/smartphone-info/${product.smartphone_id}` + } else if (producto == "TVs") { + IndividualProduct = `/tv-info/${product.tv_id}` + }; + }; + + const precio = parseInt(product.precio.replace('€', "")); + const totalPrecioEur = new Intl.NumberFormat("de-DE", { + style: "currency", + currency: "EUR", + }).format(precio); + + validacionLista(name); + validacionProduct(name); + + return ( +
+
+
+ + + +
+
+ {product.modelo} +
+
+

{product.descripcion}

+
+

{totalPrecioEur}

+ + + +
+
+ ) +} \ No newline at end of file diff --git a/src/front/js/component/product-colors.js b/src/front/js/component/product-colors.js new file mode 100644 index 0000000000..4c11f2f6c8 --- /dev/null +++ b/src/front/js/component/product-colors.js @@ -0,0 +1,9 @@ +import React from "react"; + +export const ProductColors = ({ src }) => { + return ( + <> + + + ) +} \ No newline at end of file diff --git a/src/front/js/component/product-section.js b/src/front/js/component/product-section.js new file mode 100644 index 0000000000..f6ff41129f --- /dev/null +++ b/src/front/js/component/product-section.js @@ -0,0 +1,45 @@ +import React, { useState } from "react"; +import { ProductCard } from "./product-card"; +import { Link } from "react-router-dom"; + +export const ProductSection = ({ name, products }) => { + + let catalogProduct = ""; + const validacionProduct = (producto) => { + if (producto == "Laptops") { + catalogProduct = "/laptops-catalog"; + } else if (producto == "Moviles") { + catalogProduct = "/phones-catalog"; + } else if (producto == "TVs") { + catalogProduct = "/tvs-catalog"; + }; + }; + + validacionProduct(name); + + return ( + <> +
+

{name}

+
+
+ {products.map((product, index) => { + return ( +
+ +
+ ) + })}; +
+
+
+ + + +
+
+
+
+ + ) +} \ No newline at end of file diff --git a/src/front/js/component/productCardSmall.js b/src/front/js/component/productCardSmall.js new file mode 100644 index 0000000000..cde77e4af0 --- /dev/null +++ b/src/front/js/component/productCardSmall.js @@ -0,0 +1,46 @@ +import React from 'react'; +import { useNavigate } from 'react-router-dom'; + +export const ProductCardSmall = ({ product }) => { + const navigate = useNavigate(); + let imagen; + const color = product.colores?.[0].toLowerCase()?.replace(/ /g, "_") + if (product.tipo === 'smartphone' || product.tipo === 'laptop') { + imagen = product.imagen?.[color][0]; + } else if (product.tipo === 'tv') { + imagen = product.imagen[0]; + }; + + const sendToIndividualProduct = () => { + let route; + if (product.tipo == "laptop") { + route = `/laptop-info/${product.laptop_id}`; + } else if (product.tipo == "smartphone") { + route = `/smartphone-info/${product.smartphone_id}`; + } else if (product.tipo == "tv") { + route = `/tv-info/${product.tv_id}`; + }; + navigate(route); + }; + + const precio = parseInt(product.precio.replace('€', "")); + const totalPrecioEur = new Intl.NumberFormat("de-DE", { + style: "currency", + currency: "EUR", + }).format(precio); + + return ( +
+
+ +
+
+
+ {product.modelo} +
+

{totalPrecioEur}

+ {product.tipo == "laptop" || product.tipo == "tv" ? 'Marca: ' + product.marca : product.procesador } +
+
+ ); +}; \ No newline at end of file diff --git a/src/front/js/component/productInfoLaptop.js b/src/front/js/component/productInfoLaptop.js new file mode 100644 index 0000000000..295a15447e --- /dev/null +++ b/src/front/js/component/productInfoLaptop.js @@ -0,0 +1,292 @@ +import React, { useState, useContext, useEffect } from "react"; +import { ProductColors } from "../component/product-colors"; +import { useParams } from "react-router-dom"; +import { postProduct } from "../apiservices/callToApi"; +import { Context } from "../store/appContext"; + +export const ProductInfoLaptop = () => { + + const [activeColor, setActiveColor] = useState(0); + const [laptop, setLaptop] = useState([]); + const { laptop_id } = useParams(); + const [userId, setUserId] = useState(sessionStorage.getItem("idUser")); + const [buttonText, setButtonText] = useState("Añadir al carrito"); + const [buttonClass, setButtonClass] = useState("btn-add-cart texto"); + const [infoToPost, setInfoToPost] = useState({}) + + let image; + let color; + const precio = parseInt(laptop.precio); + + const totalPrecioEur = new Intl.NumberFormat("de-DE", { + style: "currency", + currency: "EUR", + }).format(precio); + + const getLaptopById = async () => { + const urlBackend = process.env.BACKEND_URL + try { + const response = await fetch(urlBackend + "laptop/" + laptop_id); + const data = await response.json(); + setLaptop(data); + } catch (error) { + console.error("Error getting ID TVs from API"); + } + }; + + const imageValidation = (number) => { + color = (laptop.colores?.[number].toLowerCase())?.replace(/ /g, "_"); + image = laptop.imagen?.[color]; + }; + + imageValidation(activeColor); + + useEffect(() => { + getLaptopById(); + }, [laptop_id]); + + const handleAddToCart = async () => { + + await postProduct(userId,{"modelo": laptop.modelo, "descripcion": laptop.descripcion, "cantidad" : 1, "precio": laptop.precio, "imagen": image?.[0], "color": (color.replace(/_/g, " ")).toUpperCase()}); + + setButtonText("✓ Producto añadido!"); + setButtonClass("btn-add-cart texto added shake"); + + setTimeout(() => { + setButtonText("Añadir al carrito"); + setButtonClass("btn-add-cart texto"); + }, 1500); + }; + + + + return ( +
+
+
+
+
+
+ Cargando Fotografía... +
+
+
+
+ Cargando Fotografía... +
+
+
+ + +
+
+
+
{laptop?.modelo}
+

+

+ {totalPrecioEur} +

+

+ +

+
+
+

+ +

+
+
+

{laptop?.descripcion}

+
+
+
+
+

+ +

+
+
+ {laptop.colores?.map((color, index) => { + let litImage = "" + const getProductPhoto = () => { + const color = (laptop.colores?.[index].toLowerCase())?.replace(/ /g, "_") + litImage = laptop.imagen?.[color] + } + getProductPhoto(); + return ( +
{ imageValidation(index), setActiveColor(index) }} className={`continer-img-selection ${activeColor === index ? "active-border" : ""} `}> +
{color}
+ +
+ ) + })} +
+
+
+
+

+ +

+
+
+

{laptop?.marca}

+
+
+
+
+

+ +

+
+
+

{laptop?.camara}

+
+
+
+
+

+ +

+
+
+

{laptop?.almacenamiento}

+
+
+
+
+

+ +

+
+
+

{laptop?.memoria_ram}

+
+
+
+
+

+ +

+
+
+

{laptop?.pantalla}

+
+
+
+
+

+ +

+
+
+

{laptop?.bateria}

+
+
+
+
+

+ +

+
+
+

{laptop?.procesador}

+
+
+
+
+

+ +

+
+
+

{laptop?.descripcion_tarjeta_grafica}

+
+
+
+ +
+

+ +

+
+
+

{laptop?.sistema_operativo}

+
+
+
+ +
+

+ +

+
+
+

{laptop?.modelo_cpu}

+
+
+
+ +
+

+ +

+
+
+

{laptop?.tecnologia}

+
+
+
+
+

+ +

+
+
+

{laptop?.funcion_especial}

+
+
+
+
+
+
+
+

Productos relacionados (Portátiles)

+
+ ); +}; diff --git a/src/front/js/component/productInfoPhone.js b/src/front/js/component/productInfoPhone.js new file mode 100644 index 0000000000..0c76099ccd --- /dev/null +++ b/src/front/js/component/productInfoPhone.js @@ -0,0 +1,216 @@ +import React, { useState, useContext, useEffect } from "react"; +import { ProductColors } from "../component/product-colors"; +import { useParams } from "react-router-dom"; +import { postProduct } from "../apiservices/callToApi"; + +export const ProductInfoPhone = () => { + + const [activeColor, setActiveColor] = useState(0) + const [phone, setPhone] = useState([]); + const { smartphone_id } = useParams(); + const [userId, setUserId] = useState(sessionStorage.getItem("idUser")); + const [buttonText, setButtonText] = useState("Añadir al carrito"); + const [buttonClass, setButtonClass] = useState("btn-add-cart texto"); + + let image; + let color; + const precio = parseInt(phone.precio); + + const totalPrecioEur = new Intl.NumberFormat("de-DE", { + style: "currency", + currency: "EUR", + }).format(precio); + + const getPhoneById = async () => { + const urlBackend = process.env.BACKEND_URL + try { + const response = await fetch(urlBackend + "phone/" + smartphone_id); + const data = await response.json(); + setPhone(data); + } catch (error) { + console.error("Error getting ID phones from API"); + } + }; + + const imageValidation = (number) => { + color = (phone.colores?.[number].toLowerCase())?.replace(/ /g, "_"); + image = phone.imagen?.[color]; + }; + + imageValidation(activeColor); + + useEffect(() => { + getPhoneById(); + }, [smartphone_id]); + + const handleAddToCart = async () => { + await postProduct(userId,{"modelo": phone.modelo, "descripcion": phone.descripcion, "cantidad" : 1, "precio": phone.precio, "imagen": image?.[0], "color": (color.replace(/_/g, " ")).toUpperCase()}); + setButtonText("✓ Producto añadido!"); + setButtonClass("btn-add-cart texto added shake"); + + setTimeout(() => { + setButtonText("Añadir al carrito"); + setButtonClass("btn-add-cart texto"); + }, 1500); + }; + + return ( +
+
+
+
+
+
+
+ Cargando Fotografía... +
+
+
+
+ Cargando Fotografía... +
+
+
+ + +
+
+
+
{phone?.modelo}
+

+

+ {totalPrecioEur} +

+

+ {/* */} + +

+
+
+

+ +

+
+
+

{phone?.descripcion}

+
+
+
+
+

+ +

+
+
+ {phone.colores?.map((color, index) => { + let litImage = ""; + const getProductPhoto = () => { + const color = (phone.colores?.[index].toLowerCase())?.replace(/ /g, "_"); + litImage = phone.imagen?.[color]; + } + getProductPhoto(); + return ( +
{ imageValidation(index), setActiveColor(index) }} className={`continer-img-selection ${activeColor === index ? "active-border" : ""} `}> +
{color}
+ +
+ ) + })} +
+
+
+
+

+ +

+
+
+

{phone?.camara}

+
+
+
+
+

+ +

+
+
+

{phone?.almacenamiento}

+
+
+
+
+

+ +

+
+
+

{phone?.memoria_ram}

+
+
+
+
+

+ +

+
+
+

{phone?.pantalla}

+
+
+
+
+

+ +

+
+
+

{phone?.bateria}

+
+
+
+
+

+ +

+
+
+

{phone?.procesador}

+
+
+
+
+
+
+
+
+

Productos relacionados (Smartphones)

+
+ ); +}; diff --git a/src/front/js/component/productInfoTv.js b/src/front/js/component/productInfoTv.js new file mode 100644 index 0000000000..a63426076b --- /dev/null +++ b/src/front/js/component/productInfoTv.js @@ -0,0 +1,218 @@ +import React, { useState, useContext, useEffect } from "react"; +import { useParams } from "react-router-dom"; +import { postProduct } from "../apiservices/callToApi"; + +export const ProductInfoTv = () => { + + const [tv, setTv] = useState([]); + const { tv_id } = useParams(); + const [userId, setUserId] = useState(sessionStorage.getItem("idUser")); + const [buttonText, setButtonText] = useState("Añadir al carrito"); + const [buttonClass, setButtonClass] = useState("btn-add-cart texto"); + + const precio = parseInt(tv.precio); + const totalPrecioEur = new Intl.NumberFormat("de-DE", { + style: "currency", + currency: "EUR", + }).format(precio); + + const getTvById = async () => { + const urlBackend = process.env.BACKEND_URL; + try { + const response = await fetch(urlBackend + "tv/" + tv_id); + const data = await response.json(); + setTv(data); + } catch (error) { + console.error("Error getting ID TVs from API"); + } + }; + + const imagenTv1 = tv?.imagen?.[0]; + const imagenTv2 = tv?.imagen?.[1]; + + useEffect(() => { + getTvById(); + }, [tv_id]); + + const handleAddToCart = async () => { + + await postProduct(userId,{"modelo": tv.modelo, "descripcion": tv.descripcion, "cantidad" : 1, "precio": tv.precio, "imagen": imagenTv1, "color": "no"}) + + setButtonText("✓ Producto añadido!"); + setButtonClass("btn-add-cart texto added shake"); + + setTimeout(() => { + setButtonText("Añadir al carrito"); + setButtonClass("btn-add-cart texto"); + }, 1500); + }; + + return ( +
+
+
+
+
+
+ Cargando Fotografía... +
+
+
+
+ Cargando Fotografía... +
+
+
+ + +
+
+
+
{tv?.modelo}
+

+

+ {totalPrecioEur} +

+

+ {/* */} + +

+
+
+

+ +

+
+
+

{tv?.descripcion}

+
+
+
+
+

+ +

+
+
+

{tv?.marca}

+
+
+
+
+

+ +

+
+
+

{tv?.pantalla}

+
+
+
+
+

+ +

+
+
+

{tv?.medidas}

+
+
+
+
+

+ +

+
+
+

{tv?.usos_recomendados}

+
+
+
+
+

+ +

+
+
+

{tv?.año_modelo}

+
+
+
+
+

+ +

+
+
+

{tv?.conectividad}

+
+
+
+
+

+ +

+
+
+

{tv?.contenido_de_la_caja}

+
+
+
+
+

+ +

+
+
+

{tv?.usos_recomendados}

+
+
+
+
+

+ +

+
+
+

{tv?.fabricante}

+
+
+
+
+
+
+
+

Productos relacionados (TVs)

+
+ ); +}; diff --git a/src/front/js/component/recycle-carousel.js b/src/front/js/component/recycle-carousel.js new file mode 100644 index 0000000000..3545e72316 --- /dev/null +++ b/src/front/js/component/recycle-carousel.js @@ -0,0 +1,47 @@ +export const Welcome = () => { + return (<> +
+
+
+
+
+

Bienvenidos

+

Queridos internautas

+

Tecnologia en demanda, en la puerta de tu casa, tu mejor version tecnologica te espera.

+
+
+
+ Avanzar +
+
+
+
+
+
+
+ ... +
+
+
+
+ ... +
+
+
+
+ +
+
+
+
+ + +
+ ) +} \ No newline at end of file diff --git a/src/front/js/component/related-products.js b/src/front/js/component/related-products.js new file mode 100644 index 0000000000..ae57c5dce4 --- /dev/null +++ b/src/front/js/component/related-products.js @@ -0,0 +1,47 @@ +import React, { useContext } from "react"; +import { ProductCardSmall } from "./productCardSmall"; +import { Context } from "../store/appContext"; + +export const RelatedProducts = ({ productType }) => { + + const { store } = useContext(Context); + + const products = store[productType]; + + const relatedCarruselProducts = []; + for (let i = 0; i < products.length; i += 5) { + relatedCarruselProducts.push(products.slice(i, i + 5)); + }; + + return ( +
+
+
+
+
+ {relatedCarruselProducts.map((products, index) => ( +
+
+ {products.map((product, index) => ( +
+ +
+ ))} +
+
+ ))} +
+ + +
+
+
+
+ ); +}; diff --git a/src/front/js/component/welcome-catalog-phones.js b/src/front/js/component/welcome-catalog-phones.js new file mode 100644 index 0000000000..558e6e1794 --- /dev/null +++ b/src/front/js/component/welcome-catalog-phones.js @@ -0,0 +1,13 @@ +import React from 'react'; + +export const WelcomeCatalog = () => { + return ( + <> +
+
+ +
+
+ + ) +} \ No newline at end of file diff --git a/src/front/js/layout.js b/src/front/js/layout.js index d42289f0ee..4ce6614e76 100755 --- a/src/front/js/layout.js +++ b/src/front/js/layout.js @@ -1,15 +1,22 @@ import React from "react"; import { BrowserRouter, Route, Routes } from "react-router-dom"; import ScrollToTop from "./component/scrollToTop"; -import { BackendURL } from "./component/backendURL"; - import { Home } from "./pages/home"; -import { Demo } from "./pages/demo"; -import { Single } from "./pages/single"; import injectContext from "./store/appContext"; - import { Navbar } from "./component/navbar"; import { Footer } from "./component/footer"; +import { LogIn } from "./pages/logIn"; +import { SignUp } from "./pages/signUp"; +import { PersonalZone } from "./pages/personalZone"; +import { Tendencias } from "./pages/tendencias"; +import { VistaIndividualPhone } from "./pages/vistaIndividualPhone"; +import { Catalog } from "./pages/catalog"; +import { VistaIndividualTv } from "./pages/vistaIndividualTV"; +import { VistaIndividualLaptop } from "./pages/vistaIndividualLaptop"; +import { Cart } from "./pages/cart"; +import { PasarelaPago } from "./pages/PasarelaPago"; +import { MessagePayment } from "./pages/ConfirmPay"; + //create your first component const Layout = () => { @@ -17,19 +24,31 @@ const Layout = () => { // you can set the basename on the .env file located at the root of this project, E.g: BASENAME=/react-hello-webapp/ const basename = process.env.BASENAME || ""; - if(!process.env.BACKEND_URL || process.env.BACKEND_URL == "") return ; - return (
- - } path="/" /> - } path="/demo" /> - } path="/single/:theid" /> - Not found!} /> - +
+ + } path="/" /> + } path="/login" /> + } path="/signup" /> + } path="/personalzone" /> + } path="/tendencias" /> + } path="/smartphone-info/:smartphone_id" /> + } path="/tv-info/:tv_id" /> + } path="/laptop-info/:laptop_id" /> + } path="/phones-catalog" /> + } path="/tvs-catalog" /> + } path="/laptops-catalog" /> + } path="/message-payment" /> + } path="/cart" /> + } path="/pasarela-pago"/> + Not found!} /> + + +