diff --git a/.gitignore b/.gitignore index 80704f4378..1c614a0532 100755 --- a/.gitignore +++ b/.gitignore @@ -61,6 +61,8 @@ Thumbs.db # ignore node/grunt dependency directories node_modules/ +/venv/ +/dist/ # webpack output dist/* @@ -72,6 +74,8 @@ dist/* !.htaccess !.eslintrc !.env.example +.env +.env.* .now # backend stuff @@ -80,3 +84,17 @@ database.database database.db diagram.png __pycache__/ + +# Logs y caches +npm-debug.log* +yarn-debug.log* +yarn-error.log* +/coverage + +# Archivos de editor +.vscode/ +.idea/ + +# Archivos de sistema operativo +.DS_Store +Thumbs.db diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile deleted file mode 100644 index 542a325977..0000000000 --- a/.gitpod.Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -FROM gitpod/workspace-postgres:latest - -SHELL ["/bin/bash", "-c"] - -RUN sudo apt-get update \ - && sudo apt-get update \ - && sudo apt-get install -y redis-server \ - && sudo apt-get clean \ - && sudo rm -rf /var/cache/apt/* /var/lib/apt/lists/* /tmp/* - -# That Gitpod install pyenv for me? no, thanks -WORKDIR /home/gitpod/ -RUN rm .pyenv -Rf -RUN rm .gp_pyenv.d -Rf -RUN curl https://pyenv.run | bash - - -RUN pyenv update && pyenv install 3.10.7 && pyenv global 3.10.7 -RUN pip install pipenv yapf -RUN npm i heroku -g - -# remove PIP_USER environment -USER gitpod -RUN if ! grep -q "export PIP_USER=no" "$HOME/.bashrc"; then printf '%s\n' "export PIP_USER=no" >> "$HOME/.bashrc"; fi -RUN echo "" >> $HOME/.bashrc -RUN echo "unset DATABASE_URL" >> $HOME/.bashrc -RUN echo "export DATABASE_URL" >> $HOME/.bashrc diff --git a/.gitpod.yml b/.gitpod.yml deleted file mode 100644 index 8d80dd7bf2..0000000000 --- a/.gitpod.yml +++ /dev/null @@ -1,28 +0,0 @@ -image: - file: .gitpod.Dockerfile -ports: - - port: 3000 - onOpen: open-browser - visibility: public - - port: 3001 - onOpen: open-preview - visibility: public - - port: 5432 - onOpen: ignore -tasks: - - init: > - (cp .env.example .env || true) && - pipenv install && - psql -U gitpod -c 'CREATE DATABASE example;' && - psql -U gitpod -c 'CREATE EXTENSION unaccent;' -d example && - psql -c "ALTER USER gitpod PASSWORD 'postgres';" && - bash database.sh && - python docs/assets/greeting.py back - - command: > - npm install && - python docs/assets/greeting.py front - openMode: split-right - -vscode: - extensions: - - esbenp.prettier-vscode diff --git a/4geeks.ico b/4geeks.ico deleted file mode 100755 index 3583e2a98d..0000000000 Binary files a/4geeks.ico and /dev/null differ diff --git a/Dockerfile.backend b/Dockerfile.backend new file mode 100644 index 0000000000..2198ce9980 --- /dev/null +++ b/Dockerfile.backend @@ -0,0 +1,17 @@ +FROM python:3.9-slim-buster + +WORKDIR /app + +RUN apt-get update && apt-get install -y --no-install-recommends +build-essential +libpq-dev + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY src/ /app/src +COPY wsgi.py . + +EXPOSE 8000 + +CMD ["gunicorn", "--bind", "0.0.0.0:8000", "src.app:app"] \ No newline at end of file diff --git a/Dockerfile.frontend b/Dockerfile.frontend new file mode 100644 index 0000000000..3333403726 --- /dev/null +++ b/Dockerfile.frontend @@ -0,0 +1,20 @@ +FROM node:20-alpine AS builder + +WORKDIR /app + +COPY package.json package-lock.json vite.config.js ./ +RUN npm install + +COPY src/front/ ./src/front/ +COPY public/ ./public/ + +RUN npm run build + +FROM nginx:stable-alpine + +COPY --from=builder /app/dist /usr/share/nginx/html + +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/Dockerfile.render b/Dockerfile.render deleted file mode 100644 index ed38677ebe..0000000000 --- a/Dockerfile.render +++ /dev/null @@ -1,13 +0,0 @@ -FROM node:16 - -RUN apt update \ - && apt install software-properties-common \ - && add-apt-repository ppa:deadsnakes/ppa \ - && apt update \ - && apt install python3.10 - -WORKDIR /opt/app -COPY --from=build /opt/app/venv /venv - -ENV PATH="/opt/app/venv/bin:$PATH" -ENV NODE_ENV=container \ No newline at end of file diff --git a/Pipfile b/Pipfile deleted file mode 100644 index 44e04f14ff..0000000000 --- a/Pipfile +++ /dev/null @@ -1,36 +0,0 @@ -[[source]] -name = "pypi" -url = "https://pypi.org/simple" -verify_ssl = true - -[dev-packages] - -[packages] -flask = "*" -flask-sqlalchemy = "*" -flask-migrate = "*" -flask-swagger = "*" -psycopg2-binary = "*" -python-dotenv = "*" -flask-cors = "*" -gunicorn = "*" -cloudinary = "*" -flask-admin = "*" -typing-extensions = "*" -flask-jwt-extended = "==4.6.0" -wtforms = "==3.1.2" -sqlalchemy = "*" - -[requires] -python_version = "3.13" - -[scripts] -start="flask run -p 3001 -h 0.0.0.0" -init="flask db init" -migrate="flask db migrate" -local="heroku local" -upgrade="flask db upgrade" -downgrade="flask db downgrade" -insert-test-data="flask insert-test-data" -reset_db="bash ./docs/assets/reset_migrations.bash" -deploy="echo 'Please follow this 3 steps to deploy: https://github.com/4GeeksAcademy/flask-rest-hello/blob/master/README.md#deploy-your-website-to-heroku' " diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index b201c3decc..0000000000 --- a/Pipfile.lock +++ /dev/null @@ -1,555 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "d2e672e650278aeeee2fe49bd76d76497d8b65a50f8b5dbb121d265cbc6ef4e5" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.13" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "alembic": { - "hashes": [ - "sha256:1acdd7a3a478e208b0503cd73614d5e4c6efafa4e73518bb60e4f2846a37b1c5", - "sha256:496e888245a53adf1498fcab31713a469c65836f8de76e01399aa1c3e90dd213" - ], - "markers": "python_version >= '3.8'", - "version": "==1.14.1" - }, - "blinker": { - "hashes": [ - "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", - "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc" - ], - "markers": "python_version >= '3.9'", - "version": "==1.9.0" - }, - "certifi": { - "hashes": [ - "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", - "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe" - ], - "markers": "python_version >= '3.6'", - "version": "==2025.1.31" - }, - "click": { - "hashes": [ - "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", - "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a" - ], - "markers": "python_version >= '3.7'", - "version": "==8.1.8" - }, - "cloudinary": { - "hashes": [ - "sha256:ba223705409b2aaddd5196c2184d65f50a83dffcba3b94f3727658ff6a0172a3", - "sha256:e4191b470c5bae55542b64e0a78659af42971880294456dca480bc974fa9280a" - ], - "index": "pypi", - "version": "==1.42.2" - }, - "flask": { - "hashes": [ - "sha256:5f873c5184c897c8d9d1b05df1e3d01b14910ce69607a117bd3277098a5836ac", - "sha256:d667207822eb83f1c4b50949b1623c8fc8d51f2341d65f72e1a1815397551136" - ], - "index": "pypi", - "version": "==3.1.0" - }, - "flask-admin": { - "hashes": [ - "sha256:24cae2af832b6a611a01d7dc35f42d266c1d6c75a426b869d8cb241b78233369", - "sha256:fd8190f1ec3355913a22739c46ed3623f1d82b8112cde324c60a6fc9b21c9406" - ], - "index": "pypi", - "version": "==1.6.1" - }, - "flask-cors": { - "hashes": [ - "sha256:6ccb38d16d6b72bbc156c1c3f192bc435bfcc3c2bc864b2df1eb9b2d97b2403c", - "sha256:fa5cb364ead54bbf401a26dbf03030c6b18fb2fcaf70408096a572b409586b0c" - ], - "index": "pypi", - "version": "==5.0.1" - }, - "flask-jwt-extended": { - "hashes": [ - "sha256:63a28fc9731bcc6c4b8815b6f954b5904caa534fc2ae9b93b1d3ef12930dca95", - "sha256:9215d05a9413d3855764bcd67035e75819d23af2fafb6b55197eb5a3313fdfb2" - ], - "index": "pypi", - "version": "==4.6.0" - }, - "flask-migrate": { - "hashes": [ - "sha256:1a336b06eb2c3ace005f5f2ded8641d534c18798d64061f6ff11f79e1434126d", - "sha256:24d8051af161782e0743af1b04a152d007bad9772b2bca67b7ec1e8ceeb3910d" - ], - "index": "pypi", - "version": "==4.1.0" - }, - "flask-sqlalchemy": { - "hashes": [ - "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0", - "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312" - ], - "index": "pypi", - "version": "==3.1.1" - }, - "flask-swagger": { - "hashes": [ - "sha256:3caddb1311388eafc86f82f8e64ba386a5df6b84e5f16dfae19ca08173eba216", - "sha256:b4085f5bc36df4c20b6548cd1413adc9cf35719b0f0695367cd542065145294d" - ], - "index": "pypi", - "version": "==0.2.14" - }, - "greenlet": { - "hashes": [ - "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e", - "sha256:03a088b9de532cbfe2ba2034b2b85e82df37874681e8c470d6fb2f8c04d7e4b7", - "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", - "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1", - "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159", - "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563", - "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83", - "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9", - "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395", - "sha256:1d3755bcb2e02de341c55b4fca7a745a24a9e7212ac953f6b3a48d117d7257aa", - "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942", - "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1", - "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441", - "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22", - "sha256:346bed03fe47414091be4ad44786d1bd8bef0c3fcad6ed3dee074a032ab408a9", - "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0", - "sha256:37b9de5a96111fc15418819ab4c4432e4f3c2ede61e660b1e33971eba26ef9ba", - "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3", - "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1", - "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", - "sha256:47da355d8687fd65240c364c90a31569a133b7b60de111c255ef5b606f2ae291", - "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39", - "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d", - "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467", - "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475", - "sha256:54558ea205654b50c438029505def3834e80f0869a70fb15b871c29b4575ddef", - "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c", - "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511", - "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c", - "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", - "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a", - "sha256:6ef9ea3f137e5711f0dbe5f9263e8c009b7069d8a1acea822bd5e9dae0ae49c8", - "sha256:7017b2be767b9d43cc31416aba48aab0d2309ee31b4dbf10a1d38fb7972bdf9d", - "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01", - "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145", - "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80", - "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13", - "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e", - "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b", - "sha256:85f3ff71e2e60bd4b4932a043fbbe0f499e263c628390b285cb599154a3b03b1", - "sha256:8b8b36671f10ba80e159378df9c4f15c14098c4fd73a36b9ad715f057272fbef", - "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc", - "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff", - "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120", - "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437", - "sha256:95ffcf719966dd7c453f908e208e14cde192e09fde6c7186c8f1896ef778d8cd", - "sha256:98884ecf2ffb7d7fe6bd517e8eb99d31ff7855a840fa6d0d63cd07c037f6a981", - "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36", - "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a", - "sha256:a0dfc6c143b519113354e780a50381508139b07d2177cb6ad6a08278ec655798", - "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7", - "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761", - "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0", - "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e", - "sha256:b8da394b34370874b4572676f36acabac172602abf054cbc4ac910219f3340af", - "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa", - "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c", - "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42", - "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e", - "sha256:d21e10da6ec19b457b82636209cbe2331ff4306b54d06fa04b7c138ba18c8a81", - "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e", - "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617", - "sha256:db32b5348615a04b82240cc67983cb315309e88d444a288934ee6ceaebcad6cc", - "sha256:dcc62f31eae24de7f8dce72134c8651c58000d3b1868e01392baea7c32c247de", - "sha256:dfc59d69fc48664bc693842bd57acfdd490acafda1ab52c7836e3fc75c90a111", - "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383", - "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70", - "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6", - "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4", - "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011", - "sha256:f1d4aeb8891338e60d1ab6127af1fe45def5259def8094b9c7e34690c8858803", - "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79", - "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f" - ], - "markers": "python_version < '3.14' and (platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32'))))))", - "version": "==3.1.1" - }, - "gunicorn": { - "hashes": [ - "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", - "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec" - ], - "index": "pypi", - "version": "==23.0.0" - }, - "itsdangerous": { - "hashes": [ - "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", - "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173" - ], - "markers": "python_version >= '3.8'", - "version": "==2.2.0" - }, - "jinja2": { - "hashes": [ - "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", - "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb" - ], - "markers": "python_version >= '3.7'", - "version": "==3.1.5" - }, - "mako": { - "hashes": [ - "sha256:95920acccb578427a9aa38e37a186b1e43156c87260d7ba18ca63aa4c7cbd3a1", - "sha256:b5d65ff3462870feec922dbccf38f6efb44e5714d7b593a656be86663d8600ac" - ], - "markers": "python_version >= '3.8'", - "version": "==1.3.9" - }, - "markupsafe": { - "hashes": [ - "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", - "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", - "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", - "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", - "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", - "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", - "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", - "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", - "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", - "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", - "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0", - "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", - "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", - "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", - "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", - "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", - "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", - "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", - "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", - "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", - "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", - "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", - "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", - "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", - "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", - "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", - "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", - "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", - "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", - "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", - "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f", - "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", - "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", - "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", - "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", - "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", - "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", - "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", - "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", - "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", - "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", - "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", - "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", - "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", - "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", - "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", - "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", - "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", - "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29", - "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", - "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", - "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", - "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", - "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", - "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", - "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", - "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", - "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", - "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", - "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", - "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50" - ], - "markers": "python_version >= '3.9'", - "version": "==3.0.2" - }, - "packaging": { - "hashes": [ - "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", - "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f" - ], - "markers": "python_version >= '3.8'", - "version": "==24.2" - }, - "psycopg2-binary": { - "hashes": [ - "sha256:04392983d0bb89a8717772a193cfaac58871321e3ec69514e1c4e0d4957b5aff", - "sha256:056470c3dc57904bbf63d6f534988bafc4e970ffd50f6271fc4ee7daad9498a5", - "sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f", - "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5", - "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0", - "sha256:19721ac03892001ee8fdd11507e6a2e01f4e37014def96379411ca99d78aeb2c", - "sha256:1a6784f0ce3fec4edc64e985865c17778514325074adf5ad8f80636cd029ef7c", - "sha256:2286791ececda3a723d1910441c793be44625d86d1a4e79942751197f4d30341", - "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f", - "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7", - "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d", - "sha256:270934a475a0e4b6925b5f804e3809dd5f90f8613621d062848dd82f9cd62007", - "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", - "sha256:2ad26b467a405c798aaa1458ba09d7e2b6e5f96b1ce0ac15d82fd9f95dc38a92", - "sha256:2b3d2491d4d78b6b14f76881905c7a8a8abcf974aad4a8a0b065273a0ed7a2cb", - "sha256:2ce3e21dc3437b1d960521eca599d57408a695a0d3c26797ea0f72e834c7ffe5", - "sha256:30e34c4e97964805f715206c7b789d54a78b70f3ff19fbe590104b71c45600e5", - "sha256:3216ccf953b3f267691c90c6fe742e45d890d8272326b4a8b20850a03d05b7b8", - "sha256:32581b3020c72d7a421009ee1c6bf4a131ef5f0a968fab2e2de0c9d2bb4577f1", - "sha256:35958ec9e46432d9076286dda67942ed6d968b9c3a6a2fd62b48939d1d78bf68", - "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73", - "sha256:3c18f74eb4386bf35e92ab2354a12c17e5eb4d9798e4c0ad3a00783eae7cd9f1", - "sha256:3c4745a90b78e51d9ba06e2088a2fe0c693ae19cc8cb051ccda44e8df8a6eb53", - "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d", - "sha256:3e9c76f0ac6f92ecfc79516a8034a544926430f7b080ec5a0537bca389ee0906", - "sha256:48b338f08d93e7be4ab2b5f1dbe69dc5e9ef07170fe1f86514422076d9c010d0", - "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", - "sha256:512d29bb12608891e349af6a0cccedce51677725a921c07dba6342beaf576f9a", - "sha256:5a507320c58903967ef7384355a4da7ff3f28132d679aeb23572753cbf2ec10b", - "sha256:5c370b1e4975df846b0277b4deba86419ca77dbc25047f535b0bb03d1a544d44", - "sha256:6b269105e59ac96aba877c1707c600ae55711d9dcd3fc4b5012e4af68e30c648", - "sha256:6d4fa1079cab9018f4d0bd2db307beaa612b0d13ba73b5c6304b9fe2fb441ff7", - "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f", - "sha256:73aa0e31fa4bb82578f3a6c74a73c273367727de397a7a0f07bd83cbea696baa", - "sha256:7559bce4b505762d737172556a4e6ea8a9998ecac1e39b5233465093e8cee697", - "sha256:79625966e176dc97ddabc142351e0409e28acf4660b88d1cf6adb876d20c490d", - "sha256:7a813c8bdbaaaab1f078014b9b0b13f5de757e2b5d9be6403639b298a04d218b", - "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526", - "sha256:7f4152f8f76d2023aac16285576a9ecd2b11a9895373a1f10fd9db54b3ff06b4", - "sha256:7f5d859928e635fa3ce3477704acee0f667b3a3d3e4bb109f2b18d4005f38287", - "sha256:851485a42dbb0bdc1edcdabdb8557c09c9655dfa2ca0460ff210522e073e319e", - "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673", - "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0", - "sha256:8aabf1c1a04584c168984ac678a668094d831f152859d06e055288fa515e4d30", - "sha256:8aecc5e80c63f7459a1a2ab2c64df952051df196294d9f739933a9f6687e86b3", - "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e", - "sha256:8de718c0e1c4b982a54b41779667242bc630b2197948405b7bd8ce16bcecac92", - "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a", - "sha256:b5f86c56eeb91dc3135b3fd8a95dc7ae14c538a2f3ad77a19645cf55bab1799c", - "sha256:b73d6d7f0ccdad7bc43e6d34273f70d587ef62f824d7261c4ae9b8b1b6af90e8", - "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", - "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47", - "sha256:ce5ab4bf46a211a8e924d307c1b1fcda82368586a19d0a24f8ae166f5c784864", - "sha256:d00924255d7fc916ef66e4bf22f354a940c67179ad3fd7067d7a0a9c84d2fbfc", - "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00", - "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb", - "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539", - "sha256:e5720a5d25e3b99cd0dc5c8a440570469ff82659bb09431c1439b92caf184d3b", - "sha256:e8b58f0a96e7a1e341fc894f62c1177a7c83febebb5ff9123b579418fdc8a481", - "sha256:e984839e75e0b60cfe75e351db53d6db750b00de45644c5d1f7ee5d1f34a1ce5", - "sha256:eb09aa7f9cecb45027683bb55aebaaf45a0df8bf6de68801a6afdc7947bb09d4", - "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64", - "sha256:ecced182e935529727401b24d76634a357c71c9275b356efafd8a2a91ec07392", - "sha256:ee0e8c683a7ff25d23b55b11161c2663d4b099770f6085ff0a20d4505778d6b4", - "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", - "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1", - "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", - "sha256:ffe8ed017e4ed70f68b7b371d84b7d4a790368db9203dfc2d222febd3a9c8863" - ], - "index": "pypi", - "version": "==2.9.10" - }, - "pyjwt": { - "hashes": [ - "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", - "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb" - ], - "markers": "python_version >= '3.9'", - "version": "==2.10.1" - }, - "python-dotenv": { - "hashes": [ - "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", - "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a" - ], - "index": "pypi", - "version": "==1.0.1" - }, - "pyyaml": { - "hashes": [ - "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff", - "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", - "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", - "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", - "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", - "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", - "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", - "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", - "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", - "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", - "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a", - "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", - "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", - "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", - "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", - "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", - "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", - "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a", - "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", - "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", - "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", - "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", - "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", - "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", - "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", - "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", - "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", - "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", - "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", - "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706", - "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", - "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", - "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", - "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083", - "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", - "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", - "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", - "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", - "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", - "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", - "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", - "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", - "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", - "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", - "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5", - "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d", - "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", - "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", - "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", - "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", - "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", - "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", - "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4" - ], - "markers": "python_version >= '3.8'", - "version": "==6.0.2" - }, - "six": { - "hashes": [ - "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", - "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", - "version": "==1.17.0" - }, - "sqlalchemy": { - "hashes": [ - "sha256:0398361acebb42975deb747a824b5188817d32b5c8f8aba767d51ad0cc7bb08d", - "sha256:0561832b04c6071bac3aad45b0d3bb6d2c4f46a8409f0a7a9c9fa6673b41bc03", - "sha256:07258341402a718f166618470cde0c34e4cec85a39767dce4e24f61ba5e667ea", - "sha256:0a826f21848632add58bef4f755a33d45105d25656a0c849f2dc2df1c71f6f50", - "sha256:1052723e6cd95312f6a6eff9a279fd41bbae67633415373fdac3c430eca3425d", - "sha256:12d5b06a1f3aeccf295a5843c86835033797fea292c60e72b07bcb5d820e6dd3", - "sha256:12f5c9ed53334c3ce719155424dc5407aaa4f6cadeb09c5b627e06abb93933a1", - "sha256:2a0ef3f98175d77180ffdc623d38e9f1736e8d86b6ba70bff182a7e68bed7727", - "sha256:2f2951dc4b4f990a4b394d6b382accb33141d4d3bd3ef4e2b27287135d6bdd68", - "sha256:3868acb639c136d98107c9096303d2d8e5da2880f7706f9f8c06a7f961961149", - "sha256:386b7d136919bb66ced64d2228b92d66140de5fefb3c7df6bd79069a269a7b06", - "sha256:3d3043375dd5bbcb2282894cbb12e6c559654c67b5fffb462fda815a55bf93f7", - "sha256:3e35d5565b35b66905b79ca4ae85840a8d40d31e0b3e2990f2e7692071b179ca", - "sha256:402c2316d95ed90d3d3c25ad0390afa52f4d2c56b348f212aa9c8d072a40eee5", - "sha256:40310db77a55512a18827488e592965d3dec6a3f1e3d8af3f8243134029daca3", - "sha256:40e9cdbd18c1f84631312b64993f7d755d85a3930252f6276a77432a2b25a2f3", - "sha256:49aa2cdd1e88adb1617c672a09bf4ebf2f05c9448c6dbeba096a3aeeb9d4d443", - "sha256:57dd41ba32430cbcc812041d4de8d2ca4651aeefad2626921ae2a23deb8cd6ff", - "sha256:5dba1cdb8f319084f5b00d41207b2079822aa8d6a4667c0f369fce85e34b0c86", - "sha256:5e1d9e429028ce04f187a9f522818386c8b076723cdbe9345708384f49ebcec6", - "sha256:63178c675d4c80def39f1febd625a6333f44c0ba269edd8a468b156394b27753", - "sha256:6493bc0eacdbb2c0f0d260d8988e943fee06089cd239bd7f3d0c45d1657a70e2", - "sha256:64aa8934200e222f72fcfd82ee71c0130a9c07d5725af6fe6e919017d095b297", - "sha256:665255e7aae5f38237b3a6eae49d2358d83a59f39ac21036413fab5d1e810578", - "sha256:6db316d6e340f862ec059dc12e395d71f39746a20503b124edc255973977b728", - "sha256:70065dfabf023b155a9c2a18f573e47e6ca709b9e8619b2e04c54d5bcf193178", - "sha256:8455aa60da49cb112df62b4721bd8ad3654a3a02b9452c783e651637a1f21fa2", - "sha256:8b0ac78898c50e2574e9f938d2e5caa8fe187d7a5b69b65faa1ea4648925b096", - "sha256:8bf312ed8ac096d674c6aa9131b249093c1b37c35db6a967daa4c84746bc1bc9", - "sha256:92f99f2623ff16bd4aaf786ccde759c1f676d39c7bf2855eb0b540e1ac4530c8", - "sha256:9c8bcad7fc12f0cc5896d8e10fdf703c45bd487294a986903fe032c72201596b", - "sha256:9cd136184dd5f58892f24001cdce986f5d7e96059d004118d5410671579834a4", - "sha256:9eb4fa13c8c7a2404b6a8e3772c17a55b1ba18bc711e25e4d6c0c9f5f541b02a", - "sha256:a2bc4e49e8329f3283d99840c136ff2cd1a29e49b5624a46a290f04dff48e079", - "sha256:a5645cd45f56895cfe3ca3459aed9ff2d3f9aaa29ff7edf557fa7a23515a3725", - "sha256:a9afbc3909d0274d6ac8ec891e30210563b2c8bdd52ebbda14146354e7a69373", - "sha256:aa498d1392216fae47eaf10c593e06c34476ced9549657fca713d0d1ba5f7248", - "sha256:afd776cf1ebfc7f9aa42a09cf19feadb40a26366802d86c1fba080d8e5e74bdd", - "sha256:b335a7c958bc945e10c522c069cd6e5804f4ff20f9a744dd38e748eb602cbbda", - "sha256:b3c4817dff8cef5697f5afe5fec6bc1783994d55a68391be24cb7d80d2dbc3a6", - "sha256:b79ee64d01d05a5476d5cceb3c27b5535e6bb84ee0f872ba60d9a8cd4d0e6579", - "sha256:b87a90f14c68c925817423b0424381f0e16d80fc9a1a1046ef202ab25b19a444", - "sha256:bf89e0e4a30714b357f5d46b6f20e0099d38b30d45fa68ea48589faf5f12f62d", - "sha256:c058b84c3b24812c859300f3b5abf300daa34df20d4d4f42e9652a4d1c48c8a4", - "sha256:c09a6ea87658695e527104cf857c70f79f14e9484605e205217aae0ec27b45fc", - "sha256:c57b8e0841f3fce7b703530ed70c7c36269c6d180ea2e02e36b34cb7288c50c7", - "sha256:c9cea5b756173bb86e2235f2f871b406a9b9d722417ae31e5391ccaef5348f2c", - "sha256:cb39ed598aaf102251483f3e4675c5dd6b289c8142210ef76ba24aae0a8f8aba", - "sha256:e036549ad14f2b414c725349cce0772ea34a7ab008e9cd67f9084e4f371d1f32", - "sha256:e185ea07a99ce8b8edfc788c586c538c4b1351007e614ceb708fd01b095ef33e", - "sha256:e5a4d82bdb4bf1ac1285a68eab02d253ab73355d9f0fe725a97e1e0fa689decb", - "sha256:eae27ad7580529a427cfdd52c87abb2dfb15ce2b7a3e0fc29fbb63e2ed6f8120", - "sha256:ecef029b69843b82048c5b347d8e6049356aa24ed644006c9a9d7098c3bd3bfd", - "sha256:ee3bee874cb1fadee2ff2b79fc9fc808aa638670f28b2145074538d4a6a5028e", - "sha256:f0d3de936b192980209d7b5149e3c98977c3810d401482d05fb6d668d53c1c63", - "sha256:f53c0d6a859b2db58332e0e6a921582a02c1677cc93d4cbb36fdf49709b327b2", - "sha256:f9d57f1b3061b3e21476b0ad5f0397b112b94ace21d1f439f2db472e568178ae" - ], - "index": "pypi", - "version": "==2.0.38" - }, - "typing-extensions": { - "hashes": [ - "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", - "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" - ], - "index": "pypi", - "version": "==4.12.2" - }, - "urllib3": { - "hashes": [ - "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", - "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d" - ], - "markers": "python_version >= '3.9'", - "version": "==2.3.0" - }, - "werkzeug": { - "hashes": [ - "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", - "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746" - ], - "markers": "python_version >= '3.9'", - "version": "==3.1.3" - }, - "wtforms": { - "hashes": [ - "sha256:bf831c042829c8cdbad74c27575098d541d039b1faa74c771545ecac916f2c07", - "sha256:f8d76180d7239c94c6322f7990ae1216dae3659b7aa1cee94b6318bdffb474b9" - ], - "index": "pypi", - "version": "==3.1.2" - } - }, - "develop": {} -} diff --git a/Procfile b/Procfile deleted file mode 100644 index 6675c695d7..0000000000 --- a/Procfile +++ /dev/null @@ -1,2 +0,0 @@ -release: pipenv run upgrade -web: gunicorn wsgi --chdir ./src/ diff --git a/database.sh b/database.sh deleted file mode 100644 index 809823839e..0000000000 --- a/database.sh +++ /dev/null @@ -1,24 +0,0 @@ -creating_migration () -{ - pipenv run init - pipenv run migrate - pipenv run upgrade -} - -migrate_upgrade () -{ - pipenv run migrate - pipenv run upgrade -} - -dir=$(pwd) - -if [ ! -d $dir/migrations ] -then -echo 'creating migration' -creating_migration -else -echo 'migrations already created' -echo 'updating migrations' -migrate_upgrade -fi diff --git a/dist/index.html b/dist/index.html index 1e8cb81dfe..287e1dfd0b 100644 --- a/dist/index.html +++ b/dist/index.html @@ -1,14 +1,23 @@ - - - - - - Hello Rigo with React + Flux + Context.js - - - - -
- - - + + + + + + + + + + + + + + + CloudTech + + + + +
+ + + \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000..e795eb49aa --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,57 @@ +version: '3.8' + +services: + # Servicio del Backend (Python/Flask API) + server: + build: + context: . # El contexto es la raíz, donde están todos los archivos + dockerfile: Dockerfile.backend # Especifica qué Dockerfile usar + restart: always + env_file: ./.env + networks: + - app-network + depends_on: + - db + + # Servicio del Frontend (React/Vite App) + client: + build: + context: . + dockerfile: Dockerfile.frontend # Especifica el otro Dockerfile + restart: always + networks: + - app-network + + # Servicio de la Base de Datos (PostgreSQL) + db: + image: postgres:14-alpine + restart: always + env_file: ./.env + volumes: + - postgres-data:/var/lib/postgresql/data + networks: + - app-network + + # Servicio del Reverse Proxy (Nginx) + nginx-proxy: + image: nginx:stable-alpine + restart: always + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx-proxy/default.conf:/etc/nginx/conf.d/default.conf + - ./nginx-proxy/certs:/etc/nginx/certs + depends_on: + - server + - client + networks: + - app-network + +networks: + app-network: + driver: bridge + +volumes: + postgres-data: + driver: local \ No newline at end of file diff --git a/index.html b/index.html index 6692145570..5698ff9f61 100644 --- a/index.html +++ b/index.html @@ -9,6 +9,9 @@ + + + CloudTech diff --git a/migrations/versions/4517993f5dfe_.py b/migrations/versions/4517993f5dfe_.py new file mode 100644 index 0000000000..f4404db8b7 --- /dev/null +++ b/migrations/versions/4517993f5dfe_.py @@ -0,0 +1,41 @@ +"""empty message + +Revision ID: 4517993f5dfe +Revises: 727b004dfad3 +Create Date: 2025-07-25 15:48:44.313778 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '4517993f5dfe' +down_revision = '727b004dfad3' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('admin', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('email', sa.String(length=120), nullable=False), + sa.Column('password', sa.String(length=128), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('email') + ) + op.create_table('token_blocked_list', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('jti', sa.String(length=50), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('jti') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('token_blocked_list') + op.drop_table('admin') + # ### end Alembic commands ### diff --git a/migrations/versions/876cb350dd08_.py b/migrations/versions/876cb350dd08_.py new file mode 100644 index 0000000000..a37ba2ae01 --- /dev/null +++ b/migrations/versions/876cb350dd08_.py @@ -0,0 +1,42 @@ +"""empty message + +Revision ID: 876cb350dd08 +Revises: 4517993f5dfe +Create Date: 2025-07-25 16:00:47.587108 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '876cb350dd08' +down_revision = '4517993f5dfe' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('ct_admin', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('email', sa.String(length=120), nullable=False), + sa.Column('password', sa.String(length=128), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('email') + ) + op.drop_table('admin') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('admin', + sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column('email', sa.VARCHAR(length=120), autoincrement=False, nullable=False), + sa.Column('password', sa.VARCHAR(length=128), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint('id', name='admin_pkey'), + sa.UniqueConstraint('email', name='admin_email_key') + ) + op.drop_table('ct_admin') + # ### end Alembic commands ### diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000000..5c58f5da23 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,8 @@ +server { + listen 80; + root /usr/share/nginx/html; + index index.html; + location / { + try_files $uri /index.html; + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index e65cd6a2d1..1ea87af112 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,9 +14,14 @@ "@fortawesome/free-solid-svg-icons": "^6.7.2", "@fortawesome/react-fontawesome": "^0.2.2", "bootstrap": "^5.3.7", + "i18next": "^25.3.2", + "i18next-browser-languagedetector": "^8.2.0", + "i18next-http-backend": "^3.0.2", "prop-types": "^15.8.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-floating-whatsapp": "^5.0.8", + "react-i18next": "^15.6.0", "react-router-dom": "^6.18.0" }, "devDependencies": { @@ -27,7 +32,7 @@ "eslint-plugin-react": "^7.33.1", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.3", - "vite": "^4.5.14" + "vite": "^7.0.4" }, "engines": { "node": ">=20.0.0" @@ -281,6 +286,15 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", @@ -329,10 +343,27 @@ "node": ">=6.9.0" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz", + "integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==", + "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.6", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.6.tgz", + "integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==", "cpu": [ "arm" ], @@ -343,13 +374,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.6", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz", + "integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==", "cpu": [ "arm64" ], @@ -360,13 +391,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.6", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.6.tgz", + "integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==", "cpu": [ "x64" ], @@ -377,13 +408,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.6", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz", + "integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==", "cpu": [ "arm64" ], @@ -394,13 +425,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.6", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz", + "integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==", "cpu": [ "x64" ], @@ -411,13 +442,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.6", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz", + "integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==", "cpu": [ "arm64" ], @@ -428,13 +459,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.6", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz", + "integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==", "cpu": [ "x64" ], @@ -445,13 +476,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.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz", + "integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==", "cpu": [ "arm" ], @@ -462,13 +493,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.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz", + "integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==", "cpu": [ "arm64" ], @@ -479,13 +510,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.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz", + "integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==", "cpu": [ "ia32" ], @@ -496,13 +527,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.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz", + "integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==", "cpu": [ "loong64" ], @@ -513,13 +544,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.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz", + "integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==", "cpu": [ "mips64el" ], @@ -530,13 +561,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.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz", + "integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==", "cpu": [ "ppc64" ], @@ -547,13 +578,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.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz", + "integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==", "cpu": [ "riscv64" ], @@ -564,13 +595,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.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz", + "integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==", "cpu": [ "s390x" ], @@ -581,13 +612,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.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz", + "integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==", "cpu": [ "x64" ], @@ -598,13 +629,30 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz", + "integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==", + "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.6", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz", + "integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==", "cpu": [ "x64" ], @@ -615,13 +663,30 @@ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz", + "integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==", + "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.6", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz", + "integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==", "cpu": [ "x64" ], @@ -632,13 +697,30 @@ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz", + "integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "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.6", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz", + "integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==", "cpu": [ "x64" ], @@ -649,13 +731,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.6", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz", + "integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==", "cpu": [ "arm64" ], @@ -666,13 +748,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.6", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz", + "integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==", "cpu": [ "ia32" ], @@ -683,13 +765,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.6", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz", + "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==", "cpu": [ "x64" ], @@ -700,7 +782,7 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { @@ -966,6 +1048,286 @@ "dev": true, "license": "MIT" }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.2.tgz", + "integrity": "sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.2.tgz", + "integrity": "sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.2.tgz", + "integrity": "sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.2.tgz", + "integrity": "sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.2.tgz", + "integrity": "sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.2.tgz", + "integrity": "sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.2.tgz", + "integrity": "sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.2.tgz", + "integrity": "sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.2.tgz", + "integrity": "sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.2.tgz", + "integrity": "sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.2.tgz", + "integrity": "sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.2.tgz", + "integrity": "sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.2.tgz", + "integrity": "sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.2.tgz", + "integrity": "sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.2.tgz", + "integrity": "sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.2.tgz", + "integrity": "sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.2.tgz", + "integrity": "sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.2.tgz", + "integrity": "sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.2.tgz", + "integrity": "sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.2.tgz", + "integrity": "sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA==", + "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", @@ -1011,6 +1373,13 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/prop-types": { "version": "15.7.15", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", @@ -1506,6 +1875,15 @@ "dev": true, "license": "MIT" }, + "node_modules/cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.12" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1856,9 +2234,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.6", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", + "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1866,31 +2244,35 @@ "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.6", + "@esbuild/android-arm": "0.25.6", + "@esbuild/android-arm64": "0.25.6", + "@esbuild/android-x64": "0.25.6", + "@esbuild/darwin-arm64": "0.25.6", + "@esbuild/darwin-x64": "0.25.6", + "@esbuild/freebsd-arm64": "0.25.6", + "@esbuild/freebsd-x64": "0.25.6", + "@esbuild/linux-arm": "0.25.6", + "@esbuild/linux-arm64": "0.25.6", + "@esbuild/linux-ia32": "0.25.6", + "@esbuild/linux-loong64": "0.25.6", + "@esbuild/linux-mips64el": "0.25.6", + "@esbuild/linux-ppc64": "0.25.6", + "@esbuild/linux-riscv64": "0.25.6", + "@esbuild/linux-s390x": "0.25.6", + "@esbuild/linux-x64": "0.25.6", + "@esbuild/netbsd-arm64": "0.25.6", + "@esbuild/netbsd-x64": "0.25.6", + "@esbuild/openbsd-arm64": "0.25.6", + "@esbuild/openbsd-x64": "0.25.6", + "@esbuild/openharmony-arm64": "0.25.6", + "@esbuild/sunos-x64": "0.25.6", + "@esbuild/win32-arm64": "0.25.6", + "@esbuild/win32-ia32": "0.25.6", + "@esbuild/win32-x64": "0.25.6" } }, "node_modules/escalade": { @@ -2167,6 +2549,21 @@ "reusify": "^1.0.4" } }, + "node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "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", @@ -2547,6 +2944,64 @@ "node": ">= 0.4" } }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "license": "MIT", + "dependencies": { + "void-elements": "3.1.0" + } + }, + "node_modules/i18next": { + "version": "25.3.2", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.3.2.tgz", + "integrity": "sha512-JSnbZDxRVbphc5jiptxr3o2zocy5dEqpVm9qCGdJwRNO+9saUJS0/u4LnM/13C23fUEWxAylPqKU/NpMV/IjqA==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.6" + }, + "peerDependencies": { + "typescript": "^5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.2.0.tgz", + "integrity": "sha512-P+3zEKLnOF0qmiesW383vsLdtQVyKtCNA9cjSoKCppTKPQVfKd2W8hbVo5ZhNJKDqeM7BOcvNoKJOjpHh4Js9g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, + "node_modules/i18next-http-backend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-3.0.2.tgz", + "integrity": "sha512-PdlvPnvIp4E1sYi46Ik4tBYh/v/NbYfFFgTjkwFl0is8A18s7/bx9aXqsrOax9WUbeNS6mD2oix7Z0yGGf6m5g==", + "license": "MIT", + "dependencies": { + "cross-fetch": "4.0.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3248,6 +3703,26 @@ "dev": true, "license": "MIT" }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", @@ -3497,6 +3972,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.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -3613,6 +4101,45 @@ "react": "^18.3.1" } }, + "node_modules/react-floating-whatsapp": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/react-floating-whatsapp/-/react-floating-whatsapp-5.0.8.tgz", + "integrity": "sha512-4X485d04u7MoWpn61OpWPO/mL94P3gaFq1L932ztH7bo9eClAFcg5ufIxNKbJo9/1h0xpjWZKEW0KBB0apWzZw==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/react-i18next": { + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.6.0.tgz", + "integrity": "sha512-W135dB0rDfiFmbMipC17nOhGdttO5mzH8BivY+2ybsQBbXvxWIwl3cmeH3T9d+YPBSJu/ouyJKFJTtkK7rJofw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.6", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 23.2.3", + "react": ">= 16.8.0", + "typescript": "^5" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -3762,19 +4289,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.44.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.2.tgz", + "integrity": "sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg==", "dev": true, "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, "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.44.2", + "@rollup/rollup-android-arm64": "4.44.2", + "@rollup/rollup-darwin-arm64": "4.44.2", + "@rollup/rollup-darwin-x64": "4.44.2", + "@rollup/rollup-freebsd-arm64": "4.44.2", + "@rollup/rollup-freebsd-x64": "4.44.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.44.2", + "@rollup/rollup-linux-arm-musleabihf": "4.44.2", + "@rollup/rollup-linux-arm64-gnu": "4.44.2", + "@rollup/rollup-linux-arm64-musl": "4.44.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.44.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.44.2", + "@rollup/rollup-linux-riscv64-gnu": "4.44.2", + "@rollup/rollup-linux-riscv64-musl": "4.44.2", + "@rollup/rollup-linux-s390x-gnu": "4.44.2", + "@rollup/rollup-linux-x64-gnu": "4.44.2", + "@rollup/rollup-linux-x64-musl": "4.44.2", + "@rollup/rollup-win32-arm64-msvc": "4.44.2", + "@rollup/rollup-win32-ia32-msvc": "4.44.2", + "@rollup/rollup-win32-x64-msvc": "4.44.2", "fsevents": "~2.3.2" } }, @@ -4205,6 +4755,29 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "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/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4370,41 +4943,51 @@ } }, "node_modules/vite": { - "version": "4.5.14", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.14.tgz", - "integrity": "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz", + "integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.18.10", - "postcss": "^8.4.27", - "rollup": "^3.27.1" + "esbuild": "^0.25.0", + "fdir": "^6.4.6", + "picomatch": "^4.0.2", + "postcss": "^8.5.6", + "rollup": "^4.40.0", + "tinyglobby": "^0.2.14" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" }, "optionalDependencies": { - "fsevents": "~2.3.2" + "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": ">= 14", - "less": "*", + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", "lightningcss": "^1.21.0", - "sass": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, + "jiti": { + "optional": true + }, "less": { "optional": true }, @@ -4414,6 +4997,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, @@ -4422,9 +5008,40 @@ }, "terser": { "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true } } }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index b812c92a23..8a660ff580 100755 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "eslint-plugin-react": "^7.33.1", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.3", - "vite": "^4.5.14" + "vite": "^7.0.4" }, "babel": { "presets": [ @@ -59,9 +59,14 @@ "@fortawesome/free-solid-svg-icons": "^6.7.2", "@fortawesome/react-fontawesome": "^0.2.2", "bootstrap": "^5.3.7", + "i18next": "^25.3.2", + "i18next-browser-languagedetector": "^8.2.0", + "i18next-http-backend": "^3.0.2", "prop-types": "^15.8.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-floating-whatsapp": "^5.0.8", + "react-i18next": "^15.6.0", "react-router-dom": "^6.18.0" } } diff --git a/public/4geeks.ico b/public/4geeks.ico deleted file mode 100644 index 3583e2a98d..0000000000 Binary files a/public/4geeks.ico and /dev/null differ diff --git a/public/index.html b/public/index.html index 9462644fe9..97fd77c5c5 100644 --- a/public/index.html +++ b/public/index.html @@ -1 +1,35 @@ -Hello Rigo with Vanilla.js
\ No newline at end of file + + + + + + CloudTech + + + + + + +
+ + + + diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json new file mode 100644 index 0000000000..58b54b0fc1 --- /dev/null +++ b/public/locales/en/translation.json @@ -0,0 +1,311 @@ +{ + "headers": { + "headerHome": { + "headLine": "We shape ideas to create digital universes", + "headerDescription": "We build digital environments that strengthen your brand and unite your audiences. We create an online presence that ensures measurable results and scalable growth.", + "portfolioButton": "Projects", + "contactButton": "Contact Us" + }, + "headerAbout": { + "headLine": "Your Vision, The Engine of Our Creativity", + "headerDescription": "We mold your ideas into powerful web experiences that reflect your brand's essence and meet your strategic objectives.", + "portfolioButton": "Projects", + "contactButton": "Contact Us" + }, + "headerServices": { + "headLine": "Your custom-made universe", + "headerDescription": "Our services are the bridge between your ideas and powerful digital solutions that will boost your brand and generate sustained growth.", + "portfolioButton": "Projects", + "contactButton": "Contact Us" + }, + "headerProjects": { + "headLine": "We Design and Connect Your Digital Ecosystem", + "headerDescription": "We explore the intersection between strategy, design, and technology to build unique digital solutions that solve real challenges and channel efforts towards an unparalleled experience.", + "contactButton": "Contact Us" + } + }, + "footer": { + "footerSlogan": "We Mold Ideas to Create Digital Universes" + }, + "about": { + "sectionTitle": "Who We Are", + "sectionDescription": "We are a company that believes in the value of uniting creativity and technology. We combine strategy, cutting-edge design, and robust technology to create an intuitive and functional online presence." + }, + "services": { + "sectionTitle": "Meet Our Team", + "sectionDescription": "Our team is the heart of CloudTech, where creativity meets technology to bring your most ambitious projects to life. Meet the minds behind your digital universe.", + "webDevelopmentTitle": "Web Development", + "webDevelopmentSubtitle": "Building Your Digital Universe", + "communicationTitle": "Digital Communication", + "communicationSubtitle": "Uniting your audiences", + "softwareSolutionsTitle": "Software Solutions", + "softwareSolutionsSubtitle": "Tools to bring your ideas to life", + "card": { + "wordpress": { + "title": "Wordpress", + "description": "Boost your presence with the most versatile and popular CMS platform. We design and develop custom WordPress sites, easy to manage and optimized for your brand's growth.", + "link": "Your Website with WordPress" + }, + "webdev": { + "title": "Web Dev", + "description": "We build custom web development with the latest technologies, ensuring exceptional functionality, scalability, and performance for unique solutions and specific needs.", + "link": "Explore Custom Development" + }, + "ecommerce": { + "title": "E-commerce", + "description": "Launch and start selling with your online store thanks to intuitive design and advanced functionalities. We create shopping experiences that convert visitors into loyal customers and increase your sales.", + "link": "Boost your E-commerce" + }, + "webflow": { + "title": "WebFlow", + "description": "We design visually stunning and highly functional websites without complex code. Ideal for agile projects demanding cutting-edge design and management autonomy.", + "link": "Discover Webflow" + }, + "socialMedia": { + "title": "Social Media Management", + "description": "We manage your social platforms with strategy and creativity. We build communities, increase engagement, and strengthen your brand's presence and interaction with your audience.", + "link": "Boost your Brand" + }, + "copyWriting": { + "title": "Copywriting", + "description": "We create persuasive texts optimized for web, blogs, social media, and campaigns that capture attention, convey a clear message, and convert visitors and followers into customers.", + "link": "Write to Us" + }, + "contentProduction": { + "title": "Content Production", + "description": "We tell your story with videos, social media posts, blog articles, and more. We generate valuable content that positions your brand as a reference in your sector.", + "link": "Create with Us" + }, + "communicationStrategy": { + "title": "Communication Strategy", + "description": "We plan your digital communication roadmap. We research, define your voice, and create an integral plan that brings together your entire digital universe to achieve your objectives.", + "link": "Discover your Potential" + }, + "autocadLt": { + "title": "AutoCAD LT", + "description": "Transform your ideas into precise blueprints with the industry-leading 2D drawing software. Create, edit, and document your designs with efficiency, optimizing your workflow and delivering projects in record time.", + "link": "Power your 2D design" + }, + "autocadIst": { + "title": "AutoCAD IST", + "description": "Design without limits in 2D and 3D with the most complete tool for architects and engineers. Accelerate your workflows and create impactful designs with powerful modeling, visualization, and automation features.", + "link": "Acquire Full License" + } + } + }, + "process": { + "sectionToggleCapacities": "Capabilities", + "sectionToggleProcess": "Process", + "capacities": { + "webConcept": { + "label": "Web Concept Design", + "text": "A creative and strategic process that defines a website's visual and structural foundations. We identify your brand's core values and objectives, translating them into test designs aligned with the audience. We explore multiple drafts detailing layout, color palettes, typography, and user flows. This generates a clear, actionable framework for a functional, aesthetically impactful site aligned with your business goals." + }, + "webAudit": { + "label": "Web Audit", + "text": "We exhaustively evaluate your website to identify opportunities for improvement in usability, performance, accessibility, and design. We provide actionable insights, detecting barriers to user interaction and business growth. We analyze key metrics (load times, navigation). The audit identifies quick and long-term optimizations (images, structure), delivering a detailed report with prioritized recommendations for impactful web enhancements." + }, + "webIntDev": { + "label": "Full Website Development", + "text": "Comprehensive web creation service. We deliver a cohesive and personalized digital presence, aligned with your objectives and audience. We develop scalable information architecture for fluid navigation and effective communication. The design phase creates a complete visual identity with custom UI kits, responsive designs, and interactive elements. The result is an optimized, high-performance, visually appealing site that drives interaction and supports long-term business growth." + }, + "webDesignSystems": { + "label": "Web Design Systems", + "text": "We create reusable frameworks to simplify updates and ensure visual consistency in your digital ecosystem. Based on industry standards, we organize UI elements (buttons, typography, colors) into scalable modules for efficient page creation, reducing resources. We identify key components, assemble them into patterns and templates, and conduct exhaustive tests. With documentation and training, this system optimizes workflows, improves collaboration, and supports web scalability." + }, + "webUpkeep": { + "label": "Website Maintenance", + "text": "We ensure your website remains secure, functional, and aligned with your business objectives. Our service includes routine updates, performance monitoring, error correction, and proactive quality tests to anticipate issues. We optimize performance, load speed, security, and perform minor design/content updates. This guarantees consistent operation, user satisfaction, and a reliable digital presence for your brand." + }, + "serviceIntegration": { + "label": "Service Integration", + "text": "We enhance your website's functionality by connecting it with key external tools and services. This includes analytics platforms, customer support systems, payment gateways, and automated scheduling. We evaluate your needs and select the right tools. Our team manages technical integration to ensure compatibility and smooth operation, aligning everything with your brand. Post-integration support ensures efficient systems, maximizing value." + }, + "strategicCommunication": { + "label": "Communication Strategy", + "text": "We develop complete roadmaps for your digital communication. We include exhaustive audience and competitor research, define your brand's voice, create content pillars, and plan multi-channel. Our goal is for every message to contribute to your business objectives, ensuring long-term coherence and effectiveness." + }, + "seoAndDigitalContent": { + "label": "SEO & Digital Content", + "text": "We don't just write; we optimize. We create texts that capture human attention and that of search engines. This includes persuasive copywriting, keyword research, content structuring for readability (UX Writing), and message adaptation for each platform, ensuring your content is discovered and drives desired action." + }, + "communityManagement": { + "label": "Community Management", + "text": "We go beyond social media posting. We build and nurture meaningful relationships with your audience. Our capability covers active interaction, effective moderation, online reputation management, and creating experiences that grow your audience and future customers." + }, + "metrics": { + "label": "Metrics and Results", + "text": "We go beyond social media posting. We build and nurture meaningful relationships with your audience. Our capability covers active interaction, effective moderation, online reputation management, and creating experiences that grow your audience and future customers." + } + }, + "steps": { + "firstStep": { + "label": "01. Information Gathering and Needs", + "text": "Before starting any project, we focus on thoroughly understanding your needs, business goals, and any restrictions or preferences. We evaluate potential risks and define success criteria and objectives to be met. Communication is key, and we validate the project's direction through results presentations and meetings to ensure ideal communication." + }, + "secondStep": { + "label": "02. Onboarding and Discovery", + "text": "Our team immerses itself in your brand and business. We outline the essential points extracted from your identity, define clear objectives, success criteria, and limitations. If you already have an online presence, we conduct an initial audit for improvements. We also analyze the competition to identify opportunities and UX/UI solutions beneficial to you." + }, + "thirdStep": { + "label": "03. Web Content Architecture", + "text": "We structure your site's architecture. We define what information goes on each page and how pages and elements connect, creating a clear sitemap and intuitive content hierarchy. This allows visualizing flow, planning user experience, and anticipating the strategic placement of key elements, ensuring fluid navigation and optimized user experience." + }, + "fourthStep": { + "label": "04. Low-Fidelity Wireframes", + "text": "We develop basic visual schemes of your website, focusing on structure and information design without aesthetic details. We determine user experience, element distribution, content block forms, and CTA placement. We use placeholder texts to align content with visuals and resolve architectural conflicts. From a development perspective, we assess complexity to minimize implementation risks." + }, + "fifthStep": { + "label": "05. Visual Concepts", + "text": "Considering your industry, competitive analysis, brand manual, and preferences, we create visual moodboards. These show your product's emotional tone, examples of colors, shapes, and styles that will guide the final design phase. Subsequently, we apply the chosen moodboard's design to the low-fidelity wireframes, defining colors, text styles, heading sizes, and selecting illustrative content and icons." + }, + "sixthStep": { + "label": "06. Final Design and Development", + "text": "Once the design direction is approved, our team finalizes the approved pages, integrating colors and text styles into the element library. The design extends to the rest of the site, creating a library of elements and rules allowing quick construction of new pages and UI modules, ensuring coherence and efficiency. The design then moves to development, where our team fully programs it (web dev, WordPress, other CMS), optimized, responsive, and secure. Coordinated design-development work ensures the final result meets all client requirements." + } + } + }, + + "projects": { + "sectionTitle": "Our Projects", + "sectionDescription": "We have created unique digital universes that solve challenges and connect brands with their audiences. We know that every project is a story reflecting innovation and clear results, and we are ready to bring your vision to life.", + "sectionCTAButton": "Conectemos ahora", + "sectionPortfolioButton": "Explore our portfolio" + }, + "testimonials": { + "sectionTitle": "Our clients", + "testimony1": { + "position": "General Editor", + "review": "“With the production of our website, MURA has consolidated as the space for literary and artistic experimentation we sought. The team not only built a WordPress site that faithfully reflects our identity, but also provided us with editorial and security functionalities. They understood our vision for literature and self-management, allowing us to build a vibrant community of creators in Ecuador and Latin America.”" + }, + "testimony2": { + "position": "Editor", + "review": "“With 'Búsquedas', the secure and respectful digital space we needed to foster dialogue materialized. The work done allowed us to weave a living network between different currents and experiences, collecting texts and personal reflections. We greatly value how the team understood our project's essence, facilitating diverse voices to share their understandings and mutually enrich each other.”" + }, + "testimony3": { + "position": "Director", + "review": "“They delivered a solution that disseminates literature and art internationally, overcoming geographical limitations. Their team understood our collaborative model and their creativity integrated an extensive virtual library and an interactive sound section, boosting authors' work with an immersive experience.”" + } + }, + "team": { + "sectionTitle": "Meet Our Team", + "sectionDescription": "Our team is the heart of CloudTech, where creativity meets technology to bring your most ambitious projects to life. Meet the minds behind your digital universe.", + "sectionCTAButton": "Let's talk about your project", + "card": { + "teamMember1": { + "position": "Strategic Communicator", + "description": "Digital communicator and strategist with over a decade of experience. Expert in creating impactful content and campaigns with multimedia pieces and custom strategies, he uses his versatility to create complex narratives that reflect desired objectives and generate impact." + }, + "teamMember2": { + "position": "Fullstack Developer", + "description": "Fullstack Developer in JavaScript, React, and Python, with a focus on interaction and communication. His background in translation allows him to make technology understandable, creating dynamic platforms that optimize user experience, fostering global connection." + }, + "teamMember3": { + "position": "Fullstack Developer", + "description": "Fullstack Developer expert in JavaScript, React, and Python, passionate about optimizing processes and designing responsive interfaces. He transforms complex technical concepts into clear solutions, ensuring functional and user-friendly software and interfaces that enhance user experience." + } + } + }, + "benefits": { + "sectionTitle": "Web Development Benefits", + "sectionDescription": "Investing in a professional digital presence is key to your brand's success. A well-developed website is not just a showcase; it's a strategic tool that drives your growth and connects effectively with your audience.", + "card": { + "benefit1": { + "title": "Credibility & Professionalism", + "description": "A modern, functional website instantly builds trust. It reinforces your brand's authority, projects a strong image, and establishes the credibility needed to attract and retain customers." + }, + "benefit2": { + "title": "Superior User Experience (UX)", + "description": "Optimized UX ensures every interaction is satisfactory, keeping your audience engaged and facilitating access to information they seek about you, your brand, services, products, or catalog." + }, + "benefit3": { + "title": "Global Visibility & Reach", + "description": "A website opens you to the world. It allows you to reach unlimited audiences 24/7, transcending geographical and time barriers so your brand is always accessible." + }, + "benefit4": { + "title": "SEO Optimization for Search", + "description": "We boost your brand's visibility on Google and other search engines. Through integrated SEO strategies, we attract quality organic traffic, ensuring your site appears when potential clients search for your products or services." + }, + "benefit5": { + "title": "Key Competitive Advantage", + "description": "Stand out in your sector with a digital presence that surpasses the competition. A strategic, updated website not only meets your objectives but also positions you as an innovative leader, clearly differentiating you in the market." + }, + "benefit6": { + "title": "Scalability & Adaptability", + "description": "Your business evolves, and your website must too. We create flexible, scalable solutions that grow with you, allowing you to add new features, expand content, or integrate tools as your needs change." + } + } + }, + "values": { + "sectionTitle": "Our Values", + "sectionDescription": "At CloudTech, our essence is founded on solid principles that guide every project and every interaction. They are the engine that drives us to create digital universes that truly transform your vision.", + "card": { + "value1": { + "title": "Constant Innovation", + "description": "We are committed to always being at the forefront of technology and design. We explore new solutions and trends to build digital universes that not only meet your current needs but also anticipate your brand's future in cyberspace." + }, + "value2": { + "title": "Results Focus", + "description": "Every action we take is aimed at generating tangible and measurable value for your business. We are dedicated to transforming your objectives into clear metrics and sustained growth, ensuring your digital investment translates into real success and lasting connections." + }, + "value3": { + "title": "Strategic Collaboration", + "description": "We firmly believe in the power of collaborative work. We establish a transparent alliance, listening to your vision and co-creating custom digital solutions. Your success is our priority, and together we mold your online presence." + }, + "value4": { + "title": "Digital Excellence", + "description": "We seek the highest quality in every detail of your digital universe. From strategy to code and design, we strive to exceed expectations, guaranteeing robust, intuitive, and cutting-edge solutions that elevate your brand." + }, + "value5": { + "title": "Passion for Development", + "description": "Our dedication goes beyond development. We are passionate about seeing how our digital creations transform businesses and generate real connections. We drive every project with enthusiasm, committed to your brand's success and visibility." + }, + "value6": { + "title": "Transparency and Trust", + "description": "We operate with total honesty and clarity at every stage of the process. We build solid relationships based on open communication and mutual trust, ensuring you are always informed and confident in the path we take together." + } + } + }, + "workWithUs": { + "sectionTitle": "Why Work With Us?", + "sectionDescription": "We mold ideas to create digital universes that not only look good but generate real, measurable impact. Choosing us means partnering with a team that combines creativity, experience, and a structured process to take your brand to the next level with real results.", + "workWithUsCTA": "Contact Us", + "statistics": { + "customerSatisfaction": "Client Satisfaction", + "activeUsers": "Active Users per Year", + "completedProjects": "Completed Projects", + "yearsOfExperience": "Years of Experience" + } + }, + "contact": { + "sectionTitle": "Let's connect!", + "sectionDescription": "Your project is our next challenge", + "formButton": "Send", + "whatsAppButton": "Tell us your idea", + "labels": { + "nameLabel": "Full name:", + "phoneLabel": "Phone:", + "emailLabel": "Email:", + "projectNameLabel": "Name of your project:", + "messageLabel": "Tell us what you need:" + }, + "placeholders": { + "nameHolder": "Your name", + "phoneHolder": "Your phone", + "emailHolder": "Your email", + "projectHolder": "Your project or company name", + "messageHolder": "Your idea goes here..." + }, + "alerts": { + "alertSuccess": "Thanks! Your message was sent.", + "alertError": "Oops! A problem occurred, please try again.", + "loading": "Sending..." + } + }, + "whatsapp": { + "accountName": "CloudTech Sistemas", + "statusMessage": "Usually replies within an hour", + "chatMessage": "¡Hi there! 👋 How can we help you?", + "placeholder": "Type your message..." + } +} diff --git a/public/locales/es/translation.json b/public/locales/es/translation.json new file mode 100644 index 0000000000..f55da95569 --- /dev/null +++ b/public/locales/es/translation.json @@ -0,0 +1,311 @@ +{ + "headers": { + "headerHome": { + "headLine": "Moldeamos ideas para crear universos digitales", + "headerDescription": "Construimos entornos digitales que fortalecen tu marca y unen tus audiencias. Creamos presencias online que aseguran resultados medibles y un crecimiento escalable.", + "portfolioButton": "Proyectos", + "contactButton": "Contáctanos" + }, + "headerAbout": { + "headLine": "Tu visión, el motor de nuestra creatividad", + "headerDescription": "Moldeamos tus ideas en experiencias web poderosas que reflejan la esencia de tu marca y cumplen tus objetivos estratégicos.", + "portfolioButton": "Proyectos", + "contactButton": "Contáctanos" + }, + "headerServices": { + "headLine": "Tu Universo a Medida", + "headerDescription": "Nuestros servicios son el puente entre tus ideas y soluciones digitales potentes que impulsarán tu marca y generarán crecimiento sostenido.", + "portfolioButton": "Proyectos", + "contactButton": "Contáctanos" + }, + "headerProjects": { + "headLine": "Diseñamos y conectamos tu ecosistema digital", + "headerDescription": "Exploramos la intersección entre estrategia, diseño y tecnología para construir soluciones digitales únicas que resuelven desafíos reales y canalizan los esfuerzos hacia una experiencia inigualable.", + "contactButton": "Contáctanos" + } + }, + "footer": { + "footerSlogan": "Moldeamos ideas para crear universos digitales." + }, + "about": { + "sectionTitle": "¿Quiénes somos?", + "sectionDescription": "Somos una empresa que cree en el valor de unir la creatividad y la tecnología. Combinamos estrategia, diseño de vanguardia y tecnología robusta para crear una presencia online intuitiva y funcional." + }, + "services": { + "sectionTitle": "Conoce nuestros servicios", + "sectionDescription": "Cada uno de nuestros servicios está diseñado para construir y amplificar tu presencia digital, convirtiendo tus ideas en productos y resultados medibles. Explora cómo podemos dar forma a tu universo online.", + "webDevelopmentTitle": "Desarrollo Web", + "webDevelopmentSubtitle": "Construyendo tu universo digital", + "communicationTitle": "Comunicación", + "communicationSubtitle": "Reunimos tus audiencias", + "softwareSolutionsTitle": "Soluciones de software", + "softwareSolutionsSubtitle": "Herramientas para materializar tus ideas", + "card": { + "wordpress": { + "title": "Wordpress", + "description": "Potencia tu presencia con la plataforma CMS más versátil y popular. Diseñamos y desarrollamos sitios WordPress a medida, fáciles de gestionar y optimizados para el crecimiento de tu marca.", + "link": "Tu web con WordPress" + }, + "webdev": { + "title": "Web Dev", + "description": "Para soluciones únicas y necesidades específicas. Construimos desarrollos web personalizados con las últimas tecnologías, garantizando funcionalidad, escalabilidad y rendimiento excepcional.", + "link": "Explora el desarrollo a medida" + }, + "ecommerce": { + "title": "E-commerce", + "description": "Lanza y comienza a vender con tu tienda online gracias a un diseño intuitivo y funcionalidades avanzadas. Creamos experiencias de compra que convierten visitantes en clientes fieles y aumentan tus ventas.", + "link": "Impulsa tu E-commerce" + }, + "webflow": { + "title": "WebFlow", + "description": "Diseñamos sitios web visualmente impresionantes y altamente funcionales sin código complejo. Ideal para proyectos que demandan diseño de vanguardia y autonomía en la gestión.", + "link": "Descubre Webflow" + }, + "socialMedia": { + "title": "Manejo de redes", + "description": "Gestionamos tus plataformas sociales con estrategia y creatividad. Construimos comunidades, aumentamos el engagement y fortalecemos la presencia de tu marca e interacción con tu público.", + "link": "Impulsa tu marca en redes" + }, + "copyWriting": { + "title": "Copywriting", + "description": "Creamos textos persuasivos y optimizados para web, blogs, redes sociales y campañas que captan la atención, transmiten un mensaje claro y convierten visitantes y seguidores en clientes.", + "link": "Escríbenos" + }, + "contentProduction": { + "title": "Producción de contenido", + "description": "Contamos tu historia con videos, post de redes sociales, artículos de blog y más. Generamos contenido de valor que posiciona tu marca como referente en tu sector.", + "link": "Crea con nosotros" + }, + "communicationStrategy": { + "title": "Estrategia de comunicación", + "description": "Planificamos la hoja de ruta de tu comunicación digital. Investigamos, definimos tu voz y creamos un plan integral que reúne todo tu universo digital para cumplir tus objetivos.", + "link": "Descubre tu potencial" + }, + "autocadLt": { + "title": "AutoCAD LT", + "description": "Transforma tus ideas en planos precisos con el software de dibujo 2D líder en la industria. Crea, edita y documenta tus diseños con eficiencia, optimizando tu flujo de trabajo y entrega tus proyectos en tiempo récord.", + "link": "Potencia tu diseño 2D" + }, + "autocadIst": { + "title": "AutoCAD IST", + "description": "Diseña sin límites en 2D y 3D con la herramienta más completa para arquitectos e ingenieros. Acelera tus flujos de trabajo y crea diseños impactantes con potentes funciones de modelado, visualización y automatización.", + "link": "Adquirir licencia completa" + } + } + }, + "process": { + "sectionToggleCapacities": "Capacidades", + "sectionToggleProcess": "Proceso", + "capacities": { + "webConcept": { + "label": "Conceptualización Web", + "text": "Proceso creativo y estratégico que define los fundamentos visuales y estructurales de un sitio web. Identificamos los valores centrales y objetivos de tu marca, traduciéndolos en diseños de prueba alineados con la audiencia. Exploramos múltiples borradores de layout, paletas de color, tipografía y flujos de usuario. Generamos un marco claro y accionable para un sitio funcional, estéticamente impactante y alineado con las metas de tu negocio." + }, + "webAudit": { + "label": "Auditoría Web", + "text": "Evaluamos tu sitio web exhaustivamente para identificar mejoras en usabilidad, rendimiento, accesibilidad y diseño. Proporcionamos insights accionables, detectando barreras para la interacción del usuario y el crecimiento de tu negocio. Analizamos métricas clave (tiempos de carga, navegación). La auditoría identifica optimizaciones rápidas y a largo plazo (imágenes, estructura), entregando un informe detallado con recomendaciones priorizadas para mejoras impactantes en tu web." + }, + "webIntDev": { + "label": "Desarrollo Web Integral", + "text": "Servicio integral de creación web. Entregamos una presencia digital cohesiva y personalizada, alineada con tus objetivos y audiencia. Desarrollamos una arquitectura de información escalable para navegación fluida y comunicación efectiva. La fase de diseño crea una identidad visual completa con UI kits y elementos interactivos. El resultado es un sitio optimizado, de alto rendimiento y visualmente atractivo que impulsa la interacción y el crecimiento de tu negocio a largo plazo." + }, + "webDesignSystems": { + "label": "Sistemas de Diseño Web", + "text": "Creamos marcos reutilizables para simplificar actualizaciones y asegurar consistencia visual en tu ecosistema digital. Basados en estándares de la industria, organizamos elementos UI (botones, tipografía, colores) en módulos escalables para una creación eficiente de páginas, reduciendo recursos. Identificamos componentes clave, los ensamblamos en patrones y plantillas, y realizamos pruebas exhaustivas. Con documentación y capacitación, este sistema optimiza flujos, mejora la colaboración y apoya la escalabilidad web." + }, + "webUpkeep": { + "label": "Mantenimiento Web", + "text": "Aseguramos que tu sitio web se mantenga seguro, funcional y alineado con tus objetivos de negocio. Nuestro servicio incluye actualizaciones rutinarias, monitoreo de rendimiento, corrección de errores y pruebas de calidad para anticipar problemas. Optimizamos el rendimiento, la velocidad de carga, la seguridad y realizamos actualizaciones menores de diseño o contenido. Esto garantiza un funcionamiento consistente, la satisfacción del usuario y una presencia digital confiable para tu marca." + }, + "serviceIntegration": { + "label": "Integración de Servicios", + "text": "Mejoramos la funcionalidad de tu web conectándola con herramientas y servicios externos clave. Esto incluye plataformas de análisis, soporte al cliente, pasarelas de pago y agendamiento automático. Evaluamos tus necesidades y seleccionamos las herramientas adecuadas. Nuestro equipo gestiona la integración técnica para asegurar compatibilidad y un funcionamiento fluido, alineando todo con tu marca. El soporte post-integración garantiza sistemas eficientes, maximizando el valor." + }, + "strategicCommunication": { + "label": "Comunicación Estratégica", + "text": "Desarrollamos hojas de ruta completas para tu comunicación digital. Incluimos investigación exhaustiva de audiencia y competidores, definición de voz de marca, creación de pilares de contenido y planificación multicanal. Nuestro objetivo es que cada mensaje contribuya a tus metas de negocio, asegurando coherencia y efectividad a largo plazo." + }, + "seoAndDigitalContent": { + "label": "SEO & Contenido Digital", + "text": "No solo escribimos, optimizamos. Creamos textos que captan la atención humana y la de los motores de búsqueda. Esto incluye redacción persuasiva, investigación de palabras clave, estructuración de contenido para la legibilidad (UX Writing) y adaptación de mensajes para cada plataforma, asegurando que tu contenido sea descubierto y genere la acción deseada." + }, + "communityManagement": { + "label": "Gestión de Comunidades", + "text": "Vamos más allá de la publicación en redes. Construimos y nutrimos relaciones significativas con tu audiencia. Nuestra capacidad abarca la interacción activa, la moderación efectiva, la gestión de la reputación online y la creación de experiencias que aumentan tu audiencia y tus futuros clientes." + }, + "metrics": { + "label": "Métricas y Resultados", + "text": "Medimos el impacto real de cada mensaje. Analizamos métricas clave en redes sociales, blogs y campañas para obtener insights accionables sobre el comportamiento de tu audiencia y el rendimiento del contenido. Esto nos permite optimizar estrategias en tiempo real y demostrar el retorno de inversión (ROI) de cada esfuerzo, ajustando el rumbo para asegurar el cumplimiento de tus objetivos de comunicación." + } + }, + "steps": { + "firstStep": { + "label": "01. Recopilación de Información y Necesidades", + "text": "Antes de iniciar cualquier proyecto, nos enfocamos en comprender a fondo tus necesidades, metas de negocio y cualquier restricción o preferencia. Evaluamos riesgos potenciales y definimos los criterios de éxito y objetivos a cumplir. La comunicación es clave, y validamos la dirección del proyecto a través de presentaciones de resultados y reuniones para asegurar una comunicación ideal." + }, + "secondStep": { + "label": "02. Onboarding y Descubrimiento", + "text": "Nuestro equipo se sumerge en tu marca y negocio. Exponemos los puntos esenciales de tu identidad, definimos objetivos claros, criterios de éxito y limitaciones. Si ya tienes presencia online, realizamos una auditoría inicial para mejoras. Analizamos la competencia para identificar oportunidades y soluciones UX/UI a tu beneficio." + }, + "thirdStep": { + "label": "03. Arquitectura de Contenido Web", + "text": "Estructuramos la arquitectura de tu sitio. Definimos qué información va en cada página y cómo se conectan los elementos, creando un sitemap claro y una jerarquía de contenido intuitiva. Esto permite visualizar el flujo, planificar la experiencia del usuario y anticipar la ubicación estratégica de elementos clave, asegurando una navegación fluida y una experiencia de usuario optimizada." + }, + "fourthStep": { + "label": "04. Wireframes de Baja Fidelidad (Low-Fidelity)", + "text": "Desarrollamos esquemas visuales básicos de tu web, enfocándonos en la estructura y diseño sin detalles estéticos. Determinamos la experiencia de Usuario, distribución de elementos, forma de bloques de contenido y posición de CTAs. Usamos textos de prueba para alinear contenido con lo visual y resolver conflictos. Desde desarrollo, evaluamos la complejidad para minimizar riesgos en la implementación." + }, + "fifthStep": { + "label": "05. Conceptos Visuales", + "text": "Considerando tu industria, análisis competitivo, manual de marca y preferencias, creamos moodboards visuales. Estos muestran el tono emocional de tu producto, ejemplos de colores, formas y estilos que guiarán el diseño final. Aplicamos el diseño del moodboard elegido a los wireframes, definiendo colores, estilos de texto, tamaños de encabezados y seleccionando contenido ilustrativo e íconos." + }, + "sixthStep": { + "label": "06. Diseño Final y Desarrollo (Implementación)", + "text": "Una vez aprobada la dirección de diseño, finalizamos e integramos colores/estilos. Creamos una librería de elementos/reglas para construir páginas y módulos UI rápidamente, asegurando coherencia y eficiencia. El diseño pasa a desarrollo para programación completa (web dev, WordPress, otros CMS), optimizado, responsive y seguro. La coordinación diseño-desarrollo asegura que el resultado final cumpla los requisitos del cliente." + } + } + }, + + "projects": { + "sectionTitle": "Nuestros proyectos", + "sectionDescription": "Hemos creado universos digitales únicos que resuelven desafíos y conectan a las marcas con sus audiencias. Sabemos que cada proyecto es una historia que refleja innovación y resultados claros, y estamos dispuestos a plasmarlo en tu visión.", + "sectionCTAButton": "Conectemos ahora", + "sectionPortfolioButton": "Explora nuestro portafolio" + }, + "testimonials": { + "sectionTitle": "Nuestros clientes", + "testimony1": { + "position": "Editor General", + "review": "“El equipo no solo construyó un sitio web en WordPress que refleja fielmente nuestra identidad, sino que nos dotó de las funcionalidades editoriales y de seguridad. Comprendieron nuestra visión sobre la literatura y la autogestión, permitiéndonos construir una amplia comunidad de creadores en Ecuador y Latinoamérica.”" + }, + "testimony2": { + "position": "Editora", + "review": "“Con 'Búsquedas' se materializó el espacio digital seguro y respetuoso que necesitábamos para fomentar el diálogo. El trabajo realizado nos permitió tejer una red viva entre distintas corrientes y experiencias, recogiendo textos y reflexiones personales. Valoramos enormemente cómo el equipo entendió la esencia de nuestro proyecto, facilitando que distintas voces compartan sus comprensiones y se nutran mutuamente.”" + }, + "testimony3": { + "position": "Editor General", + "review": "“Nos entregaron una solución que difunde literatura y arte a nivel internacional, superando limitaciones geográficas. Su equipo comprendió nuestro modelo colaborativo y su creatividad integró una biblioteca virtual y una sección interactiva de sonido, impulsando la obra de autores con una experiencia inmersiva.”" + } + }, + "team": { + "sectionTitle": "Conoce a nuestro equipo", + "sectionDescription": "Nuestro compromiso es generar conexiones reales y un crecimiento medible para tu negocio. En CloudTech, tu singularidad cobra vida en un universo digital a medida y sofisticado.", + "sectionCTAButton": "Hablemos de tu proyecto", + "card": { + "teamMember1": { + "position": "Comunicador estratégico", + "description": "Comunicador y estratega digital con más de una década de experiencia. Experto en crear contenidos y campañas impactantes con piezas multimedia y estrategias a medida, utiliza su versatilidad para la creación de narrativas complejas que reflejen los objetivos deseados y generen impacto." + }, + "teamMember2": { + "position": "Fullstack Developer", + "description": "Programador Fullstack en JavaScript, React y Python, con un enfoque en la interacción y comunicación. Su trayectoria en traducción le permite hacer que la tecnología sea comprensible, creando plataformas dinámicas que optimizan la experiencia de usuarios, fomentando la conexión global." + }, + "teamMember3": { + "position": "Fullstack Developer", + "description": "Desarrollador Fullstack experto en JavaScript, React y Python, apasionado por optimizar procesos y diseñar interfaces responsivas. Transforma conceptos técnicos complejos en soluciones claras, garantizando software e interfaces funcionales y amigables que mejoran la experiencia del usuario." + } + } + }, + "benefits": { + "sectionTitle": "Beneficios del desarrollo web", + "sectionDescription": "Invertir en una presencia digital profesional es clave para el éxito de tu marca. Un sitio web bien desarrollado no es solo una vitrina, es una herramienta estratégica que impulsa tu crecimiento y conecta de manera efectiva con tu audiencia.", + "card": { + "benefit1": { + "title": "Credibilidad y Profesionalismo", + "description": "Un sitio web moderno y funcional genera confianza instantánea. Refuerza la autoridad de tu marca, proyecta una imagen sólida y establece la credibilidad necesaria para atraer y retener a tus clientes." + }, + "benefit2": { + "title": "Experiencia de Usuario Superior (UX)", + "description": "Una UX optimizada garantiza que cada interacción sea satisfactoria, manteniendo a tu audiencia comprometida y facilitando el acceso a la información que buscan, sobre ti, tu marca, tus servicios, tus productos o tu catálogo." + }, + "benefit3": { + "title": "Visibilidad y Alcance Global", + "description": "Un sitio web te abre al mundo. Te permite llegar a audiencias ilimitadas las 24 horas del día, 7 días a la semana, trascendiendo barreras geográficas y temporales para que tu marca esté siempre accesible." + }, + "benefit4": { + "title": "Optimización SEO en Buscadores", + "description": "Aumentamos la visibilidad de tu marca en Google y otros buscadores. A través de estrategias SEO integradas, atraemos tráfico orgánico de calidad, asegurando que tu sitio aparezca cuando tus clientes potenciales busquen tus productos o servicios." + }, + "benefit5": { + "title": "Ventaja Competitiva Clave", + "description": "Destácate en tu sector con una presencia digital que supera a la competencia. Un sitio web estratégico y actualizado no solo cumple tus objetivos, sino que te posiciona como líder innovador y te diferencia claramente en el mercado." + }, + "benefit6": { + "title": "Escalabilidad y Adaptabilidad", + "description": "Tu negocio evoluciona, y tu web también debe hacerlo. Creamos soluciones flexibles y escalables que crecen contigo, permitiéndote añadir nuevas funcionalidades, expandir contenidos o integrar herramientas conforme tus necesidades cambien." + } + } + }, + "values": { + "sectionTitle": "Valores", + "sectionDescription": "Nuestra esencia se fundamenta en principios sólidos que guían cada proyecto y cada interacción. Son el motor que nos impulsa a crear universos digitales que verdaderamente transforman tu visión.", + "card": { + "value1": { + "title": "Innovación Constante", + "description": "Nos comprometemos a estar siempre a la vanguardia de la tecnología y el diseño. Exploramos nuevas soluciones y tendencias para construir universos digitales que no solo satisfagan tus necesidades actuales, sino que también anticipen el futuro de tu marca en el ciberespacio." + }, + "value2": { + "title": "Enfoque en Resultados", + "description": "Cada acción que tomamos está orientada a generar valor tangible y medible para tu negocio. Nos dedicamos a transformar tus objetivos en métricas claras y crecimiento sostenido, asegurando que tu inversión digital se traduzca en éxito real y conexiones duraderas." + }, + "value3": { + "title": "Colaboración Estratégica", + "description": "Creemos firmemente en el poder del trabajo colaborativo. Establecemos una alianza transparente, escuchando tu visión y co-creando soluciones digitales a medida. Tu éxito es nuestra prioridad y juntos moldeamos tu presencia online." + }, + "value4": { + "title": "Excelencia Digital", + "description": "Buscamos la máxima calidad en cada detalle de tu universo digital. Desde la estrategia hasta el código y el diseño, nos esforzamos por superar las expectativas, garantizando soluciones robustas, intuitivas y de vanguardia que elevan tu marca." + }, + "value5": { + "title": "Pasión por el desarrollo", + "description": "Nuestra dedicación va más allá del desarrollo. Nos apasiona ver cómo nuestras creaciones digitales transforman negocios y generan conexiones reales. Impulsamos cada proyecto con entusiasmo, comprometidos con el éxito y la visibilidad de tu marca." + }, + "value6": { + "title": "Transparencia y Confianza", + "description": "Operamos con total honestidad y claridad en cada etapa del proceso. Construimos relaciones sólidas basadas en la comunicación abierta y la confianza mutua, asegurando que siempre estés informado y seguro del camino que seguimos juntos." + } + } + }, + "workWithUs": { + "sectionTitle": "¿Por qué trabajar con nosotros? ", + "sectionDescription": "Moldeamos ideas para crear universos digitales que no solo lucen bien, sino que generan un impacto real y medible. Elegirnos significa aliarse con un equipo que combina creatividad, experiencia y un proceso estructurado para llevar tu marca al siguiente nivel con resultados reales.", + "workWithUsCTA": "Conectemos ahora", + "statistics": { + "customerSatisfaction": "Satisfacción de clientes", + "activeUsers": "Usuarios activos al año", + "completedProjects": "Proyectos completados", + "yearsOfExperience": "Años de experiencia" + } + }, + "contact": { + "sectionTitle": "Conectemos", + "sectionDescription": "Tu proyecto es nuestro próximo reto.", + "formButton": "Enviar", + "whatsAppButton": "Cuéntanos tu idea", + "labels": { + "nameLabel": "Nombre completo:", + "phoneLabel": "Teléfono:", + "emailLabel": "Email:", + "projectNameLabel": "Nombre de tu proyecto:", + "messageLabel": "Cuéntanos lo que necesitas:" + }, + "placeholders": { + "nameHolder": "Tu nombre", + "phoneHolder": "Tu teléfono", + "emailHolder": "Tu correo", + "projectHolder": "El nombre de tu proyecto o compañía.", + "messageHolder": "Tu idea va aquí..." + }, + "alerts": { + "alertSuccess": "¡Gracias! Tu mensaje ha sido enviado.", + "alertError": "¡Ups! Ocurrió un problema, intenta nuevamente.", + "loading": "Enviando..." + } + }, + "whatsapp": { + "accountName": "CloudTech Sistemas", + "statusMessage": "Normalmente responde en 1 hora", + "chatMessage": "¡Hola! 👋 ¿Cómo podemos ayudarte?", + "placeholder": "Escribe tu mensaje..." + } +} diff --git a/public/rigo-baby.jpg b/public/rigo-baby.jpg deleted file mode 100644 index da566a74a0..0000000000 Binary files a/public/rigo-baby.jpg and /dev/null differ diff --git a/render.yaml b/render.yaml deleted file mode 100644 index b61ac68296..0000000000 --- a/render.yaml +++ /dev/null @@ -1,33 +0,0 @@ -# This file was generated by Render's heroku-import Heroku CLI plugin -# https://www.npmjs.com/package/@renderinc/heroku-import -# Schema documented at https://render.com/docs/yaml-spec -services: - - type: web # valid values: https://render.com/docs/yaml-spec#type - region: ohio - name: sample-service-name - env: python # valid values: https://render.com/docs/yaml-spec#environment - buildCommand: "./render_build.sh" - startCommand: "gunicorn wsgi --chdir ./src/" - plan: free # optional; defaults to starter - numInstances: 1 - envVars: - - key: VITE_BASENAME # Imported from Heroku app - value: / - - key: FLASK_APP # Imported from Heroku app - value: src/app.py - - key: FLASK_DEBUG # Imported from Heroku app - value: 0 - - key: FLASK_APP_KEY # Imported from Heroku app - value: "any key works" - - key: PYTHON_VERSION - value: 3.10.6 - - key: DATABASE_URL # Render PostgreSQL database - fromDatabase: - name: postgresql-trapezoidal-42170 - property: connectionString - -databases: # Render PostgreSQL database - - name: postgresql-trapezoidal-42170 - region: ohio - ipAllowList: [] # only allow internal connections - plan: free # optional; defaults to starter diff --git a/render_build.sh b/render_build.sh deleted file mode 100644 index 404821a383..0000000000 --- a/render_build.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash -# exit on error -set -o errexit - -npm install -npm run build - -pipenv install - -pipenv run upgrade diff --git a/requirements.txt b/requirements.txt index 4eac45f4f8..46ea6586e5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,6 @@ alembic==1.5.4; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' certifi==2020.12.5 click==7.1.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' -cloudinary==1.24.0 flask==1.1.2 flask-admin==1.5.7 flask-cors==3.0.10 @@ -18,7 +17,7 @@ psycopg2-binary==2.8.6 python-dateutil==2.8.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' python-dotenv==0.15.0 python-editor==1.0.4 -pyyaml==5.4.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' +pyyaml==6.0.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' six==1.15.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' 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' diff --git a/src/api/admin.py b/src/api/admin.py index d312ff7990..c05ef5be2f 100644 --- a/src/api/admin.py +++ b/src/api/admin.py @@ -1,17 +1,36 @@ - + import os from flask_admin import Admin -from .models import db, Lead +from .models import db, Lead, CTAdmin, TokenBlockedList from flask_admin.contrib.sqla import ModelView +from wtforms import PasswordField + + +class CTAdminModelView(ModelView): + column_list = ('id', 'email') + form_columns = ('email', 'password_field') + column_exclude_list = ['_password'] + form_extra_fields = { + 'password_field': PasswordField('Password') + } + + def on_model_change(self, form, model, is_created): + if form.password_field.data: + model.password = form.password_field.data + elif is_created and not form.password_field.data: + raise ValueError( + 'El password es obligatorio para nuevos administradores') + def setup_admin(app): app.secret_key = os.environ.get('FLASK_APP_KEY', 'sample key') app.config['FLASK_ADMIN_SWATCH'] = 'cerulean' - admin = Admin(app, name='4Geeks Admin', template_mode='bootstrap3') + admin = Admin(app, name='CloudTech Admin', template_mode='bootstrap3') - # Add your models here, for example this is how we add a the User model to the admin admin.add_view(ModelView(Lead, db.session)) + admin.add_view(CTAdminModelView(CTAdmin, db.session)) + admin.add_view(ModelView(TokenBlockedList, db.session)) # You can duplicate that line to add mew models - # admin.add_view(ModelView(YourModelName, db.session)) \ No newline at end of file + # admin.add_view(ModelView(YourModelName, db.session)) diff --git a/src/api/models.py b/src/api/models.py index 68dcb82cfd..16c780cc5f 100644 --- a/src/api/models.py +++ b/src/api/models.py @@ -5,6 +5,35 @@ db = SQLAlchemy() +class CTAdmin(db.Model): + id: Mapped[int] = mapped_column(primary_key=True) + email: Mapped[str] = mapped_column( + String(120), unique=True, nullable=False) + _password: Mapped[str] = mapped_column( + "password", String(128), nullable=False) + + @property + def password(self): + raise AttributeError('Password is not a readable attribute.') + + @password.setter + def password(self, password): + from app import bcrypt + self._password = bcrypt.generate_password_hash( + password).decode('utf-8') + + # Método para verificar el password + def check_password(self, password): + from app import bcrypt + return bcrypt.check_password_hash(self._password, password) + + def serialize(self): + return { + "id": self.id, + "email": self.email + } + + class Lead(db.Model): id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] = mapped_column( @@ -25,3 +54,11 @@ def serialize(self): "company": self.company, "message": self.message } + + +class TokenBlockedList(db.Model): + id: Mapped[int] = mapped_column(primary_key=True) + jti: Mapped[str] = mapped_column(String(50), unique=True, nullable=False) + + def __repr__(self): + return f'' diff --git a/src/api/routes.py b/src/api/routes.py index da7a2296b4..ba2d39559f 100644 --- a/src/api/routes.py +++ b/src/api/routes.py @@ -2,10 +2,12 @@ This module takes care of starting the API Server, Loading the DB and Adding the endpoints """ from flask import Flask, request, jsonify, url_for, Blueprint -from api.models import db, Lead +from api.models import db, Lead, CTAdmin from api.utils import generate_sitemap, APIException +from sqlalchemy import select from flask_cors import CORS from sqlalchemy.exc import IntegrityError +from flask_jwt_extended import create_access_token api = Blueprint('api', __name__) @@ -52,6 +54,49 @@ def validate_lead_data(data): } +@api.route('/admin/login', methods=['POST']) +def admin_login(): + admin_data = request.get_json() + + if not admin_data: + return jsonify({"message": "Invalid Json or empty request body"}), 400 + + email = admin_data.get("email") + password = admin_data.get("password") + + if not email: + return jsonify({"message": "No email entered"}), 400 + if not password: + return jsonify({"message": "Password is required"}), 400 + + ct_admin = None + + try: + ct_admin = db.session.execute(select(CTAdmin).where( + CTAdmin.email == email)).scalar_one_or_none() + + if ct_admin is None: + return jsonify({"message": "Invalid credentials"}), 401 + + if not ct_admin.check_password(password): + return jsonify({"message": "Invalid credentials"}), 401 + + token = create_access_token( + identity=ct_admin.id, + additional_claims={"role": "ct_admin"} + ) + + return jsonify({ + "token": token, + "user_id": ct_admin.id, + "message": "Login successful" + }), 200 + + except Exception as e: + print(f"Login error: {e}") + return jsonify({"message": "Failed login. Please try again later"}), 500 + + @api.route('/contact', methods=['POST']) def add_lead(): lead_data = request.get_json() diff --git a/src/app.py b/src/app.py index 1b3340c0fa..f09503b59a 100644 --- a/src/app.py +++ b/src/app.py @@ -5,6 +5,8 @@ from flask import Flask, request, jsonify, url_for, send_from_directory from flask_migrate import Migrate from flask_swagger import swagger +from flask_bcrypt import Bcrypt +from flask_jwt_extended import JWTManager from api.utils import APIException, generate_sitemap from api.models import db from api.routes import api @@ -17,6 +19,9 @@ static_file_dir = os.path.join(os.path.dirname( os.path.realpath(__file__)), '../dist/') app = Flask(__name__) +bcrypt = Bcrypt(app) +jwt = JWTManager(app) +app.config["JWT_SECRET_KEY"] = os.environ.get("JWT_SECRET_KEY", "super-secret-jwt-key") app.url_map.strict_slashes = False # database condiguration @@ -43,6 +48,12 @@ # Handle/serialize errors like a JSON object +@app.before_request +def handle_options_request(): + if request.method == 'OPTIONS': + return '', 204 + + @app.errorhandler(APIException) def handle_invalid_usage(error): return jsonify(error.to_dict()), error.status_code @@ -57,6 +68,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/assets/img/Flags/es.svg b/src/front/assets/img/Flags/es.svg new file mode 100644 index 0000000000..a296ebf7e1 --- /dev/null +++ b/src/front/assets/img/Flags/es.svg @@ -0,0 +1,544 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/front/assets/img/Flags/us.svg b/src/front/assets/img/Flags/us.svg new file mode 100644 index 0000000000..9cfd0c927f --- /dev/null +++ b/src/front/assets/img/Flags/us.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/front/assets/img/HomeContact.jpg b/src/front/assets/img/HomeContact.jpg new file mode 100644 index 0000000000..97cbd258b9 Binary files /dev/null and b/src/front/assets/img/HomeContact.jpg differ diff --git a/src/front/assets/img/LogoWhats.svg b/src/front/assets/img/LogoWhats.svg new file mode 100644 index 0000000000..286dbb7e22 --- /dev/null +++ b/src/front/assets/img/LogoWhats.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/front/assets/img/Testimonials/clientProfile2.png b/src/front/assets/img/Testimonials/clientProfile2.png new file mode 100644 index 0000000000..9166b08ced Binary files /dev/null and b/src/front/assets/img/Testimonials/clientProfile2.png differ diff --git a/src/front/assets/img/Testimonials/clientProfile3.png b/src/front/assets/img/Testimonials/clientProfile3.png new file mode 100644 index 0000000000..1ccad8f98f Binary files /dev/null and b/src/front/assets/img/Testimonials/clientProfile3.png differ diff --git a/src/front/assets/img/Testimonials/clientprofile2.png b/src/front/assets/img/Testimonials/clientprofile2.png deleted file mode 100644 index 2b063d65f0..0000000000 Binary files a/src/front/assets/img/Testimonials/clientprofile2.png and /dev/null differ diff --git a/src/front/assets/img/Testimonials/companylogo2.png b/src/front/assets/img/Testimonials/companylogo2.png index 8f6306b1a4..1dcaf48b16 100644 Binary files a/src/front/assets/img/Testimonials/companylogo2.png and b/src/front/assets/img/Testimonials/companylogo2.png differ diff --git a/src/front/assets/img/Testimonials/companylogo3.png b/src/front/assets/img/Testimonials/companylogo3.png new file mode 100644 index 0000000000..9a29df2312 Binary files /dev/null and b/src/front/assets/img/Testimonials/companylogo3.png differ diff --git a/src/front/components/Admin/Leads.jsx b/src/front/components/Admin/Leads.jsx new file mode 100644 index 0000000000..0651b54e85 --- /dev/null +++ b/src/front/components/Admin/Leads.jsx @@ -0,0 +1,87 @@ + +import { useEffect, useReducer } from "react"; +import storeReducer, { initialStore } from "../../store"; + +const fetchAllLeads = async (dispatch) => { + const apiUrl = import.meta.env.VITE_BACKEND_URL; + dispatch({ type: 'GET_ALL_LEADS_START' }) + try { + const response = await fetch(`${apiUrl}/api/leads`) + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || "Error al obtener los leads"); + } + const leadsData = await response.json(); + dispatch({ type: 'GET_ALL_LEADS_SUCCESS', payload: leadsData }) + } catch (error) { + console.error("Error fetching leads:", error); + dispatch({ type: 'GET_ALL_LEADS_FAILURE', payload: error.message }) + } +} + +export const Leads = () => { + const [store, dispatch] = useReducer(storeReducer, initialStore()); + + useEffect(() => { + fetchAllLeads(dispatch); + }, [dispatch]) + + const { leads, leadsFetchStatus } = store; + + if (leadsFetchStatus.status === 'loading') { + return ( +
+
+ Cargando leads... +
+
+ ) + } + + if (leadsFetchStatus.status === 'error') { + return ( +
+ Error al cargar los leads: {leadsFetchStatus.error} +
+ ) + } + + return ( +
+
+
+ {leads.length === 0 ? ( +

No se encontraron leads

+ ) : ( + + + + + + + + + + + + + {leads.map((lead, index) => ( + + + + + + + + + ))} + +
#NombreEmailTeléfonoProyectoMensaje
{lead.id || index + 1}{lead.name}{lead.email}{lead.phone}{lead.company}{lead.message}
+ ) + } +
+
+
+ ) +} \ No newline at end of file diff --git a/src/front/components/Benefits/Benefits.jsx b/src/front/components/Benefits/Benefits.jsx index c0621a59e5..2770e5ea00 100644 --- a/src/front/components/Benefits/Benefits.jsx +++ b/src/front/components/Benefits/Benefits.jsx @@ -1,22 +1,25 @@ import { Card } from "./Card"; import { benefitsContent } from "../../utils/benefitsContent"; +import { useTranslation } from "react-i18next"; export const Benefits = () => { + const { t } = useTranslation(); + return (
-

Beneficios del desarrollo web

-

Invertir en una presencia digital profesional es clave para el éxito de tu marca. Un sitio web bien desarrollado no es solo una vitrina, es una herramienta estratégica que impulsa tu crecimiento y conecta de manera efectiva con tu audiencia.

+

{t('benefits.sectionTitle')}

+

{t('benefits.sectionDescription')}

{benefitsContent.map(benefit => (
))} diff --git a/src/front/components/Benefits/Card.jsx b/src/front/components/Benefits/Card.jsx index 4324e91d21..c81f2edba1 100644 --- a/src/front/components/Benefits/Card.jsx +++ b/src/front/components/Benefits/Card.jsx @@ -2,14 +2,14 @@ import { Link } from "react-router-dom"; export const Card = ({ title, description, showDivider }) => { return ( -
-
-
{title}
-

{description}

-
- {showDivider && ( -
- )} +
+
+
{title}
+

{description}

+ {showDivider && ( +
+ )} +
) }; \ No newline at end of file diff --git a/src/front/components/Footer.jsx b/src/front/components/Footer.jsx index 91d3517cbc..7e6675de64 100644 --- a/src/front/components/Footer.jsx +++ b/src/front/components/Footer.jsx @@ -5,34 +5,40 @@ import { faInstagram } from "@fortawesome/free-brands-svg-icons"; import { faLinkedin } from "@fortawesome/free-brands-svg-icons"; import { faYoutube } from '@fortawesome/free-brands-svg-icons' import { Link } from "react-router-dom"; +import { useTranslation } from "react-i18next"; -export const Footer = () => ( -
-
-
-
- clooudTech logo -

Moldeamos ideas para crear universos digitales.

-
- - - - +export const Footer = () => { + const { t } = useTranslation(); + + return ( +
+
+
+
+ clooudTech logo +

{t('footer.footerSlogan')}

+
+ + + + +
-
-
- CloudTech - Nosotros - Servicios - Proyectos - Contacto +
+ CloudTech + Nosotros + Servicios + Proyectos + Contacto +
-
-
-
-

Copyright © 2025 CloudTech

Terms and Conditions | Privacy Policy

-
-
-); +
+
+

Copyright © 2025 CloudTech

Terms and Conditions | Privacy Policy

+
+ + ) + +}; diff --git a/src/front/components/HeaderAbout.jsx b/src/front/components/HeaderAbout.jsx index 4b86bb8bad..4929197c16 100644 --- a/src/front/components/HeaderAbout.jsx +++ b/src/front/components/HeaderAbout.jsx @@ -1,33 +1,35 @@ import { Link } from "react-router-dom"; import HomeAbout from "../assets/img/HomeAbout.jpg" +import { useTranslation } from "react-i18next"; export const HeaderAbout = () => { + const { t } = useTranslation(); return ( - -
- CloudTech background image -
-
-
-
-

- Lorem, ipsum
dolor sit amet. -

- -

- Lorem ipsum dolor sit amet consectetur, adipisicing elit. Eveniet dolorum iste enim consequatur corporis ipsa tenetur modi sunt ullam placeat. -

- -
- Proyectos - Contáctanos -
-
+ +
+ CloudTech background image +
+
+
+
+

+ {t('about.sectionTitle')} +

+ +

+ {t('about.sectionDescription')} +

+ +
+ {t('headers.headerAbout.portfolioButton')} + {t('headers.headerAbout.contactButton')}
-
+
+
+
+ - ) } \ No newline at end of file diff --git a/src/front/components/HeaderContact.jsx b/src/front/components/HeaderContact.jsx new file mode 100644 index 0000000000..36df008b2b --- /dev/null +++ b/src/front/components/HeaderContact.jsx @@ -0,0 +1,225 @@ +import React, { useState, useContext, useEffect } from 'react'; +import { AppContext } from '../pages/Layout.jsx'; +import HomeContact from '../assets/img/HomeContact.jpg'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faWhatsapp } from '@fortawesome/free-brands-svg-icons'; +import { useTranslation } from 'react-i18next'; + +const HeaderContact = () => { + const { t } = useTranslation(); + const { store, dispatch } = useContext(AppContext); + + const [formData, setFormData] = useState({ + name: '', + phone: '', + email: '', + company: '', + message: '' + }); + + const { status, error } = store.contactForm; + const apiUrl = import.meta.env.VITE_BACKEND_URL; + + useEffect(() => { + if (status === 'success') { + setFormData({ name: '', phone: '', email: '', company: '', message: '' }); + } + }, [status]); + + const handleChange = (e) => { + const { name, value } = e.target; + if (status === 'success' || status === 'error') { + dispatch({ type: 'CONTACT_RESET_STATUS' }); + } + setFormData(prevState => ({ ...prevState, [name]: value })); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + dispatch({ type: 'CONTACT_SUBMIT_START' }); + if (!apiUrl) { + console.error("La variable de entorno BACKEND_URL no está definida. Revisa tu archivo .env"); + dispatch({ type: 'CONTACT_SUBMIT_FAILURE', payload: { general: 'Error de configuración del cliente.' } }); + return; + } + try { + const response = await fetch(`${apiUrl}/api/contact`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(formData), + }); + const result = await response.json(); + if (!response.ok) { + dispatch({ type: 'CONTACT_SUBMIT_FAILURE', payload: result.errors || { general: result.message } }); + return; + } + dispatch({ type: 'CONTACT_SUBMIT_SUCCESS' }); + } catch (networkError) { + dispatch({ type: 'CONTACT_SUBMIT_FAILURE', payload: { general: 'No se pudo conectar al servidor.' } }); + } + }; + + return ( + <> +
+ CloudTech background image + +
+
+
+
+

+ {t('contact.sectionTitle')} +

+ +

+ {t('contact.sectionTitle')} +

+ +

+ {t('contact.sectionDescription')} +

+ +

+ {t('contact.sectionDescription')} +

+ +
+
+ + + + {status === 'error' && error?.name &&
{error.name}
} +
+ +
+
+ + + + + {status === 'error' && error?.phone && +
{error.phone}
} +
+ +
+ + + + + {status === 'error' && error?.email && +
{error.email}
} +
+
+ +
+ + + +
+ +
+ + + +
+ + {status === 'success' && +
+ {t('contact.alerts.alertSuccess')} +
} + + {status === 'error' && error?.general && +
+ {t('contact.alerts.alertError')} +
} + + +
+ + + {t('contact.whatsAppButton')} + +
+
+ +
+
+
+
+
+
+
+ + ); +}; + +export default HeaderContact \ No newline at end of file diff --git a/src/front/components/HeaderHome.jsx b/src/front/components/HeaderHome.jsx index 8145d27396..5a2569e12b 100644 --- a/src/front/components/HeaderHome.jsx +++ b/src/front/components/HeaderHome.jsx @@ -1,33 +1,74 @@ import React from "react"; import { Link } from "react-router-dom"; import HomeOne from "../assets/img/HomeOne.jpg" +import { useTranslation } from "react-i18next"; export const HeaderHome = () => { + const { t } = useTranslation(); return ( - -
-
- CloudTech background image -
-
-
-

- Moldeamos ideas
para crear universos digitales. -

- -

- Construimos entornos digitales que fortalecen tu marca y unen tus audiencias. Creamos presencias online que aseguran resultados medibles y un crecimiento escalable. -

- -
- Proyectos - Contáctanos + <> + {/* Header para escritorio */} +
+
+
+ CloudTech background image +
+
+
+

+ {t('headers.headerHome.headLine')} +

+ +

+ {t('headers.headerHome.headerDescription')} +

+ +
+ + {t('headers.headerHome.portfolioButton')} + + + {t('headers.headerHome.contactButton')} + +
+
-
+
-
+ + {/* Header para movil */} +
+
+
+ CloudTech background image +
+
+
+

+ {t('headers.headerHome.headLine')} +

+ +

+ {t('headers.headerHome.headerDescription')} +

+ +
+ + {t('headers.headerHome.portfolioButton')} + + + {t('headers.headerHome.contactButton')} + +
+
+
+
+
+
+ ) + } \ No newline at end of file diff --git a/src/front/components/HeaderProjects.jsx b/src/front/components/HeaderProjects.jsx index 2d7bbb867b..f6e3746fd8 100644 --- a/src/front/components/HeaderProjects.jsx +++ b/src/front/components/HeaderProjects.jsx @@ -1,34 +1,37 @@ import { Link } from "react-router-dom"; import HomeProjects from "../assets/img/HomeProjects.jpg" +import { useTranslation } from "react-i18next"; export const HeaderProjects = () => { + const { t } = useTranslation(); return ( -
- CloudTech background image -
-
-
-
-
-

- Lorem, ipsum
dolor sit amet. -

- -

- Lorem ipsum dolor sit amet consectetur, adipisicing elit. Eveniet dolorum iste enim consequatur corporis ipsa tenetur modi sunt ullam placeat. -

- -
- {/* Proyectos */} - Contáctanos -
-
+
+ CloudTech background image +
+
+
+
+

+ {t('headers.headerProjects.headLine')} +

+ +

+ {t('headers.headerProjects.headerDescription')} +

+ +
+ {/* Proyectos */} + + {t('headers.headerHome.contactButton')} +
-
+
+
+
+ - ) } \ No newline at end of file diff --git a/src/front/components/HeaderServices.jsx b/src/front/components/HeaderServices.jsx index 0e4bece18b..674643edb7 100644 --- a/src/front/components/HeaderServices.jsx +++ b/src/front/components/HeaderServices.jsx @@ -1,34 +1,39 @@ import { Link } from "react-router-dom"; import HomeServices from "../assets/img/HomeServices.jpg" +import { useTranslation } from "react-i18next"; export const HeaderServices = () => { + const { t } = useTranslation(); return ( -
- CloudTech background image -
-
-
-
-

- Lorem, ipsum
dolor sit amet. -

- -

- Lorem ipsum dolor sit amet consectetur, adipisicing elit. Eveniet dolorum iste enim consequatur corporis ipsa tenetur modi sunt ullam placeat. -

- -
- Proyectos - Contáctanos -
-
-
+
+ CloudTech background image +
+
+
+
+

+ {t('headers.headerServices.headLine')} +

+ +

+ {t('headers.headerServices.headerDescription')} +

+ +
+ + {t('headers.headerHome.portfolioButton')} + + + {t('headers.headerHome.contactButton')} +
-
+
+
+
+ - ) } \ No newline at end of file diff --git a/src/front/components/HomeHeaderAlt.jsx b/src/front/components/HomeHeaderAlt.jsx deleted file mode 100644 index b110b8427a..0000000000 --- a/src/front/components/HomeHeaderAlt.jsx +++ /dev/null @@ -1,28 +0,0 @@ -import backHomeHeader from "../assets/img/HomeOne.jpg" -import { Link } from "react-router-dom" - -export const HomeHeaderAlt = () => { - return ( -
- CloudTech services background image -
-
-
-

- Moldeamos ideas
para crear universos digitales. -

- -

- Construimos entornos digitales que fortalecen tu marca y unen tus audiencias. Creamos presencias online que aseguran resultados medibles y un crecimiento escalable. -

- -
- Proyectos - Contáctanos -
-
-
-
-
- ) -} \ No newline at end of file diff --git a/src/front/components/LanguageSwitcher.jsx b/src/front/components/LanguageSwitcher.jsx new file mode 100644 index 0000000000..bc27c6dc1f --- /dev/null +++ b/src/front/components/LanguageSwitcher.jsx @@ -0,0 +1,41 @@ +import { useTranslation } from 'react-i18next'; +import { useState, useEffect } from 'react'; +import flagES from '../assets/img/Flags/es.svg' +import flagEN from '../assets/img/Flags/us.svg' + +function LanguageSwitcher() { + const { i18n } = useTranslation(); + const [activeFlag, setActiveFlag] = useState(i18n.language === 'es' ? flagES : flagEN); + + useEffect(() => { + setActiveFlag(i18n.language === 'es' ? flagES : flagEN); + }, [i18n.language]) + + const changeLanguage = (lng) => { + i18n.changeLanguage(lng); + }; + + return ( +
+ +
    +
  • + +
  • +
  • + +
  • + +
+
+ + ); +} + +export default LanguageSwitcher; \ No newline at end of file diff --git a/src/front/components/Navbar.jsx b/src/front/components/Navbar.jsx index e3a2819dbd..8a891957f5 100644 --- a/src/front/components/Navbar.jsx +++ b/src/front/components/Navbar.jsx @@ -2,15 +2,27 @@ import { Link } from "react-router-dom"; import LogoNavbar from "../assets/img/LogoNavbar.svg"; import LogoNavMovil from "../assets/img/LogoNavMovil.svg"; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faBars } from '@fortawesome/free-solid-svg-icons'; +import { faBars, faXmark } from '@fortawesome/free-solid-svg-icons'; +import LanguageSwitcher from "./LanguageSwitcher"; +import { useRef } from "react"; export const Navbar = () => { + const offcanvasRef = useRef() + const handleLinkClick = () => { + const offcanvasElement = offcanvasRef.current; + const offcanvasInstance = bootstrap.Offcanvas.getInstance(offcanvasElement); + + if (offcanvasInstance) { + offcanvasInstance.hide(); + }; + }; + return ( <> {/* --- NAVBAR DE ESCRITORIO --- */} {/*
*/} -