diff --git a/.vscode/settings.json b/.vscode/settings.json
index 246b0419d0..37ade16d4b 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,10 +1,16 @@
{
- "editor.formatOnSave": true,
- "editor.defaultFormatter": "esbenp.prettier-vscode",
- "workbench.editorAssociations": {
- "*.md": "vscode.markdown.preview.editor"
- },
- "[javascriptreact]": {
- "editor.defaultFormatter": "vscode.typescript-language-features"
- }
+ "editor.formatOnSave": true,
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ "workbench.editorAssociations": {
+ "*.md": "vscode.markdown.preview.editor"
+ },
+ "[javascriptreact]": {
+ "editor.defaultFormatter": "vscode.typescript-language-features"
+ },
+ "github.copilot.enable": {
+ "*": true,
+ "plaintext": false,
+ "markdown": false,
+ "scminput": false
+ }
}
diff --git a/Pipfile b/Pipfile
index 44e04f14ff..8cfda7d8a7 100644
--- a/Pipfile
+++ b/Pipfile
@@ -11,8 +11,6 @@ flask-sqlalchemy = "*"
flask-migrate = "*"
flask-swagger = "*"
psycopg2-binary = "*"
-python-dotenv = "*"
-flask-cors = "*"
gunicorn = "*"
cloudinary = "*"
flask-admin = "*"
@@ -20,6 +18,10 @@ typing-extensions = "*"
flask-jwt-extended = "==4.6.0"
wtforms = "==3.1.2"
sqlalchemy = "*"
+requests = "*"
+flask-cors = "*"
+python-dotenv = "*"
+werkzeug = "*"
[requires]
python_version = "3.13"
diff --git a/Pipfile.lock b/Pipfile.lock
index b201c3decc..eacd3b843e 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "d2e672e650278aeeee2fe49bd76d76497d8b65a50f8b5dbb121d265cbc6ef4e5"
+ "sha256": "e942ca1d51677e09f303df46ce3e18118677e46b7bd0fd08ec9f9ccb795631cb"
},
"pipfile-spec": 6,
"requires": {
@@ -34,11 +34,109 @@
},
"certifi": {
"hashes": [
- "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651",
- "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"
+ "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6",
+ "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3"
],
"markers": "python_version >= '3.6'",
- "version": "==2025.1.31"
+ "version": "==2025.4.26"
+ },
+ "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": [
@@ -62,6 +160,7 @@
"sha256:d667207822eb83f1c4b50949b1623c8fc8d51f2341d65f72e1a1815397551136"
],
"index": "pypi",
+ "markers": "python_version >= '3.9'",
"version": "==3.1.0"
},
"flask-admin": {
@@ -78,6 +177,7 @@
"sha256:fa5cb364ead54bbf401a26dbf03030c6b18fb2fcaf70408096a572b409586b0c"
],
"index": "pypi",
+ "markers": "python_version >= '3.9' and python_version < '4.0'",
"version": "==5.0.1"
},
"flask-jwt-extended": {
@@ -199,6 +299,14 @@
"index": "pypi",
"version": "==23.0.0"
},
+ "idna": {
+ "hashes": [
+ "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9",
+ "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==3.10"
+ },
"itsdangerous": {
"hashes": [
"sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef",
@@ -209,11 +317,11 @@
},
"jinja2": {
"hashes": [
- "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb",
- "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"
+ "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d",
+ "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"
],
"markers": "python_version >= '3.7'",
- "version": "==3.1.5"
+ "version": "==3.1.6"
},
"mako": {
"hashes": [
@@ -382,11 +490,12 @@
},
"python-dotenv": {
"hashes": [
- "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca",
- "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"
+ "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5",
+ "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d"
],
"index": "pypi",
- "version": "==1.0.1"
+ "markers": "python_version >= '3.9'",
+ "version": "==1.1.0"
},
"pyyaml": {
"hashes": [
@@ -447,6 +556,15 @@
"markers": "python_version >= '3.8'",
"version": "==6.0.2"
},
+ "requests": {
+ "hashes": [
+ "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760",
+ "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"
+ ],
+ "index": "pypi",
+ "markers": "python_version >= '3.8'",
+ "version": "==2.32.3"
+ },
"six": {
"hashes": [
"sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274",
@@ -528,17 +646,18 @@
},
"urllib3": {
"hashes": [
- "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df",
- "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"
+ "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466",
+ "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"
],
"markers": "python_version >= '3.9'",
- "version": "==2.3.0"
+ "version": "==2.4.0"
},
"werkzeug": {
"hashes": [
"sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e",
"sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"
],
+ "index": "pypi",
"markers": "python_version >= '3.9'",
"version": "==3.1.3"
},
diff --git a/README.md b/README.md
index 6b782b220d..a3b1ebfded 100644
--- a/README.md
+++ b/README.md
@@ -1,81 +1,2 @@
-# WebApp boilerplate with React JS and Flask API
-
-Build web applications using React.js for the front end and python/flask for your backend API.
-
-- Documentation can be found here: https://4geeks.com/docs/start/react-flask-template
-- Here is a video on [how to use this template](https://www.loom.com/share/f37c6838b3f1496c95111e515e83dd9b)
-- Integrated with Pipenv for package managing.
-- Fast deployment to Render [in just a few steps here](https://4geeks.com/docs/start/deploy-to-render-com).
-- Use of .env file.
-- SQLAlchemy integration for database abstraction.
-
-### 1) Installation:
-
-> If you use Github Codespaces (recommended) or Gitpod this template will already come with Python, Node and the Posgres Database installed. If you are working locally make sure to install Python 3.10, Node
-
-It is recomended to install the backend first, make sure you have Python 3.10, Pipenv and a database engine (Posgress recomended)
-
-1. Install the python packages: `$ pipenv install`
-2. Create a .env file based on the .env.example: `$ cp .env.example .env`
-3. Install your database engine and create your database, depending on your database you have to create a DATABASE_URL variable with one of the possible values, make sure you replace the valudes with your database information:
-
-| Engine | DATABASE_URL |
-| --------- | --------------------------------------------------- |
-| SQLite | sqlite:////test.db |
-| MySQL | mysql://username:password@localhost:port/example |
-| Postgress | postgres://username:password@localhost:5432/example |
-
-4. Migrate the migrations: `$ pipenv run migrate` (skip if you have not made changes to the models on the `./src/api/models.py`)
-5. Run the migrations: `$ pipenv run upgrade`
-6. Run the application: `$ pipenv run start`
-
-> Note: Codespaces users can connect to psql by typing: `psql -h localhost -U gitpod example`
-
-### Undo a migration
-
-You are also able to undo a migration by running
-
-```sh
-$ pipenv run downgrade
-```
-
-### Backend Populate Table Users
-
-To insert test users in the database execute the following command:
-
-```sh
-$ flask insert-test-users 5
-```
-
-And you will see the following message:
-
-```
- Creating test users
- test_user1@test.com created.
- test_user2@test.com created.
- test_user3@test.com created.
- test_user4@test.com created.
- test_user5@test.com created.
- Users created successfully!
-```
-
-### **Important note for the database and the data inside it**
-
-Every Github codespace environment will have **its own database**, so if you're working with more people eveyone will have a different database and different records inside it. This data **will be lost**, so don't spend too much time manually creating records for testing, instead, you can automate adding records to your database by editing ```commands.py``` file inside ```/src/api``` folder. Edit line 32 function ```insert_test_data``` to insert the data according to your model (use the function ```insert_test_users``` above as an example). Then, all you need to do is run ```pipenv run insert-test-data```.
-
-### Front-End Manual Installation:
-
-- Make sure you are using node version 20 and that you have already successfully installed and runned the backend.
-
-1. Install the packages: `$ npm install`
-2. Start coding! start the webpack dev server `$ npm run start`
-
-## Publish your website!
-
-This boilerplate it's 100% read to deploy with Render.com and Heroku in a matter of minutes. Please read the [official documentation about it](https://4geeks.com/docs/start/deploy-to-render-com).
-
-### Contributors
-
-This template was built as part of the 4Geeks Academy [Coding Bootcamp](https://4geeksacademy.com/us/coding-bootcamp) by [Alejandro Sanchez](https://twitter.com/alesanchezr) and many other contributors. Find out more about our [Full Stack Developer Course](https://4geeksacademy.com/us/coding-bootcamps/part-time-full-stack-developer), and [Data Science Bootcamp](https://4geeksacademy.com/us/coding-bootcamps/datascience-machine-learning).
-
-You can find other templates and resources like this at the [school github page](https://github.com/4geeksacademy/).
+If you receive error of requests module not found:
+pipenv install requests
diff --git a/READMEE.md b/READMEE.md
new file mode 100644
index 0000000000..f6c02befdf
--- /dev/null
+++ b/READMEE.md
@@ -0,0 +1,4 @@
+team members:
+Drew Wilson
+Israel Diaz
+Jacqueline Bringas
\ No newline at end of file
diff --git a/index.html b/index.html
index 27a99f796e..e971f00f22 100644
--- a/index.html
+++ b/index.html
@@ -6,7 +6,7 @@
-
Hello Rigo
+
diff --git a/migrations/versions/1912147c7f92_.py b/migrations/versions/1912147c7f92_.py
new file mode 100644
index 0000000000..574179025c
--- /dev/null
+++ b/migrations/versions/1912147c7f92_.py
@@ -0,0 +1,32 @@
+"""empty message
+
+Revision ID: 1912147c7f92
+Revises: 6a46316c7cb3
+Create Date: 2025-05-07 04:41:33.946971
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '1912147c7f92'
+down_revision = '6a46316c7cb3'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ with op.batch_alter_table('user', schema=None) as batch_op:
+ batch_op.add_column(sa.Column('phone', sa.String(length=20), nullable=False))
+
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ with op.batch_alter_table('user', schema=None) as batch_op:
+ batch_op.drop_column('phone')
+
+ # ### end Alembic commands ###
diff --git a/migrations/versions/0763d677d453_.py b/migrations/versions/1b6206a1749f_.py
similarity index 89%
rename from migrations/versions/0763d677d453_.py
rename to migrations/versions/1b6206a1749f_.py
index 88964176f1..e1de76e9f0 100644
--- a/migrations/versions/0763d677d453_.py
+++ b/migrations/versions/1b6206a1749f_.py
@@ -1,8 +1,8 @@
"""empty message
-Revision ID: 0763d677d453
+Revision ID: 1b6206a1749f
Revises:
-Create Date: 2025-02-25 14:47:16.337069
+Create Date: 2025-05-01 01:02:33.208414
"""
from alembic import op
@@ -10,7 +10,7 @@
# revision identifiers, used by Alembic.
-revision = '0763d677d453'
+revision = '1b6206a1749f'
down_revision = None
branch_labels = None
depends_on = None
diff --git a/migrations/versions/57561960a0c5_.py b/migrations/versions/57561960a0c5_.py
new file mode 100644
index 0000000000..642c24b203
--- /dev/null
+++ b/migrations/versions/57561960a0c5_.py
@@ -0,0 +1,34 @@
+"""empty message
+
+Revision ID: 57561960a0c5
+Revises: 6a46316c7cb3
+Create Date: 2025-05-07 14:55:24.074779
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '57561960a0c5'
+down_revision = '6a46316c7cb3'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ with op.batch_alter_table('user', schema=None) as batch_op:
+ batch_op.add_column(sa.Column('phone', sa.String(length=20), nullable=False))
+ batch_op.create_unique_constraint(None, ['phone'])
+
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ with op.batch_alter_table('user', schema=None) as batch_op:
+ batch_op.drop_constraint(None, type_='unique')
+ batch_op.drop_column('phone')
+
+ # ### end Alembic commands ###
diff --git a/migrations/versions/6a46316c7cb3_.py b/migrations/versions/6a46316c7cb3_.py
new file mode 100644
index 0000000000..0019805ac0
--- /dev/null
+++ b/migrations/versions/6a46316c7cb3_.py
@@ -0,0 +1,32 @@
+"""empty message
+
+Revision ID: 6a46316c7cb3
+Revises: 1b6206a1749f
+Create Date: 2025-05-06 00:48:27.082957
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '6a46316c7cb3'
+down_revision = '1b6206a1749f'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ with op.batch_alter_table('user', schema=None) as batch_op:
+ batch_op.add_column(sa.Column('name', sa.String(length=120), nullable=False))
+
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ with op.batch_alter_table('user', schema=None) as batch_op:
+ batch_op.drop_column('name')
+
+ # ### end Alembic commands ###
diff --git a/package-lock.json b/package-lock.json
index 8d43d98ab7..b1469bd2fd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,10 +9,11 @@
"version": "1.0.1",
"license": "ISC",
"dependencies": {
+ "bootstrap-icons": "^1.12.1",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
- "react-router-dom": "^6.18.0"
+ "react-router-dom": "^6.30.0"
},
"devDependencies": {
"@types/react": "^18.2.18",
@@ -22,7 +23,7 @@
"eslint-plugin-react": "^7.33.1",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
- "vite": "^4.4.8"
+ "vite": "^6.3.3"
},
"engines": {
"node": ">=20.0.0"
@@ -229,27 +230,27 @@
}
},
"node_modules/@babel/helpers": {
- "version": "7.26.7",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz",
- "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==",
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz",
+ "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/template": "^7.25.9",
- "@babel/types": "^7.26.7"
+ "@babel/template": "^7.27.0",
+ "@babel/types": "^7.27.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
- "version": "7.26.7",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz",
- "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==",
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz",
+ "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/types": "^7.26.7"
+ "@babel/types": "^7.27.0"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -291,15 +292,15 @@
}
},
"node_modules/@babel/template": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
- "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz",
+ "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.25.9",
- "@babel/parser": "^7.25.9",
- "@babel/types": "^7.25.9"
+ "@babel/code-frame": "^7.26.2",
+ "@babel/parser": "^7.27.0",
+ "@babel/types": "^7.27.0"
},
"engines": {
"node": ">=6.9.0"
@@ -325,9 +326,9 @@
}
},
"node_modules/@babel/types": {
- "version": "7.26.7",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz",
- "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==",
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
+ "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -338,10 +339,27 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz",
+ "integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@esbuild/android-arm": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
- "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz",
+ "integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==",
"cpu": [
"arm"
],
@@ -352,13 +370,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
- "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz",
+ "integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==",
"cpu": [
"arm64"
],
@@ -369,13 +387,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
- "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz",
+ "integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==",
"cpu": [
"x64"
],
@@ -386,13 +404,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
- "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz",
+ "integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==",
"cpu": [
"arm64"
],
@@ -403,13 +421,13 @@
"darwin"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
- "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz",
+ "integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==",
"cpu": [
"x64"
],
@@ -420,13 +438,13 @@
"darwin"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
- "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz",
+ "integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==",
"cpu": [
"arm64"
],
@@ -437,13 +455,13 @@
"freebsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
- "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz",
+ "integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==",
"cpu": [
"x64"
],
@@ -454,13 +472,13 @@
"freebsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
- "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz",
+ "integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==",
"cpu": [
"arm"
],
@@ -471,13 +489,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
- "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz",
+ "integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==",
"cpu": [
"arm64"
],
@@ -488,13 +506,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
- "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz",
+ "integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==",
"cpu": [
"ia32"
],
@@ -505,13 +523,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
- "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz",
+ "integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==",
"cpu": [
"loong64"
],
@@ -522,13 +540,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
- "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz",
+ "integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==",
"cpu": [
"mips64el"
],
@@ -539,13 +557,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
- "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz",
+ "integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==",
"cpu": [
"ppc64"
],
@@ -556,13 +574,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
- "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz",
+ "integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==",
"cpu": [
"riscv64"
],
@@ -573,13 +591,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
- "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz",
+ "integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==",
"cpu": [
"s390x"
],
@@ -590,13 +608,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
- "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz",
+ "integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==",
"cpu": [
"x64"
],
@@ -607,13 +625,30 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz",
+ "integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
- "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz",
+ "integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==",
"cpu": [
"x64"
],
@@ -624,13 +659,30 @@
"netbsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz",
+ "integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
- "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz",
+ "integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==",
"cpu": [
"x64"
],
@@ -641,13 +693,13 @@
"openbsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
- "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz",
+ "integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==",
"cpu": [
"x64"
],
@@ -658,13 +710,13 @@
"sunos"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
- "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz",
+ "integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==",
"cpu": [
"arm64"
],
@@ -675,13 +727,13 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
- "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz",
+ "integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==",
"cpu": [
"ia32"
],
@@ -692,13 +744,13 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
- "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz",
+ "integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==",
"cpu": [
"x64"
],
@@ -709,7 +761,7 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@eslint-community/eslint-utils": {
@@ -944,14 +996,294 @@
}
},
"node_modules/@remix-run/router": {
- "version": "1.22.0",
- "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.22.0.tgz",
- "integrity": "sha512-MBOl8MeOzpK0HQQQshKB7pABXbmyHizdTpqnrIseTbsv0nAepwC2ENZa1aaBExNQcpLoXmWthhak8SABLzvGPw==",
+ "version": "1.23.0",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz",
+ "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==",
"license": "MIT",
"engines": {
"node": ">=14.0.0"
}
},
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz",
+ "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz",
+ "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz",
+ "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz",
+ "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz",
+ "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz",
+ "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz",
+ "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz",
+ "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz",
+ "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz",
+ "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz",
+ "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz",
+ "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz",
+ "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz",
+ "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz",
+ "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz",
+ "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz",
+ "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz",
+ "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz",
+ "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz",
+ "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
"node_modules/@types/babel__core": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
@@ -997,13 +1329,12 @@
"@babel/types": "^7.20.7"
}
},
- "node_modules/@types/node": {
- "version": "16.11.12",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz",
- "integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==",
+ "node_modules/@types/estree": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
+ "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
"dev": true,
- "optional": true,
- "peer": true
+ "license": "MIT"
},
"node_modules/@types/prop-types": {
"version": "15.7.14",
@@ -1264,6 +1595,22 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
+ "node_modules/bootstrap-icons": {
+ "version": "1.12.1",
+ "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.12.1.tgz",
+ "integrity": "sha512-ekwupjsteHQmgGV+haQ0nNMoSyKCbJj5ou+06vFzb9uR2/bwN9isNEgXBaQzcT+fLzhKS3OaBNpwz8XdZlIgYQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/twbs"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/bootstrap"
+ }
+ ],
+ "license": "MIT"
+ },
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -1743,9 +2090,9 @@
}
},
"node_modules/esbuild": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
- "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz",
+ "integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -1753,31 +2100,34 @@
"esbuild": "bin/esbuild"
},
"engines": {
- "node": ">=12"
+ "node": ">=18"
},
"optionalDependencies": {
- "@esbuild/android-arm": "0.18.20",
- "@esbuild/android-arm64": "0.18.20",
- "@esbuild/android-x64": "0.18.20",
- "@esbuild/darwin-arm64": "0.18.20",
- "@esbuild/darwin-x64": "0.18.20",
- "@esbuild/freebsd-arm64": "0.18.20",
- "@esbuild/freebsd-x64": "0.18.20",
- "@esbuild/linux-arm": "0.18.20",
- "@esbuild/linux-arm64": "0.18.20",
- "@esbuild/linux-ia32": "0.18.20",
- "@esbuild/linux-loong64": "0.18.20",
- "@esbuild/linux-mips64el": "0.18.20",
- "@esbuild/linux-ppc64": "0.18.20",
- "@esbuild/linux-riscv64": "0.18.20",
- "@esbuild/linux-s390x": "0.18.20",
- "@esbuild/linux-x64": "0.18.20",
- "@esbuild/netbsd-x64": "0.18.20",
- "@esbuild/openbsd-x64": "0.18.20",
- "@esbuild/sunos-x64": "0.18.20",
- "@esbuild/win32-arm64": "0.18.20",
- "@esbuild/win32-ia32": "0.18.20",
- "@esbuild/win32-x64": "0.18.20"
+ "@esbuild/aix-ppc64": "0.25.3",
+ "@esbuild/android-arm": "0.25.3",
+ "@esbuild/android-arm64": "0.25.3",
+ "@esbuild/android-x64": "0.25.3",
+ "@esbuild/darwin-arm64": "0.25.3",
+ "@esbuild/darwin-x64": "0.25.3",
+ "@esbuild/freebsd-arm64": "0.25.3",
+ "@esbuild/freebsd-x64": "0.25.3",
+ "@esbuild/linux-arm": "0.25.3",
+ "@esbuild/linux-arm64": "0.25.3",
+ "@esbuild/linux-ia32": "0.25.3",
+ "@esbuild/linux-loong64": "0.25.3",
+ "@esbuild/linux-mips64el": "0.25.3",
+ "@esbuild/linux-ppc64": "0.25.3",
+ "@esbuild/linux-riscv64": "0.25.3",
+ "@esbuild/linux-s390x": "0.25.3",
+ "@esbuild/linux-x64": "0.25.3",
+ "@esbuild/netbsd-arm64": "0.25.3",
+ "@esbuild/netbsd-x64": "0.25.3",
+ "@esbuild/openbsd-arm64": "0.25.3",
+ "@esbuild/openbsd-x64": "0.25.3",
+ "@esbuild/sunos-x64": "0.25.3",
+ "@esbuild/win32-arm64": "0.25.3",
+ "@esbuild/win32-ia32": "0.25.3",
+ "@esbuild/win32-x64": "0.25.3"
}
},
"node_modules/escalade": {
@@ -2240,6 +2590,21 @@
"reusify": "^1.0.4"
}
},
+ "node_modules/fdir": {
+ "version": "6.4.4",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
+ "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
"node_modules/file-entry-cache": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -2294,11 +2659,12 @@
"dev": true
},
"node_modules/fsevents": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
- "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
+ "license": "MIT",
"optional": true,
"os": [
"darwin"
@@ -3160,9 +3526,9 @@
"dev": true
},
"node_modules/nanoid": {
- "version": "3.3.8",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
- "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true,
"funding": [
{
@@ -3392,6 +3758,19 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/possible-typed-array-names": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
@@ -3403,9 +3782,9 @@
}
},
"node_modules/postcss": {
- "version": "8.5.1",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz",
- "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==",
+ "version": "8.5.3",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
+ "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
"dev": true,
"funding": [
{
@@ -3522,12 +3901,12 @@
}
},
"node_modules/react-router": {
- "version": "6.29.0",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.29.0.tgz",
- "integrity": "sha512-DXZJoE0q+KyeVw75Ck6GkPxFak63C4fGqZGNijnWgzB/HzSP1ZfTlBj5COaGWwhrMQ/R8bXiq5Ooy4KG+ReyjQ==",
+ "version": "6.30.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.0.tgz",
+ "integrity": "sha512-D3X8FyH9nBcTSHGdEKurK7r8OYE1kKFn3d/CF+CoxbSHkxU7o37+Uh7eAHRXr6k2tSExXYO++07PeXJtA/dEhQ==",
"license": "MIT",
"dependencies": {
- "@remix-run/router": "1.22.0"
+ "@remix-run/router": "1.23.0"
},
"engines": {
"node": ">=14.0.0"
@@ -3537,13 +3916,13 @@
}
},
"node_modules/react-router-dom": {
- "version": "6.29.0",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.29.0.tgz",
- "integrity": "sha512-pkEbJPATRJ2iotK+wUwHfy0xs2T59YPEN8BQxVCPeBZvK7kfPESRc/nyxzdcxR17hXgUPYx2whMwl+eo9cUdnQ==",
+ "version": "6.30.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.0.tgz",
+ "integrity": "sha512-x30B78HV5tFk8ex0ITwzC9TTZMua4jGyA9IUlH1JLQYQTFyxr/ZxwOJq7evg1JX1qGVUcvhsmQSKdPncQrjTgA==",
"license": "MIT",
"dependencies": {
- "@remix-run/router": "1.22.0",
- "react-router": "6.29.0"
+ "@remix-run/router": "1.23.0",
+ "react-router": "6.30.0"
},
"engines": {
"node": ">=14.0.0"
@@ -3632,19 +4011,42 @@
}
},
"node_modules/rollup": {
- "version": "3.29.5",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz",
- "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==",
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz",
+ "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.7"
+ },
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
- "node": ">=14.18.0",
+ "node": ">=18.0.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.40.0",
+ "@rollup/rollup-android-arm64": "4.40.0",
+ "@rollup/rollup-darwin-arm64": "4.40.0",
+ "@rollup/rollup-darwin-x64": "4.40.0",
+ "@rollup/rollup-freebsd-arm64": "4.40.0",
+ "@rollup/rollup-freebsd-x64": "4.40.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.40.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.40.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.40.0",
+ "@rollup/rollup-linux-arm64-musl": "4.40.0",
+ "@rollup/rollup-linux-loongarch64-gnu": "4.40.0",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.40.0",
+ "@rollup/rollup-linux-riscv64-musl": "4.40.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.40.0",
+ "@rollup/rollup-linux-x64-gnu": "4.40.0",
+ "@rollup/rollup-linux-x64-musl": "4.40.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.40.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.40.0",
+ "@rollup/rollup-win32-x64-msvc": "4.40.0",
"fsevents": "~2.3.2"
}
},
@@ -4109,6 +4511,23 @@
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
"dev": true
},
+ "node_modules/tinyglobby": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
+ "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -4273,41 +4692,51 @@
}
},
"node_modules/vite": {
- "version": "4.5.9",
- "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.9.tgz",
- "integrity": "sha512-qK9W4xjgD3gXbC0NmdNFFnVFLMWSNiR3swj957yutwzzN16xF/E7nmtAyp1rT9hviDroQANjE4HK3H4WqWdFtw==",
+ "version": "6.3.5",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
+ "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "esbuild": "^0.18.10",
- "postcss": "^8.4.27",
- "rollup": "^3.27.1"
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.3",
+ "rollup": "^4.34.9",
+ "tinyglobby": "^0.2.13"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
- "node": "^14.18.0 || >=16.0.0"
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
},
"optionalDependencies": {
- "fsevents": "~2.3.2"
+ "fsevents": "~2.3.3"
},
"peerDependencies": {
- "@types/node": ">= 14",
+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "jiti": ">=1.21.0",
"less": "*",
"lightningcss": "^1.21.0",
"sass": "*",
+ "sass-embedded": "*",
"stylus": "*",
"sugarss": "*",
- "terser": "^5.4.0"
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
+ "jiti": {
+ "optional": true
+ },
"less": {
"optional": true
},
@@ -4317,6 +4746,9 @@
"sass": {
"optional": true
},
+ "sass-embedded": {
+ "optional": true
+ },
"stylus": {
"optional": true
},
@@ -4325,6 +4757,12 @@
},
"terser": {
"optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
}
}
},
@@ -4615,22 +5053,22 @@
"dev": true
},
"@babel/helpers": {
- "version": "7.26.7",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz",
- "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==",
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz",
+ "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==",
"dev": true,
"requires": {
- "@babel/template": "^7.25.9",
- "@babel/types": "^7.26.7"
+ "@babel/template": "^7.27.0",
+ "@babel/types": "^7.27.0"
}
},
"@babel/parser": {
- "version": "7.26.7",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz",
- "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==",
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz",
+ "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
"dev": true,
"requires": {
- "@babel/types": "^7.26.7"
+ "@babel/types": "^7.27.0"
}
},
"@babel/plugin-transform-react-jsx-self": {
@@ -4652,14 +5090,14 @@
}
},
"@babel/template": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
- "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz",
+ "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==",
"dev": true,
"requires": {
- "@babel/code-frame": "^7.25.9",
- "@babel/parser": "^7.25.9",
- "@babel/types": "^7.25.9"
+ "@babel/code-frame": "^7.26.2",
+ "@babel/parser": "^7.27.0",
+ "@babel/types": "^7.27.0"
}
},
"@babel/traverse": {
@@ -4678,166 +5116,187 @@
}
},
"@babel/types": {
- "version": "7.26.7",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz",
- "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==",
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
+ "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
"dev": true,
"requires": {
"@babel/helper-string-parser": "^7.25.9",
"@babel/helper-validator-identifier": "^7.25.9"
}
},
+ "@esbuild/aix-ppc64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz",
+ "integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==",
+ "dev": true,
+ "optional": true
+ },
"@esbuild/android-arm": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
- "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz",
+ "integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==",
"dev": true,
"optional": true
},
"@esbuild/android-arm64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
- "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz",
+ "integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==",
"dev": true,
"optional": true
},
"@esbuild/android-x64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
- "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz",
+ "integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==",
"dev": true,
"optional": true
},
"@esbuild/darwin-arm64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
- "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz",
+ "integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==",
"dev": true,
"optional": true
},
"@esbuild/darwin-x64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
- "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz",
+ "integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==",
"dev": true,
"optional": true
},
"@esbuild/freebsd-arm64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
- "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz",
+ "integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==",
"dev": true,
"optional": true
},
"@esbuild/freebsd-x64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
- "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz",
+ "integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==",
"dev": true,
"optional": true
},
"@esbuild/linux-arm": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
- "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz",
+ "integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==",
"dev": true,
"optional": true
},
"@esbuild/linux-arm64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
- "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz",
+ "integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==",
"dev": true,
"optional": true
},
"@esbuild/linux-ia32": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
- "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz",
+ "integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==",
"dev": true,
"optional": true
},
"@esbuild/linux-loong64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
- "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz",
+ "integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==",
"dev": true,
"optional": true
},
"@esbuild/linux-mips64el": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
- "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz",
+ "integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==",
"dev": true,
"optional": true
},
"@esbuild/linux-ppc64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
- "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz",
+ "integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==",
"dev": true,
"optional": true
},
"@esbuild/linux-riscv64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
- "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz",
+ "integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==",
"dev": true,
"optional": true
},
"@esbuild/linux-s390x": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
- "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz",
+ "integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==",
"dev": true,
"optional": true
},
"@esbuild/linux-x64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
- "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz",
+ "integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/netbsd-arm64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz",
+ "integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==",
"dev": true,
"optional": true
},
"@esbuild/netbsd-x64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
- "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz",
+ "integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/openbsd-arm64": {
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz",
+ "integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==",
"dev": true,
"optional": true
},
"@esbuild/openbsd-x64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
- "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz",
+ "integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==",
"dev": true,
"optional": true
},
"@esbuild/sunos-x64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
- "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz",
+ "integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==",
"dev": true,
"optional": true
},
"@esbuild/win32-arm64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
- "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz",
+ "integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==",
"dev": true,
"optional": true
},
"@esbuild/win32-ia32": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
- "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz",
+ "integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==",
"dev": true,
"optional": true
},
"@esbuild/win32-x64": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
- "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz",
+ "integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==",
"dev": true,
"optional": true
},
@@ -4999,9 +5458,149 @@
}
},
"@remix-run/router": {
- "version": "1.22.0",
- "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.22.0.tgz",
- "integrity": "sha512-MBOl8MeOzpK0HQQQshKB7pABXbmyHizdTpqnrIseTbsv0nAepwC2ENZa1aaBExNQcpLoXmWthhak8SABLzvGPw=="
+ "version": "1.23.0",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz",
+ "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA=="
+ },
+ "@rollup/rollup-android-arm-eabi": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz",
+ "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-android-arm64": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz",
+ "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-darwin-arm64": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz",
+ "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-darwin-x64": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz",
+ "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-freebsd-arm64": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz",
+ "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-freebsd-x64": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz",
+ "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz",
+ "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz",
+ "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz",
+ "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-arm64-musl": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz",
+ "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-loongarch64-gnu": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz",
+ "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-powerpc64le-gnu": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz",
+ "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz",
+ "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz",
+ "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz",
+ "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-x64-gnu": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz",
+ "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-x64-musl": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz",
+ "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz",
+ "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz",
+ "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-win32-x64-msvc": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz",
+ "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==",
+ "dev": true,
+ "optional": true
},
"@types/babel__core": {
"version": "7.20.5",
@@ -5044,13 +5643,11 @@
"@babel/types": "^7.20.7"
}
},
- "@types/node": {
- "version": "16.11.12",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz",
- "integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==",
- "dev": true,
- "optional": true,
- "peer": true
+ "@types/estree": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
+ "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
+ "dev": true
},
"@types/prop-types": {
"version": "15.7.14",
@@ -5230,6 +5827,11 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
+ "bootstrap-icons": {
+ "version": "1.12.1",
+ "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.12.1.tgz",
+ "integrity": "sha512-ekwupjsteHQmgGV+haQ0nNMoSyKCbJj5ou+06vFzb9uR2/bwN9isNEgXBaQzcT+fLzhKS3OaBNpwz8XdZlIgYQ=="
+ },
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -5560,33 +6162,36 @@
}
},
"esbuild": {
- "version": "0.18.20",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
- "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
- "dev": true,
- "requires": {
- "@esbuild/android-arm": "0.18.20",
- "@esbuild/android-arm64": "0.18.20",
- "@esbuild/android-x64": "0.18.20",
- "@esbuild/darwin-arm64": "0.18.20",
- "@esbuild/darwin-x64": "0.18.20",
- "@esbuild/freebsd-arm64": "0.18.20",
- "@esbuild/freebsd-x64": "0.18.20",
- "@esbuild/linux-arm": "0.18.20",
- "@esbuild/linux-arm64": "0.18.20",
- "@esbuild/linux-ia32": "0.18.20",
- "@esbuild/linux-loong64": "0.18.20",
- "@esbuild/linux-mips64el": "0.18.20",
- "@esbuild/linux-ppc64": "0.18.20",
- "@esbuild/linux-riscv64": "0.18.20",
- "@esbuild/linux-s390x": "0.18.20",
- "@esbuild/linux-x64": "0.18.20",
- "@esbuild/netbsd-x64": "0.18.20",
- "@esbuild/openbsd-x64": "0.18.20",
- "@esbuild/sunos-x64": "0.18.20",
- "@esbuild/win32-arm64": "0.18.20",
- "@esbuild/win32-ia32": "0.18.20",
- "@esbuild/win32-x64": "0.18.20"
+ "version": "0.25.3",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz",
+ "integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==",
+ "dev": true,
+ "requires": {
+ "@esbuild/aix-ppc64": "0.25.3",
+ "@esbuild/android-arm": "0.25.3",
+ "@esbuild/android-arm64": "0.25.3",
+ "@esbuild/android-x64": "0.25.3",
+ "@esbuild/darwin-arm64": "0.25.3",
+ "@esbuild/darwin-x64": "0.25.3",
+ "@esbuild/freebsd-arm64": "0.25.3",
+ "@esbuild/freebsd-x64": "0.25.3",
+ "@esbuild/linux-arm": "0.25.3",
+ "@esbuild/linux-arm64": "0.25.3",
+ "@esbuild/linux-ia32": "0.25.3",
+ "@esbuild/linux-loong64": "0.25.3",
+ "@esbuild/linux-mips64el": "0.25.3",
+ "@esbuild/linux-ppc64": "0.25.3",
+ "@esbuild/linux-riscv64": "0.25.3",
+ "@esbuild/linux-s390x": "0.25.3",
+ "@esbuild/linux-x64": "0.25.3",
+ "@esbuild/netbsd-arm64": "0.25.3",
+ "@esbuild/netbsd-x64": "0.25.3",
+ "@esbuild/openbsd-arm64": "0.25.3",
+ "@esbuild/openbsd-x64": "0.25.3",
+ "@esbuild/sunos-x64": "0.25.3",
+ "@esbuild/win32-arm64": "0.25.3",
+ "@esbuild/win32-ia32": "0.25.3",
+ "@esbuild/win32-x64": "0.25.3"
}
},
"escalade": {
@@ -5907,6 +6512,13 @@
"reusify": "^1.0.4"
}
},
+ "fdir": {
+ "version": "6.4.4",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
+ "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
+ "dev": true,
+ "requires": {}
+ },
"file-entry-cache": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -5948,9 +6560,9 @@
"dev": true
},
"fsevents": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
- "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"optional": true
},
@@ -6497,9 +7109,9 @@
"dev": true
},
"nanoid": {
- "version": "3.3.8",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
- "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true
},
"natural-compare": {
@@ -6653,6 +7265,12 @@
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true
},
+ "picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "dev": true
+ },
"possible-typed-array-names": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
@@ -6660,9 +7278,9 @@
"dev": true
},
"postcss": {
- "version": "8.5.1",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz",
- "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==",
+ "version": "8.5.3",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
+ "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
"dev": true,
"requires": {
"nanoid": "^3.3.8",
@@ -6727,20 +7345,20 @@
"dev": true
},
"react-router": {
- "version": "6.29.0",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.29.0.tgz",
- "integrity": "sha512-DXZJoE0q+KyeVw75Ck6GkPxFak63C4fGqZGNijnWgzB/HzSP1ZfTlBj5COaGWwhrMQ/R8bXiq5Ooy4KG+ReyjQ==",
+ "version": "6.30.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.0.tgz",
+ "integrity": "sha512-D3X8FyH9nBcTSHGdEKurK7r8OYE1kKFn3d/CF+CoxbSHkxU7o37+Uh7eAHRXr6k2tSExXYO++07PeXJtA/dEhQ==",
"requires": {
- "@remix-run/router": "1.22.0"
+ "@remix-run/router": "1.23.0"
}
},
"react-router-dom": {
- "version": "6.29.0",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.29.0.tgz",
- "integrity": "sha512-pkEbJPATRJ2iotK+wUwHfy0xs2T59YPEN8BQxVCPeBZvK7kfPESRc/nyxzdcxR17hXgUPYx2whMwl+eo9cUdnQ==",
+ "version": "6.30.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.0.tgz",
+ "integrity": "sha512-x30B78HV5tFk8ex0ITwzC9TTZMua4jGyA9IUlH1JLQYQTFyxr/ZxwOJq7evg1JX1qGVUcvhsmQSKdPncQrjTgA==",
"requires": {
- "@remix-run/router": "1.22.0",
- "react-router": "6.29.0"
+ "@remix-run/router": "1.23.0",
+ "react-router": "6.30.0"
}
},
"reflect.getprototypeof": {
@@ -6795,11 +7413,32 @@
}
},
"rollup": {
- "version": "3.29.5",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz",
- "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==",
- "dev": true,
- "requires": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz",
+ "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==",
+ "dev": true,
+ "requires": {
+ "@rollup/rollup-android-arm-eabi": "4.40.0",
+ "@rollup/rollup-android-arm64": "4.40.0",
+ "@rollup/rollup-darwin-arm64": "4.40.0",
+ "@rollup/rollup-darwin-x64": "4.40.0",
+ "@rollup/rollup-freebsd-arm64": "4.40.0",
+ "@rollup/rollup-freebsd-x64": "4.40.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.40.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.40.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.40.0",
+ "@rollup/rollup-linux-arm64-musl": "4.40.0",
+ "@rollup/rollup-linux-loongarch64-gnu": "4.40.0",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.40.0",
+ "@rollup/rollup-linux-riscv64-musl": "4.40.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.40.0",
+ "@rollup/rollup-linux-x64-gnu": "4.40.0",
+ "@rollup/rollup-linux-x64-musl": "4.40.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.40.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.40.0",
+ "@rollup/rollup-win32-x64-msvc": "4.40.0",
+ "@types/estree": "1.0.7",
"fsevents": "~2.3.2"
}
},
@@ -7124,6 +7763,16 @@
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
"dev": true
},
+ "tinyglobby": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
+ "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
+ "dev": true,
+ "requires": {
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2"
+ }
+ },
"type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -7224,15 +7873,18 @@
}
},
"vite": {
- "version": "4.5.9",
- "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.9.tgz",
- "integrity": "sha512-qK9W4xjgD3gXbC0NmdNFFnVFLMWSNiR3swj957yutwzzN16xF/E7nmtAyp1rT9hviDroQANjE4HK3H4WqWdFtw==",
- "dev": true,
- "requires": {
- "esbuild": "^0.18.10",
- "fsevents": "~2.3.2",
- "postcss": "^8.4.27",
- "rollup": "^3.27.1"
+ "version": "6.3.5",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
+ "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
+ "dev": true,
+ "requires": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.4",
+ "fsevents": "~2.3.3",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.3",
+ "rollup": "^4.34.9",
+ "tinyglobby": "^0.2.13"
}
},
"which": {
diff --git a/package.json b/package.json
index 0caab10749..5eb61e2710 100755
--- a/package.json
+++ b/package.json
@@ -8,10 +8,10 @@
"main": "index.js",
"scripts": {
"dev": "vite",
- "start": "vite",
- "build": "vite build",
- "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
- "preview": "vite preview"
+ "start": "vite",
+ "build": "vite build",
+ "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
+ "preview": "vite preview"
},
"author": {
"name": "Alejandro Sanchez",
@@ -30,13 +30,13 @@
"license": "ISC",
"devDependencies": {
"@types/react": "^18.2.18",
- "@types/react-dom": "^18.2.7",
- "@vitejs/plugin-react": "^4.0.4",
- "eslint": "^8.46.0",
- "eslint-plugin-react": "^7.33.1",
- "eslint-plugin-react-hooks": "^4.6.0",
- "eslint-plugin-react-refresh": "^0.4.3",
- "vite": "^4.4.8"
+ "@types/react-dom": "^18.2.7",
+ "@vitejs/plugin-react": "^4.0.4",
+ "eslint": "^8.46.0",
+ "eslint-plugin-react": "^7.33.1",
+ "eslint-plugin-react-hooks": "^4.6.0",
+ "eslint-plugin-react-refresh": "^0.4.3",
+ "vite": "^6.3.3"
},
"babel": {
"presets": [
@@ -54,9 +54,10 @@
]
},
"dependencies": {
+ "bootstrap-icons": "^1.12.1",
"prop-types": "^15.8.1",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
- "react-router-dom": "^6.18.0"
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-router-dom": "^6.30.0"
}
}
diff --git a/public/index.html b/public/index.html
index 9462644fe9..d539df0090 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1 +1,35 @@
-Hello Rigo with Vanilla.js
\ No newline at end of file
+
+
+
+
+
+ Hello Rigo with Vanilla.js
+
+
+
+
+
+
+
+
+
+
+
diff --git a/requirements.txt b/requirements.txt
index 4eac45f4f8..39d8350c51 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -24,3 +24,4 @@ sqlalchemy==1.3.23
urllib3==1.26.3; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'
werkzeug==1.0.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
wtforms==2.3.3
+
diff --git a/src/api/models.py b/src/api/models.py
index da515f6a1a..0892c8516b 100644
--- a/src/api/models.py
+++ b/src/api/models.py
@@ -6,14 +6,20 @@
class User(db.Model):
id: Mapped[int] = mapped_column(primary_key=True)
+ name: Mapped[str]= mapped_column(String(120), nullable=False)
email: Mapped[str] = mapped_column(String(120), unique=True, nullable=False)
password: Mapped[str] = mapped_column(nullable=False)
+ phone : Mapped[str] = mapped_column(String(20), unique=True, nullable=False)
is_active: Mapped[bool] = mapped_column(Boolean(), nullable=False)
+
def serialize(self):
return {
"id": self.id,
+ "name": self.name,
"email": self.email,
+ "phone": self.phone,
# do not serialize the password, its a security breach
- }
\ No newline at end of file
+ }
+
diff --git a/src/api/routes.py b/src/api/routes.py
index 029589a3a1..56b8ac326f 100644
--- a/src/api/routes.py
+++ b/src/api/routes.py
@@ -1,22 +1,390 @@
"""
This module takes care of starting the API Server, Loading the DB and Adding the endpoints
"""
+import requests
+import os
from flask import Flask, request, jsonify, url_for, Blueprint
from api.models import db, User
from api.utils import generate_sitemap, APIException
from flask_cors import CORS
+# ==>> loads the environment variables from the .env file, pip install python-dotenv
+from dotenv import load_dotenv
+from werkzeug.security import check_password_hash, generate_password_hash
+# ==>> this is used to create a JWT token for the user, pip install flask-jwt-extended
+from flask_jwt_extended import create_access_token
+
+load_dotenv() # reads .env and sets those variables into your environment
+
+# db and User → for querying the database.
+# check_password_hash → for checking if the password is correct.
+# create_access_token → for making a token.
+
+# ==>>a collection of routes, error handlers, etc., that you group together in one file (here, api/routes.py).
api = Blueprint('api', __name__)
# Allow CORS requests to this API
+# ==>> you can talk to your flask endpoins whithout the browser blocking the request.
CORS(api)
-@api.route('/hello', methods=['POST', 'GET'])
-def handle_hello():
+# ==>> this is the endpoint that will be called from the front end
+# ==>> Search Places by coordinates: Accepts E.G: { latitude: 40.75, longitude: -73.99, cocktail: "Mojito" }
+@api.route('/places', methods=['POST'])
+def get_places_of_drinks():
+
+ # ==>> get the google api key from the environment variables
+ GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
+ # ==>>Looks at the body of the incoming HTTP request and it parses the JSON text and returns a Python dict (or list) representing that JSON.
+ data = request.get_json()
+
+ if not isinstance(data, dict): # ==>> check if the data is a dictionary
+ # ==>> if the data is not a dictionary, return a 400 error
+ return jsonify({"error": "Payload must be a JSON object"}), 400
+
+ if not GOOGLE_API_KEY:
+ # ==>> if the key is not found, return a 500 error
+ return jsonify({"error": "Google API key not found"}), 500
+
+ # ==>> get the page token from the request that is used for pagination.
+ page_token = data.get("next_page_token")
+ # ==>> url for the google api
+ url = "https://maps.googleapis.com/maps/api/place/nearbysearch/json"
+
+ if page_token: # ==>> check if the page token is present in the request # We’re loading a “next” page
+ params = {
+ "pagetoken": page_token, # ==>> pass the page token to the request
+ "key": GOOGLE_API_KEY # ==>> get the google api key from the environment variables
+ }
+ else:
+ opennow = data.get("opennow")
+ latitude = data.get("latitude")
+ longitude = data.get("longitude")
+ cocktail = data.get("cocktail")
+
+ # ==>> check if all the values are present in the request
+ if not all([latitude, longitude, cocktail]):
+ # ==>> return a 400 error if any of the values are missing
+ return jsonify({"error": "Missing data"}), 400
+ try:
+ latitude = float(latitude)
+ longitude = float(longitude)
+
+ except (ValueError, TypeError): # ==>> check if the values are numbers
+ # ==>> if not, return a 400 error
+ return jsonify({"error": "Latitude and longitude must be numbers"}), 400
+ # First page: use location, radius, keyword…
+ params = { # ==>> parameters for the google api
+ "location": f"{latitude},{longitude}", # ==>> location of the user
+ "radius": 5000,
+ "type": "bar",
+ "keyword": cocktail,
+ "key": GOOGLE_API_KEY
+ }
+ if opennow: # ==>> check if the opennow parameter is present in the request
+ # ==>> add the opennow parameter to the request
+ params["opennow"] = "true"
+
+ # ==>> make a request to the google api with the parameters
+ res = requests.get(url, params=params)
+
+ if res.status_code != 200: # ==>> check if the request was successful
+ return jsonify({
+ "error": "Failed to fetch data from Google",
+ "details": res.text
+ }), 500
+ body = res.json()
+ print("📄 [Flask] Google JSON:", body)
+ places = body.get("results", [])
+ next_page_token = body.get("next_page_token")
+
+ filtered_places = []
+ for place in places:
+ if place.get("business_status") != "OPERATIONAL":
+ continue
+ # ==>> ↓↓↓get the photo reference of the place.↓↓↓ This accesses the "photos" key in the place dictionary, which is a list of dictionaries. ↓↓↓It tries to get the first dictionary in that list (or an empty dictionary if the list is empty) and then accesses the "photo_reference" key.
+ ref = place.get("photos", [{}])[0].get("photo_reference")
+ # ==>> check if the photo reference is not empty ## ↑↑↑one representative image per place,↑↑↑ and the first one is usually the best (it’s what Google thinks is most relevant)↑↑↑
+ if ref:
+ photo_url = (
+ f"https://maps.googleapis.com/maps/api/place/photo" # ==>> url for the google api
+ f"?maxwidth=400"
+ f"&photoreference={ref}" # ==>> photo reference of the place
+ f"&key={GOOGLE_API_KEY}" # ==>> google api key
+ )
+ else:
+ photo_url = None
+
+ filtered_places.append({
+ "name": place.get("name"),
+ # ==>> get the address of the place, First, it tries to get the "vicinity" key (a nearby address). If "vicinity" is not available, it falls back to "formatted_address" (the full address).
+ "address": place.get("vicinity") or place.get("formatted_address"),
+ "rating": place.get("rating"),
+ # ==>> get the location of the place This accesses the "geometry" key in the place dictionary, which contains a "location" key. "location" is another dictionary with latitude and longitude values.
+ "location": place["geometry"]["location"],
+ "user_ratings_total": place.get("user_ratings_total"),
+ "place_id": place.get("place_id"),
+ "photo_url": photo_url
+
+ })
+ return jsonify({
+ "places": filtered_places,
+ "next_page_token": next_page_token
+ }), 200 # ==>> return the filtered places as a json object with a 200 status code
+
+
+# ==>> Search Places in a specific location:Accepts E.G: { zip_code: "10001" } Returns { latitude: 40.75, longitude: -73.99}
+@api.route('/places/by-location', methods=['POST'])
+def get_places_by_location():
+ data = request.get_json()
+ GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
+ print(">>> [by-location] STEP 1: data =", data, " type:", type(data))
+ if not isinstance(data, dict):
+ return jsonify({"error": "Data must be a JSON object"}), 400
+ if not GOOGLE_API_KEY:
+ return jsonify({"error": "Google API key not found"}), 500
+
+ page_token = data.get("next_page_token")
+ filtered_places = []
+
+ if page_token: # ==>> check if the page token is present in the request # We’re loading a “next” page
+ # ==>> url for the google api
+ places_url = "https://maps.googleapis.com/maps/api/place/nearbysearch/json"
+ places_params = {
+ "pagetoken": page_token, # ==>> pass the page token to the request
+ "key": GOOGLE_API_KEY # ==>> get the google api key from the environment variables
+ }
+ # ==>> make a request to the google api with the parameters
+ places_res = requests.get(places_url, params=places_params)
+ if places_res.status_code != 200: # ==>> check if the request was successful
+ return jsonify({"error": "Failed to fetch data from Google", "details": places_res.text}), 500
+ body = places_res.json() # ==>> parse the response as json
+ raw_places = body.get("results", [])
+ next_page_token = body.get("next_page_token")
+
+ else:
+ location = data.get("location")
+ cocktail = data.get("cocktail")
+ print(">>> [by-location] STEP 2: location, cocktail =",
+ location, cocktail)
+
+ if not all([location, cocktail]): # ==>> check if all the values are present in the request
+ print(">>> [by-location] Missing location or cocktail")
+ # ==>> return a 400 error if any of the values are missing
+ return jsonify({"error": "Missing data"}), 400
+
+ # ==>> Call Geocoding API
+ geo_url = "https://maps.googleapis.com/maps/api/geocode/json"
+ geo_params = {
+ "address": location,
+ "key": GOOGLE_API_KEY
+ }
+ geo_res = requests.get(geo_url, params=geo_params)
+ print(">>> [by-location] STEP 3b: geocode status =",
+ geo_res.status_code)
+ print(">>> [by-location] geocode body snippet:",
+ geo_res.text[:200], "…")
+
+ if geo_res.status_code != 200: # ==>> check if the request was successful
+ # ==>> return a 500 error if the request failed
+ return jsonify({"error": "Failed to fetch data from Google Geocoding API"}), 500
+ geo_data = geo_res.json() # ==>> # parse JSON into a dict
+ # ==>> get the results from the response
+ results = geo_data.get("results", [])
+ if not results:
+ # ==>> return a 404 error if there are no results
+ return jsonify({"error": "No results found"}), 404
+
+ # ==>> get the location data from the first result
+ location_data = results[0]["geometry"]["location"]
+ # ==>> extract latitude and longitude
+ lat, lng = location_data["lat"], location_data["lng"]
+ print(
+ f">>> [by-location] STEP 4: resolved '{location_data}' → lat={lat}, lng={lng}")
+
+ # Build Nearby Search request
+ places_url = "https://maps.googleapis.com/maps/api/place/nearbysearch/json"
+ places_params = {
+ "location": f"{lat},{lng}",
+ "radius": 5000,
+ "type": "bar",
+ "keyword": cocktail,
+ "key": GOOGLE_API_KEY
+ }
- 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"
+ if data.get("opennow"): # ==>> check if the opennow parameter is present in the request
+ # ==>> add the opennow parameter to the request
+ places_params["opennow"] = "true"
+ print(">>> [by-location] STEP 5: places params =", places_params)
+
+ places_res = requests.get(places_url, params=places_params)
+ print(">>> [by-location] STEP 5b: places status =",
+ places_res.status_code)
+ print(">>> [by-location] places body snippet:",
+ places_res.text[:200], "…")
+ if places_res.status_code != 200:
+ return jsonify({"error": "Failed to fetch data from Google Places API", "details": places_res.text}), 500
+ body = places_res.json()
+ raw_places = body.get("results", [])
+ next_page_token = body.get("next_page_token")
+ print(">>> [by-location] STEP 6: received",
+ len(raw_places), "raw places")
+
+ for place in raw_places:
+ if place.get("business_status") != "OPERATIONAL":
+ continue
+ # ==>> ↓↓↓get the photo reference of the place.↓↓↓ This accesses the "photos" key in the place dictionary, which is a list of dictionaries. ↓↓↓It tries to get the first dictionary in that list (or an empty dictionary if the list is empty) and then accesses the "photo_reference" key.
+ ref = place.get("photos", [{}])[0].get("photo_reference")
+ # ==>> check if the photo reference is not empty ## ↑↑↑one representative image per place,↑↑↑ and the first one is usually the best (it’s what Google thinks is most relevant)↑↑↑
+ if ref:
+ photo_url = (
+ f"https://maps.googleapis.com/maps/api/place/photo" # ==>> url for the google api
+ f"?maxwidth=400"
+ f"&photoreference={ref}" # ==>> photo reference of the place
+ f"&key={GOOGLE_API_KEY}" # ==>> google api key
+ )
+ else:
+ photo_url = None
+
+ filtered_places.append(
+ {
+ "name": place.get("name"),
+ "address": place.get("vicinity") or place.get("formatted_address"),
+ "rating": place.get("rating"),
+ "user_ratings_total": place.get("user_ratings_total"),
+ "location": place["geometry"]["location"],
+ "place_id": place.get("place_id"),
+ "photo_url": photo_url
+ })
+ print(
+ f">>> [by-location] STEP 7: filtered down to {len(filtered_places)} bars")
+ return jsonify({
+ "places": filtered_places,
+ "next_page_token": next_page_token
+ }), 200
+
+
+# ==>> Endpoint for the Place Details API within the Google Maps Platform. It allows you to request detailed information about a specific place, such as a business or point of interest.
+@api.route('/places/details', methods=['POST'])
+def get_place_details():
+ # ==>> get the google api key from the environment variables
+ GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
+ if not GOOGLE_API_KEY: # ==>> check if the google api key is present
+ # ==>> return a 500 error if the google api key is missing
+ return jsonify({"error": "Google API key not found"}), 500
+ data = request.get_json() # ==>> get the data from the request
+ place_id = data.get("place_id") # ==>> get the place id from the request
+
+ if not place_id: # ==>> check if the place id is present in the request
+ # ==>> return a 400 error if the place id is missing
+ return jsonify({"error": "Missing place_id"}), 400
+
+ # ==>> url for the google api
+ url = "https://maps.googleapis.com/maps/api/place/details/json"
+ params = {
+ "place_id": place_id,
+ # ==>> fields to be returned in the response
+ "fields": "name,formatted_phone_number,opening_hours,website,reviews,photos,formatted_address",
+ "key": GOOGLE_API_KEY
}
+ # ==>> make a request to the google api with the parameters
+ res = requests.get(url, params=params)
+ if res.status_code != 200: # ==>> check if the request was successful
+ # ==>> return a 500 error if the request failed
+ return jsonify({"error": "Failed to fetch data from Google", "details": res.text}), 500
+
+ body = res.json() # ==>> parse the response as json
+ # Grab the “result” object from the Google response (or an empty dict if it’s missing)
+ details = body.get("result", {})
+ if not details: # ==>> check if the details are present in the response
+ # ==>> return a 404 error if the details are missing
+ return jsonify({"error": "No details found"}), 404
+
+ photo_urls = [] # ==>> initialize an empty list for the photo urls
+ for i in details.get("photos", []): # ==>> iterate over the photos in the details
+ # ==>> get the photo reference from the photo
+ ref = i.get("photo_reference")
+ photo_urls.append(
+ f"https://maps.googleapis.com/maps/api/place/photo"
+ f"?maxwidth=400"
+ f"&photoreference={ref}"
+ f"&key={GOOGLE_API_KEY}"
+ )
+
+ return jsonify({
+ "name": details.get("name"),
+ "formatted_address": details.get("formatted_address"),
+ "formatted_phone_number": details.get("formatted_phone_number", "N/A"),
+ "opening_hours": details.get("opening_hours", {}),
+ "website": details.get("website"),
+ "reviews": details.get("reviews", []),
+ "photos": photo_urls, # ==>> return the photo urls as a list
+ }), 200
+
+
+@api.route('/signin', methods=['POST'])
+def sign_in_user():
+ data = request.get_json() # ==>> get the data from the request
+ if not data:
+ # ==>> check if the data is present
+ return jsonify({"error": "Missing data"}), 400
+ email = data.get("email")
+ password = data.get("password")
+ if not email or not password:
+ return jsonify({"error": "Missing email or password"}), 400
+ # ==>> query the database for the user first value is the column name, second is the value from the request.
+ user = User.query.filter_by(email=email).first()
+ if not user:
+ return jsonify({"error": "User not found"}), 404
+ if not check_password_hash(user.password, password):
+ return jsonify({"error": "Invalid password"}), 401
+
+ access_token = create_access_token(identity=user.id)
+
+ return jsonify({
+ "token": access_token,
+ "user": user.serialize()
+ }), 200
+
+# Jackie
+
+
+@api.route("/signup", methods=["POST"])
+def signup():
+ name = request.json.get("name", None)
+ email = request.json.get("email", None)
+ password = request.json.get("password", None)
+ phone = request.json.get("phone", None)
+
+ if not name or not email or not password or not phone:
+ return jsonify({"msg": "Name, email, phone and password are required"}), 400
+
+ user = User.query.filter_by(email=email).first()
+ if user:
+ return jsonify({"msg": "User already exists"}), 409
+
+ hashed_password = generate_password_hash(password)
+
+ new_user = User(
+ name=name,
+ email=email,
+ phone=phone,
+ password=hashed_password,
+ is_active=True,
+ )
+
+ db.session.add(new_user)
+ try:
+ db.session.commit()
+ except Exception as e:
+ db.session.rollback()
+ print("Error saving user:", e)
+ return jsonify({"msg": "Database error"}), 500
+
+ return jsonify({"msg": "User created successfully"}), 201
+
- return jsonify(response_body), 200
+@api.route("/users", methods=["GET"])
+def get_users():
+ users = User.query.all()
+ return jsonify([user.serialize() for user in users]), 200
diff --git a/src/app.py b/src/app.py
index 0ea8351d5f..f3bd389174 100644
--- a/src/app.py
+++ b/src/app.py
@@ -3,6 +3,7 @@
"""
import os
from flask import Flask, request, jsonify, url_for, send_from_directory
+from flask_cors import CORS
from flask_migrate import Migrate
from flask_swagger import swagger
from api.utils import APIException, generate_sitemap
@@ -10,6 +11,7 @@
from api.routes import api
from api.admin import setup_admin
from api.commands import setup_commands
+from flask_jwt_extended import JWTManager
# from models import Person
@@ -17,7 +19,10 @@
static_file_dir = os.path.join(os.path.dirname(
os.path.realpath(__file__)), '../public/')
app = Flask(__name__)
+CORS(app)
app.url_map.strict_slashes = False
+app.config["JWT_SECRET_KEY"] = os.environ.get("JWT_SECRET_KEY")
+jwt = JWTManager(app)
# database condiguration
db_url = os.getenv("DATABASE_URL")
@@ -38,6 +43,7 @@
setup_commands(app)
# Add all endpoints form the API with a "api" prefix
+# ==>With url_prefix='/api', every route defined in your blueprint (like @api.route('/places') in routes.py) becomes reachable at /api/places.
app.register_blueprint(api, url_prefix='/api')
# Handle/serialize errors like a JSON object
@@ -57,6 +63,8 @@ def sitemap():
return send_from_directory(static_file_dir, 'index.html')
# any other endpoint will try to serve it like a static file
+
+
@app.route('/', methods=['GET'])
def serve_any_other_file(path):
if not os.path.isfile(os.path.join(static_file_dir, path)):
diff --git a/src/front/components/Navbar.jsx b/src/front/components/Navbar.jsx
index 30d43a2636..23a9c4d843 100644
--- a/src/front/components/Navbar.jsx
+++ b/src/front/components/Navbar.jsx
@@ -13,6 +13,17 @@ export const Navbar = () => {
Check the Context in action
+
+
+ Google Api
+
+
+
+
+ Custom ingredients
+
+
+
);
diff --git a/src/front/components/PrivateRoute.jsx b/src/front/components/PrivateRoute.jsx
new file mode 100644
index 0000000000..ba86075f62
--- /dev/null
+++ b/src/front/components/PrivateRoute.jsx
@@ -0,0 +1,7 @@
+import { Navigate } from "react-router-dom";
+import { useGlobalReducer } from "../hooks/useGlobalReducer";
+
+export const PrivateRoute = ({ children }) => {
+ const { store } = useGlobalReducer();
+ return store.token ? children : ;
+};
diff --git a/src/front/hooks/useGlobalReducer.jsx b/src/front/hooks/useGlobalReducer.jsx
index 6aeb9d768e..28ee35db42 100644
--- a/src/front/hooks/useGlobalReducer.jsx
+++ b/src/front/hooks/useGlobalReducer.jsx
@@ -18,7 +18,9 @@ export function StoreProvider({ children }) {
}
// Custom hook to access the global state and dispatch function.
-export default function useGlobalReducer() {
+export function useGlobalReducer() {
const { dispatch, store } = useContext(StoreContext)
return { dispatch, store };
-}
\ No newline at end of file
+}
+
+export default useGlobalReducer;
\ No newline at end of file
diff --git a/src/front/index.css b/src/front/index.css
index e69de29bb2..7f9f40cd92 100644
--- a/src/front/index.css
+++ b/src/front/index.css
@@ -0,0 +1,241 @@
+/* General Styles */
+.custom-app {
+ font-family: Arial, sans-serif;
+ text-align: center;
+ padding: 20px;
+ background-color: #f8f9fa;
+}
+
+.modal button:hover {
+ background: #0056b3;
+}
+/* body {
+ margin: 0;
+ padding: 0;
+ background: linear-gradient(135deg, #a1c4fd, #c2e9fb); Gradient background
+ font-family: 'Arial', sans-serif;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ min-height: 100vh; Full screen height
+ color: #333; Text color
+} */
+
+h1, h2, h3 {
+ font-family: 'Helvetica', sans-serif;
+ color: #444; /* Darker text color for headers */
+}
+
+button {
+ background-color: #007bff; /* Button background */
+ color: #fff; /* Button text */
+ border: none;
+ border-radius: 5px;
+ padding: 10px 15px;
+ cursor: pointer;
+ font-size: 1rem;
+ transition: background-color 0.3s, transform 0.3s;
+}
+
+button:hover {
+ background-color: #0056b3; /* Hover background */
+ transform: scale(1.05); /* Slight zoom on hover */
+}
+/* .Cocktail {
+/* Ingredient List */
+.custom-ingredient-list {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: 20px;
+ margin-top: 20px;
+}
+
+/* Ingredient Card */
+.ingredient-card {
+ background-color: white;
+ border-radius: 10px;
+ padding: 15px;
+ box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
+ width: 200px;
+ text-align: center;
+}
+
+.ingredient-card img {
+ width: 100px;
+ border-radius: 10px;
+ margin-top: 10px;
+}
+
+.ingredient-card h2 {
+ font-size: 18px;
+ margin-bottom: 10px;
+}
+
+/* Checkbox Styling */
+.ingredient-checkbox {
+ margin-top: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 20px;
+ background: rgba(255, 255, 255, 0.8);
+ border-radius: 10px;
+ box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
+ max-width: 600px;
+ margin: auto;
+}
+
+/* Search Bar */
+.search-bar {
+ display: flex;
+ width: 100%;
+ gap: 10px;
+}
+
+/* Create Cocktail Button */
+.create-cocktail-btn {
+ background-color: #28a745;
+ color: white;
+ border: none;
+ padding: 10px 15px;
+ font-size: 16px;
+ cursor: pointer;
+ border-radius: 5px;
+ margin-top: 20px;
+}
+
+.create-cocktail-btn:hover {
+ background-color: #218838;
+}
+
+/* Cocktail Card */
+.cocktail-card {
+ background-color: white;
+ border-radius: 10px;
+ padding: 15px;
+ box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
+ width: 250px;
+ text-align: center;
+ margin: 20px auto;
+}
+
+.cocktail-card h2 {
+ font-size: 20px;
+ margin-bottom: 10px;
+}
+
+.cocktail-card ul {
+ list-style-type: none;
+ padding: 0;
+}
+
+.cocktail-card ul li {
+ background-color: #ff6f61;
+ color: white;
+ padding: 5px;
+ margin: 3px;
+ border-radius: 5px;
+}
+
+/* General Styles */
+.favorites-app {
+ font-family: Arial, sans-serif;
+ padding: 20px;
+ background-color: #f8f9fa;
+ text-align: center;
+ display: flex;
+ flex-direction: row;
+ min-height: 100vh;
+}
+
+/* User Info Section (Fixed to the Left) */
+.user-info {
+ position: fixed;
+ top: 100px; /* Adjust based on navbar height */
+ left: 20px;
+ background-color: white;
+ padding: 15px;
+ border-radius: 8px;
+ box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
+ width: 220px;
+ text-align: left;
+}
+
+.user-info h3 {
+ margin: 0;
+ font-size: 18px;
+}
+
+.user-info p {
+ margin: 5px 0;
+ font-size: 14px;
+ color: gray;
+}
+
+/* Favorites Content (Ensuring Proper Spacing) */
+.favorites-content {
+ margin-left: 250px; /* Adjust to prevent overlap with user info */
+ flex-grow: 1;
+ padding: 20px;
+}
+
+/* Favorites Banner */
+h1 {
+ font-size: 24px;
+ margin-bottom: 20px;
+}
+
+/* Favorites List */
+.favorites-list {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: 20px;
+ margin-top: 20px;
+}
+
+/* Favorite Card */
+.favorite-card {
+ background-color: white;
+ border-radius: 10px;
+ padding: 15px;
+ box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
+ width: 250px;
+ text-align: center;
+}
+
+.favorite-card img {
+ width: 100px;
+ border-radius: 10px;
+ margin-top: 10px;
+}
+
+.favorite-card h2 {
+ font-size: 20px;
+ margin-bottom: 10px;
+}
+
+.favorite-card p {
+ font-size: 16px;
+ margin: 5px 0;
+}
+
+/* Remove Favorite Button */
+.remove-favorite-btn {
+ background-color: #dc3545;
+ color: white;
+ border: none;
+ padding: 10px 15px;
+ font-size: 16px;
+ cursor: pointer;
+ border-radius: 5px;
+ margin-top: 10px;
+}
+
+.remove-favorite-btn:hover {
+ background-color: #c82333;
+}
+
+
+
diff --git a/src/front/main.jsx b/src/front/main.jsx
index a5a3c781dc..e1648f4643 100644
--- a/src/front/main.jsx
+++ b/src/front/main.jsx
@@ -8,11 +8,11 @@ import { BackendURL } from './components/BackendURL';
const Main = () => {
- if(! import.meta.env.VITE_BACKEND_URL || import.meta.env.VITE_BACKEND_URL == "") return (
-
-
-
- );
+ // if(! import.meta.env.VITE_BACKEND_URL || import.meta.env.VITE_BACKEND_URL == "") return (
+ //
+ //
+ //
+ // );
return (
{/* Provide global state to all components */}
diff --git a/src/front/pages/Custom.jsx b/src/front/pages/Custom.jsx
new file mode 100644
index 0000000000..6c7afc1022
--- /dev/null
+++ b/src/front/pages/Custom.jsx
@@ -0,0 +1,104 @@
+import React, { useState, useEffect } from "react";
+
+
+const handleFetch = (setIngredients) => {
+ fetch("https://www.thecocktaildb.com/api/json/v1/1/list.php?i=list") // Fetch all ingredients
+ .then((res) => res.json())
+ .then((data) => {
+ if (data.drinks) {
+ setIngredients(data.drinks.map((drink) => drink.strIngredient1)); // Extract ingredient names
+ } else {
+ setIngredients([]);
+ }
+ })
+ .catch((err) => console.error(err));
+};
+
+// Function to generate a random cocktail name
+const generateCocktailName = () => {
+ const adjectives = ["Zesty", "Smooth", "Fiery", "Refreshing", "Bold", "Exotic", "Mystic", "Golden"];
+ const nouns = ["Sunset", "Storm", "Delight", "Twist", "Fusion", "Bliss", "Sensation", "Elixir"];
+
+ const randomAdjective = adjectives[Math.floor(Math.random() * adjectives.length)];
+ const randomNoun = nouns[Math.floor(Math.random() * nouns.length)];
+
+ return `${randomAdjective} ${randomNoun}`;
+};
+
+export const Custom = () => {
+ const [ingredients, setIngredients] = useState([]);
+ const [selectedIngredients, setSelectedIngredients] = useState([]);
+ const [cocktailCreated, setCocktailCreated] = useState(null);
+
+ useEffect(() => {
+ handleFetch(setIngredients);
+ }, []);
+
+ const handleIngredientToggle = (ingredient) => {
+ const newSelected = selectedIngredients.includes(ingredient)
+ ? selectedIngredients.filter((ing) => ing !== ingredient)
+ : [...selectedIngredients, ingredient];
+
+ setSelectedIngredients(newSelected);
+ };
+
+ const createCocktail = () => {
+ if (selectedIngredients.length === 0) {
+ alert("Please select at least one ingredient to create a cocktail!");
+ return;
+ }
+
+ setCocktailCreated({
+ name: generateCocktailName(), // Generate random cocktail name
+ ingredients: selectedIngredients,
+ });
+ };
+
+ return (
+
+
Ingredients List
+
+
+ {ingredients.length > 0 ? (
+ ingredients.map((ingredient) => (
+
+
{ingredient}
+
+
+ handleIngredientToggle(ingredient)}
+ />
+ Select Ingredient
+
+
+ ))
+ ) : (
+ "No Ingredients available!!!"
+ )}
+
+
+ {/* Button to create cocktail */}
+
+ Create Cocktail
+
+
+ {/* Display created cocktail */}
+ {cocktailCreated && (
+
+
{cocktailCreated.name}
+
Ingredients:
+
+ {cocktailCreated.ingredients.map((ingredient, index) => (
+ {ingredient}
+ ))}
+
+
+ )}
+
+ );
+};
diff --git a/src/front/pages/Favorites.jsx b/src/front/pages/Favorites.jsx
new file mode 100644
index 0000000000..8def43c278
--- /dev/null
+++ b/src/front/pages/Favorites.jsx
@@ -0,0 +1,67 @@
+import React, { useState, useEffect } from "react";
+
+const UserInfo = () => {
+ // Example user data (Replace with actual user data from local storage or backend)
+ const user = JSON.parse(localStorage.getItem("user")) || {
+ name: "John Doe",
+ email: "johndoe@example.com",
+ };
+
+ return (
+
+
{user.name}
+
{user.email}
+
+ );
+};
+
+export const Favorites = () => {
+ const [favorites, setFavorites] = useState([]);
+
+ // Load favorites from local storage on mount
+ useEffect(() => {
+ const storedFavorites = JSON.parse(localStorage.getItem("favorites")) || [];
+ setFavorites(storedFavorites);
+ }, []);
+
+ // Remove favorite and update local storage
+ const removeFavorite = (drinkId) => {
+ const updatedFavorites = favorites.filter(fav => fav.idDrink !== drinkId);
+ setFavorites(updatedFavorites);
+ localStorage.setItem("favorites", JSON.stringify(updatedFavorites));
+ };
+
+ return (
+
+ {/* Sidebar for User Info */}
+
+
+ {/* Favorites List */}
+
+
My Favorite Cocktails
+ {favorites.length > 0 ? (
+
+ {favorites.map((drink) => (
+
+
{drink.strDrink}
+
+
Glass: {drink.strGlass}
+
Category: {drink.strCategory}
+
+ {/* Remove Favorite Button */}
+
removeFavorite(drink.idDrink)}
+ >
+ Remove Favorite
+
+
+ ))}
+
+ ) : (
+
You haven't added any favorites yet.
+ )}
+
+
+ );
+};
diff --git a/src/front/pages/GoogleApi.jsx b/src/front/pages/GoogleApi.jsx
new file mode 100644
index 0000000000..2198c0c8a7
--- /dev/null
+++ b/src/front/pages/GoogleApi.jsx
@@ -0,0 +1,309 @@
+import { Link } from "react-router-dom";
+import useGlobalReducer from "../hooks/useGlobalReducer";
+import { useState } from "react";
+//import 'bootstrap-icons/font/bootstrap-icons.css';
+
+export const GoogleApi = () => {
+ const [places, setPlaces] = useState([]);
+ const [error, setError] = useState("");
+ const [selectedPlace, setSelectedPlace] = useState(null);
+ const [reviews, setReviews] = useState([]) // State to hold reviews of the selected place
+ const [selectedReview, setSelectedReview] = useState(null);
+ const [showPhotos, setShowPhotos] = useState(false);
+ const [showReviewModal, setShowReviewModal] = useState(false);
+ const [modalReviewText, setModalReviewText] = useState("");
+
+
+
+ const handleSearch = () => { // Function to handle the search button click
+ setSelectedPlace(null); // clear out old details
+ setError(""); // clear any past error
+ if (!navigator.geolocation) { // Check if geolocation is supported on browser // Function to handle the search button click--object is provided by the browser's JavaScript engine
+ setError("Geolocation is not supported by this browser."); // If geolocation is not supported, set an error message
+ return; // Exit the function if geolocation is not supported
+ }
+
+ navigator.geolocation.getCurrentPosition( // Get the current position of the user JavaScript feature for web development.
+ async (position) => { // Callback function to handle the position received from geolocation
+ const { latitude, longitude } = position.coords; // Extract latitude and longitude from the position object
+ const payload = { latitude, longitude, cocktail: "mojito" };
+ console.log("▶️ Using coords:", latitude, longitude);
+ console.log("!!!!This is the object of the geolocation!!! :", position);
+
+
+ try { // Try to fetch places from the backend API using the coordinates and cocktail type
+ const res = await fetch(`${import.meta.env.VITE_BACKEND_URL}/api/places`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" }, // Fixed header key
+ body: JSON.stringify(payload),
+ });
+ console.log("▶️ Fetch returned status:", res.status);
+ const data = await res.json();
+ console.log("▶️ Data received:", data);
+
+ if (data.error) { // Check if there is an error in the response
+ console.error("!!!Backend error:", data.error);
+ setError(data.error); // Set the error state with the error message from the backend
+ setPlaces([]);
+ } else {
+ setError(""); // Clear any previous error messages
+ console.log("▶️ Places found:", data.places);
+ setPlaces(data.places);
+ }
+ } catch (err) { // Catch any errors that occur during the fetch operation
+ console.error("!!!Error fetching places:", err);
+ setError(err.message);
+ }
+ },
+ (geoErr) => {
+ console.error("!!!Geolocation error:", geoErr);
+ setError("Unable to retrieve your location. Please try again later.");
+ }
+ );
+ };
+
+ const handleSelect = async (placeId) => { // Function to handle the selection of a place
+ const payload = { place_id: placeId }; // Create a payload with the selected place ID
+ const options = {
+ method: "POST",
+ headers: { "Content-Type": "application/json" }, // Fixed header key
+ body: JSON.stringify(payload), // Send the selected place ID to the backend
+ }
+ try {
+ const res = await fetch(`${import.meta.env.VITE_BACKEND_URL}/api/places/details`, options); // Fetch details of the selected place from the backend API
+ console.log("==>> DATA SENT:", res);
+ if (!res.ok) {
+ console.log("!!!Error fetching place details:", res.statusText);
+ setError("Failed to fetch place details. Please try again later.");
+ return; // Exit the function if the response is not OK so that you don’t try to parse an empty or error body.
+ }
+ const details = await res.json(); // Parse the response JSON to get the place details
+ setSelectedPlace(details); // Set the selected place details in the state// //update state so React re-renders your detail pane
+ setSelectedReview(null); // Clear any previously selected review
+ setReviews([]); // Clear the reviews state when a new place is selected
+ console.log("▶️ Place details:", details); // Log the place details to the console
+ }
+ catch (err) {
+ console.log("!!!Error fetching place details:", err);
+ setError(err.message); // Set the error state with the error message
+ }
+ };
+
+ const showsReviews = (reviews) => { // Function to show reviews of the selected place
+ if (!reviews || reviews.length === 0) { // Check if reviews are undefined or empty
+ console.log("!!!No reviews found for this place or undefined reviews:", reviews);
+ setReviews([]); // Clear the reviews state if no reviews are found
+ return; // Exit the function if no reviews are available //stops the function if something's wrong → no unnecessary code runs after it.
+ }
+ setReviews(reviews); // Set the reviews state with the reviews of the selected place
+ }
+
+ const showMorePhotos = () => {
+ setShowPhotos(!showPhotos); // Toggle the showPhotos state to show or hide photos
+ }
+
+
+
+ return (
+
+
+
+ {/* LEFT: Place details */}
+
+ {selectedPlace ? (
+
+ {selectedPlace.photos[0] && (
+
+ )}
+
+
+ ) :
Click a place to see more details
}
+
+
+ {/* CENTER: Photos */}
+
+ {(!showPhotos || !selectedPlace || !selectedPlace.photos || selectedPlace.photos.length <= 1) && (
+
+ Click "See more photos" to view the gallery.
+
+ )}
+ {showPhotos && selectedPlace.photos && selectedPlace.photos.slice(1).map((photoUrl, idx) => (
+
+ ))}
+
+
+
+ {/* RIGHT: List of places */}
+
+
+ {error &&
{error}
}
+ {places.length > 0 ? (
+
+ ) :
No places found.
}
+
+
+
+
+
+
+
+
+ {reviews.length === 0 && (
+
+ Please click on See Reviews above to see all the reviews.
+
+ )}
+
+ {reviews.map((r, index) => (
+
+
+ Rating: {r.rating}
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
Search drinks
+
+
Back home
+
+ {showReviewModal && (
+
setShowReviewModal(false)}>
+
e.stopPropagation()}>
+
+
+
Full Review
+ setShowReviewModal(false)}>
+ ×
+
+
+
+
+
+
+ )}
+
+
+
+ );
+};
+
+
+
+
+
+
+
+
+
+
diff --git a/src/front/pages/Home.jsx b/src/front/pages/Home.jsx
index 341ed21768..a9884c0ed9 100644
--- a/src/front/pages/Home.jsx
+++ b/src/front/pages/Home.jsx
@@ -34,7 +34,7 @@ export const Home = () => {
return (
-
Hello Rigo!!
+
Cocktail!!
diff --git a/src/front/pages/Profile.jsx b/src/front/pages/Profile.jsx
new file mode 100644
index 0000000000..e6b6e4d454
--- /dev/null
+++ b/src/front/pages/Profile.jsx
@@ -0,0 +1,31 @@
+import React from "react";
+import { Navigate, useNavigate } from "react-router-dom";
+import useGlobalReducer from "../hooks/useGlobalReducer";
+
+export const Profile = () => {
+ const { store, dispatch } = useGlobalReducer();
+ const navigate = useNavigate();
+
+ const handleLogout = () => {
+ localStorage.removeItem("token");
+ dispatch({ type: "SET_TOKEN", payload: null });
+ navigate("/signin");
+ };
+
+ return (
+ <>
+ {store.token ? (
+
+
👋 Welcome!
+
This is your private profile. You are logged in correctly ✅
+
+
Logout
+
+ ) : (
+
+ )}
+ >
+ );
+};
+
+export default Profile;
diff --git a/src/front/pages/Search.jsx b/src/front/pages/Search.jsx
new file mode 100644
index 0000000000..ad94d619c6
--- /dev/null
+++ b/src/front/pages/Search.jsx
@@ -0,0 +1,75 @@
+import React, { useState } from "react";
+
+const handleSearch = (searchItem, setDrinks) => {
+ if (!searchItem.trim()) {
+ console.error("Search input is empty");
+ return;
+ }
+
+ fetch(`https://www.thecocktaildb.com/api/json/v1/1/search.php?s=${searchItem}`)
+ .then(res => {
+ if (!res.ok) throw new Error(`HTTP error! Status: ${res.status}`);
+ return res.json();
+ })
+ .then(data => {
+ if (data.drinks) {
+ setDrinks(data.drinks);
+ } else {
+ setDrinks([]);
+ console.error("No drinks found for this search");
+ }
+ })
+ .catch(err => console.error("Fetch Error:", err));
+};
+
+export const Search = () => {
+ const [search, setSearch] = useState("");
+ const [drinks, setDrinks] = useState([]);
+
+ return (
+
+
+ setSearch(e.target.value)}
+ value={search}
+ />
+ handleSearch(search, setDrinks)} className="search-button">
+ Search
+
+
+
+ {/* Display the fetched drinks */}
+
+ {drinks.length > 0 ? (
+ drinks.map((drink) => (
+
+
{drink.strDrink}
+
+
Glass: {drink.strGlass}
+
Category: {drink.strCategory}
+
Ingredients:
+
+ {[...Array(15).keys()].map((i) => {
+ const ingredient = drink[`strIngredient${i + 1}`];
+ const measure = drink[`strMeasure${i + 1}`];
+ return (
+ ingredient && (
+
+ {`${measure || ""} ${ingredient}`.trim()}
+
+ )
+ );
+ })}
+
+
Instructions: {drink.strInstructions}
+
+ ))
+ ) : (
+
No drinks found
+ )}
+
+
+ );
+};
diff --git a/src/front/pages/mainpage.jsx b/src/front/pages/mainpage.jsx
new file mode 100644
index 0000000000..05768fe50a
--- /dev/null
+++ b/src/front/pages/mainpage.jsx
@@ -0,0 +1,65 @@
+import React from "react";
+
+export const MainPage = () => {
+ return (
+
+ {/* Logo */}
+
+
LOGO
+
+
+ {/* Sign Up */}
+
+ Sign Up
+
+
+ {/* Sign In */}
+
+ Sign In
+
+
+ {/* Password Recovery */}
+
+
+ Password Recovery
+
+
+
+ );
+};
diff --git a/src/front/pages/password.jsx b/src/front/pages/password.jsx
new file mode 100644
index 0000000000..626749e05e
--- /dev/null
+++ b/src/front/pages/password.jsx
@@ -0,0 +1,67 @@
+import React from "react";
+
+export const Password = () => {
+ return (
+
+
+
+
+
+
+
+
Reset Password
+
+ );
+};
diff --git a/src/front/pages/signIn.jsx b/src/front/pages/signIn.jsx
new file mode 100644
index 0000000000..69f54a1f82
--- /dev/null
+++ b/src/front/pages/signIn.jsx
@@ -0,0 +1,125 @@
+import React, { useState } from "react";
+import useGlobalReducer from "../hooks/useGlobalReducer";
+import { useNavigate} from "react-router-dom";
+
+export const SignIn = () => {
+ const { dispatch } = useGlobalReducer();
+ const navigate = useNavigate();
+
+ const [formData, setFormData] = useState({
+ email: "",
+ password: "",
+ });
+
+ const [error, setError] = useState("");
+
+ const handleChange = (e) => {
+ setFormData({ ...formData, [e.target.name]: e.target.value });
+ setError("");
+ };
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+
+ if (!formData.email || !formData.password) {
+ setError("Please complete all fields");
+ return;
+ }
+
+ try {
+ console.log(`${import.meta.env.VITE_BACKEND_URL}/api/signin`);
+
+ const response = await fetch(`${import.meta.env.VITE_BACKEND_URL}/api/signin`, {
+
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(formData),
+ });
+
+ const data = await response.json();
+
+ if (response.ok && data.token) {
+ localStorage.setItem("token", data.token);
+ dispatch({ type: "SET_TOKEN", payload: data.token });
+ navigate("/profile");
+ } else {
+ setError("Invalid email or password.");
+ }
+ } catch (err) {
+ console.error("Login error:", err);
+ setError("Invalid email or password.");
+ }
+ };
+
+ return (
+
+ );
+};
diff --git a/src/front/pages/signUp.jsx b/src/front/pages/signUp.jsx
new file mode 100644
index 0000000000..bcaaacca9c
--- /dev/null
+++ b/src/front/pages/signUp.jsx
@@ -0,0 +1,136 @@
+import React, { useState } from "react";
+
+export const SignUp = () => {
+
+ const [formData, setFormData] = useState({
+ name: "",
+ email: "",
+ password: "",
+ phone: ""
+ });
+
+ const handleChange = (e) => {
+ setFormData({ ...formData, [e.target.name]: e.target.value });
+ };
+
+ const handleSubmit = async (e) => {
+ e.preventDefault(); // stops page to reload
+ console.log(">>>Form Data to be sent!!!:", formData);
+ try {
+ const fetchSignUp = await fetch(`${import.meta.env.VITE_BACKEND_URL}/api/signup`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(formData) //
+ })
+ console.log(">>>Response Status!!!:", fetchSignUp.status);
+
+
+ const data = await fetchSignUp.json()
+ console.log(">>>Response Body!!!!:", data);
+ if (fetchSignUp.ok) {
+ alert("Sign Up Successful!")
+ setFormData( // reset form data
+ {
+ name: "",
+ email: "",
+ password: "",
+ phone: ""
+ })
+ }
+ else {
+ alert("Sign Up failed: " + data.msg);
+ setFormData({
+ name: "",
+ email: "",
+ password: "",
+ phone: ""
+ }) // clear password field
+ }
+ }
+ catch (err) {
+ console.log("Error:", err);
+ }
+ };
+
+ return (
+
+
+ );
+};
diff --git a/src/front/routes.jsx b/src/front/routes.jsx
index 0557df6141..a9c67472da 100644
--- a/src/front/routes.jsx
+++ b/src/front/routes.jsx
@@ -1,30 +1,42 @@
-// Import necessary components and functions from react-router-dom.
-
+// Import necessary components and functions from react-router-dom
import {
- createBrowserRouter,
- createRoutesFromElements,
- Route,
+ createBrowserRouter,
+ createRoutesFromElements,
+ Route,
} from "react-router-dom";
import { Layout } from "./pages/Layout";
import { Home } from "./pages/Home";
import { Single } from "./pages/Single";
import { Demo } from "./pages/Demo";
+import { SignUp } from "./pages/signUp";
+import { GoogleApi } from "./pages/GoogleApi";
+import { Search } from "./pages/Search";
+import { Custom } from "./pages/Custom";
+import { SignIn } from "./pages/signIn";
+import { Password } from "./pages/password";
+import { MainPage } from "./pages/mainpage";
+import { Profile} from "./pages/Profile";
+import { PrivateRoute } from "./components/PrivateRoute";
+import { Favorites } from "./pages/Favorites";
export const router = createBrowserRouter(
- createRoutesFromElements(
- // CreateRoutesFromElements function allows you to build route elements declaratively.
- // Create your routes here, if you want to keep the Navbar and Footer in all views, add your new routes inside the containing Route.
- // Root, on the contrary, create a sister Route, if you have doubts, try it!
- // Note: keep in mind that errorElement will be the default page when you don't get a route, customize that page to make your project more attractive.
- // Note: The child paths of the Layout element replace the Outlet component with the elements contained in the "element" attribute of these child paths.
-
- // Root Route: All navigation will start from here.
-
} errorElement={
Not found! } >
+ createRoutesFromElements(
+ // Root Route: All navigation will start from Layout
+
} errorElement={
Not Found! }>
- {/* Nested Routes: Defines sub-routes within the BaseHome component. */}
-
} />
-
} /> {/* Dynamic route for single items */}
-
} />
-
- )
-);
\ No newline at end of file
+ {/* Nested Routes */}
+
} /> {/* Default homepage */}
+
} /> {/* Dynamic route */}
+
} />
+
} />
+
} />
+
} />
+
} />
+
} />
+
} />
+
} />
+
} />
+ } />
+
+ )
+);
diff --git a/src/front/store.js b/src/front/store.js
index 3062cd222d..10cb71c144 100644
--- a/src/front/store.js
+++ b/src/front/store.js
@@ -1,5 +1,9 @@
-export const initialStore=()=>{
- return{
+import React, { useReducer, createContext } from "react";
+
+
+export const initialStore = () => {
+ return {
+ token: localStorage.getItem("token") || null,
message: null,
todos: [
{
@@ -13,26 +17,46 @@ export const initialStore=()=>{
background: null,
}
]
- }
-}
+ };
+};
export default function storeReducer(store, action = {}) {
- switch(action.type){
+ switch (action.type) {
case 'set_hello':
return {
...store,
message: action.payload
};
-
- case 'add_task':
- const { id, color } = action.payload
+ case 'add_task':
+ const { id, color } = action.payload;
+ return {
+ ...store,
+ todos: store.todos.map(todo =>
+ todo.id === id ? { ...todo, background: color } : todo
+ )
+ };
+ case 'SET_TOKEN':
return {
...store,
- todos: store.todos.map((todo) => (todo.id === id ? { ...todo, background: color } : todo))
+ token: action.payload
};
+
default:
throw Error('Unknown action.');
- }
+ }
}
+
+
+export const Context = createContext();
+
+export const StoreProvider = ({ children }) => {
+ const [store, dispatch] = useReducer(storeReducer, initialStore());
+
+ return React.createElement(
+ Context.Provider,
+ { value: { store, dispatch } },
+ children
+ );
+};