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
+ ) : (
+
+
+
+ #
+ Nombre
+ Email
+ Teléfono
+ Proyecto
+ Mensaje
+
+
+
+ {leads.map((lead, index) => (
+
+ {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 = () => (
-
-
-
-
-
-
Moldeamos ideas para crear universos digitales.
-
-
-
-
-
+export const Footer = () => {
+ const { t } = useTranslation();
+
+ return (
+
+
+
+
+
+
{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 (
-
-
-
-
-
-
-
-
- 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
-
-
+
+
+
+
+
+
+
+
+ {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 (
+ <>
+
+
+
+
+
+
+
+
+ {t('contact.sectionTitle')}
+
+
+
+ {t('contact.sectionTitle')}
+
+
+
+ {t('contact.sectionDescription')}
+
+
+
+ {t('contact.sectionDescription')}
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+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 (
-
-
-
-
-
-
-
-
- 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 */}
+
+
+
+
+
+
+
+
+ {t('headers.headerHome.headLine')}
+
+
+
+ {t('headers.headerHome.headerDescription')}
+
+
+
+
+ {t('headers.headerHome.portfolioButton')}
+
+
+ {t('headers.headerHome.contactButton')}
+
+
+
-
+
-
+
+ {/* Header para movil */}
+
+
+
+
+
+
+
+
+ {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 (
-
-
-
-
-
-
-
-
- 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
-
-
+
+
+
+
+
+
+
+ {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 (
-
-
-
-
-
-
-
- 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
-
-
-
+
+
+
+
+
+
+
+ {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 (
-
-
-
-
-
-
- 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 (
+
+
+
+
+
+
+ changeLanguage('es')}>
+
+
+
+
+ changeLanguage('en')}>
+
+
+
+
+
+
+
+ );
+}
+
+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 --- */}
{/*
*/}
-
+
@@ -30,6 +42,9 @@ export const Navbar = () => {
Proyectos
+
+
+
Contáctanos
@@ -39,8 +54,10 @@ export const Navbar = () => {
{/* --- NAVBAR MÓVIL (TRIGGER Y OFFCANVAS) --- */}
-
-
+
+
+
+
{
>
+
@@ -50,7 +55,7 @@ export const Process = () => {
onClick={() => handleMainSection('proceso')}
style={{ color: activeSection === 'proceso' ? activeColor : inactiveColor }}
>
- Proceso
+ {t('process.sectionToggleProcess')}
@@ -69,7 +74,7 @@ export const Process = () => {
className={`ct-btn-outline-accent px-4 py-2 ${activeContent === capacity.id ? 'active-inner-button' : ''}`}
onClick={() => setActiveContent(capacity.id)}
>
- {capacity.label}
+ {t(capacity.label)}
))}
@@ -81,7 +86,7 @@ export const Process = () => {
className={`ct-btn-outline-accent px-4 py-2 ${activeContent === capacity.id ? 'active-inner-button' : ''}`}
onClick={() => setActiveContent(capacity.id)}
>
- {capacity.label}
+ {t(capacity.label)}
))}
@@ -104,7 +109,7 @@ export const Process = () => {
className={`ct-btn-outline-accent px-4 py-2 ${activeContent === process.id ? 'active-inner-button' : ''}`}
onClick={() => setActiveContent(process.id)}
>
- {process.label}
+ {t(process.label)}
))}
diff --git a/src/front/components/Projects.jsx b/src/front/components/Projects.jsx
index 227a471a37..e2b8834605 100644
--- a/src/front/components/Projects.jsx
+++ b/src/front/components/Projects.jsx
@@ -1,41 +1,121 @@
+import { useState } from "react";
import { Link } from "react-router-dom";
-import vocaltechProject from "../assets/img/Portfolio/vocaltech2.png"
-import vocaltechProject2 from "../assets/img/Portfolio/vocaltech1.png"
-import vocaltechProject3 from "../assets/img/Portfolio/vocaltech4.png"
-import vocaltechProject4 from "../assets/img/Portfolio/vocaltech3.png"
-import muraProject1 from "../assets/img/Portfolio/mura1.png"
-import bestfriendProject1 from "../assets/img/Portfolio/bestfriend1.png"
-import comparadiseProject1 from "../assets/img/Portfolio/comparadise2.png"
-
-export const Projects = () => {
+import { projectsContent } from "../utils/projectsContent";
+import { useTranslation } from "react-i18next";
+
+export const Projects = ({ limit = 0 }) => {
+ const { t } = useTranslation();
+ const [showModal, setShowModal] = useState(false);
+ const [selectedImage, setSelectedImage] = useState("");
+
+ const handleImageClick = (imageSrc) => {
+ setShowModal(true);
+ setSelectedImage(imageSrc);
+ }
+
+ const handleCloseModal = () => {
+ setShowModal(false);
+ setSelectedImage("");
+ }
+
+ let imagesToDisplay = [];
+
+ if (limit > 0) {
+ const projectsToDisplay = projectsContent.slice(0, limit);
+ imagesToDisplay = projectsToDisplay.map(project => project.images[0])
+ } else {
+ imagesToDisplay = projectsContent.flatMap(project => project.images)
+ }
+
+
+ const column1Projects = imagesToDisplay.slice(0, Math.ceil(imagesToDisplay.length / 2));
+ const column2Projects = imagesToDisplay.slice(Math.ceil(imagesToDisplay.length / 2), imagesToDisplay.length);
+
return (
-
Nuestros proyectos
-
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.
+
{t('projects.sectionTitle')}
+
{t('projects.sectionDescription')}
-
-
+ {column1Projects.map((image, index) => (
+
handleImageClick(image.src)}
+ style={{ cursor: "pointer" }}
+ />
+ ))}
-
-
-
+ {column2Projects.map((image, index) => (
+
handleImageClick(image.src)}
+ style={{ cursor: "pointer" }}
+ />
+ ))}
-
-
-
Explora nuestro portafolio
+ {limit > 0 ? (
+
+
+
+ {t('projects.sectionPortfolioButton')}
+
+
-
+ ) : (
+
+
+
+ {t('projects.sectionCTAButton')}
+
+
+
+ )}
+
+ {showModal && (
+ <>
+
+
+
e.stopPropagation()}>
+
+
+
+
+
+
+
+
+
+
+ >
+ )}
)
}
\ No newline at end of file
diff --git a/src/front/components/ProtectedRoute.jsx b/src/front/components/ProtectedRoute.jsx
new file mode 100644
index 0000000000..abcd4327df
--- /dev/null
+++ b/src/front/components/ProtectedRoute.jsx
@@ -0,0 +1,8 @@
+import React, { Children } from 'react';
+import { Navigate } from 'react-router-dom';
+
+export const ProtectedRoute = ({ children }) => {
+ const token = localStorage.getItem('accessToken');
+
+ return token ? children :
+};
\ No newline at end of file
diff --git a/src/front/components/Services/Services.jsx b/src/front/components/Services/Services.jsx
index 5e47b08a95..46ee31fd45 100644
--- a/src/front/components/Services/Services.jsx
+++ b/src/front/components/Services/Services.jsx
@@ -1,48 +1,64 @@
import { Card } from "./Card"
import { servicesContent } from "../../utils/servicesContent"
import serviceBg from "../../assets/img/servicesBackground.png"
+import { useTranslation } from "react-i18next"
export const Services = () => {
+ const { t } = useTranslation();
+
+ const groupedServices = servicesContent.reduce((acc, service) => {
+ if (!acc[service.id]) {
+ acc[service.id] = [];
+ }
+ acc[service.id].push(service);
+ return acc;
+ }, {})
+
+ const sections = [
+ {
+ id: 'web',
+ title: t('services.webDevelopmentTitle'),
+ subtitle: t('services.webDevelopmentSubtitle')
+ },
+ {
+ id: 'communication',
+ title: t('services.communicationTitle'),
+ subtitle: t('services.communicationSubtitle')
+ },
+ {
+ id: 'software',
+ title: t('services.softwareSolutionsTitle'),
+ subtitle: t('services.softwareSolutionsSubtitle')
+ }
+ ]
+
return (
-
+
-
+
-
Conoce nuestros servicios
-
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.
+
{t('services.sectionTitle')}
+
{t('services.sectionDescription')}
-
-
Desarrollo Web
-
Construyendo tu universo digital
- {servicesContent.filter(service => service.id < 5).map(service => (
-
-
-
- ))
- }
-
-
-
Comunicación
-
Reunimos tus audiencias
- {servicesContent.filter(service => service.id > 4).map(service => (
-
-
-
- ))
- }
-
+ {sections.map(section => (
+
+
{section.title}
+
{section.subtitle}
+ {groupedServices[section.id] && groupedServices[section.id].map(service => (
+
+
+
+ ))
+ }
+
+ ))}
)
diff --git a/src/front/components/Team/AboutUs.jsx b/src/front/components/Team/AboutUs.jsx
index efccb1d128..cd53cafad6 100644
--- a/src/front/components/Team/AboutUs.jsx
+++ b/src/front/components/Team/AboutUs.jsx
@@ -1,11 +1,15 @@
+import { useTranslation } from "react-i18next"
+
export const AboutUs = () => {
+ const { t } = useTranslation();
+
return (
-
¿Quiénes somos?
-
“Lorem ipsum dolor sit amet consectetur. Sed adipiscing cursus sed ipsum pellentesque pulvinar. Diam lectus non posuere urna porta dolor. Aliquet sed in eleifend arcu sit sed quis elementum. Turpis facilisi convallis”.
+
{t('about.sectionTitle')}
+
{t('about.sectionDescription')}
diff --git a/src/front/components/Team/Card.jsx b/src/front/components/Team/Card.jsx
index bdb8d8a710..854a02012e 100644
--- a/src/front/components/Team/Card.jsx
+++ b/src/front/components/Team/Card.jsx
@@ -18,7 +18,7 @@ export const Card = ({ name, position, description, image, catImage, mailLink, l
{name}
{position}
-
{description}
+
{description}
diff --git a/src/front/components/Team/Team.jsx b/src/front/components/Team/Team.jsx
index bc1faf1c77..8f5be00013 100644
--- a/src/front/components/Team/Team.jsx
+++ b/src/front/components/Team/Team.jsx
@@ -1,23 +1,26 @@
import { teamContent } from "../../utils/teamContent"
import { Card } from "./Card"
import teamBg from "../../assets/img/teamBackground.png"
+import { useTranslation } from "react-i18next"
export const Team = () => {
+ const { t } = useTranslation();
+
return (
-
+
< div className="container py-4">
-
Conoce a nuestro equipo
-
Lorem ipsum dolor sit amet consectetur adipiscing elit semper dalar elementum tempus hac tellus libero accumsan.
+
{t('team.sectionTitle')}
+
{t('team.sectionDescription')}
{teamContent.map(teamMember => (
{
return (
-
-
{review}
+
diff --git a/src/front/components/Testimonial/Testimonials.jsx b/src/front/components/Testimonial/Testimonials.jsx
index 2ee859339d..578afd42e9 100644
--- a/src/front/components/Testimonial/Testimonials.jsx
+++ b/src/front/components/Testimonial/Testimonials.jsx
@@ -1,12 +1,15 @@
import { Card } from "./Card";
import { testimonialsContent } from "../../utils/testimonialsContent";
+import { useTranslation } from "react-i18next";
export const Testimonials = () => {
+ const { t } = useTranslation();
+
return (
-
Nuestros clientes
+ {t('testimonials.sectionTitle')}
@@ -17,8 +20,8 @@ export const Testimonials = () => {
className={`carousel-item ${index === 0 ? 'active' : ''}`}>
diff --git a/src/front/components/Values/Card.jsx b/src/front/components/Values/Card.jsx
index 6179be36a5..ff91158c90 100644
--- a/src/front/components/Values/Card.jsx
+++ b/src/front/components/Values/Card.jsx
@@ -1,15 +1,13 @@
-import { Link } from "react-router-dom";
-
-export const Card = ({ title, description, showDivider}) => {
+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/Values/Values.jsx b/src/front/components/Values/Values.jsx
index 6f50c2dbcb..61d2d03edd 100644
--- a/src/front/components/Values/Values.jsx
+++ b/src/front/components/Values/Values.jsx
@@ -1,27 +1,30 @@
import { Card } from "./Card";
import { valuesContent } from "../../utils/valuesContent";
+import { useTranslation } from "react-i18next";
export const Values = () => {
+ const { t } = useTranslation();
+
return (
-
-
-
Valores
-
Lorem ipsum dolor sit amet consectetur adipisicing elit. Est hic illo laudantium deleniti aut, quaerat nulla?
-
-
-
- {valuesContent.map(value => (
-
-
-
- ))}
+
+
+
{t('values.sectionTitle')}
+
{t('values.sectionDescription')}
+
+
+
+ {valuesContent.map(value => (
+
+
-
-
+ ))}
+
+
+
)
}
\ No newline at end of file
diff --git a/src/front/components/WhatsappButton.jsx b/src/front/components/WhatsappButton.jsx
new file mode 100644
index 0000000000..2d52b8bf6f
--- /dev/null
+++ b/src/front/components/WhatsappButton.jsx
@@ -0,0 +1,22 @@
+import React from "react";
+import LogoWhats from "../assets/img/LogoWhats.svg"
+import { FloatingWhatsApp } from "react-floating-whatsapp";
+import { useTranslation } from "react-i18next";
+
+export default function WhatsappButton() {
+ const { t } = useTranslation();
+
+ return (
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/components/WorkWithUs.jsx b/src/front/components/WorkWithUs.jsx
new file mode 100644
index 0000000000..2b40909ec4
--- /dev/null
+++ b/src/front/components/WorkWithUs.jsx
@@ -0,0 +1,62 @@
+import { Link } from "react-router-dom";
+import { useTranslation } from "react-i18next";
+
+export const WorkWithUS = () => {
+ const { t } = useTranslation();
+
+ return (
+
+
+
+
+
{t('workWithUs.sectionTitle')}
+
+ {t('workWithUs.sectionDescription')}
+
+
+
+ {t('workWithUs.workWithUsCTA')}
+
+
+
+
+
+ 99
+ %
+
+
{t('workWithUs.statistics.customerSatisfaction')}
+
+
+
+
+ 18
+ K
+
+
{t('workWithUs.statistics.activeUsers')}
+
+
+
+
+ +
+ 10
+
+
{t('workWithUs.statistics.completedProjects')}
+
+
+
+
+ +
+ 5
+
+
{t('workWithUs.statistics.yearsOfExperience')}
+
+
+
+
+ {t('workWithUs.workWithUsCTA')}
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/i18n.js b/src/front/i18n.js
new file mode 100644
index 0000000000..8ca157fdde
--- /dev/null
+++ b/src/front/i18n.js
@@ -0,0 +1,21 @@
+import i18n from "i18next";
+import { initReactI18next } from "react-i18next";
+
+import Backend from "i18next-http-backend";
+import LanguageDetector from "i18next-browser-languagedetector";
+
+i18n
+ .use(Backend)
+ .use(LanguageDetector)
+ .use(initReactI18next)
+ .init({
+ lng: "es",
+ fallbackLng: "en",
+ debug: true,
+
+ interpolation: {
+ escapeValue: false, // not needed for react as it escapes by default
+ },
+ });
+
+export default i18n;
diff --git a/src/front/index.css b/src/front/index.css
index 5459475abd..8b8dd4f5b4 100644
--- a/src/front/index.css
+++ b/src/front/index.css
@@ -6,12 +6,11 @@ body {
h1 {
font-family: "Exo 2", sans-serif;
font-weight: bold;
- color: #e2e505;
+ color: #fbff06;
}
p {
font-family: "Manrope", sans-serif;
-
}
h2,
h3,
@@ -38,14 +37,19 @@ h6 {
font-weight: bolder;
}
+.custom-modal-close-btn {
+ color: #fbff06;
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fbff06'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e") !important;
+}
+
/* Carousel next and prev buttons color style */
.carousel-control-prev-icon {
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fbff06'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e");
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fbff06'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e") !important;
}
.carousel-control-next-icon {
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fbff06'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fbff06'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e") !important;
}
/* Titles & text styles */
@@ -77,7 +81,7 @@ h6 {
.ct-paragraph {
font-size: 16pt;
- line-height: 180%;
+ line-height: 170%;
}
/* Cat transition effect */
@@ -147,11 +151,40 @@ h6 {
.ct-description-p {
width: 100%;
+ font-size: 16pt;
+ line-height: 170%;
+}
+
+@media (min-width: 800px) {
+ .ct-description-p {
+ width: 80%;
+ margin: auto;
+ }
}
@media (min-width: 1200px) {
.ct-description-p {
- width: 40%;
+ width: 50%;
+ margin: auto;
+ }
+}
+
+.ct-reviews {
+ width: 100%;
+ font-size: 16pt;
+ line-height: 170%;
+}
+
+@media (min-width: 800px) {
+ .ct-reviews {
+ width: 80%;
+ margin: auto;
+ }
+}
+
+@media (min-width: 1200px) {
+ .ct-reviews {
+ width: 70%;
margin: auto;
}
}
@@ -184,6 +217,32 @@ h6 {
transform: translateX(0);
}
+/* Footer links*/
+
+.footerLink {
+ color: white;
+ transition: color 0.3s ease;
+ text-decoration: none;
+}
+
+.footerLink:hover {
+ color: #fbff06;
+}
+
+/* Language switch */
+
+.flag-icon-main {
+ width: 24px;
+ height: auto;
+ vertical-align: middle;
+}
+
+.flag-icon-dropdown {
+ width: 20px;
+ height: auto;
+ margin-right: 8px;
+}
+
/* Navbar */
.btn-outline-custom-yellow {
color: #fbff06 !important;
@@ -237,12 +296,16 @@ h6 {
background-color: rgba(10, 25, 30, 0.5);
}
-.btn-outline-yellow {
+.hero-title-home {
+ color: #fbff06 !important;
+}
+
+.btn-outline {
color: #fbff06 !important;
border-color: #fbff06 !important;
}
-.btn-outline-yellow:hover {
+.btn-outline:hover {
background-color: #fbff06 !important;
color: #217b8f !important;
border-color: #fbff06 !important;
@@ -259,7 +322,7 @@ h6 {
}
.hero-subtitle-home {
- font-size: 1rem;
+ font-size: 3rem;
}
}
/* Benefits cards, components & background */
@@ -268,16 +331,65 @@ h6 {
font-weight: bold;
}
.text-ct-secondary {
- color: #60E0A5;
+ color: #60e0a5;
}
-.ct-description-p{
+.ct-description-p {
color: #ffff;
}
.card-divider {
- border: none;
- border-top: 1px solid #6ee6b9;
- opacity: 1;
+ border: none;
+ border-top: 1px solid #6ee6b9;
+ opacity: 1;
}
.card-background {
color: transparent;
}
+/* header contacto */
+.header {
+ margin-bottom: 4rem !important;
+}
+.form-label-contact {
+ color: #60e0a5 !important;
+}
+
+.btn-submit-contact {
+ background-color: #217b8f !important;
+ color: #e2e505 !important;
+ font-weight: bold;
+ border: none;
+}
+
+.btn-submit-contact:hover {
+ background-color: #2799b3 !important;
+}
+/* Work With Us */
+.text-stat-number {
+ color: #60e0a5;
+}
+.text-stat-symbol {
+ color: #fbff06;
+}
+
+/* Admin Styles */
+
+.adminOption {
+ color: white;
+ transition: color 0.3s ease;
+ text-decoration: none;
+}
+
+.adminOption:hover {
+ color: #fbff06;
+}
+
+/* Login styles */
+
+@media (max-width: 780px) {
+ .login-logo {
+ min-width: 280px;
+ }
+}
+
+.login-logo {
+ min-width: 200px;
+}
diff --git a/src/front/main.jsx b/src/front/main.jsx
index ca1026c5d2..15c01ecd4d 100644
--- a/src/front/main.jsx
+++ b/src/front/main.jsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, { Suspense } from 'react'
import ReactDOM from 'react-dom/client'
import './index.css' // Global styles for your application
import { RouterProvider } from "react-router-dom"; // Import RouterProvider to use the router
@@ -6,22 +6,39 @@ import { router } from "./routes"; // Import the router configuration
import { StoreProvider } from './hooks/useGlobalReducer'; // Import the StoreProvider for global state management
import { BackendURL } from './components/BackendURL';
import 'bootstrap/dist/css/bootstrap.min.css';
+import './i18n';
const Main = () => {
-
- if(! import.meta.env.VITE_BACKEND_URL || import.meta.env.VITE_BACKEND_URL == "") return (
+
+ if (! import.meta.env.VITE_BACKEND_URL || import.meta.env.VITE_BACKEND_URL == "") return (
-
+
- );
+ );
return (
-
- {/* Provide global state to all components */}
-
- {/* Set up routing for the application */}
-
-
-
+
+
+
+ }>
+ {/* Provide global state to all components */}
+
+ {/* Set up routing for the application */}
+
+
+
+
);
}
diff --git a/src/front/pages/Admin.jsx b/src/front/pages/Admin.jsx
new file mode 100644
index 0000000000..959c7b0b0d
--- /dev/null
+++ b/src/front/pages/Admin.jsx
@@ -0,0 +1,77 @@
+import { useState, useEffect, useContext } from "react"
+import { Link, useNavigate } from "react-router-dom"
+import { Leads } from "../components/Admin/Leads"
+import { AppContext } from "./Layout"
+
+export const Admin = () => {
+ const navigate = useNavigate();
+ const { setShowNavbar, setShowFooter } = useContext(AppContext)
+ const [activeContent, setActiveContent] = useState("");
+
+ useEffect(() => {
+ if (setShowNavbar || setShowFooter) {
+ setShowNavbar(false);
+ setShowFooter(false);
+ }
+ return () => {
+ if (setShowNavbar || setShowFooter) {
+ setShowNavbar(true);
+ setShowFooter(true);
+ }
+ }
+ }, [setShowNavbar, setShowFooter])
+
+ const handleActiveContent = (contentName) => {
+ setActiveContent(contentName);
+ }
+
+ const handleLogout = () => {
+ localStorage.removeItem('accessToken')
+ navigate('/')
+ }
+
+ const renderContent = () => {
+ switch (activeContent) {
+ case 'leads':
+ return
+ default:
+ return Selecciona una opción del panel.
+ }
+ }
+
+ return (
+
+
+
+
Panel de Administrador
+
¡Bienvenido!
+
+
+
+ {
+ e.preventDefault();
+ handleActiveContent('leads');
+ }}
+ >
+ Ver Leads
+
+
+
+
+
+ Cerrar sesión
+
+
+
+
+ {renderContent()}
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/front/pages/Contact.jsx b/src/front/pages/Contact.jsx
index 9565a01ca2..6ac3b58d44 100644
--- a/src/front/pages/Contact.jsx
+++ b/src/front/pages/Contact.jsx
@@ -1,13 +1,9 @@
+import HeaderContact from "../components/HeaderContact"
+
export const Contact = () => {
return (
-
+ <>
+
+ >
)
}
\ No newline at end of file
diff --git a/src/front/pages/Home.jsx b/src/front/pages/Home.jsx
index bcfd03464d..65d43249f2 100644
--- a/src/front/pages/Home.jsx
+++ b/src/front/pages/Home.jsx
@@ -6,47 +6,22 @@ import { Projects } from "../components/Projects.jsx";
import { Testimonials } from "../components/Testimonial/Testimonials.jsx";
import { HeaderHome } from "../components/HeaderHome.jsx";
import { Benefits } from "../components/Benefits/Benefits.jsx";
+import { WorkWithUS } from "../components/WorkWithUs.jsx";
export const Home = () => {
const { store, dispatch } = useGlobalReducer()
- const loadMessage = async () => {
- try {
- const backendUrl = import.meta.env.VITE_BACKEND_URL
-
- if (!backendUrl) throw new Error("VITE_BACKEND_URL is not defined in .env file")
-
- const response = await fetch(backendUrl + "/api/hello")
- const data = await response.json()
-
- if (response.ok) dispatch({ type: "set_hello", payload: data.message })
-
- return data
-
- } catch (error) {
- if (error.message) throw new Error(
- `Could not fetch the message from the backend.
- Please check if the backend is running and the backend port is public.`
- );
- }
-
- }
-
- useEffect(() => {
- loadMessage()
- }, [])
-
return (
<>
-
-
+
+
>
);
diff --git a/src/front/pages/Layout.jsx b/src/front/pages/Layout.jsx
index 702684ec05..91beca4358 100644
--- a/src/front/pages/Layout.jsx
+++ b/src/front/pages/Layout.jsx
@@ -1,15 +1,32 @@
-import { Outlet } from "react-router-dom/dist"
+import React, { useReducer, createContext, useState } from "react"
+import { Outlet, useLocation } from "react-router-dom/dist";
+import storeReducer, { initialStore } from "../store.js";
import ScrollToTop from "../components/ScrollToTop"
import { Navbar } from "../components/Navbar.jsx"
import { Footer } from "../components/Footer"
+import WhatsappButton from "../components/WhatsappButton.jsx"
+
// Base component that maintains the navbar and footer throughout the page and the scroll to top functionality.
+export const AppContext = createContext(null);
+
export const Layout = () => {
+
+ const [store, dispatch] = useReducer(storeReducer, initialStore());
+ const [showNavbar, setShowNavbar] = useState(true)
+ const [showFooter, setShowFooter] = useState(true)
+
+ const location = useLocation();
+
return (
-
-
+
+ <>
+
+ {showNavbar && }
-
-
+
+ {showFooter && }
+ >
+
)
}
\ No newline at end of file
diff --git a/src/front/pages/Login.jsx b/src/front/pages/Login.jsx
new file mode 100644
index 0000000000..eb919ca2f2
--- /dev/null
+++ b/src/front/pages/Login.jsx
@@ -0,0 +1,134 @@
+import { useState, useEffect, useReducer, useContext } from "react"
+import storeReducer, { initialStore } from "../store";
+import { useNavigate } from "react-router-dom"
+import { AppContext } from "./Layout"
+import yellowLogo from '../assets/img/LogoNavbar.svg'
+
+const adminLogin = async (dispatch, loginData) => {
+ const apiUrl = import.meta.env.VITE_BACKEND_URL;
+ dispatch({ type: 'ADMIN_LOGIN_START' })
+ try {
+ const response = await fetch(`${apiUrl}/api/admin/login`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(loginData)
+ })
+
+ const data = await response.json();
+
+ if (!response.ok) {
+ throw new Error(data.message || "Error al loguearse");
+ }
+ dispatch({ type: 'ADMIN_LOGIN_SUCCESS', payload: data });
+ return data;
+ } catch (error) {
+ console.error("Error al loguearse:", error);
+ dispatch({ type: 'ADMIN_LOGIN_FAILURE', payload: error.message });
+ throw error;
+ }
+}
+
+export const Login = () => {
+ const navigate = useNavigate();
+ const { setShowNavbar, setShowFooter } = useContext(AppContext)
+
+ const [store, dispatch] = useReducer(storeReducer, initialStore());
+ const { loginStatus } = store;
+
+ useEffect(() => {
+ if (setShowNavbar || setShowFooter) {
+ setShowNavbar(false);
+ setShowFooter(false);
+ }
+ return () => {
+ if (setShowNavbar || setShowFooter) {
+ setShowNavbar(true);
+ setShowFooter(true);
+ }
+ }
+ }, [setShowNavbar, setShowFooter])
+
+ const [loginData, setLoginData] = useState({
+ email: "",
+ password: ""
+ })
+
+ const handleChange = (e) => {
+ const { name, value } = e.target;
+ setLoginData(prevLoginData => ({ ...prevLoginData, [name]: value }));
+ }
+
+ const handleLogin = async (e) => {
+ e.preventDefault();
+ try {
+ const response = await adminLogin(dispatch, loginData);
+ console.log("Successful login", response)
+ alert("Successful login!")
+ if (response.token) {
+ localStorage.setItem('accessToken', response.token);
+ }
+ setLoginData({
+ email: "",
+ password: ""
+ })
+ navigate("/admin")
+ } catch (error) {
+ console.error("Error de logueo", error)
+ alert("Credenciales incorrectas")
+ }
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
Welcome back!
+
+
+
Email address
+
+
We'll never share your email with anyone else.
+
+
+ Password
+
+
+
+
+ {loginStatus?.status === 'loading' ? 'Logueando...' : 'Submit'}
+
+
+ {loginStatus?.status === 'error' && (
+
+ {loginStatus.error || "Error inesperado."}
+
+ )}
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/front/routes.jsx b/src/front/routes.jsx
index 6d0b424430..8b7b25f62b 100644
--- a/src/front/routes.jsx
+++ b/src/front/routes.jsx
@@ -11,6 +11,9 @@ import { About } from "./pages/About";
import { ServicesPage } from "./pages/Services";
import { Portfolio } from "./pages/Projects";
import { Contact } from "./pages/Contact";
+import { Login } from "./pages/Login";
+import { Admin } from "./pages/Admin";
+import { ProtectedRoute } from "./components/ProtectedRoute";
export const router = createBrowserRouter(
createRoutesFromElements(
@@ -29,6 +32,9 @@ export const router = createBrowserRouter(
} />
} />
} />
+ } />
+ {/* } /> */}
+ } />
)
);
\ No newline at end of file
diff --git a/src/front/store.js b/src/front/store.js
index 3062cd222d..6005e5a75f 100644
--- a/src/front/store.js
+++ b/src/front/store.js
@@ -1,38 +1,115 @@
-export const initialStore=()=>{
- return{
+export const initialStore = () => {
+ return {
message: null,
- todos: [
- {
- id: 1,
- title: "Make the bed",
- background: null,
- },
- {
- id: 2,
- title: "Do my homework",
- background: null,
- }
- ]
- }
-}
+ contactForm: initialContactFormState,
+ leads: [],
+ leadsFetchStatus: {
+ status: "idle",
+ error: null,
+ },
+ };
+};
+
+const initialContactFormState = {
+ status: "idle",
+ error: null,
+};
export default function storeReducer(store, action = {}) {
- switch(action.type){
- case 'set_hello':
+ switch (action.type) {
+ case "ADMIN_LOGIN_START":
+ return {
+ ...store,
+ loginStatus: {
+ status: "loading",
+ error: null,
+ },
+ };
+
+ case "ADMIN_LOGIN_SUCCESS":
+ return {
+ ...store,
+ loginStatus: {
+ status: "success",
+ error: null,
+ },
+ };
+
+ case "ADMIN_LOGIN_FAILURE":
+ return {
+ ...store,
+ loginStatus: {
+ status: "error",
+ error: action.payload,
+ },
+ };
+
+ case "GET_ALL_LEADS_START":
+ return {
+ ...store,
+ leadsFetchStatus: {
+ status: "loading",
+ error: null,
+ },
+ };
+
+ case "GET_ALL_LEADS_SUCCESS":
+ return {
+ ...store,
+ leads: action.payload,
+ leadsFetchStatus: {
+ status: "success",
+ error: null,
+ },
+ };
+
+ case "GET_ALL_LEADS_FAILURE":
+ return {
+ ...store,
+ leadsFetchStatus: {
+ status: "error",
+ error: action.payload,
+ },
+ };
+
+ case "CONTACT_SUBMIT_START":
return {
...store,
- message: action.payload
+ contactForm: {
+ ...store.contactForm,
+ status: "loading",
+ error: null,
+ },
};
-
- case 'add_task':
- const { id, color } = action.payload
+ case "CONTACT_SUBMIT_SUCCESS":
+ return {
+ ...store,
+ contactForm: {
+ ...store.contactForm,
+ status: "success",
+ },
+ };
+ case "CONTACT_SUBMIT_FAILURE":
return {
...store,
- todos: store.todos.map((todo) => (todo.id === id ? { ...todo, background: color } : todo))
+ contactForm: {
+ ...store.contactForm,
+ status: "error",
+ error: action.payload,
+ },
};
+
+ case "CONTACT_RESET_STATUS":
+ // Resetea el estado del formulario a su valor inicial
+ return {
+ ...store,
+ contactForm: initialContactFormState,
+ };
+
default:
- throw Error('Unknown action.');
- }
+ throw Error("Unknown action.");
+ return store;
+ }
}
diff --git a/src/front/utils/benefitsContent.jsx b/src/front/utils/benefitsContent.jsx
index 10bdc27efa..c22f157586 100644
--- a/src/front/utils/benefitsContent.jsx
+++ b/src/front/utils/benefitsContent.jsx
@@ -1,42 +1,32 @@
export const benefitsContent = [
{
id: 1,
- 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."
+ title: "benefits.card.benefit1.title",
+ description: "benefits.card.benefit1.description"
},
{
id: 2,
- title: "Experiencia de Usuario Superior (UX)",
- description: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Libero soluta omnis fugiat nulla nobis quod cupiditate numquam placeat assumenda animi maiores veniam vero quis, beatae recusandae ducimus praesentium corrupti suscipit."
+ title: "benefits.card.benefit2.title",
+ description: "benefits.card.benefit2.description"
},
{
id: 3,
- title: "Mobile App",
- description: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Libero soluta omnis fugiat nulla nobis quod cupiditate numquam placeat assumenda animi maiores veniam vero quis, beatae recusandae ducimus praesentium corrupti suscipit."
+ title: "benefits.card.benefit3.title",
+ description: "benefits.card.benefit3.description"
},
{
id: 4,
- title: "Mobile App",
- description: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Libero soluta omnis fugiat nulla nobis quod cupiditate numquam placeat assumenda animi maiores veniam vero quis, beatae recusandae ducimus praesentium corrupti suscipit."
+ title: "benefits.card.benefit4.title",
+ description: "benefits.card.benefit4.description"
},
{
id: 5,
- title: "Mobile App",
- description: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Libero soluta omnis fugiat nulla nobis quod cupiditate numquam placeat assumenda animi maiores veniam vero quis, beatae recusandae ducimus praesentium corrupti suscipit."
+ title: "benefits.card.benefit5.title",
+ description: "benefits.card.benefit5.description"
},
{
id: 6,
- title: "Mobile App",
- description: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Libero soluta omnis fugiat nulla nobis quod cupiditate numquam placeat assumenda animi maiores veniam vero quis, beatae recusandae ducimus praesentium corrupti suscipit."
- },
- {
- id: 7,
- title: "Mobile App",
- description: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Libero soluta omnis fugiat nulla nobis quod cupiditate numquam placeat assumenda animi maiores veniam vero quis, beatae recusandae ducimus praesentium corrupti suscipit."
- },
- {
- id: 8,
- title: "Mobile App",
- description: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Libero soluta omnis fugiat nulla nobis quod cupiditate numquam placeat assumenda animi maiores veniam vero quis, beatae recusandae ducimus praesentium corrupti suscipit."
- }
+ title: "benefits.card.benefit6.title",
+ description: "benefits.card.benefit6.description"
+ }
];
diff --git a/src/front/utils/processContent.jsx b/src/front/utils/processContent.jsx
index 472f9b2968..b0a8e6a44a 100644
--- a/src/front/utils/processContent.jsx
+++ b/src/front/utils/processContent.jsx
@@ -1,85 +1,85 @@
export const capacitiesData = [
{
id: 'capacidad1',
- label: 'Desarrollo de Concepto Web',
- text: 'Este es un proceso creativo y estratégico que establece los fundamentos visuales y estructurales de un sitio web. Comenzamos identificando los valores centrales de tu marca y sus objetivos, traduciéndolos en diseños de prueba que coincidan con las preferencias de la audiencia. Exploramos múltiples borradores que detallan el layout, las paletas de color, la tipografía y los flujos de usuario, asegurando la alineación con la identidad y objetivos de la marca. Este proceso genera un marco claro y accionable para fases posteriores, resultando en un sitio web funcional y estéticamente impactante que alinea el negocio con los objetivos del usuario.'
+ label: 'process.capacities.webConcept.label',
+ text: 'process.capacities.webConcept.text'
},
{
id: 'capacidad2',
- label: 'Auditoría Web',
- text: 'Realizamos una evaluación exhaustiva de tu sitio web para identificar oportunidades de mejora en usabilidad, rendimiento, accesibilidad y diseño. Proporcionamos insights accionables sobre el desempeño web, identificando barreras para la interacción del usuario y el crecimiento del negocio. Utilizamos herramientas avanzadas y analizamos métricas como tiempos de carga, rutas de navegación y patrones de interacción. La auditoría también identifica mejoras rápidas y a largo plazo, como la optimización de imágenes o la reestructuración de la navegación, entregando un informe detallado con recomendaciones priorizadas para mejoras impactantes.'
+ label: 'process.capacities.webAudit.label',
+ text: 'process.capacities.webAudit.text'
},
{
id: 'capacidad3',
- label: 'Creación de Sitios Web Completos',
- text: 'Ofrecemos un servicio integral de principio a fin para la creación de sitios web, entregando una presencia digital cohesiva y personalizada según los objetivos y la audiencia de cada cliente. Comenzamos con la alineación estratégica para reflejar la visión de la marca y sus metas de negocio. La arquitectura web de información escalable organiza el contenido para una navegación y un engagement fluidos y de comunicación efectiva. La fase de diseño desarrolla una identidad visual completa, con UI kits a medida, diseños responsivos y elementos interactivos. El resultado es un sitio web optimizado, responsive, de alto rendimiento y atractivo visualmente que impulsa la interacción y apoya el crecimiento a largo plazo.'
+ label: 'process.capacities.webIntDev.label',
+ text: 'process.capacities.webIntDev.text'
},
{
id: 'capacidad4',
- label: 'Sistemas de Diseño Web',
- text: 'Desarrollamos un marco reutilizable para simplificar actualizaciones y asegurar consistencia visual en tu ecosistema digital. Basado en principios de diseño estándares de la industria, organizamos elementos de UI, como botones, tipografía y paletas de colores, en módulos escalables, permitiendo la creación eficiente de páginas y reduciendo recursos para futuras expansiones. El proceso incluye la identificación de componentes de diseño clave, su ensamblaje en patrones y plantillas, y pruebas exhaustivas. Acompañado de documentación detallada y capacitación, este sistema optimiza flujos de trabajo, mejora la colaboración y apoya la escalabilidad de la solución web.'
+ label: 'process.capacities.webDesignSystems.label',
+ text: 'process.capacities.webDesignSystems.text'
},
{
id: 'capacidad5',
- label: 'Mantenimiento Web',
- text: 'Aseguramos que tu sitio web se mantenga seguro, funcional y alineado con tus objetivos de negocio. Este servicio incluye actualizaciones rutinarias, monitoreo de rendimiento, corrección de errores y pruebas proactivas de control de calidad para abordar posibles problemas. Incluimos la optimización del rendimiento, operaciones de carga rápida, actualizaciones de seguridad y actualizaciones menores de diseño o contenido. Estos esfuerzos garantizan un rendimiento consistente y la satisfacción del usuario, y una presencia digital confiable.'
+ label: 'process.capacities.webUpkeep.label',
+ text: 'process.capacities.webUpkeep.text'
},
{
id: 'capacidad6',
- label: 'Integración con Servicios de Terceros',
- text: 'Mejoramos la funcionalidad de tu sitio web conectándolo con herramientas y servicios externos clave. Esto incluye plataformas de análisis, sistemas de soporte al cliente, pasarelas de pago, agendamiento automático de citas, entre otros. El proceso inicia evaluando las necesidades del cliente y seleccionando las herramientas adecuadas. Nuestro equipo gestiona la integración técnica para asegurar compatibilidad y un funcionamiento fluido, alineando estas herramientas con la identidad de marca y las expectativas del usuario. El soporte post-integración asegura que los sistemas permanezcan eficientes, maximizando el valor para el cliente.'
+ label: 'process.capacities.serviceIntegration.label',
+ text: 'process.capacities.serviceIntegration.text'
},
{
id: 'capacidad7',
- label: 'Estrategia Integral de Comunicación',
- text: 'Desarrollamos hojas de ruta completas para tu comunicación digital. Esto incluye investigación exhaustiva de tu audiencia y competidores, definición de tu 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.'
+ label: 'process.capacities.strategicCommunication.label',
+ text: 'process.capacities.strategicCommunication.text'
},
{
id: 'capacidad8',
- label: 'Optimización de 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.'
+ label: 'process.capacities.seoAndDigitalContent.label',
+ text: 'process.capacities.seoAndDigitalContent.text'
},
{
id: 'capacidad9',
- label: 'Gestión y Dinamizació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.'
+ label: 'process.capacities.communityManagement.label',
+ text: 'process.capacities.communityManagement.text'
},
{
id: 'capacidad10',
- label: 'Analítica de Comunicación 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 de tu contenido. Esta capacidad nos permite optimizar estrategias en tiempo real y demostrar el retorno de inversión de cada esfuerzo de comunicación, ajustando el rumbo para asegurar el cumplimiento de tus objetivos.'
+ label: 'process.capacities.metrics.label',
+ text: 'process.capacities.metrics.text'
}
];
export const processData = [
{
id: 'paso1',
- 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 periódicas para asegurar una comunicación ideal.'
+ label: 'process.steps.firstStep.label',
+ text: 'process.steps.firstStep.text'
},
{
id: 'paso2',
- label: '02. Onboarding y Descubrimiento',
- text: 'Nuestro equipo se sumerge en tu marca y negocio. Exponemos los puntos esenciales que hemos extraído de tu identidad, definimos objetivos claros, criterios de éxito y limitaciones. Si ya tienes una presencia online, realizamos una auditoría inicial para identificar puntos de mejora. Analizamos también a la competencia para identificar oportunidades y soluciones UX/UI que podemos adoptar para tu beneficio.'
+ label: 'process.steps.secondStep.label',
+ text: 'process.steps.secondStep.text'
},
{
id: 'paso3',
- label: '03. Arquitectura de Contenido Web',
- text: 'Estructuramos la arquitectura de tu sitio. Definimos qué información debe ir en cada página y cómo se conectan las páginas y los elementos entre sí, creando un sitemap claro y una jerarquía de contenido intuitiva. Esto permite visualizar el flujo de la información, planificar el viaje del usuario y anticipar la ubicación estratégica de elementos clave, asegurando una navegación fluida y una experiencia de usuario optimizada.'
+ label: 'process.steps.thirdStep.label',
+ text: 'process.steps.thirdStep.text'
},
{
id: 'paso4',
- label: '04. Wireframes de Baja Fidelidad (Low-Fidelity)',
- text: 'Desarrollamos esquemas visuales básicos del sitio web, enfocándonos en la estructura y el diseño de la información sin detalles estéticos. Determinamos la distribución de elementos, la forma de bloques de contenido y la posición de los llamados a la acción (CTAs). En esta etapa, utilizaremos textos de prueba para asegurar que el contenido se alinee con los elementos visuales y resolver posibles conflictos en la arquitectura. Desde una mirada de desarrollo evaluamos la complejidad para minimizar riesgos, al momento de la implementación.'
+ label: 'process.steps.fourthStep.label',
+ text: 'process.steps.fourthStep.text'
},
{
id: 'paso5',
- label: '05. Conceptos Visuales',
- text: 'Considerando tu industria, un análisis competitivo, tu manual de marca, si lo tienes, y tus preferencias, creamos moodboards visuales. Estos muestran el tono emocional de tu producto, ejemplos de colores, formas y estilos que guiarán la fase de diseño final. Posteriormente, aplicamos el diseño del moodboard elegido a los wireframes de baja fidelidad, definiendo colores, estilos de texto, tamaños de encabezados y seleccionando contenido ilustrativo e íconos.'
+ label: 'process.steps.fifthStep.label',
+ text: 'process.steps.fifthStep.text'
},
{
id: 'paso6',
- label: '06. Diseño Final y Desarrollo (Implementación)',
- text: 'Una vez aprobada la dirección de diseño, nuestro equipo finaliza las páginas aprobadas, integrando colores y estilos de texto en la biblioteca de elementos. El diseño se extiende al resto del sitio, creando una librería de elementos y reglas que permiten construir nuevas páginas y módulos rápidamente. Esto incluye componentes de UI y sus estados, color y estilos de fuente, asegurando coherencia y eficiencia. Después, el diseño pasa a desarrollo, donde nuestro equipo lo programa completamente sea en web dev, wordpress o algún CMS, optimizado, responsive y seguro. El trabajo coordinado entre diseño y desarrollo asegura que el resultado final cumpla con todos los requisitos del cliente.'
+ label: 'process.steps.sixthStep.label',
+ text: 'process.steps.sixthStep.text'
}
];
\ No newline at end of file
diff --git a/src/front/utils/projectsContent.jsx b/src/front/utils/projectsContent.jsx
new file mode 100644
index 0000000000..cd34097a1b
--- /dev/null
+++ b/src/front/utils/projectsContent.jsx
@@ -0,0 +1,72 @@
+import bestfriend1 from "../assets/img/Portfolio/bestfriend1.png"
+import bestfriend2 from "../assets/img/Portfolio/bestfriend2.png"
+import bestfriend3 from "../assets/img/Portfolio/bestfriend3.png"
+import comparadise1 from "../assets/img/Portfolio/comparadise1.png"
+import comparadise2 from "../assets/img/Portfolio/comparadise2.png"
+import elipsis1 from "../assets/img/Portfolio/elipsis1.png"
+import elipsis2 from "../assets/img/Portfolio/elipsis2.png"
+import elipsis3 from "../assets/img/Portfolio/elipsis3.png"
+import elipsis4 from "../assets/img/Portfolio/elipsis4.png"
+import mura1 from "../assets/img/Portfolio/mura1.png"
+import mura2 from "../assets/img/Portfolio/mura2.png"
+import mura3 from "../assets/img/Portfolio/mura3.png"
+import mura4 from "../assets/img/Portfolio/mura4.png"
+import vocaltech1 from "../assets/img/Portfolio/vocaltech1.png"
+import vocaltech2 from "../assets/img/Portfolio/vocaltech2.png"
+import vocaltech3 from "../assets/img/Portfolio/vocaltech3.png"
+import vocaltech4 from "../assets/img/Portfolio/vocaltech4.png"
+
+export const projectsContent = [
+ {
+ id: "mura",
+ name: "Mura",
+ description: "Lorem ipsum",
+ images: [
+ { src: mura1, alt: "Imagen de la revista Mura" },
+ { src: mura3, alt: "Imagen de la revista Mura" },
+ { src: mura4, alt: "Imagen de la revista Mura" },
+ { src: mura2, alt: "Imagen de la revista Mura" }
+ ]
+ },
+ {
+ id: "bestfriend",
+ name: "Best Friend",
+ description: "Lorem ipsum",
+ images: [
+ { src: bestfriend1, alt: "Imagen de la revista Mura" },
+ { src: bestfriend2, alt: "Imagen de la revista Mura" },
+ { src: bestfriend3, alt: "Imagen de la revista Mura" }
+ ]
+ },
+ {
+ id: "comparadise",
+ name: "ComParadise",
+ description: "Lorem ipsum",
+ images: [
+ { src: comparadise2, alt: "Imagen de la revista Mura" },
+ { src: comparadise1, alt: "Imagen de la revista Mura" }
+ ]
+ },
+ {
+ id: "vocaltech",
+ name: "VocalTech",
+ description: "Lorem ipsum",
+ images: [
+ { src: vocaltech4, alt: "Imagen de la revista Mura" },
+ { src: vocaltech1, alt: "Imagen de la revista Mura" },
+ { src: vocaltech2, alt: "Imagen de la revista Mura" },
+ { src: vocaltech3, alt: "Imagen de la revista Mura" }
+ ]
+ },
+ {
+ id: "elipsis",
+ name: "Elipsis",
+ description: "Lorem ipsum",
+ images: [
+ { src: elipsis1, alt: "Imagen de la revista Mura" },
+ { src: elipsis3, alt: "Imagen de la revista Mura" },
+ { src: elipsis2, alt: "Imagen de la revista Mura" },
+ { src: elipsis4, alt: "Imagen de la revista Mura" }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/src/front/utils/servicesContent.jsx b/src/front/utils/servicesContent.jsx
index bab6e44ded..312e2f29e3 100644
--- a/src/front/utils/servicesContent.jsx
+++ b/src/front/utils/servicesContent.jsx
@@ -7,62 +7,78 @@ import { faHexagonNodes } from "@fortawesome/free-solid-svg-icons";
import { faPenNib } from "@fortawesome/free-solid-svg-icons";
import { faComments } from "@fortawesome/free-solid-svg-icons";
import { faChessBishop } from "@fortawesome/free-solid-svg-icons";
+import { faPenRuler } from "@fortawesome/free-solid-svg-icons";
+import { faCube } from "@fortawesome/free-solid-svg-icons";
export const servicesContent = [
{
- id: 1,
+ id: 'web',
icon: ,
- 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"
+ title: 'services.card.wordpress.title',
+ description: 'services.card.wordpress.description',
+ link: 'services.card.wordpress.link'
},
{
- id: 2,
+ id: 'web',
icon: ,
- 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"
+ title: 'services.card.webdev.title',
+ description: 'services.card.webdev.description',
+ link: 'services.card.webdev.link'
},
{
- id: 3,
+ id: 'web',
icon: ,
- 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"
+ title: 'services.card.ecommerce.title',
+ description: 'services.card.ecommerce.description',
+ link: 'services.card.ecommerce.link'
},
{
- id: 4,
+ id: 'web',
icon: ,
- 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"
+ title: 'services.card.webflow.title',
+ description: 'services.card.webflow.description',
+ link: 'services.card.webflow.link'
},
{
- id: 5,
+ id: 'communication',
icon: ,
- 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"
+ title: 'services.card.socialMedia.title',
+ description: 'services.card.socialMedia.description',
+ link: 'services.card.socialMedia.link'
},
{
- id: 6,
+ id: 'communication',
icon: ,
- 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"
+ title: 'services.card.copyWriting.title',
+ description: 'services.card.copyWriting.description',
+ link: 'services.card.copyWriting.link'
},
{
- id: 7,
+ id: 'communication',
icon: ,
- 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"
+ title: 'services.card.contentProduction.title',
+ description: 'services.card.contentProduction.description',
+ link: 'services.card.contentProduction.link'
},
{
- id: 8,
+ id: 'communication',
icon: ,
- 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"
+ title: 'services.card.communicationStrategy.title',
+ description: 'services.card.communicationStrategy.description',
+ link: 'services.card.communicationStrategy.link'
+ },
+ {
+ id: 'software',
+ icon: ,
+ title: 'services.card.autocadLt.title',
+ description: 'services.card.autocadLt.description',
+ link: 'services.card.autocadLt.link'
+ },
+ {
+ id: 'software',
+ icon: ,
+ title: 'services.card.autocadIst.title',
+ description: 'services.card.autocadIst.description',
+ link: 'services.card.autocadIst.link'
}
];
\ No newline at end of file
diff --git a/src/front/utils/teamContent.jsx b/src/front/utils/teamContent.jsx
index 6451252b7d..44f3745dc7 100644
--- a/src/front/utils/teamContent.jsx
+++ b/src/front/utils/teamContent.jsx
@@ -10,8 +10,8 @@ export const teamContent = [
{
id: 0,
name: "David Pinto",
- position: "Comunicador y Desarrollador",
- description: "Lorem ipsum dolor sit amet consecte adipiscing elit amet hendrerit pretium nulla sed enim iaculis mi. ",
+ position: 'team.card.teamMember1.position',
+ description: 'team.card.teamMember1.description',
image: devProfile1,
catImage: cat1,
mailLink: 'dpinto@cloudtech.com.ec',
@@ -21,8 +21,8 @@ export const teamContent = [
{
id: 1,
name: "José Pinto",
- position: "Fullstack Developer",
- description: "Lorem ipsum dolor sit amet consecte adipiscing elit amet hendrerit pretium nulla sed enim iaculis mi. ",
+ position: 'team.card.teamMember2.position',
+ description: 'team.card.teamMember2.description',
image: devProfile2,
catImage: cat2,
mailLink: 'jmpinto@cloudtech.com.ec',
@@ -32,8 +32,8 @@ export const teamContent = [
{
id: 2,
name: "Santiago Montoya",
- position: "Fullstack Developer",
- description: "Lorem ipsum dolor sit amet consecte adipiscing elit amet hendrerit pretium nulla sed enim iaculis mi. ",
+ position: 'team.card.teamMember3.position',
+ description: 'team.card.teamMember3.description',
image: devProfile3,
catImage: cat3,
mailLink: 'smontoya@cloudtech.com.ec',
diff --git a/src/front/utils/testimonialsContent.jsx b/src/front/utils/testimonialsContent.jsx
index 90333c5d36..0c3db97f7d 100644
--- a/src/front/utils/testimonialsContent.jsx
+++ b/src/front/utils/testimonialsContent.jsx
@@ -1,34 +1,36 @@
import client1 from "../assets/img/Testimonials/clientprofile1.jpg"
import company1 from "../assets/img/Testimonials/companylogo1.png"
-import client2 from "../assets/img/Testimonials/clientprofile2.png"
+import client2 from "../assets/img/Testimonials/clientProfile2.png"
import company2 from "../assets/img/Testimonials/companylogo2.png"
+import client3 from "../assets/img/Testimonials/clientProfile3.png"
+import company3 from "../assets/img/Testimonials/companylogo3.png"
export const testimonialsContent = [
{
id: 0,
- name: "Abril Altamirano",
- position: "Editora 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."',
- profilePicture: client1,
+ name: "Juan Romero",
+ position: 'testimonials.testimony1.position',
+ review: 'testimonials.testimony1.review',
+ profilePicture: client3,
logo: company1
},
{
id: 1,
name: "Gabriela Suárez",
- 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.”'}`,
+ position: 'testimonials.testimony2.position',
+ review: 'testimonials.testimony2.review',
profilePicture: client2,
logo: company2
},
{
id: 2,
- name: "David Pinto",
- position: "Editora 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.”',
+ name: "Abril Altamirano",
+ position: 'testimonials.testimony3.position',
+ review: 'testimonials.testimony3.review',
profilePicture: client1,
- logo: company1
+ logo: company3
}
]
\ No newline at end of file
diff --git a/src/front/utils/valuesContent.jsx b/src/front/utils/valuesContent.jsx
index cea13f90c3..87d5b1dc05 100644
--- a/src/front/utils/valuesContent.jsx
+++ b/src/front/utils/valuesContent.jsx
@@ -1,42 +1,32 @@
export const valuesContent = [
{
id: 1,
- title: "Mobile App",
- description: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Libero soluta omnis fugiat nulla nobis quod cupiditate numquam placeat assumenda animi maiores veniam vero quis, beatae recusandae ducimus praesentium corrupti suscipit."
+ title: "values.card.value1.title",
+ description: "values.card.value1.description"
},
{
id: 2,
- title: "Mobile App",
- description: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Libero soluta omnis fugiat nulla nobis quod cupiditate numquam placeat assumenda animi maiores veniam vero quis, beatae recusandae ducimus praesentium corrupti suscipit."
+ title: "values.card.value2.title",
+ description: "values.card.value2.description"
},
{
id: 3,
- title: "Mobile App",
- description: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Libero soluta omnis fugiat nulla nobis quod cupiditate numquam placeat assumenda animi maiores veniam vero quis, beatae recusandae ducimus praesentium corrupti suscipit."
+ title: "values.card.value3.title",
+ description: "values.card.value3.description"
},
{
id: 4,
- title: "Mobile App",
- description: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Libero soluta omnis fugiat nulla nobis quod cupiditate numquam placeat assumenda animi maiores veniam vero quis, beatae recusandae ducimus praesentium corrupti suscipit."
+ title: "values.card.value4.title",
+ description: "values.card.value4.description"
},
{
id: 5,
- title: "Mobile App",
- description: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Libero soluta omnis fugiat nulla nobis quod cupiditate numquam placeat assumenda animi maiores veniam vero quis, beatae recusandae ducimus praesentium corrupti suscipit."
+ title: "values.card.value5.title",
+ description: "values.card.value5.description"
},
{
id: 6,
- title: "Mobile App",
- description: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Libero soluta omnis fugiat nulla nobis quod cupiditate numquam placeat assumenda animi maiores veniam vero quis, beatae recusandae ducimus praesentium corrupti suscipit."
- },
- {
- id: 7,
- title: "Mobile App",
- description: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Libero soluta omnis fugiat nulla nobis quod cupiditate numquam placeat assumenda animi maiores veniam vero quis, beatae recusandae ducimus praesentium corrupti suscipit."
- },
- {
- id: 8,
- title: "Mobile App",
- description: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Libero soluta omnis fugiat nulla nobis quod cupiditate numquam placeat assumenda animi maiores veniam vero quis, beatae recusandae ducimus praesentium corrupti suscipit."
- }
+ title: "values.card.value6.title",
+ description: "values.card.value6.description"
+ }
];